From 778d8f755f8316cb6f66a6ea542d607bc2431372 Mon Sep 17 00:00:00 2001 From: Nicolas <69248573+niwoerner@users.noreply.github.com> Date: Mon, 13 Jan 2025 11:43:45 +0100 Subject: [PATCH 01/13] [receiver/gitlab] add tracing via webhook skeleton (#36838) #### Description This PR adds the structure and trace skeleton for a new and already accepted Gitlabreceiver. (thanks @atoulme for sponsoring this!) The Gitlabreceiver aligns very closely with the Githubreceiver and this PR mostly mirrors the change from this PR: https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/36632 I'm working together with @adrielp on building out the Gitlabreceiver. More PRs to introduce metrics and actual tracing functionality are about to follow with subsequent PRs. #### Link to tracking issue https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/35207 #### Testing Added basic tests and built the component to test that the health check endpoint, when tracing is enabled, operates correctly. #### Documentation Docs how to configure the Gitlabreceiver via webhooks have been added. While the Gitlabreceiver can be configured after this PR, it will not actually do anything since it is under development and just the skeleton PR. --- .chloggen/gl-receiver-skeleton-traces.yaml | 30 ++ .github/CODEOWNERS | 1 + .github/ISSUE_TEMPLATE/bug_report.yaml | 1 + .github/ISSUE_TEMPLATE/feature_request.yaml | 1 + .github/ISSUE_TEMPLATE/other.yaml | 1 + .github/ISSUE_TEMPLATE/unmaintained.yaml | 1 + receiver/gitlabreceiver/Makefile | 1 + receiver/gitlabreceiver/README.md | 55 +++ receiver/gitlabreceiver/config.go | 136 ++++++++ receiver/gitlabreceiver/config_test.go | 123 +++++++ receiver/gitlabreceiver/doc.go | 6 + receiver/gitlabreceiver/factory.go | 31 ++ receiver/gitlabreceiver/factory_test.go | 23 ++ .../generated_component_test.go | 69 ++++ .../gitlabreceiver/generated_package_test.go | 13 + receiver/gitlabreceiver/go.mod | 138 ++++++++ receiver/gitlabreceiver/go.sum | 321 ++++++++++++++++++ .../internal/metadata/generated_status.go | 16 + receiver/gitlabreceiver/metadata.yaml | 13 + receiver/gitlabreceiver/testdata/config.yaml | 36 ++ receiver/gitlabreceiver/traces_receiver.go | 114 +++++++ .../gitlabreceiver/traces_receiver_test.go | 36 ++ versions.yaml | 1 + 23 files changed, 1167 insertions(+) create mode 100644 .chloggen/gl-receiver-skeleton-traces.yaml create mode 100644 receiver/gitlabreceiver/Makefile create mode 100644 receiver/gitlabreceiver/README.md create mode 100644 receiver/gitlabreceiver/config.go create mode 100644 receiver/gitlabreceiver/config_test.go create mode 100644 receiver/gitlabreceiver/doc.go create mode 100644 receiver/gitlabreceiver/factory.go create mode 100644 receiver/gitlabreceiver/factory_test.go create mode 100644 receiver/gitlabreceiver/generated_component_test.go create mode 100644 receiver/gitlabreceiver/generated_package_test.go create mode 100644 receiver/gitlabreceiver/go.mod create mode 100644 receiver/gitlabreceiver/go.sum create mode 100644 receiver/gitlabreceiver/internal/metadata/generated_status.go create mode 100644 receiver/gitlabreceiver/metadata.yaml create mode 100644 receiver/gitlabreceiver/testdata/config.yaml create mode 100644 receiver/gitlabreceiver/traces_receiver.go create mode 100644 receiver/gitlabreceiver/traces_receiver_test.go diff --git a/.chloggen/gl-receiver-skeleton-traces.yaml b/.chloggen/gl-receiver-skeleton-traces.yaml new file mode 100644 index 000000000000..d5d4e0fb339c --- /dev/null +++ b/.chloggen/gl-receiver-skeleton-traces.yaml @@ -0,0 +1,30 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: new_component + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: gitlabreceiver + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Adds webhook skeleton to GitLab receiver to receive events from GitLab for tracing. + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [35207] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: + This PR adds a skeleton for the GitLab receiver to receive events from GitLab + for tracing via a webhook. The trace portion of this receiver will run and + respond to GET requests for the health check only. + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [user] diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index e1010b465647..9956b99e4795 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -226,6 +226,7 @@ receiver/filestatsreceiver/ @open-telemetry/collector-cont receiver/flinkmetricsreceiver/ @open-telemetry/collector-contrib-approvers @JonathanWamsley receiver/fluentforwardreceiver/ @open-telemetry/collector-contrib-approvers @dmitryax receiver/githubreceiver/ @open-telemetry/collector-contrib-approvers @adrielp @andrzej-stencel @crobert-1 @TylerHelmuth +receiver/gitlabreceiver/ @open-telemetry/collector-contrib-approvers @adrielp @atoulme receiver/googlecloudmonitoringreceiver/ @open-telemetry/collector-contrib-approvers @dashpole @TylerHelmuth @abhishek-at-cloudwerx receiver/googlecloudpubsubreceiver/ @open-telemetry/collector-contrib-approvers @alexvanboxel receiver/googlecloudspannerreceiver/ @open-telemetry/collector-contrib-approvers @dashpole @dsimil @KiranmayiB @harishbohara11 diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index ea469c572bcb..e3a425d12b06 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -223,6 +223,7 @@ body: - receiver/flinkmetrics - receiver/fluentforward - receiver/github + - receiver/gitlab - receiver/googlecloudmonitoring - receiver/googlecloudpubsub - receiver/googlecloudspanner diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml index 99f72f1c13b1..6bd5fcdd0bc7 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yaml +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -217,6 +217,7 @@ body: - receiver/flinkmetrics - receiver/fluentforward - receiver/github + - receiver/gitlab - receiver/googlecloudmonitoring - receiver/googlecloudpubsub - receiver/googlecloudspanner diff --git a/.github/ISSUE_TEMPLATE/other.yaml b/.github/ISSUE_TEMPLATE/other.yaml index c65b1b3fa089..03bc39c5ed22 100644 --- a/.github/ISSUE_TEMPLATE/other.yaml +++ b/.github/ISSUE_TEMPLATE/other.yaml @@ -217,6 +217,7 @@ body: - receiver/flinkmetrics - receiver/fluentforward - receiver/github + - receiver/gitlab - receiver/googlecloudmonitoring - receiver/googlecloudpubsub - receiver/googlecloudspanner diff --git a/.github/ISSUE_TEMPLATE/unmaintained.yaml b/.github/ISSUE_TEMPLATE/unmaintained.yaml index a6402ed0e246..ef0678ebdbcd 100644 --- a/.github/ISSUE_TEMPLATE/unmaintained.yaml +++ b/.github/ISSUE_TEMPLATE/unmaintained.yaml @@ -222,6 +222,7 @@ body: - receiver/flinkmetrics - receiver/fluentforward - receiver/github + - receiver/gitlab - receiver/googlecloudmonitoring - receiver/googlecloudpubsub - receiver/googlecloudspanner diff --git a/receiver/gitlabreceiver/Makefile b/receiver/gitlabreceiver/Makefile new file mode 100644 index 000000000000..ded7a36092dc --- /dev/null +++ b/receiver/gitlabreceiver/Makefile @@ -0,0 +1 @@ +include ../../Makefile.Common diff --git a/receiver/gitlabreceiver/README.md b/receiver/gitlabreceiver/README.md new file mode 100644 index 000000000000..bd0464f9d79c --- /dev/null +++ b/receiver/gitlabreceiver/README.md @@ -0,0 +1,55 @@ +# GitLab Receiver + +<!-- status autogenerated section --> +| Status | | +| ------------- |-----------| +| Stability | [development]: traces | +| Distributions | [] | +| Issues | [](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aopen+is%3Aissue+label%3Areceiver%2Fgitlab) [](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aclosed+is%3Aissue+label%3Areceiver%2Fgitlab) | +| [Code Owners](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/CONTRIBUTING.md#becoming-a-code-owner) | [@adrielp](https://www.github.com/adrielp), [@atoulme](https://www.github.com/atoulme) | + +[development]: https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/component-stability.md#development +<!-- end autogenerated section --> + +## Traces - Getting Started + +Workflow tracing support is actively being added to the GitLab receiver. +This is accomplished through the processing of GitLab webhook +events for pipelines. The [`pipeline`](https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#pipeline-events) event payloads are then constructed into `trace` +telemetry. + +Each GitLab pipeline, along with its jobs, is converted +into trace spans, allowing the observation of workflow execution times, +success, and failure rates. + +### Configuration + +**IMPORTANT: At this time the tracing portion of this receiver only serves a health check endpoint.** + +The WebHook configuration exposes the following settings: + +* `endpoint`: (default = `localhost:8080`) - The address and port to bind the WebHook to. +* `path`: (default = `/events`) - The path for Action events to be sent to. +* `health_path`: (default = `/health`) - The path for health checks. +* `secret`: (optional) - The secret used to [validate the payload](https://docs.gitlab.com/ee/user/project/integrations/webhooks.html#custom-headers). +* `required_headers`: (optional) - One or more key-value pairs representing required headers for incoming requests. These headers must not conflict with the fixed default GitLab headers. See the customizable and fixed GitLab headers in [config.go](./config.go). + +The WebHook configuration block also accepts all the [confighttp](https://pkg.go.dev/go.opentelemetry.io/collector/config/confighttp#ServerConfig) +settings. + +An example configuration is as follows: + +```yaml +receivers: + gitlab: + webhook: + endpoint: localhost:19418 + path: /events + health_path: /health + secret: ${env:SECRET_STRING_VAR} + required_headers: + WAF-Header: "value" +``` + +For tracing, all configuration is set under the `webhook` key. The full set +of exposed configuration values can be found in [`config.go`](config.go). diff --git a/receiver/gitlabreceiver/config.go b/receiver/gitlabreceiver/config.go new file mode 100644 index 000000000000..bf753f6c0608 --- /dev/null +++ b/receiver/gitlabreceiver/config.go @@ -0,0 +1,136 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package gitlabreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/gitlabreceiver" + +import ( + "errors" + "time" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/config/confighttp" + "go.opentelemetry.io/collector/config/configopaque" + "go.opentelemetry.io/collector/confmap" + "go.uber.org/multierr" +) + +const ( + defaultReadTimeout = 500 * time.Millisecond + defaultWriteTimeout = 500 * time.Millisecond + + defaultEndpoint = "localhost:8080" + + defaultPath = "/events" + defaultHealthPath = "/health" + + // GitLab default headers: https://docs.gitlab.com/ee/user/project/integrations/webhooks.html#delivery-headers + defaultUserAgentHeader = "User-Agent" + defaultGitlabInstanceHeader = "X-Gitlab-Instance" + defaultGitlabWebhookUUIDHeader = "X-Gitlab-Webhook-UUID" + defaultGitlabEventHeader = "X-Gitlab-Event" + defaultGitlabEventUUIDHeader = "X-Gitlab-Event-UUID" + defaultIdempotencyKeyHeader = "Idempotency-Key" +) + +var ( + errReadTimeoutExceedsMaxValue = errors.New("the duration specified for read_timeout exceeds the maximum allowed value of 10s") + errWriteTimeoutExceedsMaxValue = errors.New("the duration specified for write_timeout exceeds the maximum allowed value of 10s") + errRequiredHeader = errors.New("both key and value are required to assign a required_header") + errGitlabHeader = errors.New("gitlab default headers [X-Gitlab-Webhook-UUID, X-Gitlab-Event, X-Gitlab-Event-UUID, Idempotency-Key] cannot be configured") + errConfigNotValid = errors.New("configuration is not valid for the gitlab receiver") +) + +// Config that is exposed to this gitlab receiver through the OTEL config.yaml +type Config struct { + WebHook WebHook `mapstructure:"webhook"` +} + +type WebHook struct { + confighttp.ServerConfig `mapstructure:",squash"` // squash ensures fields are correctly decoded in embedded struct + + Path string `mapstructure:"path"` // path for data collection. default is /events + HealthPath string `mapstructure:"health_path"` // path for health check api. default is /health_check + + RequiredHeaders map[string]configopaque.String `mapstructure:"required_headers"` // optional setting to set one or more required headers for all requests to have (except the health check) + GitlabHeaders GitlabHeaders `mapstructure:",squash"` // GitLab headers set by default + + Secret string `mapstructure:"secret"` // secret for webhook +} + +type GitlabHeaders struct { + Customizable map[string]string `mapstructure:","` // can be overwritten via required_headers + Fixed map[string]string `mapstructure:","` // are not allowed to be overwritten +} + +func createDefaultConfig() component.Config { + return &Config{ + WebHook: WebHook{ + ServerConfig: confighttp.ServerConfig{ + Endpoint: defaultEndpoint, + ReadTimeout: defaultReadTimeout, + WriteTimeout: defaultWriteTimeout, + }, + GitlabHeaders: GitlabHeaders{ + Customizable: map[string]string{ + defaultUserAgentHeader: "", + defaultGitlabInstanceHeader: "https://gitlab.com", + }, + Fixed: map[string]string{ + defaultGitlabWebhookUUIDHeader: "", + defaultGitlabEventHeader: "Pipeline Hook", + defaultGitlabEventUUIDHeader: "", + defaultIdempotencyKeyHeader: "", + }, + }, + Path: defaultPath, + HealthPath: defaultHealthPath, + }, + } +} + +func (cfg *Config) Validate() error { + var errs error + + maxReadWriteTimeout, _ := time.ParseDuration("10s") + + if cfg.WebHook.ServerConfig.ReadTimeout > maxReadWriteTimeout { + errs = multierr.Append(errs, errReadTimeoutExceedsMaxValue) + } + + if cfg.WebHook.ServerConfig.WriteTimeout > maxReadWriteTimeout { + errs = multierr.Append(errs, errWriteTimeoutExceedsMaxValue) + } + + for key, value := range cfg.WebHook.RequiredHeaders { + if key == "" || value == "" { + errs = multierr.Append(errs, errRequiredHeader) + } + + if _, exists := cfg.WebHook.GitlabHeaders.Fixed[key]; exists { + errs = multierr.Append(errs, errGitlabHeader) + } + } + + return errs +} + +func (cfg *Config) Unmarshal(componentParser *confmap.Conf) error { + if componentParser == nil { + return nil + } + + // load the non-dynamic config normally + err := componentParser.Unmarshal(cfg, confmap.WithIgnoreUnused()) + if err != nil { + return err + } + + // overwrite customizable GitLab default headers if configured within the required_headers + for key, header := range cfg.WebHook.RequiredHeaders { + if _, exists := cfg.WebHook.GitlabHeaders.Customizable[key]; exists { + cfg.WebHook.GitlabHeaders.Customizable[key] = string(header) + } + } + + return nil +} diff --git a/receiver/gitlabreceiver/config_test.go b/receiver/gitlabreceiver/config_test.go new file mode 100644 index 000000000000..54d0dbc2d936 --- /dev/null +++ b/receiver/gitlabreceiver/config_test.go @@ -0,0 +1,123 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package gitlabreceiver + +import ( + "path/filepath" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componenttest" + "go.opentelemetry.io/collector/config/confighttp" + "go.opentelemetry.io/collector/config/configopaque" + "go.opentelemetry.io/collector/otelcol/otelcoltest" + + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/gitlabreceiver/internal/metadata" +) + +func TestCreateDefaultConfig(t *testing.T) { + factory := NewFactory() + cfg := factory.CreateDefaultConfig() + + expectedConfig := &Config{ + WebHook: WebHook{ + ServerConfig: confighttp.ServerConfig{ + Endpoint: defaultEndpoint, + ReadTimeout: defaultReadTimeout, + WriteTimeout: defaultWriteTimeout, + }, + Path: defaultPath, + HealthPath: defaultHealthPath, + GitlabHeaders: GitlabHeaders{ + Customizable: map[string]string{ + defaultUserAgentHeader: "", + defaultGitlabInstanceHeader: "https://gitlab.com", + }, + Fixed: map[string]string{ + defaultGitlabWebhookUUIDHeader: "", + defaultGitlabEventHeader: "Pipeline Hook", + defaultGitlabEventUUIDHeader: "", + defaultIdempotencyKeyHeader: "", + }, + }, + }, + } + + assert.Equal(t, expectedConfig, cfg, "failed to create default config") + assert.NoError(t, componenttest.CheckConfigStruct(cfg)) +} + +func TestLoadConfig(t *testing.T) { + factories, err := otelcoltest.NopFactories() + require.NoError(t, err) + + factory := NewFactory() + factories.Receivers[metadata.Type] = factory + + cfg, err := otelcoltest.LoadConfigAndValidate(filepath.Join("testdata", "config.yaml"), factories) + + require.NoError(t, err) + require.NotNil(t, cfg) + + assert.Len(t, cfg.Receivers, 2) + + expectedConfig := &Config{ + WebHook: WebHook{ + ServerConfig: confighttp.ServerConfig{ + Endpoint: "localhost:8080", + ReadTimeout: 500 * time.Millisecond, + WriteTimeout: 500 * time.Millisecond, + }, + Path: "some/path", + HealthPath: "health/path", + RequiredHeaders: map[string]configopaque.String{ + "key1-present": "value1-present", + }, + GitlabHeaders: GitlabHeaders{ + Customizable: map[string]string{ + defaultUserAgentHeader: "", + defaultGitlabInstanceHeader: "https://gitlab.com", + }, + Fixed: map[string]string{ + defaultGitlabWebhookUUIDHeader: "", + defaultGitlabEventHeader: "Pipeline Hook", + defaultGitlabEventUUIDHeader: "", + defaultIdempotencyKeyHeader: "", + }, + }, + }, + } + + r0 := cfg.Receivers[component.NewID(metadata.Type)] + + assert.Equal(t, expectedConfig, r0) + + // r1 requires multiple headers and overwrites gitlab default headers + expectedConfig.WebHook.RequiredHeaders = map[string]configopaque.String{ + "key1-present": "value1-present", + "key2-present": "value2-present", + "User-Agent": "GitLab/1.2.3-custom-version", + "X-Gitlab-Instance": "https://gitlab.self-hosted.xyz", + } + + expectedConfig.WebHook.GitlabHeaders = GitlabHeaders{ + Customizable: map[string]string{ + defaultUserAgentHeader: "GitLab/1.2.3-custom-version", + defaultGitlabInstanceHeader: "https://gitlab.self-hosted.xyz", + }, + Fixed: map[string]string{ + defaultGitlabWebhookUUIDHeader: "", + defaultGitlabEventHeader: "Pipeline Hook", + defaultGitlabEventUUIDHeader: "", + defaultIdempotencyKeyHeader: "", + }, + } + + r1 := cfg.Receivers[component.NewIDWithName(metadata.Type, "customname")].(*Config) + + assert.Equal(t, expectedConfig, r1) +} diff --git a/receiver/gitlabreceiver/doc.go b/receiver/gitlabreceiver/doc.go new file mode 100644 index 000000000000..346240ef41dc --- /dev/null +++ b/receiver/gitlabreceiver/doc.go @@ -0,0 +1,6 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +//go:generate mdatagen metadata.yaml + +package gitlabreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/gitlabreceiver" diff --git a/receiver/gitlabreceiver/factory.go b/receiver/gitlabreceiver/factory.go new file mode 100644 index 000000000000..dbcd3b23334f --- /dev/null +++ b/receiver/gitlabreceiver/factory.go @@ -0,0 +1,31 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package gitlabreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/gitlabreceiver" + +import ( + "context" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/consumer" + "go.opentelemetry.io/collector/receiver" + + "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/gitlabreceiver/internal/metadata" +) + +func createTracesReceiver(_ context.Context, params receiver.Settings, cfg component.Config, consumer consumer.Traces) (receiver.Traces, error) { + // check that the configuration is valid + conf, ok := cfg.(*Config) + if !ok { + return nil, errConfigNotValid + } + + return newTracesReceiver(params, conf, consumer) +} + +func NewFactory() receiver.Factory { + return receiver.NewFactory( + metadata.Type, + createDefaultConfig, + receiver.WithTraces(createTracesReceiver, component.StabilityLevelDevelopment)) +} diff --git a/receiver/gitlabreceiver/factory_test.go b/receiver/gitlabreceiver/factory_test.go new file mode 100644 index 000000000000..5ae5a73fe031 --- /dev/null +++ b/receiver/gitlabreceiver/factory_test.go @@ -0,0 +1,23 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package gitlabreceiver + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "go.opentelemetry.io/collector/consumer/consumertest" + "go.opentelemetry.io/collector/receiver/receivertest" +) + +func TestCreateTracesReceiver(t *testing.T) { + factory := NewFactory() + cfg := factory.CreateDefaultConfig() + assert.NotNil(t, cfg, "failed to create default config") + + tReceiver, err := factory.CreateTraces(context.Background(), receivertest.NewNopSettings(), cfg, consumertest.NewNop()) + assert.NoError(t, err) + assert.NotNil(t, tReceiver, "traces receiver creation failed") +} diff --git a/receiver/gitlabreceiver/generated_component_test.go b/receiver/gitlabreceiver/generated_component_test.go new file mode 100644 index 000000000000..ca3c0b56f562 --- /dev/null +++ b/receiver/gitlabreceiver/generated_component_test.go @@ -0,0 +1,69 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package gitlabreceiver + +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, "gitlab", 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.Settings, cfg component.Config) (component.Component, error) + }{ + + { + name: "traces", + createFn: func(ctx context.Context, set receiver.Settings, cfg component.Config) (component.Component, error) { + return factory.CreateTraces(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, sub.Unmarshal(&cfg)) + + for _, tt := range tests { + t.Run(tt.name+"-shutdown", func(t *testing.T) { + c, err := tt.createFn(context.Background(), receivertest.NewNopSettings(), cfg) + require.NoError(t, err) + err = c.Shutdown(context.Background()) + require.NoError(t, err) + }) + t.Run(tt.name+"-lifecycle", func(t *testing.T) { + firstRcvr, err := tt.createFn(context.Background(), receivertest.NewNopSettings(), 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 := tt.createFn(context.Background(), receivertest.NewNopSettings(), cfg) + require.NoError(t, err) + require.NoError(t, secondRcvr.Start(context.Background(), host)) + require.NoError(t, secondRcvr.Shutdown(context.Background())) + }) + } +} diff --git a/receiver/gitlabreceiver/generated_package_test.go b/receiver/gitlabreceiver/generated_package_test.go new file mode 100644 index 000000000000..7224acad01dc --- /dev/null +++ b/receiver/gitlabreceiver/generated_package_test.go @@ -0,0 +1,13 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package gitlabreceiver + +import ( + "testing" + + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + goleak.VerifyTestMain(m) +} diff --git a/receiver/gitlabreceiver/go.mod b/receiver/gitlabreceiver/go.mod new file mode 100644 index 000000000000..d56db37efedb --- /dev/null +++ b/receiver/gitlabreceiver/go.mod @@ -0,0 +1,138 @@ +module github.com/open-telemetry/opentelemetry-collector-contrib/receiver/gitlabreceiver + +go 1.22.8 + +require ( + github.com/stretchr/testify v1.10.0 + go.opentelemetry.io/collector/component v0.117.0 + go.opentelemetry.io/collector/component/componenttest v0.117.0 + go.opentelemetry.io/collector/config/confighttp v0.117.0 + go.opentelemetry.io/collector/confmap v1.23.0 + go.opentelemetry.io/collector/consumer v1.23.0 + go.opentelemetry.io/collector/consumer/consumertest v0.117.0 + go.opentelemetry.io/collector/receiver v0.117.0 + go.opentelemetry.io/collector/receiver/receivertest v0.117.0 + go.uber.org/goleak v1.3.0 +) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/ebitengine/purego v0.8.1 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 // indirect + github.com/hashicorp/go-version v1.7.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/prometheus/client_golang v1.20.5 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.61.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/shirou/gopsutil/v4 v4.24.12 // indirect + github.com/spf13/cobra v1.8.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/collector/confmap/provider/envprovider v1.23.0 // indirect + go.opentelemetry.io/collector/confmap/provider/fileprovider v1.23.0 // indirect + go.opentelemetry.io/collector/confmap/provider/httpprovider v1.23.0 // indirect + go.opentelemetry.io/collector/confmap/provider/yamlprovider v1.23.0 // indirect + go.opentelemetry.io/collector/connector v0.117.0 // indirect + go.opentelemetry.io/collector/connector/connectortest v0.117.0 // indirect + go.opentelemetry.io/collector/connector/xconnector v0.117.0 // indirect + go.opentelemetry.io/collector/consumer/xconsumer v0.117.0 // indirect + go.opentelemetry.io/collector/exporter v0.117.0 // indirect + go.opentelemetry.io/collector/exporter/exportertest v0.117.0 // indirect + go.opentelemetry.io/collector/exporter/xexporter v0.117.0 // indirect + go.opentelemetry.io/collector/extension/extensioncapabilities v0.117.0 // indirect + go.opentelemetry.io/collector/extension/extensiontest v0.117.0 // indirect + go.opentelemetry.io/collector/featuregate v1.23.0 // indirect + go.opentelemetry.io/collector/internal/fanoutconsumer v0.117.0 // indirect + go.opentelemetry.io/collector/otelcol v0.117.0 // indirect + go.opentelemetry.io/collector/pdata/testdata v0.117.0 // indirect + go.opentelemetry.io/collector/pipeline/xpipeline v0.117.0 // indirect + go.opentelemetry.io/collector/processor v0.117.0 // indirect + go.opentelemetry.io/collector/processor/processortest v0.117.0 // indirect + go.opentelemetry.io/collector/processor/xprocessor v0.117.0 // indirect + go.opentelemetry.io/collector/receiver/xreceiver v0.117.0 // indirect + go.opentelemetry.io/collector/semconv v0.117.0 // indirect + go.opentelemetry.io/collector/service v0.117.0 // indirect + go.opentelemetry.io/contrib/bridges/otelzap v0.6.0 // indirect + go.opentelemetry.io/contrib/config v0.10.0 // indirect + go.opentelemetry.io/contrib/propagators/b3 v1.31.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.7.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.54.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.7.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.32.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0 // indirect + go.opentelemetry.io/otel/log v0.8.0 // indirect + go.opentelemetry.io/otel/sdk/log v0.7.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect + golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect + gonum.org/v1/gonum v0.15.1 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 // indirect +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.8.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-viper/mapstructure/v2 v2.2.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/gorilla/mux v1.8.1 + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.17.11 // 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.2 // 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/pierrec/lz4/v4 v4.1.22 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rs/cors v1.11.1 // indirect + go.opentelemetry.io/collector/client v1.23.0 // indirect + go.opentelemetry.io/collector/component/componentstatus v0.117.0 + go.opentelemetry.io/collector/config/configauth v0.117.0 // indirect + go.opentelemetry.io/collector/config/configcompression v1.23.0 // indirect + go.opentelemetry.io/collector/config/configopaque v1.23.0 + go.opentelemetry.io/collector/config/configtelemetry v0.117.0 // indirect + go.opentelemetry.io/collector/config/configtls v1.23.0 // indirect + go.opentelemetry.io/collector/consumer/consumererror v0.117.0 // indirect + go.opentelemetry.io/collector/extension v0.117.0 // indirect + go.opentelemetry.io/collector/extension/auth v0.117.0 // indirect + go.opentelemetry.io/collector/otelcol/otelcoltest v0.117.0 + go.opentelemetry.io/collector/pdata v1.23.0 // indirect + go.opentelemetry.io/collector/pdata/pprofile v0.117.0 // indirect + go.opentelemetry.io/collector/pipeline v0.117.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect + go.opentelemetry.io/otel v1.33.0 // indirect + go.opentelemetry.io/otel/metric v1.33.0 // indirect + go.opentelemetry.io/otel/sdk v1.32.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.32.0 // indirect + go.opentelemetry.io/otel/trace v1.33.0 // indirect + go.uber.org/multierr v1.11.0 + go.uber.org/zap v1.27.0 + golang.org/x/net v0.34.0 // indirect + golang.org/x/sys v0.29.0 // indirect + golang.org/x/text v0.21.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422 // indirect + google.golang.org/grpc v1.69.2 // indirect + google.golang.org/protobuf v1.36.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/receiver/gitlabreceiver/go.sum b/receiver/gitlabreceiver/go.sum new file mode 100644 index 000000000000..81527f4c3675 --- /dev/null +++ b/receiver/gitlabreceiver/go.sum @@ -0,0 +1,321 @@ +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/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +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/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +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/ebitengine/purego v0.8.1 h1:sdRKd6plj7KYW33EH5As6YKfe8m9zbN9JMrOjNVF/BE= +github.com/ebitengine/purego v0.8.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +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.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/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-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= +github.com/go-viper/mapstructure/v2 v2.2.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/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +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.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 h1:ad0vkEBuk23VJzZR9nkLVG0YAoN9coASF1GusYX6AlU= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0/go.mod h1:igFoXX2ELCW06bol23DWPB5BEWfZISOzSP5K2sbLea0= +github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +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.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +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.2 h1:I2rtLRqXRy1p01m/utEtpZSSA6dcJbgGVuE27kW2PzQ= +github.com/knadh/koanf/v2 v2.1.2/go.mod h1:Gphfaen0q1Fc1HTgJgSTC4oRX9R2R5ErYMZJy8fLJBo= +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/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= +github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +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/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= +github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +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.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ= +github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= +github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shirou/gopsutil/v4 v4.24.12 h1:qvePBOk20e0IKA1QXrIIU+jmk+zEiYVVx06WjBRlZo4= +github.com/shirou/gopsutil/v4 v4.24.12/go.mod h1:DCtMPAad2XceTeIAbGyVfycbYQNBGk2P8cvDi7/VN9o= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/collector v0.117.0 h1:nj/Q89KGmev1l4YxWJt4JH3+fV1YFmci9MRmr9bULf4= +go.opentelemetry.io/collector v0.117.0/go.mod h1:z8XawVuKONaUkJW5w1GrfAXokrgxdF8mGtekK0sFIyQ= +go.opentelemetry.io/collector/client v1.23.0 h1:X11yEZ2T3T1Cr1CfDPI0xjZgw7ekes7CVbF/NVYxGG0= +go.opentelemetry.io/collector/client v1.23.0/go.mod h1:pfhOGJ13n5xH3HgmFwUHa1nBE1kCIa9X/DLTJVxtbVM= +go.opentelemetry.io/collector/component v0.117.0 h1:A3Im4PqLyfduAdVyUgbOZdUs7J/USegdpnkoIAOuN3Y= +go.opentelemetry.io/collector/component v0.117.0/go.mod h1:+SxJgeMwNV6y3aKNR2sP0PfovcUlRwC0+pEv4tTYdXA= +go.opentelemetry.io/collector/component/componentstatus v0.117.0 h1:8PGN66p9o5L7xCfT4jDJHd3d2VdtIuzPU2mEXOSONt8= +go.opentelemetry.io/collector/component/componentstatus v0.117.0/go.mod h1:u8tVDI+S9TxBa5NtxJNdxqjI0CLIzbmqbRl9DPrdR/0= +go.opentelemetry.io/collector/component/componenttest v0.117.0 h1:r3k0BsU/cJlqVQRtgFjxfduNEGaM2qCAU7JitIGkRds= +go.opentelemetry.io/collector/component/componenttest v0.117.0/go.mod h1:MoBWSGb3KwGc5FAIO+htez/QWK2uqJ4fnbEnfHB384c= +go.opentelemetry.io/collector/config/configauth v0.117.0 h1:o+sEz1aeS01XD3procwMmvDAhGHFFH1dxmC6XHwxG6s= +go.opentelemetry.io/collector/config/configauth v0.117.0/go.mod h1:oWkIayfVGS/ED6jEDTILSypW8MVNZ/bHd11lXrt7fsQ= +go.opentelemetry.io/collector/config/configcompression v1.23.0 h1:KCEztOb+2L4+dUCCadOW/byRiw7LbgguNqHD5LxJcwY= +go.opentelemetry.io/collector/config/configcompression v1.23.0/go.mod h1:LvYG00tbPTv0NOLoZN0wXq1F5thcxvukO8INq7xyfWU= +go.opentelemetry.io/collector/config/confighttp v0.117.0 h1:0BRGo1aivqIsGtAMmxTZ0u3rlGJ073+iyHD5RvUOtQk= +go.opentelemetry.io/collector/config/confighttp v0.117.0/go.mod h1:iNCp62v5k9SPTOdOxQlPfs/4gLGh7YLGpjP//9uvT0A= +go.opentelemetry.io/collector/config/configopaque v1.23.0 h1:SEnEzOHufGc4KGOjQq8zKIQuDBmRFl9ncZ3qs1SRpJk= +go.opentelemetry.io/collector/config/configopaque v1.23.0/go.mod h1:sW0t0iI/VfRL9VYX7Ik6XzVgPcR+Y5kejTLsYcMyDWs= +go.opentelemetry.io/collector/config/configretry v1.23.0 h1:0Ox2KvTZyNdgureAs3kJzsNIa6ttrx9bwlKjj/p4fGU= +go.opentelemetry.io/collector/config/configretry v1.23.0/go.mod h1:cleBc9I0DIWpTiiHfu9v83FUaCTqcPXmebpLxjEIqro= +go.opentelemetry.io/collector/config/configtelemetry v0.117.0 h1:xsMfc89VByIF2fJzWuxs/2eqy44DWfNBAysReG4TAr8= +go.opentelemetry.io/collector/config/configtelemetry v0.117.0/go.mod h1:SlBEwQg0qly75rXZ6W1Ig8jN25KBVBkFIIAUI1GiAAE= +go.opentelemetry.io/collector/config/configtls v1.23.0 h1:52q9dAV923hHn1aoYQyKGnrRXCPvTTT3DXurtxcpZaQ= +go.opentelemetry.io/collector/config/configtls v1.23.0/go.mod h1:cjMoqKm4MX9sc9qyEW5/kRepiKLuDYqFofGa0f/rqFE= +go.opentelemetry.io/collector/confmap v1.23.0 h1:EY+auc0kbyZ4HIfkLYeJyLDCZIFzMA1u8QRGW4bC1Ag= +go.opentelemetry.io/collector/confmap v1.23.0/go.mod h1:Rrhs+MWoaP6AswZp+ReQ2VO9dfOfcUjdjiSHBsG+nec= +go.opentelemetry.io/collector/confmap/provider/envprovider v1.23.0 h1:b1aZxceRYYH1wXqqVHYZpepiVKYy8WJ27NHq/KQFQJs= +go.opentelemetry.io/collector/confmap/provider/envprovider v1.23.0/go.mod h1:c1zdel/NZJumOWY8RhKfOuF/uxihNxQrJzBQcnY0HFw= +go.opentelemetry.io/collector/confmap/provider/fileprovider v1.23.0 h1:2CwU9PZLCP76iNTk6vP1CN3BA1C9OnnebpCE0WQf6F4= +go.opentelemetry.io/collector/confmap/provider/fileprovider v1.23.0/go.mod h1:tDUen3bEdWlgJtJEc2OrNV6sTfR/QkImyAFlxUXcplY= +go.opentelemetry.io/collector/confmap/provider/httpprovider v1.23.0 h1:CdGl8CI5rnbAhNIJwisCKvKEiqOKWTZqnsQ2WqcCPs8= +go.opentelemetry.io/collector/confmap/provider/httpprovider v1.23.0/go.mod h1:lvljQaUjATZhFghYNPGNjIO3lsedzv7lOlkQfOdiung= +go.opentelemetry.io/collector/confmap/provider/yamlprovider v1.23.0 h1:ZaK8+EcQxlYZZgJn7ew700AUhH9CpXA3VBn46OHHEHk= +go.opentelemetry.io/collector/confmap/provider/yamlprovider v1.23.0/go.mod h1:WrlXU+lshUTmsgyacD7jijs0Nh85Xf0xU/0sqtkHDNs= +go.opentelemetry.io/collector/connector v0.117.0 h1:7MM6FOrquYyLSftp3vJSeahRLcVcJ+EwgsqZpsPtGas= +go.opentelemetry.io/collector/connector v0.117.0/go.mod h1:Qp3KAr/S3vMjOtWG5tZxQ+6JgFFYBUzFx6xzM6Xt30A= +go.opentelemetry.io/collector/connector/connectortest v0.117.0 h1:tRes8VpoYEXbOZtT5NQdYhWd7PyHy4N3R/9M2VMZt7U= +go.opentelemetry.io/collector/connector/connectortest v0.117.0/go.mod h1:rb7ax+hQzL2fiUFI9QpfOPQX2S6GfJlyxjT4tsIYODQ= +go.opentelemetry.io/collector/connector/xconnector v0.117.0 h1:H4tTVBKDW9bfEJ+6p6ZDIdN7yUkGl59ELs0+46UtQ78= +go.opentelemetry.io/collector/connector/xconnector v0.117.0/go.mod h1:aAfKBBFnJrPgKC653Lt1gwfTDbSZUuTY4TPI7Fcv9MM= +go.opentelemetry.io/collector/consumer v1.23.0 h1:JT0nE1vikL5yIk97IHBGzwx8co3w1WsAd3GFEl8r9XA= +go.opentelemetry.io/collector/consumer v1.23.0/go.mod h1:8d0uQ6gq64LbPktV4sc888lRj1cQCmrdl13hRIEURgA= +go.opentelemetry.io/collector/consumer/consumererror v0.117.0 h1:PPIZCcYZcENnyIrpRV4ERvMUoPSTV0zIP0QPzJvz80g= +go.opentelemetry.io/collector/consumer/consumererror v0.117.0/go.mod h1:L47xOVC+Vzos8350j3SWtU43w7rzms6UDhb6IrFxymY= +go.opentelemetry.io/collector/consumer/consumertest v0.117.0 h1:9WFyyjLudvfJDEuUaGsQyNRd1m6D1iRg8Iyg3xliFko= +go.opentelemetry.io/collector/consumer/consumertest v0.117.0/go.mod h1:B7A+OS76QKAzM8W7cmvlfVynFELj9Sa444hSm1SILFw= +go.opentelemetry.io/collector/consumer/xconsumer v0.117.0 h1:vsBNJGaEbYqgMU3PEsOcqjMxX5ul++Cxda44sttoi8c= +go.opentelemetry.io/collector/consumer/xconsumer v0.117.0/go.mod h1:dTr+Tms53lRLvR3OAzYic0yhcwldhTUdVIwJNSDmBmw= +go.opentelemetry.io/collector/exporter v0.117.0 h1:A9kVXzdb8i1eFELImuaSPyijAfg4qMIpM/4y/98mlxk= +go.opentelemetry.io/collector/exporter v0.117.0/go.mod h1:Cbrorch2s18w1X7+A+zXQtAffbInnIOP7Su26gbRG+k= +go.opentelemetry.io/collector/exporter/exportertest v0.117.0 h1:u+loeqxpniMiJL1iqc/lCCcfniWrqHBgJTAjXfqVBqQ= +go.opentelemetry.io/collector/exporter/exportertest v0.117.0/go.mod h1:GyHwJLsOPPau0m+TYrIA7jWD9/GU+ID+l/9sL0cAqhE= +go.opentelemetry.io/collector/exporter/xexporter v0.117.0 h1:BB8D0Dvb46CVAZrnPEg5nYgXO7LzONmXeGKEfzSIOZs= +go.opentelemetry.io/collector/exporter/xexporter v0.117.0/go.mod h1:yo0T8WkvLCJ7NOqIquHGFe4Xpuc4CbDb8a06T2G5De4= +go.opentelemetry.io/collector/extension v0.117.0 h1:B3cG7g+wbhmpMFugaDxOcyiPKeulaW8+EQdJbZxDfho= +go.opentelemetry.io/collector/extension v0.117.0/go.mod h1:WjyD5h9N5Y0SF8azB2rulvHJieJoWqroGO5hi3ax5+8= +go.opentelemetry.io/collector/extension/auth v0.117.0 h1:tXQdYIdcABXalWyFZP22pREY7+nWUNurx8Y6FseWs7w= +go.opentelemetry.io/collector/extension/auth v0.117.0/go.mod h1:ofrV2BuE46+k7Su/h0ccrMl5Zk5Y7NVlzOb3AwU7Dzw= +go.opentelemetry.io/collector/extension/auth/authtest v0.117.0 h1:wV4OIiWrt7gteQrxL8MCmGvjGhMiu5TplKJHOfVZO6Y= +go.opentelemetry.io/collector/extension/auth/authtest v0.117.0/go.mod h1:nHxcAOyo26JnuYwKIoQM9mDlSXpERQrbjIw3Dtp9hug= +go.opentelemetry.io/collector/extension/extensioncapabilities v0.117.0 h1:YbCF0s0jywLZgwNUyKdPUMLMZs3nUPm1tGvJz8x6wTU= +go.opentelemetry.io/collector/extension/extensioncapabilities v0.117.0/go.mod h1:VArn6UKAy4pqlATfhDFfc8UOwX/TtavPF5pgAL70AEw= +go.opentelemetry.io/collector/extension/extensiontest v0.117.0 h1:XH+tkHdATylYZtASZKK3rCoN/xlaFi8MXLh07ZlQQWw= +go.opentelemetry.io/collector/extension/extensiontest v0.117.0/go.mod h1:ABqB9D41p4MCeGVmABOgJi7i7roWZlFbqeFJDy7lskQ= +go.opentelemetry.io/collector/extension/xextension v0.117.0 h1:ADUKWHGaVvvmebJHiNRuX6YAfQXFDW/UaXK9W1hCo1k= +go.opentelemetry.io/collector/extension/xextension v0.117.0/go.mod h1:BmR8xN7Ja+El4IJ9aVmtON2miudjsbq2COZ9azVXsNg= +go.opentelemetry.io/collector/extension/zpagesextension v0.117.0 h1:TNSMgnCYZ1He2ANJQDoBEQ2tuHMa97qM5cpLErNLous= +go.opentelemetry.io/collector/extension/zpagesextension v0.117.0/go.mod h1:c82ly0hN5nMEtXY9mNdS9xVToYxbUjXblnXZCoICwCc= +go.opentelemetry.io/collector/featuregate v1.23.0 h1:N033ROo85qKrsK16QzR6RV+3UWOWF7kpOO8FSnX99s0= +go.opentelemetry.io/collector/featuregate v1.23.0/go.mod h1:3GaXqflNDVwWndNGBJ1+XJFy3Fv/XrFgjMN60N3z7yg= +go.opentelemetry.io/collector/internal/fanoutconsumer v0.117.0 h1:IfObXF9WEixWA9baPt0d4GOv8XGxmlsX7oAyD9Gdq/4= +go.opentelemetry.io/collector/internal/fanoutconsumer v0.117.0/go.mod h1:n+hmwNk4CbOTmQyUo1K4CEnCGcrPd7RY3E6ljrQ2GYo= +go.opentelemetry.io/collector/otelcol v0.117.0 h1:BWmXS+Qh8ypu95w5PKz4NEcyRfX9TzoXQaqD6antji8= +go.opentelemetry.io/collector/otelcol v0.117.0/go.mod h1:jbEizqJKjZ1Q7XIbKvYc+vF2sxW5aw0LO7U8wj7wysM= +go.opentelemetry.io/collector/otelcol/otelcoltest v0.117.0 h1:uELK5WYoofV19gAq0cZrmAAcBO/SP94fheYJ/+bm49g= +go.opentelemetry.io/collector/otelcol/otelcoltest v0.117.0/go.mod h1:Xo9X7JBQVCCjkkMjkRdsuvrwM82xk67HuUge7zXm5FU= +go.opentelemetry.io/collector/pdata v1.23.0 h1:tEk0dkfB8RdSukoOMfEa8duB938gfZowdfRkrJxGDrw= +go.opentelemetry.io/collector/pdata v1.23.0/go.mod h1:I2jggpBMiO8A+7TXhzNpcJZkJtvi1cU0iVNIi+6bc+o= +go.opentelemetry.io/collector/pdata/pprofile v0.117.0 h1:AyOK+rkNGeawmLGUqF84wYks22BSGJtEV++3YSfvD1I= +go.opentelemetry.io/collector/pdata/pprofile v0.117.0/go.mod h1:eh7TLIkLrSI79/R3RL+sZsKpLS0k+83WntucPtXC5Ak= +go.opentelemetry.io/collector/pdata/testdata v0.117.0 h1:ainpacShKHaDkPK6lcvgJ0aPKYUD/E3+I0gYJZleedo= +go.opentelemetry.io/collector/pdata/testdata v0.117.0/go.mod h1:LZAymmRKHQEqJqJUSO15rej3+V1rNRyBMF5mWCKCMBY= +go.opentelemetry.io/collector/pipeline v0.117.0 h1:CSv0Dd3n9AQNQ73e7PdEkgexkSMRZliKATxkoZKUFcY= +go.opentelemetry.io/collector/pipeline v0.117.0/go.mod h1:qE3DmoB05AW0C3lmPvdxZqd/H4po84NPzd5MrqgtL74= +go.opentelemetry.io/collector/pipeline/xpipeline v0.117.0 h1:jnHQNaNfVRIdrtOPCORUy8s1cEJyxql3uv/WQ1ve1Js= +go.opentelemetry.io/collector/pipeline/xpipeline v0.117.0/go.mod h1:lNY3uQjRcb3f7CW1JQMXJcWzCJp5122LOKrKs5eito8= +go.opentelemetry.io/collector/processor v0.117.0 h1:K4WdaNC5ROIoLRGgyHmXxtw7xVpAMR4cIMQ5PVLP5cI= +go.opentelemetry.io/collector/processor v0.117.0/go.mod h1:4ewsyJD4n8GjFN+mFbxgr7uXLZYNcJEnH3wl47aDV7s= +go.opentelemetry.io/collector/processor/processortest v0.117.0 h1:c2zjsm3nQDkq9GErzhczN7psGI5Wk0eqXM5LGrX3wxg= +go.opentelemetry.io/collector/processor/processortest v0.117.0/go.mod h1:nywNHogkxp++ab3QkXpWKlv41Gkm9cAYB4PHvyoHwjs= +go.opentelemetry.io/collector/processor/xprocessor v0.117.0 h1:yGBjlY8HRb2AqYo1Q8pKJOLRbmZKrjeeTO4COiP45OU= +go.opentelemetry.io/collector/processor/xprocessor v0.117.0/go.mod h1:MnyEaS47cqol7Cph6LnYIp0g2Km4M+I1vWTwiDeuBN0= +go.opentelemetry.io/collector/receiver v0.117.0 h1:jm+b2G2IKKwGE213lB9cviKEdeATvYtNSY1kO0XdpMM= +go.opentelemetry.io/collector/receiver v0.117.0/go.mod h1:fZXigB3afp54OE+ogPcup/RPwI7j+CwZh9Mz6ObB/Cg= +go.opentelemetry.io/collector/receiver/receivertest v0.117.0 h1:aN4zOuWsiARa+RG9f89JyIrJbx5wsQ71Y0giiHsO1z8= +go.opentelemetry.io/collector/receiver/receivertest v0.117.0/go.mod h1:1wnGEowDmlO89feq1P+b4tQI2G/+iJxRrMallw7zeJE= +go.opentelemetry.io/collector/receiver/xreceiver v0.117.0 h1:HJjBj6P3/WQoYaRKZkWZHnUUCVFpBieqGKzKHcT6HUw= +go.opentelemetry.io/collector/receiver/xreceiver v0.117.0/go.mod h1:K1qMjIiAg6i3vHA+/EpM8nkhna3uIgoEellE2yuhz7A= +go.opentelemetry.io/collector/semconv v0.117.0 h1:SavOvSbHPVD/QdAnXlI/cMca+yxCNyXStY1mQzerHs4= +go.opentelemetry.io/collector/semconv v0.117.0/go.mod h1:N6XE8Q0JKgBN2fAhkUQtqK9LT7rEGR6+Wu/Rtbal1iI= +go.opentelemetry.io/collector/service v0.117.0 h1:yx3ZwnjHcL1YAWZDK2Kxco1BSB228+RaCwgmMzXykqE= +go.opentelemetry.io/collector/service v0.117.0/go.mod h1:Mtxu9Qn/90kdRrEqRr6n7MbtnW6qF1qCIi/u2LMYrRo= +go.opentelemetry.io/contrib/bridges/otelzap v0.6.0 h1:j8icMXyyqNf6HGuwlYhniPnVsbJIq7n+WirDu3VAJdQ= +go.opentelemetry.io/contrib/bridges/otelzap v0.6.0/go.mod h1:evIOZpl+kAlU5IsaYX2Siw+IbpacAZvXemVsgt70uvw= +go.opentelemetry.io/contrib/config v0.10.0 h1:2JknAzMaYjxrHkTnZh3eOme/Y2P5eHE2SWfhfV6Xd6c= +go.opentelemetry.io/contrib/config v0.10.0/go.mod h1:aND2M6/KfNkntI5cyvHriR/zvZgPf8j9yETdSmvpfmc= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= +go.opentelemetry.io/contrib/propagators/b3 v1.31.0 h1:PQPXYscmwbCp76QDvO4hMngF2j8Bx/OTV86laEl8uqo= +go.opentelemetry.io/contrib/propagators/b3 v1.31.0/go.mod h1:jbqfV8wDdqSDrAYxVpXQnpM0XFMq2FtDesblJ7blOwQ= +go.opentelemetry.io/contrib/zpages v0.56.0 h1:W7vP6s3juzL5KiHpr41zLNmsJ0QAZudYu8ay0zGAoko= +go.opentelemetry.io/contrib/zpages v0.56.0/go.mod h1:IxPRP4TYHw9jLeaEOSDIiA9zmyJNZNO6sbW55iMvSXs= +go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= +go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.7.0 h1:mMOmtYie9Fx6TSVzw4W+NTpvoaS1JWWga37oI1a/4qQ= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.7.0/go.mod h1:yy7nDsMMBUkD+jeekJ36ur5f3jJIrmCwUrY67VFhNpA= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0 h1:j7ZSD+5yn+lo3sGV69nW04rRR0jhYnBwjuX3r0HvnK0= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0/go.mod h1:WXbYJTUaZXAbYd8lbgGuvih0yuCfOFC5RJoYnoLcGz8= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0 h1:t/Qur3vKSkUCcDVaSumWF2PKHt85pc7fRvFuoVT8qFU= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0/go.mod h1:Rl61tySSdcOJWoEgYZVtmnKdA0GeKrSqkHC1t+91CH8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 h1:K0XaT3DwHAcV4nKLzcQvwAgSyisUghWoY20I7huthMk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0/go.mod h1:B5Ki776z/MBnVha1Nzwp5arlzBbE3+1jk+pGmaP5HME= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 h1:FFeLy03iVTXP6ffeN2iXrxfGsZGCjVx0/4KlizjyBwU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0/go.mod h1:TMu73/k1CP8nBUpDLc71Wj/Kf7ZS9FK5b53VapRsP9o= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 h1:lUsI2TYsQw2r1IASwoROaCnjdj2cvC2+Jbxvk6nHnWU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4= +go.opentelemetry.io/otel/exporters/prometheus v0.54.0 h1:rFwzp68QMgtzu9PgP3jm9XaMICI6TsofWWPcBDKwlsU= +go.opentelemetry.io/otel/exporters/prometheus v0.54.0/go.mod h1:QyjcV9qDP6VeK5qPyKETvNjmaaEc7+gqjh4SS0ZYzDU= +go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.7.0 h1:TwmL3O3fRR80m8EshBrd8YydEZMcUCsZXzOUlnFohwM= +go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.7.0/go.mod h1:tH98dDv5KPmPThswbXA0fr0Lwfs+OhK8HgaCo7PjRrk= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.32.0 h1:SZmDnHcgp3zwlPBS2JX2urGYe/jBKEIT6ZedHRUyCz8= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.32.0/go.mod h1:fdWW0HtZJ7+jNpTKUR0GpMEDP69nR8YBJQxNiVCE3jk= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0 h1:UGZ1QwZWY67Z6BmckTU+9Rxn04m2bD3gD6Mk0OIOCPk= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0/go.mod h1:fcwWuDuaObkkChiDlhEpSq9+X1C0omv+s5mBtToAQ64= +go.opentelemetry.io/otel/log v0.8.0 h1:egZ8vV5atrUWUbnSsHn6vB8R21G2wrKqNiDt3iWertk= +go.opentelemetry.io/otel/log v0.8.0/go.mod h1:M9qvDdUTRCopJcGRKg57+JSQ9LgLBrwwfC32epk5NX8= +go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= +go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= +go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= +go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= +go.opentelemetry.io/otel/sdk/log v0.7.0 h1:dXkeI2S0MLc5g0/AwxTZv6EUEjctiH8aG14Am56NTmQ= +go.opentelemetry.io/otel/sdk/log v0.7.0/go.mod h1:oIRXpW+WD6M8BuGj5rtS0aRu/86cbDV/dAfNaZBIjYM= +go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= +go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= +go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= +go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= +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/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= +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.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +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-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.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.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +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= +gonum.org/v1/gonum v0.15.1 h1:FNy7N6OUZVUaWG9pTiD+jlhdQ3lMP+/LcTpJ6+a8sQ0= +gonum.org/v1/gonum v0.15.1/go.mod h1:eZTZuRFrzu5pcyjN5wJhcIhnUdNijYxX1T2IcrOGY0o= +google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28 h1:M0KvPgPmDZHPlbRbaNU1APr28TvwvvdUPlSv7PUvy8g= +google.golang.org/genproto/googleapis/api v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:dguCy7UOdZhTvLzDyt15+rOrawrpM4q7DD9dQ1P11P4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422 h1:3UsHvIr4Wc2aW4brOaSCmcxh9ksica6fHEr8P1XhkYw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4= +google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU= +google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= +google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU= +google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +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/receiver/gitlabreceiver/internal/metadata/generated_status.go b/receiver/gitlabreceiver/internal/metadata/generated_status.go new file mode 100644 index 000000000000..733e05fb0fd6 --- /dev/null +++ b/receiver/gitlabreceiver/internal/metadata/generated_status.go @@ -0,0 +1,16 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package metadata + +import ( + "go.opentelemetry.io/collector/component" +) + +var ( + Type = component.MustNewType("gitlab") + ScopeName = "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/gitlabreceiver" +) + +const ( + TracesStability = component.StabilityLevelDevelopment +) diff --git a/receiver/gitlabreceiver/metadata.yaml b/receiver/gitlabreceiver/metadata.yaml new file mode 100644 index 000000000000..4df4990025da --- /dev/null +++ b/receiver/gitlabreceiver/metadata.yaml @@ -0,0 +1,13 @@ +type: gitlab + +status: + class: receiver + stability: + development: [traces] + distributions: [] + codeowners: + active: [adrielp, atoulme] +tests: + config: + webhook: + endpoint: "localhost:0" #dynamic port allocation to avoid falky tests \ No newline at end of file diff --git a/receiver/gitlabreceiver/testdata/config.yaml b/receiver/gitlabreceiver/testdata/config.yaml new file mode 100644 index 000000000000..c9e3ccfb96e0 --- /dev/null +++ b/receiver/gitlabreceiver/testdata/config.yaml @@ -0,0 +1,36 @@ +receivers: + gitlab: + webhook: + endpoint: localhost:8080 + read_timeout: "500ms" + write_timeout: "500ms" + path: "some/path" + health_path: "health/path" + required_headers: + key1-present: "value1-present" + + gitlab/customname: + webhook: + endpoint: localhost:8080 + read_timeout: "500ms" + write_timeout: "500ms" + path: "some/path" + health_path: "health/path" + required_headers: + key1-present: "value1-present" + key2-present: "value2-present" + User-Agent: "GitLab/1.2.3-custom-version" + X-Gitlab-Instance: "https://gitlab.self-hosted.xyz" + +processors: + nop: + +exporters: + nop: + +service: + pipelines: + traces: + receivers: [gitlab, gitlab/customname] + processors: [nop] + exporters: [nop] \ No newline at end of file diff --git a/receiver/gitlabreceiver/traces_receiver.go b/receiver/gitlabreceiver/traces_receiver.go new file mode 100644 index 000000000000..7d753700cef1 --- /dev/null +++ b/receiver/gitlabreceiver/traces_receiver.go @@ -0,0 +1,114 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package gitlabreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/gitlabreceiver" + +import ( + "context" + "errors" + "fmt" + "net/http" + "sync" + + "github.com/gorilla/mux" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componentstatus" + "go.opentelemetry.io/collector/consumer" + "go.opentelemetry.io/collector/receiver" + "go.opentelemetry.io/collector/receiver/receiverhelper" + "go.uber.org/zap" +) + +const healthyResponse = `{"text": "GitLab receiver webhook is healthy"}` + +type gitlabTracesReceiver struct { + cfg *Config + settings receiver.Settings + traceConsumer consumer.Traces + obsrecv *receiverhelper.ObsReport + server *http.Server + shutdownWG sync.WaitGroup + logger *zap.Logger +} + +func newTracesReceiver(settings receiver.Settings, cfg *Config, traceConsumer consumer.Traces) (*gitlabTracesReceiver, error) { + transport := "http" + if cfg.WebHook.TLSSetting != nil { + transport = "https" + } + + obsrecv, err := receiverhelper.NewObsReport(receiverhelper.ObsReportSettings{ + ReceiverID: settings.ID, + Transport: transport, + ReceiverCreateSettings: settings, + }) + if err != nil { + return nil, err + } + + gtr := &gitlabTracesReceiver{ + traceConsumer: traceConsumer, + cfg: cfg, + settings: settings, + logger: settings.Logger, + obsrecv: obsrecv, + } + + return gtr, nil +} + +func (gtr *gitlabTracesReceiver) Start(ctx context.Context, host component.Host) error { + endpoint := fmt.Sprintf("%s%s", gtr.cfg.WebHook.Endpoint, gtr.cfg.WebHook.Path) + gtr.logger.Info("Starting GitLab WebHook receiving server", zap.String("endpoint", endpoint)) + + // noop if not nil. if start has not been called before these values should be nil. + if gtr.server != nil && gtr.server.Handler != nil { + return nil + } + + // create listener from config + ln, err := gtr.cfg.WebHook.ServerConfig.ToListener(ctx) + if err != nil { + return err + } + + // use gorilla mux to set up a router + router := mux.NewRouter() + + // setup health route + router.HandleFunc(gtr.cfg.WebHook.HealthPath, gtr.handleHealthCheck) + + // webhook server standup and configuration + gtr.server, err = gtr.cfg.WebHook.ServerConfig.ToServer(ctx, host, gtr.settings.TelemetrySettings, router) + if err != nil { + return err + } + gtr.logger.Info("Health check now listening at", zap.String("health_path", fmt.Sprintf("%s%s", gtr.cfg.WebHook.Endpoint, gtr.cfg.WebHook.HealthPath))) + + gtr.shutdownWG.Add(1) + go func() { + defer gtr.shutdownWG.Done() + if errHTTP := gtr.server.Serve(ln); !errors.Is(errHTTP, http.ErrServerClosed) && errHTTP != nil { + componentstatus.ReportStatus(host, componentstatus.NewFatalErrorEvent(errHTTP)) + } + }() + + return nil +} + +func (gtr *gitlabTracesReceiver) Shutdown(ctx context.Context) error { + if gtr.server != nil { + err := gtr.server.Shutdown(ctx) + return err + } + gtr.shutdownWG.Wait() + return nil +} + +// Simple healthcheck endpoint. +func (gtr *gitlabTracesReceiver) handleHealthCheck(w http.ResponseWriter, _ *http.Request) { + w.Header().Add("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + _, _ = w.Write([]byte(healthyResponse)) +} diff --git a/receiver/gitlabreceiver/traces_receiver_test.go b/receiver/gitlabreceiver/traces_receiver_test.go new file mode 100644 index 000000000000..355c993bf81e --- /dev/null +++ b/receiver/gitlabreceiver/traces_receiver_test.go @@ -0,0 +1,36 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package gitlabreceiver + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component/componenttest" + "go.opentelemetry.io/collector/consumer/consumertest" + "go.opentelemetry.io/collector/receiver/receivertest" +) + +func TestHealthCheck(t *testing.T) { + defaultConfig := createDefaultConfig().(*Config) + defaultConfig.WebHook.Endpoint = "localhost:0" + consumer := consumertest.NewNop() + receiver, err := newTracesReceiver(receivertest.NewNopSettings(), defaultConfig, consumer) + require.NoError(t, err, "failed to create receiver") + + r := receiver + require.NoError(t, r.Start(context.Background(), componenttest.NewNopHost()), "failed to start receiver") + defer func() { + require.NoError(t, r.Shutdown(context.Background()), "failed to shutdown revceiver") + }() + + w := httptest.NewRecorder() + r.handleHealthCheck(w, httptest.NewRequest(http.MethodGet, "http://localhost/health", nil)) + + response := w.Result() + require.Equal(t, http.StatusOK, response.StatusCode) +} diff --git a/versions.yaml b/versions.yaml index c44692c506a9..8dc8a837cf9a 100644 --- a/versions.yaml +++ b/versions.yaml @@ -220,6 +220,7 @@ module-sets: - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/flinkmetricsreceiver - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/fluentforwardreceiver - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/githubreceiver + - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/gitlabreceiver - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/googlecloudmonitoringreceiver - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/googlecloudpubsubreceiver - github.com/open-telemetry/opentelemetry-collector-contrib/receiver/googlecloudspannerreceiver From bc20ac974f49bd6ef0cb39c69e60dc96dea7f101 Mon Sep 17 00:00:00 2001 From: Jade Guiton <jade.guiton@datadoghq.com> Date: Mon, 13 Jan 2025 16:10:43 +0100 Subject: [PATCH 02/13] [chore] Use `crosslink tidylist` in `make gotidy` (#37142) --- .github/workflows/build-and-test.yml | 4 + Makefile | 15 +- internal/tidylist/allow-circular.txt | 12 ++ internal/tidylist/tidylist.txt | 296 +++++++++++++++++++++++++++ internal/tools/go.mod | 4 +- internal/tools/go.sum | 8 +- 6 files changed, 332 insertions(+), 7 deletions(-) create mode 100644 internal/tidylist/allow-circular.txt create mode 100644 internal/tidylist/tidylist.txt diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index d830458da194..cf6d1d2d4302 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -221,6 +221,10 @@ jobs: run: | make crosslink git diff --exit-code || (echo 'Replace statements are out of date, please run "make crosslink" and commit the changes in this PR.' && exit 1) + - name: tidylist + run: | + make tidylist + git diff --exit-code || (echo 'Tidylist is out of date, please run "make tidylist" and commit the changes in this PR.' && exit 1) - name: Check for go mod dependency changes run: | make gotidy diff --git a/Makefile b/Makefile index 341c909dbfc3..439d8eb1671d 100644 --- a/Makefile +++ b/Makefile @@ -115,9 +115,22 @@ stability-tests: otelcontribcol gogci: $(MAKE) $(FOR_GROUP_TARGET) TARGET="gci" +# `internal/tidylist/tidylist.txt` lists modules in topological order, to ensure `go mod tidy` converges. +# `make tidylist` will update this list. +# We exclude otelcontribcol and oteltestbedcol as those modules are not gitted and may not be present. +.PHONY: tidylist +tidylist: $(CROSSLINK) + cd internal/tidylist && \ + $(CROSSLINK) tidylist --validate --allow-circular allow-circular.txt tidylist.txt && \ + sed -i.bak -E '/cmd\/otel(contrib|testbed)col/d' tidylist.txt && \ + rm tidylist.txt.bak + .PHONY: gotidy gotidy: - $(MAKE) $(FOR_GROUP_TARGET) TARGET="tidy" + @for mod in $$(cat internal/tidylist/tidylist.txt); do \ + echo "Tidying $$mod"; \ + (cd $$mod && rm -rf go.sum && $(GOCMD) mod tidy -compat=1.22.0) || exit $?; \ + done .PHONY: remove-toolchain remove-toolchain: diff --git a/internal/tidylist/allow-circular.txt b/internal/tidylist/allow-circular.txt new file mode 100644 index 000000000000..26b23b684f53 --- /dev/null +++ b/internal/tidylist/allow-circular.txt @@ -0,0 +1,12 @@ +# This file lists modules that are known to have intra-repository circular dependencies. +# The `make tidylist` command will check against this list and error out if circular dependencies +# are accidentally added or removed. + +# exporter/datadog <-> connector/datadog +exporter/datadogexporter +connector/datadogconnector + +# receiver/otelarrow <-> internal/otelarrow <-> exporter/otelarrow +receiver/otelarrowreceiver +exporter/otelarrowexporter +internal/otelarrow diff --git a/internal/tidylist/tidylist.txt b/internal/tidylist/tidylist.txt new file mode 100644 index 000000000000..7fdc798f68df --- /dev/null +++ b/internal/tidylist/tidylist.txt @@ -0,0 +1,296 @@ +cmd/githubgen +cmd/opampsupervisor +confmap/provider/s3provider +confmap/provider/secretsmanagerprovider +pkg/pdatautil +pkg/golden +pkg/pdatatest +internal/coreinternal +pkg/ottl +internal/filter +connector/countconnector +internal/common +internal/aws/ecsutil +internal/k8sconfig +internal/metadataproviders +pkg/resourcetotelemetry +internal/k8stest +processor/k8sattributesprocessor +pkg/sampling +processor/probabilisticsamplerprocessor +processor/resourcedetectionprocessor +internal/pdatautil +processor/transformprocessor +internal/docker +receiver/dockerstatsreceiver +extension/storage +pkg/stanza +receiver/filelogreceiver +pkg/experimentalmetricmetadata +receiver/hostmetricsreceiver +pkg/translator/prometheus +pkg/translator/prometheusremotewrite +exporter/prometheusremotewriteexporter +receiver/prometheusreceiver +pkg/datadog +processor/tailsamplingprocessor +exporter/datadogexporter +connector/datadogconnector +exporter/datadogexporter +connector/exceptionsconnector +connector/failoverconnector +connector/grafanacloudconnector +connector/otlpjsonconnector +connector/roundrobinconnector +connector/routingconnector +connector/servicegraphconnector +connector/spanmetricsconnector +connector/sumconnector +exporter/alertmanagerexporter +exporter/alibabacloudlogserviceexporter +internal/aws/awsutil +internal/aws/cwlogs +exporter/awscloudwatchlogsexporter +internal/aws/metrics +exporter/awsemfexporter +pkg/translator/jaeger +pkg/translator/zipkin +exporter/awskinesisexporter +exporter/awss3exporter +internal/aws/xray +exporter/awsxrayexporter +exporter/azuredataexplorerexporter +exporter/azuremonitorexporter +exporter/carbonexporter +exporter/cassandraexporter +exporter/clickhouseexporter +exporter/coralogixexporter +exporter/datasetexporter +exporter/dorisexporter +exporter/elasticsearchexporter +extension/encoding +extension/encoding/otlpencodingextension +internal/sharedcomponent +exporter/fileexporter +exporter/googlecloudexporter +exporter/googlecloudpubsubexporter +exporter/googlemanagedprometheusexporter +exporter/honeycombmarkerexporter +exporter/influxdbexporter +internal/kafka +pkg/batchpersignal +pkg/kafka/topic +exporter/kafkaexporter +internal/exp/metrics +exporter/loadbalancingexporter +exporter/logicmonitorexporter +exporter/logzioexporter +pkg/translator/loki +exporter/lokiexporter +exporter/mezmoexporter +pkg/translator/opencensus +receiver/opencensusreceiver +exporter/opencensusexporter +exporter/opensearchexporter +internal/grpcutil +receiver/otelarrowreceiver +internal/otelarrow +exporter/otelarrowexporter +receiver/otelarrowreceiver +internal/otelarrow +exporter/otelarrowexporter +receiver/otelarrowreceiver +exporter/prometheusexporter +exporter/pulsarexporter +internal/rabbitmq +exporter/rabbitmqexporter +internal/splunk +pkg/batchperresourceattr +exporter/sapmexporter +exporter/sentryexporter +pkg/translator/signalfx +exporter/signalfxexporter +exporter/splunkhecexporter +extension/sumologicextension +exporter/sumologicexporter +exporter/syslogexporter +exporter/tencentcloudlogserviceexporter +receiver/zipkinreceiver +exporter/zipkinexporter +extension/ackextension +extension/asapauthextension +internal/aws/proxy +extension/awsproxy +extension/basicauthextension +extension/bearertokenauthextension +extension/encoding/avrologencodingextension +extension/encoding/jaegerencodingextension +extension/encoding/jsonlogencodingextension +extension/encoding/textencodingextension +extension/encoding/zipkinencodingextension +extension/googleclientauthextension +extension/headerssetterextension +extension/healthcheckextension +pkg/status +extension/healthcheckv2extension +extension/httpforwarderextension +extension/jaegerremotesampling +extension/oauth2clientauthextension +extension/observer +extension/observer/cfgardenobserver +extension/observer/dockerobserver +extension/observer/ecsobserver +extension/observer/ecstaskobserver +extension/observer/hostobserver +extension/observer/k8sobserver +extension/oidcauthextension +extension/opampcustommessages +extension/opampextension +extension/pprofextension +extension/remotetapextension +extension/sigv4authextension +extension/solarwindsapmsettingsextension +extension/storage/dbstorage +extension/storage/filestorage +extension/storage/redisstorageextension +processor/attributesprocessor +processor/cumulativetodeltaprocessor +processor/deltatocumulativeprocessor +processor/deltatorateprocessor +processor/filterprocessor +processor/geoipprocessor +processor/groupbyattrsprocessor +processor/groupbytraceprocessor +processor/intervalprocessor +processor/logdedupprocessor +processor/metricsgenerationprocessor +processor/metricstransformprocessor +processor/redactionprocessor +processor/remotetapprocessor +processor/resourceprocessor +processor/routingprocessor +processor/spanprocessor +processor/sumologicprocessor +pkg/winperfcounters +receiver/activedirectorydsreceiver +receiver/aerospikereceiver +receiver/apachereceiver +receiver/apachesparkreceiver +receiver/awscloudwatchreceiver +internal/aws/containerinsight +internal/aws/k8s +internal/kubelet +receiver/awscontainerinsightreceiver +receiver/awsecscontainermetricsreceiver +receiver/awsfirehosereceiver +receiver/awss3receiver +receiver/awsxrayreceiver +receiver/azureblobreceiver +pkg/translator/azure +pkg/translator/azurelogs +receiver/azureeventhubreceiver +receiver/azuremonitorreceiver +receiver/bigipreceiver +receiver/carbonreceiver +receiver/chronyreceiver +receiver/cloudflarereceiver +receiver/cloudfoundryreceiver +internal/collectd +receiver/collectdreceiver +receiver/couchdbreceiver +receiver/datadogreceiver +receiver/elasticsearchreceiver +receiver/expvarreceiver +receiver/filestatsreceiver +receiver/flinkmetricsreceiver +receiver/fluentforwardreceiver +receiver/githubreceiver +receiver/googlecloudmonitoringreceiver +receiver/googlecloudpubsubreceiver +receiver/googlecloudspannerreceiver +receiver/haproxyreceiver +receiver/httpcheckreceiver +receiver/huaweicloudcesreceiver +receiver/iisreceiver +receiver/influxdbreceiver +receiver/jaegerreceiver +receiver/jmxreceiver +receiver/journaldreceiver +receiver/k8sclusterreceiver +receiver/k8seventsreceiver +receiver/k8sobjectsreceiver +receiver/kafkametricsreceiver +receiver/kafkareceiver +receiver/kubeletstatsreceiver +receiver/libhoneyreceiver +receiver/lokireceiver +receiver/memcachedreceiver +receiver/mongodbatlasreceiver +receiver/mongodbreceiver +receiver/mysqlreceiver +receiver/namedpipereceiver +receiver/nginxreceiver +receiver/nsxtreceiver +receiver/ntpreceiver +receiver/oracledbreceiver +receiver/otlpjsonfilereceiver +receiver/podmanreceiver +receiver/postgresqlreceiver +receiver/pulsarreceiver +receiver/purefareceiver +receiver/purefbreceiver +receiver/rabbitmqreceiver +receiver/receivercreator +receiver/redisreceiver +receiver/riakreceiver +receiver/sapmreceiver +receiver/signalfxreceiver +receiver/simpleprometheusreceiver +pkg/translator/skywalking +receiver/skywalkingreceiver +receiver/snmpreceiver +receiver/snowflakereceiver +receiver/solacereceiver +receiver/splunkenterprisereceiver +receiver/splunkhecreceiver +internal/sqlquery +receiver/sqlqueryreceiver +receiver/sqlserverreceiver +receiver/sshcheckreceiver +receiver/statsdreceiver +receiver/syslogreceiver +receiver/tcplogreceiver +receiver/udplogreceiver +receiver/vcenterreceiver +receiver/wavefrontreceiver +receiver/webhookeventreceiver +receiver/windowseventlogreceiver +receiver/windowsperfcountersreceiver +receiver/zookeeperreceiver +cmd/telemetrygen +cmd/telemetrygen/internal/e2etest +confmap/provider/aesprovider +connector/signaltometricsconnector +examples/demo/client +examples/demo/server +exporter/datadogexporter/integrationtest +testbed/mockdatasenders/mockdatadogagentexporter +testbed +exporter/elasticsearchexporter/integrationtest +exporter/kineticaexporter +extension/cgroupruntimeextension +. +internal/aws/xray/testdata/sampleapp +internal/aws/xray/testdata/sampleserver +internal/tools +processor/coralogixprocessor +processor/logstransformprocessor +processor/schemaprocessor +receiver/awscloudwatchmetricsreceiver +receiver/netflowreceiver +receiver/osqueryreceiver +receiver/prometheusremotewritereceiver +receiver/saphanareceiver +receiver/simpleprometheusreceiver/examples/federation/prom-counter +receiver/systemdreceiver +receiver/tlscheckreceiver \ No newline at end of file diff --git a/internal/tools/go.mod b/internal/tools/go.mod index 9bdc7b0dfe35..19632b70f6a8 100644 --- a/internal/tools/go.mod +++ b/internal/tools/go.mod @@ -13,7 +13,7 @@ require ( github.com/jstemmer/go-junit-report v1.0.0 go.opentelemetry.io/build-tools/checkfile v0.15.0 go.opentelemetry.io/build-tools/chloggen v0.15.0 - go.opentelemetry.io/build-tools/crosslink v0.15.0 + go.opentelemetry.io/build-tools/crosslink v0.16.0 go.opentelemetry.io/build-tools/issuegenerator v0.15.0 go.opentelemetry.io/build-tools/multimod v0.15.0 go.opentelemetry.io/collector/cmd/builder v0.117.0 @@ -233,7 +233,7 @@ require ( gitlab.com/bosi/decorder v0.4.2 // indirect go-simpler.org/musttag v0.13.0 // indirect go-simpler.org/sloglint v0.7.2 // indirect - go.opentelemetry.io/build-tools v0.15.0 // indirect + go.opentelemetry.io/build-tools v0.16.0 // indirect go.opentelemetry.io/collector/component v0.117.0 // indirect go.opentelemetry.io/collector/config/configtelemetry v0.117.0 // indirect go.opentelemetry.io/collector/confmap v1.23.0 // indirect diff --git a/internal/tools/go.sum b/internal/tools/go.sum index a744c63f9fde..11dbc7064d38 100644 --- a/internal/tools/go.sum +++ b/internal/tools/go.sum @@ -564,14 +564,14 @@ go-simpler.org/musttag v0.13.0 h1:Q/YAW0AHvaoaIbsPj3bvEI5/QFP7w696IMUpnKXQfCE= go-simpler.org/musttag v0.13.0/go.mod h1:FTzIGeK6OkKlUDVpj0iQUXZLUO1Js9+mvykDQy9C5yM= go-simpler.org/sloglint v0.7.2 h1:Wc9Em/Zeuu7JYpl+oKoYOsQSy2X560aVueCW/m6IijY= go-simpler.org/sloglint v0.7.2/go.mod h1:US+9C80ppl7VsThQclkM7BkCHQAzuz8kHLsW3ppuluo= -go.opentelemetry.io/build-tools v0.15.0 h1:SJFD+MSKKrSIP0oujmmY/zRi8TgVFzUc1080nIBmRzA= -go.opentelemetry.io/build-tools v0.15.0/go.mod h1:xyjzzjE7WEtBPVqZ0BwC8RxbGGW3DkD33YFgVLkvOUs= +go.opentelemetry.io/build-tools v0.16.0 h1:KxKRH+jOSNbRDRESkibfpaKmaVb1GsXhf0pQfPZE5zI= +go.opentelemetry.io/build-tools v0.16.0/go.mod h1:ZhuNyO/aAkGEFTfNhH7Nhv7fIWpxIOp8t7XshpPWiOU= go.opentelemetry.io/build-tools/checkfile v0.15.0 h1:C4qF0t+CMgkNdj125SKwpjjX6RG+NW75KdF9KigbaXk= go.opentelemetry.io/build-tools/checkfile v0.15.0/go.mod h1:UbB2iej3/BOBeK+796FGUSP1Yu6ppq2SoklGBspQp/E= go.opentelemetry.io/build-tools/chloggen v0.15.0 h1:G5UeYUgP6x4QXie0yNs/6TjK9nCuuVXgXeDWE9/cxQQ= go.opentelemetry.io/build-tools/chloggen v0.15.0/go.mod h1:oovDPiOQS4iruTVH469/68hEYjWC48c8u+qJpWJc8Eg= -go.opentelemetry.io/build-tools/crosslink v0.15.0 h1:cGwaVTtYi4wUQrQss8i9qmSoE9x7JXj9ou3JNMIe0nw= -go.opentelemetry.io/build-tools/crosslink v0.15.0/go.mod h1:BB5bv1xmtugy4lL9IWE9zNbpwtRwoFRdM8JmusHs3xw= +go.opentelemetry.io/build-tools/crosslink v0.16.0 h1:7Y5QPt5TR3qpiW5bwIOnsCJdt1yjZSFDPKtjt2g2zHw= +go.opentelemetry.io/build-tools/crosslink v0.16.0/go.mod h1:xogE6iWmt53bsDazb81dQrZw9TQ30+9hc4D8QfVG9aA= go.opentelemetry.io/build-tools/issuegenerator v0.15.0 h1:M2cnoXKf0yRmZ7SO2mOYYpiKtOWZyFZnnAHzQhgFeIw= go.opentelemetry.io/build-tools/issuegenerator v0.15.0/go.mod h1:GW53mhELVGByYf/Z6K3I4Ll37osqGY7w/r9mmu9VG1g= go.opentelemetry.io/build-tools/multimod v0.15.0 h1:mF4+7rf0uW06VhentAI9puZZoTKa9I9s7IXGgLSTD+E= From 2c141764880201532ae4293884e7acb9fb89e54d Mon Sep 17 00:00:00 2001 From: Tyler Helmuth <12352919+TylerHelmuth@users.noreply.github.com> Date: Mon, 13 Jan 2025 10:47:53 -0700 Subject: [PATCH 03/13] Revert "[chore] Use `crosslink tidylist` in `make gotidy` (#37142)" (#37173) Reverts https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/37142 as something with this change is not working as expected. Reverting for now to unblock PRs. See https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/37170 and https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/37172 --- .github/workflows/build-and-test.yml | 4 - Makefile | 15 +- internal/tidylist/allow-circular.txt | 12 -- internal/tidylist/tidylist.txt | 296 --------------------------- internal/tools/go.mod | 4 +- internal/tools/go.sum | 8 +- 6 files changed, 7 insertions(+), 332 deletions(-) delete mode 100644 internal/tidylist/allow-circular.txt delete mode 100644 internal/tidylist/tidylist.txt diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index cf6d1d2d4302..d830458da194 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -221,10 +221,6 @@ jobs: run: | make crosslink git diff --exit-code || (echo 'Replace statements are out of date, please run "make crosslink" and commit the changes in this PR.' && exit 1) - - name: tidylist - run: | - make tidylist - git diff --exit-code || (echo 'Tidylist is out of date, please run "make tidylist" and commit the changes in this PR.' && exit 1) - name: Check for go mod dependency changes run: | make gotidy diff --git a/Makefile b/Makefile index 439d8eb1671d..341c909dbfc3 100644 --- a/Makefile +++ b/Makefile @@ -115,22 +115,9 @@ stability-tests: otelcontribcol gogci: $(MAKE) $(FOR_GROUP_TARGET) TARGET="gci" -# `internal/tidylist/tidylist.txt` lists modules in topological order, to ensure `go mod tidy` converges. -# `make tidylist` will update this list. -# We exclude otelcontribcol and oteltestbedcol as those modules are not gitted and may not be present. -.PHONY: tidylist -tidylist: $(CROSSLINK) - cd internal/tidylist && \ - $(CROSSLINK) tidylist --validate --allow-circular allow-circular.txt tidylist.txt && \ - sed -i.bak -E '/cmd\/otel(contrib|testbed)col/d' tidylist.txt && \ - rm tidylist.txt.bak - .PHONY: gotidy gotidy: - @for mod in $$(cat internal/tidylist/tidylist.txt); do \ - echo "Tidying $$mod"; \ - (cd $$mod && rm -rf go.sum && $(GOCMD) mod tidy -compat=1.22.0) || exit $?; \ - done + $(MAKE) $(FOR_GROUP_TARGET) TARGET="tidy" .PHONY: remove-toolchain remove-toolchain: diff --git a/internal/tidylist/allow-circular.txt b/internal/tidylist/allow-circular.txt deleted file mode 100644 index 26b23b684f53..000000000000 --- a/internal/tidylist/allow-circular.txt +++ /dev/null @@ -1,12 +0,0 @@ -# This file lists modules that are known to have intra-repository circular dependencies. -# The `make tidylist` command will check against this list and error out if circular dependencies -# are accidentally added or removed. - -# exporter/datadog <-> connector/datadog -exporter/datadogexporter -connector/datadogconnector - -# receiver/otelarrow <-> internal/otelarrow <-> exporter/otelarrow -receiver/otelarrowreceiver -exporter/otelarrowexporter -internal/otelarrow diff --git a/internal/tidylist/tidylist.txt b/internal/tidylist/tidylist.txt deleted file mode 100644 index 7fdc798f68df..000000000000 --- a/internal/tidylist/tidylist.txt +++ /dev/null @@ -1,296 +0,0 @@ -cmd/githubgen -cmd/opampsupervisor -confmap/provider/s3provider -confmap/provider/secretsmanagerprovider -pkg/pdatautil -pkg/golden -pkg/pdatatest -internal/coreinternal -pkg/ottl -internal/filter -connector/countconnector -internal/common -internal/aws/ecsutil -internal/k8sconfig -internal/metadataproviders -pkg/resourcetotelemetry -internal/k8stest -processor/k8sattributesprocessor -pkg/sampling -processor/probabilisticsamplerprocessor -processor/resourcedetectionprocessor -internal/pdatautil -processor/transformprocessor -internal/docker -receiver/dockerstatsreceiver -extension/storage -pkg/stanza -receiver/filelogreceiver -pkg/experimentalmetricmetadata -receiver/hostmetricsreceiver -pkg/translator/prometheus -pkg/translator/prometheusremotewrite -exporter/prometheusremotewriteexporter -receiver/prometheusreceiver -pkg/datadog -processor/tailsamplingprocessor -exporter/datadogexporter -connector/datadogconnector -exporter/datadogexporter -connector/exceptionsconnector -connector/failoverconnector -connector/grafanacloudconnector -connector/otlpjsonconnector -connector/roundrobinconnector -connector/routingconnector -connector/servicegraphconnector -connector/spanmetricsconnector -connector/sumconnector -exporter/alertmanagerexporter -exporter/alibabacloudlogserviceexporter -internal/aws/awsutil -internal/aws/cwlogs -exporter/awscloudwatchlogsexporter -internal/aws/metrics -exporter/awsemfexporter -pkg/translator/jaeger -pkg/translator/zipkin -exporter/awskinesisexporter -exporter/awss3exporter -internal/aws/xray -exporter/awsxrayexporter -exporter/azuredataexplorerexporter -exporter/azuremonitorexporter -exporter/carbonexporter -exporter/cassandraexporter -exporter/clickhouseexporter -exporter/coralogixexporter -exporter/datasetexporter -exporter/dorisexporter -exporter/elasticsearchexporter -extension/encoding -extension/encoding/otlpencodingextension -internal/sharedcomponent -exporter/fileexporter -exporter/googlecloudexporter -exporter/googlecloudpubsubexporter -exporter/googlemanagedprometheusexporter -exporter/honeycombmarkerexporter -exporter/influxdbexporter -internal/kafka -pkg/batchpersignal -pkg/kafka/topic -exporter/kafkaexporter -internal/exp/metrics -exporter/loadbalancingexporter -exporter/logicmonitorexporter -exporter/logzioexporter -pkg/translator/loki -exporter/lokiexporter -exporter/mezmoexporter -pkg/translator/opencensus -receiver/opencensusreceiver -exporter/opencensusexporter -exporter/opensearchexporter -internal/grpcutil -receiver/otelarrowreceiver -internal/otelarrow -exporter/otelarrowexporter -receiver/otelarrowreceiver -internal/otelarrow -exporter/otelarrowexporter -receiver/otelarrowreceiver -exporter/prometheusexporter -exporter/pulsarexporter -internal/rabbitmq -exporter/rabbitmqexporter -internal/splunk -pkg/batchperresourceattr -exporter/sapmexporter -exporter/sentryexporter -pkg/translator/signalfx -exporter/signalfxexporter -exporter/splunkhecexporter -extension/sumologicextension -exporter/sumologicexporter -exporter/syslogexporter -exporter/tencentcloudlogserviceexporter -receiver/zipkinreceiver -exporter/zipkinexporter -extension/ackextension -extension/asapauthextension -internal/aws/proxy -extension/awsproxy -extension/basicauthextension -extension/bearertokenauthextension -extension/encoding/avrologencodingextension -extension/encoding/jaegerencodingextension -extension/encoding/jsonlogencodingextension -extension/encoding/textencodingextension -extension/encoding/zipkinencodingextension -extension/googleclientauthextension -extension/headerssetterextension -extension/healthcheckextension -pkg/status -extension/healthcheckv2extension -extension/httpforwarderextension -extension/jaegerremotesampling -extension/oauth2clientauthextension -extension/observer -extension/observer/cfgardenobserver -extension/observer/dockerobserver -extension/observer/ecsobserver -extension/observer/ecstaskobserver -extension/observer/hostobserver -extension/observer/k8sobserver -extension/oidcauthextension -extension/opampcustommessages -extension/opampextension -extension/pprofextension -extension/remotetapextension -extension/sigv4authextension -extension/solarwindsapmsettingsextension -extension/storage/dbstorage -extension/storage/filestorage -extension/storage/redisstorageextension -processor/attributesprocessor -processor/cumulativetodeltaprocessor -processor/deltatocumulativeprocessor -processor/deltatorateprocessor -processor/filterprocessor -processor/geoipprocessor -processor/groupbyattrsprocessor -processor/groupbytraceprocessor -processor/intervalprocessor -processor/logdedupprocessor -processor/metricsgenerationprocessor -processor/metricstransformprocessor -processor/redactionprocessor -processor/remotetapprocessor -processor/resourceprocessor -processor/routingprocessor -processor/spanprocessor -processor/sumologicprocessor -pkg/winperfcounters -receiver/activedirectorydsreceiver -receiver/aerospikereceiver -receiver/apachereceiver -receiver/apachesparkreceiver -receiver/awscloudwatchreceiver -internal/aws/containerinsight -internal/aws/k8s -internal/kubelet -receiver/awscontainerinsightreceiver -receiver/awsecscontainermetricsreceiver -receiver/awsfirehosereceiver -receiver/awss3receiver -receiver/awsxrayreceiver -receiver/azureblobreceiver -pkg/translator/azure -pkg/translator/azurelogs -receiver/azureeventhubreceiver -receiver/azuremonitorreceiver -receiver/bigipreceiver -receiver/carbonreceiver -receiver/chronyreceiver -receiver/cloudflarereceiver -receiver/cloudfoundryreceiver -internal/collectd -receiver/collectdreceiver -receiver/couchdbreceiver -receiver/datadogreceiver -receiver/elasticsearchreceiver -receiver/expvarreceiver -receiver/filestatsreceiver -receiver/flinkmetricsreceiver -receiver/fluentforwardreceiver -receiver/githubreceiver -receiver/googlecloudmonitoringreceiver -receiver/googlecloudpubsubreceiver -receiver/googlecloudspannerreceiver -receiver/haproxyreceiver -receiver/httpcheckreceiver -receiver/huaweicloudcesreceiver -receiver/iisreceiver -receiver/influxdbreceiver -receiver/jaegerreceiver -receiver/jmxreceiver -receiver/journaldreceiver -receiver/k8sclusterreceiver -receiver/k8seventsreceiver -receiver/k8sobjectsreceiver -receiver/kafkametricsreceiver -receiver/kafkareceiver -receiver/kubeletstatsreceiver -receiver/libhoneyreceiver -receiver/lokireceiver -receiver/memcachedreceiver -receiver/mongodbatlasreceiver -receiver/mongodbreceiver -receiver/mysqlreceiver -receiver/namedpipereceiver -receiver/nginxreceiver -receiver/nsxtreceiver -receiver/ntpreceiver -receiver/oracledbreceiver -receiver/otlpjsonfilereceiver -receiver/podmanreceiver -receiver/postgresqlreceiver -receiver/pulsarreceiver -receiver/purefareceiver -receiver/purefbreceiver -receiver/rabbitmqreceiver -receiver/receivercreator -receiver/redisreceiver -receiver/riakreceiver -receiver/sapmreceiver -receiver/signalfxreceiver -receiver/simpleprometheusreceiver -pkg/translator/skywalking -receiver/skywalkingreceiver -receiver/snmpreceiver -receiver/snowflakereceiver -receiver/solacereceiver -receiver/splunkenterprisereceiver -receiver/splunkhecreceiver -internal/sqlquery -receiver/sqlqueryreceiver -receiver/sqlserverreceiver -receiver/sshcheckreceiver -receiver/statsdreceiver -receiver/syslogreceiver -receiver/tcplogreceiver -receiver/udplogreceiver -receiver/vcenterreceiver -receiver/wavefrontreceiver -receiver/webhookeventreceiver -receiver/windowseventlogreceiver -receiver/windowsperfcountersreceiver -receiver/zookeeperreceiver -cmd/telemetrygen -cmd/telemetrygen/internal/e2etest -confmap/provider/aesprovider -connector/signaltometricsconnector -examples/demo/client -examples/demo/server -exporter/datadogexporter/integrationtest -testbed/mockdatasenders/mockdatadogagentexporter -testbed -exporter/elasticsearchexporter/integrationtest -exporter/kineticaexporter -extension/cgroupruntimeextension -. -internal/aws/xray/testdata/sampleapp -internal/aws/xray/testdata/sampleserver -internal/tools -processor/coralogixprocessor -processor/logstransformprocessor -processor/schemaprocessor -receiver/awscloudwatchmetricsreceiver -receiver/netflowreceiver -receiver/osqueryreceiver -receiver/prometheusremotewritereceiver -receiver/saphanareceiver -receiver/simpleprometheusreceiver/examples/federation/prom-counter -receiver/systemdreceiver -receiver/tlscheckreceiver \ No newline at end of file diff --git a/internal/tools/go.mod b/internal/tools/go.mod index 19632b70f6a8..9bdc7b0dfe35 100644 --- a/internal/tools/go.mod +++ b/internal/tools/go.mod @@ -13,7 +13,7 @@ require ( github.com/jstemmer/go-junit-report v1.0.0 go.opentelemetry.io/build-tools/checkfile v0.15.0 go.opentelemetry.io/build-tools/chloggen v0.15.0 - go.opentelemetry.io/build-tools/crosslink v0.16.0 + go.opentelemetry.io/build-tools/crosslink v0.15.0 go.opentelemetry.io/build-tools/issuegenerator v0.15.0 go.opentelemetry.io/build-tools/multimod v0.15.0 go.opentelemetry.io/collector/cmd/builder v0.117.0 @@ -233,7 +233,7 @@ require ( gitlab.com/bosi/decorder v0.4.2 // indirect go-simpler.org/musttag v0.13.0 // indirect go-simpler.org/sloglint v0.7.2 // indirect - go.opentelemetry.io/build-tools v0.16.0 // indirect + go.opentelemetry.io/build-tools v0.15.0 // indirect go.opentelemetry.io/collector/component v0.117.0 // indirect go.opentelemetry.io/collector/config/configtelemetry v0.117.0 // indirect go.opentelemetry.io/collector/confmap v1.23.0 // indirect diff --git a/internal/tools/go.sum b/internal/tools/go.sum index 11dbc7064d38..a744c63f9fde 100644 --- a/internal/tools/go.sum +++ b/internal/tools/go.sum @@ -564,14 +564,14 @@ go-simpler.org/musttag v0.13.0 h1:Q/YAW0AHvaoaIbsPj3bvEI5/QFP7w696IMUpnKXQfCE= go-simpler.org/musttag v0.13.0/go.mod h1:FTzIGeK6OkKlUDVpj0iQUXZLUO1Js9+mvykDQy9C5yM= go-simpler.org/sloglint v0.7.2 h1:Wc9Em/Zeuu7JYpl+oKoYOsQSy2X560aVueCW/m6IijY= go-simpler.org/sloglint v0.7.2/go.mod h1:US+9C80ppl7VsThQclkM7BkCHQAzuz8kHLsW3ppuluo= -go.opentelemetry.io/build-tools v0.16.0 h1:KxKRH+jOSNbRDRESkibfpaKmaVb1GsXhf0pQfPZE5zI= -go.opentelemetry.io/build-tools v0.16.0/go.mod h1:ZhuNyO/aAkGEFTfNhH7Nhv7fIWpxIOp8t7XshpPWiOU= +go.opentelemetry.io/build-tools v0.15.0 h1:SJFD+MSKKrSIP0oujmmY/zRi8TgVFzUc1080nIBmRzA= +go.opentelemetry.io/build-tools v0.15.0/go.mod h1:xyjzzjE7WEtBPVqZ0BwC8RxbGGW3DkD33YFgVLkvOUs= go.opentelemetry.io/build-tools/checkfile v0.15.0 h1:C4qF0t+CMgkNdj125SKwpjjX6RG+NW75KdF9KigbaXk= go.opentelemetry.io/build-tools/checkfile v0.15.0/go.mod h1:UbB2iej3/BOBeK+796FGUSP1Yu6ppq2SoklGBspQp/E= go.opentelemetry.io/build-tools/chloggen v0.15.0 h1:G5UeYUgP6x4QXie0yNs/6TjK9nCuuVXgXeDWE9/cxQQ= go.opentelemetry.io/build-tools/chloggen v0.15.0/go.mod h1:oovDPiOQS4iruTVH469/68hEYjWC48c8u+qJpWJc8Eg= -go.opentelemetry.io/build-tools/crosslink v0.16.0 h1:7Y5QPt5TR3qpiW5bwIOnsCJdt1yjZSFDPKtjt2g2zHw= -go.opentelemetry.io/build-tools/crosslink v0.16.0/go.mod h1:xogE6iWmt53bsDazb81dQrZw9TQ30+9hc4D8QfVG9aA= +go.opentelemetry.io/build-tools/crosslink v0.15.0 h1:cGwaVTtYi4wUQrQss8i9qmSoE9x7JXj9ou3JNMIe0nw= +go.opentelemetry.io/build-tools/crosslink v0.15.0/go.mod h1:BB5bv1xmtugy4lL9IWE9zNbpwtRwoFRdM8JmusHs3xw= go.opentelemetry.io/build-tools/issuegenerator v0.15.0 h1:M2cnoXKf0yRmZ7SO2mOYYpiKtOWZyFZnnAHzQhgFeIw= go.opentelemetry.io/build-tools/issuegenerator v0.15.0/go.mod h1:GW53mhELVGByYf/Z6K3I4Ll37osqGY7w/r9mmu9VG1g= go.opentelemetry.io/build-tools/multimod v0.15.0 h1:mF4+7rf0uW06VhentAI9puZZoTKa9I9s7IXGgLSTD+E= From c7a6d579b61b78e12992a4b032180b22d4ad3a89 Mon Sep 17 00:00:00 2001 From: Dmitrii Anoshin <anoshindx@gmail.com> Date: Mon, 13 Jan 2025 10:09:07 -0800 Subject: [PATCH 04/13] [chore] Remove invalid fields from update-otel GH actions (#37151) I took them from another OTel project and added them in https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/37149, but the values are invalid. A multiline YAML flag is missing. I'm removing them for now since I don't think they are necessary. --- .github/workflows/update-otel.yaml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/update-otel.yaml b/.github/workflows/update-otel.yaml index 81f433cb3076..76cf8355db4a 100644 --- a/.github/workflows/update-otel.yaml +++ b/.github/workflows/update-otel.yaml @@ -31,12 +31,6 @@ jobs: branch: opentelemetrybot/update-otel path: opentelemetry-collector-contrib base: main - author: - opentelemetrybot - <107717825+opentelemetrybot@users.noreply.github.com> - committer: - opentelemetrybot - <107717825+opentelemetrybot@users.noreply.github.com> token: ${{ secrets.OPENTELEMETRYBOT_GITHUB_TOKEN }} commit-message: [chore] Update to latest opentelemetry-collector release. title: "[chore] Update to latest opentelemetry-collector" From 7e6b19af9457ae09721b24a4ecae17113941e774 Mon Sep 17 00:00:00 2001 From: Edmo Vamerlatti Costa <11836452+edmocosta@users.noreply.github.com> Date: Mon, 13 Jan 2025 19:35:55 +0100 Subject: [PATCH 05/13] [pkg/ottl] Improve errors for invalid cache access and add option to set context's cache value (#37166) <!--Ex. Fixing a bug - Describe the bug and how this fixes the issue. Ex. Adding a feature - Explain what this achieves.--> #### Description - Improved error message for higher context `cache` access. Now when users try to configure statements like `set(log.attributes["log_key"], resource.cache["log_key"])`, they will see an error message similar to: ```diff + access to cache must be be performed using the same statement's context, please replace "resource.cache[log_key]" with "log.cache[log_key]"` - invalid argument at position 1: segment "cache" from path "resource.cache[log_key]" is not a valid path nor a valid OTTL keyword for the Resource context - review https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/pkg/ottl/contexts/ottlresource to see all valid paths ``` - Added the `WithCache` option to all OTTL transformation contexts. This change allows API consumer to set the initial cache value or share it among different contexts/executions. <!-- Issue number (e.g. #1234) or full URL to issue, if applicable. --> #### Link to tracking issue Split of https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/36888 <!--Describe what testing was performed and which tests were added.--> #### Testing Unit tests <!--Please delete paragraphs that you did not use before submitting.--> --- .chloggen/ottl-cache-changes.yaml | 27 +++++++++++++++++++ pkg/ottl/contexts/internal/errors.go | 10 ++++++- pkg/ottl/contexts/internal/path.go | 4 +++ pkg/ottl/contexts/internal/resource.go | 4 ++- pkg/ottl/contexts/internal/resource_test.go | 20 +++++++++++++- pkg/ottl/contexts/internal/scope.go | 4 ++- pkg/ottl/contexts/internal/scope_test.go | 20 +++++++++++++- pkg/ottl/contexts/internal/span.go | 4 ++- pkg/ottl/contexts/internal/span_test.go | 20 +++++++++++++- pkg/ottl/contexts/ottldatapoint/datapoint.go | 23 +++++++++++++--- .../contexts/ottldatapoint/datapoint_test.go | 18 +++++++++++++ pkg/ottl/contexts/ottllog/log.go | 23 +++++++++++++--- pkg/ottl/contexts/ottllog/log_test.go | 16 +++++++++++ pkg/ottl/contexts/ottlmetric/metrics.go | 23 +++++++++++++--- pkg/ottl/contexts/ottlmetric/metrics_test.go | 17 ++++++++++++ pkg/ottl/contexts/ottlresource/resource.go | 21 ++++++++++++--- .../contexts/ottlresource/resource_test.go | 13 +++++++++ pkg/ottl/contexts/ottlscope/scope.go | 23 +++++++++++++--- pkg/ottl/contexts/ottlscope/scope_test.go | 15 +++++++++++ pkg/ottl/contexts/ottlspan/span.go | 25 +++++++++++++---- .../contexts/ottlspanevent/span_events.go | 25 +++++++++++++---- 21 files changed, 319 insertions(+), 36 deletions(-) create mode 100644 .chloggen/ottl-cache-changes.yaml diff --git a/.chloggen/ottl-cache-changes.yaml b/.chloggen/ottl-cache-changes.yaml new file mode 100644 index 000000000000..f8d650a358c8 --- /dev/null +++ b/.chloggen/ottl-cache-changes.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: pkg/ottl + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Enhanced error messages for invalid cache access and introduced options to configure their values within the OTTL contexts. + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [29017] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [user, api] diff --git a/pkg/ottl/contexts/internal/errors.go b/pkg/ottl/contexts/internal/errors.go index 7ccc931e2ba4..eef0e8aca99b 100644 --- a/pkg/ottl/contexts/internal/errors.go +++ b/pkg/ottl/contexts/internal/errors.go @@ -3,7 +3,10 @@ package internal // import "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/internal" -import "fmt" +import ( + "fmt" + "strings" +) const ( DefaultErrorMessage = "segment %q from path %q is not a valid path nor a valid OTTL keyword for the %v context - review %v to see all valid paths" @@ -20,3 +23,8 @@ const ( func FormatDefaultErrorMessage(pathSegment, fullPath, context, ref string) error { return fmt.Errorf(DefaultErrorMessage, pathSegment, fullPath, context, ref) } + +func FormatCacheErrorMessage(lowerContext, pathContext, fullPath string) error { + pathSuggestion := strings.Replace(fullPath, pathContext+".", lowerContext+".", 1) + return fmt.Errorf(`access to cache must be performed using the same statement's context, please replace "%s" with "%s"`, fullPath, pathSuggestion) +} diff --git a/pkg/ottl/contexts/internal/path.go b/pkg/ottl/contexts/internal/path.go index ebbb12a6eaae..753419354629 100644 --- a/pkg/ottl/contexts/internal/path.go +++ b/pkg/ottl/contexts/internal/path.go @@ -16,6 +16,7 @@ type TestPath[K any] struct { N string KeySlice []ottl.Key[K] NextPath *TestPath[K] + FullPath string } func (p *TestPath[K]) Name() string { @@ -38,6 +39,9 @@ func (p *TestPath[K]) Keys() []ottl.Key[K] { } func (p *TestPath[K]) String() string { + if p.FullPath != "" { + return p.FullPath + } return p.N } diff --git a/pkg/ottl/contexts/internal/resource.go b/pkg/ottl/contexts/internal/resource.go index a3ae9d149f3f..7b454d50ae8c 100644 --- a/pkg/ottl/contexts/internal/resource.go +++ b/pkg/ottl/contexts/internal/resource.go @@ -20,7 +20,7 @@ type ResourceContext interface { GetResourceSchemaURLItem() SchemaURLItem } -func ResourcePathGetSetter[K ResourceContext](path ottl.Path[K]) (ottl.GetSetter[K], error) { +func ResourcePathGetSetter[K ResourceContext](lowerContext string, path ottl.Path[K]) (ottl.GetSetter[K], error) { if path == nil { return nil, FormatDefaultErrorMessage(ResourceContextName, ResourceContextName, "Resource", ResourceContextRef) } @@ -34,6 +34,8 @@ func ResourcePathGetSetter[K ResourceContext](path ottl.Path[K]) (ottl.GetSetter return accessResourceDroppedAttributesCount[K](), nil case "schema_url": return accessResourceSchemaURLItem[K](), nil + case "cache": + return nil, FormatCacheErrorMessage(lowerContext, path.Context(), path.String()) default: return nil, FormatDefaultErrorMessage(path.Name(), path.String(), "Resource", ResourceContextRef) } diff --git a/pkg/ottl/contexts/internal/resource_test.go b/pkg/ottl/contexts/internal/resource_test.go index f1b251a3e22e..88cee0e0e059 100644 --- a/pkg/ottl/contexts/internal/resource_test.go +++ b/pkg/ottl/contexts/internal/resource_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/pdata/pcommon" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl" @@ -311,7 +312,7 @@ func TestResourcePathGetSetter(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - accessor, err := ResourcePathGetSetter[*resourceContext](tt.path) + accessor, err := ResourcePathGetSetter[*resourceContext](tt.path.Context(), tt.path) assert.NoError(t, err) resource := createResource() @@ -331,6 +332,23 @@ func TestResourcePathGetSetter(t *testing.T) { } } +func TestResourcePathGetSetterCacheAccessError(t *testing.T) { + path := &TestPath[*resourceContext]{ + N: "cache", + C: "resource", + KeySlice: []ottl.Key[*resourceContext]{ + &TestKey[*resourceContext]{ + S: ottltest.Strp("key"), + }, + }, + FullPath: "resource.cache[key]", + } + + _, err := ResourcePathGetSetter[*resourceContext]("log", path) + require.Error(t, err) + require.Contains(t, err.Error(), `replace "resource.cache[key]" with "log.cache[key]"`) +} + func createResource() pcommon.Resource { resource := pcommon.NewResource() resource.Attributes().PutStr("str", "val") diff --git a/pkg/ottl/contexts/internal/scope.go b/pkg/ottl/contexts/internal/scope.go index 4fabf9ee7d89..35a40a3869c3 100644 --- a/pkg/ottl/contexts/internal/scope.go +++ b/pkg/ottl/contexts/internal/scope.go @@ -21,7 +21,7 @@ type InstrumentationScopeContext interface { GetScopeSchemaURLItem() SchemaURLItem } -func ScopePathGetSetter[K InstrumentationScopeContext](path ottl.Path[K]) (ottl.GetSetter[K], error) { +func ScopePathGetSetter[K InstrumentationScopeContext](lowerContext string, path ottl.Path[K]) (ottl.GetSetter[K], error) { if path == nil { return nil, FormatDefaultErrorMessage(InstrumentationScopeContextName, InstrumentationScopeContextName, "Instrumentation Scope", InstrumentationScopeRef) } @@ -40,6 +40,8 @@ func ScopePathGetSetter[K InstrumentationScopeContext](path ottl.Path[K]) (ottl. return accessInstrumentationScopeDroppedAttributesCount[K](), nil case "schema_url": return accessInstrumentationScopeSchemaURLItem[K](), nil + case "cache": + return nil, FormatCacheErrorMessage(lowerContext, path.Context(), path.String()) default: return nil, FormatDefaultErrorMessage(path.Name(), path.String(), "Instrumentation Scope", InstrumentationScopeRef) } diff --git a/pkg/ottl/contexts/internal/scope_test.go b/pkg/ottl/contexts/internal/scope_test.go index 788924115bd5..66a4c3fa397a 100644 --- a/pkg/ottl/contexts/internal/scope_test.go +++ b/pkg/ottl/contexts/internal/scope_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/pdata/pcommon" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl" @@ -349,7 +350,7 @@ func TestScopePathGetSetter(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - accessor, err := ScopePathGetSetter[*instrumentationScopeContext](tt.path) + accessor, err := ScopePathGetSetter[*instrumentationScopeContext](tt.path.Context(), tt.path) assert.NoError(t, err) is := createInstrumentationScope() @@ -369,6 +370,23 @@ func TestScopePathGetSetter(t *testing.T) { } } +func TestScopePathGetSetterCacheAccessError(t *testing.T) { + path := &TestPath[*instrumentationScopeContext]{ + N: "cache", + C: "instrumentation_scope", + KeySlice: []ottl.Key[*instrumentationScopeContext]{ + &TestKey[*instrumentationScopeContext]{ + S: ottltest.Strp("key"), + }, + }, + FullPath: "instrumentation_scope.cache[key]", + } + + _, err := ScopePathGetSetter[*instrumentationScopeContext]("metric", path) + require.Error(t, err) + require.Contains(t, err.Error(), `replace "instrumentation_scope.cache[key]" with "metric.cache[key]"`) +} + func createInstrumentationScope() pcommon.InstrumentationScope { is := pcommon.NewInstrumentationScope() is.SetName("library") diff --git a/pkg/ottl/contexts/internal/span.go b/pkg/ottl/contexts/internal/span.go index 4669adb7b8fe..fc7ca27177ef 100644 --- a/pkg/ottl/contexts/internal/span.go +++ b/pkg/ottl/contexts/internal/span.go @@ -38,7 +38,7 @@ var SpanSymbolTable = map[ottl.EnumSymbol]ottl.Enum{ "STATUS_CODE_ERROR": ottl.Enum(ptrace.StatusCodeError), } -func SpanPathGetSetter[K SpanContext](path ottl.Path[K]) (ottl.GetSetter[K], error) { +func SpanPathGetSetter[K SpanContext](lowerContext string, path ottl.Path[K]) (ottl.GetSetter[K], error) { if path == nil { return nil, FormatDefaultErrorMessage(SpanContextName, SpanContextName, SpanContextNameDescription, SpanRef) } @@ -128,6 +128,8 @@ func SpanPathGetSetter[K SpanContext](path ottl.Path[K]) (ottl.GetSetter[K], err } } return accessStatus[K](), nil + case "cache": + return nil, FormatCacheErrorMessage(lowerContext, path.Context(), path.String()) default: return nil, FormatDefaultErrorMessage(path.Name(), path.String(), SpanContextNameDescription, SpanRef) } diff --git a/pkg/ottl/contexts/internal/span_test.go b/pkg/ottl/contexts/internal/span_test.go index 899d12493424..1345a3cfde5a 100644 --- a/pkg/ottl/contexts/internal/span_test.go +++ b/pkg/ottl/contexts/internal/span_test.go @@ -10,6 +10,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/ptrace" @@ -603,7 +604,7 @@ func TestSpanPathGetSetter(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - accessor, err := SpanPathGetSetter[*spanContext](tt.path) + accessor, err := SpanPathGetSetter[*spanContext](tt.path.Context(), tt.path) assert.NoError(t, err) span := createSpan() @@ -623,6 +624,23 @@ func TestSpanPathGetSetter(t *testing.T) { } } +func TestSpanPathGetSetterCacheAccessError(t *testing.T) { + path := &TestPath[*spanContext]{ + N: "cache", + C: "span", + KeySlice: []ottl.Key[*spanContext]{ + &TestKey[*spanContext]{ + S: ottltest.Strp("key"), + }, + }, + FullPath: "span.cache[key]", + } + + _, err := SpanPathGetSetter[*spanContext]("spanevent", path) + require.Error(t, err) + require.Contains(t, err.Error(), `replace "span.cache[key]" with "spanevent.cache[key]"`) +} + func createSpan() ptrace.Span { span := ptrace.NewSpan() span.SetTraceID(traceID) diff --git a/pkg/ottl/contexts/ottldatapoint/datapoint.go b/pkg/ottl/contexts/ottldatapoint/datapoint.go index d231c4210813..4f491c82caf7 100644 --- a/pkg/ottl/contexts/ottldatapoint/datapoint.go +++ b/pkg/ottl/contexts/ottldatapoint/datapoint.go @@ -64,8 +64,10 @@ func (tCtx TransformContext) MarshalLogObject(encoder zapcore.ObjectEncoder) err type Option func(*ottl.Parser[TransformContext]) -func NewTransformContext(dataPoint any, metric pmetric.Metric, metrics pmetric.MetricSlice, instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource, scopeMetrics pmetric.ScopeMetrics, resourceMetrics pmetric.ResourceMetrics) TransformContext { - return TransformContext{ +type TransformContextOption func(*TransformContext) + +func NewTransformContext(dataPoint any, metric pmetric.Metric, metrics pmetric.MetricSlice, instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource, scopeMetrics pmetric.ScopeMetrics, resourceMetrics pmetric.ResourceMetrics, options ...TransformContextOption) TransformContext { + tc := TransformContext{ dataPoint: dataPoint, metric: metric, metrics: metrics, @@ -75,6 +77,19 @@ func NewTransformContext(dataPoint any, metric pmetric.Metric, metrics pmetric.M scopeMetrics: scopeMetrics, resourceMetrics: resourceMetrics, } + for _, opt := range options { + opt(&tc) + } + return tc +} + +// Experimental: *NOTE* this option is subject to change or removal in the future. +func WithCache(cache *pcommon.Map) TransformContextOption { + return func(p *TransformContext) { + if cache != nil { + p.cache = *cache + } + } } func (tCtx TransformContext) GetDataPoint() any { @@ -289,9 +304,9 @@ func (pep *pathExpressionParser) parsePath(path ottl.Path[TransformContext]) (ot func (pep *pathExpressionParser) parseHigherContextPath(context string, path ottl.Path[TransformContext]) (ottl.GetSetter[TransformContext], error) { switch context { case internal.ResourceContextName: - return internal.ResourcePathGetSetter(path) + return internal.ResourcePathGetSetter(ContextName, path) case internal.InstrumentationScopeContextName: - return internal.ScopePathGetSetter(path) + return internal.ScopePathGetSetter(ContextName, path) case internal.MetricContextName: return internal.MetricPathGetSetter(path) default: diff --git a/pkg/ottl/contexts/ottldatapoint/datapoint_test.go b/pkg/ottl/contexts/ottldatapoint/datapoint_test.go index 604f008865ec..1877e904d08e 100644 --- a/pkg/ottl/contexts/ottldatapoint/datapoint_test.go +++ b/pkg/ottl/contexts/ottldatapoint/datapoint_test.go @@ -94,6 +94,24 @@ func Test_newPathGetSetter_Cache(t *testing.T) { } } +func Test_newPathGetSetter_WithCache(t *testing.T) { + cacheValue := pcommon.NewMap() + cacheValue.PutStr("test", "pass") + + ctx := NewTransformContext( + pmetric.NewNumberDataPoint(), + pmetric.NewMetric(), + pmetric.NewMetricSlice(), + pcommon.NewInstrumentationScope(), + pcommon.NewResource(), + pmetric.NewScopeMetrics(), + pmetric.NewResourceMetrics(), + WithCache(&cacheValue), + ) + + assert.Equal(t, cacheValue, ctx.getCache()) +} + func Test_newPathGetSetter_NumberDataPoint(t *testing.T) { refNumberDataPoint := createNumberDataPointTelemetry(pmetric.NumberDataPointValueTypeInt) diff --git a/pkg/ottl/contexts/ottllog/log.go b/pkg/ottl/contexts/ottllog/log.go index 4983e60ddc6c..99970f4a093b 100644 --- a/pkg/ottl/contexts/ottllog/log.go +++ b/pkg/ottl/contexts/ottllog/log.go @@ -71,8 +71,10 @@ func (tCtx TransformContext) MarshalLogObject(encoder zapcore.ObjectEncoder) err type Option func(*ottl.Parser[TransformContext]) -func NewTransformContext(logRecord plog.LogRecord, instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource, scopeLogs plog.ScopeLogs, resourceLogs plog.ResourceLogs) TransformContext { - return TransformContext{ +type TransformContextOption func(*TransformContext) + +func NewTransformContext(logRecord plog.LogRecord, instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource, scopeLogs plog.ScopeLogs, resourceLogs plog.ResourceLogs, options ...TransformContextOption) TransformContext { + tc := TransformContext{ logRecord: logRecord, instrumentationScope: instrumentationScope, resource: resource, @@ -80,6 +82,19 @@ func NewTransformContext(logRecord plog.LogRecord, instrumentationScope pcommon. scopeLogs: scopeLogs, resourceLogs: resourceLogs, } + for _, opt := range options { + opt(&tc) + } + return tc +} + +// Experimental: *NOTE* this option is subject to change or removal in the future. +func WithCache(cache *pcommon.Map) TransformContextOption { + return func(p *TransformContext) { + if cache != nil { + p.cache = *cache + } + } } func (tCtx TransformContext) GetLogRecord() plog.LogRecord { @@ -290,9 +305,9 @@ func (pep *pathExpressionParser) parsePath(path ottl.Path[TransformContext]) (ot func (pep *pathExpressionParser) parseHigherContextPath(context string, path ottl.Path[TransformContext]) (ottl.GetSetter[TransformContext], error) { switch context { case internal.ResourceContextName: - return internal.ResourcePathGetSetter(path) + return internal.ResourcePathGetSetter(ContextName, path) case internal.InstrumentationScopeContextName: - return internal.ScopePathGetSetter(path) + return internal.ScopePathGetSetter(ContextName, path) default: var fullPath string if path != nil { diff --git a/pkg/ottl/contexts/ottllog/log_test.go b/pkg/ottl/contexts/ottllog/log_test.go index 0e440052f1bd..1c2c141db5c4 100644 --- a/pkg/ottl/contexts/ottllog/log_test.go +++ b/pkg/ottl/contexts/ottllog/log_test.go @@ -694,6 +694,22 @@ func Test_newPathGetSetter_higherContextPath(t *testing.T) { } } +func Test_newPathGetSetter_WithCache(t *testing.T) { + cacheValue := pcommon.NewMap() + cacheValue.PutStr("test", "pass") + + ctx := NewTransformContext( + plog.NewLogRecord(), + pcommon.NewInstrumentationScope(), + pcommon.NewResource(), + plog.NewScopeLogs(), + plog.NewResourceLogs(), + WithCache(&cacheValue), + ) + + assert.Equal(t, cacheValue, ctx.getCache()) +} + func createTelemetry(bodyType string) (plog.LogRecord, pcommon.InstrumentationScope, pcommon.Resource) { log := plog.NewLogRecord() log.SetTimestamp(pcommon.NewTimestampFromTime(time.UnixMilli(100))) diff --git a/pkg/ottl/contexts/ottlmetric/metrics.go b/pkg/ottl/contexts/ottlmetric/metrics.go index b8c26bac2e9d..04b7b5218bf1 100644 --- a/pkg/ottl/contexts/ottlmetric/metrics.go +++ b/pkg/ottl/contexts/ottlmetric/metrics.go @@ -38,8 +38,10 @@ type TransformContext struct { type Option func(*ottl.Parser[TransformContext]) -func NewTransformContext(metric pmetric.Metric, metrics pmetric.MetricSlice, instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource, scopeMetrics pmetric.ScopeMetrics, resourceMetrics pmetric.ResourceMetrics) TransformContext { - return TransformContext{ +type TransformContextOption func(*TransformContext) + +func NewTransformContext(metric pmetric.Metric, metrics pmetric.MetricSlice, instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource, scopeMetrics pmetric.ScopeMetrics, resourceMetrics pmetric.ResourceMetrics, options ...TransformContextOption) TransformContext { + tc := TransformContext{ metric: metric, metrics: metrics, instrumentationScope: instrumentationScope, @@ -48,6 +50,19 @@ func NewTransformContext(metric pmetric.Metric, metrics pmetric.MetricSlice, ins scopeMetrics: scopeMetrics, resourceMetrics: resourceMetrics, } + for _, opt := range options { + opt(&tc) + } + return tc +} + +// Experimental: *NOTE* this option is subject to change or removal in the future. +func WithCache(cache *pcommon.Map) TransformContextOption { + return func(p *TransformContext) { + if cache != nil { + p.cache = *cache + } + } } func (tCtx TransformContext) GetMetric() pmetric.Metric { @@ -185,9 +200,9 @@ func (pep *pathExpressionParser) parsePath(path ottl.Path[TransformContext]) (ot func (pep *pathExpressionParser) parseHigherContextPath(context string, path ottl.Path[TransformContext]) (ottl.GetSetter[TransformContext], error) { switch context { case internal.ResourceContextName: - return internal.ResourcePathGetSetter(path) + return internal.ResourcePathGetSetter(ContextName, path) case internal.InstrumentationScopeContextName: - return internal.ScopePathGetSetter(path) + return internal.ScopePathGetSetter(ContextName, path) default: var fullPath string if path != nil { diff --git a/pkg/ottl/contexts/ottlmetric/metrics_test.go b/pkg/ottl/contexts/ottlmetric/metrics_test.go index c434ab5735e9..5eb8fb9f44ca 100644 --- a/pkg/ottl/contexts/ottlmetric/metrics_test.go +++ b/pkg/ottl/contexts/ottlmetric/metrics_test.go @@ -239,6 +239,23 @@ func Test_newPathGetSetter_higherContextPath(t *testing.T) { } } +func Test_newPathGetSetter_WithCache(t *testing.T) { + cacheValue := pcommon.NewMap() + cacheValue.PutStr("test", "pass") + + ctx := NewTransformContext( + pmetric.NewMetric(), + pmetric.NewMetricSlice(), + pcommon.NewInstrumentationScope(), + pcommon.NewResource(), + pmetric.NewScopeMetrics(), + pmetric.NewResourceMetrics(), + WithCache(&cacheValue), + ) + + assert.Equal(t, cacheValue, ctx.getCache()) +} + func createMetricTelemetry() pmetric.Metric { metric := pmetric.NewMetric() metric.SetName("name") diff --git a/pkg/ottl/contexts/ottlresource/resource.go b/pkg/ottl/contexts/ottlresource/resource.go index fef87f37b54e..6c44ec4d161f 100644 --- a/pkg/ottl/contexts/ottlresource/resource.go +++ b/pkg/ottl/contexts/ottlresource/resource.go @@ -41,12 +41,27 @@ func (tCtx TransformContext) MarshalLogObject(encoder zapcore.ObjectEncoder) err type Option func(*ottl.Parser[TransformContext]) -func NewTransformContext(resource pcommon.Resource, schemaURLItem internal.SchemaURLItem) TransformContext { - return TransformContext{ +type TransformContextOption func(*TransformContext) + +func NewTransformContext(resource pcommon.Resource, schemaURLItem internal.SchemaURLItem, options ...TransformContextOption) TransformContext { + tc := TransformContext{ resource: resource, cache: pcommon.NewMap(), schemaURLItem: schemaURLItem, } + for _, opt := range options { + opt(&tc) + } + return tc +} + +// Experimental: *NOTE* this option is subject to change or removal in the future. +func WithCache(cache *pcommon.Map) TransformContextOption { + return func(p *TransformContext) { + if cache != nil { + p.cache = *cache + } + } } func (tCtx TransformContext) GetResource() pcommon.Resource { @@ -144,7 +159,7 @@ func (pep *pathExpressionParser) parsePath(path ottl.Path[TransformContext]) (ot } return accessCacheKey(path.Keys()), nil default: - return internal.ResourcePathGetSetter[TransformContext](path) + return internal.ResourcePathGetSetter[TransformContext](ContextName, path) } } diff --git a/pkg/ottl/contexts/ottlresource/resource_test.go b/pkg/ottl/contexts/ottlresource/resource_test.go index f37bcdf90c80..e3d9f96c8299 100644 --- a/pkg/ottl/contexts/ottlresource/resource_test.go +++ b/pkg/ottl/contexts/ottlresource/resource_test.go @@ -398,6 +398,19 @@ func Test_newPathGetSetter(t *testing.T) { } } +func Test_newPathGetSetter_WithCache(t *testing.T) { + cacheValue := pcommon.NewMap() + cacheValue.PutStr("test", "pass") + + ctx := NewTransformContext( + pcommon.NewResource(), + pmetric.NewResourceMetrics(), + WithCache(&cacheValue), + ) + + assert.Equal(t, cacheValue, ctx.getCache()) +} + func createTelemetry() pcommon.Resource { resource := pcommon.NewResource() diff --git a/pkg/ottl/contexts/ottlscope/scope.go b/pkg/ottl/contexts/ottlscope/scope.go index 13f2c7a83e83..4ccc8f472d53 100644 --- a/pkg/ottl/contexts/ottlscope/scope.go +++ b/pkg/ottl/contexts/ottlscope/scope.go @@ -44,13 +44,28 @@ func (tCtx TransformContext) MarshalLogObject(encoder zapcore.ObjectEncoder) err type Option func(*ottl.Parser[TransformContext]) -func NewTransformContext(instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource, schemaURLItem internal.SchemaURLItem) TransformContext { - return TransformContext{ +type TransformContextOption func(*TransformContext) + +func NewTransformContext(instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource, schemaURLItem internal.SchemaURLItem, options ...TransformContextOption) TransformContext { + tc := TransformContext{ instrumentationScope: instrumentationScope, resource: resource, cache: pcommon.NewMap(), schemaURLItem: schemaURLItem, } + for _, opt := range options { + opt(&tc) + } + return tc +} + +// Experimental: *NOTE* this option is subject to change or removal in the future. +func WithCache(cache *pcommon.Map) TransformContextOption { + return func(p *TransformContext) { + if cache != nil { + p.cache = *cache + } + } } func (tCtx TransformContext) GetInstrumentationScope() pcommon.InstrumentationScope { @@ -164,14 +179,14 @@ func (pep *pathExpressionParser) parsePath(path ottl.Path[TransformContext]) (ot } return accessCacheKey(path.Keys()), nil default: - return internal.ScopePathGetSetter[TransformContext](path) + return internal.ScopePathGetSetter[TransformContext](ContextName, path) } } func (pep *pathExpressionParser) parseHigherContextPath(context string, path ottl.Path[TransformContext]) (ottl.GetSetter[TransformContext], error) { switch context { case internal.ResourceContextName: - return internal.ResourcePathGetSetter[TransformContext](path) + return internal.ResourcePathGetSetter[TransformContext](ContextName, path) default: var fullPath string if path != nil { diff --git a/pkg/ottl/contexts/ottlscope/scope_test.go b/pkg/ottl/contexts/ottlscope/scope_test.go index 4f4d8cf7c3b5..9b1103df5fc6 100644 --- a/pkg/ottl/contexts/ottlscope/scope_test.go +++ b/pkg/ottl/contexts/ottlscope/scope_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/plog" + "go.opentelemetry.io/collector/pdata/pmetric" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl" "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/contexts/internal" @@ -468,6 +469,20 @@ func Test_newPathGetSetter_higherContextPath(t *testing.T) { } } +func Test_newPathGetSetter_WithCache(t *testing.T) { + cacheValue := pcommon.NewMap() + cacheValue.PutStr("test", "pass") + + ctx := NewTransformContext( + pcommon.NewInstrumentationScope(), + pcommon.NewResource(), + pmetric.NewResourceMetrics(), + WithCache(&cacheValue), + ) + + assert.Equal(t, cacheValue, ctx.getCache()) +} + func createTelemetry() (pcommon.InstrumentationScope, pcommon.Resource) { is := pcommon.NewInstrumentationScope() is.SetName("library") diff --git a/pkg/ottl/contexts/ottlspan/span.go b/pkg/ottl/contexts/ottlspan/span.go index be11495e8dde..8f7b4c05f268 100644 --- a/pkg/ottl/contexts/ottlspan/span.go +++ b/pkg/ottl/contexts/ottlspan/span.go @@ -48,8 +48,10 @@ func (tCtx TransformContext) MarshalLogObject(encoder zapcore.ObjectEncoder) err type Option func(*ottl.Parser[TransformContext]) -func NewTransformContext(span ptrace.Span, instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource, scopeSpans ptrace.ScopeSpans, resourceSpans ptrace.ResourceSpans) TransformContext { - return TransformContext{ +type TransformContextOption func(*TransformContext) + +func NewTransformContext(span ptrace.Span, instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource, scopeSpans ptrace.ScopeSpans, resourceSpans ptrace.ResourceSpans, options ...TransformContextOption) TransformContext { + tc := TransformContext{ span: span, instrumentationScope: instrumentationScope, resource: resource, @@ -57,6 +59,19 @@ func NewTransformContext(span ptrace.Span, instrumentationScope pcommon.Instrume scopeSpans: scopeSpans, resourceSpans: resourceSpans, } + for _, opt := range options { + opt(&tc) + } + return tc +} + +// Experimental: *NOTE* this option is subject to change or removal in the future. +func WithCache(cache *pcommon.Map) TransformContextOption { + return func(p *TransformContext) { + if cache != nil { + p.cache = *cache + } + } } func (tCtx TransformContext) GetSpan() ptrace.Span { @@ -181,16 +196,16 @@ func (pep *pathExpressionParser) parsePath(path ottl.Path[TransformContext]) (ot } return accessCacheKey(path.Keys()), nil default: - return internal.SpanPathGetSetter[TransformContext](path) + return internal.SpanPathGetSetter[TransformContext](ContextName, path) } } func (pep *pathExpressionParser) parseHigherContextPath(context string, path ottl.Path[TransformContext]) (ottl.GetSetter[TransformContext], error) { switch context { case internal.ResourceContextName: - return internal.ResourcePathGetSetter[TransformContext](path) + return internal.ResourcePathGetSetter[TransformContext](ContextName, path) case internal.InstrumentationScopeContextName: - return internal.ScopePathGetSetter[TransformContext](path) + return internal.ScopePathGetSetter[TransformContext](ContextName, path) default: var fullPath string if path != nil { diff --git a/pkg/ottl/contexts/ottlspanevent/span_events.go b/pkg/ottl/contexts/ottlspanevent/span_events.go index 6d654b058004..839147639d6c 100644 --- a/pkg/ottl/contexts/ottlspanevent/span_events.go +++ b/pkg/ottl/contexts/ottlspanevent/span_events.go @@ -52,8 +52,10 @@ func (tCtx TransformContext) MarshalLogObject(encoder zapcore.ObjectEncoder) err type Option func(*ottl.Parser[TransformContext]) -func NewTransformContext(spanEvent ptrace.SpanEvent, span ptrace.Span, instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource, scopeSpans ptrace.ScopeSpans, resourceSpans ptrace.ResourceSpans) TransformContext { - return TransformContext{ +type TransformContextOption func(*TransformContext) + +func NewTransformContext(spanEvent ptrace.SpanEvent, span ptrace.Span, instrumentationScope pcommon.InstrumentationScope, resource pcommon.Resource, scopeSpans ptrace.ScopeSpans, resourceSpans ptrace.ResourceSpans, options ...TransformContextOption) TransformContext { + tc := TransformContext{ spanEvent: spanEvent, span: span, instrumentationScope: instrumentationScope, @@ -62,6 +64,19 @@ func NewTransformContext(spanEvent ptrace.SpanEvent, span ptrace.Span, instrumen scopeSpans: scopeSpans, resouceSpans: resourceSpans, } + for _, opt := range options { + opt(&tc) + } + return tc +} + +// Experimental: *NOTE* this option is subject to change or removal in the future. +func WithCache(cache *pcommon.Map) TransformContextOption { + return func(p *TransformContext) { + if cache != nil { + p.cache = *cache + } + } } func (tCtx TransformContext) GetSpanEvent() ptrace.SpanEvent { @@ -214,11 +229,11 @@ func (pep *pathExpressionParser) parsePath(path ottl.Path[TransformContext]) (ot func (pep *pathExpressionParser) parseHigherContextPath(context string, path ottl.Path[TransformContext]) (ottl.GetSetter[TransformContext], error) { switch context { case internal.ResourceContextName: - return internal.ResourcePathGetSetter(path) + return internal.ResourcePathGetSetter(ContextName, path) case internal.InstrumentationScopeContextName: - return internal.ScopePathGetSetter(path) + return internal.ScopePathGetSetter(ContextName, path) case internal.SpanContextName: - return internal.SpanPathGetSetter(path) + return internal.SpanPathGetSetter(ContextName, path) default: var fullPath string if path != nil { From 32d50f5eeb3501df4db80256caef8b31b8cdb970 Mon Sep 17 00:00:00 2001 From: Florian Bacher <florian.bacher@dynatrace.com> Date: Mon, 13 Jan 2025 19:43:15 +0100 Subject: [PATCH 06/13] [k8sattributesprocessor] Fall back to only container in pod in case no container name/id has been specified in the incoming resource attributes (#36844) <!--Ex. Fixing a bug - Describe the bug and how this fixes the issue. Ex. Adding a feature - Explain what this achieves.--> #### Description This PR adds a fallback logic to the processor by adding the container attributes for an incoming resource without the container name/id being explicitly set, provided the related pod only contains one container. If there are more than one container in a pod, the container name/id still have to be set explicitly. <!-- Issue number (e.g. #1234) or full URL to issue, if applicable. --> #### Link to tracking issue Fixes #34189 <!--Describe what testing was performed and which tests were added.--> #### Testing Added unit tests <!--Describe the documentation added.--> #### Documentation Adapted the readme to reflect the changes made in this PR <!--Please delete paragraphs that you did not use before submitting.--> --------- Signed-off-by: Florian Bacher <florian.bacher@dynatrace.com> --- .../k8sattributes-container-attributes.yaml | 27 +++++++ processor/k8sattributesprocessor/README.md | 4 +- processor/k8sattributesprocessor/processor.go | 9 +++ .../k8sattributesprocessor/processor_test.go | 74 +++++++++++++++++++ 4 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 .chloggen/k8sattributes-container-attributes.yaml diff --git a/.chloggen/k8sattributes-container-attributes.yaml b/.chloggen/k8sattributes-container-attributes.yaml new file mode 100644 index 000000000000..446f7b8d024c --- /dev/null +++ b/.chloggen/k8sattributes-container-attributes.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: k8sattributesprocessor + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: For pods with only one container, the `container.id` and `k8s.container.name` are not longer required in the resource attributes to add the container attributes + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [34189] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [] diff --git a/processor/k8sattributesprocessor/README.md b/processor/k8sattributesprocessor/README.md index 425bef183f4a..7f0d9aef8475 100644 --- a/processor/k8sattributesprocessor/README.md +++ b/processor/k8sattributesprocessor/README.md @@ -98,7 +98,9 @@ are then also available for the use within association rules. Available attribut Not all the attributes are guaranteed to be added. Only attribute names from `metadata` should be used for pod_association's `resource_attribute`, because empty or non-existing values will be ignored. -Additional container level attributes can be extracted provided that certain resource attributes are provided: +Additional container level attributes can be extracted. If a pod contains more than one container, +either the `container.id`, or the `k8s.container.name` attribute must be provided in the incoming resource attributes to +correctly associate the matching container to the resource: 1. If the `container.id` resource attribute is provided, the following additional attributes will be available: - k8s.container.name diff --git a/processor/k8sattributesprocessor/processor.go b/processor/k8sattributesprocessor/processor.go index e656a41469bc..cfcc33c260f7 100644 --- a/processor/k8sattributesprocessor/processor.go +++ b/processor/k8sattributesprocessor/processor.go @@ -226,6 +226,15 @@ func (kp *kubernetesprocessor) addContainerAttributes(attrs pcommon.Map, pod *ku if !ok { return } + // if there is only one container in the pod, we can fall back to that container + case len(pod.Containers.ByID) == 1: + for _, c := range pod.Containers.ByID { + containerSpec = c + } + case len(pod.Containers.ByName) == 1: + for _, c := range pod.Containers.ByName { + containerSpec = c + } default: return } diff --git a/processor/k8sattributesprocessor/processor_test.go b/processor/k8sattributesprocessor/processor_test.go index 6e6458ea4c82..06b28caa7746 100644 --- a/processor/k8sattributesprocessor/processor_test.go +++ b/processor/k8sattributesprocessor/processor_test.go @@ -1264,6 +1264,80 @@ func TestProcessorAddContainerAttributes(t *testing.T) { conventions.AttributeContainerImageName: "test/app", }, }, + { + name: "fall back to only container", + op: func(kp *kubernetesprocessor) { + kp.podAssociations = []kube.Association{ + { + Name: "k8s.pod.uid", + Sources: []kube.AssociationSource{ + { + From: "resource_attribute", + Name: "k8s.pod.uid", + }, + }, + }, + } + kp.kc.(*fakeClient).Pods[newPodIdentifier("resource_attribute", "k8s.pod.uid", "19f651bc-73e4-410f-b3e9-f0241679d3b8")] = &kube.Pod{ + Containers: kube.PodContainers{ + ByName: map[string]*kube.Container{ + "app": { + Name: "app", + ImageName: "test/app", + ImageTag: "1.0.1", + }, + }, + }, + } + }, + resourceGens: []generateResourceFunc{ + withPodUID("19f651bc-73e4-410f-b3e9-f0241679d3b8"), + }, + wantAttrs: map[string]any{ + conventions.AttributeK8SPodUID: "19f651bc-73e4-410f-b3e9-f0241679d3b8", + conventions.AttributeK8SContainerName: "app", + conventions.AttributeContainerImageName: "test/app", + conventions.AttributeContainerImageTag: "1.0.1", + }, + }, + { + name: "multiple containers in the pod - do not fall back to any container", + op: func(kp *kubernetesprocessor) { + kp.podAssociations = []kube.Association{ + { + Name: "k8s.pod.uid", + Sources: []kube.AssociationSource{ + { + From: "resource_attribute", + Name: "k8s.pod.uid", + }, + }, + }, + } + kp.kc.(*fakeClient).Pods[newPodIdentifier("resource_attribute", "k8s.pod.uid", "19f651bc-73e4-410f-b3e9-f0241679d3b8")] = &kube.Pod{ + Containers: kube.PodContainers{ + ByName: map[string]*kube.Container{ + "app": { + Name: "app", + ImageName: "test/app", + ImageTag: "1.0.1", + }, + "app2": { + Name: "app2", + ImageName: "test/app", + ImageTag: "1.0.1", + }, + }, + }, + } + }, + resourceGens: []generateResourceFunc{ + withPodUID("19f651bc-73e4-410f-b3e9-f0241679d3b8"), + }, + wantAttrs: map[string]any{ + conventions.AttributeK8SPodUID: "19f651bc-73e4-410f-b3e9-f0241679d3b8", + }, + }, } for _, tt := range tests { From 7901da40bb6a45d412460949ce360208129e80f9 Mon Sep 17 00:00:00 2001 From: Vyacheslav Stepanov <f.fenix@gmail.com> Date: Mon, 13 Jan 2025 20:48:36 +0200 Subject: [PATCH 07/13] [exporter/clickhouse] Fix Nil Pointer Exception on Metrics/Traces export without service.name Resource Attribute (#37034) <!--Ex. Fixing a bug - Describe the bug and how this fixes the issue. Ex. Adding a feature - Explain what this achieves.--> #### Description Fixing Nil Pointer Exception regression introduced in https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/35725 <!-- Issue number (e.g. #1234) or full URL to issue, if applicable. --> #### Link to tracking issue Fixes #37030 <!--Describe what testing was performed and which tests were added.--> #### Testing Unit test adjusted to include test Metric without `service.name` Resource Attributes <!--Describe the documentation added.--> #### Documentation <!--Please delete paragraphs that you did not use before submitting.--> --- .../fix_clickhouseexporter-metrics-npe.yaml | 27 +++++++++++++++++++ exporter/clickhouseexporter/exporter_logs.go | 6 +---- .../exporter_metrics_test.go | 2 +- .../clickhouseexporter/exporter_traces.go | 6 ++--- .../internal/exponential_histogram_metrics.go | 11 ++++---- .../internal/gauge_metrics.go | 11 ++++---- .../internal/histogram_metrics.go | 11 ++++---- .../internal/metrics_model.go | 10 +++++++ .../internal/metrics_model_test.go | 22 +++++++++++++++ .../internal/sum_metrics.go | 11 ++++---- .../internal/summary_metrics.go | 11 ++++---- 11 files changed, 94 insertions(+), 34 deletions(-) create mode 100644 .chloggen/fix_clickhouseexporter-metrics-npe.yaml diff --git a/.chloggen/fix_clickhouseexporter-metrics-npe.yaml b/.chloggen/fix_clickhouseexporter-metrics-npe.yaml new file mode 100644 index 000000000000..3fd8e0cdb56c --- /dev/null +++ b/.chloggen/fix_clickhouseexporter-metrics-npe.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: bug_fix + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: clickhouseexporter + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Fix Nil Pointer Exception on Metrics/Traces export without service.name Resource Attribute + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [37030] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [user] diff --git a/exporter/clickhouseexporter/exporter_logs.go b/exporter/clickhouseexporter/exporter_logs.go index f57fbf97ee42..ba7c8b97c480 100644 --- a/exporter/clickhouseexporter/exporter_logs.go +++ b/exporter/clickhouseexporter/exporter_logs.go @@ -12,7 +12,6 @@ import ( _ "github.com/ClickHouse/clickhouse-go/v2" // For register database driver. "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/pdata/plog" - conventions "go.opentelemetry.io/collector/semconv/v1.27.0" "go.uber.org/zap" "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/clickhouseexporter/internal" @@ -77,10 +76,7 @@ func (e *logsExporter) pushLogsData(ctx context.Context, ld plog.Logs) error { res := logs.Resource() resURL := logs.SchemaUrl() resAttr := internal.AttributesToMap(res.Attributes()) - var serviceName string - if v, ok := res.Attributes().Get(conventions.AttributeServiceName); ok { - serviceName = v.Str() - } + serviceName := internal.GetServiceName(res.Attributes()) for j := 0; j < logs.ScopeLogs().Len(); j++ { rs := logs.ScopeLogs().At(j).LogRecords() diff --git a/exporter/clickhouseexporter/exporter_metrics_test.go b/exporter/clickhouseexporter/exporter_metrics_test.go index 2000303cab67..b9b8255bf355 100644 --- a/exporter/clickhouseexporter/exporter_metrics_test.go +++ b/exporter/clickhouseexporter/exporter_metrics_test.go @@ -294,7 +294,7 @@ func simpleMetrics(count int) pmetric.Metrics { } rm = metrics.ResourceMetrics().AppendEmpty() - rm.Resource().Attributes().PutStr("service.name", "demo 2") + // Removed service.name from second metric to test both with/without ServiceName cases rm.Resource().Attributes().PutStr("Resource Attributes 2", "value2") rm.Resource().SetDroppedAttributesCount(20) rm.SetSchemaUrl("Resource SchemaUrl 2") diff --git a/exporter/clickhouseexporter/exporter_traces.go b/exporter/clickhouseexporter/exporter_traces.go index 39a706c60afd..277a60e9b437 100644 --- a/exporter/clickhouseexporter/exporter_traces.go +++ b/exporter/clickhouseexporter/exporter_traces.go @@ -14,7 +14,6 @@ import ( "github.com/ClickHouse/clickhouse-go/v2/lib/column" "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/pdata/ptrace" - conventions "go.opentelemetry.io/collector/semconv/v1.27.0" "go.uber.org/zap" "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/clickhouseexporter/internal" @@ -77,7 +76,8 @@ func (e *tracesExporter) pushTraceData(ctx context.Context, td ptrace.Traces) er spans := td.ResourceSpans().At(i) res := spans.Resource() resAttr := internal.AttributesToMap(res.Attributes()) - serviceName, _ := res.Attributes().Get(conventions.AttributeServiceName) + serviceName := internal.GetServiceName(res.Attributes()) + for j := 0; j < spans.ScopeSpans().Len(); j++ { rs := spans.ScopeSpans().At(j).Spans() scopeName := spans.ScopeSpans().At(j).Scope().Name() @@ -96,7 +96,7 @@ func (e *tracesExporter) pushTraceData(ctx context.Context, td ptrace.Traces) er r.TraceState().AsRaw(), r.Name(), r.Kind().String(), - serviceName.AsString(), + serviceName, resAttr, scopeName, scopeVersion, diff --git a/exporter/clickhouseexporter/internal/exponential_histogram_metrics.go b/exporter/clickhouseexporter/internal/exponential_histogram_metrics.go index 064e12a2b234..e74e6456a70a 100644 --- a/exporter/clickhouseexporter/internal/exponential_histogram_metrics.go +++ b/exporter/clickhouseexporter/internal/exponential_histogram_metrics.go @@ -11,7 +11,6 @@ import ( "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" - conventions "go.opentelemetry.io/collector/semconv/v1.27.0" "go.uber.org/zap" ) @@ -130,20 +129,22 @@ func (e *expHistogramMetrics) insert(ctx context.Context, db *sql.DB) error { }() for _, model := range e.expHistogramModels { - serviceName, _ := model.metadata.ResAttr.Get(conventions.AttributeServiceName) + resAttr := AttributesToMap(model.metadata.ResAttr) + scopeAttr := AttributesToMap(model.metadata.ScopeInstr.Attributes()) + serviceName := GetServiceName(model.metadata.ResAttr) for i := 0; i < model.expHistogram.DataPoints().Len(); i++ { dp := model.expHistogram.DataPoints().At(i) attrs, times, values, traceIDs, spanIDs := convertExemplars(dp.Exemplars()) _, err = statement.ExecContext(ctx, - AttributesToMap(model.metadata.ResAttr), + resAttr, model.metadata.ResURL, model.metadata.ScopeInstr.Name(), model.metadata.ScopeInstr.Version(), - AttributesToMap(model.metadata.ScopeInstr.Attributes()), + scopeAttr, model.metadata.ScopeInstr.DroppedAttributesCount(), model.metadata.ScopeURL, - serviceName.AsString(), + serviceName, model.metricName, model.metricDescription, model.metricUnit, diff --git a/exporter/clickhouseexporter/internal/gauge_metrics.go b/exporter/clickhouseexporter/internal/gauge_metrics.go index e2fbfe2dc365..a0fbb4d275f1 100644 --- a/exporter/clickhouseexporter/internal/gauge_metrics.go +++ b/exporter/clickhouseexporter/internal/gauge_metrics.go @@ -11,7 +11,6 @@ import ( "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" - conventions "go.opentelemetry.io/collector/semconv/v1.27.0" "go.uber.org/zap" ) @@ -109,20 +108,22 @@ func (g *gaugeMetrics) insert(ctx context.Context, db *sql.DB) error { }() for _, model := range g.gaugeModels { - serviceName, _ := model.metadata.ResAttr.Get(conventions.AttributeServiceName) + resAttr := AttributesToMap(model.metadata.ResAttr) + scopeAttr := AttributesToMap(model.metadata.ScopeInstr.Attributes()) + serviceName := GetServiceName(model.metadata.ResAttr) for i := 0; i < model.gauge.DataPoints().Len(); i++ { dp := model.gauge.DataPoints().At(i) attrs, times, values, traceIDs, spanIDs := convertExemplars(dp.Exemplars()) _, err = statement.ExecContext(ctx, - AttributesToMap(model.metadata.ResAttr), + resAttr, model.metadata.ResURL, model.metadata.ScopeInstr.Name(), model.metadata.ScopeInstr.Version(), - AttributesToMap(model.metadata.ScopeInstr.Attributes()), + scopeAttr, model.metadata.ScopeInstr.DroppedAttributesCount(), model.metadata.ScopeURL, - serviceName.AsString(), + serviceName, model.metricName, model.metricDescription, model.metricUnit, diff --git a/exporter/clickhouseexporter/internal/histogram_metrics.go b/exporter/clickhouseexporter/internal/histogram_metrics.go index f3374b655ba2..cdd4508722e8 100644 --- a/exporter/clickhouseexporter/internal/histogram_metrics.go +++ b/exporter/clickhouseexporter/internal/histogram_metrics.go @@ -11,7 +11,6 @@ import ( "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" - conventions "go.opentelemetry.io/collector/semconv/v1.27.0" "go.uber.org/zap" ) @@ -121,20 +120,22 @@ func (h *histogramMetrics) insert(ctx context.Context, db *sql.DB) error { }() for _, model := range h.histogramModel { - serviceName, _ := model.metadata.ResAttr.Get(conventions.AttributeServiceName) + resAttr := AttributesToMap(model.metadata.ResAttr) + scopeAttr := AttributesToMap(model.metadata.ScopeInstr.Attributes()) + serviceName := GetServiceName(model.metadata.ResAttr) for i := 0; i < model.histogram.DataPoints().Len(); i++ { dp := model.histogram.DataPoints().At(i) attrs, times, values, traceIDs, spanIDs := convertExemplars(dp.Exemplars()) _, err = statement.ExecContext(ctx, - AttributesToMap(model.metadata.ResAttr), + resAttr, model.metadata.ResURL, model.metadata.ScopeInstr.Name(), model.metadata.ScopeInstr.Version(), - AttributesToMap(model.metadata.ScopeInstr.Attributes()), + scopeAttr, model.metadata.ScopeInstr.DroppedAttributesCount(), model.metadata.ScopeURL, - serviceName.AsString(), + serviceName, model.metricName, model.metricDescription, model.metricUnit, diff --git a/exporter/clickhouseexporter/internal/metrics_model.go b/exporter/clickhouseexporter/internal/metrics_model.go index a412051800c0..7c9377e83fe1 100644 --- a/exporter/clickhouseexporter/internal/metrics_model.go +++ b/exporter/clickhouseexporter/internal/metrics_model.go @@ -17,6 +17,7 @@ import ( "github.com/ClickHouse/clickhouse-go/v2/lib/column/orderedmap" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" + conventions "go.opentelemetry.io/collector/semconv/v1.27.0" "go.uber.org/zap" ) @@ -175,6 +176,15 @@ func AttributesToMap(attributes pcommon.Map) column.IterableOrderedMap { }, attributes.Len()) } +func GetServiceName(resAttr pcommon.Map) string { + var serviceName string + if v, ok := resAttr.Get(conventions.AttributeServiceName); ok { + serviceName = v.AsString() + } + + return serviceName +} + func convertSliceToArraySet[T any](slice []T) clickhouse.ArraySet { var set clickhouse.ArraySet for _, item := range slice { diff --git a/exporter/clickhouseexporter/internal/metrics_model_test.go b/exporter/clickhouseexporter/internal/metrics_model_test.go index 6b0c53b800c0..8301be087802 100644 --- a/exporter/clickhouseexporter/internal/metrics_model_test.go +++ b/exporter/clickhouseexporter/internal/metrics_model_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" + conventions "go.opentelemetry.io/collector/semconv/v1.27.0" "go.uber.org/zap/zaptest" ) @@ -225,3 +226,24 @@ func Test_newPlaceholder(t *testing.T) { expectStr := "(?,?,?,?,?)," require.Equal(t, newPlaceholder(5), &expectStr) } + +func Test_GetServiceName(t *testing.T) { + t.Run("should return empty string on unset service.name", func(t *testing.T) { + require.Equal(t, "", GetServiceName(pcommon.NewMap())) + }) + t.Run("should return correct string from service.name", func(t *testing.T) { + resAttr := pcommon.NewMap() + resAttr.PutStr(conventions.AttributeServiceName, "test-service") + require.Equal(t, "test-service", GetServiceName(resAttr)) + }) + t.Run("should return empty string on empty service.name", func(t *testing.T) { + resAttr := pcommon.NewMap() + resAttr.PutEmpty(conventions.AttributeServiceName) + require.Equal(t, "", GetServiceName(resAttr)) + }) + t.Run("should return string from non-string service.name", func(t *testing.T) { + resAttr := pcommon.NewMap() + resAttr.PutBool(conventions.AttributeServiceName, true) + require.Equal(t, "true", GetServiceName(resAttr)) + }) +} diff --git a/exporter/clickhouseexporter/internal/sum_metrics.go b/exporter/clickhouseexporter/internal/sum_metrics.go index 89455f8e3048..28e5d553a18d 100644 --- a/exporter/clickhouseexporter/internal/sum_metrics.go +++ b/exporter/clickhouseexporter/internal/sum_metrics.go @@ -11,7 +11,6 @@ import ( "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" - conventions "go.opentelemetry.io/collector/semconv/v1.27.0" "go.uber.org/zap" ) @@ -113,20 +112,22 @@ func (s *sumMetrics) insert(ctx context.Context, db *sql.DB) error { }() for _, model := range s.sumModel { - serviceName, _ := model.metadata.ResAttr.Get(conventions.AttributeServiceName) + resAttr := AttributesToMap(model.metadata.ResAttr) + scopeAttr := AttributesToMap(model.metadata.ScopeInstr.Attributes()) + serviceName := GetServiceName(model.metadata.ResAttr) for i := 0; i < model.sum.DataPoints().Len(); i++ { dp := model.sum.DataPoints().At(i) attrs, times, values, traceIDs, spanIDs := convertExemplars(dp.Exemplars()) _, err = statement.ExecContext(ctx, - AttributesToMap(model.metadata.ResAttr), + resAttr, model.metadata.ResURL, model.metadata.ScopeInstr.Name(), model.metadata.ScopeInstr.Version(), - AttributesToMap(model.metadata.ScopeInstr.Attributes()), + scopeAttr, model.metadata.ScopeInstr.DroppedAttributesCount(), model.metadata.ScopeURL, - serviceName.AsString(), + serviceName, model.metricName, model.metricDescription, model.metricUnit, diff --git a/exporter/clickhouseexporter/internal/summary_metrics.go b/exporter/clickhouseexporter/internal/summary_metrics.go index d98197c12b2e..749445ec427c 100644 --- a/exporter/clickhouseexporter/internal/summary_metrics.go +++ b/exporter/clickhouseexporter/internal/summary_metrics.go @@ -11,7 +11,6 @@ import ( "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" - conventions "go.opentelemetry.io/collector/semconv/v1.27.0" "go.uber.org/zap" ) @@ -103,21 +102,23 @@ func (s *summaryMetrics) insert(ctx context.Context, db *sql.DB) error { _ = statement.Close() }() for _, model := range s.summaryModel { - serviceName, _ := model.metadata.ResAttr.Get(conventions.AttributeServiceName) + resAttr := AttributesToMap(model.metadata.ResAttr) + scopeAttr := AttributesToMap(model.metadata.ScopeInstr.Attributes()) + serviceName := GetServiceName(model.metadata.ResAttr) for i := 0; i < model.summary.DataPoints().Len(); i++ { dp := model.summary.DataPoints().At(i) quantiles, values := convertValueAtQuantile(dp.QuantileValues()) _, err = statement.ExecContext(ctx, - AttributesToMap(model.metadata.ResAttr), + resAttr, model.metadata.ResURL, model.metadata.ScopeInstr.Name(), model.metadata.ScopeInstr.Version(), - AttributesToMap(model.metadata.ScopeInstr.Attributes()), + scopeAttr, model.metadata.ScopeInstr.DroppedAttributesCount(), model.metadata.ScopeURL, - serviceName.AsString(), + serviceName, model.metricName, model.metricDescription, model.metricUnit, From 020d9987f3f69753d141ec1c394331722f0b1d18 Mon Sep 17 00:00:00 2001 From: Aryan Goyal <137564277+ary82@users.noreply.github.com> Date: Tue, 14 Jan 2025 00:19:54 +0530 Subject: [PATCH 08/13] [extension/jaegerremotesampling] remove jaeger sampling dependency (#36977) #### Description Copy the required code from jaeger to extension/jaegerremotesampling: - Modify files to remove dependencies - Add required code from `plugin/sampling/strategyprovider/static` - Add required code from `cmd/collector/app/sampling` to `internal` #### Link to tracking issue Fixes #36976 #### Testing - Add `grpc_handler_test.go` from `cmd/collector/app/sampling/grpc_handler_test.go` - Add `provider_test.go` from `plugin/sampling/strategyprovider/static/provider_test.go` with `plugin/sampling/strategyprovider/static/fixtures` --------- Signed-off-by: Aryan Goyal <137564277+ary82@users.noreply.github.com> Co-authored-by: Yuri Shkuro <yurishkuro@users.noreply.github.com> --- .chloggen/remove-jaegersampling.yaml | 27 + extension/jaegerremotesampling/extension.go | 20 +- extension/jaegerremotesampling/go.mod | 15 - extension/jaegerremotesampling/go.sum | 66 -- .../internal/internal_test.go | 25 - .../internal/mocks/mock_source.go | 25 + .../internal/{ => server/grpc}/grpc.go | 17 +- .../internal/server/grpc/grpc_handler.go | 30 + .../internal/server/grpc/grpc_handler_test.go | 53 ++ .../internal/{ => server/grpc}/grpc_test.go | 6 +- .../internal/{ => server/http}/http.go | 9 +- .../internal/{ => server/http}/http_test.go | 18 +- .../internal/source/filesource/constants.go | 42 ++ .../internal/source/filesource/filesource.go | 371 +++++++++++ .../source/filesource/filesource_test.go | 588 ++++++++++++++++++ ...StrategiesDeprecatedBehavior_ServiceA.json | 16 + ...StrategiesDeprecatedBehavior_ServiceB.json | 6 + ...viceNoPerOperationStrategies_ServiceA.json | 16 + ...viceNoPerOperationStrategies_ServiceB.json | 17 + .../filesource/fixtures/bad_strategies.json | 1 + .../fixtures/missing-service-types.json | 33 + .../fixtures/operation_strategies.json | 74 +++ .../fixtures/service_no_per_operation.json | 25 + .../filesource/fixtures/strategies.json | 18 + .../internal/source/filesource/model.go | 31 + .../internal/source/filesource/options.go | 21 + .../internal/source/interface.go | 21 + .../internal/source/remotesource/manager.go | 34 + .../source/remotesource/manager_test.go | 59 ++ .../remotesource}/remote_strategy_cache.go | 2 +- .../remote_strategy_cache_test.go | 2 +- .../remotesource}/remote_strategy_store.go | 16 +- 32 files changed, 1558 insertions(+), 146 deletions(-) create mode 100644 .chloggen/remove-jaegersampling.yaml delete mode 100644 extension/jaegerremotesampling/internal/internal_test.go create mode 100644 extension/jaegerremotesampling/internal/mocks/mock_source.go rename extension/jaegerremotesampling/internal/{ => server/grpc}/grpc.go (80%) create mode 100644 extension/jaegerremotesampling/internal/server/grpc/grpc_handler.go create mode 100644 extension/jaegerremotesampling/internal/server/grpc/grpc_handler_test.go rename extension/jaegerremotesampling/internal/{ => server/grpc}/grpc_test.go (94%) rename extension/jaegerremotesampling/internal/{ => server/http}/http.go (88%) rename extension/jaegerremotesampling/internal/{ => server/http}/http_test.go (86%) create mode 100644 extension/jaegerremotesampling/internal/source/filesource/constants.go create mode 100644 extension/jaegerremotesampling/internal/source/filesource/filesource.go create mode 100644 extension/jaegerremotesampling/internal/source/filesource/filesource_test.go create mode 100644 extension/jaegerremotesampling/internal/source/filesource/fixtures/TestServiceNoPerOperationStrategiesDeprecatedBehavior_ServiceA.json create mode 100644 extension/jaegerremotesampling/internal/source/filesource/fixtures/TestServiceNoPerOperationStrategiesDeprecatedBehavior_ServiceB.json create mode 100644 extension/jaegerremotesampling/internal/source/filesource/fixtures/TestServiceNoPerOperationStrategies_ServiceA.json create mode 100644 extension/jaegerremotesampling/internal/source/filesource/fixtures/TestServiceNoPerOperationStrategies_ServiceB.json create mode 100644 extension/jaegerremotesampling/internal/source/filesource/fixtures/bad_strategies.json create mode 100644 extension/jaegerremotesampling/internal/source/filesource/fixtures/missing-service-types.json create mode 100644 extension/jaegerremotesampling/internal/source/filesource/fixtures/operation_strategies.json create mode 100644 extension/jaegerremotesampling/internal/source/filesource/fixtures/service_no_per_operation.json create mode 100644 extension/jaegerremotesampling/internal/source/filesource/fixtures/strategies.json create mode 100644 extension/jaegerremotesampling/internal/source/filesource/model.go create mode 100644 extension/jaegerremotesampling/internal/source/filesource/options.go create mode 100644 extension/jaegerremotesampling/internal/source/interface.go create mode 100644 extension/jaegerremotesampling/internal/source/remotesource/manager.go create mode 100644 extension/jaegerremotesampling/internal/source/remotesource/manager_test.go rename extension/jaegerremotesampling/internal/{ => source/remotesource}/remote_strategy_cache.go (96%) rename extension/jaegerremotesampling/internal/{ => source/remotesource}/remote_strategy_cache_test.go (99%) rename extension/jaegerremotesampling/internal/{ => source/remotesource}/remote_strategy_store.go (77%) diff --git a/.chloggen/remove-jaegersampling.yaml b/.chloggen/remove-jaegersampling.yaml new file mode 100644 index 000000000000..082213eec43f --- /dev/null +++ b/.chloggen/remove-jaegersampling.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: extension/jaegerremotesampling + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: remove dependency on jaeger internal code + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [36976] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [] diff --git a/extension/jaegerremotesampling/extension.go b/extension/jaegerremotesampling/extension.go index 74f7d9467b99..ba9bbbc738da 100644 --- a/extension/jaegerremotesampling/extension.go +++ b/extension/jaegerremotesampling/extension.go @@ -7,13 +7,15 @@ import ( "context" "fmt" - "github.com/jaegertracing/jaeger/cmd/collector/app/sampling/samplingstrategy" - "github.com/jaegertracing/jaeger/plugin/sampling/strategyprovider/static" "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/extension" "go.uber.org/zap" - "github.com/open-telemetry/opentelemetry-collector-contrib/extension/jaegerremotesampling/internal" + "github.com/open-telemetry/opentelemetry-collector-contrib/extension/jaegerremotesampling/internal/server/grpc" + "github.com/open-telemetry/opentelemetry-collector-contrib/extension/jaegerremotesampling/internal/server/http" + "github.com/open-telemetry/opentelemetry-collector-contrib/extension/jaegerremotesampling/internal/source" + "github.com/open-telemetry/opentelemetry-collector-contrib/extension/jaegerremotesampling/internal/source/filesource" + "github.com/open-telemetry/opentelemetry-collector-contrib/extension/jaegerremotesampling/internal/source/remotesource" ) var _ extension.Extension = (*jrsExtension)(nil) @@ -24,7 +26,7 @@ type jrsExtension struct { httpServer component.Component grpcServer component.Component - samplingStore samplingstrategy.Provider + samplingStore source.Source closers []func() error } @@ -44,11 +46,11 @@ func (jrse *jrsExtension) Start(ctx context.Context, host component.Host) error // - local file // we can then use a simplified logic here to assign the appropriate store if jrse.cfg.Source.File != "" { - opts := static.Options{ + opts := filesource.Options{ StrategiesFile: jrse.cfg.Source.File, ReloadInterval: jrse.cfg.Source.ReloadInterval, } - ss, err := static.NewProvider(opts, jrse.telemetry.Logger) + ss, err := filesource.NewFileSource(opts, jrse.telemetry.Logger) if err != nil { return fmt.Errorf("failed to create the local file strategy store: %w", err) } @@ -64,7 +66,7 @@ func (jrse *jrsExtension) Start(ctx context.Context, host component.Host) error return fmt.Errorf("failed to create the remote strategy store: %w", err) } jrse.closers = append(jrse.closers, conn.Close) - remoteStore, closer := internal.NewRemoteStrategyStore( + remoteStore, closer := remotesource.NewRemoteSource( conn, jrse.cfg.Source.Remote, jrse.cfg.Source.ReloadInterval, @@ -74,7 +76,7 @@ func (jrse *jrsExtension) Start(ctx context.Context, host component.Host) error } if jrse.cfg.HTTPServerConfig != nil { - httpServer, err := internal.NewHTTP(jrse.telemetry, *jrse.cfg.HTTPServerConfig, jrse.samplingStore) + httpServer, err := http.NewHTTP(jrse.telemetry, *jrse.cfg.HTTPServerConfig, jrse.samplingStore) if err != nil { return fmt.Errorf("error while creating the HTTP server: %w", err) } @@ -86,7 +88,7 @@ func (jrse *jrsExtension) Start(ctx context.Context, host component.Host) error } if jrse.cfg.GRPCServerConfig != nil { - grpcServer, err := internal.NewGRPC(jrse.telemetry, *jrse.cfg.GRPCServerConfig, jrse.samplingStore) + grpcServer, err := grpc.NewGRPC(jrse.telemetry, *jrse.cfg.GRPCServerConfig, jrse.samplingStore) if err != nil { return fmt.Errorf("error while creating the gRPC server: %w", err) } diff --git a/extension/jaegerremotesampling/go.mod b/extension/jaegerremotesampling/go.mod index f6225948128e..b68c879d5637 100644 --- a/extension/jaegerremotesampling/go.mod +++ b/extension/jaegerremotesampling/go.mod @@ -26,7 +26,6 @@ require ( ) require ( - github.com/apache/thrift v0.21.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect @@ -38,28 +37,16 @@ require ( github.com/golang/snappy v0.0.4 // indirect github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/go-version v1.7.0 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect github.com/klauspost/compress v1.17.11 // 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.2 // indirect - github.com/magiconair/properties v1.8.7 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/mostynb/go-grpc-compression v1.2.3 // indirect - github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/pierrec/lz4/v4 v4.1.22 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rs/cors v1.11.1 // indirect - github.com/sagikazarmark/locafero v0.4.0 // indirect - github.com/sagikazarmark/slog-shim v0.1.0 // indirect - github.com/sourcegraph/conc v0.3.0 // indirect - github.com/spf13/afero v1.11.0 // indirect - github.com/spf13/cast v1.6.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/spf13/viper v1.19.0 // indirect - github.com/subosito/gotenv v1.6.0 // indirect go.opentelemetry.io/collector/client v1.23.0 // indirect go.opentelemetry.io/collector/config/configauth v0.117.0 // indirect go.opentelemetry.io/collector/config/configcompression v1.23.0 // indirect @@ -75,13 +62,11 @@ require ( go.opentelemetry.io/otel/sdk/metric v1.32.0 // indirect go.opentelemetry.io/otel/trace v1.32.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect golang.org/x/net v0.34.0 // indirect golang.org/x/sys v0.29.0 // indirect golang.org/x/text v0.21.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect google.golang.org/protobuf v1.36.2 // indirect - gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/extension/jaegerremotesampling/go.sum b/extension/jaegerremotesampling/go.sum index 8249f59d7cb5..1dabf1878bd2 100644 --- a/extension/jaegerremotesampling/go.sum +++ b/extension/jaegerremotesampling/go.sum @@ -1,21 +1,9 @@ -github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= -github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= -github.com/apache/thrift v0.21.0 h1:tdPmh/ptjE1IJnhbhrcl2++TauVjy242rkV/UzJChnE= -github.com/apache/thrift v0.21.0/go.mod h1:W1H8aR/QRtYNvrPeFXBtobyRkd0/YVhTc6i07XIAgDw= -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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/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/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= -github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= -github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -39,10 +27,6 @@ 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.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jaegertracing/jaeger v1.64.0 h1:fc45qwaBZpBE2DeXwJlm8hK1q2PACrzGKXKgIcHLPMw= github.com/jaegertracing/jaeger v1.64.0/go.mod h1:9yVeL3z2Q9AGrW7j7TTeqohUy7Bbp9XWXgUDG3rVdO0= github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= @@ -63,12 +47,8 @@ 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/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= -github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= 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/mapstructure v1.5.1-0.20231216201459-8508981c8b6c h1:cqn374mizHuIWj+OSJCajGr/phAmuMug9qIX3l9CflE= -github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 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-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= @@ -77,55 +57,16 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mostynb/go-grpc-compression v1.2.3 h1:42/BKWMy0KEJGSdWvzqIyOZ95YcR9mLPqKctH7Uo//I= github.com/mostynb/go-grpc-compression v1.2.3/go.mod h1:AghIxF3P57umzqM9yz795+y1Vjs47Km/Y2FE6ouQ7Lg= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= -github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= -github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= -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.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ= -github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s= -github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= -github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA= github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= -github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= -github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= -github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= -github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= -github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= -github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= -github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= -github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= -github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= -github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= -github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= -github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= 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.115.0 h1:qUZ0bTeNBudMxNQ7FJKS//TxTjeJ7tfU/z22mcFavWU= @@ -181,8 +122,6 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0 h1:DheMAlT go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.57.0/go.mod h1:wZcGmeVO9nzP67aYSLDqXNWK87EZWhi7JWj1v7ZXf94= go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= -go.opentelemetry.io/otel/exporters/prometheus v0.54.0 h1:rFwzp68QMgtzu9PgP3jm9XaMICI6TsofWWPcBDKwlsU= -go.opentelemetry.io/otel/exporters/prometheus v0.54.0/go.mod h1:QyjcV9qDP6VeK5qPyKETvNjmaaEc7+gqjh4SS0ZYzDU= go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= @@ -200,8 +139,6 @@ 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/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= -golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= 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= @@ -239,8 +176,5 @@ google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojt 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/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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/extension/jaegerremotesampling/internal/internal_test.go b/extension/jaegerremotesampling/internal/internal_test.go deleted file mode 100644 index 5304740b4171..000000000000 --- a/extension/jaegerremotesampling/internal/internal_test.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright The OpenTelemetry Authors -// SPDX-License-Identifier: Apache-2.0 - -package internal - -import ( - "context" - - "github.com/jaegertracing/jaeger/proto-gen/api_v2" -) - -type mockCfgMgr struct { - getSamplingStrategyFunc func(ctx context.Context, serviceName string) (*api_v2.SamplingStrategyResponse, error) -} - -func (m *mockCfgMgr) Close() error { - return nil -} - -func (m *mockCfgMgr) GetSamplingStrategy(ctx context.Context, serviceName string) (*api_v2.SamplingStrategyResponse, error) { - if m.getSamplingStrategyFunc != nil { - return m.getSamplingStrategyFunc(ctx, serviceName) - } - return &api_v2.SamplingStrategyResponse{}, nil -} diff --git a/extension/jaegerremotesampling/internal/mocks/mock_source.go b/extension/jaegerremotesampling/internal/mocks/mock_source.go new file mode 100644 index 000000000000..66934f31fe8f --- /dev/null +++ b/extension/jaegerremotesampling/internal/mocks/mock_source.go @@ -0,0 +1,25 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package mocks // import "github.com/open-telemetry/opentelemetry-collector-contrib/extension/jaegerremotesampling/internal/mocks" + +import ( + "context" + + "github.com/jaegertracing/jaeger/proto-gen/api_v2" +) + +type MockCfgMgr struct { + GetSamplingStrategyFunc func(ctx context.Context, serviceName string) (*api_v2.SamplingStrategyResponse, error) +} + +func (m *MockCfgMgr) Close() error { + return nil +} + +func (m *MockCfgMgr) GetSamplingStrategy(ctx context.Context, serviceName string) (*api_v2.SamplingStrategyResponse, error) { + if m.GetSamplingStrategyFunc != nil { + return m.GetSamplingStrategyFunc(ctx, serviceName) + } + return &api_v2.SamplingStrategyResponse{}, nil +} diff --git a/extension/jaegerremotesampling/internal/grpc.go b/extension/jaegerremotesampling/internal/server/grpc/grpc.go similarity index 80% rename from extension/jaegerremotesampling/internal/grpc.go rename to extension/jaegerremotesampling/internal/server/grpc/grpc.go index 5b614f90fae4..3094d90b7435 100644 --- a/extension/jaegerremotesampling/internal/grpc.go +++ b/extension/jaegerremotesampling/internal/server/grpc/grpc.go @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package internal // import "github.com/open-telemetry/opentelemetry-collector-contrib/extension/jaegerremotesampling/internal" +package grpc // import "github.com/open-telemetry/opentelemetry-collector-contrib/extension/jaegerremotesampling/internal/server/grpc" import ( "context" @@ -9,8 +9,6 @@ import ( "fmt" "net" - "github.com/jaegertracing/jaeger/cmd/collector/app/sampling" - "github.com/jaegertracing/jaeger/cmd/collector/app/sampling/samplingstrategy" "github.com/jaegertracing/jaeger/proto-gen/api_v2" "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/config/configgrpc" @@ -18,11 +16,16 @@ import ( "google.golang.org/grpc/health" "google.golang.org/grpc/health/grpc_health_v1" "google.golang.org/grpc/reflection" + + "github.com/open-telemetry/opentelemetry-collector-contrib/extension/jaegerremotesampling/internal/source" ) var _ component.Component = (*SamplingGRPCServer)(nil) -var errGRPCServerNotRunning = errors.New("gRPC server is not running") +var ( + errMissingStrategyStore = errors.New("the strategy store has not been provided") + errGRPCServerNotRunning = errors.New("gRPC server is not running") +) type grpcServer interface { Serve(lis net.Listener) error @@ -34,7 +37,7 @@ type grpcServer interface { func NewGRPC( telemetry component.TelemetrySettings, settings configgrpc.ServerConfig, - strategyStore samplingstrategy.Provider, + strategyStore source.Source, ) (*SamplingGRPCServer, error) { if strategyStore == nil { return nil, errMissingStrategyStore @@ -51,7 +54,7 @@ func NewGRPC( type SamplingGRPCServer struct { telemetry component.TelemetrySettings settings configgrpc.ServerConfig - strategyStore samplingstrategy.Provider + strategyStore source.Source grpcServer grpcServer } @@ -64,7 +67,7 @@ func (s *SamplingGRPCServer) Start(ctx context.Context, host component.Host) err reflection.Register(server) s.grpcServer = server - api_v2.RegisterSamplingManagerServer(server, sampling.NewGRPCHandler(s.strategyStore)) + api_v2.RegisterSamplingManagerServer(server, NewGRPCHandler(s.strategyStore)) healthServer := health.NewServer() healthServer.SetServingStatus("jaeger.api_v2.SamplingManager", grpc_health_v1.HealthCheckResponse_SERVING) diff --git a/extension/jaegerremotesampling/internal/server/grpc/grpc_handler.go b/extension/jaegerremotesampling/internal/server/grpc/grpc_handler.go new file mode 100644 index 000000000000..230d67ebc5aa --- /dev/null +++ b/extension/jaegerremotesampling/internal/server/grpc/grpc_handler.go @@ -0,0 +1,30 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2018 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package grpc // import "github.com/open-telemetry/opentelemetry-collector-contrib/extension/jaegerremotesampling/internal/server/grpc" + +import ( + "context" + + "github.com/jaegertracing/jaeger/proto-gen/api_v2" + + "github.com/open-telemetry/opentelemetry-collector-contrib/extension/jaegerremotesampling/internal/source" +) + +// GRPCHandler is sampling strategy handler for gRPC. +type GRPCHandler struct { + samplingProvider source.Source +} + +// NewGRPCHandler creates a handler that controls sampling strategies for services. +func NewGRPCHandler(provider source.Source) GRPCHandler { + return GRPCHandler{ + samplingProvider: provider, + } +} + +// GetSamplingStrategy returns sampling decision from store. +func (s GRPCHandler) GetSamplingStrategy(ctx context.Context, param *api_v2.SamplingStrategyParameters) (*api_v2.SamplingStrategyResponse, error) { + return s.samplingProvider.GetSamplingStrategy(ctx, param.GetServiceName()) +} diff --git a/extension/jaegerremotesampling/internal/server/grpc/grpc_handler_test.go b/extension/jaegerremotesampling/internal/server/grpc/grpc_handler_test.go new file mode 100644 index 000000000000..a6197a620705 --- /dev/null +++ b/extension/jaegerremotesampling/internal/server/grpc/grpc_handler_test.go @@ -0,0 +1,53 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2018 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package grpc + +import ( + "context" + "errors" + "testing" + + "github.com/jaegertracing/jaeger/proto-gen/api_v2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type mockSamplingStore struct{} + +func (mockSamplingStore) GetSamplingStrategy(_ context.Context, serviceName string) (*api_v2.SamplingStrategyResponse, error) { + if serviceName == "error" { + return nil, errors.New("some error") + } else if serviceName == "nil" { + return nil, nil + } + return &api_v2.SamplingStrategyResponse{StrategyType: api_v2.SamplingStrategyType_PROBABILISTIC}, nil +} + +func (mockSamplingStore) Close() error { + return nil +} + +func TestNewGRPCHandler(t *testing.T) { + tests := []struct { + req *api_v2.SamplingStrategyParameters + resp *api_v2.SamplingStrategyResponse + err string + }{ + {req: &api_v2.SamplingStrategyParameters{ServiceName: "error"}, err: "some error"}, + {req: &api_v2.SamplingStrategyParameters{ServiceName: "nil"}, resp: nil}, + {req: &api_v2.SamplingStrategyParameters{ServiceName: "foo"}, resp: &api_v2.SamplingStrategyResponse{StrategyType: api_v2.SamplingStrategyType_PROBABILISTIC}}, + } + h := NewGRPCHandler(mockSamplingStore{}) + for _, test := range tests { + resp, err := h.GetSamplingStrategy(context.Background(), test.req) + if test.err != "" { + require.EqualError(t, err, test.err) + require.Nil(t, resp) + } else { + require.NoError(t, err) + assert.Equal(t, test.resp, resp) + } + } +} diff --git a/extension/jaegerremotesampling/internal/grpc_test.go b/extension/jaegerremotesampling/internal/server/grpc/grpc_test.go similarity index 94% rename from extension/jaegerremotesampling/internal/grpc_test.go rename to extension/jaegerremotesampling/internal/server/grpc/grpc_test.go index 40cc01de9998..92437c45fb87 100644 --- a/extension/jaegerremotesampling/internal/grpc_test.go +++ b/extension/jaegerremotesampling/internal/server/grpc/grpc_test.go @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package internal +package grpc import ( "context" @@ -14,6 +14,8 @@ import ( "go.opentelemetry.io/collector/component/componenttest" "go.opentelemetry.io/collector/config/configgrpc" "go.opentelemetry.io/collector/config/confignet" + + "github.com/open-telemetry/opentelemetry-collector-contrib/extension/jaegerremotesampling/internal/mocks" ) func TestMissingClientConfigManagerGRPC(t *testing.T) { @@ -30,7 +32,7 @@ func TestStartAndStopGRPC(t *testing.T) { Transport: confignet.TransportTypeTCP, }, } - s, err := NewGRPC(componenttest.NewNopTelemetrySettings(), srvSettings, &mockCfgMgr{}) + s, err := NewGRPC(componenttest.NewNopTelemetrySettings(), srvSettings, &mocks.MockCfgMgr{}) require.NoError(t, err) require.NotNil(t, s) diff --git a/extension/jaegerremotesampling/internal/http.go b/extension/jaegerremotesampling/internal/server/http/http.go similarity index 88% rename from extension/jaegerremotesampling/internal/http.go rename to extension/jaegerremotesampling/internal/server/http/http.go index 6ab9f75cba78..7786ff675322 100644 --- a/extension/jaegerremotesampling/internal/http.go +++ b/extension/jaegerremotesampling/internal/server/http/http.go @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package internal // import "github.com/open-telemetry/opentelemetry-collector-contrib/extension/jaegerremotesampling/internal" +package http // import "github.com/open-telemetry/opentelemetry-collector-contrib/extension/jaegerremotesampling/internal/server/http" import ( "context" @@ -12,10 +12,11 @@ import ( "net/http" "sync" - "github.com/jaegertracing/jaeger/cmd/collector/app/sampling/samplingstrategy" "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/component/componentstatus" "go.opentelemetry.io/collector/config/confighttp" + + "github.com/open-telemetry/opentelemetry-collector-contrib/extension/jaegerremotesampling/internal/source" ) var errMissingStrategyStore = errors.New("the strategy store has not been provided") @@ -25,14 +26,14 @@ var _ component.Component = (*SamplingHTTPServer)(nil) type SamplingHTTPServer struct { telemetry component.TelemetrySettings settings confighttp.ServerConfig - strategyStore samplingstrategy.Provider + strategyStore source.Source mux *http.ServeMux srv *http.Server shutdownWG *sync.WaitGroup } -func NewHTTP(telemetry component.TelemetrySettings, settings confighttp.ServerConfig, strategyStore samplingstrategy.Provider) (*SamplingHTTPServer, error) { +func NewHTTP(telemetry component.TelemetrySettings, settings confighttp.ServerConfig, strategyStore source.Source) (*SamplingHTTPServer, error) { if strategyStore == nil { return nil, errMissingStrategyStore } diff --git a/extension/jaegerremotesampling/internal/http_test.go b/extension/jaegerremotesampling/internal/server/http/http_test.go similarity index 86% rename from extension/jaegerremotesampling/internal/http_test.go rename to extension/jaegerremotesampling/internal/server/http/http_test.go index 075073d82215..cdbac983f811 100644 --- a/extension/jaegerremotesampling/internal/http_test.go +++ b/extension/jaegerremotesampling/internal/server/http/http_test.go @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package internal +package http import ( "context" @@ -18,6 +18,8 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/component/componenttest" "go.opentelemetry.io/collector/config/confighttp" + + "github.com/open-telemetry/opentelemetry-collector-contrib/extension/jaegerremotesampling/internal/mocks" ) func TestMissingClientConfigManagerHTTP(t *testing.T) { @@ -31,7 +33,7 @@ func TestStartAndStopHTTP(t *testing.T) { srvSettings := confighttp.ServerConfig{ Endpoint: "127.0.0.1:0", } - s, err := NewHTTP(componenttest.NewNopTelemetrySettings(), srvSettings, &mockCfgMgr{}) + s, err := NewHTTP(componenttest.NewNopTelemetrySettings(), srvSettings, &mocks.MockCfgMgr{}) require.NoError(t, err) require.NotNil(t, s) @@ -53,8 +55,8 @@ func TestEndpointsAreWired(t *testing.T) { for _, tC := range testCases { t.Run(tC.desc, func(t *testing.T) { // prepare - s, err := NewHTTP(componenttest.NewNopTelemetrySettings(), confighttp.ServerConfig{}, &mockCfgMgr{ - getSamplingStrategyFunc: func(_ context.Context, _ string) (*api_v2.SamplingStrategyResponse, error) { + s, err := NewHTTP(componenttest.NewNopTelemetrySettings(), confighttp.ServerConfig{}, &mocks.MockCfgMgr{ + GetSamplingStrategyFunc: func(_ context.Context, _ string) (*api_v2.SamplingStrategyResponse, error) { return &api_v2.SamplingStrategyResponse{ ProbabilisticSampling: &api_v2.ProbabilisticSamplingStrategy{ SamplingRate: 1, @@ -87,7 +89,7 @@ func TestEndpointsAreWired(t *testing.T) { func TestServiceNameIsRequired(t *testing.T) { // prepare - s, err := NewHTTP(componenttest.NewNopTelemetrySettings(), confighttp.ServerConfig{}, &mockCfgMgr{}) + s, err := NewHTTP(componenttest.NewNopTelemetrySettings(), confighttp.ServerConfig{}, &mocks.MockCfgMgr{}) require.NoError(t, err) require.NotNil(t, s) @@ -105,12 +107,12 @@ func TestServiceNameIsRequired(t *testing.T) { } func TestErrorFromClientConfigManager(t *testing.T) { - s, err := NewHTTP(componenttest.NewNopTelemetrySettings(), confighttp.ServerConfig{}, &mockCfgMgr{}) + s, err := NewHTTP(componenttest.NewNopTelemetrySettings(), confighttp.ServerConfig{}, &mocks.MockCfgMgr{}) require.NoError(t, err) require.NotNil(t, s) - s.strategyStore = &mockCfgMgr{ - getSamplingStrategyFunc: func(_ context.Context, _ string) (*api_v2.SamplingStrategyResponse, error) { + s.strategyStore = &mocks.MockCfgMgr{ + GetSamplingStrategyFunc: func(_ context.Context, _ string) (*api_v2.SamplingStrategyResponse, error) { return nil, errors.New("some error") }, } diff --git a/extension/jaegerremotesampling/internal/source/filesource/constants.go b/extension/jaegerremotesampling/internal/source/filesource/constants.go new file mode 100644 index 000000000000..96f6ada52263 --- /dev/null +++ b/extension/jaegerremotesampling/internal/source/filesource/constants.go @@ -0,0 +1,42 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2018 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package filesource // import "github.com/open-telemetry/opentelemetry-collector-contrib/extension/jaegerremotesampling/internal/source/filesource" + +import ( + "github.com/jaegertracing/jaeger/proto-gen/api_v2" +) + +const ( + // samplerTypeProbabilistic is the type of sampler that samples traces + // with a certain fixed probability. + samplerTypeProbabilistic = "probabilistic" + + // samplerTypeRateLimiting is the type of sampler that samples + // only up to a fixed number of traces per second. + samplerTypeRateLimiting = "ratelimiting" + + // defaultSamplingProbability is the default sampling probability the + // Strategy Store will use if none is provided. + defaultSamplingProbability = 0.001 +) + +// defaultStrategy is the default sampling strategy the Strategy Store will return +// if none is provided. +func defaultStrategyResponse() *api_v2.SamplingStrategyResponse { + return &api_v2.SamplingStrategyResponse{ + StrategyType: api_v2.SamplingStrategyType_PROBABILISTIC, + ProbabilisticSampling: &api_v2.ProbabilisticSamplingStrategy{ + SamplingRate: defaultSamplingProbability, + }, + } +} + +func defaultStrategies() *storedStrategies { + s := &storedStrategies{ + serviceStrategies: make(map[string]*api_v2.SamplingStrategyResponse), + } + s.defaultStrategy = defaultStrategyResponse() + return s +} diff --git a/extension/jaegerremotesampling/internal/source/filesource/filesource.go b/extension/jaegerremotesampling/internal/source/filesource/filesource.go new file mode 100644 index 000000000000..f0334c3cbe1d --- /dev/null +++ b/extension/jaegerremotesampling/internal/source/filesource/filesource.go @@ -0,0 +1,371 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2018 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package filesource // import "github.com/open-telemetry/opentelemetry-collector-contrib/extension/jaegerremotesampling/internal/source/filesource" + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "net/http" + "net/url" + "os" + "path/filepath" + "sync/atomic" + "time" + + "github.com/jaegertracing/jaeger/proto-gen/api_v2" + "go.uber.org/zap" + + "github.com/open-telemetry/opentelemetry-collector-contrib/extension/jaegerremotesampling/internal/source" +) + +// null represents "null" JSON value and +// it un-marshals to nil pointer. +var nullJSON = []byte("null") + +type samplingProvider struct { + logger *zap.Logger + + storedStrategies atomic.Value // holds *storedStrategies + + cancelFunc context.CancelFunc + + options Options +} + +type storedStrategies struct { + defaultStrategy *api_v2.SamplingStrategyResponse + serviceStrategies map[string]*api_v2.SamplingStrategyResponse +} + +type strategyLoader func() ([]byte, error) + +// NewFileSource creates a strategy store that holds static sampling strategies. +func NewFileSource(options Options, logger *zap.Logger) (source.Source, error) { + ctx, cancelFunc := context.WithCancel(context.Background()) + h := &samplingProvider{ + logger: logger, + cancelFunc: cancelFunc, + options: options, + } + h.storedStrategies.Store(defaultStrategies()) + + if options.StrategiesFile == "" { + h.logger.Info("No sampling strategies source provided, using defaults") + return h, nil + } + + loadFn := h.samplingStrategyLoader(options.StrategiesFile) + strategies, err := loadStrategies(loadFn) + if err != nil { + return nil, err + } else if strategies == nil { + h.logger.Info("No sampling strategies found or URL is unavailable, using defaults") + return h, nil + } + + if !h.options.IncludeDefaultOpStrategies { + h.logger.Warn("Default operations level strategies will not be included for Ratelimiting service strategies." + + "This behavior will be changed in future releases. " + + "Cf. https://github.com/jaegertracing/jaeger/issues/5270") + h.parseStrategiesDeprecated(strategies) + } else { + h.parseStrategies(strategies) + } + + if options.ReloadInterval > 0 { + go h.autoUpdateStrategies(ctx, options.ReloadInterval, loadFn) + } + return h, nil +} + +// GetSamplingStrategy implements StrategyStore#GetSamplingStrategy. +func (h *samplingProvider) GetSamplingStrategy(_ context.Context, serviceName string) (*api_v2.SamplingStrategyResponse, error) { + storedStrategies := h.storedStrategies.Load().(*storedStrategies) + serviceStrategies := storedStrategies.serviceStrategies + if strategy, ok := serviceStrategies[serviceName]; ok { + return strategy, nil + } + h.logger.Debug("sampling strategy not found, using default", zap.String("service", serviceName)) + return storedStrategies.defaultStrategy, nil +} + +// Close stops updating the strategies +func (h *samplingProvider) Close() error { + h.cancelFunc() + return nil +} + +func (h *samplingProvider) downloadSamplingStrategies(samplingURL string) ([]byte, error) { + h.logger.Info("Downloading sampling strategies", zap.String("url", samplingURL)) + + ctx, cx := context.WithTimeout(context.Background(), time.Second) + defer cx() + req, err := http.NewRequestWithContext(ctx, http.MethodGet, samplingURL, nil) + if err != nil { + return nil, fmt.Errorf("cannot construct HTTP request: %w", err) + } + resp, err := http.DefaultClient.Do(req) + if err != nil { + return nil, fmt.Errorf("failed to download sampling strategies: %w", err) + } + defer resp.Body.Close() + + buf := new(bytes.Buffer) + if _, err = buf.ReadFrom(resp.Body); err != nil { + return nil, fmt.Errorf("failed to read sampling strategies HTTP response body: %w", err) + } + + if resp.StatusCode == http.StatusServiceUnavailable { + return nullJSON, nil + } + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf( + "receiving %s while downloading strategies file: %s", + resp.Status, + buf.String(), + ) + } + + return buf.Bytes(), nil +} + +func isURL(str string) bool { + u, err := url.Parse(str) + return err == nil && u.Scheme != "" && u.Host != "" +} + +func (h *samplingProvider) samplingStrategyLoader(strategiesFile string) strategyLoader { + if isURL(strategiesFile) { + return func() ([]byte, error) { + return h.downloadSamplingStrategies(strategiesFile) + } + } + + return func() ([]byte, error) { + h.logger.Info("Loading sampling strategies", zap.String("filename", strategiesFile)) + currBytes, err := os.ReadFile(filepath.Clean(strategiesFile)) + if err != nil { + return nil, fmt.Errorf("failed to read strategies file %s: %w", strategiesFile, err) + } + return currBytes, nil + } +} + +func (h *samplingProvider) autoUpdateStrategies(ctx context.Context, interval time.Duration, loader strategyLoader) { + lastValue := string(nullJSON) + ticker := time.NewTicker(interval) + defer ticker.Stop() + for { + select { + case <-ticker.C: + lastValue = h.reloadSamplingStrategy(loader, lastValue) + case <-ctx.Done(): + return + } + } +} + +func (h *samplingProvider) reloadSamplingStrategy(loadFn strategyLoader, lastValue string) string { + newValue, err := loadFn() + if err != nil { + h.logger.Error("failed to re-load sampling strategies", zap.Error(err)) + return lastValue + } + if lastValue == string(newValue) { + return lastValue + } + if err := h.updateSamplingStrategy(newValue); err != nil { + h.logger.Error("failed to update sampling strategies", zap.Error(err)) + return lastValue + } + return string(newValue) +} + +func (h *samplingProvider) updateSamplingStrategy(dataBytes []byte) error { + var strategies strategies + if err := json.Unmarshal(dataBytes, &strategies); err != nil { + return fmt.Errorf("failed to unmarshal sampling strategies: %w", err) + } + h.parseStrategies(&strategies) + h.logger.Info("Updated sampling strategies:" + string(dataBytes)) + return nil +} + +// TODO good candidate for a global util function +func loadStrategies(loadFn strategyLoader) (*strategies, error) { + strategyBytes, err := loadFn() + if err != nil { + return nil, err + } + + var strategies *strategies + if err := json.Unmarshal(strategyBytes, &strategies); err != nil { + return nil, fmt.Errorf("failed to unmarshal strategies: %w", err) + } + return strategies, nil +} + +func (h *samplingProvider) parseStrategiesDeprecated(strategies *strategies) { + newStore := defaultStrategies() + if strategies.DefaultStrategy != nil { + newStore.defaultStrategy = h.parseServiceStrategies(strategies.DefaultStrategy) + } + + merge := true + if newStore.defaultStrategy.OperationSampling == nil || + newStore.defaultStrategy.OperationSampling.PerOperationStrategies == nil { + merge = false + } + + for _, s := range strategies.ServiceStrategies { + newStore.serviceStrategies[s.Service] = h.parseServiceStrategies(s) + + // Merge with the default operation strategies, because only merging with + // the default strategy has no effect on service strategies (the default strategy + // is not merged with and only used as a fallback). + opS := newStore.serviceStrategies[s.Service].OperationSampling + if opS == nil { + if newStore.defaultStrategy.OperationSampling == nil || + newStore.serviceStrategies[s.Service].ProbabilisticSampling == nil { + continue + } + // Service has no per-operation strategies, so just reference the default settings and change default samplingRate. + newOpS := *newStore.defaultStrategy.OperationSampling + newOpS.DefaultSamplingProbability = newStore.serviceStrategies[s.Service].ProbabilisticSampling.SamplingRate + newStore.serviceStrategies[s.Service].OperationSampling = &newOpS + continue + } + if merge { + opS.PerOperationStrategies = mergePerOperationSamplingStrategies( + opS.PerOperationStrategies, + newStore.defaultStrategy.OperationSampling.PerOperationStrategies) + } + } + h.storedStrategies.Store(newStore) +} + +func (h *samplingProvider) parseStrategies(strategies *strategies) { + newStore := defaultStrategies() + if strategies.DefaultStrategy != nil { + newStore.defaultStrategy = h.parseServiceStrategies(strategies.DefaultStrategy) + } + + for _, s := range strategies.ServiceStrategies { + newStore.serviceStrategies[s.Service] = h.parseServiceStrategies(s) + + // Config for this service may not have per-operation strategies, + // but if the default strategy has them they should still apply. + + if newStore.defaultStrategy.OperationSampling == nil { + // Default strategy doens't have them either, nothing to do. + continue + } + + opS := newStore.serviceStrategies[s.Service].OperationSampling + if opS == nil { + // Service does not have its own per-operation rules, so copy (by value) from the default strategy. + newOpS := *newStore.defaultStrategy.OperationSampling + + // If the service's own default is probabilistic, then its sampling rate should take precedence. + if newStore.serviceStrategies[s.Service].ProbabilisticSampling != nil { + newOpS.DefaultSamplingProbability = newStore.serviceStrategies[s.Service].ProbabilisticSampling.SamplingRate + } + newStore.serviceStrategies[s.Service].OperationSampling = &newOpS + continue + } + + // If the service did have its own per-operation strategies, then merge them with the default ones. + opS.PerOperationStrategies = mergePerOperationSamplingStrategies( + opS.PerOperationStrategies, + newStore.defaultStrategy.OperationSampling.PerOperationStrategies) + } + h.storedStrategies.Store(newStore) +} + +// mergePerOperationSamplingStrategies merges two operation strategies a and b, where a takes precedence over b. +func mergePerOperationSamplingStrategies( + a, b []*api_v2.OperationSamplingStrategy, +) []*api_v2.OperationSamplingStrategy { + m := make(map[string]bool) + for _, aOp := range a { + m[aOp.Operation] = true + } + for _, bOp := range b { + if m[bOp.Operation] { + continue + } + a = append(a, bOp) + } + return a +} + +func (h *samplingProvider) parseServiceStrategies(strategy *serviceStrategy) *api_v2.SamplingStrategyResponse { + resp := h.parseStrategy(&strategy.strategy) + if len(strategy.OperationStrategies) == 0 { + return resp + } + opS := &api_v2.PerOperationSamplingStrategies{ + DefaultSamplingProbability: defaultSamplingProbability, + } + if resp.StrategyType == api_v2.SamplingStrategyType_PROBABILISTIC { + opS.DefaultSamplingProbability = resp.ProbabilisticSampling.SamplingRate + } + for _, operationStrategy := range strategy.OperationStrategies { + s, ok := h.parseOperationStrategy(operationStrategy, opS) + if !ok { + continue + } + + opS.PerOperationStrategies = append(opS.PerOperationStrategies, + &api_v2.OperationSamplingStrategy{ + Operation: operationStrategy.Operation, + ProbabilisticSampling: s.ProbabilisticSampling, + }) + } + resp.OperationSampling = opS + return resp +} + +func (h *samplingProvider) parseOperationStrategy( + strategy *operationStrategy, + parent *api_v2.PerOperationSamplingStrategies, +) (s *api_v2.SamplingStrategyResponse, ok bool) { + s = h.parseStrategy(&strategy.strategy) + if s.StrategyType == api_v2.SamplingStrategyType_RATE_LIMITING { + // TODO OperationSamplingStrategy only supports probabilistic sampling + h.logger.Warn( + fmt.Sprintf( + "Operation strategies only supports probabilistic sampling at the moment,"+ + "'%s' defaulting to probabilistic sampling with probability %f", + strategy.Operation, parent.DefaultSamplingProbability), + zap.Any("strategy", strategy)) + return nil, false + } + return s, true +} + +func (h *samplingProvider) parseStrategy(strategy *strategy) *api_v2.SamplingStrategyResponse { + switch strategy.Type { + case samplerTypeProbabilistic: + return &api_v2.SamplingStrategyResponse{ + StrategyType: api_v2.SamplingStrategyType_PROBABILISTIC, + ProbabilisticSampling: &api_v2.ProbabilisticSamplingStrategy{ + SamplingRate: strategy.Param, + }, + } + case samplerTypeRateLimiting: + return &api_v2.SamplingStrategyResponse{ + StrategyType: api_v2.SamplingStrategyType_RATE_LIMITING, + RateLimitingSampling: &api_v2.RateLimitingSamplingStrategy{ + MaxTracesPerSecond: int32(strategy.Param), + }, + } + default: + h.logger.Warn("Failed to parse sampling strategy", zap.Any("strategy", strategy)) + return defaultStrategyResponse() + } +} diff --git a/extension/jaegerremotesampling/internal/source/filesource/filesource_test.go b/extension/jaegerremotesampling/internal/source/filesource/filesource_test.go new file mode 100644 index 000000000000..fae69d1e913b --- /dev/null +++ b/extension/jaegerremotesampling/internal/source/filesource/filesource_test.go @@ -0,0 +1,588 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2018 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package filesource + +import ( + "bytes" + "context" + "encoding/gob" + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "os" + "path/filepath" + "strings" + "sync/atomic" + "testing" + "time" + + "github.com/jaegertracing/jaeger/proto-gen/api_v2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + "go.uber.org/zap/zaptest/observer" +) + +const snapshotLocation = "./fixtures/" + +// Snapshots can be regenerated via: +// +// REGENERATE_SNAPSHOTS=true go test -v ./plugin/sampling/strategyprovider/static/provider_test.go +var regenerateSnapshots = os.Getenv("REGENERATE_SNAPSHOTS") == "true" + +// strategiesJSON returns the strategy with +// a given probability. +func strategiesJSON(probability float32) string { + strategy := fmt.Sprintf(` + { + "default_strategy": { + "type": "probabilistic", + "param": 0.5 + }, + "service_strategies": [ + { + "service": "foo", + "type": "probabilistic", + "param": %.1f + }, + { + "service": "bar", + "type": "ratelimiting", + "param": 5 + } + ] + } + `, + probability, + ) + return strategy +} + +func deepCopy(s *api_v2.SamplingStrategyResponse) (*api_v2.SamplingStrategyResponse, error) { + var buf bytes.Buffer + enc := gob.NewEncoder(&buf) + dec := gob.NewDecoder(&buf) + err := enc.Encode(*s) + if err != nil { + return nil, err + } + var copyValue api_v2.SamplingStrategyResponse + err = dec.Decode(©Value) + return ©Value, err +} + +// Returns strategies in JSON format. Used for testing +// URL option for sampling strategies. +func mockStrategyServer(t *testing.T) (*httptest.Server, *atomic.Pointer[string]) { + var strategy atomic.Pointer[string] + value := strategiesJSON(0.8) + strategy.Store(&value) + f := func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case "/bad-content": + _, err := w.Write([]byte("bad-content")) + assert.NoError(t, err) + return + + case "/bad-status": + w.WriteHeader(http.StatusNotFound) + return + + case "/service-unavailable": + w.WriteHeader(http.StatusServiceUnavailable) + return + + default: + w.WriteHeader(http.StatusOK) + w.Header().Set("Content-Type", "application/json") + _, err := w.Write([]byte(*strategy.Load())) + assert.NoError(t, err) + } + } + mockserver := httptest.NewServer(http.HandlerFunc(f)) + t.Cleanup(func() { + mockserver.Close() + }) + return mockserver, &strategy +} + +func TestStrategyStoreWithFile(t *testing.T) { + _, err := NewFileSource(Options{StrategiesFile: "fileNotFound.json"}, zap.NewNop()) + require.ErrorContains(t, err, "failed to read strategies file fileNotFound.json") + + _, err = NewFileSource(Options{StrategiesFile: "fixtures/bad_strategies.json"}, zap.NewNop()) + require.EqualError(t, err, + "failed to unmarshal strategies: json: cannot unmarshal string into Go value of type filesource.strategies") + + // Test default strategy + zapCore, logs := observer.New(zap.InfoLevel) + logger := zap.New(zapCore) + provider, err := NewFileSource(Options{}, logger) + require.NoError(t, err) + message := logs.FilterMessage("No sampling strategies source provided, using defaults") + assert.Equal(t, 1, message.Len(), "Expected No sampling strategies provided log message") + s, err := provider.GetSamplingStrategy(context.Background(), "foo") + require.NoError(t, err) + assert.EqualValues(t, makeResponse(api_v2.SamplingStrategyType_PROBABILISTIC, 0.001), *s) + + // Test reading strategies from a file + provider, err = NewFileSource(Options{StrategiesFile: "fixtures/strategies.json"}, logger) + require.NoError(t, err) + s, err = provider.GetSamplingStrategy(context.Background(), "foo") + require.NoError(t, err) + assert.EqualValues(t, makeResponse(api_v2.SamplingStrategyType_PROBABILISTIC, 0.8), *s) + + s, err = provider.GetSamplingStrategy(context.Background(), "bar") + require.NoError(t, err) + assert.EqualValues(t, makeResponse(api_v2.SamplingStrategyType_RATE_LIMITING, 5), *s) + + s, err = provider.GetSamplingStrategy(context.Background(), "default") + require.NoError(t, err) + assert.EqualValues(t, makeResponse(api_v2.SamplingStrategyType_PROBABILISTIC, 0.5), *s) +} + +func TestStrategyStoreWithURL(t *testing.T) { + // Test default strategy when URL is temporarily unavailable. + zapCore, logs := observer.New(zap.InfoLevel) + logger := zap.New(zapCore) + mockServer, _ := mockStrategyServer(t) + provider, err := NewFileSource(Options{StrategiesFile: mockServer.URL + "/service-unavailable"}, logger) + require.NoError(t, err) + message := logs.FilterMessage("No sampling strategies found or URL is unavailable, using defaults") + assert.Equal(t, 1, message.Len(), "Expected No sampling strategies found log message.") + s, err := provider.GetSamplingStrategy(context.Background(), "foo") + require.NoError(t, err) + assert.EqualValues(t, makeResponse(api_v2.SamplingStrategyType_PROBABILISTIC, 0.001), *s) + + // Test downloading strategies from a URL. + provider, err = NewFileSource(Options{StrategiesFile: mockServer.URL}, logger) + require.NoError(t, err) + + s, err = provider.GetSamplingStrategy(context.Background(), "foo") + require.NoError(t, err) + assert.EqualValues(t, makeResponse(api_v2.SamplingStrategyType_PROBABILISTIC, 0.8), *s) + + s, err = provider.GetSamplingStrategy(context.Background(), "bar") + require.NoError(t, err) + assert.EqualValues(t, makeResponse(api_v2.SamplingStrategyType_RATE_LIMITING, 5), *s) +} + +func TestPerOperationSamplingStrategies(t *testing.T) { + tests := []struct { + options Options + }{ + {Options{StrategiesFile: "fixtures/operation_strategies.json"}}, + {Options{ + StrategiesFile: "fixtures/operation_strategies.json", + IncludeDefaultOpStrategies: true, + }}, + } + + for _, tc := range tests { + zapCore, logs := observer.New(zap.InfoLevel) + logger := zap.New(zapCore) + provider, err := NewFileSource(tc.options, logger) + message := logs.FilterMessage("Operation strategies only supports probabilistic sampling at the moment," + + "'op2' defaulting to probabilistic sampling with probability 0.800000") + assert.Equal(t, 1, message.Len(), "Expected Operation strategies only supports probabilistic sampling, op2 change to probability 0.8 log message") + message = logs.FilterMessage("Operation strategies only supports probabilistic sampling at the moment," + + "'op4' defaulting to probabilistic sampling with probability 0.001000") + assert.Equal(t, 1, message.Len(), "Expected Operation strategies only supports probabilistic sampling, op2 change to probability 0.8 log message") + require.NoError(t, err) + + expected := makeResponse(api_v2.SamplingStrategyType_PROBABILISTIC, 0.8) + + s, err := provider.GetSamplingStrategy(context.Background(), "foo") + require.NoError(t, err) + assert.Equal(t, api_v2.SamplingStrategyType_PROBABILISTIC, s.StrategyType) + assert.Equal(t, *expected.ProbabilisticSampling, *s.ProbabilisticSampling) + + require.NotNil(t, s.OperationSampling) + opSampling := s.OperationSampling + assert.InDelta(t, 0.8, opSampling.DefaultSamplingProbability, 0.01) + require.Len(t, opSampling.PerOperationStrategies, 4) + + assert.Equal(t, "op6", opSampling.PerOperationStrategies[0].Operation) + assert.InDelta(t, 0.5, opSampling.PerOperationStrategies[0].ProbabilisticSampling.SamplingRate, 0.01) + assert.Equal(t, "op1", opSampling.PerOperationStrategies[1].Operation) + assert.InDelta(t, 0.2, opSampling.PerOperationStrategies[1].ProbabilisticSampling.SamplingRate, 0.01) + assert.Equal(t, "op0", opSampling.PerOperationStrategies[2].Operation) + assert.InDelta(t, 0.2, opSampling.PerOperationStrategies[2].ProbabilisticSampling.SamplingRate, 0.01) + assert.Equal(t, "op7", opSampling.PerOperationStrategies[3].Operation) + assert.InDelta(t, 1.0, opSampling.PerOperationStrategies[3].ProbabilisticSampling.SamplingRate, 0.01) + + expected = makeResponse(api_v2.SamplingStrategyType_RATE_LIMITING, 5) + + s, err = provider.GetSamplingStrategy(context.Background(), "bar") + require.NoError(t, err) + assert.Equal(t, api_v2.SamplingStrategyType_RATE_LIMITING, s.StrategyType) + assert.Equal(t, *expected.RateLimitingSampling, *s.RateLimitingSampling) + + require.NotNil(t, s.OperationSampling) + opSampling = s.OperationSampling + assert.InDelta(t, 0.001, opSampling.DefaultSamplingProbability, 1e-4) + require.Len(t, opSampling.PerOperationStrategies, 5) + assert.Equal(t, "op3", opSampling.PerOperationStrategies[0].Operation) + assert.InDelta(t, 0.3, opSampling.PerOperationStrategies[0].ProbabilisticSampling.SamplingRate, 0.01) + assert.Equal(t, "op5", opSampling.PerOperationStrategies[1].Operation) + assert.InDelta(t, 0.4, opSampling.PerOperationStrategies[1].ProbabilisticSampling.SamplingRate, 0.01) + assert.Equal(t, "op0", opSampling.PerOperationStrategies[2].Operation) + assert.InDelta(t, 0.2, opSampling.PerOperationStrategies[2].ProbabilisticSampling.SamplingRate, 0.01) + assert.Equal(t, "op6", opSampling.PerOperationStrategies[3].Operation) + assert.InDelta(t, 0.0, opSampling.PerOperationStrategies[3].ProbabilisticSampling.SamplingRate, 0.01) + assert.Equal(t, "op7", opSampling.PerOperationStrategies[4].Operation) + assert.InDelta(t, 1.0, opSampling.PerOperationStrategies[4].ProbabilisticSampling.SamplingRate, 0.01) + + s, err = provider.GetSamplingStrategy(context.Background(), "default") + require.NoError(t, err) + expectedRsp := makeResponse(api_v2.SamplingStrategyType_PROBABILISTIC, 0.5) + expectedRsp.OperationSampling = &api_v2.PerOperationSamplingStrategies{ + DefaultSamplingProbability: 0.5, + PerOperationStrategies: []*api_v2.OperationSamplingStrategy{ + { + Operation: "op0", + ProbabilisticSampling: &api_v2.ProbabilisticSamplingStrategy{ + SamplingRate: 0.2, + }, + }, + { + Operation: "op6", + ProbabilisticSampling: &api_v2.ProbabilisticSamplingStrategy{ + SamplingRate: 0, + }, + }, + { + Operation: "op7", + ProbabilisticSampling: &api_v2.ProbabilisticSamplingStrategy{ + SamplingRate: 1, + }, + }, + }, + } + assert.EqualValues(t, expectedRsp, *s) + } +} + +func TestMissingServiceSamplingStrategyTypes(t *testing.T) { + zapCore, logs := observer.New(zap.InfoLevel) + logger := zap.New(zapCore) + provider, err := NewFileSource(Options{StrategiesFile: "fixtures/missing-service-types.json"}, logger) + message := logs.FilterMessage("Failed to parse sampling strategy") + assert.Equal(t, "Failed to parse sampling strategy", message.All()[0].Message) + require.NoError(t, err) + + expected := makeResponse(api_v2.SamplingStrategyType_PROBABILISTIC, defaultSamplingProbability) + + s, err := provider.GetSamplingStrategy(context.Background(), "foo") + require.NoError(t, err) + assert.Equal(t, api_v2.SamplingStrategyType_PROBABILISTIC, s.StrategyType) + assert.Equal(t, *expected.ProbabilisticSampling, *s.ProbabilisticSampling) + + require.NotNil(t, s.OperationSampling) + opSampling := s.OperationSampling + assert.InDelta(t, defaultSamplingProbability, opSampling.DefaultSamplingProbability, 1e-4) + require.Len(t, opSampling.PerOperationStrategies, 1) + assert.Equal(t, "op1", opSampling.PerOperationStrategies[0].Operation) + assert.InDelta(t, 0.2, opSampling.PerOperationStrategies[0].ProbabilisticSampling.SamplingRate, 0.001) + + expected = makeResponse(api_v2.SamplingStrategyType_PROBABILISTIC, defaultSamplingProbability) + + s, err = provider.GetSamplingStrategy(context.Background(), "bar") + require.NoError(t, err) + assert.Equal(t, api_v2.SamplingStrategyType_PROBABILISTIC, s.StrategyType) + assert.Equal(t, *expected.ProbabilisticSampling, *s.ProbabilisticSampling) + + require.NotNil(t, s.OperationSampling) + opSampling = s.OperationSampling + assert.InDelta(t, 0.001, opSampling.DefaultSamplingProbability, 1e-4) + require.Len(t, opSampling.PerOperationStrategies, 2) + assert.Equal(t, "op3", opSampling.PerOperationStrategies[0].Operation) + assert.InDelta(t, 0.3, opSampling.PerOperationStrategies[0].ProbabilisticSampling.SamplingRate, 0.01) + assert.Equal(t, "op5", opSampling.PerOperationStrategies[1].Operation) + assert.InDelta(t, 0.4, opSampling.PerOperationStrategies[1].ProbabilisticSampling.SamplingRate, 0.01) + + s, err = provider.GetSamplingStrategy(context.Background(), "default") + require.NoError(t, err) + assert.EqualValues(t, makeResponse(api_v2.SamplingStrategyType_PROBABILISTIC, 0.5), *s) +} + +func TestParseStrategy(t *testing.T) { + tests := []struct { + strategy serviceStrategy + expected api_v2.SamplingStrategyResponse + }{ + { + strategy: serviceStrategy{ + Service: "svc", + strategy: strategy{Type: "probabilistic", Param: 0.2}, + }, + expected: makeResponse(api_v2.SamplingStrategyType_PROBABILISTIC, 0.2), + }, + { + strategy: serviceStrategy{ + Service: "svc", + strategy: strategy{Type: "ratelimiting", Param: 3.5}, + }, + expected: makeResponse(api_v2.SamplingStrategyType_RATE_LIMITING, 3), + }, + } + zapCore, logs := observer.New(zap.InfoLevel) + logger := zap.New(zapCore) + provider := &samplingProvider{logger: logger} + for _, test := range tests { + tt := test + t.Run("", func(t *testing.T) { + assert.EqualValues(t, tt.expected, *provider.parseStrategy(&tt.strategy.strategy)) + }) + } + assert.Empty(t, logs.Len()) + + // Test nonexistent strategy type + actual := *provider.parseStrategy(&strategy{Type: "blah", Param: 3.5}) + expected := makeResponse(api_v2.SamplingStrategyType_PROBABILISTIC, defaultSamplingProbability) + assert.EqualValues(t, expected, actual) + message := logs.FilterMessage("Failed to parse sampling strategy") + assert.Equal(t, 1, message.Len(), "Expected Failed to parse sampling strategy log message.") +} + +func makeResponse(samplerType api_v2.SamplingStrategyType, param float64) (resp api_v2.SamplingStrategyResponse) { + resp.StrategyType = samplerType + if samplerType == api_v2.SamplingStrategyType_PROBABILISTIC { + resp.ProbabilisticSampling = &api_v2.ProbabilisticSamplingStrategy{ + SamplingRate: param, + } + } else if samplerType == api_v2.SamplingStrategyType_RATE_LIMITING { + resp.RateLimitingSampling = &api_v2.RateLimitingSamplingStrategy{ + MaxTracesPerSecond: int32(param), + } + } + return resp +} + +func TestDeepCopy(t *testing.T) { + s := &api_v2.SamplingStrategyResponse{ + StrategyType: api_v2.SamplingStrategyType_PROBABILISTIC, + ProbabilisticSampling: &api_v2.ProbabilisticSamplingStrategy{ + SamplingRate: 0.5, + }, + } + cp, err := deepCopy(s) + require.NoError(t, err) + assert.NotSame(t, cp, s) + assert.EqualValues(t, cp, s) +} + +func TestAutoUpdateStrategyWithFile(t *testing.T) { + tempFile, _ := os.CreateTemp("", "for_go_test_*.json") + require.NoError(t, tempFile.Close()) + defer func() { + require.NoError(t, os.Remove(tempFile.Name())) + }() + + // copy known fixture content into temp file which we can later overwrite + srcFile, dstFile := "fixtures/strategies.json", tempFile.Name() + srcBytes, err := os.ReadFile(srcFile) + require.NoError(t, err) + require.NoError(t, os.WriteFile(dstFile, srcBytes, 0o600)) + + ss, err := NewFileSource(Options{ + StrategiesFile: dstFile, + ReloadInterval: time.Millisecond * 10, + }, zap.NewNop()) + require.NoError(t, err) + provider := ss.(*samplingProvider) + defer provider.Close() + + // confirm baseline value + s, err := provider.GetSamplingStrategy(context.Background(), "foo") + require.NoError(t, err) + assert.EqualValues(t, makeResponse(api_v2.SamplingStrategyType_PROBABILISTIC, 0.8), *s) + + // verify that reloading is a no-op + value := provider.reloadSamplingStrategy(provider.samplingStrategyLoader(dstFile), string(srcBytes)) + assert.Equal(t, string(srcBytes), value) + + // update file with new probability of 0.9 + newStr := strings.Replace(string(srcBytes), "0.8", "0.9", 1) + require.NoError(t, os.WriteFile(dstFile, []byte(newStr), 0o600)) + + // wait for reload timer + for i := 0; i < 1000; i++ { // wait up to 1sec + s, err = provider.GetSamplingStrategy(context.Background(), "foo") + require.NoError(t, err) + if s.ProbabilisticSampling != nil && s.ProbabilisticSampling.SamplingRate == 0.9 { + break + } + time.Sleep(1 * time.Millisecond) + } + assert.EqualValues(t, makeResponse(api_v2.SamplingStrategyType_PROBABILISTIC, 0.9), *s) +} + +func TestAutoUpdateStrategyWithURL(t *testing.T) { + mockServer, mockStrategy := mockStrategyServer(t) + ss, err := NewFileSource(Options{ + StrategiesFile: mockServer.URL, + ReloadInterval: 10 * time.Millisecond, + }, zap.NewNop()) + require.NoError(t, err) + provider := ss.(*samplingProvider) + defer provider.Close() + + // confirm baseline value + s, err := provider.GetSamplingStrategy(context.Background(), "foo") + require.NoError(t, err) + assert.EqualValues(t, makeResponse(api_v2.SamplingStrategyType_PROBABILISTIC, 0.8), *s) + + // verify that reloading in no-op + value := provider.reloadSamplingStrategy( + provider.samplingStrategyLoader(mockServer.URL), + *mockStrategy.Load(), + ) + assert.Equal(t, *mockStrategy.Load(), value) + + // update original strategies with new probability of 0.9 + { + v09 := strategiesJSON(0.9) + mockStrategy.Store(&v09) + } + + // wait for reload timer + for i := 0; i < 1000; i++ { // wait up to 1sec + s, err = provider.GetSamplingStrategy(context.Background(), "foo") + require.NoError(t, err) + if s.ProbabilisticSampling != nil && s.ProbabilisticSampling.SamplingRate == 0.9 { + break + } + time.Sleep(1 * time.Millisecond) + } + assert.EqualValues(t, makeResponse(api_v2.SamplingStrategyType_PROBABILISTIC, 0.9), *s) +} + +func TestAutoUpdateStrategyErrors(t *testing.T) { + tempFile, _ := os.CreateTemp("", "for_go_test_*.json") + require.NoError(t, tempFile.Close()) + defer func() { + _ = os.Remove(tempFile.Name()) + }() + + zapCore, logs := observer.New(zap.InfoLevel) + logger := zap.New(zapCore) + + s, err := NewFileSource(Options{ + StrategiesFile: "fixtures/strategies.json", + ReloadInterval: time.Hour, + }, logger) + require.NoError(t, err) + provider := s.(*samplingProvider) + defer provider.Close() + + // check invalid file path or read failure + assert.Equal(t, "blah", provider.reloadSamplingStrategy(provider.samplingStrategyLoader(tempFile.Name()+"bad-path"), "blah")) + assert.Len(t, logs.FilterMessage("failed to re-load sampling strategies").All(), 1) + + // check bad file content + require.NoError(t, os.WriteFile(tempFile.Name(), []byte("bad value"), 0o600)) + assert.Equal(t, "blah", provider.reloadSamplingStrategy(provider.samplingStrategyLoader(tempFile.Name()), "blah")) + assert.Len(t, logs.FilterMessage("failed to update sampling strategies").All(), 1) + + // check invalid url + assert.Equal(t, "duh", provider.reloadSamplingStrategy(provider.samplingStrategyLoader("bad-url"), "duh")) + assert.Len(t, logs.FilterMessage("failed to re-load sampling strategies").All(), 2) + + // check status code other than 200 + mockServer, _ := mockStrategyServer(t) + assert.Equal(t, "duh", provider.reloadSamplingStrategy(provider.samplingStrategyLoader(mockServer.URL+"/bad-status"), "duh")) + assert.Len(t, logs.FilterMessage("failed to re-load sampling strategies").All(), 3) + + // check bad content from url + assert.Equal(t, "duh", provider.reloadSamplingStrategy(provider.samplingStrategyLoader(mockServer.URL+"/bad-content"), "duh")) + assert.Len(t, logs.FilterMessage("failed to update sampling strategies").All(), 2) +} + +func TestServiceNoPerOperationStrategies(t *testing.T) { + // given setup of strategy provider with no specific per operation sampling strategies + // and option "sampling.strategies.bugfix-5270=true" + provider, err := NewFileSource(Options{ + StrategiesFile: "fixtures/service_no_per_operation.json", + IncludeDefaultOpStrategies: true, + }, zap.NewNop()) + require.NoError(t, err) + + for _, service := range []string{"ServiceA", "ServiceB"} { + t.Run(service, func(t *testing.T) { + strategy, err := provider.GetSamplingStrategy(context.Background(), service) + require.NoError(t, err) + strategyJSON, err := json.MarshalIndent(strategy, "", " ") + require.NoError(t, err) + + testName := strings.ReplaceAll(t.Name(), "/", "_") + snapshotFile := filepath.Join(snapshotLocation, testName+".json") + expectedServiceResponse, err := os.ReadFile(snapshotFile) + require.NoError(t, err) + + assert.JSONEq(t, string(expectedServiceResponse), string(strategyJSON), + "comparing against stored snapshot. Use REGENERATE_SNAPSHOTS=true to rebuild snapshots.") + + if regenerateSnapshots { + err = os.WriteFile(snapshotFile, strategyJSON, 0o600) + require.NoError(t, err) + } + }) + } +} + +func TestServiceNoPerOperationStrategiesDeprecatedBehavior(t *testing.T) { + // test case to be removed along with removal of strategy_store.parseStrategies_deprecated, + // see https://github.com/jaegertracing/jaeger/issues/5270 for more details + + // given setup of strategy provider with no specific per operation sampling strategies + provider, err := NewFileSource(Options{ + StrategiesFile: "fixtures/service_no_per_operation.json", + }, zap.NewNop()) + require.NoError(t, err) + + for _, service := range []string{"ServiceA", "ServiceB"} { + t.Run(service, func(t *testing.T) { + strategy, err := provider.GetSamplingStrategy(context.Background(), service) + require.NoError(t, err) + strategyJSON, err := json.MarshalIndent(strategy, "", " ") + require.NoError(t, err) + + testName := strings.ReplaceAll(t.Name(), "/", "_") + snapshotFile := filepath.Join(snapshotLocation, testName+".json") + expectedServiceResponse, err := os.ReadFile(snapshotFile) + require.NoError(t, err) + + assert.JSONEq(t, string(expectedServiceResponse), string(strategyJSON), + "comparing against stored snapshot. Use REGENERATE_SNAPSHOTS=true to rebuild snapshots.") + + if regenerateSnapshots { + err = os.WriteFile(snapshotFile, strategyJSON, 0o600) + require.NoError(t, err) + } + }) + } +} + +func TestSamplingStrategyLoader(t *testing.T) { + provider := &samplingProvider{logger: zap.NewNop()} + // invalid file path + loader := provider.samplingStrategyLoader("not-exists") + _, err := loader() + require.ErrorContains(t, err, "failed to read strategies file not-exists") + + // status code other than 200 + mockServer, _ := mockStrategyServer(t) + loader = provider.samplingStrategyLoader(mockServer.URL + "/bad-status") + _, err = loader() + require.ErrorContains(t, err, "receiving 404 Not Found while downloading strategies file") + + // should download content from URL + loader = provider.samplingStrategyLoader(mockServer.URL + "/bad-content") + content, err := loader() + require.NoError(t, err) + assert.Equal(t, "bad-content", string(content)) +} diff --git a/extension/jaegerremotesampling/internal/source/filesource/fixtures/TestServiceNoPerOperationStrategiesDeprecatedBehavior_ServiceA.json b/extension/jaegerremotesampling/internal/source/filesource/fixtures/TestServiceNoPerOperationStrategiesDeprecatedBehavior_ServiceA.json new file mode 100644 index 000000000000..6834df079eb6 --- /dev/null +++ b/extension/jaegerremotesampling/internal/source/filesource/fixtures/TestServiceNoPerOperationStrategiesDeprecatedBehavior_ServiceA.json @@ -0,0 +1,16 @@ +{ + "probabilisticSampling": { + "samplingRate": 1 + }, + "operationSampling": { + "defaultSamplingProbability": 1, + "perOperationStrategies": [ + { + "operation": "/health", + "probabilisticSampling": { + "samplingRate": 0.1 + } + } + ] + } +} \ No newline at end of file diff --git a/extension/jaegerremotesampling/internal/source/filesource/fixtures/TestServiceNoPerOperationStrategiesDeprecatedBehavior_ServiceB.json b/extension/jaegerremotesampling/internal/source/filesource/fixtures/TestServiceNoPerOperationStrategiesDeprecatedBehavior_ServiceB.json new file mode 100644 index 000000000000..56e51c78391f --- /dev/null +++ b/extension/jaegerremotesampling/internal/source/filesource/fixtures/TestServiceNoPerOperationStrategiesDeprecatedBehavior_ServiceB.json @@ -0,0 +1,6 @@ +{ + "strategyType": 1, + "rateLimitingSampling": { + "maxTracesPerSecond": 3 + } +} \ No newline at end of file diff --git a/extension/jaegerremotesampling/internal/source/filesource/fixtures/TestServiceNoPerOperationStrategies_ServiceA.json b/extension/jaegerremotesampling/internal/source/filesource/fixtures/TestServiceNoPerOperationStrategies_ServiceA.json new file mode 100644 index 000000000000..6834df079eb6 --- /dev/null +++ b/extension/jaegerremotesampling/internal/source/filesource/fixtures/TestServiceNoPerOperationStrategies_ServiceA.json @@ -0,0 +1,16 @@ +{ + "probabilisticSampling": { + "samplingRate": 1 + }, + "operationSampling": { + "defaultSamplingProbability": 1, + "perOperationStrategies": [ + { + "operation": "/health", + "probabilisticSampling": { + "samplingRate": 0.1 + } + } + ] + } +} \ No newline at end of file diff --git a/extension/jaegerremotesampling/internal/source/filesource/fixtures/TestServiceNoPerOperationStrategies_ServiceB.json b/extension/jaegerremotesampling/internal/source/filesource/fixtures/TestServiceNoPerOperationStrategies_ServiceB.json new file mode 100644 index 000000000000..cc28f904fefa --- /dev/null +++ b/extension/jaegerremotesampling/internal/source/filesource/fixtures/TestServiceNoPerOperationStrategies_ServiceB.json @@ -0,0 +1,17 @@ +{ + "strategyType": 1, + "rateLimitingSampling": { + "maxTracesPerSecond": 3 + }, + "operationSampling": { + "defaultSamplingProbability": 0.2, + "perOperationStrategies": [ + { + "operation": "/health", + "probabilisticSampling": { + "samplingRate": 0.1 + } + } + ] + } +} \ No newline at end of file diff --git a/extension/jaegerremotesampling/internal/source/filesource/fixtures/bad_strategies.json b/extension/jaegerremotesampling/internal/source/filesource/fixtures/bad_strategies.json new file mode 100644 index 000000000000..209a97341c53 --- /dev/null +++ b/extension/jaegerremotesampling/internal/source/filesource/fixtures/bad_strategies.json @@ -0,0 +1 @@ +"nonsense" diff --git a/extension/jaegerremotesampling/internal/source/filesource/fixtures/missing-service-types.json b/extension/jaegerremotesampling/internal/source/filesource/fixtures/missing-service-types.json new file mode 100644 index 000000000000..0d3d5f2a3c00 --- /dev/null +++ b/extension/jaegerremotesampling/internal/source/filesource/fixtures/missing-service-types.json @@ -0,0 +1,33 @@ +{ + "default_strategy": { + "type": "probabilistic", + "param": 0.5 + }, + "service_strategies": [ + { + "service": "foo", + "operation_strategies": [ + { + "operation": "op1", + "type": "probabilistic", + "param": 0.2 + } + ] + }, + { + "service": "bar", + "operation_strategies": [ + { + "operation": "op3", + "type": "probabilistic", + "param": 0.3 + }, + { + "operation": "op5", + "type": "probabilistic", + "param": 0.4 + } + ] + } + ] +} diff --git a/extension/jaegerremotesampling/internal/source/filesource/fixtures/operation_strategies.json b/extension/jaegerremotesampling/internal/source/filesource/fixtures/operation_strategies.json new file mode 100644 index 000000000000..8a1b7677aab1 --- /dev/null +++ b/extension/jaegerremotesampling/internal/source/filesource/fixtures/operation_strategies.json @@ -0,0 +1,74 @@ +{ + "default_strategy": { + "type": "probabilistic", + "param": 0.5, + "operation_strategies": [ + { + "operation": "op0", + "type": "probabilistic", + "param": 0.2 + }, + { + "operation": "op6", + "type": "probabilistic", + "param": 0 + }, + { + "operation": "spam", + "type": "ratelimiting", + "param": 1 + }, + { + "operation": "op7", + "type": "probabilistic", + "param": 1 + } + ] + }, + "service_strategies": [ + { + "service": "foo", + "type": "probabilistic", + "param": 0.8, + "operation_strategies": [ + { + "operation": "op6", + "type": "probabilistic", + "param": 0.5 + }, + { + "operation": "op1", + "type": "probabilistic", + "param": 0.2 + }, + { + "operation": "op2", + "type": "ratelimiting", + "param": 10 + } + ] + }, + { + "service": "bar", + "type": "ratelimiting", + "param": 5, + "operation_strategies": [ + { + "operation": "op3", + "type": "probabilistic", + "param": 0.3 + }, + { + "operation": "op4", + "type": "ratelimiting", + "param": 100 + }, + { + "operation": "op5", + "type": "probabilistic", + "param": 0.4 + } + ] + } + ] +} diff --git a/extension/jaegerremotesampling/internal/source/filesource/fixtures/service_no_per_operation.json b/extension/jaegerremotesampling/internal/source/filesource/fixtures/service_no_per_operation.json new file mode 100644 index 000000000000..29b50d9f4d3f --- /dev/null +++ b/extension/jaegerremotesampling/internal/source/filesource/fixtures/service_no_per_operation.json @@ -0,0 +1,25 @@ +{ + "service_strategies": [ + { + "service": "ServiceA", + "type": "probabilistic", + "param": 1.0 + }, + { + "service": "ServiceB", + "type": "ratelimiting", + "param": 3 + } + ], + "default_strategy": { + "type": "probabilistic", + "param": 0.2, + "operation_strategies": [ + { + "operation": "/health", + "type": "probabilistic", + "param": 0.1 + } + ] + } +} diff --git a/extension/jaegerremotesampling/internal/source/filesource/fixtures/strategies.json b/extension/jaegerremotesampling/internal/source/filesource/fixtures/strategies.json new file mode 100644 index 000000000000..e81d43984963 --- /dev/null +++ b/extension/jaegerremotesampling/internal/source/filesource/fixtures/strategies.json @@ -0,0 +1,18 @@ +{ + "default_strategy": { + "type": "probabilistic", + "param": 0.5 + }, + "service_strategies": [ + { + "service": "foo", + "type": "probabilistic", + "param": 0.8 + }, + { + "service": "bar", + "type": "ratelimiting", + "param": 5 + } + ] +} diff --git a/extension/jaegerremotesampling/internal/source/filesource/model.go b/extension/jaegerremotesampling/internal/source/filesource/model.go new file mode 100644 index 000000000000..90f70d9087a0 --- /dev/null +++ b/extension/jaegerremotesampling/internal/source/filesource/model.go @@ -0,0 +1,31 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2018 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package filesource // import "github.com/open-telemetry/opentelemetry-collector-contrib/extension/jaegerremotesampling/internal/source/filesource" + +// strategy defines a sampling strategy. Type can be "probabilistic" or "ratelimiting" +// and Param will represent "sampling probability" and "max traces per second" respectively. +type strategy struct { + Type string `json:"type"` + Param float64 `json:"param"` +} + +// operationStrategy defines an operation specific sampling strategy. +type operationStrategy struct { + Operation string `json:"operation"` + strategy +} + +// serviceStrategy defines a service specific sampling strategy. +type serviceStrategy struct { + Service string `json:"service"` + OperationStrategies []*operationStrategy `json:"operation_strategies"` + strategy +} + +// strategies holds a default sampling strategy and service specific sampling strategies. +type strategies struct { + DefaultStrategy *serviceStrategy `json:"default_strategy"` + ServiceStrategies []*serviceStrategy `json:"service_strategies"` +} diff --git a/extension/jaegerremotesampling/internal/source/filesource/options.go b/extension/jaegerremotesampling/internal/source/filesource/options.go new file mode 100644 index 000000000000..f0c8ab9d2cd2 --- /dev/null +++ b/extension/jaegerremotesampling/internal/source/filesource/options.go @@ -0,0 +1,21 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2018 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package filesource // import "github.com/open-telemetry/opentelemetry-collector-contrib/extension/jaegerremotesampling/internal/source/filesource" + +import ( + "time" +) + +// Options holds configuration for the static sampling strategy store. +type Options struct { + // StrategiesFile is the path for the sampling strategies file in JSON format + StrategiesFile string + // ReloadInterval is the time interval to check and reload sampling strategies file + ReloadInterval time.Duration + // Flag for enabling possibly breaking change which includes default operations level + // strategies when calculating Ratelimiting type service level strategy + // more information https://github.com/jaegertracing/jaeger/issues/5270 + IncludeDefaultOpStrategies bool +} diff --git a/extension/jaegerremotesampling/internal/source/interface.go b/extension/jaegerremotesampling/internal/source/interface.go new file mode 100644 index 000000000000..69743ee822ec --- /dev/null +++ b/extension/jaegerremotesampling/internal/source/interface.go @@ -0,0 +1,21 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2018 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package source // import "github.com/open-telemetry/opentelemetry-collector-contrib/extension/jaegerremotesampling/internal/source" + +import ( + "context" + "io" + + "github.com/jaegertracing/jaeger/proto-gen/api_v2" +) + +// Source keeps track of service specific sampling strategies. +type Source interface { + // Close() from io.Closer stops the processor from calculating probabilities. + io.Closer + + // GetSamplingStrategy retrieves the sampling strategy for the specified service. + GetSamplingStrategy(ctx context.Context, serviceName string) (*api_v2.SamplingStrategyResponse, error) +} diff --git a/extension/jaegerremotesampling/internal/source/remotesource/manager.go b/extension/jaegerremotesampling/internal/source/remotesource/manager.go new file mode 100644 index 000000000000..7fd11b15b4bc --- /dev/null +++ b/extension/jaegerremotesampling/internal/source/remotesource/manager.go @@ -0,0 +1,34 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2018 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package remotesource // import "github.com/open-telemetry/opentelemetry-collector-contrib/extension/jaegerremotesampling/internal/source/remotesource" + +import ( + "context" + "fmt" + + "github.com/jaegertracing/jaeger/proto-gen/api_v2" + "google.golang.org/grpc" +) + +// ConfigManagerProxy returns sampling decisions from collector over gRPC. +type ConfigManagerProxy struct { + client api_v2.SamplingManagerClient +} + +// NewConfigManager creates gRPC sampling manager. +func NewConfigManager(conn *grpc.ClientConn) *ConfigManagerProxy { + return &ConfigManagerProxy{ + client: api_v2.NewSamplingManagerClient(conn), + } +} + +// GetSamplingStrategy returns sampling strategies from collector. +func (s *ConfigManagerProxy) GetSamplingStrategy(ctx context.Context, serviceName string) (*api_v2.SamplingStrategyResponse, error) { + resp, err := s.client.GetSamplingStrategy(ctx, &api_v2.SamplingStrategyParameters{ServiceName: serviceName}) + if err != nil { + return nil, fmt.Errorf("failed to get sampling strategy: %w", err) + } + return resp, nil +} diff --git a/extension/jaegerremotesampling/internal/source/remotesource/manager_test.go b/extension/jaegerremotesampling/internal/source/remotesource/manager_test.go new file mode 100644 index 000000000000..f85df51cbc41 --- /dev/null +++ b/extension/jaegerremotesampling/internal/source/remotesource/manager_test.go @@ -0,0 +1,59 @@ +// Copyright The OpenTelemetry Authors +// Copyright (c) 2018 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package remotesource + +import ( + "context" + "net" + "testing" + + "github.com/jaegertracing/jaeger/proto-gen/api_v2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +func TestSamplingManager_GetSamplingStrategy(t *testing.T) { + s, addr := initializeGRPCTestServer(t, func(s *grpc.Server) { + api_v2.RegisterSamplingManagerServer(s, &mockSamplingHandler{}) + }) + conn, err := grpc.NewClient(addr.String(), grpc.WithTransportCredentials(insecure.NewCredentials())) + t.Cleanup(func() { require.NoError(t, conn.Close()) }) + require.NoError(t, err) + defer s.GracefulStop() + manager := NewConfigManager(conn) + resp, err := manager.GetSamplingStrategy(context.Background(), "any") + require.NoError(t, err) + assert.Equal(t, &api_v2.SamplingStrategyResponse{StrategyType: api_v2.SamplingStrategyType_PROBABILISTIC}, resp) +} + +func TestSamplingManager_GetSamplingStrategy_error(t *testing.T) { + conn, err := grpc.NewClient("foo", grpc.WithTransportCredentials(insecure.NewCredentials())) + t.Cleanup(func() { require.NoError(t, conn.Close()) }) + require.NoError(t, err) + manager := NewConfigManager(conn) + resp, err := manager.GetSamplingStrategy(context.Background(), "any") + require.Nil(t, resp) + assert.ErrorContains(t, err, "failed to get sampling strategy") +} + +type mockSamplingHandler struct{} + +func (*mockSamplingHandler) GetSamplingStrategy(context.Context, *api_v2.SamplingStrategyParameters) (*api_v2.SamplingStrategyResponse, error) { + return &api_v2.SamplingStrategyResponse{StrategyType: api_v2.SamplingStrategyType_PROBABILISTIC}, nil +} + +func initializeGRPCTestServer(t *testing.T, beforeServe func(server *grpc.Server)) (*grpc.Server, net.Addr) { + server := grpc.NewServer() + lis, err := net.Listen("tcp", "localhost:0") + require.NoError(t, err) + beforeServe(server) + go func() { + err := server.Serve(lis) + assert.NoError(t, err) + }() + return server, lis.Addr() +} diff --git a/extension/jaegerremotesampling/internal/remote_strategy_cache.go b/extension/jaegerremotesampling/internal/source/remotesource/remote_strategy_cache.go similarity index 96% rename from extension/jaegerremotesampling/internal/remote_strategy_cache.go rename to extension/jaegerremotesampling/internal/source/remotesource/remote_strategy_cache.go index cb6375c2ce0b..4dc29c912ec4 100644 --- a/extension/jaegerremotesampling/internal/remote_strategy_cache.go +++ b/extension/jaegerremotesampling/internal/source/remotesource/remote_strategy_cache.go @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package internal // import "github.com/open-telemetry/opentelemetry-collector-contrib/extension/jaegerremotesampling/internal" +package remotesource // import "github.com/open-telemetry/opentelemetry-collector-contrib/extension/jaegerremotesampling/internal/source/remotesource" import ( "context" diff --git a/extension/jaegerremotesampling/internal/remote_strategy_cache_test.go b/extension/jaegerremotesampling/internal/source/remotesource/remote_strategy_cache_test.go similarity index 99% rename from extension/jaegerremotesampling/internal/remote_strategy_cache_test.go rename to extension/jaegerremotesampling/internal/source/remotesource/remote_strategy_cache_test.go index 816c333e02c9..e4d3e7f2a082 100644 --- a/extension/jaegerremotesampling/internal/remote_strategy_cache_test.go +++ b/extension/jaegerremotesampling/internal/source/remotesource/remote_strategy_cache_test.go @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package internal +package remotesource import ( "context" diff --git a/extension/jaegerremotesampling/internal/remote_strategy_store.go b/extension/jaegerremotesampling/internal/source/remotesource/remote_strategy_store.go similarity index 77% rename from extension/jaegerremotesampling/internal/remote_strategy_store.go rename to extension/jaegerremotesampling/internal/source/remotesource/remote_strategy_store.go index 68ce1be8843b..a38c5c728236 100644 --- a/extension/jaegerremotesampling/internal/remote_strategy_store.go +++ b/extension/jaegerremotesampling/internal/source/remotesource/remote_strategy_store.go @@ -1,7 +1,7 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 -package internal // import "github.com/open-telemetry/opentelemetry-collector-contrib/extension/jaegerremotesampling/internal" +package remotesource // import "github.com/open-telemetry/opentelemetry-collector-contrib/extension/jaegerremotesampling/internal/source/remotesource" import ( "context" @@ -9,30 +9,30 @@ import ( "io" "time" - grpcstore "github.com/jaegertracing/jaeger/cmd/agent/app/configmanager/grpc" - "github.com/jaegertracing/jaeger/cmd/collector/app/sampling/samplingstrategy" "github.com/jaegertracing/jaeger/proto-gen/api_v2" "go.opentelemetry.io/collector/config/configgrpc" "go.opentelemetry.io/collector/config/configopaque" "google.golang.org/grpc" "google.golang.org/grpc/metadata" + + "github.com/open-telemetry/opentelemetry-collector-contrib/extension/jaegerremotesampling/internal/source" ) type grpcRemoteStrategyStore struct { headerAdditions map[string]configopaque.String - delegate *grpcstore.ConfigManagerProxy + delegate *ConfigManagerProxy cache serviceStrategyCache } -// NewRemoteStrategyStore returns a StrategyStore that delegates to the configured Jaeger gRPC endpoint, making +// NewRemoteSource returns a StrategyStore that delegates to the configured Jaeger gRPC endpoint, making // extension-configured enhancements (header additions only for now) to the gRPC context of every outbound gRPC call. // Note: it would be nice to expand the configuration surface to include an optional TTL-based caching behavior // for service-specific outbound GetSamplingStrategy calls. -func NewRemoteStrategyStore( +func NewRemoteSource( conn *grpc.ClientConn, grpcClientSettings *configgrpc.ClientConfig, reloadInterval time.Duration, -) (samplingstrategy.Provider, io.Closer) { +) (source.Source, io.Closer) { cache := newNoopStrategyCache() if reloadInterval > 0 { cache = newServiceStrategyCache(reloadInterval) @@ -40,7 +40,7 @@ func NewRemoteStrategyStore( return &grpcRemoteStrategyStore{ headerAdditions: grpcClientSettings.Headers, - delegate: grpcstore.NewConfigManager(conn), + delegate: NewConfigManager(conn), cache: cache, }, cache } From 0448c783eebafbfda1289a08d95ed664285014ac Mon Sep 17 00:00:00 2001 From: Nassim Boutekedjiret <nassim@sentrysoftware.com> Date: Mon, 13 Jan 2025 19:51:18 +0100 Subject: [PATCH 09/13] [exporter/bmchelix] New component: BMC Helix Exporter (#36964) #### Description This pull request introduces a new component for exporting metrics to BMC Helix. The changes include adding the new component to various configuration files, creating necessary documentation, and implementing the component's configuration and factory logic. Key changes include: ##### New Component Addition: * Added a new changelog entry for the BMC Helix exporter in `.chloggen/bmchelixexporter-new-component.yaml`. * Updated `.github/CODEOWNERS` to include the new BMC Helix exporter. ##### Documentation: * Created `README.md` for the BMC Helix exporter with detailed setup instructions and examples. ##### Configuration and Factory Implementation: * Implemented configuration struct and validation logic in `config.go`. * Created tests for the configuration in `config_test.go`. * Added factory methods for creating the exporter in `factory.go`. * Created tests for the factory methods in `factory_test.go`. ##### Miscellaneous: * Included the common Makefile in `exporter/bmchelixexporter/Makefile`. * Added package documentation in `doc.go`. #### Link to tracking issue Fixes #36773 --------- Co-authored-by: Bertrand Martin <32521698+bertysentry@users.noreply.github.com> --- .chloggen/bmchelixexporter-new-component.yaml | 27 +++ .github/CODEOWNERS | 1 + .github/ISSUE_TEMPLATE/bug_report.yaml | 1 + .github/ISSUE_TEMPLATE/feature_request.yaml | 1 + .github/ISSUE_TEMPLATE/other.yaml | 1 + .github/ISSUE_TEMPLATE/unmaintained.yaml | 1 + exporter/bmchelixexporter/Makefile | 1 + exporter/bmchelixexporter/README.md | 125 ++++++++++++++ exporter/bmchelixexporter/config.go | 34 ++++ exporter/bmchelixexporter/config_test.go | 132 +++++++++++++++ exporter/bmchelixexporter/doc.go | 7 + exporter/bmchelixexporter/factory.go | 46 +++++ exporter/bmchelixexporter/factory_test.go | 39 +++++ .../generated_component_test.go | 136 +++++++++++++++ .../generated_package_test.go | 13 ++ exporter/bmchelixexporter/go.mod | 63 +++++++ exporter/bmchelixexporter/go.sum | 160 ++++++++++++++++++ .../internal/metadata/generated_status.go | 16 ++ exporter/bmchelixexporter/metadata.yaml | 9 + .../bmchelixexporter/testdata/config.yaml | 12 ++ versions.yaml | 1 + 21 files changed, 826 insertions(+) create mode 100644 .chloggen/bmchelixexporter-new-component.yaml create mode 100644 exporter/bmchelixexporter/Makefile create mode 100644 exporter/bmchelixexporter/README.md create mode 100644 exporter/bmchelixexporter/config.go create mode 100644 exporter/bmchelixexporter/config_test.go create mode 100644 exporter/bmchelixexporter/doc.go create mode 100644 exporter/bmchelixexporter/factory.go create mode 100644 exporter/bmchelixexporter/factory_test.go create mode 100644 exporter/bmchelixexporter/generated_component_test.go create mode 100644 exporter/bmchelixexporter/generated_package_test.go create mode 100644 exporter/bmchelixexporter/go.mod create mode 100644 exporter/bmchelixexporter/go.sum create mode 100644 exporter/bmchelixexporter/internal/metadata/generated_status.go create mode 100644 exporter/bmchelixexporter/metadata.yaml create mode 100644 exporter/bmchelixexporter/testdata/config.yaml diff --git a/.chloggen/bmchelixexporter-new-component.yaml b/.chloggen/bmchelixexporter-new-component.yaml new file mode 100644 index 000000000000..dff4b8eb0a65 --- /dev/null +++ b/.chloggen/bmchelixexporter-new-component.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: new_component + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: exporter/bmchelix + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: "Add a new component for exporting metrics to BMC Helix" + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [36773] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [user] \ No newline at end of file diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 9956b99e4795..9ab6d5813018 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -49,6 +49,7 @@ exporter/awss3exporter/ @open-telemetry/collector-cont exporter/awsxrayexporter/ @open-telemetry/collector-contrib-approvers @wangzlei @srprash exporter/azuredataexplorerexporter/ @open-telemetry/collector-contrib-approvers @ag-ramachandran exporter/azuremonitorexporter/ @open-telemetry/collector-contrib-approvers @pcwiese +exporter/bmchelixexporter/ @open-telemetry/collector-contrib-approvers @bertysentry @NassimBtk exporter/carbonexporter/ @open-telemetry/collector-contrib-approvers @aboguszewski-sumo exporter/cassandraexporter/ @open-telemetry/collector-contrib-approvers @atoulme @emreyalvac exporter/clickhouseexporter/ @open-telemetry/collector-contrib-approvers @hanjm @dmitryax @Frapschen @SpencerTorres diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index e3a425d12b06..a43e01a2883c 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -48,6 +48,7 @@ body: - exporter/awsxray - exporter/azuredataexplorer - exporter/azuremonitor + - exporter/bmchelix - exporter/carbon - exporter/cassandra - exporter/clickhouse diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml index 6bd5fcdd0bc7..939526e5d2b6 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yaml +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -42,6 +42,7 @@ body: - exporter/awsxray - exporter/azuredataexplorer - exporter/azuremonitor + - exporter/bmchelix - exporter/carbon - exporter/cassandra - exporter/clickhouse diff --git a/.github/ISSUE_TEMPLATE/other.yaml b/.github/ISSUE_TEMPLATE/other.yaml index 03bc39c5ed22..372577dade46 100644 --- a/.github/ISSUE_TEMPLATE/other.yaml +++ b/.github/ISSUE_TEMPLATE/other.yaml @@ -42,6 +42,7 @@ body: - exporter/awsxray - exporter/azuredataexplorer - exporter/azuremonitor + - exporter/bmchelix - exporter/carbon - exporter/cassandra - exporter/clickhouse diff --git a/.github/ISSUE_TEMPLATE/unmaintained.yaml b/.github/ISSUE_TEMPLATE/unmaintained.yaml index ef0678ebdbcd..2ea0e7ce18bc 100644 --- a/.github/ISSUE_TEMPLATE/unmaintained.yaml +++ b/.github/ISSUE_TEMPLATE/unmaintained.yaml @@ -47,6 +47,7 @@ body: - exporter/awsxray - exporter/azuredataexplorer - exporter/azuremonitor + - exporter/bmchelix - exporter/carbon - exporter/cassandra - exporter/clickhouse diff --git a/exporter/bmchelixexporter/Makefile b/exporter/bmchelixexporter/Makefile new file mode 100644 index 000000000000..c1496226e590 --- /dev/null +++ b/exporter/bmchelixexporter/Makefile @@ -0,0 +1 @@ +include ../../Makefile.Common \ No newline at end of file diff --git a/exporter/bmchelixexporter/README.md b/exporter/bmchelixexporter/README.md new file mode 100644 index 000000000000..c66eea196c5f --- /dev/null +++ b/exporter/bmchelixexporter/README.md @@ -0,0 +1,125 @@ +# BMC Helix Exporter + +<!-- status autogenerated section --> +| Status | | +| ------------- |-----------| +| Stability | [development]: metrics | +| Distributions | [] | +| Issues | [](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aopen+is%3Aissue+label%3Aexporter%2Fbmchelix) [](https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aclosed+is%3Aissue+label%3Aexporter%2Fbmchelix) | +| [Code Owners](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/CONTRIBUTING.md#becoming-a-code-owner) | [@bertysentry](https://www.github.com/bertysentry), [@NassimBtk](https://www.github.com/NassimBtk) | + +[development]: https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/component-stability.md#development +<!-- end autogenerated section --> + +This exporter supports sending metrics to [BMC Helix Operations Management](https://www.bmc.com/it-solutions/bmc-helix-operations-management.html) through its [metric ingestion REST API](https://docs.bmc.com/docs/helixoperationsmanagement/244/en/metric-operation-management-endpoints-in-the-rest-api-1392780044.html). + +## Getting Started + +The following settings are **required**: + +- `endpoint`: is the *BMC Helix Portal URL* of your environment, at **onbmc.com** for a BMC Helix SaaS tenant (e.g., `https://company.onbmc.com`), or your own Helix Portal URL for an on-prem instance. +- `api_key`: API key to authenticate the exporter. Connect to BMC Helix Operations Management, go to the Administration > Repository page, and click on the Copy API Key button to get your API Key. Alternatively, it is recommended to create and use a dedicated [authentication key for external integration](https://docs.bmc.com/docs/helixportal244/using-api-keys-for-external-integrations-1391501992.html). + +Example: + +```yaml +exporters: + bmchelix/helix1: + endpoint: https://company.onbmc.com + api_key: <api-key> +``` + +### Optional Settings + +The following settings can be **optionally configured**: + +- `timeout`: (default = `10s`) Timeout for requests made to the BMC Helix. +- `retry_on_failure` [details here](https://github.com/open-telemetry/opentelemetry-collector/tree/main/exporter/exporterhelper#configuration) + - `enabled` (default = true) + - `initial_interval` (default = 5s) Time to wait after the first failure before retrying; ignored if `enabled` is false. + - `max_interval` (default = 30s) The upper bound on backoff; ignored if `enabled` is false. + - `max_elapsed_time` (default = 300s) The maximum amount of time spent trying to send a batch; ignored if `enabled` is false. If set to 0, the retries are never stopped. + +Example: + +```yaml +exporters: + bmchelix/helix2: + endpoint: https://company.onbmc.com + api_key: <api-key> + timeout: 20s + retry_on_failure: + enabled: true + initial_interval: 5s + max_interval: 1m + max_elapsed_time: 8m +``` + +--- + +## Setting Required Attributes for Metrics + +To ensure metrics are correctly populated in BMC Helix, the following attributes must be set either at the *Resource* level, or at the *Metric* level: + +- `entityName`: Unique identifier for the entity. Used as display name if `instanceName` is missing. +- `entityTypeId`: Type identifier for the entity. +- `instanceName`: Display name of the entity. + +> **Note:** If `entityName` or `entityTypeId` is missing, the metric will not be exported. + +To ensure the necessary attributes are present, it is recommended to leverage the [transform processor](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/processor/transformprocessor) with [OTTL](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/pkg/ottl), and include it in the configuration of the telemetry pipeline. + +The minimal pipeline most often looks like: `OTEL metrics --> (batch/memory limit) --> transform processor --> bmchelix exporter`. + +### Transformer Example for Hardware Metrics + +You can use the following OpenTelemetry Transformation Language (OTTL) configuration to map these attributes dynamically: + +```yaml + transform/hw_to_helix: + # Apply transformations to all metrics + metric_statements: + + - context: datapoint + statements: + # Create a new attribute 'entityName' with the value of 'id' + - set(attributes["entityName"], attributes["id"]) where attributes["id"] != nil + # Create a new attribute 'instanceName' with the value of 'name' + - set(attributes["instanceName"], attributes["name"]) where attributes["name"] != nil + + - context: datapoint + conditions: + - IsMatch(metric.name, ".*\\.agent\\..*") + statements: + - set(attributes["entityName"], attributes["host.id"]) where attributes["host.id"] != nil + - set(attributes["instanceName"], attributes["service.name"]) where attributes["service.name"] != nil + - set(attributes["entityTypeId"], "agent") + + - context: datapoint + statements: + # Mapping entityTypeId based on metric names and attributes + - set(attributes["entityTypeId"], "connector") where IsMatch(metric.name, ".*\\.connector\\..*") + - set(attributes["entityTypeId"], "host") where IsMatch(metric.name, ".*\\.host\\..*") or attributes["hw.type"] == "host" + - set(attributes["entityTypeId"], "battery") where IsMatch(metric.name, "hw\\.battery\\..*") or attributes["hw.type"] == "battery" + - set(attributes["entityTypeId"], "blade") where IsMatch(metric.name, "hw\\.blade\\..*") or attributes["hw.type"] == "blade" + - set(attributes["entityTypeId"], "cpu") where IsMatch(metric.name, "hw\\.cpu\\..*") or attributes["hw.type"] == "cpu" + - set(attributes["entityTypeId"], "disk_controller") where IsMatch(metric.name, "hw\\.disk_controller\\..*") or attributes["hw.type"] == "disk_controller" + - set(attributes["entityTypeId"], "enclosure") where IsMatch(metric.name, "hw\\.enclosure\\..*") or attributes["hw.type"] == "enclosure" + - set(attributes["entityTypeId"], "fan") where IsMatch(metric.name, "hw\\.fan\\..*") or attributes["hw.type"] == "fan" + - set(attributes["entityTypeId"], "gpu") where IsMatch(metric.name, "hw\\.gpu\\..*") or attributes["hw.type"] == "gpu" + - set(attributes["entityTypeId"], "led") where IsMatch(metric.name, "hw\\.led\\..*") or attributes["hw.type"] == "led" + - set(attributes["entityTypeId"], "logical_disk") where IsMatch(metric.name, "hw\\.logical_disk\\..*") or attributes["hw.type"] == "logical_disk" + - set(attributes["entityTypeId"], "lun") where IsMatch(metric.name, "hw\\.lun\\..*") or attributes["hw.type"] == "lun" + - set(attributes["entityTypeId"], "memory") where IsMatch(metric.name, "hw\\.memory\\..*") or attributes["hw.type"] == "memory" + - set(attributes["entityTypeId"], "network") where IsMatch(metric.name, "hw\\.network\\..*") or attributes["hw.type"] == "network" + - set(attributes["entityTypeId"], "other_device") where IsMatch(metric.name, "hw\\.other_device\\..*") or attributes["hw.type"] == "other_device" + - set(attributes["entityTypeId"], "physical_disk") where IsMatch(metric.name, "hw\\.physical_disk\\..*") or attributes["hw.type"] == "physical_disk" + - set(attributes["entityTypeId"], "power_supply") where IsMatch(metric.name, "hw\\.power_supply\\..*") or attributes["hw.type"] == "power_supply" + - set(attributes["entityTypeId"], "robotics") where IsMatch(metric.name, "hw\\.robotics\\..*") or attributes["hw.type"] == "robotics" + - set(attributes["entityTypeId"], "tape_drive") where IsMatch(metric.name, "hw\\.tape_drive\\..*") or attributes["hw.type"] == "tape_drive" + - set(attributes["entityTypeId"], "temperature") where IsMatch(metric.name, "hw\\.temperature.*") or attributes["hw.type"] == "temperature" + - set(attributes["entityTypeId"], "vm") where IsMatch(metric.name, "hw\\.vm\\..*") or attributes["hw.type"] == "vm" + - set(attributes["entityTypeId"], "voltage") where IsMatch(metric.name, "hw\\.voltage.*") or attributes["hw.type"] == "voltage" +``` + +This transformer dynamically sets the attributes required for BMC Helix based on metric names and resource attributes. diff --git a/exporter/bmchelixexporter/config.go b/exporter/bmchelixexporter/config.go new file mode 100644 index 000000000000..35590af2d880 --- /dev/null +++ b/exporter/bmchelixexporter/config.go @@ -0,0 +1,34 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package bmchelixexporter // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/bmchelixexporter" + +import ( + "errors" + "time" + + "go.opentelemetry.io/collector/config/configretry" +) + +// Config struct is used to store the configuration of the exporter +type Config struct { + Endpoint string `mapstructure:"endpoint"` + APIKey string `mapstructure:"api_key"` + Timeout time.Duration `mapstructure:"timeout"` + RetryConfig configretry.BackOffConfig `mapstructure:"retry_on_failure"` +} + +// validate the configuration +func (c *Config) Validate() error { + if c.Endpoint == "" { + return errors.New("endpoint is required") + } + if c.APIKey == "" { + return errors.New("api key is required") + } + if c.Timeout <= 0 { + return errors.New("timeout must be a positive integer") + } + + return nil +} diff --git a/exporter/bmchelixexporter/config_test.go b/exporter/bmchelixexporter/config_test.go new file mode 100644 index 000000000000..140ce5526447 --- /dev/null +++ b/exporter/bmchelixexporter/config_test.go @@ -0,0 +1,132 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package bmchelixexporter + +import ( + "path/filepath" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/config/configretry" + "go.opentelemetry.io/collector/confmap/confmaptest" + + "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/bmchelixexporter/internal/metadata" +) + +func TestLoadConfig(t *testing.T) { + t.Parallel() + + cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml")) + require.NoError(t, err) + + tests := []struct { + id component.ID + expected component.Config + errorMessage string + }{ + { + id: component.NewIDWithName(metadata.Type, "helix1"), + expected: &Config{ + Endpoint: "https://helix1:8080", + APIKey: "api_key", + Timeout: 10 * time.Second, + RetryConfig: configretry.NewDefaultBackOffConfig(), + }, + }, + { + id: component.NewIDWithName(metadata.Type, "helix2"), + expected: &Config{ + Endpoint: "https://helix2:8080", + APIKey: "api_key", + Timeout: 20 * time.Second, + RetryConfig: configretry.BackOffConfig{ + Enabled: true, + InitialInterval: 5 * time.Second, + RandomizationFactor: 0.5, + Multiplier: 1.5, + MaxInterval: 1 * time.Minute, + MaxElapsedTime: 8 * time.Minute, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.id.String(), func(t *testing.T) { + factory := NewFactory() + cfg := factory.CreateDefaultConfig() + + sub, err := cm.Sub(tt.id.String()) + require.NoError(t, err) + require.NoError(t, sub.Unmarshal(cfg)) + + assert.NoError(t, component.ValidateConfig(cfg)) + assert.Equal(t, tt.expected, cfg) + }) + } +} + +func TestValidateConfig(t *testing.T) { + tests := []struct { + name string + config *Config + err string + }{ + { + name: "valid_config", + config: &Config{ + Endpoint: "https://helix:8080", + APIKey: "api_key", + Timeout: 10 * time.Second, + }, + }, + { + name: "invalid_config1", + config: &Config{ + APIKey: "api_key", + }, + err: "endpoint is required", + }, + { + name: "invalid_config2", + config: &Config{ + Endpoint: "https://helix:8080", + }, + err: "api key is required", + }, + { + name: "invalid_config3", + config: &Config{ + Endpoint: "https://helix:8080", + APIKey: "api_key", + Timeout: -1, + }, + err: "timeout must be a positive integer", + }, + { + name: "invalid_config4", + config: &Config{ + Endpoint: "https://helix:8080", + APIKey: "api_key", + Timeout: 0, + }, + err: "timeout must be a positive integer", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.err != "" { + err := tt.config.Validate() + assert.Error(t, err) + assert.Equal(t, tt.err, err.Error()) + } else { + assert.NoError(t, tt.config.Validate()) + } + }) + } +} diff --git a/exporter/bmchelixexporter/doc.go b/exporter/bmchelixexporter/doc.go new file mode 100644 index 000000000000..8185b6df85c5 --- /dev/null +++ b/exporter/bmchelixexporter/doc.go @@ -0,0 +1,7 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +//go:generate mdatagen metadata.yaml + +// Package bmchelixexporter implements an exporter that sends data to BMC Helix. +package bmchelixexporter // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/bmchelixexporter" diff --git a/exporter/bmchelixexporter/factory.go b/exporter/bmchelixexporter/factory.go new file mode 100644 index 000000000000..5c2dcf4603e2 --- /dev/null +++ b/exporter/bmchelixexporter/factory.go @@ -0,0 +1,46 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package bmchelixexporter // import "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/bmchelixexporter" + +import ( + "context" + "time" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/config/configretry" + "go.opentelemetry.io/collector/exporter" + "go.opentelemetry.io/collector/exporter/exporterhelper" + "go.opentelemetry.io/collector/pdata/pmetric" + + "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/bmchelixexporter/internal/metadata" +) + +// create BMC Helix Exporter factory +func NewFactory() exporter.Factory { + return exporter.NewFactory( + metadata.Type, + createDefaultConfig, + exporter.WithMetrics(createMetricsExporter, metadata.MetricsStability), + ) +} + +// creates the default configuration for the BMC Helix exporter +func createDefaultConfig() component.Config { + return &Config{ + Timeout: 10 * time.Second, + RetryConfig: configretry.NewDefaultBackOffConfig(), + } +} + +// creates an exporter.Metrics that records observability metrics for BMC Helix +func createMetricsExporter(ctx context.Context, set exporter.Settings, cfg component.Config) (exporter.Metrics, error) { + return exporterhelper.NewMetrics( + ctx, + set, + cfg, + func(_ context.Context, _ pmetric.Metrics) error { + return nil + }, + ) +} diff --git a/exporter/bmchelixexporter/factory_test.go b/exporter/bmchelixexporter/factory_test.go new file mode 100644 index 000000000000..0e2830a54891 --- /dev/null +++ b/exporter/bmchelixexporter/factory_test.go @@ -0,0 +1,39 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package bmchelixexporter + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "go.opentelemetry.io/collector/component/componenttest" + "go.opentelemetry.io/collector/exporter/exportertest" +) + +func TestCreateDefaultConfig(t *testing.T) { + cfg := createDefaultConfig() + assert.NotNil(t, cfg, "failed to create default config") + assert.NoError(t, componenttest.CheckConfigStruct(cfg)) +} + +func TestCreateMetricsExporter(t *testing.T) { + cfg := createDefaultConfig() + _, err := createMetricsExporter(context.Background(), exportertest.NewNopSettings(), cfg) + assert.NoError(t, err) +} + +func TestCreateInstanceViaFactory(t *testing.T) { + factory := NewFactory() + + cfg := factory.CreateDefaultConfig() + exp, err := factory.CreateMetrics( + context.Background(), + exportertest.NewNopSettings(), + cfg) + assert.NoError(t, err) + assert.NotNil(t, exp) + + assert.NoError(t, exp.Shutdown(context.Background())) +} diff --git a/exporter/bmchelixexporter/generated_component_test.go b/exporter/bmchelixexporter/generated_component_test.go new file mode 100644 index 000000000000..df9fd6a72fc2 --- /dev/null +++ b/exporter/bmchelixexporter/generated_component_test.go @@ -0,0 +1,136 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package bmchelixexporter + +import ( + "context" + "testing" + "time" + + "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/exporter" + "go.opentelemetry.io/collector/exporter/exportertest" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/plog" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/pdata/ptrace" +) + +func TestComponentFactoryType(t *testing.T) { + require.Equal(t, "bmchelix", 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 exporter.Settings, cfg component.Config) (component.Component, error) + }{ + + { + name: "metrics", + createFn: func(ctx context.Context, set exporter.Settings, cfg component.Config) (component.Component, error) { + return factory.CreateMetrics(ctx, set, cfg) + }, + }, + } + + 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, sub.Unmarshal(&cfg)) + + for _, tt := range tests { + t.Run(tt.name+"-shutdown", func(t *testing.T) { + c, err := tt.createFn(context.Background(), exportertest.NewNopSettings(), cfg) + require.NoError(t, err) + err = c.Shutdown(context.Background()) + require.NoError(t, err) + }) + t.Run(tt.name+"-lifecycle", func(t *testing.T) { + c, err := tt.createFn(context.Background(), exportertest.NewNopSettings(), cfg) + require.NoError(t, err) + host := componenttest.NewNopHost() + err = c.Start(context.Background(), host) + require.NoError(t, err) + require.NotPanics(t, func() { + switch tt.name { + case "logs": + e, ok := c.(exporter.Logs) + require.True(t, ok) + logs := generateLifecycleTestLogs() + if !e.Capabilities().MutatesData { + logs.MarkReadOnly() + } + err = e.ConsumeLogs(context.Background(), logs) + case "metrics": + e, ok := c.(exporter.Metrics) + require.True(t, ok) + metrics := generateLifecycleTestMetrics() + if !e.Capabilities().MutatesData { + metrics.MarkReadOnly() + } + err = e.ConsumeMetrics(context.Background(), metrics) + case "traces": + e, ok := c.(exporter.Traces) + require.True(t, ok) + traces := generateLifecycleTestTraces() + if !e.Capabilities().MutatesData { + traces.MarkReadOnly() + } + err = e.ConsumeTraces(context.Background(), traces) + } + }) + + require.NoError(t, err) + + err = c.Shutdown(context.Background()) + require.NoError(t, err) + }) + } +} + +func generateLifecycleTestLogs() plog.Logs { + logs := plog.NewLogs() + rl := logs.ResourceLogs().AppendEmpty() + rl.Resource().Attributes().PutStr("resource", "R1") + l := rl.ScopeLogs().AppendEmpty().LogRecords().AppendEmpty() + l.Body().SetStr("test log message") + l.SetTimestamp(pcommon.NewTimestampFromTime(time.Now())) + return logs +} + +func generateLifecycleTestMetrics() pmetric.Metrics { + metrics := pmetric.NewMetrics() + rm := metrics.ResourceMetrics().AppendEmpty() + rm.Resource().Attributes().PutStr("resource", "R1") + m := rm.ScopeMetrics().AppendEmpty().Metrics().AppendEmpty() + m.SetName("test_metric") + dp := m.SetEmptyGauge().DataPoints().AppendEmpty() + dp.Attributes().PutStr("test_attr", "value_1") + dp.SetIntValue(123) + dp.SetTimestamp(pcommon.NewTimestampFromTime(time.Now())) + return metrics +} + +func generateLifecycleTestTraces() ptrace.Traces { + traces := ptrace.NewTraces() + rs := traces.ResourceSpans().AppendEmpty() + rs.Resource().Attributes().PutStr("resource", "R1") + span := rs.ScopeSpans().AppendEmpty().Spans().AppendEmpty() + span.Attributes().PutStr("test_attr", "value_1") + span.SetName("test_span") + span.SetStartTimestamp(pcommon.NewTimestampFromTime(time.Now().Add(-1 * time.Second))) + span.SetEndTimestamp(pcommon.NewTimestampFromTime(time.Now())) + return traces +} diff --git a/exporter/bmchelixexporter/generated_package_test.go b/exporter/bmchelixexporter/generated_package_test.go new file mode 100644 index 000000000000..8c1dcd903340 --- /dev/null +++ b/exporter/bmchelixexporter/generated_package_test.go @@ -0,0 +1,13 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package bmchelixexporter + +import ( + "testing" + + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + goleak.VerifyTestMain(m) +} diff --git a/exporter/bmchelixexporter/go.mod b/exporter/bmchelixexporter/go.mod new file mode 100644 index 000000000000..8bac4bc3b615 --- /dev/null +++ b/exporter/bmchelixexporter/go.mod @@ -0,0 +1,63 @@ +module github.com/open-telemetry/opentelemetry-collector-contrib/exporter/bmchelixexporter + +go 1.22.0 + +require ( + github.com/stretchr/testify v1.10.0 + go.opentelemetry.io/collector/component v0.117.0 + go.opentelemetry.io/collector/component/componenttest v0.117.0 + go.opentelemetry.io/collector/config/configretry v1.23.0 + go.opentelemetry.io/collector/confmap v1.23.0 + go.opentelemetry.io/collector/exporter v0.117.0 + go.opentelemetry.io/collector/exporter/exportertest v0.117.0 + go.opentelemetry.io/collector/pdata v1.23.0 + go.uber.org/goleak v1.3.0 +) + +require ( + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-viper/mapstructure/v2 v2.2.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/hashicorp/go-version v1.7.0 // indirect + github.com/json-iterator/go v1.1.12 // 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.2 // 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/pmezard/go-difflib v1.0.0 // indirect + go.opentelemetry.io/collector/config/configtelemetry v0.117.0 // indirect + go.opentelemetry.io/collector/consumer v1.23.0 // indirect + go.opentelemetry.io/collector/consumer/consumererror v0.117.0 // indirect + go.opentelemetry.io/collector/consumer/consumertest v0.117.0 // indirect + go.opentelemetry.io/collector/consumer/xconsumer v0.117.0 // indirect + go.opentelemetry.io/collector/exporter/xexporter v0.117.0 // indirect + go.opentelemetry.io/collector/extension v0.117.0 // indirect + go.opentelemetry.io/collector/extension/xextension v0.117.0 // indirect + go.opentelemetry.io/collector/featuregate v1.23.0 // indirect + go.opentelemetry.io/collector/pdata/pprofile v0.117.0 // indirect + go.opentelemetry.io/collector/pipeline v0.117.0 // indirect + go.opentelemetry.io/collector/receiver v0.117.0 // indirect + go.opentelemetry.io/collector/receiver/receivertest v0.117.0 // indirect + go.opentelemetry.io/collector/receiver/xreceiver v0.117.0 // indirect + go.opentelemetry.io/otel v1.32.0 // indirect + go.opentelemetry.io/otel/metric v1.32.0 // indirect + go.opentelemetry.io/otel/sdk v1.32.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.32.0 // indirect + go.opentelemetry.io/otel/trace v1.32.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/net v0.33.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect + google.golang.org/grpc v1.69.2 // indirect + google.golang.org/protobuf v1.36.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/exporter/bmchelixexporter/go.sum b/exporter/bmchelixexporter/go.sum new file mode 100644 index 000000000000..837b80263d96 --- /dev/null +++ b/exporter/bmchelixexporter/go.sum @@ -0,0 +1,160 @@ +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +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/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/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.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= +github.com/go-viper/mapstructure/v2 v2.2.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/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +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.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= +github.com/hashicorp/go-version v1.7.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/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.2 h1:I2rtLRqXRy1p01m/utEtpZSSA6dcJbgGVuE27kW2PzQ= +github.com/knadh/koanf/v2 v2.1.2/go.mod h1:Gphfaen0q1Fc1HTgJgSTC4oRX9R2R5ErYMZJy8fLJBo= +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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +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/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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.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/component v0.117.0 h1:A3Im4PqLyfduAdVyUgbOZdUs7J/USegdpnkoIAOuN3Y= +go.opentelemetry.io/collector/component v0.117.0/go.mod h1:+SxJgeMwNV6y3aKNR2sP0PfovcUlRwC0+pEv4tTYdXA= +go.opentelemetry.io/collector/component/componenttest v0.117.0 h1:r3k0BsU/cJlqVQRtgFjxfduNEGaM2qCAU7JitIGkRds= +go.opentelemetry.io/collector/component/componenttest v0.117.0/go.mod h1:MoBWSGb3KwGc5FAIO+htez/QWK2uqJ4fnbEnfHB384c= +go.opentelemetry.io/collector/config/configretry v1.23.0 h1:0Ox2KvTZyNdgureAs3kJzsNIa6ttrx9bwlKjj/p4fGU= +go.opentelemetry.io/collector/config/configretry v1.23.0/go.mod h1:cleBc9I0DIWpTiiHfu9v83FUaCTqcPXmebpLxjEIqro= +go.opentelemetry.io/collector/config/configtelemetry v0.117.0 h1:xsMfc89VByIF2fJzWuxs/2eqy44DWfNBAysReG4TAr8= +go.opentelemetry.io/collector/config/configtelemetry v0.117.0/go.mod h1:SlBEwQg0qly75rXZ6W1Ig8jN25KBVBkFIIAUI1GiAAE= +go.opentelemetry.io/collector/confmap v1.23.0 h1:EY+auc0kbyZ4HIfkLYeJyLDCZIFzMA1u8QRGW4bC1Ag= +go.opentelemetry.io/collector/confmap v1.23.0/go.mod h1:Rrhs+MWoaP6AswZp+ReQ2VO9dfOfcUjdjiSHBsG+nec= +go.opentelemetry.io/collector/consumer v1.23.0 h1:JT0nE1vikL5yIk97IHBGzwx8co3w1WsAd3GFEl8r9XA= +go.opentelemetry.io/collector/consumer v1.23.0/go.mod h1:8d0uQ6gq64LbPktV4sc888lRj1cQCmrdl13hRIEURgA= +go.opentelemetry.io/collector/consumer/consumererror v0.117.0 h1:PPIZCcYZcENnyIrpRV4ERvMUoPSTV0zIP0QPzJvz80g= +go.opentelemetry.io/collector/consumer/consumererror v0.117.0/go.mod h1:L47xOVC+Vzos8350j3SWtU43w7rzms6UDhb6IrFxymY= +go.opentelemetry.io/collector/consumer/consumertest v0.117.0 h1:9WFyyjLudvfJDEuUaGsQyNRd1m6D1iRg8Iyg3xliFko= +go.opentelemetry.io/collector/consumer/consumertest v0.117.0/go.mod h1:B7A+OS76QKAzM8W7cmvlfVynFELj9Sa444hSm1SILFw= +go.opentelemetry.io/collector/consumer/xconsumer v0.117.0 h1:vsBNJGaEbYqgMU3PEsOcqjMxX5ul++Cxda44sttoi8c= +go.opentelemetry.io/collector/consumer/xconsumer v0.117.0/go.mod h1:dTr+Tms53lRLvR3OAzYic0yhcwldhTUdVIwJNSDmBmw= +go.opentelemetry.io/collector/exporter v0.117.0 h1:A9kVXzdb8i1eFELImuaSPyijAfg4qMIpM/4y/98mlxk= +go.opentelemetry.io/collector/exporter v0.117.0/go.mod h1:Cbrorch2s18w1X7+A+zXQtAffbInnIOP7Su26gbRG+k= +go.opentelemetry.io/collector/exporter/exportertest v0.117.0 h1:u+loeqxpniMiJL1iqc/lCCcfniWrqHBgJTAjXfqVBqQ= +go.opentelemetry.io/collector/exporter/exportertest v0.117.0/go.mod h1:GyHwJLsOPPau0m+TYrIA7jWD9/GU+ID+l/9sL0cAqhE= +go.opentelemetry.io/collector/exporter/xexporter v0.117.0 h1:BB8D0Dvb46CVAZrnPEg5nYgXO7LzONmXeGKEfzSIOZs= +go.opentelemetry.io/collector/exporter/xexporter v0.117.0/go.mod h1:yo0T8WkvLCJ7NOqIquHGFe4Xpuc4CbDb8a06T2G5De4= +go.opentelemetry.io/collector/extension v0.117.0 h1:B3cG7g+wbhmpMFugaDxOcyiPKeulaW8+EQdJbZxDfho= +go.opentelemetry.io/collector/extension v0.117.0/go.mod h1:WjyD5h9N5Y0SF8azB2rulvHJieJoWqroGO5hi3ax5+8= +go.opentelemetry.io/collector/extension/extensiontest v0.117.0 h1:XH+tkHdATylYZtASZKK3rCoN/xlaFi8MXLh07ZlQQWw= +go.opentelemetry.io/collector/extension/extensiontest v0.117.0/go.mod h1:ABqB9D41p4MCeGVmABOgJi7i7roWZlFbqeFJDy7lskQ= +go.opentelemetry.io/collector/extension/xextension v0.117.0 h1:ADUKWHGaVvvmebJHiNRuX6YAfQXFDW/UaXK9W1hCo1k= +go.opentelemetry.io/collector/extension/xextension v0.117.0/go.mod h1:BmR8xN7Ja+El4IJ9aVmtON2miudjsbq2COZ9azVXsNg= +go.opentelemetry.io/collector/featuregate v1.23.0 h1:N033ROo85qKrsK16QzR6RV+3UWOWF7kpOO8FSnX99s0= +go.opentelemetry.io/collector/featuregate v1.23.0/go.mod h1:3GaXqflNDVwWndNGBJ1+XJFy3Fv/XrFgjMN60N3z7yg= +go.opentelemetry.io/collector/pdata v1.23.0 h1:tEk0dkfB8RdSukoOMfEa8duB938gfZowdfRkrJxGDrw= +go.opentelemetry.io/collector/pdata v1.23.0/go.mod h1:I2jggpBMiO8A+7TXhzNpcJZkJtvi1cU0iVNIi+6bc+o= +go.opentelemetry.io/collector/pdata/pprofile v0.117.0 h1:AyOK+rkNGeawmLGUqF84wYks22BSGJtEV++3YSfvD1I= +go.opentelemetry.io/collector/pdata/pprofile v0.117.0/go.mod h1:eh7TLIkLrSI79/R3RL+sZsKpLS0k+83WntucPtXC5Ak= +go.opentelemetry.io/collector/pdata/testdata v0.117.0 h1:ainpacShKHaDkPK6lcvgJ0aPKYUD/E3+I0gYJZleedo= +go.opentelemetry.io/collector/pdata/testdata v0.117.0/go.mod h1:LZAymmRKHQEqJqJUSO15rej3+V1rNRyBMF5mWCKCMBY= +go.opentelemetry.io/collector/pipeline v0.117.0 h1:CSv0Dd3n9AQNQ73e7PdEkgexkSMRZliKATxkoZKUFcY= +go.opentelemetry.io/collector/pipeline v0.117.0/go.mod h1:qE3DmoB05AW0C3lmPvdxZqd/H4po84NPzd5MrqgtL74= +go.opentelemetry.io/collector/receiver v0.117.0 h1:jm+b2G2IKKwGE213lB9cviKEdeATvYtNSY1kO0XdpMM= +go.opentelemetry.io/collector/receiver v0.117.0/go.mod h1:fZXigB3afp54OE+ogPcup/RPwI7j+CwZh9Mz6ObB/Cg= +go.opentelemetry.io/collector/receiver/receivertest v0.117.0 h1:aN4zOuWsiARa+RG9f89JyIrJbx5wsQ71Y0giiHsO1z8= +go.opentelemetry.io/collector/receiver/receivertest v0.117.0/go.mod h1:1wnGEowDmlO89feq1P+b4tQI2G/+iJxRrMallw7zeJE= +go.opentelemetry.io/collector/receiver/xreceiver v0.117.0 h1:HJjBj6P3/WQoYaRKZkWZHnUUCVFpBieqGKzKHcT6HUw= +go.opentelemetry.io/collector/receiver/xreceiver v0.117.0/go.mod h1:K1qMjIiAg6i3vHA+/EpM8nkhna3uIgoEellE2yuhz7A= +go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= +go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= +go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= +go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= +go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= +go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= +go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= +go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= +go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= +go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= +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.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +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.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.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.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +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-20241104194629-dd2ea8efbc28 h1:XVhgTWWV3kGQlwJHR3upFWZeTsei6Oks1apkZSeonIE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= +google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU= +google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= +google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU= +google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +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/exporter/bmchelixexporter/internal/metadata/generated_status.go b/exporter/bmchelixexporter/internal/metadata/generated_status.go new file mode 100644 index 000000000000..8df6c7f2cbc7 --- /dev/null +++ b/exporter/bmchelixexporter/internal/metadata/generated_status.go @@ -0,0 +1,16 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package metadata + +import ( + "go.opentelemetry.io/collector/component" +) + +var ( + Type = component.MustNewType("bmchelix") + ScopeName = "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/bmchelixexporter" +) + +const ( + MetricsStability = component.StabilityLevelDevelopment +) diff --git a/exporter/bmchelixexporter/metadata.yaml b/exporter/bmchelixexporter/metadata.yaml new file mode 100644 index 000000000000..a44ba57a0ac6 --- /dev/null +++ b/exporter/bmchelixexporter/metadata.yaml @@ -0,0 +1,9 @@ +type: bmchelix + +status: + class: exporter + stability: + development: [metrics] + distributions: [] + codeowners: + active: [bertysentry, NassimBtk] \ No newline at end of file diff --git a/exporter/bmchelixexporter/testdata/config.yaml b/exporter/bmchelixexporter/testdata/config.yaml new file mode 100644 index 000000000000..51156b1deb05 --- /dev/null +++ b/exporter/bmchelixexporter/testdata/config.yaml @@ -0,0 +1,12 @@ +bmchelix/helix1: + endpoint: https://helix1:8080 + api_key: api_key +bmchelix/helix2: + endpoint: https://helix2:8080 + api_key: api_key + timeout: 20s + retry_on_failure: + enabled: true + initial_interval: 5s + max_interval: 1m + max_elapsed_time: 8m diff --git a/versions.yaml b/versions.yaml index 8dc8a837cf9a..1a3501ca6367 100644 --- a/versions.yaml +++ b/versions.yaml @@ -35,6 +35,7 @@ module-sets: - github.com/open-telemetry/opentelemetry-collector-contrib/exporter/awsxrayexporter - github.com/open-telemetry/opentelemetry-collector-contrib/exporter/azuredataexplorerexporter - github.com/open-telemetry/opentelemetry-collector-contrib/exporter/azuremonitorexporter + - github.com/open-telemetry/opentelemetry-collector-contrib/exporter/bmchelixexporter - github.com/open-telemetry/opentelemetry-collector-contrib/exporter/carbonexporter - github.com/open-telemetry/opentelemetry-collector-contrib/exporter/cassandraexporter - github.com/open-telemetry/opentelemetry-collector-contrib/exporter/clickhouseexporter From 667e81e28b7da3e50b1338a3becf4aef7aa231e4 Mon Sep 17 00:00:00 2001 From: Mingxi <71588583+joker-star-l@users.noreply.github.com> Date: Tue, 14 Jan 2025 02:52:02 +0800 Subject: [PATCH 10/13] [exporter/doris] Send json lines to doris rather than json array (#36912) <!--Ex. Fixing a bug - Describe the bug and how this fixes the issue. Ex. Adding a feature - Explain what this achieves.--> #### Description Send json lines to doris rather than json array. <!-- Issue number (e.g. #1234) or full URL to issue, if applicable. --> #### Link to tracking issue Fixes #36896 <!--Describe what testing was performed and which tests were added.--> #### Testing unit test <!--Describe the documentation added.--> #### Documentation <!--Please delete paragraphs that you did not use before submitting.--> --- .chloggen/json_line.yaml | 27 +++++++++++++++++++ exporter/dorisexporter/exporter_common.go | 19 ++++++++++++- .../dorisexporter/exporter_common_test.go | 9 +++++++ exporter/dorisexporter/exporter_logs.go | 2 +- exporter/dorisexporter/exporter_traces.go | 2 +- .../metrics_exponential_histogram.go | 3 +-- exporter/dorisexporter/metrics_gauge.go | 3 +-- exporter/dorisexporter/metrics_histogram.go | 3 +-- exporter/dorisexporter/metrics_sum.go | 3 +-- exporter/dorisexporter/metrics_summary.go | 3 +-- 10 files changed, 61 insertions(+), 13 deletions(-) create mode 100644 .chloggen/json_line.yaml diff --git a/.chloggen/json_line.yaml b/.chloggen/json_line.yaml new file mode 100644 index 000000000000..42f3e351c65d --- /dev/null +++ b/.chloggen/json_line.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: dorisexporter + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: send json lines to doris rather than json array + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [36896] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [user] diff --git a/exporter/dorisexporter/exporter_common.go b/exporter/dorisexporter/exporter_common.go index 7c889c3d42f8..cc474fe47d5e 100644 --- a/exporter/dorisexporter/exporter_common.go +++ b/exporter/dorisexporter/exporter_common.go @@ -7,6 +7,7 @@ import ( "bytes" "context" "database/sql" + "encoding/json" "fmt" "net/http" "time" @@ -81,7 +82,7 @@ func streamLoadRequest(ctx context.Context, cfg *Config, table string, data []by req.Header.Set("format", "json") req.Header.Set("Expect", "100-continue") - req.Header.Set("strip_outer_array", "true") + req.Header.Set("read_json_by_line", "true") if cfg.ClientConfig.Timeout != 0 { req.Header.Set("timeout", fmt.Sprintf("%d", cfg.ClientConfig.Timeout/time.Second)) } @@ -118,3 +119,19 @@ func createAndUseDatabase(ctx context.Context, conn *sql.DB, cfg *Config) error _, err = conn.ExecContext(ctx, "USE "+cfg.Database) return err } + +type metric interface { + dMetricGauge | dMetricSum | dMetricHistogram | dMetricExponentialHistogram | dMetricSummary +} + +func toJsonLines[T dLog | dTrace | metric](data []*T) ([]byte, error) { + buf := &bytes.Buffer{} + enc := json.NewEncoder(buf) + for _, d := range data { + err := enc.Encode(d) + if err != nil { + return nil, err + } + } + return buf.Bytes(), nil +} diff --git a/exporter/dorisexporter/exporter_common_test.go b/exporter/dorisexporter/exporter_common_test.go index baea71b36bb9..afd6c83d2aee 100644 --- a/exporter/dorisexporter/exporter_common_test.go +++ b/exporter/dorisexporter/exporter_common_test.go @@ -5,6 +5,7 @@ package dorisexporter // import "github.com/open-telemetry/opentelemetry-collect import ( "net" + "strings" "testing" "time" @@ -60,3 +61,11 @@ func findRandomPort() (int, error) { return port, nil } + +func TestToJsonLines(t *testing.T) { + logs, err := toJsonLines([]*dLog{ + {}, {}, + }) + require.NoError(t, err) + require.Len(t, strings.Split(string(logs), "\n"), 2+1) +} diff --git a/exporter/dorisexporter/exporter_logs.go b/exporter/dorisexporter/exporter_logs.go index d3022e0c6290..8a81937c616f 100644 --- a/exporter/dorisexporter/exporter_logs.go +++ b/exporter/dorisexporter/exporter_logs.go @@ -122,7 +122,7 @@ func (e *logsExporter) pushLogData(ctx context.Context, ld plog.Logs) error { } func (e *logsExporter) pushLogDataInternal(ctx context.Context, logs []*dLog) error { - marshal, err := json.Marshal(logs) + marshal, err := toJsonLines(logs) if err != nil { return err } diff --git a/exporter/dorisexporter/exporter_traces.go b/exporter/dorisexporter/exporter_traces.go index 40793c613605..c59db592fb73 100644 --- a/exporter/dorisexporter/exporter_traces.go +++ b/exporter/dorisexporter/exporter_traces.go @@ -180,7 +180,7 @@ func (e *tracesExporter) pushTraceData(ctx context.Context, td ptrace.Traces) er } func (e *tracesExporter) pushTraceDataInternal(ctx context.Context, traces []*dTrace) error { - marshal, err := json.Marshal(traces) + marshal, err := toJsonLines(traces) if err != nil { return err } diff --git a/exporter/dorisexporter/metrics_exponential_histogram.go b/exporter/dorisexporter/metrics_exponential_histogram.go index 1498546f3229..561fbbd37aa3 100644 --- a/exporter/dorisexporter/metrics_exponential_histogram.go +++ b/exporter/dorisexporter/metrics_exponential_histogram.go @@ -5,7 +5,6 @@ package dorisexporter // import "github.com/open-telemetry/opentelemetry-collect import ( _ "embed" - "encoding/json" "fmt" "go.opentelemetry.io/collector/pdata/pmetric" @@ -118,5 +117,5 @@ func (m *metricModelExponentialHistogram) size() int { } func (m *metricModelExponentialHistogram) bytes() ([]byte, error) { - return json.Marshal(m.data) + return toJsonLines(m.data) } diff --git a/exporter/dorisexporter/metrics_gauge.go b/exporter/dorisexporter/metrics_gauge.go index a5cf1b9388dd..c27bc4142245 100644 --- a/exporter/dorisexporter/metrics_gauge.go +++ b/exporter/dorisexporter/metrics_gauge.go @@ -5,7 +5,6 @@ package dorisexporter // import "github.com/open-telemetry/opentelemetry-collect import ( _ "embed" - "encoding/json" "fmt" "go.opentelemetry.io/collector/pdata/pmetric" @@ -84,5 +83,5 @@ func (m *metricModelGauge) size() int { } func (m *metricModelGauge) bytes() ([]byte, error) { - return json.Marshal(m.data) + return toJsonLines(m.data) } diff --git a/exporter/dorisexporter/metrics_histogram.go b/exporter/dorisexporter/metrics_histogram.go index 18d1b3f3afdb..4166dd34b8f0 100644 --- a/exporter/dorisexporter/metrics_histogram.go +++ b/exporter/dorisexporter/metrics_histogram.go @@ -5,7 +5,6 @@ package dorisexporter // import "github.com/open-telemetry/opentelemetry-collect import ( _ "embed" - "encoding/json" "fmt" "go.opentelemetry.io/collector/pdata/pmetric" @@ -108,5 +107,5 @@ func (m *metricModelHistogram) size() int { } func (m *metricModelHistogram) bytes() ([]byte, error) { - return json.Marshal(m.data) + return toJsonLines(m.data) } diff --git a/exporter/dorisexporter/metrics_sum.go b/exporter/dorisexporter/metrics_sum.go index 56c66ba86419..d5281f8be92a 100644 --- a/exporter/dorisexporter/metrics_sum.go +++ b/exporter/dorisexporter/metrics_sum.go @@ -5,7 +5,6 @@ package dorisexporter // import "github.com/open-telemetry/opentelemetry-collect import ( _ "embed" - "encoding/json" "fmt" "go.opentelemetry.io/collector/pdata/pmetric" @@ -88,5 +87,5 @@ func (m *metricModelSum) size() int { } func (m *metricModelSum) bytes() ([]byte, error) { - return json.Marshal(m.data) + return toJsonLines(m.data) } diff --git a/exporter/dorisexporter/metrics_summary.go b/exporter/dorisexporter/metrics_summary.go index 499d56fbcb47..19683f1c30a1 100644 --- a/exporter/dorisexporter/metrics_summary.go +++ b/exporter/dorisexporter/metrics_summary.go @@ -5,7 +5,6 @@ package dorisexporter // import "github.com/open-telemetry/opentelemetry-collect import ( _ "embed" - "encoding/json" "fmt" "go.opentelemetry.io/collector/pdata/pmetric" @@ -89,5 +88,5 @@ func (m *metricModelSummary) size() int { } func (m *metricModelSummary) bytes() ([]byte, error) { - return json.Marshal(m.data) + return toJsonLines(m.data) } From 981e9f5cc58149ea37c8909a4a6eb30faee96351 Mon Sep 17 00:00:00 2001 From: odubajDT <93584209+odubajDT@users.noreply.github.com> Date: Mon, 13 Jan 2025 21:14:01 +0100 Subject: [PATCH 11/13] [processor/resourcedetection] introduce kubeadm detector (#35450) **Description:** <Describe what has changed.> <!--Ex. Fixing a bug - Describe the bug and how this fixes the issue. Ex. Adding a feature - Explain what this achieves.--> - introduce `kubeadm` detector to detect local cluster name **Link to tracking Issue:** #35116 --------- Signed-off-by: odubajDT <ondrej.dubaj@dynatrace.com> Co-authored-by: Christos Markou <chrismarkou92@gmail.com> --- .../resourcedetection-local-cluster.yaml | 27 ++++++ .../metadataproviders/kubeadm/metadata.go | 56 ++++++++++++ .../kubeadm/metadata_test.go | 87 +++++++++++++++++++ .../metadataproviders/kubeadm/package_test.go | 14 +++ .../resourcedetectionprocessor/README.md | 37 ++++++++ .../resourcedetectionprocessor/config.go | 7 ++ processor/resourcedetectionprocessor/doc.go | 1 + .../resourcedetectionprocessor/factory.go | 2 + .../internal/kubeadm/config.go | 21 +++++ .../internal/kubeadm/documentation.md | 11 +++ .../kubeadm/generated_package_test.go | 13 +++ .../internal/metadata/generated_config.go | 39 +++++++++ .../metadata/generated_config_test.go | 56 ++++++++++++ .../internal/metadata/generated_resource.go | 36 ++++++++ .../metadata/generated_resource_test.go | 40 +++++++++ .../kubeadm/internal/metadata/package_test.go | 14 +++ .../internal/metadata/testdata/config.yaml | 9 ++ .../internal/kubeadm/kubeadm.go | 61 +++++++++++++ .../internal/kubeadm/kubeadm_test.go | 75 ++++++++++++++++ .../internal/kubeadm/metadata.yaml | 9 ++ 20 files changed, 615 insertions(+) create mode 100644 .chloggen/resourcedetection-local-cluster.yaml create mode 100644 internal/metadataproviders/kubeadm/metadata.go create mode 100644 internal/metadataproviders/kubeadm/metadata_test.go create mode 100644 internal/metadataproviders/kubeadm/package_test.go create mode 100644 processor/resourcedetectionprocessor/internal/kubeadm/config.go create mode 100644 processor/resourcedetectionprocessor/internal/kubeadm/documentation.md create mode 100644 processor/resourcedetectionprocessor/internal/kubeadm/generated_package_test.go create mode 100644 processor/resourcedetectionprocessor/internal/kubeadm/internal/metadata/generated_config.go create mode 100644 processor/resourcedetectionprocessor/internal/kubeadm/internal/metadata/generated_config_test.go create mode 100644 processor/resourcedetectionprocessor/internal/kubeadm/internal/metadata/generated_resource.go create mode 100644 processor/resourcedetectionprocessor/internal/kubeadm/internal/metadata/generated_resource_test.go create mode 100644 processor/resourcedetectionprocessor/internal/kubeadm/internal/metadata/package_test.go create mode 100644 processor/resourcedetectionprocessor/internal/kubeadm/internal/metadata/testdata/config.yaml create mode 100644 processor/resourcedetectionprocessor/internal/kubeadm/kubeadm.go create mode 100644 processor/resourcedetectionprocessor/internal/kubeadm/kubeadm_test.go create mode 100644 processor/resourcedetectionprocessor/internal/kubeadm/metadata.yaml diff --git a/.chloggen/resourcedetection-local-cluster.yaml b/.chloggen/resourcedetection-local-cluster.yaml new file mode 100644 index 000000000000..2227c7ef6d22 --- /dev/null +++ b/.chloggen/resourcedetection-local-cluster.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: resourcedetectionprocessor + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: "Introduce kubeadm detector to retrieve local cluster name." + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [35116] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [] diff --git a/internal/metadataproviders/kubeadm/metadata.go b/internal/metadataproviders/kubeadm/metadata.go new file mode 100644 index 000000000000..2b594e54614c --- /dev/null +++ b/internal/metadataproviders/kubeadm/metadata.go @@ -0,0 +1,56 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package kubeadm // import "github.com/open-telemetry/opentelemetry-collector-contrib/internal/metadataproviders/kubeadm" + +import ( + "context" + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes" + + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/k8sconfig" +) + +type Provider interface { + // ClusterName returns the current K8S cluster name + ClusterName(ctx context.Context) (string, error) +} + +type LocalCache struct { + ClusterName string +} + +type kubeadmProvider struct { + kubeadmClient kubernetes.Interface + configMapName string + configMapNamespace string + cache LocalCache +} + +func NewProvider(configMapName string, configMapNamespace string, apiConf k8sconfig.APIConfig) (Provider, error) { + k8sAPIClient, err := k8sconfig.MakeClient(apiConf) + if err != nil { + return nil, fmt.Errorf("failed to create K8s API client: %w", err) + } + return &kubeadmProvider{ + kubeadmClient: k8sAPIClient, + configMapName: configMapName, + configMapNamespace: configMapNamespace, + }, nil +} + +func (k *kubeadmProvider) ClusterName(ctx context.Context) (string, error) { + if k.cache.ClusterName != "" { + return k.cache.ClusterName, nil + } + configmap, err := k.kubeadmClient.CoreV1().ConfigMaps(k.configMapNamespace).Get(ctx, k.configMapName, metav1.GetOptions{}) + if err != nil { + return "", fmt.Errorf("failed to fetch ConfigMap with name %s and namespace %s from K8s API: %w", k.configMapName, k.configMapNamespace, err) + } + + k.cache.ClusterName = configmap.Data["clusterName"] + + return k.cache.ClusterName, nil +} diff --git a/internal/metadataproviders/kubeadm/metadata_test.go b/internal/metadataproviders/kubeadm/metadata_test.go new file mode 100644 index 000000000000..7f383b203a7e --- /dev/null +++ b/internal/metadataproviders/kubeadm/metadata_test.go @@ -0,0 +1,87 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package kubeadm + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/fake" + + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/k8sconfig" +) + +func TestNewProvider(t *testing.T) { + // set k8s cluster env variables to make the API client happy + t.Setenv("KUBERNETES_SERVICE_HOST", "127.0.0.1") + t.Setenv("KUBERNETES_SERVICE_PORT", "6443") + + _, err := NewProvider("name", "ns", k8sconfig.APIConfig{AuthType: k8sconfig.AuthTypeNone}) + assert.NoError(t, err) +} + +func TestClusterName(t *testing.T) { + client := fake.NewSimpleClientset() + err := setupConfigMap(client) + assert.NoError(t, err) + + tests := []struct { + testName string + CMname string + CMnamespace string + clusterName string + errMsg string + }{ + { + testName: "valid", + CMname: "cm", + CMnamespace: "ns", + clusterName: "myClusterName", + errMsg: "", + }, + { + testName: "configmap not found", + CMname: "cm2", + CMnamespace: "ns", + errMsg: "failed to fetch ConfigMap with name cm2 and namespace ns from K8s API: configmaps \"cm2\" not found", + }, + } + + for _, tt := range tests { + t.Run(tt.testName, func(t *testing.T) { + kubeadmP := &kubeadmProvider{ + kubeadmClient: client, + configMapName: tt.CMname, + configMapNamespace: tt.CMnamespace, + } + clusterName, err := kubeadmP.ClusterName(context.Background()) + if tt.errMsg != "" { + assert.EqualError(t, err, tt.errMsg) + } else { + assert.NoError(t, err) + assert.Equal(t, clusterName, tt.clusterName) + } + }) + } +} + +func setupConfigMap(client *fake.Clientset) error { + cm := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cm", + Namespace: "ns", + }, + Data: map[string]string{ + "clusterName": "myClusterName", + }, + } + _, err := client.CoreV1().ConfigMaps("ns").Create(context.Background(), cm, metav1.CreateOptions{}) + if err != nil { + return err + } + return nil +} diff --git a/internal/metadataproviders/kubeadm/package_test.go b/internal/metadataproviders/kubeadm/package_test.go new file mode 100644 index 000000000000..48baf7f293d0 --- /dev/null +++ b/internal/metadataproviders/kubeadm/package_test.go @@ -0,0 +1,14 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package kubeadm + +import ( + "testing" + + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + goleak.VerifyTestMain(m) +} diff --git a/processor/resourcedetectionprocessor/README.md b/processor/resourcedetectionprocessor/README.md index 6186156d57ab..a19bc1471f91 100644 --- a/processor/resourcedetectionprocessor/README.md +++ b/processor/resourcedetectionprocessor/README.md @@ -426,6 +426,43 @@ processors: override: false ``` +### Kubeadm Metadata + +Queries the K8S API server to retrieve kubeadm resource attributes: + +The list of the populated resource attributes can be found at [kubeadm Detector Resource Attributes](./internal/kubeadm/documentation.md). + +The following permissions are required: +```yaml +kind: Role +metadata: + name: otel-collector + namespace: kube-system +rules: + - apiGroups: [""] + resources: ["configmaps"] + resourceNames: ["kubeadm-config"] + verbs: ["get"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: otel-collector-rolebinding + namespace: kube-system +subjects: +- kind: ServiceAccount + name: default + namespace: default +roleRef: + kind: Role + name: otel-collector + apiGroup: rbac.authorization.k8s.io +``` + +| Name | Type | Required | Default | Docs | +| ---- | ---- |----------|-----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| auth_type | string | No | `serviceAccount` | How to authenticate to the K8s API server. This can be one of `none` (for no auth), `serviceAccount` (to use the standard service account token provided to the agent pod), or `kubeConfig` to use credentials from `~/.kube/config`. | + ### K8S Node Metadata Queries the K8S api server to retrieve node resource attributes. diff --git a/processor/resourcedetectionprocessor/config.go b/processor/resourcedetectionprocessor/config.go index ba34649e2c72..8685443bea18 100644 --- a/processor/resourcedetectionprocessor/config.go +++ b/processor/resourcedetectionprocessor/config.go @@ -19,6 +19,7 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/internal/gcp" "github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/internal/heroku" "github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/internal/k8snode" + "github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/internal/kubeadm" "github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/internal/openshift" "github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/internal/system" ) @@ -85,6 +86,9 @@ type DetectorConfig struct { // K8SNode contains user-specified configurations for the K8SNode detector K8SNodeConfig k8snode.Config `mapstructure:"k8snode"` + + // Kubeadm contains user-specified configurations for the Kubeadm detector + KubeadmConfig kubeadm.Config `mapstructure:"kubeadm"` } func detectorCreateDefaultConfig() DetectorConfig { @@ -103,6 +107,7 @@ func detectorCreateDefaultConfig() DetectorConfig { SystemConfig: system.CreateDefaultConfig(), OpenShiftConfig: openshift.CreateDefaultConfig(), K8SNodeConfig: k8snode.CreateDefaultConfig(), + KubeadmConfig: kubeadm.CreateDefaultConfig(), } } @@ -136,6 +141,8 @@ func (d *DetectorConfig) GetConfigFromType(detectorType internal.DetectorType) i return d.OpenShiftConfig case k8snode.TypeStr: return d.K8SNodeConfig + case kubeadm.TypeStr: + return d.KubeadmConfig default: return nil } diff --git a/processor/resourcedetectionprocessor/doc.go b/processor/resourcedetectionprocessor/doc.go index 67a30eb31cf3..91c606b11568 100644 --- a/processor/resourcedetectionprocessor/doc.go +++ b/processor/resourcedetectionprocessor/doc.go @@ -16,6 +16,7 @@ //go:generate mdatagen internal/openshift/metadata.yaml //go:generate mdatagen internal/system/metadata.yaml //go:generate mdatagen internal/k8snode/metadata.yaml +//go:generate mdatagen internal/kubeadm/metadata.yaml // package resourcedetectionprocessor implements a processor // which can be used to detect resource information from the host, diff --git a/processor/resourcedetectionprocessor/factory.go b/processor/resourcedetectionprocessor/factory.go index 36cfb7136beb..9f47c2ce69d4 100644 --- a/processor/resourcedetectionprocessor/factory.go +++ b/processor/resourcedetectionprocessor/factory.go @@ -32,6 +32,7 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/internal/gcp" "github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/internal/heroku" "github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/internal/k8snode" + "github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/internal/kubeadm" "github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/internal/metadata" "github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/internal/openshift" "github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/internal/system" @@ -66,6 +67,7 @@ func NewFactory() processor.Factory { system.TypeStr: system.NewDetector, openshift.TypeStr: openshift.NewDetector, k8snode.TypeStr: k8snode.NewDetector, + kubeadm.TypeStr: kubeadm.NewDetector, }) f := &factory{ diff --git a/processor/resourcedetectionprocessor/internal/kubeadm/config.go b/processor/resourcedetectionprocessor/internal/kubeadm/config.go new file mode 100644 index 000000000000..c9932dc332d5 --- /dev/null +++ b/processor/resourcedetectionprocessor/internal/kubeadm/config.go @@ -0,0 +1,21 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package kubeadm // import "github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/internal/kubeadm" + +import ( + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/k8sconfig" + "github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/internal/kubeadm/internal/metadata" +) + +type Config struct { + k8sconfig.APIConfig `mapstructure:",squash"` + ResourceAttributes metadata.ResourceAttributesConfig `mapstructure:"resource_attributes"` +} + +func CreateDefaultConfig() Config { + return Config{ + APIConfig: k8sconfig.APIConfig{AuthType: k8sconfig.AuthTypeServiceAccount}, + ResourceAttributes: metadata.DefaultResourceAttributesConfig(), + } +} diff --git a/processor/resourcedetectionprocessor/internal/kubeadm/documentation.md b/processor/resourcedetectionprocessor/internal/kubeadm/documentation.md new file mode 100644 index 000000000000..5e2c0561a2ad --- /dev/null +++ b/processor/resourcedetectionprocessor/internal/kubeadm/documentation.md @@ -0,0 +1,11 @@ +[comment]: <> (Code generated by mdatagen. DO NOT EDIT.) + +# resourcedetectionprocessor/kubeadm + +**Parent Component:** resourcedetection + +## Resource Attributes + +| Name | Description | Values | Enabled | +| ---- | ----------- | ------ | ------- | +| k8s.cluster.name | The Kubernetes cluster name | Any Str | true | diff --git a/processor/resourcedetectionprocessor/internal/kubeadm/generated_package_test.go b/processor/resourcedetectionprocessor/internal/kubeadm/generated_package_test.go new file mode 100644 index 000000000000..4dc8cc67e4da --- /dev/null +++ b/processor/resourcedetectionprocessor/internal/kubeadm/generated_package_test.go @@ -0,0 +1,13 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package kubeadm + +import ( + "testing" + + "go.uber.org/goleak" +) + +func TestMain(m *testing.M) { + goleak.VerifyTestMain(m) +} diff --git a/processor/resourcedetectionprocessor/internal/kubeadm/internal/metadata/generated_config.go b/processor/resourcedetectionprocessor/internal/kubeadm/internal/metadata/generated_config.go new file mode 100644 index 000000000000..cb9d4839877d --- /dev/null +++ b/processor/resourcedetectionprocessor/internal/kubeadm/internal/metadata/generated_config.go @@ -0,0 +1,39 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package metadata + +import ( + "go.opentelemetry.io/collector/confmap" +) + +// ResourceAttributeConfig provides common config for a particular resource attribute. +type ResourceAttributeConfig struct { + Enabled bool `mapstructure:"enabled"` + + enabledSetByUser bool +} + +func (rac *ResourceAttributeConfig) Unmarshal(parser *confmap.Conf) error { + if parser == nil { + return nil + } + err := parser.Unmarshal(rac) + if err != nil { + return err + } + rac.enabledSetByUser = parser.IsSet("enabled") + return nil +} + +// ResourceAttributesConfig provides config for resourcedetectionprocessor/kubeadm resource attributes. +type ResourceAttributesConfig struct { + K8sClusterName ResourceAttributeConfig `mapstructure:"k8s.cluster.name"` +} + +func DefaultResourceAttributesConfig() ResourceAttributesConfig { + return ResourceAttributesConfig{ + K8sClusterName: ResourceAttributeConfig{ + Enabled: true, + }, + } +} diff --git a/processor/resourcedetectionprocessor/internal/kubeadm/internal/metadata/generated_config_test.go b/processor/resourcedetectionprocessor/internal/kubeadm/internal/metadata/generated_config_test.go new file mode 100644 index 000000000000..c2ed830cc2ad --- /dev/null +++ b/processor/resourcedetectionprocessor/internal/kubeadm/internal/metadata/generated_config_test.go @@ -0,0 +1,56 @@ +// 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/confmap/confmaptest" +) + +func TestResourceAttributesConfig(t *testing.T) { + tests := []struct { + name string + want ResourceAttributesConfig + }{ + { + name: "default", + want: DefaultResourceAttributesConfig(), + }, + { + name: "all_set", + want: ResourceAttributesConfig{ + K8sClusterName: ResourceAttributeConfig{Enabled: true}, + }, + }, + { + name: "none_set", + want: ResourceAttributesConfig{ + K8sClusterName: ResourceAttributeConfig{Enabled: false}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := loadResourceAttributesConfig(t, tt.name) + diff := cmp.Diff(tt.want, cfg, cmpopts.IgnoreUnexported(ResourceAttributeConfig{})) + require.Emptyf(t, diff, "Config mismatch (-expected +actual):\n%s", diff) + }) + } +} + +func loadResourceAttributesConfig(t *testing.T, name string) ResourceAttributesConfig { + cm, err := confmaptest.LoadConf(filepath.Join("testdata", "config.yaml")) + require.NoError(t, err) + sub, err := cm.Sub(name) + require.NoError(t, err) + sub, err = sub.Sub("resource_attributes") + require.NoError(t, err) + cfg := DefaultResourceAttributesConfig() + require.NoError(t, sub.Unmarshal(&cfg)) + return cfg +} diff --git a/processor/resourcedetectionprocessor/internal/kubeadm/internal/metadata/generated_resource.go b/processor/resourcedetectionprocessor/internal/kubeadm/internal/metadata/generated_resource.go new file mode 100644 index 000000000000..83834dfd62c8 --- /dev/null +++ b/processor/resourcedetectionprocessor/internal/kubeadm/internal/metadata/generated_resource.go @@ -0,0 +1,36 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package metadata + +import ( + "go.opentelemetry.io/collector/pdata/pcommon" +) + +// ResourceBuilder is a helper struct to build resources predefined in metadata.yaml. +// The ResourceBuilder is not thread-safe and must not to be used in multiple goroutines. +type ResourceBuilder struct { + config ResourceAttributesConfig + res pcommon.Resource +} + +// NewResourceBuilder creates a new ResourceBuilder. This method should be called on the start of the application. +func NewResourceBuilder(rac ResourceAttributesConfig) *ResourceBuilder { + return &ResourceBuilder{ + config: rac, + res: pcommon.NewResource(), + } +} + +// SetK8sClusterName sets provided value as "k8s.cluster.name" attribute. +func (rb *ResourceBuilder) SetK8sClusterName(val string) { + if rb.config.K8sClusterName.Enabled { + rb.res.Attributes().PutStr("k8s.cluster.name", val) + } +} + +// Emit returns the built resource and resets the internal builder state. +func (rb *ResourceBuilder) Emit() pcommon.Resource { + r := rb.res + rb.res = pcommon.NewResource() + return r +} diff --git a/processor/resourcedetectionprocessor/internal/kubeadm/internal/metadata/generated_resource_test.go b/processor/resourcedetectionprocessor/internal/kubeadm/internal/metadata/generated_resource_test.go new file mode 100644 index 000000000000..c43337278947 --- /dev/null +++ b/processor/resourcedetectionprocessor/internal/kubeadm/internal/metadata/generated_resource_test.go @@ -0,0 +1,40 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package metadata + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestResourceBuilder(t *testing.T) { + for _, tt := range []string{"default", "all_set", "none_set"} { + t.Run(tt, func(t *testing.T) { + cfg := loadResourceAttributesConfig(t, tt) + rb := NewResourceBuilder(cfg) + rb.SetK8sClusterName("k8s.cluster.name-val") + + res := rb.Emit() + assert.Equal(t, 0, rb.Emit().Attributes().Len()) // Second call should return empty Resource + + switch tt { + case "default": + assert.Equal(t, 1, res.Attributes().Len()) + case "all_set": + assert.Equal(t, 1, res.Attributes().Len()) + case "none_set": + assert.Equal(t, 0, res.Attributes().Len()) + return + default: + assert.Failf(t, "unexpected test case: %s", tt) + } + + val, ok := res.Attributes().Get("k8s.cluster.name") + assert.True(t, ok) + if ok { + assert.EqualValues(t, "k8s.cluster.name-val", val.Str()) + } + }) + } +} diff --git a/processor/resourcedetectionprocessor/internal/kubeadm/internal/metadata/package_test.go b/processor/resourcedetectionprocessor/internal/kubeadm/internal/metadata/package_test.go new file mode 100644 index 000000000000..1aba5ec4bb0b --- /dev/null +++ b/processor/resourcedetectionprocessor/internal/kubeadm/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/processor/resourcedetectionprocessor/internal/kubeadm/internal/metadata/testdata/config.yaml b/processor/resourcedetectionprocessor/internal/kubeadm/internal/metadata/testdata/config.yaml new file mode 100644 index 000000000000..4017b2f2932c --- /dev/null +++ b/processor/resourcedetectionprocessor/internal/kubeadm/internal/metadata/testdata/config.yaml @@ -0,0 +1,9 @@ +default: +all_set: + resource_attributes: + k8s.cluster.name: + enabled: true +none_set: + resource_attributes: + k8s.cluster.name: + enabled: false diff --git a/processor/resourcedetectionprocessor/internal/kubeadm/kubeadm.go b/processor/resourcedetectionprocessor/internal/kubeadm/kubeadm.go new file mode 100644 index 000000000000..ca39c6dde262 --- /dev/null +++ b/processor/resourcedetectionprocessor/internal/kubeadm/kubeadm.go @@ -0,0 +1,61 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package kubeadm // import "github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/internal/kubeadm" + +import ( + "context" + "fmt" + + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/processor" + conventions "go.opentelemetry.io/collector/semconv/v1.6.1" + "go.uber.org/zap" + + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/metadataproviders/kubeadm" + "github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/internal" + "github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor/internal/kubeadm/internal/metadata" +) + +const ( + TypeStr = "kubeadm" + defaultConfigMapName = "kubeadm-config" + defaultConfigMapNamespace = "kube-system" +) + +var _ internal.Detector = (*detector)(nil) + +type detector struct { + provider kubeadm.Provider + logger *zap.Logger + ra *metadata.ResourceAttributesConfig + rb *metadata.ResourceBuilder +} + +func NewDetector(set processor.Settings, dcfg internal.DetectorConfig) (internal.Detector, error) { + cfg := dcfg.(Config) + + kubeadmProvider, err := kubeadm.NewProvider(defaultConfigMapName, defaultConfigMapNamespace, cfg.APIConfig) + if err != nil { + return nil, fmt.Errorf("failed creating kubeadm provider: %w", err) + } + + return &detector{ + provider: kubeadmProvider, + logger: set.Logger, + ra: &cfg.ResourceAttributes, + rb: metadata.NewResourceBuilder(cfg.ResourceAttributes), + }, nil +} + +func (d *detector) Detect(ctx context.Context) (resource pcommon.Resource, schemaURL string, err error) { + if d.ra.K8sClusterName.Enabled { + clusterName, err := d.provider.ClusterName(ctx) + if err != nil { + return pcommon.NewResource(), "", fmt.Errorf("failed getting k8s cluster name: %w", err) + } + d.rb.SetK8sClusterName(clusterName) + } + + return d.rb.Emit(), conventions.SchemaURL, nil +} diff --git a/processor/resourcedetectionprocessor/internal/kubeadm/kubeadm_test.go b/processor/resourcedetectionprocessor/internal/kubeadm/kubeadm_test.go new file mode 100644 index 000000000000..a7125552bc2a --- /dev/null +++ b/processor/resourcedetectionprocessor/internal/kubeadm/kubeadm_test.go @@ -0,0 +1,75 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package kubeadm + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/processor/processortest" + conventions "go.opentelemetry.io/collector/semconv/v1.6.1" + + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/k8sconfig" + "github.com/open-telemetry/opentelemetry-collector-contrib/internal/metadataproviders/kubeadm" +) + +var _ kubeadm.Provider = (*mockMetadata)(nil) + +type mockMetadata struct { + mock.Mock +} + +func (m *mockMetadata) ClusterName(_ context.Context) (string, error) { + args := m.MethodCalled("ClusterName") + return args.String(0), args.Error(1) +} + +func TestDetect(t *testing.T) { + md := &mockMetadata{} + md.On("ClusterName").Return("cluster-1", nil) + cfg := CreateDefaultConfig() + // set k8s cluster env variables and auth type to create a dummy API client + cfg.APIConfig.AuthType = k8sconfig.AuthTypeNone + t.Setenv("KUBERNETES_SERVICE_HOST", "127.0.0.1") + t.Setenv("KUBERNETES_SERVICE_PORT", "6443") + + k8sDetector, err := NewDetector(processortest.NewNopSettings(), cfg) + require.NoError(t, err) + k8sDetector.(*detector).provider = md + res, schemaURL, err := k8sDetector.Detect(context.Background()) + require.NoError(t, err) + assert.Equal(t, conventions.SchemaURL, schemaURL) + md.AssertExpectations(t) + + expected := map[string]any{ + conventions.AttributeK8SClusterName: "cluster-1", + } + + assert.Equal(t, expected, res.Attributes().AsRaw()) +} + +func TestDetectDisabledResourceAttributes(t *testing.T) { + md := &mockMetadata{} + cfg := CreateDefaultConfig() + cfg.ResourceAttributes.K8sClusterName.Enabled = false + // set k8s cluster env variables and auth type to create a dummy API client + cfg.APIConfig.AuthType = k8sconfig.AuthTypeNone + t.Setenv("KUBERNETES_SERVICE_HOST", "127.0.0.1") + t.Setenv("KUBERNETES_SERVICE_PORT", "6443") + + k8sDetector, err := NewDetector(processortest.NewNopSettings(), cfg) + require.NoError(t, err) + k8sDetector.(*detector).provider = md + res, schemaURL, err := k8sDetector.Detect(context.Background()) + require.NoError(t, err) + assert.Equal(t, conventions.SchemaURL, schemaURL) + md.AssertExpectations(t) + + expected := map[string]any{} + + assert.Equal(t, expected, res.Attributes().AsRaw()) +} diff --git a/processor/resourcedetectionprocessor/internal/kubeadm/metadata.yaml b/processor/resourcedetectionprocessor/internal/kubeadm/metadata.yaml new file mode 100644 index 000000000000..ed2518a06686 --- /dev/null +++ b/processor/resourcedetectionprocessor/internal/kubeadm/metadata.yaml @@ -0,0 +1,9 @@ +type: resourcedetectionprocessor/kubeadm + +parent: resourcedetection + +resource_attributes: + k8s.cluster.name: + description: The Kubernetes cluster name + type: string + enabled: true From 09d4ae303370d708e916afd951157a3635eb69e0 Mon Sep 17 00:00:00 2001 From: Josh Soref <2119212+jsoref@users.noreply.github.com> Date: Mon, 13 Jan 2025 18:24:23 -0500 Subject: [PATCH 12/13] [chore] Spelling changelog (#37129) #### Description Fix spelling in changelog https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/37128#discussion_r1909840706 #### Link to tracking issue * #37128 --------- Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com> --- CHANGELOG.md | 152 +++++++++++++++++++++++++-------------------------- 1 file changed, 76 insertions(+), 76 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae3efe22bd74..c8c177130508 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -139,7 +139,7 @@ If you are looking for developer-facing changes, check out [CHANGELOG-API.md](./ respond to GET requests for the health check only. - `kafkaexporter, kafkareceiver`: Add a new mechanism "AWS_MSK_IAM_OAUTHBEARER" for kafka exporter and kafka receiver. This mechanism use the AWS MSK IAM SASL Signer for Go https://github.com/aws/aws-msk-iam-sasl-signer-go. (#19747) -- `loadbalancingexporter`: Adds a an optional configuration to the k8s resolver which returns hostnames instead of IPs for headless services pointing at statefulsets (#18412) +- `loadbalancingexporter`: Adds an optional configuration to the k8s resolver which returns hostnames instead of IPs for headless services pointing at statefulsets (#18412) - `mongodbatlasreceiver`: Adds additional metrics to the MongoDB Atlas receiver (#36525) Adds a number of new default disabled metrics to the MongoDB Atlas receiver. These metrics are: - mongodbatlas.disk.partition.queue.depth @@ -154,7 +154,7 @@ If you are looking for developer-facing changes, check out [CHANGELOG-API.md](./ - `signaltometrics`: Add config validation and custom OTTL functions (#35930) Adds config validation for the signal to metrics connector. Also introduces `AdjustedCount` OTTL function. - `testbed`: Add batcher performance tests (#36206) -- `tesbed`: add options for retry/storage for filelog sender (#36781) +- `testbed`: add options for retry/storage for filelog sender (#36781) ### 🧰 Bug fixes 🧰 @@ -212,7 +212,7 @@ If you are looking for developer-facing changes, check out [CHANGELOG-API.md](./ - `awsemfexporter`: Add support for 1 second metric resolution in CloudWatch Embedded Metrics Format based on metric attributes (#29506) - `awsemfexporter`: Improvement unit conversion during EMF log translation (#35937) - `sumologicexporter`: adding new products for auto discovery (#35622) -- `postgresqlreceiver`: Added new postgresql metrics to acheive parity with Telegraf (#36528) +- `postgresqlreceiver`: Added new postgresql metrics to achieve parity with Telegraf (#36528) - `loadbalancingexporter`: Adding sending_queue, retry_on_failure and timeout settings to loadbalancing exporter configuration (#35378, #16826) When switching to top-level sending_queue configuration - users should carefully review queue size In some rare cases setting top-level queue size to n*queueSize might be not enough to prevent data loss @@ -222,7 +222,7 @@ If you are looking for developer-facing changes, check out [CHANGELOG-API.md](./ - `tailsamplingprocessor`: Adds decision cache for non-sampled trace IDs (#31583) - `cmd/opampsupervisor`: Support environment variable expansion in the OpAMP supervisor config. (#36269) - `pkg/ottl`: Move debug log to `Statement.Execute` so that components using it instead of `StatementSequence` also get debug logs. (#36456) -- `routingconnector`: Add abiilty to route by 'datapoint' context (#36523) +- `routingconnector`: Add ability to route by 'datapoint' context (#36523) - `signalfxreceiver`: Follow receiver contract based on type of error (#5909) Use 503 error code for retryable and 400 error code for not-retryable errors instead of responding with a 500 unconditionally. @@ -296,7 +296,7 @@ If you are looking for developer-facing changes, check out [CHANGELOG-API.md](./ - `exporter/loadbalancing`: Shutdown exporters during collector shutdown. This fixes a memory leak. (#36024) - `pkg/ottl`: Respect the `depth` option when flattening slices using `flatten` (#36161) The `depth` option is also now required to be at least `1`. -- `prometheusexporter`: reject metrics whose types have changed, use pre-existing descriptions when help strings change (#28617) +- `prometheusexporter`: reject metrics whose types have changed, use preexisting descriptions when help strings change (#28617) - `pkg/stanza`: Synchronous handling of entries passed from the log emitter to the receiver adapter (#35453) - `prometheusreceiver`: Fix prometheus receiver to support static scrape config with Target Allocator (#36062) @@ -360,7 +360,7 @@ If you are looking for developer-facing changes, check out [CHANGELOG-API.md](./ ### 🧰 Bug fixes 🧰 - `awsfirehosereceiver`: make otlp_v1 a valid record type (#35750, #36125) -- `datadogreceiver`: Return a json reponse instead of "OK" when a trace is received with a newer protocol version. (#35705) +- `datadogreceiver`: Return a json response instead of "OK" when a trace is received with a newer protocol version. (#35705) - `datadogreceiver`: Changes response message for `/api/v1/check_run` 202 response to be JSON and on par with Datadog API spec (#36027) - `receiver/windowseventlog`: Fix panic when rendering long event messages. (#36179) - `hostmetricsreceiver`: Do not set the default value of HOST_PROC_MOUNTINFO to respect root_path (#35990) @@ -714,7 +714,7 @@ arrow.waiter_limit -> admission.waiter_limit Span events are now supported in OTel mapping mode. They will be routed to `logs-${data_stream.dataset}-${data_stream.namespace}` if `traces_dynamic_index::enabled` is `true`. - `transformprocessor`: Support aggregating metrics based on their attribute values and substituting the values with a new value. (#16224) - `kafkareceiver`: Adds tunable fetch sizes to Kafka Receiver (#22741, #34431) - Adds the ability to tune the minumum, default and maximum fetch sizes for the Kafka Receiver + Adds the ability to tune the minimum, default and maximum fetch sizes for the Kafka Receiver - `solarwindsapmsettingsextension`: Added logic for refresh function (#27668) - `githubreceiver`: Promote GitHub receiver metrics to alpha status. (#34960) - `googlecloudmonitoringreceiver`: Enhancing the Google Cloud monitoring receiver to establish a client connection, scrape GCP Cloud Metrics, and transform them into an OpenTelemetry compatible format for pipeline processing. (#33762) @@ -980,7 +980,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) ### 🚀 New components 🚀 -- `logdedupeprocessor`: Add new logdedupeprocessor processor that deduplicates log entries. (#34118) +- `logdedupprocessor`: Add new logdedupprocessor processor that deduplicates log entries. (#34118) - `coralogixprocessor`: creating new component for coralogix features (#33090) - `googlecloudmonitoringreceiver`: Adding new component - [Google Cloud monitoring](https://cloud.google.com/monitoring/api/metrics_gcp) receiver to fetch GCP Cloud Metrics and transform to OpenTelemetry compatible format. (#33762) @@ -995,7 +995,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `bearertokenauthextension`: use constant time comparison. This fixes CVE-2024-42368 (#34516) - `processor/k8sattributes`: Add support for `container.image.repo_digests` metadata (#34029) - `datadogconnector`: Move feature gate `connector.datadogconnector.NativeIngest` to beta (#34549) - When this feature gate is enabled (default), the datadog connector uses the new API to produce APM stats under the hood. | The new API has better throughput when your spans have many attributes (especially container related attributes). Funtional-wise the new API should have no user-facing change compared to the old API. | However if you observe any unexpected behaviors, you can disable this feature gate to revert to the old stats processing APIs. + When this feature gate is enabled (default), the datadog connector uses the new API to produce APM stats under the hood. | The new API has better throughput when your spans have many attributes (especially container related attributes). Functional-wise the new API should have no user-facing change compared to the old API. | However if you observe any unexpected behaviors, you can disable this feature gate to revert to the old stats processing APIs. - `elasticsearchexporter`: Add opt-in support for the experimental `batcher` config (#32377) By enabling (or explicitly disabling) the batcher, the Elasticsearch exporter's existing batching/buffering logic will be disabled, and the batch sender will be used. @@ -1153,7 +1153,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `pkg/ottl`: Added Hex() converter function (#31929) - `pkg/ottl`: Add IsRootSpan() converter function. (#32918) - Converter `IsRootSpan()` returns `true` if the span in the corresponding context is root, that means its `parent_span_id` equals to hexadecimal representation of zero. In all other scenarios function returns `false`. + Converter `IsRootSpan()` returns `true` if the span in the corresponding context is root, that means its `parent_span_id` equals the hexadecimal representation of zero. In all other scenarios function returns `false`. - `vcenterreceiver`: Adds additional vCenter resource pool metrics and a memory_usage_type attribute for vcenter.resource_pool.memory.usage metric to use. (#33607) Added "vcenter.resource_pool.memory.swapped", "vcenter.resource_pool.memory.ballooned", and "vcenter.resource_pool.memory.granted" metrics. Also added an additional attribute, "memory_usage_type" for "vcenter.resource_pool.memory.usage" metric, which is @@ -1200,7 +1200,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) which fields should be redacted. As a result, fields that are not sensitive are no longer redacted. - `azuremonitorreceiver`: Upgrade stability to alpha (#33689) -- `windowsperfcountersreceiver`: `windowsperfcountersreceiver` now appends an index number to additional instance names that share a name. An example of this is when scraping `
rocess(*)` counters with multiple running instances of the same executable. (#32319) +- `windowsperfcountersreceiver`: `windowsperfcountersreceiver` now appends an index number to additional instance names that share a name. An example of this is when scraping `\Process(*)` counters with multiple running instances of the same executable. (#32319) **NOTES** - This change can expose cardinality issues where the counters were previously collapsed under the non-indexed instance name. - The change mimics Windows Performance Monitor behavior: The first instance name remains unchanged, additional instances are suffixed with `#<N>` where `N=1` and is increased for each duplicate. @@ -1436,7 +1436,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `OTel-Arrow`: Update to OTel-Arrow v0.24.0 (#26491) - `pkg/ottl`: Add debug logs to help troubleshoot OTTL statements/conditions (#33274) - `pkg/ottl`: Introducing `append` function for appending items into an existing array (#32141) -- `pkg/ottl`: Introducing `Uri` converter parsing URI string into SemConv (#32433) +- `pkg/ottl`: Introducing `URI` converter parsing URI string into SemConv (#32433) - `probabilisticsamplerprocessor`: Add Proportional and Equalizing sampling modes (#31918) Both the existing hash_seed mode and the two new modes use OTEP 235 semantic conventions to encode sampling probability. - `prometheusreceiver`: Resource attributes produced by the prometheus receiver now include stable semantic conventions for `server` and `url`. (#32814) @@ -1540,7 +1540,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) * do not support source headers - `sumologicexporter`: change metrics behavior (#31479) - * remove suppport for carbon2 and graphite + * remove support for carbon2 and graphite * add support for otlp format * do not support metadata attributes * do not support source headers @@ -1581,7 +1581,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `filelogreceiver`: Add container operator parser (#31959) - `jsonlogencodingextension`: Move jsonlogencodingextension to alpha (#32697) -- `exceptionsconnector`: Make span name a default dimension for ouput metrics and log records. (#32162) +- `exceptionsconnector`: Make span name a default dimension for output metrics and log records. (#32162) - `azureblobreceiver`: Support service principal authentication for Blob storage (#32705) - `deltatocumulativeprocessor`: exponential histogram accumulation (#31340) accumulates exponential histogram datapoints by adding respective bucket counts. also handles downscaling, changing zero-counts, offset adaptions and optional fields @@ -1607,7 +1607,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `processor/transform`: Allow common where clause (#27830) - `loadbalancingexporter`: Improve the performance when merging traces belonging to the same backend (#32032) - `pkg/ottl`: Added support for timezone in Time converter (#32140) -- `jsonlogencodingextension`: Adds a new encoding option for JSON log encoding exension to grab attributes and resources from a log and output that in JSON format. (#32679) +- `jsonlogencodingextension`: Adds a new encoding option for JSON log encoding extension to grab attributes and resources from a log and output that in JSON format. (#32679) - `probabilisticsamplerprocessor`: Adds the `FailClosed` flag to solidify current behavior when randomness source is missing. (#31918) - `prometheusremotewriteexporter`: Add `exporter.prometheusremotewritexporter.RetryOn429` feature gate to retry on http status code 429 response. (#31032) The feature gate is initially disabled by default. @@ -1639,11 +1639,11 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) This fixes the bug when all errors are retriable in bulk request response, retried docs will be included in failed docs. - `cmd/opampsupervisor`: The OpAMP supervisor now configures the `ppid` parameter of the opamp extension, which allows the collector to shut down if the supervisor is no longer running. (#32189) -- `vcenterreceiver`: Adds inititially disabled packet drop rate metric for VMs. (#32929) +- `vcenterreceiver`: Adds initially disabled packet drop rate metric for VMs. (#32929) - `awskinesisexporter`: fixed compressed data not generating the compression footers (#32860) - `splunkhecreceiver`: Fix single metric value parsing (#33084) - `vcenterreceiver`: vcenterreceiver client no longer returns error if no Virtual Apps are found. (#33073) -- `vcenterreceiver`: Adds inititially disabled new packet rate metrics to replace the existing ones for VMs & Hosts. (#32835) +- `vcenterreceiver`: Adds initially disabled new packet rate metrics to replace the existing ones for VMs & Hosts. (#32835) - `googlecloudpubsubreceiver`: Fix memory leak during shutdown (#32361) - `datadogexporter`: Compress host metadata before sending with gzip. (#32992) - `resourcedetectionprocessor`: Change type of `host.cpu.stepping` from int to string. (#31136) @@ -1968,8 +1968,8 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `prometheusremotewriteexporter`: Publish telemetry about translation of metrics from Otel to Prometheus. Don't drop all data points if some fail translation. (#29729) - `prometheusreceiver`: Use confighttp for target allocator client (#31449) - `spanmetricsconnector`: Add `metrics_expiration` option to enable expiration of metrics if spans are not received within a certain time frame. (#30559) - The feature can be configured by specifiying the desired duration in the `metrics_expiration` option. By default, the expiration is disabled (set to 0). -- `splunkentreceiver`: Updated the config.go and propogated these changes to other receiver components. Change was necessary to differentiate different configurable endpoints. (#30254) + The feature can be configured by specifying the desired duration in the `metrics_expiration` option. By default, the expiration is disabled (set to 0). +- `splunkentreceiver`: Updated the config.go and propagated these changes to other receiver components. Change was necessary to differentiate different configurable endpoints. (#30254) - `exporter/datadogexporter`: Do not drop traces when payload channel is full. (#31893) - `connector/datadogconnector`: Do not resolve container tags if payload already has tags associated with it. (#31893) @@ -2160,7 +2160,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) ### 🚀 New components 🚀 - `sumologicextension`: add configuration and readme (#29601) -- `failoverconnector`: Refactor of connector to seperate concerns between managing indexes and core failover component (#20766) +- `failoverconnector`: Refactor of connector to separate concerns between managing indexes and core failover component (#20766) - `otelarrow`: Skeleton of new OpenTelemetry Protocol with Apache Arrow Receiver (#26491) - `processor/interval`: Adds the initial structure for a new processor that aggregates metrics and periodically forwards the latest values to the next component in the pipeline. (#29461) As per the CONTRIBUTING.md recommendations, this PR only creates the basic structure of the processor. The concrete implementation will come as a separate followup PR @@ -2237,7 +2237,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `pkg/ottl`: Fix parsing of string escapes in OTTL (#23238) - `pkg/stanza`: Recombine operator should always recombine partial logs (#30797) Previously, certain circumstances could result in partial logs being emitted without any - recombiniation. This could occur when using `is_first_entry`, if the first partial log from + recombination. This could occur when using `is_first_entry`, if the first partial log from a source was emitted before a matching "start of log" indicator was found. This could also occur when the collector was shutting down. @@ -2276,7 +2276,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) ### 💡 Enhancements 💡 - `pkg/stanza`: Add a json array parser operator and an assign keys transformer. (#30321) - Json array parser opreator can be used to parse a json array string input into a list of objects. | + Json array parser operator can be used to parse a json array string input into a list of objects. | Assign keys transformer can be used to assigns keys from the configuration to an input list - `splunkhecexporter`: Batch data according to access token and index, if present. (#30404) @@ -2297,7 +2297,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `opensearchexporter`: added opensearch exporter to the contrib distribution metadata (#30183) - `pkg/ottl`: Add `flatten` function for flattening maps (#30455) -- `redisreciever`: adds metric for slave_repl_offset (#6942) +- `redisreceiver`: adds metric for slave_repl_offset (#6942) also adds a shell script to set up docker-compose integration test - `exporter/datadogexporter`: Add kafka metrics mapping. This allows users of the JMX Receiver/JMX Metrics Gatherer and kafka metrics receiver to have access to the OOTB kafka Dashboard. (#30731) - `receiver/sqlquery`: Add debug log when running SQL query (#29672) @@ -2308,7 +2308,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `receiver/filelog`: fix panic after upgrading from v0.71.0 when using storage (#30235) - `clickhouseexporter`: Fix clickhouse exporter insert metrics data bug (#30210) - `prometheusremotewriteexporter`: Check if the context was canceled by a timeout in the component level to avoid unnecessary retries. (#30308) -- `elasticsearchreceifver`: Fix nil panic on non-linux systems (#30140) +- `elasticsearchreceiver`: Fix nil panic on non-linux systems (#30140) - `kafkareceiver`: The Kafka receiver now exports some partition-specific metrics per-partition, with a `partition` tag (#30177) The following metrics now render per partition: - kafka_receiver_messages @@ -2517,7 +2517,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) This is an alternative to `system.memory.usage` metric with `state=free`. Linux starting from 3.14 exports "available" memory. It takes "free" memory as a baseline, and then factors in kernel-specific values. This is supposed to be more accurate than just "free" memory. - For reference, see the calculations [here](https://superuser.com/a/980821). + For reference, see the calculations referenced in [What is the difference between MemFree and MemAvailable in /proc/meminfo](https://superuser.com/a/980821). See also `MemAvailable` in [/proc/meminfo](https://man7.org/linux/man-pages/man5/proc.5.html). - `azuremonitorexporter`: Updated Azure Monitor Exporter service version from v2.0 to v2.1. (#29234) @@ -2551,7 +2551,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) The purpose of this list was to allow us to emit a lot whenever a path was seen for the first time. This removes the separate list and relies instead on the same mechanism as checkpointing. Now, a similar log is emitted any time a file is found which is not currently checkpointed. Because the checkpointing mechanism does not maintain history - indefintiely, it is now possible that a log will be emitted for the same file path. This will happen when no file exists at + indefinitely, it is now possible that a log will be emitted for the same file path. This will happen when no file exists at the path for a period of time. - `dockerstatsreceiver`: cpu.container.percent metric is removed in favor of container.cpu.utilization (#21807) @@ -2660,7 +2660,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `processor/k8sattributes`: Set attributes from namespace/node labels or annotations even if node/namespaces attribute are not set. (#28837) - `datadogexporter`: Only extract DD container tags from resource attributes. Previously, container tags were also extracted from span attributes. (#29156) - `datadogexporter`: Only add container tags in dedicated container tag section. Previously, container tags were also added as span tags. Container tags will now only be accessible via the span container tab, and not as span tags. (#29156) -- `pkg/stanza`: Fix data-corruption/race-condition issue in udp async (reuse of buffer); use buffer pool isntead. (#27613) +- `pkg/stanza`: Fix data-corruption/race-condition issue in udp async (reuse of buffer); use buffer pool instead. (#27613) - `datadogexporter`: Fixes potential log records loss on a transient network/connectivity error (#24550) The Datadog exporter threats network/connectivity errors (http client doesn't receive a response) as permanent errors, which can lead to log records loss. This change makes these errors retryable. - `servicegraphprocessor, servicegraphconnector`: Measure latency in seconds instead of milliseconds (#27488) @@ -2711,7 +2711,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `doubleconverter`: Adding a double converter into pkg/ottl (#22056) - `syslogreceiver`: validate protocol name (#27581) - `elasticsearchexporter`: add missing scope info in span attributes (#27282) -- `entension/storage/filestorage`: Add support for setting bbolt fsync option (#20266) +- `extension/storage/filestorage`: Add support for setting bbolt fsync option (#20266) - `filelogreceiver`: Add a new "top_n" option to specify the number of files to track when using ordering criteria (#23788) - `azuredataexplorerexporter`: Added exporter helper config support for Azure Data Explorer exporter (#24329) - `k8sclusterreceiver`: add optional k8s.pod.qos_class resource attribute (#27483) @@ -2820,7 +2820,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) In Linux and Darwin all fields are populated. In Windows only family, vendor.id and model.name are populated. - `pkg/stanza`: Add 'omit_pattern' setting to `split.Config`. (#26381) This can be used omit the start or end pattern from a log entry. -- `skywaklingreceiver`: implement receiver for JVM metrics in Skywalking and adapted it to the OpenTelemetry protocol. (#20315) +- `skywalkingreceiver`: implement receiver for JVM metrics in Skywalking and adapted it to the OpenTelemetry protocol. (#20315) - `statsdreceiver`: Add TCP support to statsdreceiver (#23327) - `azuredataexplorerexporter`: Added an optional column in the exported trace data to store the status code and message as a dynamic field. (#26496) - `statsdreceiver`: Allow for empty tag sets (#27011) @@ -2907,12 +2907,12 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `processor/routing`: When using attributes instead of resource attributes, the routing processor would crash the collector. This does not affect the connector version of this component. (#26462) - `awsemfexporter`: Fix possible panic in when configuration option `awsemf.output_destination:stdout` is set (#26250) - `snmpreceiver`: Fix how to determine how many RAs on a metric are scalar (#26363) - We now create the proper number of resources for configurations where a resource uses fewer than the available number of scalar resource attribtues. + We now create the proper number of resources for configurations where a resource uses fewer than the available number of scalar resource attributes. - `processor/tailsampling`: Added saving instrumentation library information for tail-sampling (#13642) - `receiver/kubeletstats`: Fixes client to refresh service account token when authenticating with kubelet (#26120) - `datadogexporter`: Fixes crash when mapping OTLP Exponential Histograms with no buckets. These will now be dropped instead. (#26103) - `filelogreceiver`: Fix the behavior of the add operator to continue to support EXPR(env("MY_ENV_VAR")) expressions (#26373) -- `snmpreceiver`: SNMP values of type Counter64 were seen as unsupported, because the returned data type unint64 was unhandeled. (#23897, #26119) +- `snmpreceiver`: SNMP values of type Counter64 were seen as unsupported, because the returned data type uint64 was unhandeled. (#23897, #26119) - `pkg/stanza`: Fix issue unsupported type 'syslog_parser' (#26452) ## v0.84.0 @@ -2937,7 +2937,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `redisreceiver`: Adding username parameter for connecting to redis (#24408) - `postgresqlreceiver`: Added `postgresql.temp_files` metric. (#26080) -- `receiver/azuremonitor`: Added new attrbutes to the metrics like name, type and resource_group. (#24774) +- `receiver/azuremonitor`: Added new attributes to the metrics like name, type and resource_group. (#24774) - `clickhouseexporter`: Change writing of metrics data to batch (#24403) - `signalfxexporter`: Added a mechanism to drop histogram buckets (#25845) - `journaldreceiver`: add support for identifiers (#20295) @@ -3014,7 +3014,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) This allows OTTL to pass Converters themselves as a parameter so they can be executed within the function. - `resourcedetectionprocessor`: GCP resource detection processor can automatically add `gcp.gce.instance.hostname` and `gcp.gce.instance.name` attributes. (#24598) -- `splunkhecexporter`: Add heartbeat check while startup and new config param, heartbeat/startup (defaults to false). This is different than the healtcheck_startup, as the latter doesn't take token or index into account. (#24411) +- `splunkhecexporter`: Add heartbeat check while startup and new config param, heartbeat/startup (defaults to false). This is different than the healthcheck_startup, as the latter doesn't take token or index into account. (#24411) - `hostmetricsreceiver`: Report logical and physical number of CPUs as metric. (#22099) Use the `system.cpu.logical.count::enabled` and `system.cpu.physical.count::enabled` flags to enable them @@ -3121,7 +3121,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `receiver/nsxt`: Change the type of `Config.Password` to be `configopaque.String` (#17273) - `receiver/podman`: Change the type of `Config.SSHPassphrase` to be `configopaque.String` (#17273) - `receiver/postgresql`: Change the type of `Config.Password` to be `configopaque.String` (#17273) -- `prometheusreciever`: Remove unused buffer_period and buffer_count configuration options (#24258) +- `prometheusreceiver`: Remove unused buffer_period and buffer_count configuration options (#24258) - `prometheusreceiver`: Add the `trim_metric_suffixes` configuration option to allow enable metric suffix trimming. (#21743, #8950) When enabled, suffixes for unit and type are trimmed from metric names. If you previously enabled the `pkg.translator.prometheus.NormalizeName` @@ -3145,7 +3145,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `dynatraceexporter`: Add deprecation note to Dynatrace metrics exporter (#23992) - `pkg/stanza`: Deprecate fileconsumer.EmitFunc in favor of fileconsumer.emit.Callback (#24036) -- `pkg/stanza`: Deprecate filconsumer.Finder and related sortation structs and functions (#24013) +- `pkg/stanza`: Deprecate fileconsumer.Finder and related sortation structs and functions (#24013) - `servicegraphprocessor`: Service Graph Processor is deprecated in favor of the Service Graph Connector (#19737) ### 🚀 New components 🚀 @@ -3414,7 +3414,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `pkg/ottl`: Add new `IsString` and `IsMap` functions to facilitate type checking. (#22750) Especially useful for checking log body type before parsing. - `pkg/ottl`: Adds `StandardFuncs` and `StandardConverters` to facilitate function map generation. (#23190) - This change means that new functions added to ottlfuncs get automatically added to Cotnrib components that use OTTL + This change means that new functions added to ottlfuncs get automatically added to Contrib components that use OTTL - `pkg/ottl`: Change replacement functions to accept a path expression as a replacement (#22787) The following replacement functions now accept a path expression as a replacement: - replace_match @@ -3539,7 +3539,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `filelogreceiver`: Fix issue where empty files would not be skipped, resulting in extraneous errors. (#22815) - `servicegraphprocessor`: consume traces even metric count is equal to 0 (#23028) - `fileexporter`: Fixes broken lines when rotation is set. (#22747) -- `receiver/purefareceiver`: Ensure that all endpoints beyond volumes and hosts are beeing scraped. (#14886) +- `receiver/purefareceiver`: Ensure that all endpoints beyond volumes and hosts are being scraped. (#14886) - `exporter/datadog`: `tls::insecure_skip_verify` is now honored when exporting traces. (#22772) - `exporter/splunk_hec`: Make sure the `max_event_size` option is used to drop events larger than `max_event_size` instead of using it for batch size. (#18066) - `httpcheckreceiver`: Update default collection interval to match documented value (#23019) @@ -3567,7 +3567,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `receivers`: Updating receivers that run intervals to use standard interval by default (#22138) - `datadog receiver`: Updating datadog translations to align more closely to semantic convention (#21210, #21525) - `service.name` is moved from assigned from span attributes to resource attributes - - Moved from using datadog's `span.Resouce` to `span.Name` to set span name + - Moved from using datadog's `span.Resource` to `span.Name` to set span name - Exported traces are now grouped by `service.name` by default - `nginxreceiver`: Add featuregate to emit 'nginx.connections_current' as a non-monotonic sum (#4326) @@ -3612,7 +3612,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `cmd/mdatagen`: Allow setting resource_attributes without introducing the metrics builder. (#21516) - `receiver/mongodbatlasreceiver`: Allow collection of MongoDB Atlas Access Logs as a new feature of the MongoDBAtlas receiver. (#21182) - `sshcheckreceiver`: Promote sshcheckreceiver to alpha (#21488) -- `pkg/ottl`: Add `FloatLikeGetter` and `FloatGetter` to facilitate float retrival for functions. (#21896) +- `pkg/ottl`: Add `FloatLikeGetter` and `FloatGetter` to facilitate float retrieval for functions. (#21896) - `pkg/ottl`: Add access to get and set span kind using a string (#21773) - `processor/routingprocessor`: Instrument the routing processor with non-routed spans/metricpoints/logrecords counters (OTel SDK). (#21476) - `skywalkingreceiver`: Refactoring the code structure/directory for the following metrics receiver implementation (#20315) @@ -3804,7 +3804,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `clickhouseexporter`: Insert instrumentation scope name and version to SpanAttributes column in traces table (#17408) - `stanza`: Enhancement pkg/stanza/flatten to support resource and attributes (#20448) -- `azuredataexplorerexporter`: Migrate stablity to beta. All the dependent libraries have moved to beta. (#19161) +- `azuredataexplorerexporter`: Migrate stability to beta. All the dependent libraries have moved to beta. (#19161) - `awsecscontainermetricsreceiver`: Add ServiceName from task metadata endpoint (#19728) - `mdatagen`: Warnings can now be specified in metadata.yaml to be included in the generated status table. (#20242) - `coralogixexporter`: Change coralogixexporter to default to gzip compression for logs and traces (#20337) @@ -3873,7 +3873,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) ### 🚀 New components 🚀 -- `lokireceiver`: The Loki receiver implements the [Loki push api](https://grafana.com/docs/loki/latest/clients/promtail/#loki-push-api) as specified [here](https://grafana.com/docs/loki/latest/api/#push-log-entries-to-loki) (#18635) +- `lokireceiver`: The Loki receiver implements the [Loki push api](https://grafana.com/docs/loki/latest/clients/promtail/#loki-push-api) as specified in [Loki HTTP API: Ingest logs](https://grafana.com/docs/loki/latest/reference/loki-http-api/#ingest-logs) (#18635) - `cloudflarereceiver`: Adds support for receiving logs from Cloudflare's LogPush API. (#19201) - `webhookeventreceiver`: New component wireframe for webhookeventreceiver (#18101) - `spanmetricsconnector`: Add the `spanmetricsconnector` connector to build. (#18760) @@ -3904,7 +3904,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `splunkhecreceiver`: Appends query param (index, source, sourcetype, and host) for raw path (#19632) - `splunkhecreceiver`: align error message with splunk enterprise to include No Data, Invalid Data Format, Event field is required, and Event field cannot be blank (#19219) -- `reciver/statsdreceiver`: Metrics emitted by the statsd receiver are batched by source IP address, available in context. (#15290) +- `receiver/statsdreceiver`: Metrics emitted by the statsd receiver are batched by source IP address, available in context. (#15290) ### 🧰 Bug fixes 🧰 @@ -3962,7 +3962,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) Add emf and raw log support for aws cloudwatch exporter. - `awsemfexporter`: The AWS EMF exporter now supports the additional configuration flag `retain_initial_value_of_delta_metric`. With this flag active the first value of a metric is not discarded but instead sent to AWS. (#16218) - `processor/tailsampling`: adds support for a BooleanAttribute PolicyType (#17545) - enables use of boolean attrbiutes in defining tail sampling policies + enables use of boolean attributes in defining tail sampling policies - `pkg/stanza`: Add `header_delimiter` option to the `csv_parser`. (#18198) - `datadogexporter`: Enable gzip compression for metric payloads submitted by native Datadog client (#17373) - `hostmetrics`: Have the hostmetrics receiver file system scraper use more debug messages (#18236) @@ -4026,7 +4026,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) They are now being removed. You should use the following metrics instead: `process.memory.usage`, `process.memory.virtual`. For details, see the [docs](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/v0.71.0/receiver/hostmetricsreceiver#transition-to-process-memory-metric-names-aligned-with-opentelemetry-specification). -- `promtailreceiver`: Promtail recevier is completely removed from the repository (#18474, #18493) +- `promtailreceiver`: Promtail receiver is completely removed from the repository (#18474, #18493) Promtail receiver in its current implementation has a dependency on Loki that leads to a huge amount of | dependencies. It is getting difficult to maintain those dependencies. That's why the decision to | remove promtail receiver was made @@ -4186,7 +4186,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `receiver/splunkhec`: Return 400 status code when nested indextime fields are present (#17308) - `exporter/loki`: Do not retry on 4xx status code (excluding 429), as these are permanent errors (#18059) - `mezmoexporter`: No longer require a specific path. This will allow for compatibility with both log analysis and pipeline. (#18011) -- `servicegraphprocessor`: Fix cache cleanup in servicegraph proccesor to also purge stale series (#16262) +- `servicegraphprocessor`: Fix cache cleanup in servicegraph processor to also purge stale series (#16262) - `snmpreceiver`: Set StartTimestamp to scraper start time. (#17984) - `snowflakereceiver`: added doc.go containing pragma for mdatagen (#17978) - `spanmetricsprocessor`: Fix a flaky test caused by a race condition between WaitGroup completion and observed logs being written and flushed. (#18014) @@ -4239,7 +4239,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `coralogixexporter`: improve coralogix exporter performance (#17268) improves coralogix exporter to send batched telemetry data to the backend - `internal/comparetest`: Do not ignore order of any slices by default, use an options for that. (#17551) -- `mysqlreceiver`: add mysql.commands metric with supprot for delete, insert, select, update (#14138) +- `mysqlreceiver`: add mysql.commands metric with support for delete, insert, select, update (#14138) - `exporter/dynatrace`: Provide more logs on the results of metrics submissions (#15248) - `prometheusremotewriteexporter`: Add support for converting OTLP Exponential Histograms to Prometheus Native Histograms (#16207) - `pkg/pdatautil`: Export comparetest and pdatautil modules under github.com/open-telemetry/opentelemetry-collector-contrib/pkg (#17873) @@ -4250,7 +4250,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `datadogprocessor`: Now that the Datadog processor is part of the official contrib distribution, it has been moved to the beta stability level. (#17862) - `kafkareceiver`: Prevent offset commit failures and connection issues by ensuring that sessions are quickly completed after consumer group rebalances. (#17312) - `lokiexporter`: Added QueueSettings validation into Config Validate method (#7841) -- `telemetrygen`: Moves tracegen functionality to the telemetrygen traces subcommand, as well as the existing Github actions (#9597) +- `telemetrygen`: Moves tracegen functionality to the telemetrygen traces subcommand, as well as the existing GitHub actions (#9597) - `pkg/ottl`: Add new `cache` path to all contexts which can be used as temporary cache during complex transformations (#16994) - `pkg/pdatatest`: Metric support compare exemplar (#17580) - `processor/probabilisticsampler`: Implement the FNV hash library for the probabilistic sampler. (#16456) @@ -4329,7 +4329,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `internal/comparetest`: add golden functions ReadLogs, WriteLogs, CompareLogs (#10896) - `haproxyreceiver`: Adds a new socket interface and CSV reader for haproxy stats (#16829) - `resourcedetectionprocessor`: Add support to detect Heroku resources (#16833) -- `logstransformprocessor`: Lets the logs transform processor directly pass messags to next consumer, avoiding the timing issues it previously exhibited. (#16604, #15378, #9761) +- `logstransformprocessor`: Lets the logs transform processor directly pass messages to next consumer, avoiding the timing issues it previously exhibited. (#16604, #15378, #9761) - `mdatagen`: Add ability to specify additional warnings in metadata.yaml (#17180) - `signalfxexporter`: Add all HTTP client settings to the SignalFx exporter configuration (#16807) - `snowflakereceiver`: added client to snowflakereceiver (#14754) @@ -4351,7 +4351,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `receiver/purefareceiver`: Set an explicit reload interval for all scrapers. (#16992) - `servicegraphprocessor`: fix servicegraphprocessor concurrent map read and write (#16850) - `filelogreceiver`: Truncate log entry if it is longer than `max_log_size` (#16487) -- `cmd/metadata`: Ensure template files are downloaded as part of the `go get` and embeded into the application (#17442) +- `cmd/metadata`: Ensure template files are downloaded as part of the `go get` and embedded into the application (#17442) ## v0.68.0 @@ -4394,7 +4394,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `postgresqlreceiver`: Fix issue where WAL stats query was incorrectly coalescing intervals. (#16769) - `splunkhecexporter`: Fix data race when gzip compression is enabled (#17083) Removed gzip writer pool and create a new one when needed. -- `splunkhecexporter`: Fix isssue where splunkhec exporter always returns over capacity error when compression is enabled and MaxContentLength is 0 (#17035) +- `splunkhecexporter`: Fix issue where splunkhec exporter always returns over capacity error when compression is enabled and MaxContentLength is 0 (#17035) ## v0.67.0 @@ -4506,7 +4506,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `splunkhecreceiver`: Add a healthcheck endpoint as part of the splunkhecreceiver. Just returns 200 for now if the receiver is running. (#15367) - `filterprocessor`: Add ability to filter spans, span events, metrics, datapoints, and logs via OTTL conditions (#16369) - `googlecloudspannerreceiver`: Configurably mask the PII in lock stats metrics. (#16343) -- `interna/coreinternal`: Split internal/coreinternal/processor into a separate internal/filter module (#16410) +- `internal/coreinternal`: Split internal/coreinternal/processor into a separate internal/filter module (#16410) ### 🧰 Bug fixes 🧰 @@ -4524,7 +4524,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) ### 💡 Enhancements 💡 -- `exporter/loki`: Automatic mapping beetwen `LogRecord.SeverityNumber` to `LogRecord.Attributes["level"]` (#14313) +- `exporter/loki`: Automatic mapping between `LogRecord.SeverityNumber` to `LogRecord.Attributes["level"]` (#14313) - `jmxreceiver`: Add the JMX metrics gatherer version 1.20.0-alpha to the supported jars hash list (#16356) - `mongodbreceiver`: Add additional metrics for mongodb locks (#13661) Add additional metrics for locks.acquire_count, locks.acquire_wait_count, locks.deadlock_count, locks.time_acquiring_micros @@ -4575,7 +4575,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `mongodbatlasreceiver`: Checks host and port before assigning attributes in `poll` mode (#16284) - `datadogexporter`: Fixes crash when logging error on logs exporter (#16077) - `pkg/ottl`: Fix list argument parsing when using internal arguments (#16298) -- `pkg/stanza, filelog, journald, windowseventlog`: Fix issue where specifying a non-existent storage extension caused panic during shutdown. (#16212) +- `pkg/stanza, filelog, journald, windowseventlog`: Fix issue where specifying a nonexistent storage extension caused panic during shutdown. (#16212) - `pkg/stanza`: Fix severity range unmarshaling (#16339) - `splunkhecexporter`: Do not log a warning on mapping empty metrics. (#3549) - `exporter/datadog`: Fixes bug to append tags in attributes instead of replacing them, simplifies filelog receiver setup, and adds `otel_source` tag. (#15387) @@ -4634,7 +4634,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - Add mysql.client.network.io metric (#14744) - `elasticsearchreceiver`: Add metrics related to GET operations (#14635) -- `elastisearchreceiver`: Add new metrics related to segments, aggregated by all shards (#14635) +- `elasticsearchreceiver`: Add new metrics related to segments, aggregated by all shards (#14635) - `elasticsearchreceiver`: add store size metric for index level (#14635) - `elasticsearchreceiver`: Add metrics related to merge operations with aggregated for all shards (#14635) - `elasticsearchreceiver`: add translog metrics on index level (#14635) @@ -4711,10 +4711,10 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) ### 🚀 New components 🚀 -- `azureblobreciver`: Add a new component `azureblobreciver` (#8834) +- `azureblobreceiver`: Add a new component `azureblobreceiver` (#8834) Add a new component `azureblobreceiver` that reads logs and traces from Azure Blob Storage - `k8sobjectsreceiver`: Add a new k8sobjects receiver to collect(pull/watch) Kubernetes Objects (#14185) -- `httpcheckreceiver`: New HTTP Check receiver allows users to run synthethic checks from the collector (#10607) +- `httpcheckreceiver`: New HTTP Check receiver allows users to run synthetic checks from the collector (#10607) ### 💡 Enhancements 💡 @@ -4808,7 +4808,7 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `pkg/stanza`: `readerFactory` and `Reader` use `helper.Encoding` directly, no longer depends on `helper.Splitter` (#14593) - `pkg/stanza`: `readerFactory` and `Reader` use `bufio.SplitFunc` directly, no longer depends on `helper.Splitter` (#14766) - `pkg/stanza`: add splitter factory which return a split func (#14766) -- `exporter/awsxrayexporter`: Change the value of xraysegment.url from `dbConnectionString` to the span name. This makes the XRay segment timeline more informationally useful. (#14342) +- `exporter/awsxrayexporter`: Change the value of xraysegment.url from `dbConnectionString` to the span name. This makes the XRay segment timeline more useful. (#14342) This change contravenes the AWS documentation for what values should go into this segment field. - `pkg/translator/zipkin`: Change zipkin V1 conversion to use pdata. (#14592) - `filterexpr`: Prevent the matcher from panicking (#13573) @@ -4979,9 +4979,9 @@ This release fixes CVE-2024-42368 on the `bearerauthtokenextension` (#34516) - `chronyreceiver`: When trying to read from socket, the socket type was incorrect (#13862) - `postgresqlreceiver`: Uses the postgres databasename when retrieving database inventory information (#13641) - `prometheusreceiver/prometheusremotewriteexporter`: Leave the sum unset in histograms without sums, and don't produce _sum metric points for histograms without sums (#7546) -- `processor/redaction`: Update redaction attributes in case if data sent through the processor more than once, not not ignore them. (#13854) +- `processor/redaction`: Update redaction attributes in case if data sent through the processor more than once, not ignore them. (#13854) - `chloggen`: changelog generation tool moved to https://github.com/open-telemetry/opentelemetry-go-build-tools (#14022) -- `exporter/AlibabaCloudLogServiceExporter`: Fix issue that promethus occuring error when the resource metric labels contains dot (#3429) +- `exporter/AlibabaCloudLogServiceExporter`: Fix issue that prometheus occurring error when the resource metric labels contains dot (#3429) - `exporter/tanzuobservabilityexporter`: This change causes tanzuobservabilityexporter to depend on 0.10.4 of the wavefront-sdk-go library. (#13417) @@ -5052,7 +5052,7 @@ in outgoing logs data. This allows users to disable particular metrics in through user settings. - `mongodbatlasreceiver`: Add logs retrieval capability (#12347) - `mongodbreceiver`: Enhance partial error handling with descriptive metric/attribute messages. (#13367) -- `filelog, journald, syslog, tcplog, udplog, windowseventlog receivers`: Add ability to set log body when when parsing. (#10274) +- `filelog, journald, syslog, tcplog, udplog, windowseventlog receivers`: Add ability to set log body when parsing. (#10274) - `filelog, journald, syslog, tcplog, udplog, windowseventlog receivers`: Enhance error message when csv_parser finds unexpected number of fields (#13427) - `filelog, journald, syslog, tcplog, udplog, windowseventlog receivers`: Enable debugging operators `stdout` and `file_output` (#13394) - `postgresqlreceiver`: Adds background writer metrics (#13327) @@ -5130,7 +5130,7 @@ in outgoing logs data. - `dotnetdiagnosticsreceiver`: Change status to unmaintained (#12757) ### 🚀 New components 🚀 -- `azureblobreceiver`: Add a new component `azureblobreciver` (#8834) +- `azureblobreceiver`: Add a new component `azureblobreceiver` (#8834) Add a new component `azureblobreceiver` that reads logs and traces from Azure Blob Storage - `azureeventhubreceiver`: New component to receive logs from Azure Event Hubs (#12786) - `headers_setter`: Add support for setting exporter headers from the upstream requests context (#5733, #7945, #4814) @@ -5286,7 +5286,7 @@ This version has been skipped. - `vcenter.host.disk.latency.avg.read` - `vcenter.host.disk.latency.avg.write` - `vcenter.host.network.throughput` will become: - - `vcenter.host.network.throughputt.receive` + - `vcenter.host.network.throughput.receive` - `vcenter.host.network.throughput.transmit` - `vcenter.host.network.packet.errors` will become: - `vcenter.host.network.packet.errors.receive` @@ -5337,7 +5337,7 @@ This version has been skipped. - `dockerobserver`: incorporate observer.EndpointsWatcher in preparation of multiple event subscribers and use existing internal event loop watcher (#10830, #11541) - `hostmetrics`: Adding connection tracking count and max metrics for linux (#11769) - `hostmetricsreceiver`: New config setting scrape_process_delay is used to indicate the minimum amount of time a process must be running before process metrics can be scraped for it. The default value is 0 seconds ("0s"). (#8976) -- `transformprocessor`: Add ability to interact with enums via sybmols. (#10349) +- `transformprocessor`: Add ability to interact with enums via symbols. (#10349) - `telemetryquerylanguage`: Adds to the grammar the ability to interpret Enums. (#11751) - `transformprocessor`: Update the transform processor to use the Telemetry Query Language package (#11751) - `transformprocessor`: Add `delete_key` and `delete_matching_keys` which allow deleting keys from maps. (#11823) @@ -5360,7 +5360,7 @@ This version has been skipped. - `cmd/chloggen`: Compare changelog to common ancestor with main (#12149) - `logstransformprocessor`: Remove support for storage (#12424) - `elasticsearchreceiver`: Remove unused `disk_usage_state` attribute from documentation (#12429) -- `filterlog`: change OR to AND logic for filtering logs - as desribed, and as is done for span filtering (#11439) +- `filterlog`: change OR to AND logic for filtering logs - as described, and as is done for span filtering (#11439) - `lokiexporter`: Wrap quotes around log message (#11827) - `tooling`: Fix a bug in the makefile causing `make rpm-package` to fail (#12162) - `prometheusexporter`: Fix cumulative condition for the delta-to-cumulative (#4153) @@ -5371,7 +5371,7 @@ This version has been skipped. - `datadogexporter`: Fix logs related to the source provider. (#12160) - `pkg/stanza/fileconsumer`: Fix issue where reader could become stuck on newlines (#10125, #10127, #10128) - `receivercreator`: dynamically created receivers log with their `name` field (#16481) -- `skywalkingreceiver`: Fix skywalking traceid and spanid convertion (#11562) +- `skywalkingreceiver`: Fix skywalking traceid and spanid conversion (#11562) - `spanmetricsprocessor`: Fixes the number of explicit bucket counts by removing the manually added "catch-all" bucket. (#11784) - `spanmetricsprocessor`: Fix concurrency bug causing premature key eviction. (#9018) - `spanmetricsprocessor`: Removes a comment that is no longer relevant due to a fix. (#12427) @@ -5508,7 +5508,7 @@ This version has been skipped. ### 🧰 Bug fixes 🧰 -- `kubletetstatsreceiver`: Bring back `k8s.container.name` attribute (#10848) +- `kubeletstatsreceiver`: Bring back `k8s.container.name` attribute (#10848) - `transformprocessor`: Fix issue where some metric fields were not working correctly in conditions. (#10473) - `transformprocessor`: Fix issue where some trace fields were not working correctly in conditions. (#10471) - `transformprocessor`: Fix issue where some log fields were not working correctly in conditions. (#10903) @@ -5533,7 +5533,7 @@ This version has been skipped. - `resourcedetectionprocessor`: 'gke' and 'gce' resource detectors are replaced with a single 'gcp' detector (#10347) - `pkg/stanza`: Removed reference to deprecated `ClusterName` (#10426) - `couchbasereceiver`: Fully removed unimplemented Couchbase receiver (#10482) -- `hostmetricsreciever`: Fix Load Scraper to normalize 1m, 5m, and 15m averages independently (#8267) +- `hostmetricsreceiver`: Fix Load Scraper to normalize 1m, 5m, and 15m averages independently (#8267) ### 🚀 New components 🚀 @@ -5548,7 +5548,7 @@ This version has been skipped. - `awsemfexporter`: Add min and max support for histograms (#10577) - `tailsamplingprocessor`: Add support for string invert matching to `and` policy (#9553) -- `mezemoexporter`: Add user agent string to outgoing HTTP requests (#10470) +- `mezmoexporter`: Add user agent string to outgoing HTTP requests (#10470) - `prometheusreceiver`: Improve performance of metrics builder (#10546) - `transformprocessor`: Add functions for conversion of scalar metric types (`gauge_to_sum` and `sum_to_gauge`) (#10255) - `dynatraceexporter`: Use min and max when provided in a data point for histograms (#10815) @@ -5712,7 +5712,7 @@ This version has been skipped. - `fluentforwardreceiver`: Release port on shutdown (#9111) - `prometheusexporter`: Prometheus fails to generate logs when prometheus exporter produced a check exception occurs. (#8949) - `resourcedetectionprocessor`: Wire docker detector (#9372) -- `kafkametricsreceiver`: The kafkametricsreceiver was changed to connect to kafka during scrape, rather than startup. If kafka is unavailable the receiver will attempt to connect during subsequent scrapes until succcessful (#8817). +- `kafkametricsreceiver`: The kafkametricsreceiver was changed to connect to kafka during scrape, rather than startup. If kafka is unavailable the receiver will attempt to connect during subsequent scrapes until successful (#8817). - `datadogexporter`: Update Kubernetes example manifest to new executable name. (#9425). - `riakreceiver`: Fix issue where user configured metric settings were ignored. (#9561) - `sqlserverreceiver`: Update `sqlserver.transaction_log.growth.count` and `sqlserver.transaction_log.shrink.count` to be monotonic sums. (#9522) @@ -5780,7 +5780,7 @@ https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/9278. - `filestorageextension`: use correct bbolt options for compaction (#9134) - `hostmetricsreceiver`: Use cpu times for time delta in cpu.utilization calculation (#8857) - `dynatraceexporter`: Remove overly verbose stacktrace from certain logs (#8989) -- `googlecloudexporter`: fix the `exporter.googlecloud.OTLPDirect` fature-gate, which was not applied when the flag was provided (#9116) +- `googlecloudexporter`: fix the `exporter.googlecloud.OTLPDirect` feature-gate, which was not applied when the flag was provided (#9116) - `signalfxexporter`: Fix bug to enable timeouts for correlating traces and metrics (#9101) - `windowsperfcountersreceiver`: fix exported values being integers instead of doubles (#9138) - `prometheusreceiver`: Fix issues with relabelling the `job` and `instance` labels. (#8780) @@ -5895,7 +5895,7 @@ https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/9278. - `honeycombexporter`: Add validation for `sending_queue` setting (#8113) - `routingprocessor`: Expand error handling on failure to build exporters (#8125) - `skywalkingreceiver`: Add new skywalking receiver component folder and structure (#8107) -- `groupbyattrsprocesor`: Allow empty keys, which allows to use the processor for compaction (#7793) +- `groupbyattrsprocessor`: Allow empty keys, which allows to use the processor for compaction (#7793) - `datadogexporter`: Add rbac to example k8s manifest file (#8186) - `splunkhecexporter`: Add validation for `sending_queue` setting (#8256) @@ -6052,7 +6052,7 @@ https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/9278. - `elasticsearchreceiver`: Use same metrics as JMX receiver for JVM metrics (#7160) - `elasticsearchreceiver`: Implement scraping logic (#7174) - `datadogexporter`: Add http.status_code tag to trace stats (#6889) -- `datadogexporter`: Add configuration option to use OTel span name into the Datatog resource name (#6611) +- `datadogexporter`: Add configuration option to use OTel span name into the Datadog resource name (#6611) - `mongodbreceiver`: Add initial client code to the component (#7125) - `tanzuobservabilityexporter`: Support delta histograms (#6897) - `awscloudwatchlogsexporter`: Use cwlogs package to export logs (#7152) @@ -6638,7 +6638,7 @@ The OpenTelemetry Collector Contrib contains everything in the [opentelemetry-co ### 💡 Enhancements 💡 -- Enabled Dependabot for Github Actions (#3543) +- Enabled Dependabot for GitHub Actions (#3543) - Change obsreport helpers for receivers to use the new pattern created in Collector (#3439,#3443,#3449,#3504,#3521,#3548) - `datadog` exporter: - Add logging for unknown or unsupported metric types (#3421) @@ -7139,7 +7139,7 @@ The OpenTelemetry Collector Contrib contains everything in the [opentelemetry-co ### 🚀 New components 🚀 - `zookeeper` receiver: Collects metrics from a Zookeeper instance using the `mntr` command -- `loadbalacing` exporter: Consistently exports spans belonging to the same trace to the same backend +- `loadbalancing` exporter: Consistently exports spans belonging to the same trace to the same backend - `windowsperfcounters` receiver: Captures the configured system, application, or custom performance counter data from the Windows registry using the PDH interface - `awsprometheusremotewrite` exporter: Sends metrics data in Prometheus TimeSeries format to a Prometheus Remote Write Backend and signs each outgoing HTTP request following the AWS Signature Version 4 signing process @@ -7205,7 +7205,7 @@ The OpenTelemetry Collector Contrib contains everything in the [opentelemetry-co - Add EC2 hostname attribute (#1324) - Add ECS Resource detector (#1360) - `sapm` exporter: Add queue settings (#1390) -- `metrictransform` processor: Add metric filter option (#1447) +- `metricstransform` processor: Add metric filter option (#1447) - `awsxray` exporter: Improve ECS attribute and origin translation (#1428) - `resourcedetection` processor: Initial system detector (#1405) @@ -7419,7 +7419,7 @@ The OpenTelemetry Collector Contrib contains everything in the [opentelemetry-co - Add working-set and page-fault metrics (#666) - Add basic support for volume metrics (#667) - `stackdriver` trace exporter: Move to new interface and pdata (#486) -- `metricstranform` processor: Keep timeseries and points in order after aggregation (#663) +- `metricstransform` processor: Keep timeseries and points in order after aggregation (#663) - `k8scluster` receiver: Change `container.spec.name` label to `k8s.container.name` (#681) - Migrate receiver creator to internal data model (#701) - Add ec2 support to `resourcedetection` processor (#587) From 22c647a3ae134697d90b67c45879227ea54d63be Mon Sep 17 00:00:00 2001 From: Vishal Raj <vishal.raj@elastic.co> Date: Tue, 14 Jan 2025 08:47:06 +0000 Subject: [PATCH 13/13] =?UTF-8?q?[connector/signaltometrics]Add=20collecto?= =?UTF-8?q?r=20telemetry=20as=20resource=20attrib=E2=80=A6=20(#37117)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit <!--Ex. Fixing a bug - Describe the bug and how this fixes the issue. Ex. Adding a feature - Explain what this achieves.--> #### Description Update signal to metrics connector to add resource attribute from telemetry settings. More information can be perused here: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/connector/signaltometricsconnector/README.md#single-writer <!-- Issue number (e.g. #1234) or full URL to issue, if applicable. --> #### Link to tracking issue Related to https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/35930 <!--Describe what testing was performed and which tests were added.--> #### Testing Unit tests added <!--Describe the documentation added.--> #### Documentation Documentation is already added in README as part of the initial PR: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/connector/signaltometricsconnector/README.md#single-writer <!--Please delete paragraphs that you did not use before submitting.--> --- .chloggen/singlewriter-signaltometrics.yaml | 27 ++++++ .../signaltometricsconnector/connector.go | 11 ++- connector/signaltometricsconnector/factory.go | 15 ++- .../internal/model/collectorinstanceinfo.go | 75 +++++++++++++++ .../model/collectorinstanceinfo_test.go | 94 +++++++++++++++++++ .../internal/model/model.go | 6 +- .../logs/exponential_histograms/output.yaml | 18 ++++ .../testdata/logs/histograms/output.yaml | 18 ++++ .../testdata/logs/sum/output.yaml | 18 ++++ .../exponential_histograms/output.yaml | 9 ++ .../testdata/metrics/histograms/output.yaml | 9 ++ .../testdata/metrics/sum/output.yaml | 9 ++ .../traces/exponential_histograms/output.yaml | 18 ++++ .../testdata/traces/histograms/output.yaml | 18 ++++ .../testdata/traces/sum/output.yaml | 18 ++++ 15 files changed, 353 insertions(+), 10 deletions(-) create mode 100644 .chloggen/singlewriter-signaltometrics.yaml create mode 100644 connector/signaltometricsconnector/internal/model/collectorinstanceinfo.go create mode 100644 connector/signaltometricsconnector/internal/model/collectorinstanceinfo_test.go diff --git a/.chloggen/singlewriter-signaltometrics.yaml b/.chloggen/singlewriter-signaltometrics.yaml new file mode 100644 index 000000000000..008b2e384bf6 --- /dev/null +++ b/.chloggen/singlewriter-signaltometrics.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: signaltometrics + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Adds resource attributes based on telemetry settings to the connector to ensure single writer + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [35930] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [user] diff --git a/connector/signaltometricsconnector/connector.go b/connector/signaltometricsconnector/connector.go index b1a9234fe2f5..2253faf6e4e6 100644 --- a/connector/signaltometricsconnector/connector.go +++ b/connector/signaltometricsconnector/connector.go @@ -23,8 +23,9 @@ import ( ) type signalToMetrics struct { - next consumer.Metrics - logger *zap.Logger + next consumer.Metrics + collectorInstanceInfo *model.CollectorInstanceInfo + logger *zap.Logger spanMetricDefs []model.MetricDef[ottlspan.TransformContext] dpMetricDefs []model.MetricDef[ottldatapoint.TransformContext] @@ -75,7 +76,7 @@ func (sm *signalToMetrics) ConsumeTraces(ctx context.Context, td ptrace.Traces) } } - filteredResAttrs := md.FilterResourceAttributes(resourceAttrs) + filteredResAttrs := md.FilterResourceAttributes(resourceAttrs, sm.collectorInstanceInfo) if err := aggregator.Aggregate(ctx, tCtx, md, filteredResAttrs, filteredSpanAttrs, 1); err != nil { return err } @@ -104,7 +105,7 @@ func (sm *signalToMetrics) ConsumeMetrics(ctx context.Context, m pmetric.Metrics metrics := scopeMetric.Metrics() metric := metrics.At(k) for _, md := range sm.dpMetricDefs { - filteredResAttrs := md.FilterResourceAttributes(resourceAttrs) + filteredResAttrs := md.FilterResourceAttributes(resourceAttrs, sm.collectorInstanceInfo) aggregate := func(dp any, dpAttrs pcommon.Map) error { // The transform context is created from original attributes so that the // OTTL expressions are also applied on the original attributes. @@ -230,7 +231,7 @@ func (sm *signalToMetrics) ConsumeLogs(ctx context.Context, logs plog.Logs) erro continue } } - filteredResAttrs := md.FilterResourceAttributes(resourceAttrs) + filteredResAttrs := md.FilterResourceAttributes(resourceAttrs, sm.collectorInstanceInfo) if err := aggregator.Aggregate(ctx, tCtx, md, filteredResAttrs, filteredLogAttrs, 1); err != nil { return err } diff --git a/connector/signaltometricsconnector/factory.go b/connector/signaltometricsconnector/factory.go index 673722dd6ce0..04a3038448c3 100644 --- a/connector/signaltometricsconnector/factory.go +++ b/connector/signaltometricsconnector/factory.go @@ -57,7 +57,10 @@ func createTracesToMetrics( } return &signalToMetrics{ - logger: set.Logger, + logger: set.Logger, + collectorInstanceInfo: model.NewCollectorInstanceInfo( + set.TelemetrySettings, + ), next: nextConsumer, spanMetricDefs: metricDefs, }, nil @@ -85,7 +88,10 @@ func createMetricsToMetrics( } return &signalToMetrics{ - logger: set.Logger, + logger: set.Logger, + collectorInstanceInfo: model.NewCollectorInstanceInfo( + set.TelemetrySettings, + ), next: nextConsumer, dpMetricDefs: metricDefs, }, nil @@ -113,7 +119,10 @@ func createLogsToMetrics( } return &signalToMetrics{ - logger: set.Logger, + logger: set.Logger, + collectorInstanceInfo: model.NewCollectorInstanceInfo( + set.TelemetrySettings, + ), next: nextConsumer, logMetricDefs: metricDefs, }, nil diff --git a/connector/signaltometricsconnector/internal/model/collectorinstanceinfo.go b/connector/signaltometricsconnector/internal/model/collectorinstanceinfo.go new file mode 100644 index 000000000000..f6742fc7271c --- /dev/null +++ b/connector/signaltometricsconnector/internal/model/collectorinstanceinfo.go @@ -0,0 +1,75 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package model // import "github.com/open-telemetry/opentelemetry-collector-contrib/connector/signaltometricsconnector/internal/model" + +import ( + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/pdata/pcommon" + semconv "go.opentelemetry.io/collector/semconv/v1.26.0" + + "github.com/open-telemetry/opentelemetry-collector-contrib/connector/signaltometricsconnector/internal/metadata" +) + +var prefix = metadata.Type.String() + +// CollectorInstanceInfo holds the attributes that could uniquely identify +// the current collector instance. These attributes are initialized from the +// telemetry settings. The CollectorInstanceInfo can copy these attributes, +// with a given prefix, to a provided map. +type CollectorInstanceInfo struct { + size int + serviceInstanceID string + serviceName string + serviceNamespace string +} + +func NewCollectorInstanceInfo( + set component.TelemetrySettings, +) *CollectorInstanceInfo { + var info CollectorInstanceInfo + set.Resource.Attributes().Range(func(k string, v pcommon.Value) bool { + switch k { + case semconv.AttributeServiceInstanceID: + if str := v.Str(); str != "" { + info.serviceInstanceID = str + info.size++ + } + case semconv.AttributeServiceName: + if str := v.Str(); str != "" { + info.serviceName = str + info.size++ + } + case semconv.AttributeServiceNamespace: + if str := v.Str(); str != "" { + info.serviceNamespace = str + info.size++ + } + } + return true + }) + return &info +} + +// Size returns the max number of attributes that defines a collector's +// instance information. Can be used to presize the attributes. +func (info CollectorInstanceInfo) Size() int { + return info.size +} + +func (info CollectorInstanceInfo) Copy(to pcommon.Map) { + to.EnsureCapacity(info.Size()) + if info.serviceInstanceID != "" { + to.PutStr(keyWithPrefix(semconv.AttributeServiceInstanceID), info.serviceInstanceID) + } + if info.serviceName != "" { + to.PutStr(keyWithPrefix(semconv.AttributeServiceName), info.serviceName) + } + if info.serviceNamespace != "" { + to.PutStr(keyWithPrefix(semconv.AttributeServiceNamespace), info.serviceNamespace) + } +} + +func keyWithPrefix(key string) string { + return prefix + "." + key +} diff --git a/connector/signaltometricsconnector/internal/model/collectorinstanceinfo_test.go b/connector/signaltometricsconnector/internal/model/collectorinstanceinfo_test.go new file mode 100644 index 000000000000..c36f6c3d8f7c --- /dev/null +++ b/connector/signaltometricsconnector/internal/model/collectorinstanceinfo_test.go @@ -0,0 +1,94 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package model + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/component/componenttest" + "go.opentelemetry.io/collector/pdata/pcommon" + semconv "go.opentelemetry.io/collector/semconv/v1.26.0" +) + +func TestCollectorInstanceInfo(t *testing.T) { + for _, tc := range []struct { + name string + input component.TelemetrySettings + expected pcommon.Map + }{ + { + name: "empty", + input: componenttest.NewNopTelemetrySettings(), + expected: pcommon.NewMap(), + }, + { + name: "with_service_instance_id", + input: func() component.TelemetrySettings { + ts := componenttest.NewNopTelemetrySettings() + ts.Resource.Attributes().PutStr(semconv.AttributeServiceInstanceID, "627cc493-f310-47de-96bd-71410b7dec09") + return ts + }(), + expected: func() pcommon.Map { + m := pcommon.NewMap() + m.PutStr( + "signaltometrics."+semconv.AttributeServiceInstanceID, + "627cc493-f310-47de-96bd-71410b7dec09", + ) + return m + }(), + }, + { + name: "with_all_values", + input: func() component.TelemetrySettings { + ts := componenttest.NewNopTelemetrySettings() + ts.Resource.Attributes().PutStr(semconv.AttributeServiceInstanceID, "627cc493-f310-47de-96bd-71410b7dec09") + ts.Resource.Attributes().PutStr(semconv.AttributeServiceName, "signaltometrics") + ts.Resource.Attributes().PutStr(semconv.AttributeServiceNamespace, "test") + return ts + }(), + expected: func() pcommon.Map { + m := pcommon.NewMap() + m.PutStr( + "signaltometrics."+semconv.AttributeServiceInstanceID, + "627cc493-f310-47de-96bd-71410b7dec09", + ) + m.PutStr( + "signaltometrics."+semconv.AttributeServiceName, + "signaltometrics", + ) + m.PutStr( + "signaltometrics."+semconv.AttributeServiceNamespace, + "test", + ) + return m + }(), + }, + } { + t.Run(tc.name, func(t *testing.T) { + ci := NewCollectorInstanceInfo(tc.input) + require.NotNil(t, ci) + + actual := pcommon.NewMap() + ci.Copy(actual) + assert.Equal(t, ci.Size(), actual.Len()) + assertMapEquality(t, tc.expected, actual) + }) + } +} + +func assertMapEquality(t *testing.T, expected, actual pcommon.Map) bool { + t.Helper() + + expectedRaw := expected.AsRaw() + actualRaw := actual.AsRaw() + return assert.True( + t, reflect.DeepEqual(expectedRaw, actualRaw), + "attributes don't match expected: %v, actual: %v", + expectedRaw, actualRaw, + ) +} diff --git a/connector/signaltometricsconnector/internal/model/model.go b/connector/signaltometricsconnector/internal/model/model.go index 16d05b129eb5..05ebdabf47f1 100644 --- a/connector/signaltometricsconnector/internal/model/model.go +++ b/connector/signaltometricsconnector/internal/model/model.go @@ -172,17 +172,19 @@ func (md *MetricDef[K]) FromMetricInfo( // definition. func (md *MetricDef[K]) FilterResourceAttributes( attrs pcommon.Map, + collectorInfo *CollectorInstanceInfo, ) pcommon.Map { var filteredAttributes pcommon.Map switch { case len(md.IncludeResourceAttributes) == 0: filteredAttributes = pcommon.NewMap() - filteredAttributes.EnsureCapacity(attrs.Len()) + filteredAttributes.EnsureCapacity(attrs.Len() + collectorInfo.Size()) attrs.CopyTo(filteredAttributes) default: - expectedLen := len(md.IncludeResourceAttributes) + expectedLen := len(md.IncludeResourceAttributes) + collectorInfo.Size() filteredAttributes = filterAttributes(attrs, md.IncludeResourceAttributes, expectedLen) } + collectorInfo.Copy(filteredAttributes) return filteredAttributes } diff --git a/connector/signaltometricsconnector/testdata/logs/exponential_histograms/output.yaml b/connector/signaltometricsconnector/testdata/logs/exponential_histograms/output.yaml index c0e6e450b560..732e026b85e3 100644 --- a/connector/signaltometricsconnector/testdata/logs/exponential_histograms/output.yaml +++ b/connector/signaltometricsconnector/testdata/logs/exponential_histograms/output.yaml @@ -7,6 +7,15 @@ resourceMetrics: - key: resource.foo value: stringValue: foo + - key: signaltometrics.service.instance.id + value: + stringValue: 627cc493-f310-47de-96bd-71410b7dec09 + - key: signaltometrics.service.name + value: + stringValue: signaltometrics + - key: signaltometrics.service.namespace + value: + stringValue: test scopeMetrics: - metrics: - description: Logrecords as exponential histogram with log.duration from attributes @@ -327,6 +336,15 @@ resourceMetrics: - key: resource.foo value: stringValue: foo + - key: signaltometrics.service.instance.id + value: + stringValue: 627cc493-f310-47de-96bd-71410b7dec09 + - key: signaltometrics.service.name + value: + stringValue: signaltometrics + - key: signaltometrics.service.namespace + value: + stringValue: test scopeMetrics: - metrics: - description: Logrecords with resource attribute foo as exponential histogram with log.duration from attributes diff --git a/connector/signaltometricsconnector/testdata/logs/histograms/output.yaml b/connector/signaltometricsconnector/testdata/logs/histograms/output.yaml index 45d0973f3eed..013c4ac1e8d0 100644 --- a/connector/signaltometricsconnector/testdata/logs/histograms/output.yaml +++ b/connector/signaltometricsconnector/testdata/logs/histograms/output.yaml @@ -7,6 +7,15 @@ resourceMetrics: - key: resource.foo value: stringValue: foo + - key: signaltometrics.service.instance.id + value: + stringValue: 627cc493-f310-47de-96bd-71410b7dec09 + - key: signaltometrics.service.name + value: + stringValue: signaltometrics + - key: signaltometrics.service.namespace + value: + stringValue: test scopeMetrics: - metrics: - description: Logrecords as histogram with log.duration from attributes @@ -127,6 +136,15 @@ resourceMetrics: - key: resource.foo value: stringValue: foo + - key: signaltometrics.service.instance.id + value: + stringValue: 627cc493-f310-47de-96bd-71410b7dec09 + - key: signaltometrics.service.name + value: + stringValue: signaltometrics + - key: signaltometrics.service.namespace + value: + stringValue: test scopeMetrics: - metrics: - description: Logrecords with resource attribute foo as histogram with log.duration from attributes diff --git a/connector/signaltometricsconnector/testdata/logs/sum/output.yaml b/connector/signaltometricsconnector/testdata/logs/sum/output.yaml index f575a5231e79..3fc35b338c63 100644 --- a/connector/signaltometricsconnector/testdata/logs/sum/output.yaml +++ b/connector/signaltometricsconnector/testdata/logs/sum/output.yaml @@ -7,6 +7,15 @@ resourceMetrics: - key: resource.foo value: stringValue: foo + - key: signaltometrics.service.instance.id + value: + stringValue: 627cc493-f310-47de-96bd-71410b7dec09 + - key: signaltometrics.service.name + value: + stringValue: signaltometrics + - key: signaltometrics.service.namespace + value: + stringValue: test scopeMetrics: - metrics: - description: Count total number of log records @@ -57,6 +66,15 @@ resourceMetrics: - key: resource.foo value: stringValue: foo + - key: signaltometrics.service.instance.id + value: + stringValue: 627cc493-f310-47de-96bd-71410b7dec09 + - key: signaltometrics.service.name + value: + stringValue: signaltometrics + - key: signaltometrics.service.namespace + value: + stringValue: test scopeMetrics: - metrics: - description: Count total number of log records with resource attribute foo diff --git a/connector/signaltometricsconnector/testdata/metrics/exponential_histograms/output.yaml b/connector/signaltometricsconnector/testdata/metrics/exponential_histograms/output.yaml index 42b210383c32..68ed74214770 100644 --- a/connector/signaltometricsconnector/testdata/metrics/exponential_histograms/output.yaml +++ b/connector/signaltometricsconnector/testdata/metrics/exponential_histograms/output.yaml @@ -4,6 +4,15 @@ resourceMetrics: - key: resource.foo value: stringValue: foo + - key: signaltometrics.service.instance.id + value: + stringValue: 627cc493-f310-47de-96bd-71410b7dec09 + - key: signaltometrics.service.name + value: + stringValue: signaltometrics + - key: signaltometrics.service.namespace + value: + stringValue: test scopeMetrics: - metrics: - description: An exponential histogram created from gague values diff --git a/connector/signaltometricsconnector/testdata/metrics/histograms/output.yaml b/connector/signaltometricsconnector/testdata/metrics/histograms/output.yaml index 2cda97a02d15..fd42993a5cde 100644 --- a/connector/signaltometricsconnector/testdata/metrics/histograms/output.yaml +++ b/connector/signaltometricsconnector/testdata/metrics/histograms/output.yaml @@ -4,6 +4,15 @@ resourceMetrics: - key: resource.foo value: stringValue: foo + - key: signaltometrics.service.instance.id + value: + stringValue: 627cc493-f310-47de-96bd-71410b7dec09 + - key: signaltometrics.service.name + value: + stringValue: signaltometrics + - key: signaltometrics.service.namespace + value: + stringValue: test scopeMetrics: - metrics: - description: A histogram created from gague values diff --git a/connector/signaltometricsconnector/testdata/metrics/sum/output.yaml b/connector/signaltometricsconnector/testdata/metrics/sum/output.yaml index ba8e962693e5..899ed0f6dcef 100644 --- a/connector/signaltometricsconnector/testdata/metrics/sum/output.yaml +++ b/connector/signaltometricsconnector/testdata/metrics/sum/output.yaml @@ -7,6 +7,15 @@ resourceMetrics: - key: resource.foo value: stringValue: foo + - key: signaltometrics.service.instance.id + value: + stringValue: 627cc493-f310-47de-96bd-71410b7dec09 + - key: signaltometrics.service.name + value: + stringValue: signaltometrics + - key: signaltometrics.service.namespace + value: + stringValue: test scopeMetrics: - metrics: - description: Count total number of datapoints diff --git a/connector/signaltometricsconnector/testdata/traces/exponential_histograms/output.yaml b/connector/signaltometricsconnector/testdata/traces/exponential_histograms/output.yaml index 7904d70e5f7c..fecd340313c6 100644 --- a/connector/signaltometricsconnector/testdata/traces/exponential_histograms/output.yaml +++ b/connector/signaltometricsconnector/testdata/traces/exponential_histograms/output.yaml @@ -4,6 +4,15 @@ resourceMetrics: - key: resource.foo value: stringValue: foo + - key: signaltometrics.service.instance.id + value: + stringValue: 627cc493-f310-47de-96bd-71410b7dec09 + - key: signaltometrics.service.name + value: + stringValue: signaltometrics + - key: signaltometrics.service.namespace + value: + stringValue: test scopeMetrics: - metrics: - description: Spans with resource attribute including resource.foo as a exponential histogram metric @@ -138,6 +147,15 @@ resourceMetrics: - key: resource.foo value: stringValue: foo + - key: signaltometrics.service.instance.id + value: + stringValue: 627cc493-f310-47de-96bd-71410b7dec09 + - key: signaltometrics.service.name + value: + stringValue: signaltometrics + - key: signaltometrics.service.namespace + value: + stringValue: test scopeMetrics: - metrics: - description: Spans with custom count OTTL expression as a exponential histogram metric diff --git a/connector/signaltometricsconnector/testdata/traces/histograms/output.yaml b/connector/signaltometricsconnector/testdata/traces/histograms/output.yaml index a25d144566c1..f4066729d9d8 100644 --- a/connector/signaltometricsconnector/testdata/traces/histograms/output.yaml +++ b/connector/signaltometricsconnector/testdata/traces/histograms/output.yaml @@ -4,6 +4,15 @@ resourceMetrics: - key: resource.foo value: stringValue: foo + - key: signaltometrics.service.instance.id + value: + stringValue: 627cc493-f310-47de-96bd-71410b7dec09 + - key: signaltometrics.service.name + value: + stringValue: signaltometrics + - key: signaltometrics.service.namespace + value: + stringValue: test scopeMetrics: - metrics: - description: Spans with resource attribute including resource.foo as a histogram metric @@ -60,6 +69,15 @@ resourceMetrics: - key: resource.foo value: stringValue: foo + - key: signaltometrics.service.instance.id + value: + stringValue: 627cc493-f310-47de-96bd-71410b7dec09 + - key: signaltometrics.service.name + value: + stringValue: signaltometrics + - key: signaltometrics.service.namespace + value: + stringValue: test scopeMetrics: - metrics: - description: Spans with custom count OTTL expression as a histogram metric diff --git a/connector/signaltometricsconnector/testdata/traces/sum/output.yaml b/connector/signaltometricsconnector/testdata/traces/sum/output.yaml index 5beb92e861f0..07acd1697635 100644 --- a/connector/signaltometricsconnector/testdata/traces/sum/output.yaml +++ b/connector/signaltometricsconnector/testdata/traces/sum/output.yaml @@ -4,6 +4,15 @@ resourceMetrics: - key: resource.foo value: stringValue: foo + - key: signaltometrics.service.instance.id + value: + stringValue: 627cc493-f310-47de-96bd-71410b7dec09 + - key: signaltometrics.service.name + value: + stringValue: signaltometrics + - key: signaltometrics.service.namespace + value: + stringValue: test scopeMetrics: - metrics: - description: Spans with resource attribute including resource.foo as a int sum metric @@ -24,6 +33,15 @@ resourceMetrics: - key: resource.foo value: stringValue: foo + - key: signaltometrics.service.instance.id + value: + stringValue: 627cc493-f310-47de-96bd-71410b7dec09 + - key: signaltometrics.service.name + value: + stringValue: signaltometrics + - key: signaltometrics.service.namespace + value: + stringValue: test scopeMetrics: - metrics: - description: Adjusted count for the span as a sum metric