Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test out contrib's httpcheckreceiver with response logging support. #13

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions collector/components/httpcheckreceiver/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# HTTP Check Receiver

<!-- status autogenerated section -->
| Status | |
| ------------- |-----------|
| Stability | [development]: metrics, logs |
| Distributions | [contrib] |
| Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aopen%20label%3Areceiver%2Fhttpcheck%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aopen+is%3Aissue+label%3Areceiver%2Fhttpcheck) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector-contrib?query=is%3Aissue%20is%3Aclosed%20label%3Areceiver%2Fhttpcheck%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aclosed+is%3Aissue+label%3Areceiver%2Fhttpcheck) |
| [Code Owners](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/CONTRIBUTING.md#becoming-a-code-owner) | [@codeboten](https://www.github.com/codeboten) |

[development]: https://github.com/open-telemetry/opentelemetry-collector#development
[contrib]: https://github.com/open-telemetry/opentelemetry-collector-releases/tree/main/distributions/otelcol-contrib
<!-- end autogenerated section -->

The HTTP Check Receiver can be used for synthethic checks against HTTP endpoints. This receiver will make a request to the specified `endpoint` using the
configured `method`. This scraper generates a metric with a label for each HTTP response status class with a value of `1` if the status code matches the
class. For example, the following metrics will be generated if the endpoint returned a `200`:

```
httpcheck.status{http.status_class:1xx, http.status_code:200,...} = 0
httpcheck.status{http.status_class:2xx, http.status_code:200,...} = 1
httpcheck.status{http.status_class:3xx, http.status_code:200,...} = 0
httpcheck.status{http.status_class:4xx, http.status_code:200,...} = 0
httpcheck.status{http.status_class:5xx, http.status_code:200,...} = 0
```

## Configuration

The following configuration settings are available:

- `targets` (required): The list of targets to be monitored.
- `collection_interval` (optional, default = `60s`): This receiver collects metrics on an interval. Valid time units are `ns`, `us` (or `µs`), `ms`, `s`, `m`, `h`.
- `initial_delay` (optional, default = `1s`): defines how long this receiver waits before starting.

Each target has the following properties:

- `endpoint` (required): the URL to be monitored
- `method` (optional, default: `GET`): The HTTP method used to call the endpoint

Additionally, each target supports the client configuration options of [confighttp].

### Example Configuration

```yaml
receivers:
httpcheck:
targets:
- endpoint: http://endpoint:80
method: GET
- endpoint: http://localhost:8080/health
method: GET
- endpoint: http://localhost:8081/health
method: POST
headers:
test-header: "test-value"
collection_interval: 10s
```

## Metrics

Details about the metrics produced by this receiver can be found in [documentation.md](./documentation.md)

## Logs

Additionally, the responses from the checks can be reported as logs, including
response body, duration (nanoseconds), endpoint and status code. `httpcheck` MUST
be enabled for both metrics and logs, e.g.

```yaml
service:
pipelines:
metrics:
receivers: [otlp, httpcheck]
processors: [batch]
exporters: [debug]
logs:
receivers: [otlp, httpcheck]
processors: [batch]
exporters: [debug]
```

[confighttp]: https://github.com/open-telemetry/opentelemetry-collector/tree/main/config/confighttp#client-configuration
65 changes: 65 additions & 0 deletions collector/components/httpcheckreceiver/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package httpcheckreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/httpcheckreceiver"

import (
"errors"
"fmt"
"net/url"

"go.opentelemetry.io/collector/config/confighttp"
"go.opentelemetry.io/collector/receiver/scraperhelper"
"go.uber.org/multierr"

"github.com/lightstep/sn-collector/collector/httpcheckreceiver/internal/metadata"
)

// Predefined error responses for configuration validation failures
var (
errMissingEndpoint = errors.New(`"endpoint" must be specified`)
errInvalidEndpoint = errors.New(`"endpoint" must be in the form of <scheme>://<hostname>[:<port>]`)
)

// Config defines the configuration for the various elements of the receiver agent.
type Config struct {
scraperhelper.ControllerConfig `mapstructure:",squash"`
metadata.MetricsBuilderConfig `mapstructure:",squash"`
Targets []*targetConfig `mapstructure:"targets"`
}

type targetConfig struct {
confighttp.ClientConfig `mapstructure:",squash"`
Method string `mapstructure:"method"`
}

// Validate validates the configuration by checking for missing or invalid fields
func (cfg *targetConfig) Validate() error {
var err error

if cfg.Endpoint == "" {
err = multierr.Append(err, errMissingEndpoint)
} else {
_, parseErr := url.ParseRequestURI(cfg.Endpoint)
if parseErr != nil {
err = multierr.Append(err, fmt.Errorf("%s: %w", errInvalidEndpoint.Error(), parseErr))
}
}

return err
}

// Validate validates the configuration by checking for missing or invalid fields
func (cfg *Config) Validate() error {
var err error

if len(cfg.Targets) == 0 {
err = multierr.Append(err, errors.New("no targets configured"))
}

for _, target := range cfg.Targets {
err = multierr.Append(err, target.Validate())
}

return err
}
121 changes: 121 additions & 0 deletions collector/components/httpcheckreceiver/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package httpcheckreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/httpcheckreceiver"

import (
"fmt"
"testing"

"github.com/stretchr/testify/require"
"go.opentelemetry.io/collector/config/confighttp"
"go.opentelemetry.io/collector/receiver/scraperhelper"
"go.uber.org/multierr"
)

func TestValidate(t *testing.T) {
testCases := []struct {
desc string
cfg *Config
expectedErr error
}{
{
desc: "missing endpoint",
cfg: &Config{
Targets: []*targetConfig{
{
ClientConfig: confighttp.ClientConfig{},
},
},
ControllerConfig: scraperhelper.NewDefaultControllerConfig(),
},
expectedErr: multierr.Combine(
errMissingEndpoint,
),
},
{
desc: "invalid endpoint",
cfg: &Config{
Targets: []*targetConfig{
{
ClientConfig: confighttp.ClientConfig{
Endpoint: "invalid://endpoint: 12efg",
},
},
},
ControllerConfig: scraperhelper.NewDefaultControllerConfig(),
},
expectedErr: multierr.Combine(
fmt.Errorf("%w: %s", errInvalidEndpoint, `parse "invalid://endpoint: 12efg": invalid port ": 12efg" after host`),
),
},
{
desc: "invalid config with multiple targets",
cfg: &Config{
Targets: []*targetConfig{
{
ClientConfig: confighttp.ClientConfig{
Endpoint: "https://localhost:80",
},
},
{
ClientConfig: confighttp.ClientConfig{
Endpoint: "invalid://endpoint: 12efg",
},
},
},
ControllerConfig: scraperhelper.NewDefaultControllerConfig(),
},
expectedErr: multierr.Combine(
fmt.Errorf("%w: %s", errInvalidEndpoint, `parse "invalid://endpoint: 12efg": invalid port ": 12efg" after host`),
),
},
{
desc: "missing scheme",
cfg: &Config{
Targets: []*targetConfig{
{
ClientConfig: confighttp.ClientConfig{
Endpoint: "www.opentelemetry.io/docs",
},
},
},
ControllerConfig: scraperhelper.NewDefaultControllerConfig(),
},
expectedErr: multierr.Combine(
fmt.Errorf("%w: %s", errInvalidEndpoint, `parse "www.opentelemetry.io/docs": invalid URI for request`),
),
},
{
desc: "valid config",
cfg: &Config{
Targets: []*targetConfig{
{
ClientConfig: confighttp.ClientConfig{
Endpoint: "https://opentelemetry.io",
},
},
{
ClientConfig: confighttp.ClientConfig{
Endpoint: "https://opentelemetry.io:80/docs",
},
},
},
ControllerConfig: scraperhelper.NewDefaultControllerConfig(),
},
expectedErr: nil,
},
}

for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
actualErr := tc.cfg.Validate()
if tc.expectedErr != nil {
require.EqualError(t, actualErr, tc.expectedErr.Error())
} else {
require.NoError(t, actualErr)
}

})
}
}
6 changes: 6 additions & 0 deletions collector/components/httpcheckreceiver/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

