From ff2073bea359b1803eef26c5951a5aa2a3e874c3 Mon Sep 17 00:00:00 2001 From: Arthur Silva Sens Date: Wed, 4 Mar 2026 15:23:35 -0300 Subject: [PATCH 1/6] Implement prometheus-collector-bridge Signed-off-by: Arthur Silva Sens --- otelcollector/config.go | 160 +++++++++++++++++++++++++ otelcollector/config_test.go | 154 ++++++++++++++++++++++++ otelcollector/factory.go | 27 +++++ otelcollector/factory_test.go | 43 +++++++ otelcollector/go.mod | 75 ++++++++++++ otelcollector/go.sum | 191 ++++++++++++++++++++++++++++++ otelcollector/lifecycle.go | 199 ++++++++++++++++++++++++++++++++ otelcollector/lifecycle_test.go | 131 +++++++++++++++++++++ 8 files changed, 980 insertions(+) create mode 100644 otelcollector/config.go create mode 100644 otelcollector/config_test.go create mode 100644 otelcollector/factory.go create mode 100644 otelcollector/factory_test.go create mode 100644 otelcollector/go.mod create mode 100644 otelcollector/go.sum create mode 100644 otelcollector/lifecycle.go create mode 100644 otelcollector/lifecycle_test.go diff --git a/otelcollector/config.go b/otelcollector/config.go new file mode 100644 index 00000000..8f6a34ae --- /dev/null +++ b/otelcollector/config.go @@ -0,0 +1,160 @@ +package otelcollector + +import ( + "fmt" + "net/http" + "time" + + prombridge "github.com/ArthurSens/prometheus-collector-bridge" +) + +// Config maps stackdriver_exporter runtime settings into exporter_config. +type Config struct { + ProjectIDs []string `mapstructure:"project_ids"` + ProjectsFilter string `mapstructure:"projects_filter"` + UniverseDomain string `mapstructure:"universe_domain"` + MaxRetries int `mapstructure:"max_retries"` + HTTPTimeout string `mapstructure:"http_timeout"` + MaxBackoff string `mapstructure:"max_backoff"` + BackoffJitter string `mapstructure:"backoff_jitter"` + RetryStatuses []int `mapstructure:"retry_statuses"` + MetricsPrefixes []string `mapstructure:"metrics_prefixes"` + MetricsInterval string `mapstructure:"metrics_interval"` + MetricsOffset string `mapstructure:"metrics_offset"` + MetricsIngest bool `mapstructure:"metrics_ingest_delay"` + FillMissing bool `mapstructure:"fill_missing_labels"` + DropDelegated bool `mapstructure:"drop_delegated_projects"` + Filters []string `mapstructure:"filters"` + AggregateDeltas bool `mapstructure:"aggregate_deltas"` + DeltasTTL string `mapstructure:"aggregate_deltas_ttl"` + DescriptorTTL string `mapstructure:"descriptor_cache_ttl"` + DescriptorGoogleOnly bool `mapstructure:"descriptor_cache_only_google"` +} + +var _ prombridge.Config = (*Config)(nil) + +func defaultConfig() *Config { + return &Config{ + UniverseDomain: "googleapis.com", + MaxRetries: 0, + HTTPTimeout: "10s", + MaxBackoff: "5s", + BackoffJitter: "1s", + RetryStatuses: []int{503}, + MetricsInterval: "5m", + MetricsOffset: "0s", + MetricsIngest: false, + FillMissing: true, + DropDelegated: false, + AggregateDeltas: false, + DeltasTTL: "30m", + DescriptorTTL: "0s", + DescriptorGoogleOnly: true, + } +} + +func defaultComponentDefaults() map[string]interface{} { + cfg := defaultConfig() + return map[string]interface{}{ + "max_retries": cfg.MaxRetries, + "http_timeout": cfg.HTTPTimeout, + "max_backoff": cfg.MaxBackoff, + "backoff_jitter": cfg.BackoffJitter, + "retry_statuses": cfg.RetryStatuses, + "universe_domain": cfg.UniverseDomain, + "metrics_interval": cfg.MetricsInterval, + "metrics_offset": cfg.MetricsOffset, + "metrics_ingest_delay": cfg.MetricsIngest, + "fill_missing_labels": cfg.FillMissing, + "drop_delegated_projects": cfg.DropDelegated, + "aggregate_deltas": cfg.AggregateDeltas, + "aggregate_deltas_ttl": cfg.DeltasTTL, + "descriptor_cache_ttl": cfg.DescriptorTTL, + "descriptor_cache_only_google": cfg.DescriptorGoogleOnly, + } +} + +func (c *Config) Validate() error { + if len(c.MetricsPrefixes) == 0 { + return fmt.Errorf("metrics_prefixes must have at least one entry") + } + + _, err := c.parsedDurations() + if err != nil { + return err + } + + for _, code := range c.RetryStatuses { + if code < http.StatusContinue || code > 599 { + return fmt.Errorf("retry status %d is not a valid HTTP status code", code) + } + } + + return nil +} + +type parsedConfig struct { + HTTPTimeout time.Duration + MaxBackoff time.Duration + BackoffJitter time.Duration + MetricsInterval time.Duration + MetricsOffset time.Duration + DeltasTTL time.Duration + DescriptorTTL time.Duration +} + +func (c *Config) parsedDurations() (parsedConfig, error) { + parse := func(name, raw string) (time.Duration, error) { + d, err := time.ParseDuration(raw) + if err != nil { + return 0, fmt.Errorf("%s: invalid duration %q: %w", name, raw, err) + } + return d, nil + } + + httpTimeout, err := parse("http_timeout", c.HTTPTimeout) + if err != nil { + return parsedConfig{}, err + } + maxBackoff, err := parse("max_backoff", c.MaxBackoff) + if err != nil { + return parsedConfig{}, err + } + backoffJitter, err := parse("backoff_jitter", c.BackoffJitter) + if err != nil { + return parsedConfig{}, err + } + metricsInterval, err := parse("metrics_interval", c.MetricsInterval) + if err != nil { + return parsedConfig{}, err + } + metricsOffset, err := parse("metrics_offset", c.MetricsOffset) + if err != nil { + return parsedConfig{}, err + } + deltasTTL, err := parse("aggregate_deltas_ttl", c.DeltasTTL) + if err != nil { + return parsedConfig{}, err + } + descriptorTTL, err := parse("descriptor_cache_ttl", c.DescriptorTTL) + if err != nil { + return parsedConfig{}, err + } + + return parsedConfig{ + HTTPTimeout: httpTimeout, + MaxBackoff: maxBackoff, + BackoffJitter: backoffJitter, + MetricsInterval: metricsInterval, + MetricsOffset: metricsOffset, + DeltasTTL: deltasTTL, + DescriptorTTL: descriptorTTL, + }, nil +} + +type configUnmarshaler struct{} + +func (configUnmarshaler) GetConfigStruct() prombridge.Config { + return defaultConfig() +} + diff --git a/otelcollector/config_test.go b/otelcollector/config_test.go new file mode 100644 index 00000000..61bcb0a6 --- /dev/null +++ b/otelcollector/config_test.go @@ -0,0 +1,154 @@ +package otelcollector + +import ( + "testing" + "time" +) + +func TestConfig_Validate(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + cfg Config + wantErr bool + }{ + { + name: "valid with project IDs", + cfg: Config{ + ProjectIDs: []string{"my-project"}, + MetricsPrefixes: []string{"compute.googleapis.com/instance"}, + HTTPTimeout: "10s", + MaxBackoff: "5s", + BackoffJitter: "1s", + MetricsInterval: "5m", + MetricsOffset: "0s", + DeltasTTL: "30m", + DescriptorTTL: "0s", + }, + }, + { + name: "valid with projects filter", + cfg: Config{ + ProjectsFilter: "parent.type:folder parent.id:123", + MetricsPrefixes: []string{"pubsub.googleapis.com/subscription"}, + HTTPTimeout: "10s", + MaxBackoff: "5s", + BackoffJitter: "1s", + MetricsInterval: "5m", + MetricsOffset: "0s", + DeltasTTL: "30m", + DescriptorTTL: "0s", + }, + }, + { + name: "valid with implicit default project discovery", + cfg: Config{ + MetricsPrefixes: []string{"compute.googleapis.com/instance"}, + HTTPTimeout: "10s", + MaxBackoff: "5s", + BackoffJitter: "1s", + MetricsInterval: "5m", + MetricsOffset: "0s", + DeltasTTL: "30m", + DescriptorTTL: "0s", + }, + }, + { + name: "invalid without metrics prefixes", + cfg: Config{ + ProjectIDs: []string{"my-project"}, + HTTPTimeout: "10s", + MaxBackoff: "5s", + BackoffJitter: "1s", + }, + wantErr: true, + }, + { + name: "invalid timeout", + cfg: Config{ + ProjectIDs: []string{"my-project"}, + MetricsPrefixes: []string{"compute.googleapis.com/instance"}, + HTTPTimeout: "not-a-duration", + MaxBackoff: "5s", + BackoffJitter: "1s", + MetricsInterval: "5m", + MetricsOffset: "0s", + DeltasTTL: "30m", + DescriptorTTL: "0s", + }, + wantErr: true, + }, + { + name: "invalid retry status", + cfg: Config{ + ProjectIDs: []string{"my-project"}, + MetricsPrefixes: []string{"compute.googleapis.com/instance"}, + HTTPTimeout: "10s", + MaxBackoff: "5s", + BackoffJitter: "1s", + MetricsInterval: "5m", + MetricsOffset: "0s", + DeltasTTL: "30m", + DescriptorTTL: "0s", + RetryStatuses: []int{99}, + }, + wantErr: true, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + err := tt.cfg.Validate() + if (err != nil) != tt.wantErr { + t.Fatalf("Validate() error = %v, wantErr = %v", err, tt.wantErr) + } + }) + } +} + +func TestConfig_Durations(t *testing.T) { + t.Parallel() + + cfg := Config{ + ProjectIDs: []string{"my-project"}, + MetricsPrefixes: []string{"compute.googleapis.com/instance"}, + HTTPTimeout: "10s", + MaxBackoff: "5s", + BackoffJitter: "1s", + MetricsInterval: "5m", + MetricsOffset: "2m", + DeltasTTL: "30m", + DescriptorTTL: "0s", + } + + parsed, err := cfg.parsedDurations() + if err != nil { + t.Fatalf("parsedDurations() error = %v", err) + } + + if parsed.HTTPTimeout != 10*time.Second { + t.Fatalf("HTTPTimeout = %v, want %v", parsed.HTTPTimeout, 10*time.Second) + } + if parsed.MaxBackoff != 5*time.Second { + t.Fatalf("MaxBackoff = %v, want %v", parsed.MaxBackoff, 5*time.Second) + } + if parsed.BackoffJitter != 1*time.Second { + t.Fatalf("BackoffJitter = %v, want %v", parsed.BackoffJitter, 1*time.Second) + } + if parsed.MetricsInterval != 5*time.Minute { + t.Fatalf("MetricsInterval = %v, want %v", parsed.MetricsInterval, 5*time.Minute) + } + if parsed.MetricsOffset != 2*time.Minute { + t.Fatalf("MetricsOffset = %v, want %v", parsed.MetricsOffset, 2*time.Minute) + } + if parsed.DeltasTTL != 30*time.Minute { + t.Fatalf("DeltasTTL = %v, want %v", parsed.DeltasTTL, 30*time.Minute) + } + if parsed.DescriptorTTL != 0 { + t.Fatalf("DescriptorTTL = %v, want %v", parsed.DescriptorTTL, 0*time.Second) + } +} + diff --git a/otelcollector/factory.go b/otelcollector/factory.go new file mode 100644 index 00000000..48a3573a --- /dev/null +++ b/otelcollector/factory.go @@ -0,0 +1,27 @@ +package otelcollector + +import ( + "log/slog" + + prombridge "github.com/ArthurSens/prometheus-collector-bridge" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/receiver" +) + +var receiverType = component.MustNewType("stackdriver_exporter") + +func NewFactory() receiver.Factory { + return prombridge.NewFactory( + receiverType, + newLifecycleManager(slog.Default()), + configUnmarshaler{}, + prombridge.WithComponentDefaults(defaultComponentDefaults()), + ) +} + +// Keep compiler checks close to factory wiring. +var ( + _ prombridge.ExporterLifecycleManager = (*lifecycleManager)(nil) + _ prombridge.ConfigUnmarshaler = (configUnmarshaler{}) +) + diff --git a/otelcollector/factory_test.go b/otelcollector/factory_test.go new file mode 100644 index 00000000..19233747 --- /dev/null +++ b/otelcollector/factory_test.go @@ -0,0 +1,43 @@ +package otelcollector + +import ( + "context" + "testing" + + prombridge "github.com/ArthurSens/prometheus-collector-bridge" + "go.opentelemetry.io/collector/consumer/consumertest" + "go.opentelemetry.io/collector/receiver/receivertest" +) + +func TestNewFactory(t *testing.T) { + t.Parallel() + + factory := NewFactory() + if factory == nil { + t.Fatal("NewFactory() returned nil") + } +} + +func TestFactory_CreateMetrics(t *testing.T) { + t.Parallel() + + factory := NewFactory() + cfg := factory.CreateDefaultConfig().(*prombridge.ReceiverConfig) + + cfg.ExporterConfig = map[string]interface{}{ + "project_ids": []string{"my-project"}, + "metrics_prefixes": []string{"compute.googleapis.com/instance"}, + } + + settings := receivertest.NewNopSettings(receiverType) + consumer := new(consumertest.MetricsSink) + + recv, err := factory.CreateMetrics(context.Background(), settings, cfg, consumer) + if err != nil { + t.Fatalf("CreateMetrics() error = %v", err) + } + if recv == nil { + t.Fatal("CreateMetrics() returned nil receiver") + } +} + diff --git a/otelcollector/go.mod b/otelcollector/go.mod new file mode 100644 index 00000000..f7f60f75 --- /dev/null +++ b/otelcollector/go.mod @@ -0,0 +1,75 @@ +module github.com/prometheus-community/stackdriver_exporter/otelcollector + +go 1.25.0 + +require ( + github.com/ArthurSens/prometheus-collector-bridge v0.0.0-20260227220117-1744a07e66f4 + github.com/PuerkitoBio/rehttp v1.4.0 + github.com/prometheus-community/stackdriver_exporter v0.0.0 + github.com/prometheus/client_golang v1.23.2 + go.opentelemetry.io/collector/component v1.53.0 + go.opentelemetry.io/collector/consumer/consumertest v0.147.0 + go.opentelemetry.io/collector/receiver v1.53.0 + go.opentelemetry.io/collector/receiver/receivertest v0.147.0 + golang.org/x/oauth2 v0.34.0 + google.golang.org/api v0.251.0 +) + +require ( + cloud.google.com/go/auth v0.16.5 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect + cloud.google.com/go/compute/metadata v0.9.0 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fatih/camelcase v1.0.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/google/s2a-go v0.1.9 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect + github.com/googleapis/gax-go/v2 v2.15.0 // indirect + github.com/hashicorp/go-version v1.8.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.67.5 // indirect + github.com/prometheus/procfs v0.19.2 // indirect + github.com/stretchr/testify v1.11.1 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/collector/component/componenttest v0.147.0 // indirect + go.opentelemetry.io/collector/consumer v1.53.0 // indirect + go.opentelemetry.io/collector/consumer/consumererror v0.147.0 // indirect + go.opentelemetry.io/collector/consumer/xconsumer v0.147.0 // indirect + go.opentelemetry.io/collector/featuregate v1.53.0 // indirect + go.opentelemetry.io/collector/internal/componentalias v0.147.0 // indirect + go.opentelemetry.io/collector/pdata v1.53.0 // indirect + go.opentelemetry.io/collector/pdata/pprofile v0.147.0 // indirect + go.opentelemetry.io/collector/pipeline v1.53.0 // indirect + go.opentelemetry.io/collector/receiver/xreceiver v0.147.0 // indirect + go.opentelemetry.io/contrib/bridges/prometheus v0.65.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect + go.opentelemetry.io/otel v1.40.0 // indirect + go.opentelemetry.io/otel/metric v1.40.0 // indirect + go.opentelemetry.io/otel/sdk v1.40.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.40.0 // indirect + go.opentelemetry.io/otel/trace v1.40.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.1 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect + golang.org/x/crypto v0.48.0 // indirect + golang.org/x/net v0.51.0 // indirect + golang.org/x/sys v0.41.0 // indirect + golang.org/x/text v0.34.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect + google.golang.org/grpc v1.79.1 // indirect + google.golang.org/protobuf v1.36.11 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace github.com/prometheus-community/stackdriver_exporter => ../ diff --git a/otelcollector/go.sum b/otelcollector/go.sum new file mode 100644 index 00000000..c983bb83 --- /dev/null +++ b/otelcollector/go.sum @@ -0,0 +1,191 @@ +cloud.google.com/go/auth v0.16.5 h1:mFWNQ2FEVWAliEQWpAdH80omXFokmrnbDhUS9cBywsI= +cloud.google.com/go/auth v0.16.5/go.mod h1:utzRfHMP+Vv0mpOkTRQoWD2q3BatTOoWbA7gCc2dUhQ= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= +github.com/ArthurSens/prometheus-collector-bridge v0.0.0-20260227220117-1744a07e66f4 h1:Cxm7zt1opKBW2EVoQi4D6THdQofmgcUsFNGy+HaZwmA= +github.com/ArthurSens/prometheus-collector-bridge v0.0.0-20260227220117-1744a07e66f4/go.mod h1:542FBzGmxriMHXZj6/PLgg1OXJW93TOIitB43+8sq/Y= +github.com/PuerkitoBio/rehttp v1.4.0 h1:rIN7A2s+O9fmHUM1vUcInvlHj9Ysql4hE+Y0wcl/xk8= +github.com/PuerkitoBio/rehttp v1.4.0/go.mod h1:LUwKPoDbDIA2RL5wYZCNsQ90cx4OJ4AWBmq6KzWZL1s= +github.com/aybabtme/iocontrol v0.0.0-20150809002002-ad15bcfc95a0 h1:0NmehRCgyk5rljDQLKUO+cRJCnduDyn11+zGZIc9Z48= +github.com/aybabtme/iocontrol v0.0.0-20150809002002-ad15bcfc95a0/go.mod h1:6L7zgvqo0idzI7IO8de6ZC051AfXb5ipkIJ7bIA2tGA= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= +github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= +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.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/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/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.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= +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/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= +github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= +github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo= +github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc= +github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= +github.com/hashicorp/go-version v1.8.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/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/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +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/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/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/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= +github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= +github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= +github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= +github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +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.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/collector/component v1.53.0 h1:A+GU9n4eKnFVmrr7NPpbVvJ1kp985jXtachb9gy12mk= +go.opentelemetry.io/collector/component v1.53.0/go.mod h1:yqyFwDuP4JKwOFaxdqoWj25aVthtavGkSDp2K42x+YY= +go.opentelemetry.io/collector/component/componenttest v0.147.0 h1:9XTwUT87gFWScoP29GEyMjKjr0jycVon6u/EVLrw08w= +go.opentelemetry.io/collector/component/componenttest v0.147.0/go.mod h1:ph5UnCbKUeX3xBg9eSdueRnGmNB4DmhQ0KC6lTsGYTs= +go.opentelemetry.io/collector/consumer v1.53.0 h1:Gyy80dX5r1Lv9lvQk8XFtUkWs1eniicOzzCQBejLseg= +go.opentelemetry.io/collector/consumer v1.53.0/go.mod h1:f5U6ibd+XpC5eOSeEYhERAQJ2a5bp1d2RzW3MFddMDM= +go.opentelemetry.io/collector/consumer/consumererror v0.147.0 h1:c4jjAEke6AEqoxalOAIEudGuN4rnnheaLWdpJXPCAPQ= +go.opentelemetry.io/collector/consumer/consumererror v0.147.0/go.mod h1:9MwE9k6xHd3TGBSAeKSmt42dwWyxwUhYqfwPUx1ZQJY= +go.opentelemetry.io/collector/consumer/consumertest v0.147.0 h1:AU3sUm2L3pezrg6hzPJAO19ZANQoCcfgbyanN0q360g= +go.opentelemetry.io/collector/consumer/consumertest v0.147.0/go.mod h1:QWGFRmeYNbKaseDTNT3a2iGDmjl+DCZnLzMP7Rjj0JM= +go.opentelemetry.io/collector/consumer/xconsumer v0.147.0 h1:XJVQc2dYyalaFXMTa4/RE+aweQTiBpw1edfwdCIJSxw= +go.opentelemetry.io/collector/consumer/xconsumer v0.147.0/go.mod h1:mtwh1VsUoGjxwdmXEzjbswH7KAGByJNCIMHmhqwXeK0= +go.opentelemetry.io/collector/featuregate v1.53.0 h1:cgjXdtl7jezWxq6V0eohe/JqjY4PBotZGb5+bTR2OJw= +go.opentelemetry.io/collector/featuregate v1.53.0/go.mod h1:PS7zY/zaCb28EqciePVwRHVhc3oKortTFXsi3I6ee4g= +go.opentelemetry.io/collector/internal/componentalias v0.147.0 h1:cC1gEQwzQnDvbELVjE3FXqgBkrsUl5JhzOT+6hISaLI= +go.opentelemetry.io/collector/internal/componentalias v0.147.0/go.mod h1:RxuMjMy1j+2jZcY1Ej0E+NC6DnoqTMEvIwRiXtk82rc= +go.opentelemetry.io/collector/internal/testutil v0.147.0 h1:DFlRxBRp23/sZnpTITK25yqe0d56yNvK+63IaWc6OsU= +go.opentelemetry.io/collector/internal/testutil v0.147.0/go.mod h1:Jkjs6rkqs973LqgZ0Fe3zrokQRKULYXPIf4HuqStiEE= +go.opentelemetry.io/collector/pdata v1.53.0 h1:DlYDbRwammEZaxDZHINx5v0n8SEOVNniPbi6FRTlVkA= +go.opentelemetry.io/collector/pdata v1.53.0/go.mod h1:LRSYGNjKXaUrZEwZv3Yl+8/zV2HmRGKXW62zB2bysms= +go.opentelemetry.io/collector/pdata/pprofile v0.147.0 h1:yQS3RBvcvRcy9N7AnJvsxmse0AxJcRqBZfwMA22xBA8= +go.opentelemetry.io/collector/pdata/pprofile v0.147.0/go.mod h1:pm9mUqHNpT1SaCkxILu4FW1BvMAelh7EKhpSKe2KJIQ= +go.opentelemetry.io/collector/pdata/testdata v0.147.0 h1:fZB5jY5F+zC/oeGYBa92IknhPQIlLSwoxDUMzhrpTP4= +go.opentelemetry.io/collector/pdata/testdata v0.147.0/go.mod h1:+AB6qTXrYEBvqrv394SEXzuWxtL9LLrnVgIjYpP9HHU= +go.opentelemetry.io/collector/pipeline v1.53.0 h1:+RrNuAmHnzldGOzCCYLJv0qTFoi9QJGrLm+MEYMozmo= +go.opentelemetry.io/collector/pipeline v1.53.0/go.mod h1:RD90NG3Jbk965Xaqym3JyHkuol4uZJjQVUkD9ddXJIs= +go.opentelemetry.io/collector/receiver v1.53.0 h1:FACspX7EMj91g8OY3twlJKzw2LKj0g5wZAXT4Ys2XRU= +go.opentelemetry.io/collector/receiver v1.53.0/go.mod h1:rhBr1+X3N9ijDBBKrVCiRMfVTUlOSWj+Gj0A6qevmoA= +go.opentelemetry.io/collector/receiver/receivertest v0.147.0 h1:t+AqCUJT0ivO1eE09f8gIqnO73UeEFqjvL/annt6rWg= +go.opentelemetry.io/collector/receiver/receivertest v0.147.0/go.mod h1:8kZCwsG8KNpWRf+2izpoY8iIOyfC2cQ2CLSZc9LgOP0= +go.opentelemetry.io/collector/receiver/xreceiver v0.147.0 h1:/KAxTban2sQhiksAu/EG+ri0mNgSxldhJ4lj/XGT+xQ= +go.opentelemetry.io/collector/receiver/xreceiver v0.147.0/go.mod h1:DCjNMipiIv59Jc/YfWFxAvgonurJET9cw3D79U1yLMc= +go.opentelemetry.io/contrib/bridges/prometheus v0.65.0 h1:I/7S/yWobR3QHFLqHsJ8QOndoiFsj1VgHpQiq43KlUI= +go.opentelemetry.io/contrib/bridges/prometheus v0.65.0/go.mod h1:jPF6gn3y1E+nozCAEQj3c6NZ8KY+tvAgSVfvoOJUFac= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= +go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms= +go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g= +go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g= +go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc= +go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8= +go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE= +go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw= +go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg= +go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw= +go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA= +go.opentelemetry.io/proto/slim/otlp v1.9.0 h1:fPVMv8tP3TrsqlkH1HWYUpbCY9cAIemx184VGkS6vlE= +go.opentelemetry.io/proto/slim/otlp v1.9.0/go.mod h1:xXdeJJ90Gqyll+orzUkY4bOd2HECo5JofeoLpymVqdI= +go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.2.0 h1:o13nadWDNkH/quoDomDUClnQBpdQQ2Qqv0lQBjIXjE8= +go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.2.0/go.mod h1:Gyb6Xe7FTi/6xBHwMmngGoHqL0w29Y4eW8TGFzpefGA= +go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.2.0 h1:EiUYvtwu6PMrMHVjcPfnsG3v+ajPkbUeH+IL93+QYyk= +go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.2.0/go.mod h1:mUUHKFiN2SST3AhJ8XhJxEoeVW12oqfXog0Bo8W3Ec4= +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.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= +golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= +golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= +golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= +golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= +golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= +golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= +golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/api v0.251.0 h1:6lea5nHRT8RUmpy9kkC2PJYnhnDAB13LqrLSVQlMIE8= +google.golang.org/api v0.251.0/go.mod h1:Rwy0lPf/TD7+T2VhYcffCHhyyInyuxGjICxdfLqT7KI= +google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4= +google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s= +google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M= +google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY= +google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +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/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +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/otelcollector/lifecycle.go b/otelcollector/lifecycle.go new file mode 100644 index 00000000..6cc1fb38 --- /dev/null +++ b/otelcollector/lifecycle.go @@ -0,0 +1,199 @@ +package otelcollector + +import ( + "context" + "fmt" + "log/slog" + "slices" + "strings" + "time" + + prombridge "github.com/ArthurSens/prometheus-collector-bridge" + "github.com/PuerkitoBio/rehttp" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus-community/stackdriver_exporter/collectors" + "github.com/prometheus-community/stackdriver_exporter/delta" + "github.com/prometheus-community/stackdriver_exporter/utils" + "golang.org/x/oauth2/google" + "google.golang.org/api/compute/v1" + "google.golang.org/api/monitoring/v3" + "google.golang.org/api/option" +) + +type collectorFactoryFunc func(projectID string, service *monitoring.Service, opts collectors.MonitoringCollectorOptions, deltasTTL time.Duration, logger *slog.Logger) (prometheus.Collector, error) + +type lifecycleManager struct { + logger *slog.Logger + + monitoringServiceFactory func(ctx context.Context, parsed parsedConfig, cfg *Config) (*monitoring.Service, error) + collectorFactory collectorFactoryFunc + filterProjectDiscoverer func(ctx context.Context, filter string) ([]string, error) + defaultProjectDiscoverer func(ctx context.Context) (string, error) +} + +func newLifecycleManager(logger *slog.Logger) *lifecycleManager { + return &lifecycleManager{ + logger: logger, + monitoringServiceFactory: createMonitoringService, + collectorFactory: func(projectID string, service *monitoring.Service, opts collectors.MonitoringCollectorOptions, deltasTTL time.Duration, logger *slog.Logger) (prometheus.Collector, error) { + return collectors.NewMonitoringCollector( + projectID, + service, + opts, + logger, + delta.NewInMemoryCounterStore(logger, deltasTTL), + delta.NewInMemoryHistogramStore(logger, deltasTTL), + ) + }, + filterProjectDiscoverer: utils.GetProjectIDsFromFilter, + defaultProjectDiscoverer: discoverDefaultProjectID, + } +} + +func (m *lifecycleManager) Start(ctx context.Context, exporterConfig prombridge.Config) (*prometheus.Registry, error) { + cfg, ok := exporterConfig.(*Config) + if !ok { + return nil, fmt.Errorf("invalid exporter config type: %T", exporterConfig) + } + + parsed, err := cfg.parsedDurations() + if err != nil { + return nil, err + } + + projectIDs, err := m.resolveProjectIDs(ctx, cfg) + if err != nil { + return nil, err + } + + monitoringService, err := m.monitoringServiceFactory(ctx, parsed, cfg) + if err != nil { + return nil, err + } + + registry := prometheus.NewRegistry() + metricPrefixes := parseMetricTypePrefixes(cfg.MetricsPrefixes) + extraFilters := parseMetricExtraFilters(cfg.Filters) + + for _, projectID := range projectIDs { + opts := collectors.MonitoringCollectorOptions{ + MetricTypePrefixes: metricPrefixes, + ExtraFilters: extraFilters, + RequestInterval: parsed.MetricsInterval, + RequestOffset: parsed.MetricsOffset, + IngestDelay: cfg.MetricsIngest, + FillMissingLabels: cfg.FillMissing, + DropDelegatedProjects: cfg.DropDelegated, + AggregateDeltas: cfg.AggregateDeltas, + DescriptorCacheTTL: parsed.DescriptorTTL, + DescriptorCacheOnlyGoogle: cfg.DescriptorGoogleOnly, + } + + collector, err := m.collectorFactory(projectID, monitoringService, opts, parsed.DeltasTTL, m.logger) + if err != nil { + return nil, fmt.Errorf("failed to create collector for project %q: %w", projectID, err) + } + if err := registry.Register(collector); err != nil { + return nil, fmt.Errorf("failed to register collector for project %q: %w", projectID, err) + } + } + + return registry, nil +} + +func (m *lifecycleManager) Shutdown(context.Context) error { + return nil +} + +func (m *lifecycleManager) resolveProjectIDs(ctx context.Context, cfg *Config) ([]string, error) { + var projectIDs []string + + if cfg.ProjectsFilter != "" { + ids, err := m.filterProjectDiscoverer(ctx, cfg.ProjectsFilter) + if err != nil { + return nil, fmt.Errorf("failed to resolve project IDs from projects_filter: %w", err) + } + projectIDs = append(projectIDs, ids...) + } + + projectIDs = append(projectIDs, cfg.ProjectIDs...) + + if len(projectIDs) == 0 { + projectID, err := m.defaultProjectDiscoverer(ctx) + if err != nil { + return nil, fmt.Errorf("failed to discover default GCP project: %w", err) + } + projectIDs = append(projectIDs, projectID) + } + + slices.Sort(projectIDs) + return slices.Compact(projectIDs), nil +} + +func discoverDefaultProjectID(ctx context.Context) (string, error) { + credentials, err := google.FindDefaultCredentials(ctx, compute.ComputeScope) + if err != nil { + return "", err + } + if credentials.ProjectID == "" { + return "", fmt.Errorf("unable to identify default GCP project") + } + return credentials.ProjectID, nil +} + +func createMonitoringService(ctx context.Context, parsed parsedConfig, cfg *Config) (*monitoring.Service, error) { + googleClient, err := google.DefaultClient(ctx, monitoring.MonitoringReadScope) + if err != nil { + return nil, fmt.Errorf("error creating Google client: %w", err) + } + + googleClient.Timeout = parsed.HTTPTimeout + googleClient.Transport = rehttp.NewTransport( + googleClient.Transport, + rehttp.RetryAll( + rehttp.RetryMaxRetries(cfg.MaxRetries), + rehttp.RetryStatuses(cfg.RetryStatuses...), + ), + rehttp.ExpJitterDelay(parsed.BackoffJitter, parsed.MaxBackoff), + ) + + service, err := monitoring.NewService(ctx, option.WithHTTPClient(googleClient), option.WithUniverseDomain(cfg.UniverseDomain)) + if err != nil { + return nil, fmt.Errorf("error creating Google Stackdriver Monitoring service: %w", err) + } + return service, nil +} + +func parseMetricTypePrefixes(input []string) []string { + in := append([]string(nil), input...) + slices.Sort(in) + unique := slices.Compact(in) + out := make([]string, 0, len(unique)) + + for i, prefix := range unique { + if i > 0 && len(out) > 0 { + prev := out[len(out)-1] + if strings.HasPrefix(prefix, prev) { + continue + } + } + out = append(out, prefix) + } + return out +} + +func parseMetricExtraFilters(raw []string) []collectors.MetricFilter { + out := make([]collectors.MetricFilter, 0, len(raw)) + for _, entry := range raw { + prefix, filter := utils.SplitExtraFilter(entry, ":") + if prefix == "" { + continue + } + out = append(out, collectors.MetricFilter{ + TargetedMetricPrefix: strings.ToLower(prefix), + FilterQuery: filter, + }) + } + return out +} + diff --git a/otelcollector/lifecycle_test.go b/otelcollector/lifecycle_test.go new file mode 100644 index 00000000..87c61909 --- /dev/null +++ b/otelcollector/lifecycle_test.go @@ -0,0 +1,131 @@ +package otelcollector + +import ( + "context" + "errors" + "log/slog" + "strings" + "testing" + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus-community/stackdriver_exporter/collectors" + "google.golang.org/api/monitoring/v3" +) + +func TestLifecycleManager_Start(t *testing.T) { + t.Parallel() + + cfg := &Config{ + ProjectIDs: []string{"project-a", "project-b"}, + MetricsPrefixes: []string{"compute.googleapis.com/instance"}, + HTTPTimeout: "10s", + MaxBackoff: "5s", + BackoffJitter: "1s", + MetricsInterval: "5m", + MetricsOffset: "0s", + DeltasTTL: "30m", + DescriptorTTL: "0s", + } + + var createdProjects []string + mgr := newLifecycleManager(slog.Default()) + mgr.monitoringServiceFactory = func(context.Context, parsedConfig, *Config) (*monitoring.Service, error) { + return &monitoring.Service{}, nil + } + mgr.collectorFactory = func(projectID string, _ *monitoring.Service, _ collectors.MonitoringCollectorOptions, _ time.Duration, _ *slog.Logger) (prometheus.Collector, error) { + createdProjects = append(createdProjects, projectID) + return prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "test_metric_" + strings.ReplaceAll(projectID, "-", "_"), + Help: "test", + }), nil + } + + reg, err := mgr.Start(context.Background(), cfg) + if err != nil { + t.Fatalf("Start() error = %v", err) + } + if reg == nil { + t.Fatal("Start() returned nil registry") + } + if len(createdProjects) != 2 { + t.Fatalf("collectorFactory called %d times, want 2", len(createdProjects)) + } + + // Ensure the created registry can gather metrics. + if _, err := reg.Gather(); err != nil { + t.Fatalf("registry.Gather() error = %v", err) + } +} + +func TestLifecycleManager_Start_UsesDefaultProjectDiscovery(t *testing.T) { + t.Parallel() + + cfg := &Config{ + MetricsPrefixes: []string{"compute.googleapis.com/instance"}, + HTTPTimeout: "10s", + MaxBackoff: "5s", + BackoffJitter: "1s", + MetricsInterval: "5m", + MetricsOffset: "0s", + DeltasTTL: "30m", + DescriptorTTL: "0s", + } + + mgr := newLifecycleManager(slog.Default()) + mgr.defaultProjectDiscoverer = func(context.Context) (string, error) { + return "auto-project", nil + } + mgr.monitoringServiceFactory = func(context.Context, parsedConfig, *Config) (*monitoring.Service, error) { + return &monitoring.Service{}, nil + } + mgr.collectorFactory = func(projectID string, _ *monitoring.Service, _ collectors.MonitoringCollectorOptions, _ time.Duration, _ *slog.Logger) (prometheus.Collector, error) { + if projectID != "auto-project" { + t.Fatalf("projectID = %q, want %q", projectID, "auto-project") + } + return prometheus.NewGauge(prometheus.GaugeOpts{Name: "auto_project_metric", Help: "test"}), nil + } + + if _, err := mgr.Start(context.Background(), cfg); err != nil { + t.Fatalf("Start() error = %v", err) + } +} + +func TestLifecycleManager_Start_ReturnsErrorFromCollectorFactory(t *testing.T) { + t.Parallel() + + cfg := &Config{ + ProjectIDs: []string{"project-a"}, + MetricsPrefixes: []string{"compute.googleapis.com/instance"}, + HTTPTimeout: "10s", + MaxBackoff: "5s", + BackoffJitter: "1s", + MetricsInterval: "5m", + MetricsOffset: "0s", + DeltasTTL: "30m", + DescriptorTTL: "0s", + } + + mgr := newLifecycleManager(slog.Default()) + mgr.monitoringServiceFactory = func(context.Context, parsedConfig, *Config) (*monitoring.Service, error) { + return &monitoring.Service{}, nil + } + mgr.collectorFactory = func(string, *monitoring.Service, collectors.MonitoringCollectorOptions, time.Duration, *slog.Logger) (prometheus.Collector, error) { + return nil, errors.New("boom") + } + + if _, err := mgr.Start(context.Background(), cfg); err == nil { + t.Fatal("Start() expected error, got nil") + } +} + +func TestLifecycleManager_Shutdown(t *testing.T) { + t.Parallel() + + mgr := newLifecycleManager(slog.Default()) + if err := mgr.Shutdown(context.Background()); err != nil { + t.Fatalf("Shutdown() error = %v", err) + } +} + + From 56ffa00e0368dbc2ed4a1090b3558dc7fe63419a Mon Sep 17 00:00:00 2001 From: Arthur Silva Sens Date: Wed, 4 Mar 2026 15:37:41 -0300 Subject: [PATCH 2/6] lint Signed-off-by: Arthur Silva Sens --- otelcollector/config.go | 67 ++++++++++++++++----------------- otelcollector/config_test.go | 1 - otelcollector/factory.go | 1 - otelcollector/factory_test.go | 1 - otelcollector/lifecycle.go | 5 +-- otelcollector/lifecycle_test.go | 4 +- 6 files changed, 36 insertions(+), 43 deletions(-) diff --git a/otelcollector/config.go b/otelcollector/config.go index 8f6a34ae..a0646e98 100644 --- a/otelcollector/config.go +++ b/otelcollector/config.go @@ -10,45 +10,45 @@ import ( // Config maps stackdriver_exporter runtime settings into exporter_config. type Config struct { - ProjectIDs []string `mapstructure:"project_ids"` - ProjectsFilter string `mapstructure:"projects_filter"` - UniverseDomain string `mapstructure:"universe_domain"` - MaxRetries int `mapstructure:"max_retries"` - HTTPTimeout string `mapstructure:"http_timeout"` - MaxBackoff string `mapstructure:"max_backoff"` - BackoffJitter string `mapstructure:"backoff_jitter"` - RetryStatuses []int `mapstructure:"retry_statuses"` - MetricsPrefixes []string `mapstructure:"metrics_prefixes"` - MetricsInterval string `mapstructure:"metrics_interval"` - MetricsOffset string `mapstructure:"metrics_offset"` - MetricsIngest bool `mapstructure:"metrics_ingest_delay"` - FillMissing bool `mapstructure:"fill_missing_labels"` - DropDelegated bool `mapstructure:"drop_delegated_projects"` - Filters []string `mapstructure:"filters"` - AggregateDeltas bool `mapstructure:"aggregate_deltas"` - DeltasTTL string `mapstructure:"aggregate_deltas_ttl"` - DescriptorTTL string `mapstructure:"descriptor_cache_ttl"` - DescriptorGoogleOnly bool `mapstructure:"descriptor_cache_only_google"` + ProjectIDs []string `mapstructure:"project_ids"` + ProjectsFilter string `mapstructure:"projects_filter"` + UniverseDomain string `mapstructure:"universe_domain"` + MaxRetries int `mapstructure:"max_retries"` + HTTPTimeout string `mapstructure:"http_timeout"` + MaxBackoff string `mapstructure:"max_backoff"` + BackoffJitter string `mapstructure:"backoff_jitter"` + RetryStatuses []int `mapstructure:"retry_statuses"` + MetricsPrefixes []string `mapstructure:"metrics_prefixes"` + MetricsInterval string `mapstructure:"metrics_interval"` + MetricsOffset string `mapstructure:"metrics_offset"` + MetricsIngest bool `mapstructure:"metrics_ingest_delay"` + FillMissing bool `mapstructure:"fill_missing_labels"` + DropDelegated bool `mapstructure:"drop_delegated_projects"` + Filters []string `mapstructure:"filters"` + AggregateDeltas bool `mapstructure:"aggregate_deltas"` + DeltasTTL string `mapstructure:"aggregate_deltas_ttl"` + DescriptorTTL string `mapstructure:"descriptor_cache_ttl"` + DescriptorGoogleOnly bool `mapstructure:"descriptor_cache_only_google"` } var _ prombridge.Config = (*Config)(nil) func defaultConfig() *Config { return &Config{ - UniverseDomain: "googleapis.com", - MaxRetries: 0, - HTTPTimeout: "10s", - MaxBackoff: "5s", - BackoffJitter: "1s", - RetryStatuses: []int{503}, - MetricsInterval: "5m", - MetricsOffset: "0s", - MetricsIngest: false, - FillMissing: true, - DropDelegated: false, - AggregateDeltas: false, - DeltasTTL: "30m", - DescriptorTTL: "0s", + UniverseDomain: "googleapis.com", + MaxRetries: 0, + HTTPTimeout: "10s", + MaxBackoff: "5s", + BackoffJitter: "1s", + RetryStatuses: []int{503}, + MetricsInterval: "5m", + MetricsOffset: "0s", + MetricsIngest: false, + FillMissing: true, + DropDelegated: false, + AggregateDeltas: false, + DeltasTTL: "30m", + DescriptorTTL: "0s", DescriptorGoogleOnly: true, } } @@ -157,4 +157,3 @@ type configUnmarshaler struct{} func (configUnmarshaler) GetConfigStruct() prombridge.Config { return defaultConfig() } - diff --git a/otelcollector/config_test.go b/otelcollector/config_test.go index 61bcb0a6..e3f2129a 100644 --- a/otelcollector/config_test.go +++ b/otelcollector/config_test.go @@ -151,4 +151,3 @@ func TestConfig_Durations(t *testing.T) { t.Fatalf("DescriptorTTL = %v, want %v", parsed.DescriptorTTL, 0*time.Second) } } - diff --git a/otelcollector/factory.go b/otelcollector/factory.go index 48a3573a..784155c4 100644 --- a/otelcollector/factory.go +++ b/otelcollector/factory.go @@ -24,4 +24,3 @@ var ( _ prombridge.ExporterLifecycleManager = (*lifecycleManager)(nil) _ prombridge.ConfigUnmarshaler = (configUnmarshaler{}) ) - diff --git a/otelcollector/factory_test.go b/otelcollector/factory_test.go index 19233747..631db135 100644 --- a/otelcollector/factory_test.go +++ b/otelcollector/factory_test.go @@ -40,4 +40,3 @@ func TestFactory_CreateMetrics(t *testing.T) { t.Fatal("CreateMetrics() returned nil receiver") } } - diff --git a/otelcollector/lifecycle.go b/otelcollector/lifecycle.go index 6cc1fb38..587e721b 100644 --- a/otelcollector/lifecycle.go +++ b/otelcollector/lifecycle.go @@ -10,10 +10,10 @@ import ( prombridge "github.com/ArthurSens/prometheus-collector-bridge" "github.com/PuerkitoBio/rehttp" - "github.com/prometheus/client_golang/prometheus" "github.com/prometheus-community/stackdriver_exporter/collectors" "github.com/prometheus-community/stackdriver_exporter/delta" "github.com/prometheus-community/stackdriver_exporter/utils" + "github.com/prometheus/client_golang/prometheus" "golang.org/x/oauth2/google" "google.golang.org/api/compute/v1" "google.golang.org/api/monitoring/v3" @@ -33,7 +33,7 @@ type lifecycleManager struct { func newLifecycleManager(logger *slog.Logger) *lifecycleManager { return &lifecycleManager{ - logger: logger, + logger: logger, monitoringServiceFactory: createMonitoringService, collectorFactory: func(projectID string, service *monitoring.Service, opts collectors.MonitoringCollectorOptions, deltasTTL time.Duration, logger *slog.Logger) (prometheus.Collector, error) { return collectors.NewMonitoringCollector( @@ -196,4 +196,3 @@ func parseMetricExtraFilters(raw []string) []collectors.MetricFilter { } return out } - diff --git a/otelcollector/lifecycle_test.go b/otelcollector/lifecycle_test.go index 87c61909..28c0f8f4 100644 --- a/otelcollector/lifecycle_test.go +++ b/otelcollector/lifecycle_test.go @@ -8,8 +8,8 @@ import ( "testing" "time" - "github.com/prometheus/client_golang/prometheus" "github.com/prometheus-community/stackdriver_exporter/collectors" + "github.com/prometheus/client_golang/prometheus" "google.golang.org/api/monitoring/v3" ) @@ -127,5 +127,3 @@ func TestLifecycleManager_Shutdown(t *testing.T) { t.Fatalf("Shutdown() error = %v", err) } } - - From e62b3b0e88f77183ddb185f0b85be5fd985c8e89 Mon Sep 17 00:00:00 2001 From: Arthur Silva Sens Date: Wed, 4 Mar 2026 15:47:01 -0300 Subject: [PATCH 3/6] Add license headers to otelcollector files Signed-off-by: Arthur Silva Sens Made-with: Cursor --- otelcollector/config.go | 13 +++++++++++++ otelcollector/config_test.go | 13 +++++++++++++ otelcollector/factory.go | 13 +++++++++++++ otelcollector/factory_test.go | 13 +++++++++++++ otelcollector/lifecycle.go | 13 +++++++++++++ otelcollector/lifecycle_test.go | 13 +++++++++++++ 6 files changed, 78 insertions(+) diff --git a/otelcollector/config.go b/otelcollector/config.go index a0646e98..ff812a54 100644 --- a/otelcollector/config.go +++ b/otelcollector/config.go @@ -1,3 +1,16 @@ +// Copyright 2020 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package otelcollector import ( diff --git a/otelcollector/config_test.go b/otelcollector/config_test.go index e3f2129a..403c0abc 100644 --- a/otelcollector/config_test.go +++ b/otelcollector/config_test.go @@ -1,3 +1,16 @@ +// Copyright 2020 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package otelcollector import ( diff --git a/otelcollector/factory.go b/otelcollector/factory.go index 784155c4..fac8c9d9 100644 --- a/otelcollector/factory.go +++ b/otelcollector/factory.go @@ -1,3 +1,16 @@ +// Copyright 2020 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package otelcollector import ( diff --git a/otelcollector/factory_test.go b/otelcollector/factory_test.go index 631db135..1f700d46 100644 --- a/otelcollector/factory_test.go +++ b/otelcollector/factory_test.go @@ -1,3 +1,16 @@ +// Copyright 2020 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package otelcollector import ( diff --git a/otelcollector/lifecycle.go b/otelcollector/lifecycle.go index 587e721b..6751f98d 100644 --- a/otelcollector/lifecycle.go +++ b/otelcollector/lifecycle.go @@ -1,3 +1,16 @@ +// Copyright 2020 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package otelcollector import ( diff --git a/otelcollector/lifecycle_test.go b/otelcollector/lifecycle_test.go index 28c0f8f4..89729d62 100644 --- a/otelcollector/lifecycle_test.go +++ b/otelcollector/lifecycle_test.go @@ -1,3 +1,16 @@ +// Copyright 2020 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package otelcollector import ( From 1ab6197d6096fdc7028ae2b60b7785c2ad9503b9 Mon Sep 17 00:00:00 2001 From: Arthur Silva Sens Date: Wed, 1 Apr 2026 12:16:32 -0300 Subject: [PATCH 4/6] fix imports Signed-off-by: Arthur Silva Sens --- stackdriver_exporter_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/stackdriver_exporter_test.go b/stackdriver_exporter_test.go index b0e65f0d..2a1b31f9 100644 --- a/stackdriver_exporter_test.go +++ b/stackdriver_exporter_test.go @@ -13,8 +13,10 @@ package main -import "testing" -import "reflect" +import ( + "reflect" + "testing" +) func TestParseMetricTypePrefixes(t *testing.T) { inputPrefixes := []string{ From 321e426d388baaa6959231f732a9c30a05295106 Mon Sep 17 00:00:00 2001 From: Arthur Silva Sens Date: Wed, 1 Apr 2026 12:29:53 -0300 Subject: [PATCH 5/6] Deduplicate utility functions Signed-off-by: Arthur Silva Sens --- collectors/monitoring_collector.go | 15 +++++++++ collectors/monitoring_collector_test.go | 29 +++++++++++++++- otelcollector/lifecycle.go | 38 ++------------------- stackdriver_exporter.go | 45 ++----------------------- stackdriver_exporter_test.go | 20 ----------- utils/utils.go | 22 ++++++++++++ utils/utils_test.go | 24 +++++++++++++ 7 files changed, 94 insertions(+), 99 deletions(-) diff --git a/collectors/monitoring_collector.go b/collectors/monitoring_collector.go index 21a58611..2f95fbac 100644 --- a/collectors/monitoring_collector.go +++ b/collectors/monitoring_collector.go @@ -36,6 +36,21 @@ type MetricFilter struct { FilterQuery string } +func ParseMetricExtraFilters(raw []string) []MetricFilter { + out := make([]MetricFilter, 0, len(raw)) + for _, entry := range raw { + prefix, filter := utils.SplitExtraFilter(entry, ":") + if prefix == "" { + continue + } + out = append(out, MetricFilter{ + TargetedMetricPrefix: strings.ToLower(prefix), + FilterQuery: filter, + }) + } + return out +} + type MonitoringCollector struct { projectID string metricsTypePrefixes []string diff --git a/collectors/monitoring_collector_test.go b/collectors/monitoring_collector_test.go index e80fdfbb..a7706f3d 100644 --- a/collectors/monitoring_collector_test.go +++ b/collectors/monitoring_collector_test.go @@ -13,7 +13,10 @@ package collectors -import "testing" +import ( + "reflect" + "testing" +) func TestIsGoogleMetric(t *testing.T) { good := []string{ @@ -37,3 +40,27 @@ func TestIsGoogleMetric(t *testing.T) { } } } + +func TestParseMetricExtraFilters(t *testing.T) { + input := []string{ + "pubsub.googleapis.com/subscription:resource.labels.subscription_id=monitoring.regex.full_match(\"my-subs-prefix.*\")", + "missing-separator", + "compute.googleapis.com/instance:metric.labels.instance_name=\"example:vm\"", + } + + got := ParseMetricExtraFilters(input) + want := []MetricFilter{ + { + TargetedMetricPrefix: "pubsub.googleapis.com/subscription", + FilterQuery: "resource.labels.subscription_id=monitoring.regex.full_match(\"my-subs-prefix.*\")", + }, + { + TargetedMetricPrefix: "compute.googleapis.com/instance", + FilterQuery: "metric.labels.instance_name=\"example:vm\"", + }, + } + + if !reflect.DeepEqual(got, want) { + t.Fatalf("ParseMetricExtraFilters() = %#v, want %#v", got, want) + } +} diff --git a/otelcollector/lifecycle.go b/otelcollector/lifecycle.go index 6751f98d..082e8d1b 100644 --- a/otelcollector/lifecycle.go +++ b/otelcollector/lifecycle.go @@ -18,7 +18,6 @@ import ( "fmt" "log/slog" "slices" - "strings" "time" prombridge "github.com/ArthurSens/prometheus-collector-bridge" @@ -85,8 +84,8 @@ func (m *lifecycleManager) Start(ctx context.Context, exporterConfig prombridge. } registry := prometheus.NewRegistry() - metricPrefixes := parseMetricTypePrefixes(cfg.MetricsPrefixes) - extraFilters := parseMetricExtraFilters(cfg.Filters) + metricPrefixes := utils.ParseMetricTypePrefixes(cfg.MetricsPrefixes) + extraFilters := collectors.ParseMetricExtraFilters(cfg.Filters) for _, projectID := range projectIDs { opts := collectors.MonitoringCollectorOptions{ @@ -176,36 +175,3 @@ func createMonitoringService(ctx context.Context, parsed parsedConfig, cfg *Conf } return service, nil } - -func parseMetricTypePrefixes(input []string) []string { - in := append([]string(nil), input...) - slices.Sort(in) - unique := slices.Compact(in) - out := make([]string, 0, len(unique)) - - for i, prefix := range unique { - if i > 0 && len(out) > 0 { - prev := out[len(out)-1] - if strings.HasPrefix(prefix, prev) { - continue - } - } - out = append(out, prefix) - } - return out -} - -func parseMetricExtraFilters(raw []string) []collectors.MetricFilter { - out := make([]collectors.MetricFilter, 0, len(raw)) - for _, entry := range raw { - prefix, filter := utils.SplitExtraFilter(entry, ":") - if prefix == "" { - continue - } - out = append(out, collectors.MetricFilter{ - TargetedMetricPrefix: strings.ToLower(prefix), - FilterQuery: filter, - }) - } - return out -} diff --git a/stackdriver_exporter.go b/stackdriver_exporter.go index 14876fb1..a265d111 100644 --- a/stackdriver_exporter.go +++ b/stackdriver_exporter.go @@ -300,7 +300,7 @@ func (h *handler) filterMetricTypePrefixes(filters map[string]bool) []string { } } } - return parseMetricTypePrefixes(filteredPrefixes) + return utils.ParseMetricTypePrefixes(filteredPrefixes) } func main() { @@ -377,8 +377,8 @@ func main() { "projectsFilter", *projectsFilter, ) - parsedMetricsPrefixes := parseMetricTypePrefixes(metricsPrefixes) - metricExtraFilters := parseMetricExtraFilters() + parsedMetricsPrefixes := utils.ParseMetricTypePrefixes(metricsPrefixes) + metricExtraFilters := collectors.ParseMetricExtraFilters(*monitoringMetricsExtraFilter) // drop duplicate projects slices.Sort(discoveredProjectIDs) uniqueProjectIds := slices.Compact(discoveredProjectIDs) @@ -429,42 +429,3 @@ func main() { os.Exit(1) } } - -func parseMetricTypePrefixes(inputPrefixes []string) []string { - metricTypePrefixes := []string{} - - // Drop duplicate prefixes. - slices.Sort(inputPrefixes) - uniquePrefixes := slices.Compact(inputPrefixes) - - // Drop prefixes that start with another existing prefix to avoid error: - // "collected metric xxx was collected before with the same name and label values". - for i, prefix := range uniquePrefixes { - if i != 0 { - previousIndex := len(metricTypePrefixes) - 1 - - // Drop current prefix if it starts with the previous one. - if strings.HasPrefix(prefix, metricTypePrefixes[previousIndex]) { - continue - } - } - metricTypePrefixes = append(metricTypePrefixes, prefix) - } - - return metricTypePrefixes -} - -func parseMetricExtraFilters() []collectors.MetricFilter { - var extraFilters []collectors.MetricFilter - for _, ef := range *monitoringMetricsExtraFilter { - targetedMetricPrefix, filterQuery := utils.SplitExtraFilter(ef, ":") - if targetedMetricPrefix != "" { - extraFilter := collectors.MetricFilter{ - TargetedMetricPrefix: strings.ToLower(targetedMetricPrefix), - FilterQuery: filterQuery, - } - extraFilters = append(extraFilters, extraFilter) - } - } - return extraFilters -} diff --git a/stackdriver_exporter_test.go b/stackdriver_exporter_test.go index 2a1b31f9..cabf1954 100644 --- a/stackdriver_exporter_test.go +++ b/stackdriver_exporter_test.go @@ -18,26 +18,6 @@ import ( "testing" ) -func TestParseMetricTypePrefixes(t *testing.T) { - inputPrefixes := []string{ - "redis.googleapis.com/stats/memory/usage", - "loadbalancing.googleapis.com/https/request_count", - "loadbalancing.googleapis.com", - "redis.googleapis.com/stats/memory/usage_ratio", - "redis.googleapis.com/stats/memory/usage_ratio", - } - expectedOutputPrefixes := []string{ - "loadbalancing.googleapis.com", - "redis.googleapis.com/stats/memory/usage", - } - - outputPrefixes := parseMetricTypePrefixes(inputPrefixes) - - if !reflect.DeepEqual(outputPrefixes, expectedOutputPrefixes) { - t.Errorf("Metric type prefix sanitization did not produce expected output. Expected:\n%s\nGot:\n%s", expectedOutputPrefixes, outputPrefixes) - } -} - func TestFilterMetricTypePrefixes(t *testing.T) { metricPrefixes := []string{ "redis.googleapis.com/stats/", diff --git a/utils/utils.go b/utils/utils.go index ce97f097..67a23bdf 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -16,6 +16,7 @@ package utils import ( "context" "regexp" + "slices" "strings" "github.com/fatih/camelcase" @@ -49,6 +50,27 @@ func SplitExtraFilter(extraFilter string, separator string) (string, string) { return mPrefix[0], mPrefix[1] } +// ParseMetricTypePrefixes sorts prefixes, removes duplicates, and skips prefixes +// already covered by a broader parent prefix. +func ParseMetricTypePrefixes(inputPrefixes []string) []string { + sortedPrefixes := append([]string(nil), inputPrefixes...) + slices.Sort(sortedPrefixes) + uniquePrefixes := slices.Compact(sortedPrefixes) + metricTypePrefixes := make([]string, 0, len(uniquePrefixes)) + + for _, prefix := range uniquePrefixes { + if len(metricTypePrefixes) > 0 { + previousIndex := len(metricTypePrefixes) - 1 + if strings.HasPrefix(prefix, metricTypePrefixes[previousIndex]) { + continue + } + } + metricTypePrefixes = append(metricTypePrefixes, prefix) + } + + return metricTypePrefixes +} + func ProjectResource(projectID string) string { return "projects/" + projectID } diff --git a/utils/utils_test.go b/utils/utils_test.go index a179cf14..f7b2f523 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -51,3 +51,27 @@ var _ = Describe("SplitExtraFilter", func() { Expect(filterQuery).To(Equal("filter.name=\"filter:value\"")) }) }) + +var _ = Describe("ParseMetricTypePrefixes", func() { + It("drops duplicates and redundant nested prefixes", func() { + inputPrefixes := []string{ + "redis.googleapis.com/stats/memory/usage", + "loadbalancing.googleapis.com/https/request_count", + "loadbalancing.googleapis.com", + "redis.googleapis.com/stats/memory/usage_ratio", + "redis.googleapis.com/stats/memory/usage_ratio", + } + + Expect(ParseMetricTypePrefixes(inputPrefixes)).To(Equal([]string{ + "loadbalancing.googleapis.com", + "redis.googleapis.com/stats/memory/usage", + })) + Expect(inputPrefixes).To(Equal([]string{ + "redis.googleapis.com/stats/memory/usage", + "loadbalancing.googleapis.com/https/request_count", + "loadbalancing.googleapis.com", + "redis.googleapis.com/stats/memory/usage_ratio", + "redis.googleapis.com/stats/memory/usage_ratio", + })) + }) +}) From cc7145e27fe64906f23630857ef59aa3e3f547f0 Mon Sep 17 00:00:00 2001 From: Arthur Silva Sens Date: Thu, 2 Apr 2026 10:16:52 -0300 Subject: [PATCH 6/6] Update collector-bridge dependency Signed-off-by: Arthur Silva Sens --- otelcollector/go.mod | 44 +++++++++--------- otelcollector/go.sum | 108 +++++++++++++++++++++---------------------- 2 files changed, 76 insertions(+), 76 deletions(-) diff --git a/otelcollector/go.mod b/otelcollector/go.mod index f7f60f75..0c16a4d5 100644 --- a/otelcollector/go.mod +++ b/otelcollector/go.mod @@ -7,10 +7,10 @@ require ( github.com/PuerkitoBio/rehttp v1.4.0 github.com/prometheus-community/stackdriver_exporter v0.0.0 github.com/prometheus/client_golang v1.23.2 - go.opentelemetry.io/collector/component v1.53.0 - go.opentelemetry.io/collector/consumer/consumertest v0.147.0 - go.opentelemetry.io/collector/receiver v1.53.0 - go.opentelemetry.io/collector/receiver/receivertest v0.147.0 + go.opentelemetry.io/collector/component v1.54.0 + go.opentelemetry.io/collector/consumer/consumertest v0.148.0 + go.opentelemetry.io/collector/receiver v1.54.0 + go.opentelemetry.io/collector/receiver/receivertest v0.148.0 golang.org/x/oauth2 v0.34.0 google.golang.org/api v0.251.0 ) @@ -39,26 +39,26 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.67.5 // indirect - github.com/prometheus/procfs v0.19.2 // indirect + github.com/prometheus/procfs v0.20.1 // indirect github.com/stretchr/testify v1.11.1 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/collector/component/componenttest v0.147.0 // indirect - go.opentelemetry.io/collector/consumer v1.53.0 // indirect - go.opentelemetry.io/collector/consumer/consumererror v0.147.0 // indirect - go.opentelemetry.io/collector/consumer/xconsumer v0.147.0 // indirect - go.opentelemetry.io/collector/featuregate v1.53.0 // indirect - go.opentelemetry.io/collector/internal/componentalias v0.147.0 // indirect - go.opentelemetry.io/collector/pdata v1.53.0 // indirect - go.opentelemetry.io/collector/pdata/pprofile v0.147.0 // indirect - go.opentelemetry.io/collector/pipeline v1.53.0 // indirect - go.opentelemetry.io/collector/receiver/xreceiver v0.147.0 // indirect - go.opentelemetry.io/contrib/bridges/prometheus v0.65.0 // indirect + go.opentelemetry.io/collector/component/componenttest v0.148.0 // indirect + go.opentelemetry.io/collector/consumer v1.54.0 // indirect + go.opentelemetry.io/collector/consumer/consumererror v0.148.0 // indirect + go.opentelemetry.io/collector/consumer/xconsumer v0.148.0 // indirect + go.opentelemetry.io/collector/featuregate v1.54.0 // indirect + go.opentelemetry.io/collector/internal/componentalias v0.148.0 // indirect + go.opentelemetry.io/collector/pdata v1.54.0 // indirect + go.opentelemetry.io/collector/pdata/pprofile v0.148.0 // indirect + go.opentelemetry.io/collector/pipeline v1.54.0 // indirect + go.opentelemetry.io/collector/receiver/xreceiver v0.148.0 // indirect + go.opentelemetry.io/contrib/bridges/prometheus v0.67.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect - go.opentelemetry.io/otel v1.40.0 // indirect - go.opentelemetry.io/otel/metric v1.40.0 // indirect - go.opentelemetry.io/otel/sdk v1.40.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.40.0 // indirect - go.opentelemetry.io/otel/trace v1.40.0 // indirect + go.opentelemetry.io/otel v1.42.0 // indirect + go.opentelemetry.io/otel/metric v1.42.0 // indirect + go.opentelemetry.io/otel/sdk v1.42.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.42.0 // indirect + go.opentelemetry.io/otel/trace v1.42.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.1 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect @@ -67,7 +67,7 @@ require ( golang.org/x/sys v0.41.0 // indirect golang.org/x/text v0.34.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect - google.golang.org/grpc v1.79.1 // indirect + google.golang.org/grpc v1.79.2 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/otelcollector/go.sum b/otelcollector/go.sum index c983bb83..641dc91f 100644 --- a/otelcollector/go.sum +++ b/otelcollector/go.sum @@ -76,8 +76,8 @@ github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNw github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= -github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= -github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= +github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= +github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -87,58 +87,58 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/collector/component v1.53.0 h1:A+GU9n4eKnFVmrr7NPpbVvJ1kp985jXtachb9gy12mk= -go.opentelemetry.io/collector/component v1.53.0/go.mod h1:yqyFwDuP4JKwOFaxdqoWj25aVthtavGkSDp2K42x+YY= -go.opentelemetry.io/collector/component/componenttest v0.147.0 h1:9XTwUT87gFWScoP29GEyMjKjr0jycVon6u/EVLrw08w= -go.opentelemetry.io/collector/component/componenttest v0.147.0/go.mod h1:ph5UnCbKUeX3xBg9eSdueRnGmNB4DmhQ0KC6lTsGYTs= -go.opentelemetry.io/collector/consumer v1.53.0 h1:Gyy80dX5r1Lv9lvQk8XFtUkWs1eniicOzzCQBejLseg= -go.opentelemetry.io/collector/consumer v1.53.0/go.mod h1:f5U6ibd+XpC5eOSeEYhERAQJ2a5bp1d2RzW3MFddMDM= -go.opentelemetry.io/collector/consumer/consumererror v0.147.0 h1:c4jjAEke6AEqoxalOAIEudGuN4rnnheaLWdpJXPCAPQ= -go.opentelemetry.io/collector/consumer/consumererror v0.147.0/go.mod h1:9MwE9k6xHd3TGBSAeKSmt42dwWyxwUhYqfwPUx1ZQJY= -go.opentelemetry.io/collector/consumer/consumertest v0.147.0 h1:AU3sUm2L3pezrg6hzPJAO19ZANQoCcfgbyanN0q360g= -go.opentelemetry.io/collector/consumer/consumertest v0.147.0/go.mod h1:QWGFRmeYNbKaseDTNT3a2iGDmjl+DCZnLzMP7Rjj0JM= -go.opentelemetry.io/collector/consumer/xconsumer v0.147.0 h1:XJVQc2dYyalaFXMTa4/RE+aweQTiBpw1edfwdCIJSxw= -go.opentelemetry.io/collector/consumer/xconsumer v0.147.0/go.mod h1:mtwh1VsUoGjxwdmXEzjbswH7KAGByJNCIMHmhqwXeK0= -go.opentelemetry.io/collector/featuregate v1.53.0 h1:cgjXdtl7jezWxq6V0eohe/JqjY4PBotZGb5+bTR2OJw= -go.opentelemetry.io/collector/featuregate v1.53.0/go.mod h1:PS7zY/zaCb28EqciePVwRHVhc3oKortTFXsi3I6ee4g= -go.opentelemetry.io/collector/internal/componentalias v0.147.0 h1:cC1gEQwzQnDvbELVjE3FXqgBkrsUl5JhzOT+6hISaLI= -go.opentelemetry.io/collector/internal/componentalias v0.147.0/go.mod h1:RxuMjMy1j+2jZcY1Ej0E+NC6DnoqTMEvIwRiXtk82rc= -go.opentelemetry.io/collector/internal/testutil v0.147.0 h1:DFlRxBRp23/sZnpTITK25yqe0d56yNvK+63IaWc6OsU= -go.opentelemetry.io/collector/internal/testutil v0.147.0/go.mod h1:Jkjs6rkqs973LqgZ0Fe3zrokQRKULYXPIf4HuqStiEE= -go.opentelemetry.io/collector/pdata v1.53.0 h1:DlYDbRwammEZaxDZHINx5v0n8SEOVNniPbi6FRTlVkA= -go.opentelemetry.io/collector/pdata v1.53.0/go.mod h1:LRSYGNjKXaUrZEwZv3Yl+8/zV2HmRGKXW62zB2bysms= -go.opentelemetry.io/collector/pdata/pprofile v0.147.0 h1:yQS3RBvcvRcy9N7AnJvsxmse0AxJcRqBZfwMA22xBA8= -go.opentelemetry.io/collector/pdata/pprofile v0.147.0/go.mod h1:pm9mUqHNpT1SaCkxILu4FW1BvMAelh7EKhpSKe2KJIQ= -go.opentelemetry.io/collector/pdata/testdata v0.147.0 h1:fZB5jY5F+zC/oeGYBa92IknhPQIlLSwoxDUMzhrpTP4= -go.opentelemetry.io/collector/pdata/testdata v0.147.0/go.mod h1:+AB6qTXrYEBvqrv394SEXzuWxtL9LLrnVgIjYpP9HHU= -go.opentelemetry.io/collector/pipeline v1.53.0 h1:+RrNuAmHnzldGOzCCYLJv0qTFoi9QJGrLm+MEYMozmo= -go.opentelemetry.io/collector/pipeline v1.53.0/go.mod h1:RD90NG3Jbk965Xaqym3JyHkuol4uZJjQVUkD9ddXJIs= -go.opentelemetry.io/collector/receiver v1.53.0 h1:FACspX7EMj91g8OY3twlJKzw2LKj0g5wZAXT4Ys2XRU= -go.opentelemetry.io/collector/receiver v1.53.0/go.mod h1:rhBr1+X3N9ijDBBKrVCiRMfVTUlOSWj+Gj0A6qevmoA= -go.opentelemetry.io/collector/receiver/receivertest v0.147.0 h1:t+AqCUJT0ivO1eE09f8gIqnO73UeEFqjvL/annt6rWg= -go.opentelemetry.io/collector/receiver/receivertest v0.147.0/go.mod h1:8kZCwsG8KNpWRf+2izpoY8iIOyfC2cQ2CLSZc9LgOP0= -go.opentelemetry.io/collector/receiver/xreceiver v0.147.0 h1:/KAxTban2sQhiksAu/EG+ri0mNgSxldhJ4lj/XGT+xQ= -go.opentelemetry.io/collector/receiver/xreceiver v0.147.0/go.mod h1:DCjNMipiIv59Jc/YfWFxAvgonurJET9cw3D79U1yLMc= -go.opentelemetry.io/contrib/bridges/prometheus v0.65.0 h1:I/7S/yWobR3QHFLqHsJ8QOndoiFsj1VgHpQiq43KlUI= -go.opentelemetry.io/contrib/bridges/prometheus v0.65.0/go.mod h1:jPF6gn3y1E+nozCAEQj3c6NZ8KY+tvAgSVfvoOJUFac= +go.opentelemetry.io/collector/component v1.54.0 h1:LvtX0Tzz18n44OrUFVk77N1FNsejfWJqztB28hrmDM8= +go.opentelemetry.io/collector/component v1.54.0/go.mod h1:yUMBYsySY/sDcXm8kOzEoZxt+JLdala6hxzSW0npOxY= +go.opentelemetry.io/collector/component/componenttest v0.148.0 h1:tBXJWmy2X6KD8S0QU2YZa2zYBqP+IycSM4iOtwDD2pA= +go.opentelemetry.io/collector/component/componenttest v0.148.0/go.mod h1:1c1+6mZOmI0raoya5vA/X0F+fawEjNS6tCEs5xLATtA= +go.opentelemetry.io/collector/consumer v1.54.0 h1:RGGtUN+GbkV1px3T6XdUHmgJ+ldJ1hAHdesFzW/wgL0= +go.opentelemetry.io/collector/consumer v1.54.0/go.mod h1:1PC6XINTL9DdT1bwvfMdHE72EB4RWU/WcPemUrhqKN8= +go.opentelemetry.io/collector/consumer/consumererror v0.148.0 h1:lKVkNWBeRXG41lHBf5KzA9oErRZifx6qTd9erAFfEkE= +go.opentelemetry.io/collector/consumer/consumererror v0.148.0/go.mod h1:N/UppmtknIdzpEiy3xirH1EiBEBOqKqD77NCyNi2Rbc= +go.opentelemetry.io/collector/consumer/consumertest v0.148.0 h1:ms0HtWMj17tI1Yds0hSuUI5QYpNEqd11AAhwIoUY2HE= +go.opentelemetry.io/collector/consumer/consumertest v0.148.0/go.mod h1:wScw/OzKkf/ZzJn4ToI30OoI1kJiY16WNrcFToXSzK0= +go.opentelemetry.io/collector/consumer/xconsumer v0.148.0 h1:m3b9rY7CLD5Pcge6sSKHIT3OlcPN6xqYsdtVs9oJ528= +go.opentelemetry.io/collector/consumer/xconsumer v0.148.0/go.mod h1:bG+Wz6xmIBl/gHzq1sqvksWXqTLuTX17Wo//zIsdZpw= +go.opentelemetry.io/collector/featuregate v1.54.0 h1:ufo5Hy4Co9pcHVg24hyanm8qFG3TkkYbVyQXPVAbwDc= +go.opentelemetry.io/collector/featuregate v1.54.0/go.mod h1:PS7zY/zaCb28EqciePVwRHVhc3oKortTFXsi3I6ee4g= +go.opentelemetry.io/collector/internal/componentalias v0.148.0 h1:Y6MftNIZSzOr47TTj6A2z2UR3IwbeG46sAQshicGtDg= +go.opentelemetry.io/collector/internal/componentalias v0.148.0/go.mod h1:uwKzfehzwRgHxdHgFXYSBHNBeWSSqsqQYGWr5fk08G0= +go.opentelemetry.io/collector/internal/testutil v0.148.0 h1:3Z9hperte3vSmbBTYeNndoEUICICrNz8hzx+v0FYXBQ= +go.opentelemetry.io/collector/internal/testutil v0.148.0/go.mod h1:Jkjs6rkqs973LqgZ0Fe3zrokQRKULYXPIf4HuqStiEE= +go.opentelemetry.io/collector/pdata v1.54.0 h1:3LharKb792cQ3VrUGxd3IcpWwfu3ST+GSTU382jVz1s= +go.opentelemetry.io/collector/pdata v1.54.0/go.mod h1:+MqC3VVOv/EX9YVFUo+mI4F0YmwJ+fXBYwjmu+mRiZ8= +go.opentelemetry.io/collector/pdata/pprofile v0.148.0 h1:MgrNZmqwhZGfiYwcKKtM/iXgTZqqvG5dUphriRXMZHU= +go.opentelemetry.io/collector/pdata/pprofile v0.148.0/go.mod h1:MTTMnZPqWX1S/rBDatU0W19udlycBkWuzVV5qnemHdc= +go.opentelemetry.io/collector/pdata/testdata v0.148.0 h1:yzakPuFgoKK8WcrlhyYHLMLA/kLScQKGsXkIgwieAQ8= +go.opentelemetry.io/collector/pdata/testdata v0.148.0/go.mod h1:2rFvxm8qwd3nlO90FtJw6ZGAjt+bLndxmQuJaMO9kfQ= +go.opentelemetry.io/collector/pipeline v1.54.0 h1:jYlCkdFLITVBdeB+IGS07zXWywEgvT3Ky46vdKKT+Ks= +go.opentelemetry.io/collector/pipeline v1.54.0/go.mod h1:RD90NG3Jbk965Xaqym3JyHkuol4uZJjQVUkD9ddXJIs= +go.opentelemetry.io/collector/receiver v1.54.0 h1:2e9o+eihZ/nJnzVj5JAcJ+VQ653HcZRiT127qBZRqa8= +go.opentelemetry.io/collector/receiver v1.54.0/go.mod h1:xFZnvYTBjdi9iS/d/UUXzss4h311mLsZliQFQXk4o/k= +go.opentelemetry.io/collector/receiver/receivertest v0.148.0 h1:Fu+B4jCqgZVZmhsKBz3tcgimFryR6TRAK2D5VGLD2Xc= +go.opentelemetry.io/collector/receiver/receivertest v0.148.0/go.mod h1:K8dMDMEggEg6jB688VOHutivOGEEZ20FJGe4jV9RtWU= +go.opentelemetry.io/collector/receiver/xreceiver v0.148.0 h1:u66Zi3udD9RMRiNOsZzsVcUjRwqJEK+5LV76Ry9l3K0= +go.opentelemetry.io/collector/receiver/xreceiver v0.148.0/go.mod h1:jyHxf8SOfH48ZXb32IS3vPbVYDinsLlZYQddyrveqMg= +go.opentelemetry.io/contrib/bridges/prometheus v0.67.0 h1:dkBzNEAIKADEaFnuESzcXvpd09vxvDZsOjx11gjUqLk= +go.opentelemetry.io/contrib/bridges/prometheus v0.67.0/go.mod h1:Z5RIwRkZgauOIfnG5IpidvLpERjhTninpP1dTG2jTl4= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= -go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms= -go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g= -go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g= -go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc= -go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8= -go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE= -go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw= -go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg= -go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw= -go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA= -go.opentelemetry.io/proto/slim/otlp v1.9.0 h1:fPVMv8tP3TrsqlkH1HWYUpbCY9cAIemx184VGkS6vlE= -go.opentelemetry.io/proto/slim/otlp v1.9.0/go.mod h1:xXdeJJ90Gqyll+orzUkY4bOd2HECo5JofeoLpymVqdI= -go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.2.0 h1:o13nadWDNkH/quoDomDUClnQBpdQQ2Qqv0lQBjIXjE8= -go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.2.0/go.mod h1:Gyb6Xe7FTi/6xBHwMmngGoHqL0w29Y4eW8TGFzpefGA= -go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.2.0 h1:EiUYvtwu6PMrMHVjcPfnsG3v+ajPkbUeH+IL93+QYyk= -go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.2.0/go.mod h1:mUUHKFiN2SST3AhJ8XhJxEoeVW12oqfXog0Bo8W3Ec4= +go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho= +go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc= +go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4= +go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI= +go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo= +go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts= +go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA= +go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc= +go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY= +go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc= +go.opentelemetry.io/proto/slim/otlp v1.10.0 h1:iR97Vs/ZDR+y9TfuP9b1XBtdPWeC+OMslIBmhcLU7jM= +go.opentelemetry.io/proto/slim/otlp v1.10.0/go.mod h1:lV9250stpjYLPNA5viFabIgP2QlUGRT1GdTgAf8SIUk= +go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.3.0 h1:RUF5rO0hAlgiJt1fzQVzcVs3vZVNHIcMLgOgG4rWNcQ= +go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.3.0/go.mod h1:I89cynRj8y+383o7tEQVg2SVA6SRgDVIouWPUVXjx0U= +go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.3.0 h1:CQvJSldHRUN6Z8jsUeYv8J0lXRvygALXIzsmAeCcZE0= +go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.3.0/go.mod h1:xSQ+mEfJe/GjK1LXEyVOoSI1N9JV9ZI923X5kup43W4= 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= @@ -177,8 +177,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1: google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I= google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= -google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY= -google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= +google.golang.org/grpc v1.79.2 h1:fRMD94s2tITpyJGtBBn7MkMseNpOZU8ZxgC3MMBaXRU= +google.golang.org/grpc v1.79.2/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=