From 9265d00fbe241a78347a01937f46e5b66a3c706d Mon Sep 17 00:00:00 2001 From: Carlos Alberto Cortez Date: Fri, 26 Apr 2024 01:12:40 +0200 Subject: [PATCH 1/3] Test out contrib's httpcheckreceiver with response logging support. --- .../components/httpcheckreceiver/README.md | 82 +++++ .../components/httpcheckreceiver/config.go | 65 ++++ .../httpcheckreceiver/config_test.go | 121 +++++++ collector/components/httpcheckreceiver/doc.go | 6 + .../httpcheckreceiver/documentation.md | 59 ++++ .../components/httpcheckreceiver/factory.go | 112 +++++++ .../httpcheckreceiver/factory_test.go | 81 +++++ .../generated_component_test.go | 76 +++++ .../generated_package_test.go | 13 + collector/components/httpcheckreceiver/go.mod | 80 +++++ collector/components/httpcheckreceiver/go.sum | 176 ++++++++++ .../internal/metadata/generated_config.go | 58 ++++ .../metadata/generated_config_test.go | 64 ++++ .../internal/metadata/generated_metrics.go | 310 ++++++++++++++++++ .../metadata/generated_metrics_test.go | 161 +++++++++ .../internal/metadata/generated_status.go | 26 ++ .../internal/metadata/package_test.go | 14 + .../internal/metadata/testdata/config.yaml | 17 + .../httpcheckreceiver/metadata.yaml | 57 ++++ .../components/httpcheckreceiver/scraper.go | 156 +++++++++ .../httpcheckreceiver/scraper_test.go | 235 +++++++++++++ .../expected_metrics/endpoint_404.yaml | 103 ++++++ .../expected_metrics/invalid_endpoint.yaml | 117 +++++++ .../expected_metrics/metrics_golden.yaml | 103 ++++++ .../expected_metrics/multiple_targets.yaml | 188 +++++++++++ collector/otelcol-builder.yaml | 5 +- 26 files changed, 2483 insertions(+), 2 deletions(-) create mode 100644 collector/components/httpcheckreceiver/README.md create mode 100644 collector/components/httpcheckreceiver/config.go create mode 100644 collector/components/httpcheckreceiver/config_test.go create mode 100644 collector/components/httpcheckreceiver/doc.go create mode 100644 collector/components/httpcheckreceiver/documentation.md create mode 100644 collector/components/httpcheckreceiver/factory.go create mode 100644 collector/components/httpcheckreceiver/factory_test.go create mode 100644 collector/components/httpcheckreceiver/generated_component_test.go create mode 100644 collector/components/httpcheckreceiver/generated_package_test.go create mode 100644 collector/components/httpcheckreceiver/go.mod create mode 100644 collector/components/httpcheckreceiver/go.sum create mode 100644 collector/components/httpcheckreceiver/internal/metadata/generated_config.go create mode 100644 collector/components/httpcheckreceiver/internal/metadata/generated_config_test.go create mode 100644 collector/components/httpcheckreceiver/internal/metadata/generated_metrics.go create mode 100644 collector/components/httpcheckreceiver/internal/metadata/generated_metrics_test.go create mode 100644 collector/components/httpcheckreceiver/internal/metadata/generated_status.go create mode 100644 collector/components/httpcheckreceiver/internal/metadata/package_test.go create mode 100644 collector/components/httpcheckreceiver/internal/metadata/testdata/config.yaml create mode 100644 collector/components/httpcheckreceiver/metadata.yaml create mode 100644 collector/components/httpcheckreceiver/scraper.go create mode 100644 collector/components/httpcheckreceiver/scraper_test.go create mode 100644 collector/components/httpcheckreceiver/testdata/expected_metrics/endpoint_404.yaml create mode 100644 collector/components/httpcheckreceiver/testdata/expected_metrics/invalid_endpoint.yaml create mode 100644 collector/components/httpcheckreceiver/testdata/expected_metrics/metrics_golden.yaml create mode 100644 collector/components/httpcheckreceiver/testdata/expected_metrics/multiple_targets.yaml diff --git a/collector/components/httpcheckreceiver/README.md b/collector/components/httpcheckreceiver/README.md new file mode 100644 index 0000000..a4388b1 --- /dev/null +++ b/collector/components/httpcheckreceiver/README.md @@ -0,0 +1,82 @@ +# HTTP Check Receiver + + +| 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 + + +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 diff --git a/collector/components/httpcheckreceiver/config.go b/collector/components/httpcheckreceiver/config.go new file mode 100644 index 0000000..61019ac --- /dev/null +++ b/collector/components/httpcheckreceiver/config.go @@ -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/open-telemetry/opentelemetry-collector-contrib/receiver/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 ://[:]`) +) + +// 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 +} diff --git a/collector/components/httpcheckreceiver/config_test.go b/collector/components/httpcheckreceiver/config_test.go new file mode 100644 index 0000000..905de86 --- /dev/null +++ b/collector/components/httpcheckreceiver/config_test.go @@ -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) + } + + }) + } +} diff --git a/collector/components/httpcheckreceiver/doc.go b/collector/components/httpcheckreceiver/doc.go new file mode 100644 index 0000000..0f7a1e8 --- /dev/null +++ b/collector/components/httpcheckreceiver/doc.go @@ -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" diff --git a/collector/components/httpcheckreceiver/documentation.md b/collector/components/httpcheckreceiver/documentation.md new file mode 100644 index 0000000..913cc70 --- /dev/null +++ b/collector/components/httpcheckreceiver/documentation.md @@ -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: + : + 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 | diff --git a/collector/components/httpcheckreceiver/factory.go b/collector/components/httpcheckreceiver/factory.go new file mode 100644 index 0000000..e743429 --- /dev/null +++ b/collector/components/httpcheckreceiver/factory.go @@ -0,0 +1,112 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package httpcheckreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/httpcheckreceiver" + +import ( + "context" + "errors" + "time" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/consumer" + "go.opentelemetry.io/collector/receiver" + "go.opentelemetry.io/collector/receiver/scraperhelper" + + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/httpcheckreceiver/internal/metadata" +) + +var errConfigNotHTTPCheck = errors.New("config was not a HTTP check receiver config") + +// NewFactory creates a new receiver factory +func NewFactory() receiver.Factory { + f := &httpcheckReceiverFactory{ + httpScrapers: make(map[*Config]*httpcheckScraper), + } + return receiver.NewFactory( + metadata.Type, + createDefaultConfig, + receiver.WithMetrics(f.createMetricsReceiver, metadata.MetricsStability), + receiver.WithLogs(f.createLogsReceiver, metadata.LogsStability), + ) +} + +type httpcheckReceiverFactory struct { + httpScrapers map[*Config]*httpcheckScraper +} + +func createDefaultConfig() component.Config { + cfg := scraperhelper.NewDefaultControllerConfig() + cfg.CollectionInterval = 60 * time.Second + + return &Config{ + ControllerConfig: cfg, + MetricsBuilderConfig: metadata.DefaultMetricsBuilderConfig(), + Targets: []*targetConfig{}, + } +} + +func (factory *httpcheckReceiverFactory) ensureScraper( + params receiver.CreateSettings, + config component.Config) (*httpcheckScraper, error) { + + rconfig, ok := config.(*Config) + if !ok { + return nil, errConfigNotHTTPCheck + } + + httpcheckScraper := factory.httpScrapers[rconfig] + if httpcheckScraper != nil { + return httpcheckScraper, nil + } + + httpcheckScraper = newScraper(rconfig, params) + factory.httpScrapers[rconfig] = httpcheckScraper + return httpcheckScraper, nil +} + +func (factory *httpcheckReceiverFactory) createMetricsReceiver( + _ context.Context, + params receiver.CreateSettings, + rConf component.Config, + consumer consumer.Metrics) (receiver.Metrics, error) { + + httpcheckScraper, err := factory.ensureScraper(params, rConf) + if err != nil { + return nil, err + } + + cfg := rConf.(*Config) + scraper, err := scraperhelper.NewScraper(metadata.Type.String(), httpcheckScraper.scrape, scraperhelper.WithStart(httpcheckScraper.start)) + if err != nil { + return nil, err + } + + return scraperhelper.NewScraperControllerReceiver(&cfg.ControllerConfig, params, consumer, scraperhelper.AddScraper(scraper)) +} + +func (factory *httpcheckReceiverFactory) createLogsReceiver( + _ context.Context, + params receiver.CreateSettings, + rConf component.Config, + consumer consumer.Logs) (receiver.Logs, error) { + + httpcheckScraper, err := factory.ensureScraper(params, rConf) + if err != nil { + return nil, err + } + + httpcheckScraper.logs = consumer + return &nopReceiver{}, nil +} + +type nopReceiver struct { +} + +func (receiver *nopReceiver) Start(_ context.Context, _ component.Host) error { + return nil +} + +func (receiver *nopReceiver) Shutdown(_ context.Context) error { + return nil +} diff --git a/collector/components/httpcheckreceiver/factory_test.go b/collector/components/httpcheckreceiver/factory_test.go new file mode 100644 index 0000000..c34ecec --- /dev/null +++ b/collector/components/httpcheckreceiver/factory_test.go @@ -0,0 +1,81 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package httpcheckreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/httpcheckreceiver" + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/consumer/consumertest" + "go.opentelemetry.io/collector/receiver/receivertest" + "go.opentelemetry.io/collector/receiver/scraperhelper" + + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/httpcheckreceiver/internal/metadata" +) + +func TestNewFactory(t *testing.T) { + testCases := []struct { + desc string + testFunc func(*testing.T) + }{ + { + desc: "creates a new factory with correct type", + testFunc: func(t *testing.T) { + factory := NewFactory() + require.EqualValues(t, metadata.Type, factory.Type()) + }, + }, + { + desc: "creates a new factory with default config", + testFunc: func(t *testing.T) { + factory := NewFactory() + + var expectedCfg component.Config = &Config{ + ControllerConfig: scraperhelper.ControllerConfig{ + CollectionInterval: 60 * time.Second, + InitialDelay: time.Second, + }, + MetricsBuilderConfig: metadata.DefaultMetricsBuilderConfig(), + Targets: []*targetConfig{}, + } + + require.Equal(t, expectedCfg, factory.CreateDefaultConfig()) + }, + }, + { + desc: "creates a new factory and CreateMetricsReceiver returns no error", + testFunc: func(t *testing.T) { + factory := NewFactory() + cfg := factory.CreateDefaultConfig() + _, err := factory.CreateMetricsReceiver( + context.Background(), + receivertest.NewNopCreateSettings(), + cfg, + consumertest.NewNop(), + ) + require.NoError(t, err) + }, + }, + { + desc: "creates a new factory and CreateMetricsReceiver returns error with incorrect config", + testFunc: func(t *testing.T) { + factory := NewFactory() + _, err := factory.CreateMetricsReceiver( + context.Background(), + receivertest.NewNopCreateSettings(), + nil, + consumertest.NewNop(), + ) + require.ErrorIs(t, err, errConfigNotHTTPCheck) + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, tc.testFunc) + } +} diff --git a/collector/components/httpcheckreceiver/generated_component_test.go b/collector/components/httpcheckreceiver/generated_component_test.go new file mode 100644 index 0000000..7a6e80e --- /dev/null +++ b/collector/components/httpcheckreceiver/generated_component_test.go @@ -0,0 +1,76 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package httpcheckreceiver + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componenttest" + "go.opentelemetry.io/collector/confmap/confmaptest" + "go.opentelemetry.io/collector/consumer/consumertest" + "go.opentelemetry.io/collector/receiver" + "go.opentelemetry.io/collector/receiver/receivertest" +) + +func TestComponentFactoryType(t *testing.T) { + require.Equal(t, "httpcheck", NewFactory().Type().String()) +} + +func TestComponentConfigStruct(t *testing.T) { + require.NoError(t, componenttest.CheckConfigStruct(NewFactory().CreateDefaultConfig())) +} + +func TestComponentLifecycle(t *testing.T) { + factory := NewFactory() + + tests := []struct { + name string + createFn func(ctx context.Context, set receiver.CreateSettings, cfg component.Config) (component.Component, error) + }{ + + { + name: "logs", + createFn: func(ctx context.Context, set receiver.CreateSettings, cfg component.Config) (component.Component, error) { + return factory.CreateLogsReceiver(ctx, set, cfg, consumertest.NewNop()) + }, + }, + + { + name: "metrics", + createFn: func(ctx context.Context, set receiver.CreateSettings, cfg component.Config) (component.Component, error) { + return factory.CreateMetricsReceiver(ctx, set, cfg, consumertest.NewNop()) + }, + }, + } + + cm, err := confmaptest.LoadConf("metadata.yaml") + require.NoError(t, err) + cfg := factory.CreateDefaultConfig() + sub, err := cm.Sub("tests::config") + require.NoError(t, err) + require.NoError(t, component.UnmarshalConfig(sub, cfg)) + + for _, test := range tests { + t.Run(test.name+"-shutdown", func(t *testing.T) { + c, err := test.createFn(context.Background(), receivertest.NewNopCreateSettings(), cfg) + require.NoError(t, err) + err = c.Shutdown(context.Background()) + require.NoError(t, err) + }) + t.Run(test.name+"-lifecycle", func(t *testing.T) { + firstRcvr, err := test.createFn(context.Background(), receivertest.NewNopCreateSettings(), cfg) + require.NoError(t, err) + host := componenttest.NewNopHost() + require.NoError(t, err) + require.NoError(t, firstRcvr.Start(context.Background(), host)) + require.NoError(t, firstRcvr.Shutdown(context.Background())) + secondRcvr, err := test.createFn(context.Background(), receivertest.NewNopCreateSettings(), cfg) + require.NoError(t, err) + require.NoError(t, secondRcvr.Start(context.Background(), host)) + require.NoError(t, secondRcvr.Shutdown(context.Background())) + }) + } +} diff --git a/collector/components/httpcheckreceiver/generated_package_test.go b/collector/components/httpcheckreceiver/generated_package_test.go new file mode 100644 index 0000000..73e8dbb --- /dev/null +++ b/collector/components/httpcheckreceiver/generated_package_test.go @@ -0,0 +1,13 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package httpcheckreceiver + +import ( + "testing" + + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + goleak.VerifyTestMain(m) +} diff --git a/collector/components/httpcheckreceiver/go.mod b/collector/components/httpcheckreceiver/go.mod new file mode 100644 index 0000000..009fe0f --- /dev/null +++ b/collector/components/httpcheckreceiver/go.mod @@ -0,0 +1,80 @@ +module github.com/open-telemetry/opentelemetry-collector-contrib/receiver/httpcheckreceiver + +go 1.21.0 + +require ( + github.com/google/go-cmp v0.6.0 + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden v0.99.0 + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.99.0 + github.com/stretchr/testify v1.9.0 + go.opentelemetry.io/collector/component v0.99.0 + go.opentelemetry.io/collector/config/confighttp v0.99.0 + go.opentelemetry.io/collector/config/configtls v0.99.0 + go.opentelemetry.io/collector/confmap v0.99.0 + go.opentelemetry.io/collector/consumer v0.99.0 + go.opentelemetry.io/collector/pdata v1.6.0 + go.opentelemetry.io/collector/receiver v0.99.0 + go.opentelemetry.io/otel/metric v1.25.0 + go.opentelemetry.io/otel/trace v1.25.0 + go.uber.org/goleak v1.3.0 + go.uber.org/multierr v1.11.0 + go.uber.org/zap v1.27.0 +) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/hashicorp/go-version v1.6.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.17.8 // indirect + github.com/knadh/koanf/maps v0.1.1 // indirect + github.com/knadh/koanf/providers/confmap v0.1.0 // indirect + github.com/knadh/koanf/v2 v2.1.1 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.99.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_golang v1.19.0 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.52.3 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + github.com/rs/cors v1.10.1 // indirect + go.opentelemetry.io/collector v0.99.0 // indirect + go.opentelemetry.io/collector/config/configauth v0.99.0 // indirect + go.opentelemetry.io/collector/config/configcompression v1.6.0 // indirect + go.opentelemetry.io/collector/config/configopaque v1.6.0 // indirect + go.opentelemetry.io/collector/config/configtelemetry v0.99.0 // indirect + go.opentelemetry.io/collector/config/internal v0.99.0 // indirect + go.opentelemetry.io/collector/extension v0.99.0 // indirect + go.opentelemetry.io/collector/extension/auth v0.99.0 // indirect + go.opentelemetry.io/collector/featuregate v1.6.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0 // indirect + go.opentelemetry.io/otel v1.25.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.47.0 // indirect + go.opentelemetry.io/otel/sdk v1.25.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.25.0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect + google.golang.org/grpc v1.63.2 // indirect + google.golang.org/protobuf v1.33.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +retract ( + v0.76.2 + v0.76.1 + v0.65.0 +) diff --git a/collector/components/httpcheckreceiver/go.sum b/collector/components/httpcheckreceiver/go.sum new file mode 100644 index 0000000..d1cfde0 --- /dev/null +++ b/collector/components/httpcheckreceiver/go.sum @@ -0,0 +1,176 @@ +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 h1:TQcrn6Wq+sKGkpyPvppOz99zsMBaUOKXq6HSv655U1c= +github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= +github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= +github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs= +github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI= +github.com/knadh/koanf/providers/confmap v0.1.0 h1:gOkxhHkemwG4LezxxN8DMOFopOPghxRVp7JbIvdvqzU= +github.com/knadh/koanf/providers/confmap v0.1.0/go.mod h1:2uLhxQzJnyHKfxG927awZC7+fyHFdQkd697K4MdLnIU= +github.com/knadh/koanf/v2 v2.1.1 h1:/R8eXqasSTsmDCsAyYj+81Wteg8AqrV9CP6gvsTsOmM= +github.com/knadh/koanf/v2 v2.1.1/go.mod h1:4mnTRbZCK+ALuBXHZMjDfG9y714L7TykVnZkXbMU3Es= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden v0.99.0 h1:UasccXIsMcNGsGuyft5N7C1vvzRmQCYHWVXF7YBcY/I= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden v0.99.0/go.mod h1:v9snZu986K9CFjBYeYMwYs2hyqfTiOgLZDw5qphiObI= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.99.0 h1:EEgERxtXvEIhtHtC7kWbRmfX5LWNIBQqx0RI70r1bfw= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.99.0/go.mod h1:5KimRKjmPn++ylvv00qpVnaInTejqcrrYlZ24pCiymQ= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.99.0 h1:773dRpLIpySHFwejaGl8cPbyf1Q2jwhMOgJjDPSqQj0= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.99.0/go.mod h1:PXpmpyK6VWoq1zb5ltUZxqSEpuBpFsY5WmlInpNiDUI= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= +github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.52.3 h1:5f8uj6ZwHSscOGNdIQg6OiZv/ybiK2CO2q2drVZAQSA= +github.com/prometheus/common v0.52.3/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= +github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/collector v0.99.0 h1:O3EtCr+Bp2FoYI4KZCcC10FbMOjtRPXN1JBgFmi2WvY= +go.opentelemetry.io/collector v0.99.0/go.mod h1:rdrDdSy+184UZ7YhJEo7aq9KHdrq6J46WWC//Tg7FBo= +go.opentelemetry.io/collector/component v0.99.0 h1:uU8m9d19Jf+zaf7T8Bl12Mm1qozqTZkDISCnnBnS0u4= +go.opentelemetry.io/collector/component v0.99.0/go.mod h1:sGAyyOtJRlqqt396jisIQxsOW7cOIKOTLi+iCarx++s= +go.opentelemetry.io/collector/config/configauth v0.99.0 h1:ggq8ow4HCSqab+YsdrbWRiePamHJdZlkUg1ve6Gg/Cc= +go.opentelemetry.io/collector/config/configauth v0.99.0/go.mod h1:24vfHNtW9sekwkje7C6kerbqqcG4V0Ezj/HZ0Clllc0= +go.opentelemetry.io/collector/config/configcompression v1.6.0 h1:uSQ5nNMLOdUVYEIBkATcJvwOasZbGUPGHXGDmaRRU8s= +go.opentelemetry.io/collector/config/configcompression v1.6.0/go.mod h1:O0fOPCADyGwGLLIf5lf7N3960NsnIfxsm6dr/mIpL+M= +go.opentelemetry.io/collector/config/confighttp v0.99.0 h1:tstF3CdiRId6etg9FbN5SLKPxhlW1TasErHF7AzxTvE= +go.opentelemetry.io/collector/config/confighttp v0.99.0/go.mod h1:CeLCwdaMLBlWdyruxFMH1hVGgTINYUaY2K79OHnr4KI= +go.opentelemetry.io/collector/config/configopaque v1.6.0 h1:MVlbCzVln1+8+VWxKVCLWONZNISVrSkbIz0+Q/bneOc= +go.opentelemetry.io/collector/config/configopaque v1.6.0/go.mod h1:i5d1RN7jwmChc78dCCF5ZE4Sm5EXXpksHbf1/tOBXho= +go.opentelemetry.io/collector/config/configtelemetry v0.99.0 h1:Fks8xkTUnxw1nEcTyYOXnIHttI9BGgjOCB0bwBH3LcU= +go.opentelemetry.io/collector/config/configtelemetry v0.99.0/go.mod h1:YV5PaOdtnU1xRomPcYqoHmyCr48tnaAREeGO96EZw8o= +go.opentelemetry.io/collector/config/configtls v0.99.0 h1:T83FIw+f0SZu0pNoAccbNLNsaQJRX541q2R+pQXVGEY= +go.opentelemetry.io/collector/config/configtls v0.99.0/go.mod h1:TQO3AhguNC8GZxFCu3PpxMw0ZNoyFAAyRsqcz/ID2qY= +go.opentelemetry.io/collector/config/internal v0.99.0 h1:CkYpKq05qe/9v0us16Mtr3p+EvBI0ePTKIUC2gYcBns= +go.opentelemetry.io/collector/config/internal v0.99.0/go.mod h1:pCqivIZCN0wP2IjNZfDvTLjjdLAZgm7jOHVhrPwt+/Y= +go.opentelemetry.io/collector/confmap v0.99.0 h1:0ZJOl79eEm/oxR6aTIbhL9E5liq6UEod2gt1pYNaIoc= +go.opentelemetry.io/collector/confmap v0.99.0/go.mod h1:BWKPIpYeUzSG6ZgCJMjF7xsLvyrvJCfYURl57E5vhiQ= +go.opentelemetry.io/collector/consumer v0.99.0 h1:juBa4nikGfi5QxjvKnscWG88BXyyozmtSLiLrw2An84= +go.opentelemetry.io/collector/consumer v0.99.0/go.mod h1:YzGeaxvKqkgtPFbFWXf4WtNO6KC8pdw209PaBQzV8Pk= +go.opentelemetry.io/collector/extension v0.99.0 h1:o8Lb7oT/CvqLz9JC9qJCs5h8ABlDVsdGeIJp/a8BFvs= +go.opentelemetry.io/collector/extension v0.99.0/go.mod h1:Whm3qKOk4F6336T6a0BlAxtt4+fEOLECuqTBazLG8mM= +go.opentelemetry.io/collector/extension/auth v0.99.0 h1:txyH8hQugRinASfuRmNgFj24TpkXN7q5H+oLVB9VaS4= +go.opentelemetry.io/collector/extension/auth v0.99.0/go.mod h1:brtmx1Xgj+2WBM5vYX59TRYiDjZ7+CNP3QM/V9WL2dI= +go.opentelemetry.io/collector/featuregate v1.6.0 h1:1Q0tt/GPx+PRBGAE7kNJaWLIXYNVD74K/KYf0DTXZfM= +go.opentelemetry.io/collector/featuregate v1.6.0/go.mod h1:w7nUODKxEi3FLf1HslCiE6YWtMtOOrMnSwsDam8Mg9w= +go.opentelemetry.io/collector/pdata v1.6.0 h1:ZIByleLu7ZfHkfPuL8xIMb9M4Gv1R6568LAjhNOO9zY= +go.opentelemetry.io/collector/pdata v1.6.0/go.mod h1:pQv6AJO6wDUDxrPxhNaj3JdSzaOIo5glTGL1b4h4KTg= +go.opentelemetry.io/collector/pdata/testdata v0.99.0 h1:/cEg4jdR3ntR3kZ0XjSelaBnm7GNSsFF1K3VK+ZHvL8= +go.opentelemetry.io/collector/pdata/testdata v0.99.0/go.mod h1:YzEkHFLPsxeNI2gv6UQvvn73nsgRNxMRnBpY63qvdsg= +go.opentelemetry.io/collector/receiver v0.99.0 h1:NdYShaEaabxVBRQaxK/HcKqRGl1eUFaipKmjZlQb5FA= +go.opentelemetry.io/collector/receiver v0.99.0/go.mod h1:aU9ftU4FhdEY9/eREf86FWHmZHz8kufXchfpHrTTrn0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0 h1:cEPbyTSEHlQR89XVlyo78gqluF8Y3oMeBkXGWzQsfXY= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0/go.mod h1:DKdbWcT4GH1D0Y3Sqt/PFXt2naRKDWtU+eE6oLdFNA8= +go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k= +go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg= +go.opentelemetry.io/otel/exporters/prometheus v0.47.0 h1:OL6yk1Z/pEGdDnrBbxSsH+t4FY1zXfBRGd7bjwhlMLU= +go.opentelemetry.io/otel/exporters/prometheus v0.47.0/go.mod h1:xF3N4OSICZDVbbYZydz9MHFro1RjmkPUKEvar2utG+Q= +go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA= +go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s= +go.opentelemetry.io/otel/sdk v1.25.0 h1:PDryEJPC8YJZQSyLY5eqLeafHtG+X7FWnf3aXMtxbqo= +go.opentelemetry.io/otel/sdk v1.25.0/go.mod h1:oFgzCM2zdsxKzz6zwpTZYLLQsFwc+K0daArPdIhuxkw= +go.opentelemetry.io/otel/sdk/metric v1.25.0 h1:7CiHOy08LbrxMAp4vWpbiPcklunUshVpAvGBrdDRlGw= +go.opentelemetry.io/otel/sdk/metric v1.25.0/go.mod h1:LzwoKptdbBBdYfvtGCzGwk6GWMA3aUzBOwtQpR6Nz7o= +go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM= +go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= +google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/collector/components/httpcheckreceiver/internal/metadata/generated_config.go b/collector/components/httpcheckreceiver/internal/metadata/generated_config.go new file mode 100644 index 0000000..4030a82 --- /dev/null +++ b/collector/components/httpcheckreceiver/internal/metadata/generated_config.go @@ -0,0 +1,58 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package metadata + +import ( + "go.opentelemetry.io/collector/confmap" +) + +// MetricConfig provides common config for a particular metric. +type MetricConfig struct { + Enabled bool `mapstructure:"enabled"` + + enabledSetByUser bool +} + +func (ms *MetricConfig) Unmarshal(parser *confmap.Conf) error { + if parser == nil { + return nil + } + err := parser.Unmarshal(ms) + if err != nil { + return err + } + ms.enabledSetByUser = parser.IsSet("enabled") + return nil +} + +// MetricsConfig provides config for httpcheck metrics. +type MetricsConfig struct { + HttpcheckDuration MetricConfig `mapstructure:"httpcheck.duration"` + HttpcheckError MetricConfig `mapstructure:"httpcheck.error"` + HttpcheckStatus MetricConfig `mapstructure:"httpcheck.status"` +} + +func DefaultMetricsConfig() MetricsConfig { + return MetricsConfig{ + HttpcheckDuration: MetricConfig{ + Enabled: true, + }, + HttpcheckError: MetricConfig{ + Enabled: true, + }, + HttpcheckStatus: MetricConfig{ + Enabled: true, + }, + } +} + +// MetricsBuilderConfig is a configuration for httpcheck metrics builder. +type MetricsBuilderConfig struct { + Metrics MetricsConfig `mapstructure:"metrics"` +} + +func DefaultMetricsBuilderConfig() MetricsBuilderConfig { + return MetricsBuilderConfig{ + Metrics: DefaultMetricsConfig(), + } +} diff --git a/collector/components/httpcheckreceiver/internal/metadata/generated_config_test.go b/collector/components/httpcheckreceiver/internal/metadata/generated_config_test.go new file mode 100644 index 0000000..cb3011f --- /dev/null +++ b/collector/components/httpcheckreceiver/internal/metadata/generated_config_test.go @@ -0,0 +1,64 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package metadata + +import ( + "path/filepath" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/confmap/confmaptest" +) + +func TestMetricsBuilderConfig(t *testing.T) { + tests := []struct { + name string + want MetricsBuilderConfig + }{ + { + name: "default", + want: DefaultMetricsBuilderConfig(), + }, + { + name: "all_set", + want: MetricsBuilderConfig{ + Metrics: MetricsConfig{ + HttpcheckDuration: MetricConfig{Enabled: true}, + HttpcheckError: MetricConfig{Enabled: true}, + HttpcheckStatus: MetricConfig{Enabled: true}, + }, + }, + }, + { + name: "none_set", + want: MetricsBuilderConfig{ + Metrics: MetricsConfig{ + HttpcheckDuration: MetricConfig{Enabled: false}, + HttpcheckError: MetricConfig{Enabled: false}, + HttpcheckStatus: MetricConfig{Enabled: false}, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := loadMetricsBuilderConfig(t, tt.name) + if diff := cmp.Diff(tt.want, cfg, cmpopts.IgnoreUnexported(MetricConfig{})); diff != "" { + t.Errorf("Config mismatch (-expected +actual):\n%s", diff) + } + }) + } +} + +func loadMetricsBuilderConfig(t *testing.T, name string) MetricsBuilderConfig { + cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml")) + require.NoError(t, err) + sub, err := cm.Sub(name) + require.NoError(t, err) + cfg := DefaultMetricsBuilderConfig() + require.NoError(t, component.UnmarshalConfig(sub, &cfg)) + return cfg +} diff --git a/collector/components/httpcheckreceiver/internal/metadata/generated_metrics.go b/collector/components/httpcheckreceiver/internal/metadata/generated_metrics.go new file mode 100644 index 0000000..628ccb1 --- /dev/null +++ b/collector/components/httpcheckreceiver/internal/metadata/generated_metrics.go @@ -0,0 +1,310 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package metadata + +import ( + "time" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/receiver" +) + +type metricHttpcheckDuration struct { + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills httpcheck.duration metric with initial data. +func (m *metricHttpcheckDuration) init() { + m.data.SetName("httpcheck.duration") + m.data.SetDescription("Measures the duration of the HTTP check.") + m.data.SetUnit("ms") + m.data.SetEmptyGauge() + m.data.Gauge().DataPoints().EnsureCapacity(m.capacity) +} + +func (m *metricHttpcheckDuration) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, httpURLAttributeValue string) { + if !m.config.Enabled { + return + } + dp := m.data.Gauge().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntValue(val) + dp.Attributes().PutStr("http.url", httpURLAttributeValue) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricHttpcheckDuration) updateCapacity() { + if m.data.Gauge().DataPoints().Len() > m.capacity { + m.capacity = m.data.Gauge().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricHttpcheckDuration) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricHttpcheckDuration(cfg MetricConfig) metricHttpcheckDuration { + m := metricHttpcheckDuration{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + +type metricHttpcheckError struct { + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills httpcheck.error metric with initial data. +func (m *metricHttpcheckError) init() { + m.data.SetName("httpcheck.error") + m.data.SetDescription("Records errors occurring during HTTP check.") + m.data.SetUnit("{error}") + m.data.SetEmptySum() + m.data.Sum().SetIsMonotonic(false) + m.data.Sum().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative) + m.data.Sum().DataPoints().EnsureCapacity(m.capacity) +} + +func (m *metricHttpcheckError) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, httpURLAttributeValue string, errorMessageAttributeValue string) { + if !m.config.Enabled { + return + } + dp := m.data.Sum().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntValue(val) + dp.Attributes().PutStr("http.url", httpURLAttributeValue) + dp.Attributes().PutStr("error.message", errorMessageAttributeValue) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricHttpcheckError) updateCapacity() { + if m.data.Sum().DataPoints().Len() > m.capacity { + m.capacity = m.data.Sum().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricHttpcheckError) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Sum().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricHttpcheckError(cfg MetricConfig) metricHttpcheckError { + m := metricHttpcheckError{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + +type metricHttpcheckStatus struct { + data pmetric.Metric // data buffer for generated metric. + config MetricConfig // metric config provided by user. + capacity int // max observed number of data points added to the metric. +} + +// init fills httpcheck.status metric with initial data. +func (m *metricHttpcheckStatus) init() { + m.data.SetName("httpcheck.status") + m.data.SetDescription("1 if the check resulted in status_code matching the status_class, otherwise 0.") + m.data.SetUnit("1") + m.data.SetEmptySum() + m.data.Sum().SetIsMonotonic(false) + m.data.Sum().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative) + m.data.Sum().DataPoints().EnsureCapacity(m.capacity) +} + +func (m *metricHttpcheckStatus) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, httpURLAttributeValue string, httpStatusCodeAttributeValue int64, httpMethodAttributeValue string, httpStatusClassAttributeValue string) { + if !m.config.Enabled { + return + } + dp := m.data.Sum().DataPoints().AppendEmpty() + dp.SetStartTimestamp(start) + dp.SetTimestamp(ts) + dp.SetIntValue(val) + dp.Attributes().PutStr("http.url", httpURLAttributeValue) + dp.Attributes().PutInt("http.status_code", httpStatusCodeAttributeValue) + dp.Attributes().PutStr("http.method", httpMethodAttributeValue) + dp.Attributes().PutStr("http.status_class", httpStatusClassAttributeValue) +} + +// updateCapacity saves max length of data point slices that will be used for the slice capacity. +func (m *metricHttpcheckStatus) updateCapacity() { + if m.data.Sum().DataPoints().Len() > m.capacity { + m.capacity = m.data.Sum().DataPoints().Len() + } +} + +// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points. +func (m *metricHttpcheckStatus) emit(metrics pmetric.MetricSlice) { + if m.config.Enabled && m.data.Sum().DataPoints().Len() > 0 { + m.updateCapacity() + m.data.MoveTo(metrics.AppendEmpty()) + m.init() + } +} + +func newMetricHttpcheckStatus(cfg MetricConfig) metricHttpcheckStatus { + m := metricHttpcheckStatus{config: cfg} + if cfg.Enabled { + m.data = pmetric.NewMetric() + m.init() + } + return m +} + +// MetricsBuilder provides an interface for scrapers to report metrics while taking care of all the transformations +// required to produce metric representation defined in metadata and user config. +type MetricsBuilder struct { + config MetricsBuilderConfig // config of the metrics builder. + startTime pcommon.Timestamp // start time that will be applied to all recorded data points. + metricsCapacity int // maximum observed number of metrics per resource. + metricsBuffer pmetric.Metrics // accumulates metrics data before emitting. + buildInfo component.BuildInfo // contains version information. + metricHttpcheckDuration metricHttpcheckDuration + metricHttpcheckError metricHttpcheckError + metricHttpcheckStatus metricHttpcheckStatus +} + +// metricBuilderOption applies changes to default metrics builder. +type metricBuilderOption func(*MetricsBuilder) + +// WithStartTime sets startTime on the metrics builder. +func WithStartTime(startTime pcommon.Timestamp) metricBuilderOption { + return func(mb *MetricsBuilder) { + mb.startTime = startTime + } +} + +func NewMetricsBuilder(mbc MetricsBuilderConfig, settings receiver.CreateSettings, options ...metricBuilderOption) *MetricsBuilder { + mb := &MetricsBuilder{ + config: mbc, + startTime: pcommon.NewTimestampFromTime(time.Now()), + metricsBuffer: pmetric.NewMetrics(), + buildInfo: settings.BuildInfo, + metricHttpcheckDuration: newMetricHttpcheckDuration(mbc.Metrics.HttpcheckDuration), + metricHttpcheckError: newMetricHttpcheckError(mbc.Metrics.HttpcheckError), + metricHttpcheckStatus: newMetricHttpcheckStatus(mbc.Metrics.HttpcheckStatus), + } + + for _, op := range options { + op(mb) + } + return mb +} + +// updateCapacity updates max length of metrics and resource attributes that will be used for the slice capacity. +func (mb *MetricsBuilder) updateCapacity(rm pmetric.ResourceMetrics) { + if mb.metricsCapacity < rm.ScopeMetrics().At(0).Metrics().Len() { + mb.metricsCapacity = rm.ScopeMetrics().At(0).Metrics().Len() + } +} + +// ResourceMetricsOption applies changes to provided resource metrics. +type ResourceMetricsOption func(pmetric.ResourceMetrics) + +// WithResource sets the provided resource on the emitted ResourceMetrics. +// It's recommended to use ResourceBuilder to create the resource. +func WithResource(res pcommon.Resource) ResourceMetricsOption { + return func(rm pmetric.ResourceMetrics) { + res.CopyTo(rm.Resource()) + } +} + +// WithStartTimeOverride overrides start time for all the resource metrics data points. +// This option should be only used if different start time has to be set on metrics coming from different resources. +func WithStartTimeOverride(start pcommon.Timestamp) ResourceMetricsOption { + return func(rm pmetric.ResourceMetrics) { + var dps pmetric.NumberDataPointSlice + metrics := rm.ScopeMetrics().At(0).Metrics() + for i := 0; i < metrics.Len(); i++ { + switch metrics.At(i).Type() { + case pmetric.MetricTypeGauge: + dps = metrics.At(i).Gauge().DataPoints() + case pmetric.MetricTypeSum: + dps = metrics.At(i).Sum().DataPoints() + } + for j := 0; j < dps.Len(); j++ { + dps.At(j).SetStartTimestamp(start) + } + } + } +} + +// EmitForResource saves all the generated metrics under a new resource and updates the internal state to be ready for +// recording another set of data points as part of another resource. This function can be helpful when one scraper +// needs to emit metrics from several resources. Otherwise calling this function is not required, +// just `Emit` function can be called instead. +// Resource attributes should be provided as ResourceMetricsOption arguments. +func (mb *MetricsBuilder) EmitForResource(rmo ...ResourceMetricsOption) { + rm := pmetric.NewResourceMetrics() + ils := rm.ScopeMetrics().AppendEmpty() + ils.Scope().SetName("otelcol/httpcheckreceiver") + ils.Scope().SetVersion(mb.buildInfo.Version) + ils.Metrics().EnsureCapacity(mb.metricsCapacity) + mb.metricHttpcheckDuration.emit(ils.Metrics()) + mb.metricHttpcheckError.emit(ils.Metrics()) + mb.metricHttpcheckStatus.emit(ils.Metrics()) + + for _, op := range rmo { + op(rm) + } + + if ils.Metrics().Len() > 0 { + mb.updateCapacity(rm) + rm.MoveTo(mb.metricsBuffer.ResourceMetrics().AppendEmpty()) + } +} + +// Emit returns all the metrics accumulated by the metrics builder and updates the internal state to be ready for +// recording another set of metrics. This function will be responsible for applying all the transformations required to +// produce metric representation defined in metadata and user config, e.g. delta or cumulative. +func (mb *MetricsBuilder) Emit(rmo ...ResourceMetricsOption) pmetric.Metrics { + mb.EmitForResource(rmo...) + metrics := mb.metricsBuffer + mb.metricsBuffer = pmetric.NewMetrics() + return metrics +} + +// RecordHttpcheckDurationDataPoint adds a data point to httpcheck.duration metric. +func (mb *MetricsBuilder) RecordHttpcheckDurationDataPoint(ts pcommon.Timestamp, val int64, httpURLAttributeValue string) { + mb.metricHttpcheckDuration.recordDataPoint(mb.startTime, ts, val, httpURLAttributeValue) +} + +// RecordHttpcheckErrorDataPoint adds a data point to httpcheck.error metric. +func (mb *MetricsBuilder) RecordHttpcheckErrorDataPoint(ts pcommon.Timestamp, val int64, httpURLAttributeValue string, errorMessageAttributeValue string) { + mb.metricHttpcheckError.recordDataPoint(mb.startTime, ts, val, httpURLAttributeValue, errorMessageAttributeValue) +} + +// RecordHttpcheckStatusDataPoint adds a data point to httpcheck.status metric. +func (mb *MetricsBuilder) RecordHttpcheckStatusDataPoint(ts pcommon.Timestamp, val int64, httpURLAttributeValue string, httpStatusCodeAttributeValue int64, httpMethodAttributeValue string, httpStatusClassAttributeValue string) { + mb.metricHttpcheckStatus.recordDataPoint(mb.startTime, ts, val, httpURLAttributeValue, httpStatusCodeAttributeValue, httpMethodAttributeValue, httpStatusClassAttributeValue) +} + +// Reset resets metrics builder to its initial state. It should be used when external metrics source is restarted, +// and metrics builder should update its startTime and reset it's internal state accordingly. +func (mb *MetricsBuilder) Reset(options ...metricBuilderOption) { + mb.startTime = pcommon.NewTimestampFromTime(time.Now()) + for _, op := range options { + op(mb) + } +} diff --git a/collector/components/httpcheckreceiver/internal/metadata/generated_metrics_test.go b/collector/components/httpcheckreceiver/internal/metadata/generated_metrics_test.go new file mode 100644 index 0000000..3f027cb --- /dev/null +++ b/collector/components/httpcheckreceiver/internal/metadata/generated_metrics_test.go @@ -0,0 +1,161 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package metadata + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/receiver/receivertest" + "go.uber.org/zap" + "go.uber.org/zap/zaptest/observer" +) + +type testDataSet int + +const ( + testDataSetDefault testDataSet = iota + testDataSetAll + testDataSetNone +) + +func TestMetricsBuilder(t *testing.T) { + tests := []struct { + name string + metricsSet testDataSet + resAttrsSet testDataSet + expectEmpty bool + }{ + { + name: "default", + }, + { + name: "all_set", + metricsSet: testDataSetAll, + resAttrsSet: testDataSetAll, + }, + { + name: "none_set", + metricsSet: testDataSetNone, + resAttrsSet: testDataSetNone, + expectEmpty: true, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + start := pcommon.Timestamp(1_000_000_000) + ts := pcommon.Timestamp(1_000_001_000) + observedZapCore, observedLogs := observer.New(zap.WarnLevel) + settings := receivertest.NewNopCreateSettings() + settings.Logger = zap.New(observedZapCore) + mb := NewMetricsBuilder(loadMetricsBuilderConfig(t, test.name), settings, WithStartTime(start)) + + expectedWarnings := 0 + + assert.Equal(t, expectedWarnings, observedLogs.Len()) + + defaultMetricsCount := 0 + allMetricsCount := 0 + + defaultMetricsCount++ + allMetricsCount++ + mb.RecordHttpcheckDurationDataPoint(ts, 1, "http.url-val") + + defaultMetricsCount++ + allMetricsCount++ + mb.RecordHttpcheckErrorDataPoint(ts, 1, "http.url-val", "error.message-val") + + defaultMetricsCount++ + allMetricsCount++ + mb.RecordHttpcheckStatusDataPoint(ts, 1, "http.url-val", 16, "http.method-val", "http.status_class-val") + + res := pcommon.NewResource() + metrics := mb.Emit(WithResource(res)) + + if test.expectEmpty { + assert.Equal(t, 0, metrics.ResourceMetrics().Len()) + return + } + + assert.Equal(t, 1, metrics.ResourceMetrics().Len()) + rm := metrics.ResourceMetrics().At(0) + assert.Equal(t, res, rm.Resource()) + assert.Equal(t, 1, rm.ScopeMetrics().Len()) + ms := rm.ScopeMetrics().At(0).Metrics() + if test.metricsSet == testDataSetDefault { + assert.Equal(t, defaultMetricsCount, ms.Len()) + } + if test.metricsSet == testDataSetAll { + assert.Equal(t, allMetricsCount, ms.Len()) + } + validatedMetrics := make(map[string]bool) + for i := 0; i < ms.Len(); i++ { + switch ms.At(i).Name() { + case "httpcheck.duration": + assert.False(t, validatedMetrics["httpcheck.duration"], "Found a duplicate in the metrics slice: httpcheck.duration") + validatedMetrics["httpcheck.duration"] = true + assert.Equal(t, pmetric.MetricTypeGauge, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Gauge().DataPoints().Len()) + assert.Equal(t, "Measures the duration of the HTTP check.", ms.At(i).Description()) + assert.Equal(t, "ms", ms.At(i).Unit()) + dp := ms.At(i).Gauge().DataPoints().At(0) + assert.Equal(t, start, dp.StartTimestamp()) + assert.Equal(t, ts, dp.Timestamp()) + assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) + assert.Equal(t, int64(1), dp.IntValue()) + attrVal, ok := dp.Attributes().Get("http.url") + assert.True(t, ok) + assert.EqualValues(t, "http.url-val", attrVal.Str()) + case "httpcheck.error": + assert.False(t, validatedMetrics["httpcheck.error"], "Found a duplicate in the metrics slice: httpcheck.error") + validatedMetrics["httpcheck.error"] = true + assert.Equal(t, pmetric.MetricTypeSum, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, "Records errors occurring during HTTP check.", ms.At(i).Description()) + assert.Equal(t, "{error}", ms.At(i).Unit()) + assert.Equal(t, false, ms.At(i).Sum().IsMonotonic()) + assert.Equal(t, pmetric.AggregationTemporalityCumulative, ms.At(i).Sum().AggregationTemporality()) + dp := ms.At(i).Sum().DataPoints().At(0) + assert.Equal(t, start, dp.StartTimestamp()) + assert.Equal(t, ts, dp.Timestamp()) + assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) + assert.Equal(t, int64(1), dp.IntValue()) + attrVal, ok := dp.Attributes().Get("http.url") + assert.True(t, ok) + assert.EqualValues(t, "http.url-val", attrVal.Str()) + attrVal, ok = dp.Attributes().Get("error.message") + assert.True(t, ok) + assert.EqualValues(t, "error.message-val", attrVal.Str()) + case "httpcheck.status": + assert.False(t, validatedMetrics["httpcheck.status"], "Found a duplicate in the metrics slice: httpcheck.status") + validatedMetrics["httpcheck.status"] = true + assert.Equal(t, pmetric.MetricTypeSum, ms.At(i).Type()) + assert.Equal(t, 1, ms.At(i).Sum().DataPoints().Len()) + assert.Equal(t, "1 if the check resulted in status_code matching the status_class, otherwise 0.", ms.At(i).Description()) + assert.Equal(t, "1", ms.At(i).Unit()) + assert.Equal(t, false, ms.At(i).Sum().IsMonotonic()) + assert.Equal(t, pmetric.AggregationTemporalityCumulative, ms.At(i).Sum().AggregationTemporality()) + dp := ms.At(i).Sum().DataPoints().At(0) + assert.Equal(t, start, dp.StartTimestamp()) + assert.Equal(t, ts, dp.Timestamp()) + assert.Equal(t, pmetric.NumberDataPointValueTypeInt, dp.ValueType()) + assert.Equal(t, int64(1), dp.IntValue()) + attrVal, ok := dp.Attributes().Get("http.url") + assert.True(t, ok) + assert.EqualValues(t, "http.url-val", attrVal.Str()) + attrVal, ok = dp.Attributes().Get("http.status_code") + assert.True(t, ok) + assert.EqualValues(t, 16, attrVal.Int()) + attrVal, ok = dp.Attributes().Get("http.method") + assert.True(t, ok) + assert.EqualValues(t, "http.method-val", attrVal.Str()) + attrVal, ok = dp.Attributes().Get("http.status_class") + assert.True(t, ok) + assert.EqualValues(t, "http.status_class-val", attrVal.Str()) + } + } + }) + } +} diff --git a/collector/components/httpcheckreceiver/internal/metadata/generated_status.go b/collector/components/httpcheckreceiver/internal/metadata/generated_status.go new file mode 100644 index 0000000..100b2cb --- /dev/null +++ b/collector/components/httpcheckreceiver/internal/metadata/generated_status.go @@ -0,0 +1,26 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package metadata + +import ( + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/trace" +) + +var ( + Type = component.MustNewType("httpcheck") +) + +const ( + MetricsStability = component.StabilityLevelDevelopment + LogsStability = component.StabilityLevelDevelopment +) + +func Meter(settings component.TelemetrySettings) metric.Meter { + return settings.MeterProvider.Meter("otelcol/httpcheckreceiver") +} + +func Tracer(settings component.TelemetrySettings) trace.Tracer { + return settings.TracerProvider.Tracer("otelcol/httpcheckreceiver") +} diff --git a/collector/components/httpcheckreceiver/internal/metadata/package_test.go b/collector/components/httpcheckreceiver/internal/metadata/package_test.go new file mode 100644 index 0000000..1aba5ec --- /dev/null +++ b/collector/components/httpcheckreceiver/internal/metadata/package_test.go @@ -0,0 +1,14 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package metadata + +import ( + "testing" + + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + goleak.VerifyTestMain(m) +} diff --git a/collector/components/httpcheckreceiver/internal/metadata/testdata/config.yaml b/collector/components/httpcheckreceiver/internal/metadata/testdata/config.yaml new file mode 100644 index 0000000..11c46e1 --- /dev/null +++ b/collector/components/httpcheckreceiver/internal/metadata/testdata/config.yaml @@ -0,0 +1,17 @@ +default: +all_set: + metrics: + httpcheck.duration: + enabled: true + httpcheck.error: + enabled: true + httpcheck.status: + enabled: true +none_set: + metrics: + httpcheck.duration: + enabled: false + httpcheck.error: + enabled: false + httpcheck.status: + enabled: false diff --git a/collector/components/httpcheckreceiver/metadata.yaml b/collector/components/httpcheckreceiver/metadata.yaml new file mode 100644 index 0000000..a1b3510 --- /dev/null +++ b/collector/components/httpcheckreceiver/metadata.yaml @@ -0,0 +1,57 @@ +type: httpcheck +scope_name: otelcol/httpcheckreceiver + +status: + class: receiver + stability: + development: [metrics, logs] + distributions: [contrib] + warnings: [] + codeowners: + active: [codeboten] + +resource_attributes: + +attributes: + http.url: + description: Full HTTP request URL. + type: string + http.status_code: + description: HTTP response status code + type: int + http.method: + description: HTTP request method + type: string + http.status_class: + description: HTTP response status class + type: string + error.message: + description: Error message recorded during check + type: string + +metrics: + httpcheck.status: + description: 1 if the check resulted in status_code matching the status_class, otherwise 0. + enabled: true + sum: + value_type: int + aggregation_temporality: cumulative + monotonic: false + unit: 1 + attributes: [http.url, http.status_code, http.method, http.status_class] + httpcheck.duration: + description: Measures the duration of the HTTP check. + enabled: true + gauge: + value_type: int + unit: ms + attributes: [http.url] + httpcheck.error: + description: Records errors occurring during HTTP check. + enabled: true + sum: + value_type: int + aggregation_temporality: cumulative + monotonic: false + unit: "{error}" + attributes: [http.url, error.message] diff --git a/collector/components/httpcheckreceiver/scraper.go b/collector/components/httpcheckreceiver/scraper.go new file mode 100644 index 0000000..a3e1459 --- /dev/null +++ b/collector/components/httpcheckreceiver/scraper.go @@ -0,0 +1,156 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package httpcheckreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/httpcheckreceiver" + +import ( + "context" + "errors" + "io" + "net/http" + "sync" + "time" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/consumer" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/plog" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/receiver" + "go.uber.org/multierr" + "go.uber.org/zap" + + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/httpcheckreceiver/internal/metadata" +) + +var ( + errClientNotInit = errors.New("client not initialized") + httpResponseClasses = map[string]int{"1xx": 1, "2xx": 2, "3xx": 3, "4xx": 4, "5xx": 5} +) + +type httpcheckScraper struct { + clients []*http.Client + cfg *Config + settings component.TelemetrySettings + version string + mb *metadata.MetricsBuilder + logs consumer.Logs +} + +// start starts the scraper by creating a new HTTP Client on the scraper +func (h *httpcheckScraper) start(ctx context.Context, host component.Host) (err error) { + for _, target := range h.cfg.Targets { + client, clentErr := target.ToClient(ctx, host, h.settings) + if clentErr != nil { + err = multierr.Append(err, clentErr) + } + h.clients = append(h.clients, client) + } + return +} + +// scrape connects to the endpoint and produces metrics based on the response +func (h *httpcheckScraper) scrape(ctx context.Context) (pmetric.Metrics, error) { + if h.clients == nil || len(h.clients) == 0 { + return pmetric.NewMetrics(), errClientNotInit + } + + var wg sync.WaitGroup + wg.Add(len(h.clients)) + var mux sync.Mutex + + for idx, client := range h.clients { + go func(targetClient *http.Client, targetIndex int) { + defer wg.Done() + + now := pcommon.NewTimestampFromTime(time.Now()) + + req, err := http.NewRequestWithContext(ctx, h.cfg.Targets[targetIndex].Method, h.cfg.Targets[targetIndex].Endpoint, http.NoBody) + if err != nil { + h.settings.Logger.Error("failed to create request", zap.Error(err)) + return + } + + start := time.Now() + resp, err := targetClient.Do(req) + duration := time.Since(start) + mux.Lock() + h.mb.RecordHttpcheckDurationDataPoint(now, time.Since(start).Milliseconds(), h.cfg.Targets[targetIndex].Endpoint) + + statusCode := 0 + if err != nil { + h.mb.RecordHttpcheckErrorDataPoint(now, int64(1), h.cfg.Targets[targetIndex].Endpoint, err.Error()) + } else { + statusCode = resp.StatusCode + } + + for class, intVal := range httpResponseClasses { + if statusCode/100 == intVal { + h.mb.RecordHttpcheckStatusDataPoint(now, int64(1), h.cfg.Targets[targetIndex].Endpoint, int64(statusCode), req.Method, class) + } else { + h.mb.RecordHttpcheckStatusDataPoint(now, int64(0), h.cfg.Targets[targetIndex].Endpoint, int64(statusCode), req.Method, class) + } + } + mux.Unlock() + + // Report logging *outside* the lock. + if statusCode != 0 { + err = h.logResponse(ctx, h.cfg.Targets[targetIndex].Endpoint, resp, now, duration) + if err != nil { + h.settings.Logger.Error("failed to log response", zap.Error(err)) + } + + } + }(client, idx) + } + + wg.Wait() + + return h.mb.Emit(), nil +} + +func (h *httpcheckScraper) logResponse( + ctx context.Context, + endpoint string, + resp *http.Response, + timestamp pcommon.Timestamp, + elapsed time.Duration) error { + + // Return if logs reporting was NOT enabled. + if h.logs == nil { + return nil + } + + defer resp.Body.Close() + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + + out := plog.NewLogs() + logs := out.ResourceLogs() + rls := logs.AppendEmpty() + + ills := rls.ScopeLogs().AppendEmpty() + ills.Scope().SetName("otelcol/httpcheckreceiver") + ills.Scope().SetVersion(h.version) + + lr := ills.LogRecords().AppendEmpty() + lr.SetTimestamp(timestamp) + lr.Body().SetStr(string(bodyBytes)) + attrs := lr.Attributes() + attrs.PutStr("http.url", endpoint) + attrs.PutInt("http.response.status_code", int64(resp.StatusCode)) + attrs.PutInt("http.client.request.duration", int64(elapsed)) + + return h.logs.ConsumeLogs(ctx, out) +} + +func newScraper(conf *Config, settings receiver.CreateSettings) *httpcheckScraper { + return &httpcheckScraper{ + cfg: conf, + settings: settings.TelemetrySettings, + version: settings.BuildInfo.Version, + mb: metadata.NewMetricsBuilder(conf.MetricsBuilderConfig, settings), + } +} diff --git a/collector/components/httpcheckreceiver/scraper_test.go b/collector/components/httpcheckreceiver/scraper_test.go new file mode 100644 index 0000000..7b54738 --- /dev/null +++ b/collector/components/httpcheckreceiver/scraper_test.go @@ -0,0 +1,235 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package httpcheckreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/httpcheckreceiver" + +import ( + "context" + "net/http" + "net/http/httptest" + "path/filepath" + "testing" + + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component/componenttest" + "go.opentelemetry.io/collector/config/confighttp" + "go.opentelemetry.io/collector/config/configtls" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/receiver/receivertest" + + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest/pmetrictest" +) + +func newMockServer(t *testing.T, responseCode int) *httptest.Server { + return httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) { + rw.WriteHeader(responseCode) + // This could be expanded if the checks for the server include + // parsing the response content + _, err := rw.Write([]byte(``)) + require.NoError(t, err) + })) +} + +func TestScraperStart(t *testing.T) { + testcases := []struct { + desc string + scraper *httpcheckScraper + expectError bool + }{ + { + desc: "Bad Config", + scraper: &httpcheckScraper{ + cfg: &Config{ + Targets: []*targetConfig{ + { + ClientConfig: confighttp.ClientConfig{ + Endpoint: "http://example.com", + TLSSetting: configtls.ClientConfig{ + Config: configtls.Config{ + CAFile: "/non/existent", + }, + }, + }, + }, + }, + }, + settings: componenttest.NewNopTelemetrySettings(), + }, + expectError: true, + }, + { + desc: "Valid Config", + scraper: &httpcheckScraper{ + cfg: &Config{ + Targets: []*targetConfig{ + { + ClientConfig: confighttp.ClientConfig{ + TLSSetting: configtls.ClientConfig{}, + Endpoint: "http://example.com", + }, + }, + }, + }, + settings: componenttest.NewNopTelemetrySettings(), + }, + expectError: false, + }, + } + + for _, tc := range testcases { + t.Run(tc.desc, func(t *testing.T) { + err := tc.scraper.start(context.Background(), componenttest.NewNopHost()) + if tc.expectError { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestScaperScrape(t *testing.T) { + testCases := []struct { + desc string + expectedResponse int + expectedMetricGen func(t *testing.T) pmetric.Metrics + expectedErr error + endpoint string + compareOptions []pmetrictest.CompareMetricsOption + }{ + { + desc: "Successful Collection", + expectedResponse: 200, + expectedMetricGen: func(t *testing.T) pmetric.Metrics { + goldenPath := filepath.Join("testdata", "expected_metrics", "metrics_golden.yaml") + expectedMetrics, err := golden.ReadMetrics(goldenPath) + require.NoError(t, err) + return expectedMetrics + }, + expectedErr: nil, + compareOptions: []pmetrictest.CompareMetricsOption{ + pmetrictest.IgnoreMetricAttributeValue("http.url"), + pmetrictest.IgnoreMetricValues("httpcheck.duration"), + pmetrictest.IgnoreMetricDataPointsOrder(), + pmetrictest.IgnoreStartTimestamp(), + pmetrictest.IgnoreTimestamp(), + }, + }, + { + desc: "Endpoint returning 404", + expectedResponse: 404, + expectedMetricGen: func(t *testing.T) pmetric.Metrics { + goldenPath := filepath.Join("testdata", "expected_metrics", "endpoint_404.yaml") + expectedMetrics, err := golden.ReadMetrics(goldenPath) + require.NoError(t, err) + return expectedMetrics + }, + expectedErr: nil, + compareOptions: []pmetrictest.CompareMetricsOption{ + pmetrictest.IgnoreMetricAttributeValue("http.url"), + pmetrictest.IgnoreMetricValues("httpcheck.duration"), + pmetrictest.IgnoreMetricDataPointsOrder(), + pmetrictest.IgnoreStartTimestamp(), + pmetrictest.IgnoreTimestamp(), + }, + }, + { + desc: "Invalid endpoint", + endpoint: "http://invalid-endpoint", + expectedMetricGen: func(t *testing.T) pmetric.Metrics { + goldenPath := filepath.Join("testdata", "expected_metrics", "invalid_endpoint.yaml") + expectedMetrics, err := golden.ReadMetrics(goldenPath) + require.NoError(t, err) + return expectedMetrics + }, + expectedErr: nil, + compareOptions: []pmetrictest.CompareMetricsOption{ + pmetrictest.IgnoreMetricValues("httpcheck.duration"), + pmetrictest.IgnoreMetricAttributeValue("error.message"), + pmetrictest.IgnoreMetricDataPointsOrder(), + pmetrictest.IgnoreStartTimestamp(), + pmetrictest.IgnoreTimestamp(), + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + cfg := createDefaultConfig().(*Config) + if len(tc.endpoint) > 0 { + cfg.Targets = []*targetConfig{{ + ClientConfig: confighttp.ClientConfig{ + Endpoint: tc.endpoint, + }}, + } + } else { + ms := newMockServer(t, tc.expectedResponse) + defer ms.Close() + cfg.Targets = []*targetConfig{{ + ClientConfig: confighttp.ClientConfig{ + Endpoint: ms.URL, + }}, + } + } + scraper := newScraper(cfg, receivertest.NewNopCreateSettings()) + require.NoError(t, scraper.start(context.Background(), componenttest.NewNopHost())) + + actualMetrics, err := scraper.scrape(context.Background()) + if tc.expectedErr == nil { + require.NoError(t, err) + } else { + require.EqualError(t, err, tc.expectedErr.Error()) + } + + expectedMetrics := tc.expectedMetricGen(t) + + require.NoError(t, pmetrictest.CompareMetrics(expectedMetrics, actualMetrics, tc.compareOptions...)) + }) + } +} + +func TestNilClient(t *testing.T) { + scraper := newScraper(createDefaultConfig().(*Config), receivertest.NewNopCreateSettings()) + actualMetrics, err := scraper.scrape(context.Background()) + require.EqualError(t, err, errClientNotInit.Error()) + require.NoError(t, pmetrictest.CompareMetrics(pmetric.NewMetrics(), actualMetrics)) + +} + +func TestScraperMultipleTargets(t *testing.T) { + cfg := createDefaultConfig().(*Config) + ms1 := newMockServer(t, 200) + defer ms1.Close() + ms2 := newMockServer(t, 404) + defer ms2.Close() + + cfg.Targets = append(cfg.Targets, &targetConfig{ + ClientConfig: confighttp.ClientConfig{ + Endpoint: ms1.URL, + }, + }) + cfg.Targets = append(cfg.Targets, &targetConfig{ + ClientConfig: confighttp.ClientConfig{ + Endpoint: ms2.URL, + }, + }) + + scraper := newScraper(cfg, receivertest.NewNopCreateSettings()) + require.NoError(t, scraper.start(context.Background(), componenttest.NewNopHost())) + + actualMetrics, err := scraper.scrape(context.Background()) + require.NoError(t, err) + + goldenPath := filepath.Join("testdata", "expected_metrics", "multiple_targets.yaml") + expectedMetrics, err := golden.ReadMetrics(goldenPath) + require.NoError(t, err) + + require.NoError(t, pmetrictest.CompareMetrics(expectedMetrics, actualMetrics, + pmetrictest.IgnoreMetricAttributeValue("http.url"), + pmetrictest.IgnoreMetricValues("httpcheck.duration"), + pmetrictest.IgnoreMetricDataPointsOrder(), + pmetrictest.IgnoreStartTimestamp(), + pmetrictest.IgnoreTimestamp(), + )) +} diff --git a/collector/components/httpcheckreceiver/testdata/expected_metrics/endpoint_404.yaml b/collector/components/httpcheckreceiver/testdata/expected_metrics/endpoint_404.yaml new file mode 100644 index 0000000..986749e --- /dev/null +++ b/collector/components/httpcheckreceiver/testdata/expected_metrics/endpoint_404.yaml @@ -0,0 +1,103 @@ +resourceMetrics: + - resource: {} + scopeMetrics: + - metrics: + - description: Measures the duration of the HTTP check. + gauge: + dataPoints: + - asInt: "0" + attributes: + - key: http.url + value: + stringValue: http://127.0.0.1:8000 + name: httpcheck.duration + unit: ms + - description: 1 if the check resulted in status_code matching the status_class, otherwise 0. + name: httpcheck.status + sum: + aggregationTemporality: 2 + dataPoints: + - asInt: "0" + attributes: + - key: http.method + value: + stringValue: GET + - key: http.status_class + value: + stringValue: 1xx + - key: http.status_code + value: + intValue: "404" + - key: http.url + value: + stringValue: http://invalid-endpoint + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: http.method + value: + stringValue: GET + - key: http.status_class + value: + stringValue: 2xx + - key: http.status_code + value: + intValue: "404" + - key: http.url + value: + stringValue: http://127.0.0.1:8000 + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: http.method + value: + stringValue: GET + - key: http.status_class + value: + stringValue: 3xx + - key: http.status_code + value: + intValue: "404" + - key: http.url + value: + stringValue: http://invalid-endpoint + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "1" + attributes: + - key: http.method + value: + stringValue: GET + - key: http.status_class + value: + stringValue: 4xx + - key: http.status_code + value: + intValue: "404" + - key: http.url + value: + stringValue: http://invalid-endpoint + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: http.method + value: + stringValue: GET + - key: http.status_class + value: + stringValue: 5xx + - key: http.status_code + value: + intValue: "404" + - key: http.url + value: + stringValue: http://127.0.0.1:8000 + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + unit: "1" + scope: + name: otelcol/httpcheckreceiver + version: latest diff --git a/collector/components/httpcheckreceiver/testdata/expected_metrics/invalid_endpoint.yaml b/collector/components/httpcheckreceiver/testdata/expected_metrics/invalid_endpoint.yaml new file mode 100644 index 0000000..7c55883 --- /dev/null +++ b/collector/components/httpcheckreceiver/testdata/expected_metrics/invalid_endpoint.yaml @@ -0,0 +1,117 @@ +resourceMetrics: + - resource: {} + scopeMetrics: + - metrics: + - description: Measures the duration of the HTTP check. + gauge: + dataPoints: + - asInt: "0" + attributes: + - key: http.url + value: + stringValue: http://invalid-endpoint + name: httpcheck.duration + unit: ms + - description: Records errors occurring during HTTP check. + name: httpcheck.error + sum: + aggregationTemporality: 2 + dataPoints: + - asInt: "1" + attributes: + - key: error.message + value: + stringValue: 'Get "http://invalid-endpoint": dial tcp: lookup invalid-endpoint: no such host' + - key: http.url + value: + stringValue: http://invalid-endpoint + unit: '{error}' + - description: 1 if the check resulted in status_code matching the status_class, otherwise 0. + name: httpcheck.status + sum: + aggregationTemporality: 2 + dataPoints: + - asInt: "0" + attributes: + - key: http.method + value: + stringValue: GET + - key: http.status_class + value: + stringValue: 1xx + - key: http.status_code + value: + intValue: "0" + - key: http.url + value: + stringValue: http://invalid-endpoint + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: http.method + value: + stringValue: GET + - key: http.status_class + value: + stringValue: 2xx + - key: http.status_code + value: + intValue: "0" + - key: http.url + value: + stringValue: http://invalid-endpoint + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: http.method + value: + stringValue: GET + - key: http.status_class + value: + stringValue: 3xx + - key: http.status_code + value: + intValue: "0" + - key: http.url + value: + stringValue: http://invalid-endpoint + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: http.method + value: + stringValue: GET + - key: http.status_class + value: + stringValue: 4xx + - key: http.status_code + value: + intValue: "0" + - key: http.url + value: + stringValue: http://invalid-endpoint + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: http.method + value: + stringValue: GET + - key: http.status_class + value: + stringValue: 5xx + - key: http.status_code + value: + intValue: "0" + - key: http.url + value: + stringValue: http://invalid-endpoint + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + unit: "1" + scope: + name: otelcol/httpcheckreceiver + version: latest diff --git a/collector/components/httpcheckreceiver/testdata/expected_metrics/metrics_golden.yaml b/collector/components/httpcheckreceiver/testdata/expected_metrics/metrics_golden.yaml new file mode 100644 index 0000000..ea9a679 --- /dev/null +++ b/collector/components/httpcheckreceiver/testdata/expected_metrics/metrics_golden.yaml @@ -0,0 +1,103 @@ +resourceMetrics: + - resource: {} + scopeMetrics: + - metrics: + - description: Measures the duration of the HTTP check. + gauge: + dataPoints: + - asInt: "0" + attributes: + - key: http.url + value: + stringValue: http://127.0.0.1:8000 + name: httpcheck.duration + unit: ms + - description: 1 if the check resulted in status_code matching the status_class, otherwise 0. + name: httpcheck.status + sum: + aggregationTemporality: 2 + dataPoints: + - asInt: "0" + attributes: + - key: http.method + value: + stringValue: GET + - key: http.status_class + value: + stringValue: 1xx + - key: http.status_code + value: + intValue: "200" + - key: http.url + value: + stringValue: http://127.0.0.1:8000 + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "1" + attributes: + - key: http.method + value: + stringValue: GET + - key: http.status_class + value: + stringValue: 2xx + - key: http.status_code + value: + intValue: "200" + - key: http.url + value: + stringValue: http://127.0.0.1:8000 + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: http.method + value: + stringValue: GET + - key: http.status_class + value: + stringValue: 3xx + - key: http.status_code + value: + intValue: "200" + - key: http.url + value: + stringValue: http://127.0.0.1:8000 + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: http.method + value: + stringValue: GET + - key: http.status_class + value: + stringValue: 4xx + - key: http.status_code + value: + intValue: "200" + - key: http.url + value: + stringValue: http://127.0.0.1:8000 + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: http.method + value: + stringValue: GET + - key: http.status_class + value: + stringValue: 5xx + - key: http.status_code + value: + intValue: "200" + - key: http.url + value: + stringValue: http://127.0.0.1:8000 + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + unit: "1" + scope: + name: otelcol/httpcheckreceiver + version: latest diff --git a/collector/components/httpcheckreceiver/testdata/expected_metrics/multiple_targets.yaml b/collector/components/httpcheckreceiver/testdata/expected_metrics/multiple_targets.yaml new file mode 100644 index 0000000..26ad52b --- /dev/null +++ b/collector/components/httpcheckreceiver/testdata/expected_metrics/multiple_targets.yaml @@ -0,0 +1,188 @@ +resourceMetrics: + - resource: {} + scopeMetrics: + - metrics: + - description: Measures the duration of the HTTP check. + gauge: + dataPoints: + - asInt: "0" + attributes: + - key: http.url + value: + stringValue: http://127.0.0.1:8000 + - asInt: "0" + attributes: + - key: http.url + value: + stringValue: http://127.0.0.1:8000 + name: httpcheck.duration + unit: ms + - description: 1 if the check resulted in status_code matching the status_class, otherwise 0. + name: httpcheck.status + sum: + aggregationTemporality: 2 + dataPoints: + - asInt: "0" + attributes: + - key: http.method + value: + stringValue: GET + - key: http.status_class + value: + stringValue: 1xx + - key: http.status_code + value: + intValue: "200" + - key: http.url + value: + stringValue: http://127.0.0.1:8000 + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: http.method + value: + stringValue: GET + - key: http.status_class + value: + stringValue: 1xx + - key: http.status_code + value: + intValue: "404" + - key: http.url + value: + stringValue: http://127.0.0.1:8000 + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "1" + attributes: + - key: http.method + value: + stringValue: GET + - key: http.status_class + value: + stringValue: 2xx + - key: http.status_code + value: + intValue: "200" + - key: http.url + value: + stringValue: http://127.0.0.1:8000 + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: http.method + value: + stringValue: GET + - key: http.status_class + value: + stringValue: 2xx + - key: http.status_code + value: + intValue: "404" + - key: http.url + value: + stringValue: http://127.0.0.1:8000 + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: http.method + value: + stringValue: GET + - key: http.status_class + value: + stringValue: 3xx + - key: http.status_code + value: + intValue: "200" + - key: http.url + value: + stringValue: http://127.0.0.1:8000 + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: http.method + value: + stringValue: GET + - key: http.status_class + value: + stringValue: 3xx + - key: http.status_code + value: + intValue: "404" + - key: http.url + value: + stringValue: http://127.0.0.1:8000 + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: http.method + value: + stringValue: GET + - key: http.status_class + value: + stringValue: 4xx + - key: http.status_code + value: + intValue: "200" + - key: http.url + value: + stringValue: http://127.0.0.1:8000 + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "1" + attributes: + - key: http.method + value: + stringValue: GET + - key: http.status_class + value: + stringValue: 4xx + - key: http.status_code + value: + intValue: "404" + - key: http.url + value: + stringValue: http://127.0.0.1:8000 + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: http.method + value: + stringValue: GET + - key: http.status_class + value: + stringValue: 5xx + - key: http.status_code + value: + intValue: "200" + - key: http.url + value: + stringValue: http://127.0.0.1:8000 + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: http.method + value: + stringValue: GET + - key: http.status_class + value: + stringValue: 5xx + - key: http.status_code + value: + intValue: "404" + - key: http.url + value: + stringValue: http://127.0.0.1:8000 + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + unit: "1" + scope: + name: otelcol/httpcheckreceiver + version: latest diff --git a/collector/otelcol-builder.yaml b/collector/otelcol-builder.yaml index 9db9706..33cffbd 100644 --- a/collector/otelcol-builder.yaml +++ b/collector/otelcol-builder.yaml @@ -51,8 +51,9 @@ receivers: github.com/open-telemetry/opentelemetry-collector-contrib/receiver/azuremonitorreceiver v0.97.0 - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/receiver/azureeventhubreceiver v0.97.0 - - gomod: - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/httpcheckreceiver v0.97.0 + - gomod: "github.com/lightstep/sn-collector/collector/httpcheckreceiver v0.0.1" + name: "httpcheckreceiver" + path: "./components/httpcheckreceiver" - gomod: github.com/open-telemetry/opentelemetry-collector-contrib/receiver/k8seventsreceiver v0.97.0 - gomod: From cf27ffe03234b7ed2ad7cd281e1368958741a33a Mon Sep 17 00:00:00 2001 From: Carlos Alberto Cortez Date: Tue, 30 Apr 2024 18:47:18 +0200 Subject: [PATCH 2/3] Send the logs all at once. --- .../httpcheckreceiver/logsbuilder.go | 62 +++++++++++++++++++ .../components/httpcheckreceiver/scraper.go | 46 +++++--------- 2 files changed, 77 insertions(+), 31 deletions(-) create mode 100644 collector/components/httpcheckreceiver/logsbuilder.go diff --git a/collector/components/httpcheckreceiver/logsbuilder.go b/collector/components/httpcheckreceiver/logsbuilder.go new file mode 100644 index 0000000..29ac683 --- /dev/null +++ b/collector/components/httpcheckreceiver/logsbuilder.go @@ -0,0 +1,62 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package httpcheckreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/httpcheckreceiver" + +import ( + "time" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/plog" + "go.opentelemetry.io/collector/receiver" +) + +type LogsBuilder struct { + logBuffer plog.Logs + logRecords plog.LogRecordSlice + buildInfo component.BuildInfo +} + +func NewLogsBuilder(settings receiver.CreateSettings) *LogsBuilder { + lb := &LogsBuilder{ + buildInfo: settings.BuildInfo, + } + lb.reset() + return lb +} + +func (lb *LogsBuilder) reset() { + out := plog.NewLogs() + logs := out.ResourceLogs() + rls := logs.AppendEmpty() + + ills := rls.ScopeLogs().AppendEmpty() + ills.Scope().SetName("otelcol/httpcheckreceiver") + ills.Scope().SetVersion(lb.buildInfo.Version) + + lb.logBuffer = out + lb.logRecords = ills.LogRecords() +} + +func (lb *LogsBuilder) RecordResponse( + endpoint string, + body string, + statusCode int, + timestamp pcommon.Timestamp, + elapsed time.Duration) { + + lr := lb.logRecords.AppendEmpty() + lr.SetTimestamp(timestamp) + lr.Body().SetStr(body) + attrs := lr.Attributes() + attrs.PutStr("http.url", endpoint) + attrs.PutInt("http.status_code", int64(statusCode)) + attrs.PutInt("http.client.request.duration", elapsed.Milliseconds()) +} + +func (lb *LogsBuilder) Emit() plog.Logs { + logs := lb.logBuffer + lb.reset() + return logs +} diff --git a/collector/components/httpcheckreceiver/scraper.go b/collector/components/httpcheckreceiver/scraper.go index a3e1459..9223970 100644 --- a/collector/components/httpcheckreceiver/scraper.go +++ b/collector/components/httpcheckreceiver/scraper.go @@ -14,7 +14,6 @@ import ( "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/consumer" "go.opentelemetry.io/collector/pdata/pcommon" - "go.opentelemetry.io/collector/pdata/plog" "go.opentelemetry.io/collector/pdata/pmetric" "go.opentelemetry.io/collector/receiver" "go.uber.org/multierr" @@ -32,8 +31,8 @@ type httpcheckScraper struct { clients []*http.Client cfg *Config settings component.TelemetrySettings - version string mb *metadata.MetricsBuilder + lb *LogsBuilder logs consumer.Logs } @@ -75,7 +74,7 @@ func (h *httpcheckScraper) scrape(ctx context.Context) (pmetric.Metrics, error) resp, err := targetClient.Do(req) duration := time.Since(start) mux.Lock() - h.mb.RecordHttpcheckDurationDataPoint(now, time.Since(start).Milliseconds(), h.cfg.Targets[targetIndex].Endpoint) + h.mb.RecordHttpcheckDurationDataPoint(now, duration.Milliseconds(), h.cfg.Targets[targetIndex].Endpoint) statusCode := 0 if err != nil { @@ -91,66 +90,51 @@ func (h *httpcheckScraper) scrape(ctx context.Context) (pmetric.Metrics, error) h.mb.RecordHttpcheckStatusDataPoint(now, int64(0), h.cfg.Targets[targetIndex].Endpoint, int64(statusCode), req.Method, class) } } - mux.Unlock() - // Report logging *outside* the lock. - if statusCode != 0 { + if h.logs != nil && statusCode != 0 { err = h.logResponse(ctx, h.cfg.Targets[targetIndex].Endpoint, resp, now, duration) if err != nil { h.settings.Logger.Error("failed to log response", zap.Error(err)) } - } + + mux.Unlock() }(client, idx) } wg.Wait() + if h.logs != nil { + err := h.logs.ConsumeLogs(ctx, h.lb.Emit()) + if err != nil { + h.settings.Logger.Error("failed to consume logs", zap.Error(err)) + } + } return h.mb.Emit(), nil } func (h *httpcheckScraper) logResponse( - ctx context.Context, + _ context.Context, endpoint string, resp *http.Response, timestamp pcommon.Timestamp, elapsed time.Duration) error { - // Return if logs reporting was NOT enabled. - if h.logs == nil { - return nil - } - defer resp.Body.Close() bodyBytes, err := io.ReadAll(resp.Body) if err != nil { return err } - out := plog.NewLogs() - logs := out.ResourceLogs() - rls := logs.AppendEmpty() - - ills := rls.ScopeLogs().AppendEmpty() - ills.Scope().SetName("otelcol/httpcheckreceiver") - ills.Scope().SetVersion(h.version) - - lr := ills.LogRecords().AppendEmpty() - lr.SetTimestamp(timestamp) - lr.Body().SetStr(string(bodyBytes)) - attrs := lr.Attributes() - attrs.PutStr("http.url", endpoint) - attrs.PutInt("http.response.status_code", int64(resp.StatusCode)) - attrs.PutInt("http.client.request.duration", int64(elapsed)) - - return h.logs.ConsumeLogs(ctx, out) + h.lb.RecordResponse(endpoint, string(bodyBytes), resp.StatusCode, timestamp, elapsed) + return nil } func newScraper(conf *Config, settings receiver.CreateSettings) *httpcheckScraper { return &httpcheckScraper{ cfg: conf, settings: settings.TelemetrySettings, - version: settings.BuildInfo.Version, mb: metadata.NewMetricsBuilder(conf.MetricsBuilderConfig, settings), + lb: NewLogsBuilder(settings), } } From 91debaa82e0f8ff60b0c01f69ccad057429f3446 Mon Sep 17 00:00:00 2001 From: Carlos Alberto Cortez Date: Wed, 1 May 2024 21:08:24 +0200 Subject: [PATCH 3/3] Re-fix the package references. --- collector/components/httpcheckreceiver/config.go | 2 +- collector/components/httpcheckreceiver/factory.go | 2 +- collector/components/httpcheckreceiver/scraper.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/collector/components/httpcheckreceiver/config.go b/collector/components/httpcheckreceiver/config.go index 61019ac..99dbfc9 100644 --- a/collector/components/httpcheckreceiver/config.go +++ b/collector/components/httpcheckreceiver/config.go @@ -12,7 +12,7 @@ import ( "go.opentelemetry.io/collector/receiver/scraperhelper" "go.uber.org/multierr" - "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/httpcheckreceiver/internal/metadata" + "github.com/lightstep/sn-collector/collector/httpcheckreceiver/internal/metadata" ) // Predefined error responses for configuration validation failures diff --git a/collector/components/httpcheckreceiver/factory.go b/collector/components/httpcheckreceiver/factory.go index e743429..78d9859 100644 --- a/collector/components/httpcheckreceiver/factory.go +++ b/collector/components/httpcheckreceiver/factory.go @@ -13,7 +13,7 @@ import ( "go.opentelemetry.io/collector/receiver" "go.opentelemetry.io/collector/receiver/scraperhelper" - "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/httpcheckreceiver/internal/metadata" + "github.com/lightstep/sn-collector/collector/httpcheckreceiver/internal/metadata" ) var errConfigNotHTTPCheck = errors.New("config was not a HTTP check receiver config") diff --git a/collector/components/httpcheckreceiver/scraper.go b/collector/components/httpcheckreceiver/scraper.go index 9223970..fb33799 100644 --- a/collector/components/httpcheckreceiver/scraper.go +++ b/collector/components/httpcheckreceiver/scraper.go @@ -19,7 +19,7 @@ import ( "go.uber.org/multierr" "go.uber.org/zap" - "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/httpcheckreceiver/internal/metadata" + "github.com/lightstep/sn-collector/collector/httpcheckreceiver/internal/metadata" ) var (