//go:generate mdatagen metadata.yaml

package httpcheckreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/httpcheckreceiver"
59 changes: 59 additions & 0 deletions collector/components/httpcheckreceiver/documentation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
[comment]: <> (Code generated by mdatagen. DO NOT EDIT.)

# httpcheck

## Default Metrics

The following metrics are emitted by default. Each of them can be disabled by applying the following configuration:

```yaml
metrics:
<metric_name>:
enabled: false
```

### httpcheck.duration

Measures the duration of the HTTP check.

| Unit | Metric Type | Value Type |
| ---- | ----------- | ---------- |
| ms | Gauge | Int |

#### Attributes

| Name | Description | Values |
| ---- | ----------- | ------ |
| http.url | Full HTTP request URL. | Any Str |

### httpcheck.error

Records errors occurring during HTTP check.

| Unit | Metric Type | Value Type | Aggregation Temporality | Monotonic |
| ---- | ----------- | ---------- | ----------------------- | --------- |
| {error} | Sum | Int | Cumulative | false |

#### Attributes

| Name | Description | Values |
| ---- | ----------- | ------ |
| http.url | Full HTTP request URL. | Any Str |
| error.message | Error message recorded during check | Any Str |

### httpcheck.status

1 if the check resulted in status_code matching the status_class, otherwise 0.

| Unit | Metric Type | Value Type | Aggregation Temporality | Monotonic |
| ---- | ----------- | ---------- | ----------------------- | --------- |
| 1 | Sum | Int | Cumulative | false |

#### Attributes

| Name | Description | Values |
| ---- | ----------- | ------ |
| http.url | Full HTTP request URL. | Any Str |
| http.status_code | HTTP response status code | Any Int |
| http.method | HTTP request method | Any Str |
| http.status_class | HTTP response status class | Any Str |
Loading
Loading