diff --git a/.gitignore b/.gitignore index bb4ec6259..5221bfd9c 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,6 @@ release_deps # OpAmp Files collector*.yaml manager*.yaml -logging*.yaml \ No newline at end of file +logging*.yaml +config.yaml +storage diff --git a/.goreleaser.yml b/.goreleaser.yml index a63a4c2fd..6c8e2f256 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -14,6 +14,8 @@ builds: env: - CGO_ENABLED=0 mod_timestamp: "{{ .CommitTimestamp }}" + tags: + - bindplane goos: - windows - linux diff --git a/Makefile b/Makefile index 53e2c9af5..90fc693a4 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,7 @@ VERSION ?= $(if $(CURRENT_TAG),$(CURRENT_TAG),$(PREVIOUS_TAG)) # Builds just the agent for current GOOS/GOARCH pair .PHONY: agent agent: - go build -ldflags "-s -w -X github.com/observiq/bindplane-agent/internal/version.version=$(VERSION)" -o $(OUTDIR)/collector_$(GOOS)_$(GOARCH)$(EXT) ./cmd/collector + go build -ldflags "-s -w -X github.com/observiq/bindplane-agent/internal/version.version=$(VERSION)" -tags bindplane -o $(OUTDIR)/collector_$(GOOS)_$(GOARCH)$(EXT) ./cmd/collector # Builds just the updater for current GOOS/GOARCH pair .PHONY: updater diff --git a/collector/collector.go b/collector/collector.go index 3b1a152f1..2cf29631a 100644 --- a/collector/collector.go +++ b/collector/collector.go @@ -24,6 +24,7 @@ import ( "time" "github.com/observiq/bindplane-agent/factories" + "github.com/observiq/bindplane-agent/internal/measurements" "go.opentelemetry.io/collector/otelcol" "go.uber.org/zap" ) @@ -199,6 +200,9 @@ func (c *collector) Stop(ctx context.Context) { close(shutdownCompleteChan) c.svc = nil + + // After shutting down, we reset the throughputs measurements registry so it's fresh for the next collector startup. + measurements.BindplaneAgentThroughputMeasurementsRegistry.Reset() } // Restart will restart the collector. It will also reset the status channel. diff --git a/extension/bindplaneextension/config.go b/extension/bindplaneextension/config.go index eb2378490..c90135eea 100644 --- a/extension/bindplaneextension/config.go +++ b/extension/bindplaneextension/config.go @@ -14,7 +14,32 @@ package bindplaneextension +import ( + "errors" + "time" + + "go.opentelemetry.io/collector/component" +) + // Config is the configuration for the bindplane extension type Config struct { + // Labels in "k1=v1,k2=v2" format Labels string `mapstructure:"labels"` + // Component ID of the opamp extension. If not specified, then + // this extension will not generate any custom messages for throughput metrics. + OpAMP component.ID `mapstructure:"opamp"` + // MeasurementsInterval is the interval on which to report measurements. + // Measurements reporting is disabled if this duration is 0. + MeasurementsInterval time.Duration `mapstructure:"measurements_interval"` + // ExtraMeasurementsAttributes are a map of key-value pairs to add to all reported measurements. + ExtraMeasurementsAttributes map[string]string `mapstructure:"extra_measurements_attributes,omitempty"` +} + +// Validate returns an error if the config is invalid +func (c Config) Validate() error { + if c.MeasurementsInterval < 0 { + return errors.New("measurements interval must be postitive or 0") + } + + return nil } diff --git a/extension/bindplaneextension/extension.go b/extension/bindplaneextension/extension.go index 23d6d4a58..003608bda 100644 --- a/extension/bindplaneextension/extension.go +++ b/extension/bindplaneextension/extension.go @@ -16,16 +16,151 @@ package bindplaneextension import ( "context" + "errors" + "fmt" + "sync" + "time" + "github.com/golang/snappy" + "github.com/observiq/bindplane-agent/internal/measurements" + "github.com/open-telemetry/opamp-go/client/types" + "github.com/open-telemetry/opentelemetry-collector-contrib/extension/opampcustommessages" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.uber.org/zap" ) -type bindplaneExtension struct{} +type bindplaneExtension struct { + logger *zap.Logger + cfg *Config + ctmr *measurements.ResettableThroughputMeasurementsRegistry + customCapabilityHandler opampcustommessages.CustomCapabilityHandler + doneChan chan struct{} + wg *sync.WaitGroup +} + +func newBindplaneExtension(logger *zap.Logger, cfg *Config) *bindplaneExtension { + return &bindplaneExtension{ + logger: logger, + cfg: cfg, + ctmr: measurements.NewResettableThroughputMeasurementsRegistry(false), + doneChan: make(chan struct{}), + wg: &sync.WaitGroup{}, + } +} + +func (b *bindplaneExtension) Start(_ context.Context, host component.Host) error { + var emptyComponentID component.ID + + // Set up measurements if enabled + if b.cfg.OpAMP != emptyComponentID && b.cfg.MeasurementsInterval > 0 { + err := b.setupCustomCapabilities(host) + if err != nil { + return fmt.Errorf("setup capability handler: %w", err) + } + + b.wg.Add(1) + go b.reportMetricsLoop() + } -func (bindplaneExtension) Start(_ context.Context, _ component.Host) error { return nil } -func (bindplaneExtension) Shutdown(_ context.Context) error { +func (b *bindplaneExtension) RegisterThroughputMeasurements(processorID string, measurements *measurements.ThroughputMeasurements) { + b.ctmr.RegisterThroughputMeasurements(processorID, measurements) +} + +func (b *bindplaneExtension) setupCustomCapabilities(host component.Host) error { + ext, ok := host.GetExtensions()[b.cfg.OpAMP] + if !ok { + return fmt.Errorf("opamp extension %q does not exist", b.cfg.OpAMP) + } + + registry, ok := ext.(opampcustommessages.CustomCapabilityRegistry) + if !ok { + return fmt.Errorf("extension %q is not an custom message registry", b.cfg.OpAMP) + } + + var err error + b.customCapabilityHandler, err = registry.Register(measurements.ReportMeasurementsV1Capability) + if err != nil { + return fmt.Errorf("register custom capability: %w", err) + } + + return nil +} + +func (b *bindplaneExtension) Dependencies() []component.ID { + var emptyComponentID component.ID + if b.cfg.OpAMP == emptyComponentID { + return nil + } + + return []component.ID{b.cfg.OpAMP} +} + +func (b *bindplaneExtension) reportMetricsLoop() { + defer b.wg.Done() + + t := time.NewTicker(b.cfg.MeasurementsInterval) + defer t.Stop() + + for { + select { + case <-t.C: + err := b.reportMetrics() + if err != nil { + b.logger.Error("Failed to report throughput metrics.", zap.Error(err)) + } + case <-b.doneChan: + return + } + } +} + +func (b *bindplaneExtension) reportMetrics() error { + m := b.ctmr.OTLPMeasurements(b.cfg.ExtraMeasurementsAttributes) + + // Send metrics as snappy-encoded otlp proto + marshaller := pmetric.ProtoMarshaler{} + marshalled, err := marshaller.MarshalMetrics(m) + if err != nil { + return fmt.Errorf("marshal metrics: %w", err) + } + + encoded := snappy.Encode(nil, marshalled) + for { + sendingChannel, err := b.customCapabilityHandler.SendMessage(measurements.ReportMeasurementsType, encoded) + switch { + case err == nil: + return nil + case errors.Is(err, types.ErrCustomMessagePending): + <-sendingChannel + continue + default: + return fmt.Errorf("send custom message: %w", err) + } + } +} + +func (b *bindplaneExtension) Shutdown(ctx context.Context) error { + close(b.doneChan) + + waitgroupDone := make(chan struct{}) + go func() { + defer close(waitgroupDone) + b.wg.Wait() + }() + + select { + case <-ctx.Done(): + return ctx.Err() + case <-waitgroupDone: // OK + } + + if b.customCapabilityHandler != nil { + b.customCapabilityHandler.Unregister() + } + return nil } diff --git a/extension/bindplaneextension/factory.go b/extension/bindplaneextension/factory.go index 5d85464cb..a0ea08c3f 100644 --- a/extension/bindplaneextension/factory.go +++ b/extension/bindplaneextension/factory.go @@ -36,6 +36,8 @@ func defaultConfig() component.Config { return &Config{} } -func createBindPlaneExtension(_ context.Context, _ extension.Settings, _ component.Config) (extension.Extension, error) { - return bindplaneExtension{}, nil +func createBindPlaneExtension(_ context.Context, cs extension.Settings, cfg component.Config) (extension.Extension, error) { + oCfg := cfg.(*Config) + + return newBindplaneExtension(cs.Logger, oCfg), nil } diff --git a/extension/bindplaneextension/go.mod b/extension/bindplaneextension/go.mod index c9f924ae3..cd17827ca 100644 --- a/extension/bindplaneextension/go.mod +++ b/extension/bindplaneextension/go.mod @@ -3,11 +3,17 @@ module github.com/observiq/bindplane-agent/extension/bindplaneextension go 1.21.9 require ( + github.com/golang/snappy v0.0.4 + github.com/observiq/bindplane-agent/internal/measurements v1.59.0 + github.com/open-telemetry/opamp-go v0.15.0 + github.com/open-telemetry/opentelemetry-collector-contrib/extension/opampcustommessages v0.107.0 github.com/stretchr/testify v1.9.0 go.opentelemetry.io/collector/component v0.107.0 go.opentelemetry.io/collector/confmap v0.107.0 go.opentelemetry.io/collector/extension v0.107.0 + go.opentelemetry.io/collector/pdata v1.13.0 go.uber.org/goleak v1.3.0 + go.uber.org/zap v1.27.0 ) require ( @@ -20,11 +26,14 @@ require ( 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.1 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.19.1 // indirect @@ -34,7 +43,6 @@ require ( go.opentelemetry.io/collector/config/configtelemetry v0.107.0 // indirect go.opentelemetry.io/collector/featuregate v1.13.0 // indirect go.opentelemetry.io/collector/internal/globalgates v0.107.0 // indirect - go.opentelemetry.io/collector/pdata v1.13.0 // indirect go.opentelemetry.io/otel v1.28.0 // indirect go.opentelemetry.io/otel/exporters/prometheus v0.50.0 // indirect go.opentelemetry.io/otel/metric v1.28.0 // indirect @@ -42,7 +50,6 @@ require ( go.opentelemetry.io/otel/sdk/metric v1.28.0 // indirect go.opentelemetry.io/otel/trace v1.28.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect golang.org/x/net v0.26.0 // indirect golang.org/x/sys v0.21.0 // indirect golang.org/x/text v0.16.0 // indirect @@ -51,3 +58,5 @@ require ( google.golang.org/protobuf v1.34.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace github.com/observiq/bindplane-agent/internal/measurements => ../../internal/measurements diff --git a/extension/bindplaneextension/go.sum b/extension/bindplaneextension/go.sum index 848b2a0be..6faab78a7 100644 --- a/extension/bindplaneextension/go.sum +++ b/extension/bindplaneextension/go.sum @@ -2,6 +2,7 @@ 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/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -13,12 +14,17 @@ github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAp github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/go-version v1.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= @@ -35,8 +41,23 @@ github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa1 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/open-telemetry/opamp-go v0.15.0 h1:X2TWhEsGQ8GP7Uos3Ic9v/1aFUqoECZXKS7xAF5HqsA= +github.com/open-telemetry/opamp-go v0.15.0/go.mod h1:QyPeN56JXlcZt5yG5RMdZ50Ju+zMFs1Ihy/hwHyF8Oo= +github.com/open-telemetry/opentelemetry-collector-contrib/extension/opampcustommessages v0.107.0 h1:ijv/+GY9xqCXmy6PdyBBomw0OWVZ7Cq4TQvVh6np74Y= +github.com/open-telemetry/opentelemetry-collector-contrib/extension/opampcustommessages v0.107.0/go.mod h1:1qSfNuJtzPlGadFK6cSZWtKmLNBhrQ5CgpOtLyn5baE= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden v0.107.0 h1:Zo2UJk4AOxU5M6q3LNiFgTZfQicY9zz/BDqJWsmWzY4= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden v0.107.0/go.mod h1:qj9lEtkVjQUzZ7FdJTeDqqTUq9xVU9kE4F8zZnHFB9M= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.107.0 h1:g1pkpFfe+dnhpfvo+f9yFIkbvTdiOvNmFOUFNzVAgvk= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.107.0/go.mod h1:oG/PliNiIOUHVARyDrFdvxFvG8DUPEjMGlmxjEqeoKM= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.107.0 h1:zTeRh4V3rMlXgNvfbDBnET6nvhOeZpYIbKTjVbSl9Ws= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.107.0/go.mod h1:/RtBag3LuHIkqN4bo8Erd3jCzA3gea70l9WyJ9TncXM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= @@ -49,6 +70,8 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= 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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/go.mod b/go.mod index 7b43a32ff..9914bc39f 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/observiq/bindplane-agent/exporter/googlecloudexporter v1.59.0 github.com/observiq/bindplane-agent/exporter/googlemanagedprometheusexporter v1.59.0 github.com/observiq/bindplane-agent/exporter/snowflakeexporter v1.59.0 + github.com/observiq/bindplane-agent/internal/measurements v1.59.0 github.com/observiq/bindplane-agent/internal/report v1.59.0 github.com/observiq/bindplane-agent/packagestate v1.59.0 github.com/observiq/bindplane-agent/processor/datapointcountprocessor v1.59.0 @@ -34,7 +35,7 @@ require ( github.com/observiq/bindplane-agent/receiver/sapnetweaverreceiver v1.59.0 github.com/observiq/bindplane-agent/receiver/telemetrygeneratorreceiver v1.59.0 github.com/oklog/ulid/v2 v2.1.0 - github.com/open-telemetry/opamp-go v0.9.0 + github.com/open-telemetry/opamp-go v0.14.0 github.com/open-telemetry/opentelemetry-collector-contrib/connector/countconnector v0.107.0 github.com/open-telemetry/opentelemetry-collector-contrib/connector/routingconnector v0.107.0 github.com/open-telemetry/opentelemetry-collector-contrib/connector/servicegraphconnector v0.107.0 @@ -75,6 +76,8 @@ require ( github.com/open-telemetry/opentelemetry-collector-contrib/extension/pprofextension v0.107.0 github.com/open-telemetry/opentelemetry-collector-contrib/extension/sigv4authextension v0.107.0 github.com/open-telemetry/opentelemetry-collector-contrib/extension/storage/filestorage v0.107.0 + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden v0.107.0 + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.107.0 github.com/open-telemetry/opentelemetry-collector-contrib/processor/attributesprocessor v0.107.0 github.com/open-telemetry/opentelemetry-collector-contrib/processor/cumulativetodeltaprocessor v0.107.0 github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatorateprocessor v0.107.0 @@ -545,7 +548,7 @@ require ( github.com/golang-sql/sqlexp v0.1.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/golang/snappy v0.0.4 // indirect + github.com/golang/snappy v0.0.4 // (Dakota): cadvisor only works on version `v0.49.1-0.20240628164550-89f779d86055` until they release v0.50.0 with updated docker dep // https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/33870 github.com/google/cadvisor v0.49.1-0.20240628164550-89f779d86055 // indirect @@ -557,7 +560,7 @@ require ( github.com/googleapis/gax-go/v2 v2.13.0 // indirect github.com/gophercloud/gophercloud v1.12.0 // indirect github.com/gorilla/mux v1.8.1 // indirect - github.com/gorilla/websocket v1.5.0 // indirect + github.com/gorilla/websocket v1.5.1 // indirect github.com/gosnmp/gosnmp v1.37.0 // indirect github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect github.com/grobie/gomemcache v0.0.0-20230213081705-239240bbc445 // indirect @@ -722,7 +725,7 @@ require ( go.opentelemetry.io/otel/exporters/prometheus v0.50.0 // indirect go.opentelemetry.io/otel/metric v1.28.0 // indirect go.opentelemetry.io/otel/sdk v1.28.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.28.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.28.0 go.opentelemetry.io/otel/trace v1.28.0 // indirect go.uber.org/atomic v1.11.0 // indirect golang.org/x/crypto v0.26.0 // indirect @@ -820,6 +823,8 @@ replace github.com/observiq/bindplane-agent/internal/testutils => ./internal/tes replace github.com/observiq/bindplane-agent/internal/report => ./internal/report +replace github.com/observiq/bindplane-agent/internal/measurements => ./internal/measurements + // Does not build with windows and only used in configschema executable // Relevant issue https://github.com/mattn/go-ieproxy/issues/45 replace github.com/mattn/go-ieproxy => github.com/mattn/go-ieproxy v0.0.1 diff --git a/go.sum b/go.sum index 617ab4ffe..d06cb3672 100644 --- a/go.sum +++ b/go.sum @@ -1504,8 +1504,8 @@ github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7Fsg github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/gosnmp/gosnmp v1.37.0 h1:/Tf8D3b9wrnNuf/SfbvO+44mPrjVphBhRtcGg22V07Y= github.com/gosnmp/gosnmp v1.37.0/go.mod h1:GDH9vNqpsD7f2HvZhKs5dlqSEcAS6s6Qp099oZRCR+M= github.com/grafana/loki/pkg/push v0.0.0-20240514112848-a1b1eeb09583 h1:dN3eF1S5fvVu2l9WoqYSvmNmPK8Uh2vjE4yUsBq80l4= @@ -1902,8 +1902,8 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk= github.com/onsi/gomega v1.32.0/go.mod h1:a4x4gW6Pz2yK1MAmvluYme5lvYTn61afQ2ETw/8n4Lg= -github.com/open-telemetry/opamp-go v0.9.0 h1:S6Mwn8uxYjcttE6ZJ6AXoiOuryU67zjKysz3xRfYL9k= -github.com/open-telemetry/opamp-go v0.9.0/go.mod h1:Pfmm5EdWqZCG0dZAJjAinlra3yEpqK5StCblxpbEp6Q= +github.com/open-telemetry/opamp-go v0.14.0 h1:KoziIK+wsFojhUXNTkCSTnCPf0eCMqFAaccOs0HrWIY= +github.com/open-telemetry/opamp-go v0.14.0/go.mod h1:XOGCigljsLSTZ8FfLwvat0M1QDj3conIIgRa77BWrKs= github.com/open-telemetry/opentelemetry-collector-contrib/connector/countconnector v0.107.0 h1:6A3R+q3JBxnhEYhM53fHF9qKGNEtvc8O5j0UnooLDrU= github.com/open-telemetry/opentelemetry-collector-contrib/connector/countconnector v0.107.0/go.mod h1:Fz1oG0B2AVwSAG22GOZc9EGpOXqdHJtYU0F0+KjA6zs= github.com/open-telemetry/opentelemetry-collector-contrib/connector/datadogconnector v0.107.0 h1:5nCidAaa6vxj85UHOz7/kqtVsnbFMXyDDUV0Ima8FHk= diff --git a/internal/measurements/bindplane_agent_throughput.go b/internal/measurements/bindplane_agent_throughput.go new file mode 100644 index 000000000..0b454a518 --- /dev/null +++ b/internal/measurements/bindplane_agent_throughput.go @@ -0,0 +1,19 @@ +// Copyright observIQ, Inc. +// +// 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 measurements + +// BindplaneAgentThroughputMeasurementsRegistry is the registry singleton used by bindplane agent to +// track throughput measurements +var BindplaneAgentThroughputMeasurementsRegistry = NewResettableThroughputMeasurementsRegistry(false) diff --git a/internal/measurements/custom_message.go b/internal/measurements/custom_message.go new file mode 100644 index 000000000..75e44f280 --- /dev/null +++ b/internal/measurements/custom_message.go @@ -0,0 +1,82 @@ +// Copyright observIQ, Inc. +// +// 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 measurements + +import ( + "time" + + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/otel/attribute" +) + +const ( + // ReportMeasurementsV1Capability is the capability needed to report measurements to bindplane + ReportMeasurementsV1Capability = "com.bindplane.measurements.v1" + // ReportMeasurementsType is the type for reporting measurements to BindPlane + ReportMeasurementsType = "reportMeasurements" +) + +// OTLPThroughputMeasurements converts a single ThroughputMeasurements to a pmetric.MetricSlice +func OTLPThroughputMeasurements(tm *ThroughputMeasurements, includeCountMetrics bool, extraAttributes map[string]string) pmetric.MetricSlice { + s := pmetric.NewMetricSlice() + + attrs := pcommon.NewMap() + sdkAttrs := tm.Attributes() + attrIter := sdkAttrs.Iter() + + for attrIter.Next() { + kv := attrIter.Attribute() + switch kv.Value.Type() { + case attribute.STRING: + attrs.PutStr(string(kv.Key), kv.Value.AsString()) + default: // Do nothing for non-string attributes; Attributes for throughput metrics can only be strings for now. + } + } + + for k, v := range extraAttributes { + attrs.PutStr(k, v) + } + + ts := pcommon.NewTimestampFromTime(time.Now()) + + setOTLPSum(s.AppendEmpty(), "otelcol_processor_throughputmeasurement_log_data_size", tm.LogSize(), attrs, ts) + setOTLPSum(s.AppendEmpty(), "otelcol_processor_throughputmeasurement_metric_data_size", tm.MetricSize(), attrs, ts) + setOTLPSum(s.AppendEmpty(), "otelcol_processor_throughputmeasurement_trace_data_size", tm.TraceSize(), attrs, ts) + + if includeCountMetrics { + setOTLPSum(s.AppendEmpty(), "otelcol_processor_throughputmeasurement_log_count", tm.LogCount(), attrs, ts) + setOTLPSum(s.AppendEmpty(), "otelcol_processor_throughputmeasurement_metric_count", tm.DatapointCount(), attrs, ts) + setOTLPSum(s.AppendEmpty(), "otelcol_processor_throughputmeasurement_trace_count", tm.TraceSize(), attrs, ts) + } + + return s +} + +func setOTLPSum(m pmetric.Metric, name string, value int64, attrs pcommon.Map, now pcommon.Timestamp) { + if value == 0 { + // Ignore value if it's 0 + return + } + + m.SetName(name) + m.SetEmptySum() + s := m.Sum() + + dp := s.DataPoints().AppendEmpty() + dp.SetIntValue(value) + attrs.CopyTo(dp.Attributes()) + dp.SetTimestamp(now) +} diff --git a/internal/measurements/go.mod b/internal/measurements/go.mod new file mode 100644 index 000000000..89c771593 --- /dev/null +++ b/internal/measurements/go.mod @@ -0,0 +1,36 @@ +module github.com/observiq/bindplane-agent/internal/measurements + +go 1.21.9 + +require ( + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden v0.107.0 + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.107.0 + github.com/stretchr/testify v1.9.0 + go.opentelemetry.io/collector/pdata v1.13.0 + go.opentelemetry.io/otel v1.27.0 + go.opentelemetry.io/otel/metric v1.27.0 + go.opentelemetry.io/otel/sdk/metric v1.27.0 +) + +require ( + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.107.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + go.opentelemetry.io/otel/sdk v1.27.0 // indirect + go.opentelemetry.io/otel/trace v1.27.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect + google.golang.org/grpc v1.65.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/internal/measurements/go.sum b/internal/measurements/go.sum new file mode 100644 index 000000000..da0bdf0f7 --- /dev/null +++ b/internal/measurements/go.sum @@ -0,0 +1,102 @@ +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/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +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/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/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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden v0.107.0 h1:Zo2UJk4AOxU5M6q3LNiFgTZfQicY9zz/BDqJWsmWzY4= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden v0.107.0/go.mod h1:qj9lEtkVjQUzZ7FdJTeDqqTUq9xVU9kE4F8zZnHFB9M= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.107.0 h1:g1pkpFfe+dnhpfvo+f9yFIkbvTdiOvNmFOUFNzVAgvk= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.107.0/go.mod h1:oG/PliNiIOUHVARyDrFdvxFvG8DUPEjMGlmxjEqeoKM= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.107.0 h1:zTeRh4V3rMlXgNvfbDBnET6nvhOeZpYIbKTjVbSl9Ws= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.107.0/go.mod h1:/RtBag3LuHIkqN4bo8Erd3jCzA3gea70l9WyJ9TncXM= +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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/collector/pdata v1.13.0 h1:eV3NQt2f1UcaibkziMvGTQI34LlpiYBUGp1yP0G/Cxw= +go.opentelemetry.io/collector/pdata v1.13.0/go.mod h1:MYeB0MmMAxeM0hstCFrCqWLzdyeYySim2dG6pDT6nYI= +go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg= +go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ= +go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik= +go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak= +go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI= +go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A= +go.opentelemetry.io/otel/sdk/metric v1.27.0 h1:5uGNOlpXi+Hbo/DRoI31BSb1v+OGcpv2NemcCrOL8gI= +go.opentelemetry.io/otel/sdk/metric v1.27.0/go.mod h1:we7jJVrYN2kh3mVBlswtPU22K0SA+769l93J6bsyvqw= +go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw= +go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4= +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= +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.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +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.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.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.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +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/internal/measurements/testdata/expected/throughput_measurements_count.yaml b/internal/measurements/testdata/expected/throughput_measurements_count.yaml new file mode 100644 index 000000000..17d0c1dad --- /dev/null +++ b/internal/measurements/testdata/expected/throughput_measurements_count.yaml @@ -0,0 +1,59 @@ +resourceMetrics: + - resource: {} + scopeMetrics: + - metrics: + - name: otelcol_processor_throughputmeasurement_log_data_size + sum: + dataPoints: + - asInt: "3974" + attributes: + - key: processor + value: + stringValue: throughputmeasurement/1 + timeUnixNano: "1000000" + - name: otelcol_processor_throughputmeasurement_metric_data_size + sum: + dataPoints: + - asInt: "5675" + attributes: + - key: processor + value: + stringValue: throughputmeasurement/1 + timeUnixNano: "1000000" + - name: otelcol_processor_throughputmeasurement_trace_data_size + sum: + dataPoints: + - asInt: "16767" + attributes: + - key: processor + value: + stringValue: throughputmeasurement/1 + timeUnixNano: "1000000" + - name: otelcol_processor_throughputmeasurement_log_count + sum: + dataPoints: + - asInt: "16" + attributes: + - key: processor + value: + stringValue: throughputmeasurement/1 + timeUnixNano: "1000000" + - name: otelcol_processor_throughputmeasurement_metric_count + sum: + dataPoints: + - asInt: "37" + attributes: + - key: processor + value: + stringValue: throughputmeasurement/1 + timeUnixNano: "1000000" + - name: otelcol_processor_throughputmeasurement_trace_count + sum: + dataPoints: + - asInt: "16767" + attributes: + - key: processor + value: + stringValue: throughputmeasurement/1 + timeUnixNano: "1000000" + scope: {} diff --git a/internal/measurements/testdata/expected/throughput_measurements_extra_attrs.yaml b/internal/measurements/testdata/expected/throughput_measurements_extra_attrs.yaml new file mode 100644 index 000000000..9e8e8f2b3 --- /dev/null +++ b/internal/measurements/testdata/expected/throughput_measurements_extra_attrs.yaml @@ -0,0 +1,41 @@ +resourceMetrics: + - resource: {} + scopeMetrics: + - metrics: + - name: otelcol_processor_throughputmeasurement_log_data_size + sum: + dataPoints: + - asInt: "3974" + attributes: + - key: gateway + value: + stringValue: "true" + - key: processor + value: + stringValue: throughputmeasurement/1 + timeUnixNano: "1000000" + - name: otelcol_processor_throughputmeasurement_metric_data_size + sum: + dataPoints: + - asInt: "5675" + attributes: + - key: gateway + value: + stringValue: "true" + - key: processor + value: + stringValue: throughputmeasurement/1 + timeUnixNano: "1000000" + - name: otelcol_processor_throughputmeasurement_trace_data_size + sum: + dataPoints: + - asInt: "16767" + attributes: + - key: gateway + value: + stringValue: "true" + - key: processor + value: + stringValue: throughputmeasurement/1 + timeUnixNano: "1000000" + scope: {} diff --git a/internal/measurements/testdata/expected/throughput_measurements_no_count.yaml b/internal/measurements/testdata/expected/throughput_measurements_no_count.yaml new file mode 100644 index 000000000..5440a0b43 --- /dev/null +++ b/internal/measurements/testdata/expected/throughput_measurements_no_count.yaml @@ -0,0 +1,32 @@ +resourceMetrics: + - resource: {} + scopeMetrics: + - metrics: + - name: otelcol_processor_throughputmeasurement_log_data_size + sum: + dataPoints: + - asInt: "3974" + attributes: + - key: processor + value: + stringValue: throughputmeasurement/1 + timeUnixNano: "1000000" + - name: otelcol_processor_throughputmeasurement_metric_data_size + sum: + dataPoints: + - asInt: "5675" + attributes: + - key: processor + value: + stringValue: throughputmeasurement/1 + timeUnixNano: "1000000" + - name: otelcol_processor_throughputmeasurement_trace_data_size + sum: + dataPoints: + - asInt: "16767" + attributes: + - key: processor + value: + stringValue: throughputmeasurement/1 + timeUnixNano: "1000000" + scope: {} diff --git a/internal/measurements/testdata/logs/w3c-logs.yaml b/internal/measurements/testdata/logs/w3c-logs.yaml new file mode 100644 index 000000000..74eb70c6e --- /dev/null +++ b/internal/measurements/testdata/logs/w3c-logs.yaml @@ -0,0 +1,505 @@ +resourceLogs: + - resource: + attributes: + - key: host.name + value: + stringValue: Brandons-Legit-Windows-PC-Not-From-Mac-I-Swear + - key: os.type + value: + stringValue: windows + schemaUrl: https://opentelemetry.io/schemas/1.6.1 + scopeLogs: + - logRecords: + - attributes: + - key: log_type + value: + stringValue: w3c + - key: log.file.name + value: + stringValue: example.log + body: + kvlistValue: + values: + - key: sc-status + value: + stringValue: "501" + - key: s-sitename + value: + stringValue: red-server + - key: s-computername + value: + stringValue: your-host + - key: s-ip + value: + stringValue: 128.68.153.13 + - key: timestamp + value: + stringValue: 30-01-2024 16:33:54.909660 + - key: cs-method + value: + stringValue: DELETE + observedTimeUnixNano: "1706632435103211000" + spanId: "" + traceId: "" + - attributes: + - key: log_type + value: + stringValue: w3c + - key: log.file.name + value: + stringValue: example.log + body: + stringValue: "This is a string body" + observedTimeUnixNano: "1706632435103211000" + spanId: "" + traceId: "" + - attributes: + - key: log_type + value: + stringValue: w3c + - key: log.file.name + value: + stringValue: example.log + - key: unique-attribute + value: + stringValue: unique-value + body: + kvlistValue: + values: + - key: s-ip + value: + stringValue: 19.25.92.158 + - key: cs-method + value: + stringValue: DELETE + - key: sc-status + value: + stringValue: "500" + - key: timestamp + value: + stringValue: 30-01-2024 16:33:54.920213 + - key: s-sitename + value: + stringValue: blue-server + - key: s-computername + value: + stringValue: my-host + observedTimeUnixNano: "1706632435103340000" + spanId: "" + traceId: "" + scope: {} + - resource: + attributes: + - key: host.name + value: + stringValue: Brandons-MBP + - key: os.type + value: + stringValue: darwin + schemaUrl: https://opentelemetry.io/schemas/1.6.1 + scopeLogs: + - logRecords: + - attributes: + - key: log_type + value: + stringValue: w3c + - key: log.file.name + value: + stringValue: example.log + body: + kvlistValue: + values: + - key: s-ip + value: + stringValue: 209.117.47.210 + - key: cs-method + value: + stringValue: POST + - key: sc-status + value: + stringValue: "200" + - key: timestamp + value: + stringValue: 30-01-2024 16:33:54.760240 + - key: s-sitename + value: + stringValue: stage-server + - key: s-computername + value: + stringValue: my-host + observedTimeUnixNano: "1706632434903385000" + spanId: "" + traceId: "" + - attributes: + - key: log.file.name + value: + stringValue: example.log + - key: log_type + value: + stringValue: w3c + body: + kvlistValue: + values: + - key: sc-status + value: + stringValue: "400" + - key: timestamp + value: + stringValue: 30-01-2024 16:33:54.771780 + - key: s-sitename + value: + stringValue: blue-server + - key: s-computername + value: + stringValue: my-host + - key: s-ip + value: + stringValue: 128.68.153.13 + - key: cs-method + value: + stringValue: POST + observedTimeUnixNano: "1706632434904438000" + spanId: "" + traceId: "" + - attributes: + - key: log.file.name + value: + stringValue: example.log + - key: log_type + value: + stringValue: w3c + body: + kvlistValue: + values: + - key: sc-status + value: + stringValue: "101" + - key: s-sitename + value: + stringValue: production-server + - key: s-computername + value: + stringValue: your-host + - key: s-ip + value: + stringValue: 99.160.143.72 + - key: cs-method + value: + stringValue: GET + - key: timestamp + value: + stringValue: 30-01-2024 16:33:54.782933 + observedTimeUnixNano: "1706632434904726000" + spanId: "" + traceId: "" + - attributes: + - key: log_type + value: + stringValue: w3c + - key: log.file.name + value: + stringValue: example.log + body: + kvlistValue: + values: + - key: timestamp + value: + stringValue: 30-01-2024 16:33:54.793415 + - key: s-sitename + value: + stringValue: dev-server + - key: s-computername + value: + stringValue: ice-box + - key: s-ip + value: + stringValue: 129.90.224.41 + - key: cs-method + value: + stringValue: GET + - key: sc-status + value: + stringValue: "409" + observedTimeUnixNano: "1706632434904962000" + spanId: "" + traceId: "" + - attributes: + - key: log_type + value: + stringValue: w3c + - key: log.file.name + value: + stringValue: example.log + body: + kvlistValue: + values: + - key: s-sitename + value: + stringValue: red-server + - key: s-computername + value: + stringValue: your-host + - key: s-ip + value: + stringValue: 99.160.143.72 + - key: cs-method + value: + stringValue: GET + - key: sc-status + value: + stringValue: "101" + - key: timestamp + value: + stringValue: 30-01-2024 16:33:54.803941 + observedTimeUnixNano: "1706632434905371000" + spanId: "" + traceId: "" + - attributes: + - key: log_type + value: + stringValue: w3c + - key: log.file.name + value: + stringValue: example.log + body: + kvlistValue: + values: + - key: s-sitename + value: + stringValue: stage-server + - key: s-computername + value: + stringValue: my-host + - key: s-ip + value: + stringValue: 87.222.90.184 + - key: cs-method + value: + stringValue: GET + - key: sc-status + value: + stringValue: "501" + - key: timestamp + value: + stringValue: 30-01-2024 16:33:54.814813 + observedTimeUnixNano: "1706632434905657000" + spanId: "" + traceId: "" + - attributes: + - key: log_type + value: + stringValue: w3c + - key: log.file.name + value: + stringValue: example.log + body: + kvlistValue: + values: + - key: s-computername + value: + stringValue: my-host + - key: s-ip + value: + stringValue: 21.36.93.8 + - key: cs-method + value: + stringValue: DELETE + - key: sc-status + value: + stringValue: "400" + - key: timestamp + value: + stringValue: 30-01-2024 16:33:54.825889 + - key: s-sitename + value: + stringValue: stage-server + observedTimeUnixNano: "1706632434905918000" + spanId: "" + traceId: "" + - attributes: + - key: log_type + value: + stringValue: w3c + - key: log.file.name + value: + stringValue: example.log + body: + kvlistValue: + values: + - key: timestamp + value: + stringValue: 30-01-2024 16:33:54.838153 + - key: s-sitename + value: + stringValue: blue-server + - key: s-computername + value: + stringValue: ice-box + - key: s-ip + value: + stringValue: 154.205.58.188 + - key: cs-method + value: + stringValue: PUT + - key: sc-status + value: + stringValue: "409" + observedTimeUnixNano: "1706632434906183000" + spanId: "" + traceId: "" + - attributes: + - key: log_type + value: + stringValue: w3c + - key: log.file.name + value: + stringValue: example.log + body: + kvlistValue: + values: + - key: s-sitename + value: + stringValue: dev-server + - key: timestamp + value: + stringValue: 30-01-2024 16:33:54.850696 + - key: s-computername + value: + stringValue: your-host + - key: s-ip + value: + stringValue: 19.25.92.158 + - key: cs-method + value: + stringValue: GET + - key: sc-status + value: + stringValue: "409" + observedTimeUnixNano: "1706632434906304000" + spanId: "" + traceId: "" + - attributes: + - key: log_type + value: + stringValue: w3c + - key: log.file.name + value: + stringValue: example.log + body: + kvlistValue: + values: + - key: s-ip + value: + stringValue: 99.160.143.72 + - key: cs-method + value: + stringValue: PUT + - key: sc-status + value: + stringValue: "409" + - key: timestamp + value: + stringValue: 30-01-2024 16:33:54.861951 + - key: s-sitename + value: + stringValue: dev-server + - key: s-computername + value: + stringValue: my-host + observedTimeUnixNano: "1706632434906407000" + spanId: "" + traceId: "" + - attributes: + - key: log_type + value: + stringValue: w3c + - key: log.file.name + value: + stringValue: example.log + body: + kvlistValue: + values: + - key: timestamp + value: + stringValue: 30-01-2024 16:33:54.874685 + - key: cs-method + value: + stringValue: DELETE + - key: sc-status + value: + stringValue: "404" + - key: s-sitename + value: + stringValue: production-server + - key: s-computername + value: + stringValue: your-host + - key: s-ip + value: + stringValue: 127.0.0.1 + observedTimeUnixNano: "1706632434906539000" + spanId: "" + traceId: "" + - attributes: + - key: log.file.name + value: + stringValue: example.log + - key: log_type + value: + stringValue: w3c + body: + kvlistValue: + values: + - key: s-computername + value: + stringValue: your-host + - key: s-ip + value: + stringValue: 21.36.93.8 + - key: cs-method + value: + stringValue: POST + - key: sc-status + value: + stringValue: "101" + - key: s-sitename + value: + stringValue: red-server + - key: timestamp + value: + stringValue: 30-01-2024 16:33:54.885779 + observedTimeUnixNano: "1706632434906584000" + spanId: "" + traceId: "" + - attributes: + - key: log_type + value: + stringValue: w3c + - key: log.file.name + value: + stringValue: example.log + body: + kvlistValue: + values: + - key: s-sitename + value: + stringValue: stage-server + - key: s-computername + value: + stringValue: ice-box + - key: timestamp + value: + stringValue: 30-01-2024 16:33:54.897564 + - key: s-ip + value: + stringValue: 19.25.92.158 + - key: cs-method + value: + stringValue: POST + - key: sc-status + value: + stringValue: "409" + observedTimeUnixNano: "1706632434906598000" + spanId: "" + traceId: "" + scope: {} diff --git a/internal/measurements/testdata/metrics/host-metrics.yaml b/internal/measurements/testdata/metrics/host-metrics.yaml new file mode 100644 index 000000000..d42978181 --- /dev/null +++ b/internal/measurements/testdata/metrics/host-metrics.yaml @@ -0,0 +1,737 @@ +resourceMetrics: + - resource: + attributes: + - key: host.name + value: + stringValue: Brandons-Awesome-Linux-Machine + - key: os.type + value: + stringValue: linux + - key: extra-resource-attr-key + value: + stringValue: extra-resource-attr-value + schemaUrl: https://opentelemetry.io/schemas/1.9.0 + scopeMetrics: + - metrics: + - description: Average CPU Load over 1 minute. + gauge: + dataPoints: + - asDouble: 3.71484375 + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + attributes: + - key: cool-attribute-key + value: + stringValue: cool-attribute-value + name: system.cpu.load_average.1m + unit: "{thread}" + scope: + name: otelcol/hostmetricsreceiver/load + version: v1.43.0 + - resource: + attributes: + - key: host.name + value: + stringValue: Brandons-MBP + - key: os.type + value: + stringValue: darwin + schemaUrl: https://opentelemetry.io/schemas/1.9.0 + scopeMetrics: + - metrics: + - description: Filesystem bytes used. + name: system.filesystem.usage + sum: + aggregationTemporality: 2 + dataPoints: + - asInt: "503869440" + attributes: + - key: device + value: + stringValue: /dev/disk2s1 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/iSCPreboot + - key: state + value: + stringValue: free + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: device + value: + stringValue: /dev/disk2s1 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/iSCPreboot + - key: state + value: + stringValue: reserved + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "20418560" + attributes: + - key: device + value: + stringValue: /dev/disk2s1 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/iSCPreboot + - key: state + value: + stringValue: used + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "503869440" + attributes: + - key: device + value: + stringValue: /dev/disk2s2 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/xarts + - key: state + value: + stringValue: free + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: device + value: + stringValue: /dev/disk2s2 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/xarts + - key: state + value: + stringValue: reserved + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "20418560" + attributes: + - key: device + value: + stringValue: /dev/disk2s2 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/xarts + - key: state + value: + stringValue: used + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "503869440" + attributes: + - key: device + value: + stringValue: /dev/disk2s3 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/Hardware + - key: state + value: + stringValue: free + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: device + value: + stringValue: /dev/disk2s3 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/Hardware + - key: state + value: + stringValue: reserved + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "20418560" + attributes: + - key: device + value: + stringValue: /dev/disk2s3 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/Hardware + - key: state + value: + stringValue: used + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "342134198272" + attributes: + - key: device + value: + stringValue: /dev/disk3s1s1 + - key: mode + value: + stringValue: ro + - key: mountpoint + value: + stringValue: / + - key: state + value: + stringValue: free + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: device + value: + stringValue: /dev/disk3s1s1 + - key: mode + value: + stringValue: ro + - key: mountpoint + value: + stringValue: / + - key: state + value: + stringValue: reserved + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "152250597376" + attributes: + - key: device + value: + stringValue: /dev/disk3s1s1 + - key: mode + value: + stringValue: ro + - key: mountpoint + value: + stringValue: / + - key: state + value: + stringValue: used + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "342134198272" + attributes: + - key: device + value: + stringValue: /dev/disk3s2 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/Preboot + - key: state + value: + stringValue: free + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: device + value: + stringValue: /dev/disk3s2 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/Preboot + - key: state + value: + stringValue: reserved + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "152250597376" + attributes: + - key: device + value: + stringValue: /dev/disk3s2 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/Preboot + - key: state + value: + stringValue: used + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "342134198272" + attributes: + - key: device + value: + stringValue: /dev/disk3s4 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/Update + - key: state + value: + stringValue: free + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: device + value: + stringValue: /dev/disk3s4 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/Update + - key: state + value: + stringValue: reserved + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "152250597376" + attributes: + - key: device + value: + stringValue: /dev/disk3s4 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/Update + - key: state + value: + stringValue: used + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "342134198272" + attributes: + - key: device + value: + stringValue: /dev/disk3s5 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/Data + - key: state + value: + stringValue: free + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: device + value: + stringValue: /dev/disk3s5 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/Data + - key: state + value: + stringValue: reserved + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "152250597376" + attributes: + - key: device + value: + stringValue: /dev/disk3s5 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/Data + - key: state + value: + stringValue: used + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "342134198272" + attributes: + - key: device + value: + stringValue: /dev/disk3s6 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/VM + - key: state + value: + stringValue: free + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: device + value: + stringValue: /dev/disk3s6 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/VM + - key: state + value: + stringValue: reserved + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "152250597376" + attributes: + - key: device + value: + stringValue: /dev/disk3s6 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/VM + - key: state + value: + stringValue: used + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "8717185024" + attributes: + - key: extra-sum-attr-key + value: + stringValue: extra-sum-attr-value + - key: device + value: + stringValue: /dev/disk4s1 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /Volumes/transfer + - key: state + value: + stringValue: free + - key: type + value: + stringValue: hfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: device + value: + stringValue: /dev/disk4s1 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /Volumes/transfer + - key: state + value: + stringValue: reserved + - key: type + value: + stringValue: hfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "7409389568" + attributes: + - key: device + value: + stringValue: /dev/disk4s1 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /Volumes/transfer + - key: state + value: + stringValue: used + - key: type + value: + stringValue: hfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "14336" + attributes: + - key: device + value: + stringValue: /dev/disk4s2 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /Volumes/UEFI_NTFS + - key: state + value: + stringValue: free + - key: type + value: + stringValue: msdos + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: device + value: + stringValue: /dev/disk4s2 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /Volumes/UEFI_NTFS + - key: state + value: + stringValue: reserved + - key: type + value: + stringValue: msdos + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "493568" + attributes: + - key: device + value: + stringValue: /dev/disk4s2 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /Volumes/UEFI_NTFS + - key: state + value: + stringValue: used + - key: type + value: + stringValue: msdos + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: device + value: + stringValue: devfs + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /dev + - key: state + value: + stringValue: free + - key: type + value: + stringValue: devfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: device + value: + stringValue: devfs + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /dev + - key: state + value: + stringValue: reserved + - key: type + value: + stringValue: devfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "220672" + attributes: + - key: device + value: + stringValue: devfs + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /dev + - key: state + value: + stringValue: used + - key: type + value: + stringValue: devfs + startTimeUnixNano: "1000000" + timeUnixNano: "3000000" + - asInt: "0" + attributes: + - key: device + value: + stringValue: map auto_home + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/Data/home + - key: state + value: + stringValue: free + - key: type + value: + stringValue: autofs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: device + value: + stringValue: map auto_home + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/Data/home + - key: state + value: + stringValue: reserved + - key: type + value: + stringValue: autofs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: device + value: + stringValue: map auto_home + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/Data/home + - key: state + value: + stringValue: used + - key: type + value: + stringValue: autofs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + unit: By + scope: + name: otelcol/hostmetricsreceiver/filesystem + version: v1.43.0 diff --git a/internal/measurements/testdata/traces/bindplane-traces.yaml b/internal/measurements/testdata/traces/bindplane-traces.yaml new file mode 100644 index 000000000..e15905b84 --- /dev/null +++ b/internal/measurements/testdata/traces/bindplane-traces.yaml @@ -0,0 +1,1530 @@ +resourceSpans: + - resource: + attributes: + - key: host.arch + value: + stringValue: arm64 + - key: host.name + value: + stringValue: Sams-M1-Pro.local + - key: service.name + value: + stringValue: bindplane + - key: service.version + value: + stringValue: unknown + scopeSpans: + - scope: {} + spans: + - endTimeUnixNano: "1706791445370505958" + kind: 1 + name: featuregatedstore/ProcessorType + parentSpanId: 01f07757e7bb6612 + spanId: 5d85207073e1d06b + startTimeUnixNano: "1706791445369534000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445371594000" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: 1e9c016ee3394bea + spanId: 39f855dc7dbd43ae + startTimeUnixNano: "1706791445371218000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445371594459" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: aa50d71d28f47370 + spanId: 1e9c016ee3394bea + startTimeUnixNano: "1706791445370893000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445371595166" + kind: 1 + name: pgstore/pgResource + parentSpanId: 560da73813316270 + spanId: aa50d71d28f47370 + startTimeUnixNano: "1706791445370893000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445371594708" + kind: 1 + name: pgstore/SourceType + parentSpanId: 0f3367e6b090ffed + spanId: 560da73813316270 + startTimeUnixNano: "1706791445370892000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445371595959" + kind: 1 + name: featuregatedstore/SourceType + parentSpanId: fd55f461239efdfc + spanId: 0f3367e6b090ffed + startTimeUnixNano: "1706791445370892000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445371621792" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: 5aceab5faac84e72 + spanId: 34db33f306cd28d6 + startTimeUnixNano: "1706791445371252000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445371622334" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: 96cebc89a09695f9 + spanId: 5aceab5faac84e72 + startTimeUnixNano: "1706791445370905000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445371621958" + kind: 1 + name: pgstore/pgResource + parentSpanId: ed9b4f8caac8c99e + spanId: 96cebc89a09695f9 + startTimeUnixNano: "1706791445370904000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445371622458" + kind: 1 + name: pgstore/SourceType + parentSpanId: ac5df0c7e93047f1 + spanId: ed9b4f8caac8c99e + startTimeUnixNano: "1706791445370904000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445371623000" + kind: 1 + name: featuregatedstore/SourceType + parentSpanId: 01f07757e7bb6612 + spanId: ac5df0c7e93047f1 + startTimeUnixNano: "1706791445370903000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445372255042" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: ac1591195af28043 + spanId: 439f48f98419cd32 + startTimeUnixNano: "1706791445372193000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445372256000" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: 7a0d816f78afc130 + spanId: ac1591195af28043 + startTimeUnixNano: "1706791445371916000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445372255708" + kind: 1 + name: pgstore/pgResource + parentSpanId: fa26c6c00e05b5eb + spanId: 7a0d816f78afc130 + startTimeUnixNano: "1706791445371915000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445372255500" + kind: 1 + name: pgstore/Destination + parentSpanId: fd55f461239efdfc + spanId: fa26c6c00e05b5eb + startTimeUnixNano: "1706791445371914000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445372285833" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: fd5ad671cccf90ac + spanId: bb03baad52e8e234 + startTimeUnixNano: "1706791445372232000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445372286291" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: 9362c54ca8d2781f + spanId: fd5ad671cccf90ac + startTimeUnixNano: "1706791445371921000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445372286917" + kind: 1 + name: pgstore/pgResource + parentSpanId: 84b85b95e4e3302c + spanId: 9362c54ca8d2781f + startTimeUnixNano: "1706791445371921000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445372286750" + kind: 1 + name: pgstore/Destination + parentSpanId: 01f07757e7bb6612 + spanId: 84b85b95e4e3302c + startTimeUnixNano: "1706791445371920000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445372638125" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: 5caae46954be319e + spanId: b064b57576599977 + startTimeUnixNano: "1706791445372515000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445372639250" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: 6a0757daee65f85d + spanId: 5caae46954be319e + startTimeUnixNano: "1706791445372258000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445372639833" + kind: 1 + name: pgstore/pgResource + parentSpanId: 87ec389ec0a90e57 + spanId: 6a0757daee65f85d + startTimeUnixNano: "1706791445372258000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445372639375" + kind: 1 + name: pgstore/DestinationType + parentSpanId: 3d1d65aa7a6498e2 + spanId: 87ec389ec0a90e57 + startTimeUnixNano: "1706791445372257000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445372640333" + kind: 1 + name: featuregatedstore/DestinationType + parentSpanId: fd55f461239efdfc + spanId: 3d1d65aa7a6498e2 + startTimeUnixNano: "1706791445372257000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445372673791" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: ac718a9ec3ef8ce8 + spanId: 8759e7f478277358 + startTimeUnixNano: "1706791445372561000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445372674042" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: 547d3cefd8be11ba + spanId: ac718a9ec3ef8ce8 + startTimeUnixNano: "1706791445372289000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445372674709" + kind: 1 + name: pgstore/pgResource + parentSpanId: cc75d9bcab91629f + spanId: 547d3cefd8be11ba + startTimeUnixNano: "1706791445372289000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445372674208" + kind: 1 + name: pgstore/DestinationType + parentSpanId: 98bca1b4ce950e6a + spanId: cc75d9bcab91629f + startTimeUnixNano: "1706791445372288000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445372675000" + kind: 1 + name: featuregatedstore/DestinationType + parentSpanId: 01f07757e7bb6612 + spanId: 98bca1b4ce950e6a + startTimeUnixNano: "1706791445372288000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445373307000" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: 31427ef8f1cdb87e + spanId: 4c5347222741d80a + startTimeUnixNano: "1706791445373202000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445373310333" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: 623a6ef09a05d78e + spanId: 31427ef8f1cdb87e + startTimeUnixNano: "1706791445372919000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445373310958" + kind: 1 + name: pgstore/pgResource + parentSpanId: eabea48cc976c26c + spanId: 623a6ef09a05d78e + startTimeUnixNano: "1706791445372919000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445373310584" + kind: 1 + name: pgstore/ProcessorType + parentSpanId: 59c6fbff32109911 + spanId: eabea48cc976c26c + startTimeUnixNano: "1706791445372918000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445373311500" + kind: 1 + name: featuregatedstore/ProcessorType + parentSpanId: 01f07757e7bb6612 + spanId: 59c6fbff32109911 + startTimeUnixNano: "1706791445372918000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445373317417" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: 81d1379f833e45e4 + spanId: af9f808e0386dcc1 + startTimeUnixNano: "1706791445373215000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445373318125" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: 8c1b5f014af598db + spanId: 81d1379f833e45e4 + startTimeUnixNano: "1706791445372895000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445373317791" + kind: 1 + name: pgstore/pgResource + parentSpanId: ec1965c48fa74dd8 + spanId: 8c1b5f014af598db + startTimeUnixNano: "1706791445372894000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445373318500" + kind: 1 + name: pgstore/ProcessorType + parentSpanId: 2be401733ad05f07 + spanId: ec1965c48fa74dd8 + startTimeUnixNano: "1706791445372894000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445373318417" + kind: 1 + name: featuregatedstore/ProcessorType + parentSpanId: fd55f461239efdfc + spanId: 2be401733ad05f07 + startTimeUnixNano: "1706791445372893000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445373741542" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: 1073bab272ed8d99 + spanId: fe61a5e73fdfcd54 + startTimeUnixNano: "1706791445373681000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445373742542" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: 6482ba65fc5d6aea + spanId: 1073bab272ed8d99 + startTimeUnixNano: "1706791445373405000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445373742291" + kind: 1 + name: pgstore/pgResource + parentSpanId: 8a7b5c2c9a64b49b + spanId: 6482ba65fc5d6aea + startTimeUnixNano: "1706791445373404000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445373743042" + kind: 1 + name: pgstore/Destination + parentSpanId: fd55f461239efdfc + spanId: 8a7b5c2c9a64b49b + startTimeUnixNano: "1706791445373404000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445373755459" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: 92b6c441dbaf26e7 + spanId: 73cf26fff96c6144 + startTimeUnixNano: "1706791445373702000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445373756125" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: b765ba46b31ea2b7 + spanId: 92b6c441dbaf26e7 + startTimeUnixNano: "1706791445373395000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445373756125" + kind: 1 + name: pgstore/pgResource + parentSpanId: edfdd26910042116 + spanId: b765ba46b31ea2b7 + startTimeUnixNano: "1706791445373394000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445373756042" + kind: 1 + name: pgstore/Destination + parentSpanId: 01f07757e7bb6612 + spanId: edfdd26910042116 + startTimeUnixNano: "1706791445373393000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445374104125" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: ee6eba613911c7f7 + spanId: 9838adef042dc27f + startTimeUnixNano: "1706791445373993000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445374105625" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: 8fd1f88bb661dbbf + spanId: ee6eba613911c7f7 + startTimeUnixNano: "1706791445373745000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445374105292" + kind: 1 + name: pgstore/pgResource + parentSpanId: 4518fe3e7e061d28 + spanId: 8fd1f88bb661dbbf + startTimeUnixNano: "1706791445373744000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445374106000" + kind: 1 + name: pgstore/DestinationType + parentSpanId: dc8f0547997bbf7a + spanId: 4518fe3e7e061d28 + startTimeUnixNano: "1706791445373744000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445374106125" + kind: 1 + name: featuregatedstore/DestinationType + parentSpanId: fd55f461239efdfc + spanId: dc8f0547997bbf7a + startTimeUnixNano: "1706791445373743000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445374128917" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: 93e5965556047830 + spanId: b3a494cb25e7968e + startTimeUnixNano: "1706791445374013000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445374128916" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: 48e733575bb79f9a + spanId: 93e5965556047830 + startTimeUnixNano: "1706791445373758000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445374129583" + kind: 1 + name: pgstore/pgResource + parentSpanId: ab1f6cb09be6fd07 + spanId: 48e733575bb79f9a + startTimeUnixNano: "1706791445373758000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445374129084" + kind: 1 + name: pgstore/DestinationType + parentSpanId: 2a2838388f079090 + spanId: ab1f6cb09be6fd07 + startTimeUnixNano: "1706791445373757000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445374129917" + kind: 1 + name: featuregatedstore/DestinationType + parentSpanId: 01f07757e7bb6612 + spanId: 2a2838388f079090 + startTimeUnixNano: "1706791445373757000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445375289875" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: 58eb2369bc29028b + spanId: 3d367cddad3982cb + startTimeUnixNano: "1706791445374690000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445375291042" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: 8b48d61bb3bd84b5 + spanId: 58eb2369bc29028b + startTimeUnixNano: "1706791445374349000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445375291667" + kind: 1 + name: pgstore/pgResource + parentSpanId: ea1323fc9fa7bbfb + spanId: 8b48d61bb3bd84b5 + startTimeUnixNano: "1706791445374349000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445375291291" + kind: 1 + name: pgstore/ProcessorType + parentSpanId: 883ad1808f1225df + spanId: ea1323fc9fa7bbfb + startTimeUnixNano: "1706791445374348000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445375292333" + kind: 1 + name: featuregatedstore/ProcessorType + parentSpanId: fd55f461239efdfc + spanId: 883ad1808f1225df + startTimeUnixNano: "1706791445374348000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445375292458" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: 1324f3c06efff713 + spanId: 8b340a6af31402a6 + startTimeUnixNano: "1706791445374683000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445375293083" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: 0389a1ce82df8073 + spanId: 1324f3c06efff713 + startTimeUnixNano: "1706791445374369000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445375292916" + kind: 1 + name: pgstore/pgResource + parentSpanId: fbaba0d15c7fb273 + spanId: 0389a1ce82df8073 + startTimeUnixNano: "1706791445374368000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445375293750" + kind: 1 + name: pgstore/ProcessorType + parentSpanId: 2ec705763a482c63 + spanId: fbaba0d15c7fb273 + startTimeUnixNano: "1706791445374368000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445375293708" + kind: 1 + name: featuregatedstore/ProcessorType + parentSpanId: 01f07757e7bb6612 + spanId: 2ec705763a482c63 + startTimeUnixNano: "1706791445374367000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445376188917" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: aa9bc90319da6537 + spanId: d895d7bb7f1e1e22 + startTimeUnixNano: "1706791445376038000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445376190083" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: a5f8b395ad89fb20 + spanId: aa9bc90319da6537 + startTimeUnixNano: "1706791445375734000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445376190834" + kind: 1 + name: pgstore/pgResource + parentSpanId: cdff48ed0c3e72ae + spanId: a5f8b395ad89fb20 + startTimeUnixNano: "1706791445375734000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445376190500" + kind: 1 + name: pgstore/ProcessorType + parentSpanId: b67f47ab8ceb6c2c + spanId: cdff48ed0c3e72ae + startTimeUnixNano: "1706791445375733000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445376191584" + kind: 1 + name: featuregatedstore/ProcessorType + parentSpanId: fd55f461239efdfc + spanId: b67f47ab8ceb6c2c + startTimeUnixNano: "1706791445375733000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - endTimeUnixNano: "1706791445376214000" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: eff5bf5ccd3ab227 + spanId: d501cddc5c10bb68 + startTimeUnixNano: "1706791445376044000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445376214125" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: 3e9b9f81e55ccd89 + spanId: eff5bf5ccd3ab227 + startTimeUnixNano: "1706791445375749000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445376213958" + kind: 1 + name: pgstore/pgResource + parentSpanId: 220b7c5429a375d4 + spanId: 3e9b9f81e55ccd89 + startTimeUnixNano: "1706791445375748000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445376214708" + kind: 1 + name: pgstore/ProcessorType + parentSpanId: 924c1623bafaba3b + spanId: 220b7c5429a375d4 + startTimeUnixNano: "1706791445375748000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445376214834" + kind: 1 + name: featuregatedstore/ProcessorType + parentSpanId: 01f07757e7bb6612 + spanId: 924c1623bafaba3b + startTimeUnixNano: "1706791445375747000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - endTimeUnixNano: "1706791445912660750" + kind: 1 + name: pgstore/getConfigurationIndex + parentSpanId: 9e9edb4efbb989ef + spanId: 18a40a19cbb3e254 + startTimeUnixNano: "1706791445912659000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445912667166" + kind: 1 + name: pgstore/ConfigurationIndex + parentSpanId: 9d5a7e824fa7ba3b + spanId: 9e9edb4efbb989ef + startTimeUnixNano: "1706791445912658000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445914167041" + kind: 1 + name: pgstore/acquireAdvisoryLock + parentSpanId: f58f50240bd0ebcb + spanId: b296ae6c001f640a + startTimeUnixNano: "1706791445913897000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445916449917" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: cce7e1ec6abeb8dc + spanId: 5f43117727ebc00e + startTimeUnixNano: "1706791445915074000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445916456958" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: f58f50240bd0ebcb + spanId: cce7e1ec6abeb8dc + startTimeUnixNano: "1706791445914332000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445919373083" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: cc9471536eccd5db + spanId: 3536cf01c143e85f + startTimeUnixNano: "1706791445918350000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445919379208" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: bc09affd2efa24ae + spanId: cc9471536eccd5db + startTimeUnixNano: "1706791445917392000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445919379917" + kind: 1 + name: pgstore/pgResource + parentSpanId: 09ef41113f7d7845 + spanId: bc09affd2efa24ae + startTimeUnixNano: "1706791445917390000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445919490917" + kind: 1 + name: pgstore/pgEditConfiguration + parentSpanId: f58f50240bd0ebcb + spanId: 09ef41113f7d7845 + startTimeUnixNano: "1706791445917387000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445919633917" + kind: 1 + name: pgstore/addTransitiveUpdates + parentSpanId: 5fc019ad71775af4 + spanId: aa56c92fbc435074 + startTimeUnixNano: "1706791445919632000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445919634792" + kind: 1 + name: pgstore/notify + parentSpanId: f58f50240bd0ebcb + spanId: 5fc019ad71775af4 + startTimeUnixNano: "1706791445919629000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445919906375" + kind: 1 + name: pgstore/releaseAdvisoryLock + parentSpanId: f58f50240bd0ebcb + spanId: 185ab719965503f6 + startTimeUnixNano: "1706791445919637000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445919909125" + kind: 1 + name: pgstore/UpdateRollout + parentSpanId: 9d5a7e824fa7ba3b + spanId: f58f50240bd0ebcb + startTimeUnixNano: "1706791445913891000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445920184208" + kind: 1 + name: pgstore/acquireAdvisoryLock + parentSpanId: 343173155818d664 + spanId: 61c28a1ebe9a64e8 + startTimeUnixNano: "1706791445919918000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445921631916" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: c85313d8e38e60c0 + spanId: 1db8a9899aca2909 + startTimeUnixNano: "1706791445920986000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445921637667" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: 343173155818d664 + spanId: c85313d8e38e60c0 + startTimeUnixNano: "1706791445920299000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445923689583" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: ea20cd55767a0599 + spanId: dee65f909bbc0d64 + startTimeUnixNano: "1706791445923120000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445923695125" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: 0cc1b95036e397be + spanId: ea20cd55767a0599 + startTimeUnixNano: "1706791445922423000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445923696417" + kind: 1 + name: pgstore/pgResource + parentSpanId: 849c53c707b9f279 + spanId: 0cc1b95036e397be + startTimeUnixNano: "1706791445922422000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445923777000" + kind: 1 + name: pgstore/pgEditConfiguration + parentSpanId: 343173155818d664 + spanId: 849c53c707b9f279 + startTimeUnixNano: "1706791445922419000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445923905459" + kind: 1 + name: pgstore/addTransitiveUpdates + parentSpanId: 845f46aa58bbcb19 + spanId: dead9c5716ba621d + startTimeUnixNano: "1706791445923904000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445923907417" + kind: 1 + name: pgstore/notify + parentSpanId: 343173155818d664 + spanId: 845f46aa58bbcb19 + startTimeUnixNano: "1706791445923902000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445924146459" + kind: 1 + name: pgstore/releaseAdvisoryLock + parentSpanId: 343173155818d664 + spanId: 17c792e43c54f830 + startTimeUnixNano: "1706791445923908000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445924148792" + kind: 1 + name: pgstore/UpdateRollout + parentSpanId: 9d5a7e824fa7ba3b + spanId: 343173155818d664 + startTimeUnixNano: "1706791445919913000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445924361000" + kind: 1 + name: pgstore/acquireAdvisoryLock + parentSpanId: 38ff7d679d77bdbd + spanId: b6e7f0ae7967d288 + startTimeUnixNano: "1706791445924159000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445925556834" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: 48c2cef07ce74c65 + spanId: 83e3224e213642fa + startTimeUnixNano: "1706791445925082000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445925565458" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: 38ff7d679d77bdbd + spanId: 48c2cef07ce74c65 + startTimeUnixNano: "1706791445924467000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445927498625" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: 6b63f670942e88f2 + spanId: aa9d17b7b3c4ea09 + startTimeUnixNano: "1706791445926821000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445927501542" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: 88ff93a450488c13 + spanId: 6b63f670942e88f2 + startTimeUnixNano: "1706791445926216000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445927501625" + kind: 1 + name: pgstore/pgResource + parentSpanId: 1e96a9988bef1b05 + spanId: 88ff93a450488c13 + startTimeUnixNano: "1706791445926214000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445927556459" + kind: 1 + name: pgstore/pgEditConfiguration + parentSpanId: 38ff7d679d77bdbd + spanId: 1e96a9988bef1b05 + startTimeUnixNano: "1706791445926212000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445927702458" + kind: 1 + name: pgstore/addTransitiveUpdates + parentSpanId: b6e1d82a58e8fd61 + spanId: 5a055056ad7713e5 + startTimeUnixNano: "1706791445927700000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445927704250" + kind: 1 + name: pgstore/notify + parentSpanId: 38ff7d679d77bdbd + spanId: b6e1d82a58e8fd61 + startTimeUnixNano: "1706791445927698000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445928022709" + kind: 1 + name: pgstore/releaseAdvisoryLock + parentSpanId: 38ff7d679d77bdbd + spanId: 1a79a3b2c24f43db + startTimeUnixNano: "1706791445927705000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445928025459" + kind: 1 + name: pgstore/UpdateRollout + parentSpanId: 9d5a7e824fa7ba3b + spanId: 38ff7d679d77bdbd + startTimeUnixNano: "1706791445924153000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445928253125" + kind: 1 + name: pgstore/acquireAdvisoryLock + parentSpanId: f667fcb4d9278ef3 + spanId: bd29d8d73527c437 + startTimeUnixNano: "1706791445928036000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445929417833" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: fc0c17bdf1248a4b + spanId: 850448ca72ab8c7e + startTimeUnixNano: "1706791445928966000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445929422500" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: f667fcb4d9278ef3 + spanId: fc0c17bdf1248a4b + startTimeUnixNano: "1706791445928344000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445931223792" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: b4fdd461cf7cff72 + spanId: 3db6d250c2670120 + startTimeUnixNano: "1706791445930719000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445931226000" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: c3b8a5e5232d4f97 + spanId: b4fdd461cf7cff72 + startTimeUnixNano: "1706791445930124000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445931227292" + kind: 1 + name: pgstore/pgResource + parentSpanId: 9058a7f1ddbdb478 + spanId: c3b8a5e5232d4f97 + startTimeUnixNano: "1706791445930123000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445931289667" + kind: 1 + name: pgstore/pgEditConfiguration + parentSpanId: f667fcb4d9278ef3 + spanId: 9058a7f1ddbdb478 + startTimeUnixNano: "1706791445930120000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445931530167" + kind: 1 + name: pgstore/addTransitiveUpdates + parentSpanId: 174ceb1bea9655e4 + spanId: 64bdb41cf5cf5488 + startTimeUnixNano: "1706791445931528000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445931531541" + kind: 1 + name: pgstore/notify + parentSpanId: f667fcb4d9278ef3 + spanId: 174ceb1bea9655e4 + startTimeUnixNano: "1706791445931525000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445931859917" + kind: 1 + name: pgstore/releaseAdvisoryLock + parentSpanId: f667fcb4d9278ef3 + spanId: a5d5cb6812b504eb + startTimeUnixNano: "1706791445931533000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445931865666" + kind: 1 + name: pgstore/UpdateRollout + parentSpanId: 9d5a7e824fa7ba3b + spanId: f667fcb4d9278ef3 + startTimeUnixNano: "1706791445928032000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445932189291" + kind: 1 + name: pgstore/acquireAdvisoryLock + parentSpanId: a502177e675181ba + spanId: 742646f2a069a533 + startTimeUnixNano: "1706791445931875000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445933524042" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: d8578a54be7b449a + spanId: af80a97174fb9690 + startTimeUnixNano: "1706791445933082000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445933525458" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: a502177e675181ba + spanId: d8578a54be7b449a + startTimeUnixNano: "1706791445932312000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445935873208" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: 83a7c9b53a487359 + spanId: 35563160cfcceaed + startTimeUnixNano: "1706791445935438000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445935876667" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: b1f35deb03b68692 + spanId: 83a7c9b53a487359 + startTimeUnixNano: "1706791445934726000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445935877375" + kind: 1 + name: pgstore/pgResource + parentSpanId: d4cc9c9ef3675f58 + spanId: b1f35deb03b68692 + startTimeUnixNano: "1706791445934724000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445935973000" + kind: 1 + name: pgstore/pgEditConfiguration + parentSpanId: a502177e675181ba + spanId: d4cc9c9ef3675f58 + startTimeUnixNano: "1706791445934719000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445936170958" + kind: 1 + name: pgstore/addTransitiveUpdates + parentSpanId: dc67925deaac8e96 + spanId: 67dc50cbcf1a0507 + startTimeUnixNano: "1706791445936169000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445936172167" + kind: 1 + name: pgstore/notify + parentSpanId: a502177e675181ba + spanId: dc67925deaac8e96 + startTimeUnixNano: "1706791445936167000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445937520417" + kind: 1 + name: pgstore/releaseAdvisoryLock + parentSpanId: a502177e675181ba + spanId: 78bbc878254952f1 + startTimeUnixNano: "1706791445936174000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445937527333" + kind: 1 + name: pgstore/UpdateRollout + parentSpanId: 9d5a7e824fa7ba3b + spanId: a502177e675181ba + startTimeUnixNano: "1706791445931870000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445937820041" + kind: 1 + name: pgstore/acquireAdvisoryLock + parentSpanId: a894c9beda2d173a + spanId: 942d1791aef3d492 + startTimeUnixNano: "1706791445937543000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445961888125" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: a87163b208c61459 + spanId: 0ccc6475795e4cc0 + startTimeUnixNano: "1706791445960465000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445961896542" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: a894c9beda2d173a + spanId: a87163b208c61459 + startTimeUnixNano: "1706791445937922000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445973101000" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: 804ce3fb1b57be5d + spanId: 3eadb90414f2cf22 + startTimeUnixNano: "1706791445971633000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445973109084" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: 5236aa938eb9341c + spanId: 804ce3fb1b57be5d + startTimeUnixNano: "1706791445962593000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445973110334" + kind: 1 + name: pgstore/pgResource + parentSpanId: ca3b8e53681a9e8d + spanId: 5236aa938eb9341c + startTimeUnixNano: "1706791445962592000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445973258875" + kind: 1 + name: pgstore/pgEditConfiguration + parentSpanId: a894c9beda2d173a + spanId: ca3b8e53681a9e8d + startTimeUnixNano: "1706791445962589000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445973576917" + kind: 1 + name: pgstore/addTransitiveUpdates + parentSpanId: 94557eef510d0814 + spanId: c3ea8f993f9009ba + startTimeUnixNano: "1706791445973575000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445973579333" + kind: 1 + name: pgstore/notify + parentSpanId: a894c9beda2d173a + spanId: 94557eef510d0814 + startTimeUnixNano: "1706791445973572000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445975115042" + kind: 1 + name: pgstore/releaseAdvisoryLock + parentSpanId: a894c9beda2d173a + spanId: 8906c43abedb6bd9 + startTimeUnixNano: "1706791445973581000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445975119416" + kind: 1 + name: pgstore/UpdateRollout + parentSpanId: 9d5a7e824fa7ba3b + spanId: a894c9beda2d173a + startTimeUnixNano: "1706791445937536000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445975734417" + kind: 1 + name: pgstore/acquireAdvisoryLock + parentSpanId: 078ab0ab7eb707b2 + spanId: f141ff98614cfc0c + startTimeUnixNano: "1706791445975133000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445979767041" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: f06908731ed62890 + spanId: e185a7e1c60473b8 + startTimeUnixNano: "1706791445976351000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445979799208" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: 078ab0ab7eb707b2 + spanId: f06908731ed62890 + startTimeUnixNano: "1706791445975818000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445989063500" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: 484272979ab6993a + spanId: f0e946762f0f7fe4 + startTimeUnixNano: "1706791445982071000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445989071125" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: 1f3d24377d56ef1a + spanId: 484272979ab6993a + startTimeUnixNano: "1706791445981428000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445989073000" + kind: 1 + name: pgstore/pgResource + parentSpanId: f2caaa5dba4d7692 + spanId: 1f3d24377d56ef1a + startTimeUnixNano: "1706791445981427000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445995086875" + kind: 1 + name: pgstore/pgEditConfiguration + parentSpanId: 078ab0ab7eb707b2 + spanId: f2caaa5dba4d7692 + startTimeUnixNano: "1706791445981424000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445995678750" + kind: 1 + name: pgstore/addTransitiveUpdates + parentSpanId: d2cd0bbf6e7fd7b6 + spanId: 0f0e2864b0289733 + startTimeUnixNano: "1706791445995677000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445995680917" + kind: 1 + name: pgstore/notify + parentSpanId: 078ab0ab7eb707b2 + spanId: d2cd0bbf6e7fd7b6 + startTimeUnixNano: "1706791445995673000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445996202250" + kind: 1 + name: pgstore/releaseAdvisoryLock + parentSpanId: 078ab0ab7eb707b2 + spanId: 6a342964854bdad1 + startTimeUnixNano: "1706791445995682000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445996208000" + kind: 1 + name: pgstore/UpdateRollout + parentSpanId: 9d5a7e824fa7ba3b + spanId: 078ab0ab7eb707b2 + startTimeUnixNano: "1706791445975127000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445996209292" + kind: 1 + name: pgstore/UpdateRollouts + parentSpanId: aeb2a416b8796cba + spanId: 9d5a7e824fa7ba3b + startTimeUnixNano: "1706791445912654000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445996222333" + kind: 1 + name: pgstore/getConfigurationIndex + parentSpanId: 5f2c62ec22a5831f + spanId: 5920438aa93a6790 + startTimeUnixNano: "1706791445996221000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445996223000" + kind: 1 + name: pgstore/ConfigurationIndex + parentSpanId: 96ae55c03e5146b3 + spanId: 5f2c62ec22a5831f + startTimeUnixNano: "1706791445996220000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445997209625" + kind: 1 + name: pgstore/acquireAdvisoryLock + parentSpanId: 624db3b2e1cc7f20 + spanId: 70d631526845aa24 + startTimeUnixNano: "1706791445997026000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445998158167" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: e936bad42c7c2620 + spanId: 758070ce974346be + startTimeUnixNano: "1706791445997877000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445998161958" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: 624db3b2e1cc7f20 + spanId: e936bad42c7c2620 + startTimeUnixNano: "1706791445997305000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445999155375" + kind: 1 + name: pgstore/scanPostgresResource + parentSpanId: f2f75b973ffbece4 + spanId: e87d48de1c7ee8c0 + startTimeUnixNano: "1706791445998945000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445999158708" + kind: 1 + name: pgstore/pgResourceInternal + parentSpanId: 494fbd3596fa4bb7 + spanId: f2f75b973ffbece4 + startTimeUnixNano: "1706791445998570000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445999159250" + kind: 1 + name: pgstore/pgResource + parentSpanId: 9b1bf4c6a7ee0b64 + spanId: 494fbd3596fa4bb7 + startTimeUnixNano: "1706791445998569000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445999195916" + kind: 1 + name: pgstore/pgEditConfiguration + parentSpanId: 624db3b2e1cc7f20 + spanId: 9b1bf4c6a7ee0b64 + startTimeUnixNano: "1706791445998568000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445999284667" + kind: 1 + name: pgstore/addTransitiveUpdates + parentSpanId: bd51cf7f70c9e6ec + spanId: e46d93a1301ab208 + startTimeUnixNano: "1706791445999284000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445999285208" + kind: 1 + name: pgstore/notify + parentSpanId: 624db3b2e1cc7f20 + spanId: bd51cf7f70c9e6ec + startTimeUnixNano: "1706791445999283000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445999453875" + kind: 1 + name: pgstore/releaseAdvisoryLock + parentSpanId: 624db3b2e1cc7f20 + spanId: 92acfcfbc5386ef6 + startTimeUnixNano: "1706791445999286000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445999455625" + kind: 1 + name: pgstore/UpdateRollout + parentSpanId: 96ae55c03e5146b3 + spanId: 624db3b2e1cc7f20 + startTimeUnixNano: "1706791445997022000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445999455959" + kind: 1 + name: pgstore/UpdateRollouts + parentSpanId: aeb2a416b8796cba + spanId: 96ae55c03e5146b3 + startTimeUnixNano: "1706791445996218000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445999459125" + kind: 1 + name: pgstore/UpdateAllRollouts + parentSpanId: "" + spanId: aeb2a416b8796cba + startTimeUnixNano: "1706791445908223000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - scope: {} + spans: + - attributes: + - key: operation + value: + stringValue: GetConfiguration + endTimeUnixNano: "1706791445376564375" + kind: 1 + name: graphql/GetConfiguration/response + parentSpanId: 723c3f6eb4457b5c + spanId: fd55f461239efdfc + startTimeUnixNano: "1706791445359466000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - attributes: + - key: operation + value: + stringValue: GetConfiguration + endTimeUnixNano: "1706791445376589750" + kind: 1 + name: graphql/GetConfiguration/response + parentSpanId: 3e7909bbebcae0ba + spanId: 01f07757e7bb6612 + startTimeUnixNano: "1706791445359560000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - scope: {} + spans: + - attributes: + - key: http.method + value: + stringValue: POST + - key: http.scheme + value: + stringValue: http + - key: net.host.name + value: + stringValue: bindplane + - key: net.host.port + value: + intValue: "3001" + - key: net.sock.peer.addr + value: + stringValue: 127.0.0.1 + - key: net.sock.peer.port + value: + intValue: "50141" + - key: user_agent.original + value: + stringValue: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:123.0) Gecko/20100101 Firefox/123.0 + - key: http.client_ip + value: + stringValue: 127.0.0.1 + - key: http.target + value: + stringValue: /v1/graphql + - key: net.protocol.version + value: + stringValue: "1.1" + - key: http.route + value: + stringValue: /v1/graphql + - key: http.status_code + value: + intValue: "200" + endTimeUnixNano: "1706791445376694750" + kind: 2 + name: /v1/graphql + parentSpanId: "" + spanId: 723c3f6eb4457b5c + startTimeUnixNano: "1706791445332980000" + status: {} + traceId: a3fbd5dc5db5e1734cb54419ca540b66 + - attributes: + - key: http.method + value: + stringValue: POST + - key: http.scheme + value: + stringValue: http + - key: net.host.name + value: + stringValue: bindplane + - key: net.host.port + value: + intValue: "3001" + - key: net.sock.peer.addr + value: + stringValue: 127.0.0.1 + - key: net.sock.peer.port + value: + intValue: "50140" + - key: user_agent.original + value: + stringValue: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:123.0) Gecko/20100101 Firefox/123.0 + - key: http.client_ip + value: + stringValue: 127.0.0.1 + - key: http.target + value: + stringValue: /v1/graphql + - key: net.protocol.version + value: + stringValue: "1.1" + - key: http.route + value: + stringValue: /v1/graphql + - key: http.status_code + value: + intValue: "200" + endTimeUnixNano: "1706791445376708291" + kind: 2 + name: /v1/graphql + parentSpanId: "" + spanId: 3e7909bbebcae0ba + startTimeUnixNano: "1706791445332972000" + status: {} + traceId: d70c2b5eea8977bb8a0712f8c2a1fcb4 + - scope: {} + spans: + - endTimeUnixNano: "1706791445913878000" + kind: 1 + name: pgindex/Suggestions + parentSpanId: 9d5a7e824fa7ba3b + spanId: 4c2049c4cd14c987 + startTimeUnixNano: "1706791445912675000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 + - endTimeUnixNano: "1706791445997017791" + kind: 1 + name: pgindex/Suggestions + parentSpanId: 96ae55c03e5146b3 + spanId: aa69c45bc0970c2f + startTimeUnixNano: "1706791445996229000" + status: {} + traceId: c7f3bb6aa9e7a7dce92d85d1566f2c31 diff --git a/internal/measurements/throughput.go b/internal/measurements/throughput.go new file mode 100644 index 000000000..0380921ea --- /dev/null +++ b/internal/measurements/throughput.go @@ -0,0 +1,259 @@ +// Copyright observIQ, Inc. +// +// 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 measurements provides code to help manage throughput measurements for BindPlane and +// the throughput measurement processor. +package measurements + +import ( + "context" + "fmt" + "sync" + "sync/atomic" + + "go.opentelemetry.io/collector/pdata/plog" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/collector/pdata/ptrace" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/metric" +) + +// ThroughputMeasurementsRegistry represents a registry for the throughputmeasurement processor to +// register their ThroughputMeasurements. +type ThroughputMeasurementsRegistry interface { + RegisterThroughputMeasurements(processorID string, measurements *ThroughputMeasurements) +} + +// ThroughputMeasurements represents all captured throughput metrics. +// It allows for incrementing and querying the current values of throughtput metrics +type ThroughputMeasurements struct { + logSize, metricSize, traceSize *int64Counter + logCount, datapointCount, spanCount *int64Counter + attributes attribute.Set +} + +// NewThroughputMeasurements initializes a new ThroughputMeasurements, adding metrics for the measurements to the meter provider. +func NewThroughputMeasurements(mp metric.MeterProvider, processorID string, extraAttributes map[string]string) (*ThroughputMeasurements, error) { + meter := mp.Meter("github.com/observiq/bindplane-agent/internal/measurements") + + logSize, err := meter.Int64Counter( + metricName("log_data_size"), + metric.WithDescription("Size of the log package passed to the processor"), + metric.WithUnit("By"), + ) + if err != nil { + return nil, fmt.Errorf("create log_data_size counter: %w", err) + } + + metricSize, err := meter.Int64Counter( + metricName("metric_data_size"), + metric.WithDescription("Size of the metric package passed to the processor"), + metric.WithUnit("By"), + ) + if err != nil { + return nil, fmt.Errorf("create metric_data_size counter: %w", err) + } + + traceSize, err := meter.Int64Counter( + metricName("trace_data_size"), + metric.WithDescription("Size of the trace package passed to the processor"), + metric.WithUnit("By"), + ) + if err != nil { + return nil, fmt.Errorf("create trace_data_size counter: %w", err) + } + + logCount, err := meter.Int64Counter( + metricName("log_count"), + metric.WithDescription("Count of the number log records passed to the processor"), + metric.WithUnit("{logs}"), + ) + if err != nil { + return nil, fmt.Errorf("create log_count counter: %w", err) + } + + datapointCount, err := meter.Int64Counter( + metricName("metric_count"), + metric.WithDescription("Count of the number datapoints passed to the processor"), + metric.WithUnit("{datapoints}"), + ) + if err != nil { + return nil, fmt.Errorf("create metric_count counter: %w", err) + } + + spanCount, err := meter.Int64Counter( + metricName("trace_count"), + metric.WithDescription("Count of the number spans passed to the processor"), + metric.WithUnit("{spans}"), + ) + if err != nil { + return nil, fmt.Errorf("create trace_count counter: %w", err) + } + + attrs := createMeasurementsAttributeSet(processorID, extraAttributes) + + return &ThroughputMeasurements{ + logSize: newInt64Counter(logSize, attrs), + logCount: newInt64Counter(logCount, attrs), + metricSize: newInt64Counter(metricSize, attrs), + datapointCount: newInt64Counter(datapointCount, attrs), + traceSize: newInt64Counter(traceSize, attrs), + spanCount: newInt64Counter(spanCount, attrs), + attributes: attrs, + }, nil +} + +// AddLogs records throughput metrics for the provided logs. +func (tm *ThroughputMeasurements) AddLogs(ctx context.Context, l plog.Logs) { + sizer := plog.ProtoMarshaler{} + + tm.logSize.Add(ctx, int64(sizer.LogsSize(l))) + tm.logCount.Add(ctx, int64(l.LogRecordCount())) +} + +// AddMetrics records throughput metrics for the provided metrics. +func (tm *ThroughputMeasurements) AddMetrics(ctx context.Context, m pmetric.Metrics) { + sizer := pmetric.ProtoMarshaler{} + + tm.metricSize.Add(ctx, int64(sizer.MetricsSize(m))) + tm.datapointCount.Add(ctx, int64(m.DataPointCount())) +} + +// AddTraces records throughput metrics for the provided traces. +func (tm *ThroughputMeasurements) AddTraces(ctx context.Context, t ptrace.Traces) { + sizer := ptrace.ProtoMarshaler{} + + tm.traceSize.Add(ctx, int64(sizer.TracesSize(t))) + tm.spanCount.Add(ctx, int64(t.SpanCount())) +} + +// LogSize returns the total size in bytes of all log payloads added to this ThroughputMeasurements. +func (tm *ThroughputMeasurements) LogSize() int64 { + return tm.logSize.Val() +} + +// MetricSize returns the total size in bytes of all metric payloads added to this ThroughputMeasurements. +func (tm *ThroughputMeasurements) MetricSize() int64 { + return tm.metricSize.Val() +} + +// TraceSize returns the total size in bytes of all trace payloads added to this ThroughputMeasurements. +func (tm *ThroughputMeasurements) TraceSize() int64 { + return tm.traceSize.Val() +} + +// LogCount return the total number of log records that have been added to this ThroughputMeasurements. +func (tm *ThroughputMeasurements) LogCount() int64 { + return tm.logCount.Val() +} + +// DatapointCount return the total number of datapoints that have been added to this ThroughputMeasurements. +func (tm *ThroughputMeasurements) DatapointCount() int64 { + return tm.datapointCount.Val() +} + +// SpanCount return the total number of spans that have been added to this ThroughputMeasurements. +func (tm *ThroughputMeasurements) SpanCount() int64 { + return tm.spanCount.Val() +} + +// Attributes returns the full set of attributes used on each metric for this ThroughputMeasurements. +func (tm *ThroughputMeasurements) Attributes() attribute.Set { + return tm.attributes +} + +// int64Counter combines a metric.Int64Counter with a atomic.Int64 so that the value of the counter may be +// retrieved. +// The value of the metric counter and val are not guaranteed to be synchronized, but will be eventually consistent. +type int64Counter struct { + counter metric.Int64Counter + val atomic.Int64 + attributes attribute.Set +} + +func newInt64Counter(counter metric.Int64Counter, attributes attribute.Set) *int64Counter { + return &int64Counter{ + counter: counter, + attributes: attributes, + } +} + +func (i *int64Counter) Add(ctx context.Context, delta int64) { + i.counter.Add(ctx, delta, metric.WithAttributeSet(i.attributes)) + i.val.Add(delta) +} + +func (i *int64Counter) Val() int64 { + return i.val.Load() +} + +func metricName(metric string) string { + return fmt.Sprintf("otelcol_processor_throughputmeasurement_%s", metric) +} + +func createMeasurementsAttributeSet(processorID string, extraAttributes map[string]string) attribute.Set { + attrs := make([]attribute.KeyValue, 0, len(extraAttributes)+1) + + attrs = append(attrs, attribute.String("processor", processorID)) + for k, v := range extraAttributes { + attrs = append(attrs, attribute.String(k, v)) + } + + return attribute.NewSet(attrs...) +} + +// ResettableThroughputMeasurementsRegistry is a concrete version of ThroughputMeasurementsRegistry that is able to be reset. +type ResettableThroughputMeasurementsRegistry struct { + measurements *sync.Map + emitCountMetrics bool +} + +// NewResettableThroughputMeasurementsRegistry creates a new ResettableThroughputMeasurementsRegistry +func NewResettableThroughputMeasurementsRegistry(emitCountMetrics bool) *ResettableThroughputMeasurementsRegistry { + return &ResettableThroughputMeasurementsRegistry{ + measurements: &sync.Map{}, + emitCountMetrics: emitCountMetrics, + } +} + +// RegisterThroughputMeasurements registers the ThroughputMeasurements with the registry. +func (ctmr *ResettableThroughputMeasurementsRegistry) RegisterThroughputMeasurements(processorID string, measurements *ThroughputMeasurements) { + ctmr.measurements.Store(processorID, measurements) +} + +// Reset unregisters all throughput measurements in this registry +func (ctmr *ResettableThroughputMeasurementsRegistry) Reset() { + ctmr.measurements = &sync.Map{} +} + +// OTLPMeasurements returns all the measurements in this registry as OTLP metrics. +func (ctmr *ResettableThroughputMeasurementsRegistry) OTLPMeasurements(extraAttributes map[string]string) pmetric.Metrics { + m := pmetric.NewMetrics() + rm := m.ResourceMetrics().AppendEmpty() + sm := rm.ScopeMetrics().AppendEmpty() + + ctmr.measurements.Range(func(_, value any) bool { + tm := value.(*ThroughputMeasurements) + OTLPThroughputMeasurements(tm, ctmr.emitCountMetrics, extraAttributes).MoveAndAppendTo(sm.Metrics()) + return true + }) + + if m.DataPointCount() == 0 { + // If there are no datapoints in the metric, + // we don't want to have an empty ResourceMetrics in the metrics slice. + return pmetric.NewMetrics() + } + + return m +} diff --git a/internal/measurements/throughput_test.go b/internal/measurements/throughput_test.go new file mode 100644 index 000000000..3012a8eec --- /dev/null +++ b/internal/measurements/throughput_test.go @@ -0,0 +1,332 @@ +// Copyright observIQ, Inc. +// +// 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 measurements + +import ( + "context" + "path/filepath" + "testing" + + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest/pmetrictest" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/metricdata" +) + +func TestProcessor_Logs(t *testing.T) { + manualReader := metric.NewManualReader() + defer manualReader.Shutdown(context.Background()) + + mp := metric.NewMeterProvider( + metric.WithReader(manualReader), + ) + defer mp.Shutdown(context.Background()) + + processorID := "throughputmeasurement/1" + + tmp, err := NewThroughputMeasurements(mp, processorID, map[string]string{}) + require.NoError(t, err) + + logs, err := golden.ReadLogs(filepath.Join("testdata", "logs", "w3c-logs.yaml")) + require.NoError(t, err) + + tmp.AddLogs(context.Background(), logs) + + var rm metricdata.ResourceMetrics + require.NoError(t, manualReader.Collect(context.Background(), &rm)) + + // Extract the metrics we care about from the metrics we collected + var logSize, logCount int64 + + for _, sm := range rm.ScopeMetrics { + for _, metric := range sm.Metrics { + switch metric.Name { + case "otelcol_processor_throughputmeasurement_log_data_size": + sum := metric.Data.(metricdata.Sum[int64]) + require.Equal(t, 1, len(sum.DataPoints)) + + processorAttr, ok := sum.DataPoints[0].Attributes.Value(attribute.Key("processor")) + require.True(t, ok, "processor attribute was not found") + require.Equal(t, processorID, processorAttr.AsString()) + + logSize = sum.DataPoints[0].Value + + case "otelcol_processor_throughputmeasurement_log_count": + sum := metric.Data.(metricdata.Sum[int64]) + require.Equal(t, 1, len(sum.DataPoints)) + + processorAttr, ok := sum.DataPoints[0].Attributes.Value(attribute.Key("processor")) + require.True(t, ok, "processor attribute was not found") + require.Equal(t, processorID, processorAttr.AsString()) + + logCount = sum.DataPoints[0].Value + } + + } + } + + require.Equal(t, int64(3974), logSize) + require.Equal(t, int64(3974), tmp.LogSize()) + require.Equal(t, int64(16), logCount) + require.Equal(t, int64(16), tmp.LogCount()) +} + +func TestProcessor_Metrics(t *testing.T) { + manualReader := metric.NewManualReader() + defer manualReader.Shutdown(context.Background()) + + mp := metric.NewMeterProvider( + metric.WithReader(manualReader), + ) + defer mp.Shutdown(context.Background()) + + processorID := "throughputmeasurement/1" + + tmp, err := NewThroughputMeasurements(mp, processorID, map[string]string{}) + require.NoError(t, err) + + metrics, err := golden.ReadMetrics(filepath.Join("testdata", "metrics", "host-metrics.yaml")) + require.NoError(t, err) + + tmp.AddMetrics(context.Background(), metrics) + + var rm metricdata.ResourceMetrics + require.NoError(t, manualReader.Collect(context.Background(), &rm)) + + // Extract the metrics we care about from the metrics we collected + var metricSize, datapointCount int64 + + for _, sm := range rm.ScopeMetrics { + for _, metric := range sm.Metrics { + switch metric.Name { + case "otelcol_processor_throughputmeasurement_metric_data_size": + sum := metric.Data.(metricdata.Sum[int64]) + require.Equal(t, 1, len(sum.DataPoints)) + + processorAttr, ok := sum.DataPoints[0].Attributes.Value(attribute.Key("processor")) + require.True(t, ok, "processor attribute was not found") + require.Equal(t, processorID, processorAttr.AsString()) + + metricSize = sum.DataPoints[0].Value + + case "otelcol_processor_throughputmeasurement_metric_count": + sum := metric.Data.(metricdata.Sum[int64]) + require.Equal(t, 1, len(sum.DataPoints)) + + processorAttr, ok := sum.DataPoints[0].Attributes.Value(attribute.Key("processor")) + require.True(t, ok, "processor attribute was not found") + require.Equal(t, processorID, processorAttr.AsString()) + + datapointCount = sum.DataPoints[0].Value + } + + } + } + + require.Equal(t, int64(5675), metricSize) + require.Equal(t, int64(5675), tmp.MetricSize()) + require.Equal(t, int64(37), datapointCount) + require.Equal(t, int64(37), tmp.DatapointCount()) +} + +func TestProcessor_Traces(t *testing.T) { + manualReader := metric.NewManualReader() + defer manualReader.Shutdown(context.Background()) + + mp := metric.NewMeterProvider( + metric.WithReader(manualReader), + ) + defer mp.Shutdown(context.Background()) + + processorID := "throughputmeasurement/1" + + tmp, err := NewThroughputMeasurements(mp, processorID, map[string]string{}) + require.NoError(t, err) + + traces, err := golden.ReadTraces(filepath.Join("testdata", "traces", "bindplane-traces.yaml")) + require.NoError(t, err) + + tmp.AddTraces(context.Background(), traces) + + var rm metricdata.ResourceMetrics + require.NoError(t, manualReader.Collect(context.Background(), &rm)) + + // Extract the metrics we care about from the metrics we collected + var traceSize, spanCount int64 + + for _, sm := range rm.ScopeMetrics { + for _, metric := range sm.Metrics { + switch metric.Name { + case "otelcol_processor_throughputmeasurement_trace_data_size": + sum := metric.Data.(metricdata.Sum[int64]) + require.Equal(t, 1, len(sum.DataPoints)) + + processorAttr, ok := sum.DataPoints[0].Attributes.Value(attribute.Key("processor")) + require.True(t, ok, "processor attribute was not found") + require.Equal(t, processorID, processorAttr.AsString()) + + traceSize = sum.DataPoints[0].Value + + case "otelcol_processor_throughputmeasurement_trace_count": + sum := metric.Data.(metricdata.Sum[int64]) + require.Equal(t, 1, len(sum.DataPoints)) + + processorAttr, ok := sum.DataPoints[0].Attributes.Value(attribute.Key("processor")) + require.True(t, ok, "processor attribute was not found") + require.Equal(t, processorID, processorAttr.AsString()) + + spanCount = sum.DataPoints[0].Value + } + + } + } + + require.Equal(t, int64(16767), traceSize) + require.Equal(t, int64(16767), tmp.TraceSize()) + require.Equal(t, int64(178), spanCount) + require.Equal(t, int64(178), tmp.SpanCount()) +} + +func TestResettableThroughputMeasurementsRegistry(t *testing.T) { + t.Run("Test registered measurements are in OTLP payload (no count metrics)", func(t *testing.T) { + reg := NewResettableThroughputMeasurementsRegistry(false) + + mp := metric.NewMeterProvider() + defer mp.Shutdown(context.Background()) + + tmp, err := NewThroughputMeasurements(mp, "throughputmeasurement/1", map[string]string{}) + require.NoError(t, err) + + traces, err := golden.ReadTraces(filepath.Join("testdata", "traces", "bindplane-traces.yaml")) + require.NoError(t, err) + + metrics, err := golden.ReadMetrics(filepath.Join("testdata", "metrics", "host-metrics.yaml")) + require.NoError(t, err) + + logs, err := golden.ReadLogs(filepath.Join("testdata", "logs", "w3c-logs.yaml")) + require.NoError(t, err) + + tmp.AddLogs(context.Background(), logs) + tmp.AddMetrics(context.Background(), metrics) + tmp.AddTraces(context.Background(), traces) + + reg.RegisterThroughputMeasurements("throughputmeasurement/1", tmp) + + actualMetrics := reg.OTLPMeasurements(nil) + + expectedMetrics, err := golden.ReadMetrics(filepath.Join("testdata", "expected", "throughput_measurements_no_count.yaml")) + require.NoError(t, err) + + require.NoError(t, pmetrictest.CompareMetrics(expectedMetrics, actualMetrics, pmetrictest.IgnoreTimestamp())) + }) + + t.Run("Test registered measurements are in OTLP payload (with count metrics)", func(t *testing.T) { + reg := NewResettableThroughputMeasurementsRegistry(true) + + mp := metric.NewMeterProvider() + defer mp.Shutdown(context.Background()) + + tmp, err := NewThroughputMeasurements(mp, "throughputmeasurement/1", map[string]string{}) + require.NoError(t, err) + + traces, err := golden.ReadTraces(filepath.Join("testdata", "traces", "bindplane-traces.yaml")) + require.NoError(t, err) + + metrics, err := golden.ReadMetrics(filepath.Join("testdata", "metrics", "host-metrics.yaml")) + require.NoError(t, err) + + logs, err := golden.ReadLogs(filepath.Join("testdata", "logs", "w3c-logs.yaml")) + require.NoError(t, err) + + tmp.AddLogs(context.Background(), logs) + tmp.AddMetrics(context.Background(), metrics) + tmp.AddTraces(context.Background(), traces) + + reg.RegisterThroughputMeasurements("throughputmeasurement/1", tmp) + + actualMetrics := reg.OTLPMeasurements(nil) + + expectedMetrics, err := golden.ReadMetrics(filepath.Join("testdata", "expected", "throughput_measurements_count.yaml")) + require.NoError(t, err) + + require.NoError(t, pmetrictest.CompareMetrics(expectedMetrics, actualMetrics, pmetrictest.IgnoreTimestamp())) + }) + + t.Run("Test registered measurements are in OTLP payload (extra attributes)", func(t *testing.T) { + reg := NewResettableThroughputMeasurementsRegistry(false) + + mp := metric.NewMeterProvider() + defer mp.Shutdown(context.Background()) + + tmp, err := NewThroughputMeasurements(mp, "throughputmeasurement/1", map[string]string{ + "gateway": "true", + }) + require.NoError(t, err) + + traces, err := golden.ReadTraces(filepath.Join("testdata", "traces", "bindplane-traces.yaml")) + require.NoError(t, err) + + metrics, err := golden.ReadMetrics(filepath.Join("testdata", "metrics", "host-metrics.yaml")) + require.NoError(t, err) + + logs, err := golden.ReadLogs(filepath.Join("testdata", "logs", "w3c-logs.yaml")) + require.NoError(t, err) + + tmp.AddLogs(context.Background(), logs) + tmp.AddMetrics(context.Background(), metrics) + tmp.AddTraces(context.Background(), traces) + + reg.RegisterThroughputMeasurements("throughputmeasurement/1", tmp) + + actualMetrics := reg.OTLPMeasurements(nil) + + expectedMetrics, err := golden.ReadMetrics(filepath.Join("testdata", "expected", "throughput_measurements_extra_attrs.yaml")) + require.NoError(t, err) + + require.NoError(t, pmetrictest.CompareMetrics(expectedMetrics, actualMetrics, pmetrictest.IgnoreTimestamp())) + }) + + t.Run("Test reset removes registered measurements", func(t *testing.T) { + reg := NewResettableThroughputMeasurementsRegistry(true) + + mp := metric.NewMeterProvider() + defer mp.Shutdown(context.Background()) + + tmp, err := NewThroughputMeasurements(mp, "throughputmeasurement/1", map[string]string{}) + require.NoError(t, err) + + traces, err := golden.ReadTraces(filepath.Join("testdata", "traces", "bindplane-traces.yaml")) + require.NoError(t, err) + + metrics, err := golden.ReadMetrics(filepath.Join("testdata", "metrics", "host-metrics.yaml")) + require.NoError(t, err) + + logs, err := golden.ReadLogs(filepath.Join("testdata", "logs", "w3c-logs.yaml")) + require.NoError(t, err) + + tmp.AddLogs(context.Background(), logs) + tmp.AddMetrics(context.Background(), metrics) + tmp.AddTraces(context.Background(), traces) + + reg.RegisterThroughputMeasurements("throughputmeasurement/1", tmp) + + reg.Reset() + + require.NoError(t, pmetrictest.CompareMetrics(pmetric.NewMetrics(), reg.OTLPMeasurements(nil))) + }) +} diff --git a/internal/service/managed.go b/internal/service/managed.go index 145f6515c..b8733acf4 100644 --- a/internal/service/managed.go +++ b/internal/service/managed.go @@ -19,6 +19,8 @@ import ( "context" "fmt" + "github.com/observiq/bindplane-agent/internal/measurements" + "github.com/observiq/bindplane-agent/collector" "github.com/observiq/bindplane-agent/internal/version" "github.com/observiq/bindplane-agent/opamp" @@ -46,14 +48,15 @@ func NewManagedCollectorService(col collector.Collector, logger *zap.Logger, man // Create client Args clientArgs := &observiq.NewClientArgs{ - DefaultLogger: logger, - Config: *opampConfig, - Collector: col, - Version: version.Version(), - TmpPath: "./tmp", - ManagerConfigPath: managerConfigPath, - CollectorConfigPath: collectorConfigPath, - LoggerConfigPath: loggerConfigPath, + DefaultLogger: logger, + Config: *opampConfig, + Collector: col, + Version: version.Version(), + TmpPath: "./tmp", + ManagerConfigPath: managerConfigPath, + CollectorConfigPath: collectorConfigPath, + LoggerConfigPath: loggerConfigPath, + MeasurementsReporter: measurements.BindplaneAgentThroughputMeasurementsRegistry, } // Create new client diff --git a/opamp/config.go b/opamp/config.go index 95df259d3..a1c2d36e1 100644 --- a/opamp/config.go +++ b/opamp/config.go @@ -19,8 +19,10 @@ import ( "crypto/x509" "errors" "fmt" + "maps" "os" "path/filepath" + "time" "gopkg.in/yaml.v3" ) @@ -53,8 +55,10 @@ type Config struct { TLS *TLSConfig `yaml:"tls_config,omitempty"` // Updatable fields - Labels *string `yaml:"labels,omitempty"` - AgentName *string `yaml:"agent_name,omitempty"` + Labels *string `yaml:"labels,omitempty"` + AgentName *string `yaml:"agent_name,omitempty"` + MeasurementsInterval time.Duration `yaml:"measurements_interval,omitempty"` + ExtraMeasurementsAttributes map[string]string `yaml:"extra_measurements_attributes,omitempty"` } // TLSConfig represents the TLS config to connect to OpAmp server @@ -153,8 +157,9 @@ func ParseConfig(configLocation string) (*Config, error) { func (c Config) Copy() *Config { cfgCopy := &Config{ - Endpoint: c.Endpoint, - AgentID: c.AgentID, + Endpoint: c.Endpoint, + AgentID: c.AgentID, + MeasurementsInterval: c.MeasurementsInterval, } if c.SecretKey != nil { @@ -172,6 +177,9 @@ func (c Config) Copy() *Config { if c.TLS != nil { cfgCopy.TLS = c.TLS.copy() } + if c.ExtraMeasurementsAttributes != nil { + cfgCopy.ExtraMeasurementsAttributes = maps.Clone(c.ExtraMeasurementsAttributes) + } return cfgCopy } @@ -212,6 +220,14 @@ func (c Config) CmpUpdatableFields(o Config) (equal bool) { return false } + if c.MeasurementsInterval != o.MeasurementsInterval { + return false + } + + if !maps.Equal(c.ExtraMeasurementsAttributes, o.ExtraMeasurementsAttributes) { + return false + } + return cmpStringPtr(c.Labels, o.Labels) } diff --git a/opamp/mocks/mock_opamp_client.go b/opamp/mocks/mock_opamp_client.go index c2c861bf0..62f0adcd7 100644 --- a/opamp/mocks/mock_opamp_client.go +++ b/opamp/mocks/mock_opamp_client.go @@ -36,6 +36,54 @@ func (_m *MockOpAMPClient) AgentDescription() *protobufs.AgentDescription { return r0 } +// RequestConnectionSettings provides a mock function with given fields: request +func (_m *MockOpAMPClient) RequestConnectionSettings(request *protobufs.ConnectionSettingsRequest) error { + ret := _m.Called(request) + + if len(ret) == 0 { + panic("no return value specified for RequestConnectionSettings") + } + + var r0 error + if rf, ok := ret.Get(0).(func(*protobufs.ConnectionSettingsRequest) error); ok { + r0 = rf(request) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// SendCustomMessage provides a mock function with given fields: message +func (_m *MockOpAMPClient) SendCustomMessage(message *protobufs.CustomMessage) (chan struct{}, error) { + ret := _m.Called(message) + + if len(ret) == 0 { + panic("no return value specified for SendCustomMessage") + } + + var r0 chan struct{} + var r1 error + if rf, ok := ret.Get(0).(func(*protobufs.CustomMessage) (chan struct{}, error)); ok { + return rf(message) + } + if rf, ok := ret.Get(0).(func(*protobufs.CustomMessage) chan struct{}); ok { + r0 = rf(message) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(chan struct{}) + } + } + + if rf, ok := ret.Get(1).(func(*protobufs.CustomMessage) error); ok { + r1 = rf(message) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // SetAgentDescription provides a mock function with given fields: descr func (_m *MockOpAMPClient) SetAgentDescription(descr *protobufs.AgentDescription) error { ret := _m.Called(descr) @@ -54,8 +102,26 @@ func (_m *MockOpAMPClient) SetAgentDescription(descr *protobufs.AgentDescription return r0 } +// SetCustomCapabilities provides a mock function with given fields: customCapabilities +func (_m *MockOpAMPClient) SetCustomCapabilities(customCapabilities *protobufs.CustomCapabilities) error { + ret := _m.Called(customCapabilities) + + if len(ret) == 0 { + panic("no return value specified for SetCustomCapabilities") + } + + var r0 error + if rf, ok := ret.Get(0).(func(*protobufs.CustomCapabilities) error); ok { + r0 = rf(customCapabilities) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // SetHealth provides a mock function with given fields: health -func (_m *MockOpAMPClient) SetHealth(health *protobufs.AgentHealth) error { +func (_m *MockOpAMPClient) SetHealth(health *protobufs.ComponentHealth) error { ret := _m.Called(health) if len(ret) == 0 { @@ -63,7 +129,7 @@ func (_m *MockOpAMPClient) SetHealth(health *protobufs.AgentHealth) error { } var r0 error - if rf, ok := ret.Get(0).(func(*protobufs.AgentHealth) error); ok { + if rf, ok := ret.Get(0).(func(*protobufs.ComponentHealth) error); ok { r0 = rf(health) } else { r0 = ret.Error(0) diff --git a/opamp/observiq/logger.go b/opamp/observiq/logger.go new file mode 100644 index 000000000..8f7fa3424 --- /dev/null +++ b/opamp/observiq/logger.go @@ -0,0 +1,42 @@ +// Copyright observIQ, Inc. +// +// 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 observiq + +import ( + "context" + + "github.com/open-telemetry/opamp-go/client/types" + "go.uber.org/zap" +) + +type zapOpAMPLoggerAdapter struct { + logger *zap.SugaredLogger +} + +var _ types.Logger = (*zapOpAMPLoggerAdapter)(nil) + +func newZapOpAMPLoggerAdapter(logger *zap.Logger) *zapOpAMPLoggerAdapter { + return &zapOpAMPLoggerAdapter{ + logger: logger.Sugar(), + } +} + +func (o zapOpAMPLoggerAdapter) Debugf(_ context.Context, format string, v ...any) { + o.logger.Debugf(format, v...) +} + +func (o zapOpAMPLoggerAdapter) Errorf(_ context.Context, format string, v ...any) { + o.logger.Errorf(format, v...) +} diff --git a/opamp/observiq/measurements.go b/opamp/observiq/measurements.go new file mode 100644 index 000000000..367f3b408 --- /dev/null +++ b/opamp/observiq/measurements.go @@ -0,0 +1,238 @@ +// Copyright observIQ, Inc. +// +// 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 observiq + +import ( + "errors" + "sync" + "time" + + "github.com/golang/snappy" + "github.com/observiq/bindplane-agent/internal/measurements" + "github.com/open-telemetry/opamp-go/client" + "github.com/open-telemetry/opamp-go/client/types" + "github.com/open-telemetry/opamp-go/protobufs" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.uber.org/zap" +) + +const maxSendRetries = 3 + +// MeasurementsReporter represents an object that reports throughput measurements as OTLP. +type MeasurementsReporter interface { + OTLPMeasurements(extraAttributes map[string]string) pmetric.Metrics +} + +// measurementsSender is a struct that handles periodically sending measurements via a custom message to an OpAMP endpoint. +type measurementsSender struct { + logger *zap.Logger + reporter MeasurementsReporter + opampClient client.OpAMPClient + interval time.Duration + extraAttributes map[string]string + + changeIntervalChan chan time.Duration + changeAttributesChan chan map[string]string + + mux *sync.Mutex + isRunning bool + done chan struct{} + wg *sync.WaitGroup +} + +func newMeasurementsSender(l *zap.Logger, reporter MeasurementsReporter, opampClient client.OpAMPClient, interval time.Duration, extraAttributes map[string]string) *measurementsSender { + return &measurementsSender{ + logger: l, + reporter: reporter, + opampClient: opampClient, + interval: interval, + extraAttributes: extraAttributes, + + changeIntervalChan: make(chan time.Duration, 1), + changeAttributesChan: make(chan map[string]string, 1), + mux: &sync.Mutex{}, + isRunning: false, + done: make(chan struct{}), + wg: &sync.WaitGroup{}, + } +} + +// Start starts the sender. It may be called multiple times, even if the sender is already started. +func (m *measurementsSender) Start() { + m.mux.Lock() + defer m.mux.Unlock() + + if m.isRunning { + return + } + + m.isRunning = true + + m.wg.Add(1) + go func() { + defer m.wg.Done() + m.loop() + }() +} + +// SetInterval changes the interval of the measurements sender. +func (m measurementsSender) SetInterval(d time.Duration) { + select { + case m.changeIntervalChan <- d: + case <-m.done: + } + +} + +func (m measurementsSender) SetExtraAttributes(extraAttributes map[string]string) { + select { + case m.changeAttributesChan <- extraAttributes: + case <-m.done: + } +} + +func (m *measurementsSender) Stop() { + m.mux.Lock() + defer m.mux.Unlock() + + if !m.isRunning { + return + } + + close(m.done) + m.wg.Wait() + + m.isRunning = false +} + +func (m *measurementsSender) loop() { + t := newTicker() + t.SetInterval(m.interval) + defer t.Stop() + + for { + select { + case newInterval := <-m.changeIntervalChan: + m.interval = newInterval + t.SetInterval(newInterval) + case newAttributes := <-m.changeAttributesChan: + m.extraAttributes = newAttributes + case <-m.done: + return + case <-t.Chan(): + m.logger.Info("Ticker fired, sending measurements") + if m.reporter == nil { + // Continue if no reporter available + m.logger.Info("No reporter, skipping sending measurements.") + continue + } + + metrics := m.reporter.OTLPMeasurements(m.extraAttributes) + if metrics.DataPointCount() == 0 { + // don't report empty payloads + continue + } + + // Send metrics as snappy-encoded otlp proto + marshaller := pmetric.ProtoMarshaler{} + marshalled, err := marshaller.MarshalMetrics(metrics) + if err != nil { + m.logger.Error("Failed to marshal throughput metrics.", zap.Error(err)) + continue + } + + encoded := snappy.Encode(nil, marshalled) + + cm := &protobufs.CustomMessage{ + Capability: measurements.ReportMeasurementsV1Capability, + Type: measurements.ReportMeasurementsType, + Data: encoded, + } + + for i := 0; i < maxSendRetries; i++ { + sendingChannel, err := m.opampClient.SendCustomMessage(cm) + switch { + case err == nil: // OK + case errors.Is(err, types.ErrCustomMessagePending): + if i == maxSendRetries-1 { + // Bail out early, since we aren't going to try to send again + m.logger.Warn("Measurements were blocked by other custom messages, skipping...", zap.Int("retries", maxSendRetries)) + break + } + + select { + case <-sendingChannel: + continue + case <-m.done: + return + } + default: + m.logger.Error("Failed to report measurements", zap.Error(err)) + } + break + } + } + } +} + +// ticker is essentially time.ticker, but it provides a SetInterval method +// that allows the interval to be changed. It also allows the interval +// to be configured to a negative or zero duration, in which case the ticker +// never fires. +type ticker struct { + duration time.Duration + ticker *time.Ticker +} + +func newTicker() *ticker { + return &ticker{} +} + +func (t *ticker) SetInterval(d time.Duration) { + if t.duration == d { + // Nothing to do, this is already the interval + return + } + + t.duration = d + + if t.ticker != nil { + t.ticker.Stop() + t.ticker = nil + } + + if d <= 0 { + // Cannot make a ticker with zero or negative duration; + // Attempts to use the channel will give a permanently blocking channel. + return + } + + t.ticker = time.NewTicker(d) +} + +func (t *ticker) Chan() <-chan time.Time { + if t.ticker == nil { + // ticker never triggers if 0 or negative duration + return make(<-chan time.Time) + } + return t.ticker.C +} + +func (t *ticker) Stop() { + if t.ticker != nil { + t.ticker.Stop() + t.ticker = nil + } +} diff --git a/opamp/observiq/measurements_test.go b/opamp/observiq/measurements_test.go new file mode 100644 index 000000000..8779fd85f --- /dev/null +++ b/opamp/observiq/measurements_test.go @@ -0,0 +1,145 @@ +// Copyright observIQ, Inc. +// +// 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 observiq + +import ( + "context" + "path/filepath" + "testing" + "time" + + "github.com/golang/snappy" + "github.com/observiq/bindplane-agent/internal/measurements" + "github.com/observiq/bindplane-agent/opamp/mocks" + "github.com/open-telemetry/opamp-go/protobufs" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest/pmetrictest" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/pdata/pmetric" + "go.opentelemetry.io/otel/sdk/metric" + "go.uber.org/zap" +) + +func TestMeasurementsSender(t *testing.T) { + t.Run("Test emits metrics", func(t *testing.T) { + dataChan := make(chan []byte, 1) + client := mocks.NewMockOpAMPClient(t) + client.On("SendCustomMessage", mock.Anything).Run(func(args mock.Arguments) { + cm := args.Get(0).(*protobufs.CustomMessage) + select { + case dataChan <- cm.Data: + default: + } + + }).Return(make(chan struct{}), nil) + + mp := metric.NewMeterProvider() + defer mp.Shutdown(context.Background()) + + processorID := "throughputmeasurement/1" + + tm, err := measurements.NewThroughputMeasurements(mp, processorID, map[string]string{}) + require.NoError(t, err) + + m, err := golden.ReadMetrics(filepath.Join("testdata", "metrics", "host-metrics.yaml")) + require.NoError(t, err) + + tm.AddMetrics(context.Background(), m) + + reg := measurements.NewResettableThroughputMeasurementsRegistry(false) + reg.RegisterThroughputMeasurements(processorID, tm) + + ms := newMeasurementsSender(zap.NewNop(), reg, client, 1*time.Millisecond, nil) + ms.Start() + + select { + case <-time.After(1 * time.Second): + require.FailNow(t, "timed out waiting for metrics payload") + case d := <-dataChan: + decoded, err := snappy.Decode(nil, d) + require.NoError(t, err) + + um := &pmetric.ProtoUnmarshaler{} + actualMetrics, err := um.UnmarshalMetrics(decoded) + require.NoError(t, err) + + expectedMetrics, err := golden.ReadMetrics(filepath.Join("testdata", "metrics", "expected-throughput.yaml")) + require.NoError(t, err) + + require.NoError(t, pmetrictest.CompareMetrics(expectedMetrics, actualMetrics, pmetrictest.IgnoreTimestamp())) + } + + ms.Stop() + }) + + t.Run("Test set interval", func(t *testing.T) { + dataChan := make(chan []byte, 1) + client := mocks.NewMockOpAMPClient(t) + client.On("SendCustomMessage", mock.Anything).Run(func(args mock.Arguments) { + cm := args.Get(0).(*protobufs.CustomMessage) + select { + case dataChan <- cm.Data: + default: + } + }).Return(make(chan struct{}), nil) + + mp := metric.NewMeterProvider() + defer mp.Shutdown(context.Background()) + + processorID := "throughputmeasurement/1" + + tm, err := measurements.NewThroughputMeasurements(mp, processorID, map[string]string{}) + require.NoError(t, err) + + m, err := golden.ReadMetrics(filepath.Join("testdata", "metrics", "host-metrics.yaml")) + require.NoError(t, err) + + tm.AddMetrics(context.Background(), m) + + reg := measurements.NewResettableThroughputMeasurementsRegistry(false) + reg.RegisterThroughputMeasurements(processorID, tm) + + ms := newMeasurementsSender(zap.NewNop(), reg, client, 5*time.Hour, nil) + ms.Start() + + // Wait 200 ms and ensure no data emitted + time.Sleep(200 * time.Millisecond) + + require.Len(t, dataChan, 0) + + // Set time to 1ms. We should see data emit quickly after. + ms.SetInterval(1 * time.Millisecond) + + select { + case <-time.After(1 * time.Second): + require.FailNow(t, "timed out waiting for metrics payload") + case d := <-dataChan: + decoded, err := snappy.Decode(nil, d) + require.NoError(t, err) + + um := &pmetric.ProtoUnmarshaler{} + actualMetrics, err := um.UnmarshalMetrics(decoded) + require.NoError(t, err) + + expectedMetrics, err := golden.ReadMetrics(filepath.Join("testdata", "metrics", "expected-throughput.yaml")) + require.NoError(t, err) + + require.NoError(t, pmetrictest.CompareMetrics(expectedMetrics, actualMetrics, pmetrictest.IgnoreTimestamp())) + } + + ms.Stop() + }) +} diff --git a/opamp/observiq/observiq_client.go b/opamp/observiq/observiq_client.go index 705b55c52..9c250425d 100644 --- a/opamp/observiq/observiq_client.go +++ b/opamp/observiq/observiq_client.go @@ -22,9 +22,11 @@ import ( "fmt" "net/http" "net/url" + "slices" "sync" "github.com/observiq/bindplane-agent/collector" + "github.com/observiq/bindplane-agent/internal/measurements" "github.com/observiq/bindplane-agent/internal/report" "github.com/observiq/bindplane-agent/internal/version" "github.com/observiq/bindplane-agent/opamp" @@ -63,6 +65,7 @@ type Client struct { mutex sync.Mutex updatingPackage bool reportManager *report.Manager + measurementsSender *measurementsSender // To signal if we are disconnecting already and not take any actions on connection failures disconnecting bool @@ -82,10 +85,11 @@ type NewClientArgs struct { Collector collector.Collector Version string - TmpPath string - ManagerConfigPath string - CollectorConfigPath string - LoggerConfigPath string + TmpPath string + ManagerConfigPath string + CollectorConfigPath string + LoggerConfigPath string + MeasurementsReporter MeasurementsReporter } // NewClient creates a new OpAmp client @@ -136,11 +140,30 @@ func NewClient(args *NewClientArgs) (opamp.Client, error) { // Create collect client based on URL scheme switch opampURL.Scheme { case "ws", "wss": - observiqClient.opampClient = client.NewWebSocket(clientLogger.Sugar()) + logger := newZapOpAMPLoggerAdapter(clientLogger) + observiqClient.opampClient = client.NewWebSocket(logger) default: return nil, ErrUnsupportedURL } + err = observiqClient.opampClient.SetCustomCapabilities(&protobufs.CustomCapabilities{ + Capabilities: []string{ + measurements.ReportMeasurementsV1Capability, + }, + }) + if err != nil { + return nil, fmt.Errorf("error setting custom capabilities: %w", err) + } + + // Create measurements sender + observiqClient.measurementsSender = newMeasurementsSender( + clientLogger, + args.MeasurementsReporter, + observiqClient.opampClient, + args.Config.MeasurementsInterval, + args.Config.ExtraMeasurementsAttributes, + ) + return observiqClient, nil } @@ -255,7 +278,7 @@ func (c *Client) Disconnect(ctx context.Context) error { // client callbacks -func (c *Client) onConnectHandler() { +func (c *Client) onConnectHandler(_ context.Context) { c.logger.Info("Successfully connected to server") // See if we can retrieve the PackageStatuses where the collector package is in an installing state @@ -287,7 +310,7 @@ func (c *Client) onConnectHandler() { c.finishPackageInstall(pkgStatuses) } -func (c *Client) onConnectFailedHandler(err error) { +func (c *Client) onConnectFailedHandler(_ context.Context, err error) { c.logger.Error("Failed to connect to server", zap.Error(err)) // We are currently disconnecting so any Connection failed error is expected and should not affect an install @@ -297,7 +320,7 @@ func (c *Client) onConnectFailedHandler(err error) { } } -func (c *Client) onErrorHandler(errResp *protobufs.ServerErrorResponse) { +func (c *Client) onErrorHandler(_ context.Context, errResp *protobufs.ServerErrorResponse) { c.logger.Error("Server returned an error response", zap.String("Error", errResp.GetErrorMessage())) } @@ -318,6 +341,15 @@ func (c *Client) onMessageFuncHandler(ctx context.Context, msg *types.MessageDat c.logger.Error("Error while processing Packages Available Change", zap.Error(err)) } } + if msg.CustomCapabilities != nil { + if slices.Contains(msg.CustomCapabilities.Capabilities, measurements.ReportMeasurementsV1Capability) { + c.logger.Info("Server supports custom message measurements, starting sender.") + c.measurementsSender.Start() + } else { + c.logger.Info("Server does not support custom message measurements, stopping sender.") + c.measurementsSender.Stop() + } + } } func (c *Client) onRemoteConfigHandler(ctx context.Context, remoteConfig *protobufs.AgentRemoteConfig) error { diff --git a/opamp/observiq/observiq_client_test.go b/opamp/observiq/observiq_client_test.go index e94469cd7..afc216e6d 100644 --- a/opamp/observiq/observiq_client_test.go +++ b/opamp/observiq/observiq_client_test.go @@ -536,7 +536,7 @@ func TestClient_onConnectHandler(t *testing.T) { packagesStateProvider: mockStateProvider, } - c.onConnectHandler() + c.onConnectHandler(context.Background()) }, }, { @@ -555,7 +555,7 @@ func TestClient_onConnectHandler(t *testing.T) { packagesStateProvider: mockStateProvider, } - c.onConnectHandler() + c.onConnectHandler(context.Background()) }, }, { @@ -590,7 +590,7 @@ func TestClient_onConnectHandler(t *testing.T) { packagesStateProvider: mockStateProvider, } - c.onConnectHandler() + c.onConnectHandler(context.Background()) }, }, { @@ -638,7 +638,7 @@ func TestClient_onConnectHandler(t *testing.T) { packagesStateProvider: mockStateProvider, } - c.onConnectHandler() + c.onConnectHandler(context.Background()) }, }, { @@ -690,7 +690,7 @@ func TestClient_onConnectHandler(t *testing.T) { packagesStateProvider: mockStateProvider, } - c.onConnectHandler() + c.onConnectHandler(context.Background()) }, }, } @@ -718,7 +718,7 @@ func TestClient_onConnectFailedHandler(t *testing.T) { packagesStateProvider: mockStateProvider, } - c.onConnectFailedHandler(expectedErr) + c.onConnectFailedHandler(context.Background(), expectedErr) }, }, { @@ -737,7 +737,7 @@ func TestClient_onConnectFailedHandler(t *testing.T) { packagesStateProvider: mockStateProvider, } - c.onConnectFailedHandler(expectedErr) + c.onConnectFailedHandler(context.Background(), expectedErr) }, }, { @@ -751,7 +751,7 @@ func TestClient_onConnectFailedHandler(t *testing.T) { disconnecting: true, } - c.onConnectFailedHandler(expectedErr) + c.onConnectFailedHandler(context.Background(), expectedErr) }, }, { @@ -786,7 +786,7 @@ func TestClient_onConnectFailedHandler(t *testing.T) { packagesStateProvider: mockStateProvider, } - c.onConnectFailedHandler(expectedErr) + c.onConnectFailedHandler(context.Background(), expectedErr) }, }, { @@ -837,7 +837,7 @@ func TestClient_onConnectFailedHandler(t *testing.T) { packagesStateProvider: mockStateProvider, } - c.onConnectFailedHandler(expectedErr) + c.onConnectFailedHandler(context.Background(), expectedErr) }, }, } diff --git a/opamp/observiq/reload_funcs.go b/opamp/observiq/reload_funcs.go index 2c2554193..3d05c14bf 100644 --- a/opamp/observiq/reload_funcs.go +++ b/opamp/observiq/reload_funcs.go @@ -61,6 +61,8 @@ func managerReload(client *Client, managerConfigPath string) opamp.ReloadFunc { // Updatable config fields client.currentConfig.AgentName = newConfig.AgentName client.currentConfig.Labels = newConfig.Labels + client.currentConfig.MeasurementsInterval = newConfig.MeasurementsInterval + client.currentConfig.ExtraMeasurementsAttributes = newConfig.ExtraMeasurementsAttributes // Update identity client.ident.agentName = newConfig.AgentName @@ -101,6 +103,10 @@ func managerReload(client *Client, managerConfigPath string) opamp.ReloadFunc { return false, fmt.Errorf("failed to set agent description: %w ", err) } + // Set new measurements interval and attributes + client.measurementsSender.SetInterval(client.currentConfig.MeasurementsInterval) + client.measurementsSender.SetExtraAttributes(client.currentConfig.ExtraMeasurementsAttributes) + return true, nil } } diff --git a/opamp/observiq/reload_funcs_test.go b/opamp/observiq/reload_funcs_test.go index 1a2061d17..42ae6b615 100644 --- a/opamp/observiq/reload_funcs_test.go +++ b/opamp/observiq/reload_funcs_test.go @@ -92,9 +92,10 @@ func Test_managerReload(t *testing.T) { mockOpAmpClient.On("SetAgentDescription", mock.Anything).Return(nil) client := &Client{ - opampClient: mockOpAmpClient, - ident: newIdentity(zap.NewNop(), *currConfig, "0.0.0"), - currentConfig: *currConfig, + opampClient: mockOpAmpClient, + ident: newIdentity(zap.NewNop(), *currConfig, "0.0.0"), + currentConfig: *currConfig, + measurementsSender: newMeasurementsSender(zap.NewNop(), nil, mockOpAmpClient, 0, nil), } reloadFunc := managerReload(client, managerFilePath) diff --git a/opamp/observiq/testdata/metrics/expected-throughput.yaml b/opamp/observiq/testdata/metrics/expected-throughput.yaml new file mode 100644 index 000000000..a4a260616 --- /dev/null +++ b/opamp/observiq/testdata/metrics/expected-throughput.yaml @@ -0,0 +1,16 @@ +resourceMetrics: + - resource: {} + scopeMetrics: + - metrics: + - {} + - name: otelcol_processor_throughputmeasurement_metric_data_size + sum: + dataPoints: + - asInt: "5675" + attributes: + - key: processor + value: + stringValue: throughputmeasurement/1 + timeUnixNano: "1000000" + - {} + scope: {} diff --git a/opamp/observiq/testdata/metrics/host-metrics.yaml b/opamp/observiq/testdata/metrics/host-metrics.yaml new file mode 100644 index 000000000..d42978181 --- /dev/null +++ b/opamp/observiq/testdata/metrics/host-metrics.yaml @@ -0,0 +1,737 @@ +resourceMetrics: + - resource: + attributes: + - key: host.name + value: + stringValue: Brandons-Awesome-Linux-Machine + - key: os.type + value: + stringValue: linux + - key: extra-resource-attr-key + value: + stringValue: extra-resource-attr-value + schemaUrl: https://opentelemetry.io/schemas/1.9.0 + scopeMetrics: + - metrics: + - description: Average CPU Load over 1 minute. + gauge: + dataPoints: + - asDouble: 3.71484375 + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + attributes: + - key: cool-attribute-key + value: + stringValue: cool-attribute-value + name: system.cpu.load_average.1m + unit: "{thread}" + scope: + name: otelcol/hostmetricsreceiver/load + version: v1.43.0 + - resource: + attributes: + - key: host.name + value: + stringValue: Brandons-MBP + - key: os.type + value: + stringValue: darwin + schemaUrl: https://opentelemetry.io/schemas/1.9.0 + scopeMetrics: + - metrics: + - description: Filesystem bytes used. + name: system.filesystem.usage + sum: + aggregationTemporality: 2 + dataPoints: + - asInt: "503869440" + attributes: + - key: device + value: + stringValue: /dev/disk2s1 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/iSCPreboot + - key: state + value: + stringValue: free + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: device + value: + stringValue: /dev/disk2s1 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/iSCPreboot + - key: state + value: + stringValue: reserved + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "20418560" + attributes: + - key: device + value: + stringValue: /dev/disk2s1 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/iSCPreboot + - key: state + value: + stringValue: used + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "503869440" + attributes: + - key: device + value: + stringValue: /dev/disk2s2 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/xarts + - key: state + value: + stringValue: free + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: device + value: + stringValue: /dev/disk2s2 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/xarts + - key: state + value: + stringValue: reserved + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "20418560" + attributes: + - key: device + value: + stringValue: /dev/disk2s2 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/xarts + - key: state + value: + stringValue: used + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "503869440" + attributes: + - key: device + value: + stringValue: /dev/disk2s3 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/Hardware + - key: state + value: + stringValue: free + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: device + value: + stringValue: /dev/disk2s3 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/Hardware + - key: state + value: + stringValue: reserved + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "20418560" + attributes: + - key: device + value: + stringValue: /dev/disk2s3 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/Hardware + - key: state + value: + stringValue: used + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "342134198272" + attributes: + - key: device + value: + stringValue: /dev/disk3s1s1 + - key: mode + value: + stringValue: ro + - key: mountpoint + value: + stringValue: / + - key: state + value: + stringValue: free + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: device + value: + stringValue: /dev/disk3s1s1 + - key: mode + value: + stringValue: ro + - key: mountpoint + value: + stringValue: / + - key: state + value: + stringValue: reserved + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "152250597376" + attributes: + - key: device + value: + stringValue: /dev/disk3s1s1 + - key: mode + value: + stringValue: ro + - key: mountpoint + value: + stringValue: / + - key: state + value: + stringValue: used + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "342134198272" + attributes: + - key: device + value: + stringValue: /dev/disk3s2 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/Preboot + - key: state + value: + stringValue: free + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: device + value: + stringValue: /dev/disk3s2 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/Preboot + - key: state + value: + stringValue: reserved + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "152250597376" + attributes: + - key: device + value: + stringValue: /dev/disk3s2 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/Preboot + - key: state + value: + stringValue: used + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "342134198272" + attributes: + - key: device + value: + stringValue: /dev/disk3s4 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/Update + - key: state + value: + stringValue: free + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: device + value: + stringValue: /dev/disk3s4 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/Update + - key: state + value: + stringValue: reserved + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "152250597376" + attributes: + - key: device + value: + stringValue: /dev/disk3s4 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/Update + - key: state + value: + stringValue: used + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "342134198272" + attributes: + - key: device + value: + stringValue: /dev/disk3s5 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/Data + - key: state + value: + stringValue: free + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: device + value: + stringValue: /dev/disk3s5 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/Data + - key: state + value: + stringValue: reserved + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "152250597376" + attributes: + - key: device + value: + stringValue: /dev/disk3s5 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/Data + - key: state + value: + stringValue: used + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "342134198272" + attributes: + - key: device + value: + stringValue: /dev/disk3s6 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/VM + - key: state + value: + stringValue: free + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: device + value: + stringValue: /dev/disk3s6 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/VM + - key: state + value: + stringValue: reserved + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "152250597376" + attributes: + - key: device + value: + stringValue: /dev/disk3s6 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/VM + - key: state + value: + stringValue: used + - key: type + value: + stringValue: apfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "8717185024" + attributes: + - key: extra-sum-attr-key + value: + stringValue: extra-sum-attr-value + - key: device + value: + stringValue: /dev/disk4s1 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /Volumes/transfer + - key: state + value: + stringValue: free + - key: type + value: + stringValue: hfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: device + value: + stringValue: /dev/disk4s1 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /Volumes/transfer + - key: state + value: + stringValue: reserved + - key: type + value: + stringValue: hfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "7409389568" + attributes: + - key: device + value: + stringValue: /dev/disk4s1 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /Volumes/transfer + - key: state + value: + stringValue: used + - key: type + value: + stringValue: hfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "14336" + attributes: + - key: device + value: + stringValue: /dev/disk4s2 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /Volumes/UEFI_NTFS + - key: state + value: + stringValue: free + - key: type + value: + stringValue: msdos + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: device + value: + stringValue: /dev/disk4s2 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /Volumes/UEFI_NTFS + - key: state + value: + stringValue: reserved + - key: type + value: + stringValue: msdos + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "493568" + attributes: + - key: device + value: + stringValue: /dev/disk4s2 + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /Volumes/UEFI_NTFS + - key: state + value: + stringValue: used + - key: type + value: + stringValue: msdos + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: device + value: + stringValue: devfs + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /dev + - key: state + value: + stringValue: free + - key: type + value: + stringValue: devfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: device + value: + stringValue: devfs + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /dev + - key: state + value: + stringValue: reserved + - key: type + value: + stringValue: devfs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "220672" + attributes: + - key: device + value: + stringValue: devfs + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /dev + - key: state + value: + stringValue: used + - key: type + value: + stringValue: devfs + startTimeUnixNano: "1000000" + timeUnixNano: "3000000" + - asInt: "0" + attributes: + - key: device + value: + stringValue: map auto_home + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/Data/home + - key: state + value: + stringValue: free + - key: type + value: + stringValue: autofs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: device + value: + stringValue: map auto_home + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/Data/home + - key: state + value: + stringValue: reserved + - key: type + value: + stringValue: autofs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + - asInt: "0" + attributes: + - key: device + value: + stringValue: map auto_home + - key: mode + value: + stringValue: rw + - key: mountpoint + value: + stringValue: /System/Volumes/Data/home + - key: state + value: + stringValue: used + - key: type + value: + stringValue: autofs + startTimeUnixNano: "1000000" + timeUnixNano: "2000000" + unit: By + scope: + name: otelcol/hostmetricsreceiver/filesystem + version: v1.43.0 diff --git a/processor/throughputmeasurementprocessor/bindplane_registry.go b/processor/throughputmeasurementprocessor/bindplane_registry.go new file mode 100644 index 000000000..cb793be85 --- /dev/null +++ b/processor/throughputmeasurementprocessor/bindplane_registry.go @@ -0,0 +1,28 @@ +// Copyright observIQ, Inc. +// +// 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. + +//go:build bindplane + +package throughputmeasurementprocessor + +import ( + "github.com/observiq/bindplane-agent/internal/measurements" + "go.opentelemetry.io/collector/component" +) + +// GetThroughputRegistry returns the throughput registry that should be registered to based on the component ID. +// nil, nil may be returned by this function. In this case, the processor should not register it's throughput measurements anywhere. +func GetThroughputRegistry(host component.Host, bindplane component.ID) (measurements.ThroughputMeasurementsRegistry, error) { + return measurements.BindplaneAgentThroughputMeasurementsRegistry, nil +} diff --git a/processor/throughputmeasurementprocessor/config.go b/processor/throughputmeasurementprocessor/config.go index 91a4f2af7..9049ea307 100644 --- a/processor/throughputmeasurementprocessor/config.go +++ b/processor/throughputmeasurementprocessor/config.go @@ -17,6 +17,8 @@ package throughputmeasurementprocessor import ( "errors" + + "go.opentelemetry.io/collector/component" ) var errInvalidSamplingRatio = errors.New("sampling_ratio must be between 0.0 and 1.0") @@ -28,6 +30,12 @@ type Config struct { // SamplingRatio is the ratio of payloads that are measured. Values between 0.0 and 1.0 are valid. SamplingRatio float64 `mapstructure:"sampling_ratio"` + + // Bindplane extension to use in order to report metrics. Optional. + BindplaneExtension component.ID `mapstructure:"bindplane_extension"` + + // Extra labels to add to measurements and associate with emitted metrics + ExtraLabels map[string]string `mapstructure:"extra_labels"` } // Validate validates the processor configuration diff --git a/processor/throughputmeasurementprocessor/factory.go b/processor/throughputmeasurementprocessor/factory.go index 6d9137bea..2456d847b 100644 --- a/processor/throughputmeasurementprocessor/factory.go +++ b/processor/throughputmeasurementprocessor/factory.go @@ -65,7 +65,11 @@ func createTracesProcessor( return nil, fmt.Errorf("create throughputmeasurementprocessor: %w", err) } - return processorhelper.NewTracesProcessor(ctx, set, cfg, nextConsumer, tmp.processTraces, processorhelper.WithCapabilities(consumerCapabilities)) + return processorhelper.NewTracesProcessor( + ctx, set, cfg, nextConsumer, tmp.processTraces, + processorhelper.WithCapabilities(consumerCapabilities), + processorhelper.WithStart(tmp.start), + ) } func createLogsProcessor( @@ -80,7 +84,11 @@ func createLogsProcessor( return nil, fmt.Errorf("create throughputmeasurementprocessor: %w", err) } - return processorhelper.NewLogsProcessor(ctx, set, cfg, nextConsumer, tmp.processLogs, processorhelper.WithCapabilities(consumerCapabilities)) + return processorhelper.NewLogsProcessor( + ctx, set, cfg, nextConsumer, tmp.processLogs, + processorhelper.WithCapabilities(consumerCapabilities), + processorhelper.WithStart(tmp.start), + ) } func createMetricsProcessor( @@ -95,5 +103,9 @@ func createMetricsProcessor( return nil, fmt.Errorf("create throughputmeasurementprocessor: %w", err) } - return processorhelper.NewMetricsProcessor(ctx, set, cfg, nextConsumer, tmp.processMetrics, processorhelper.WithCapabilities(consumerCapabilities)) + return processorhelper.NewMetricsProcessor( + ctx, set, cfg, nextConsumer, tmp.processMetrics, + processorhelper.WithCapabilities(consumerCapabilities), + processorhelper.WithStart(tmp.start), + ) } diff --git a/processor/throughputmeasurementprocessor/go.mod b/processor/throughputmeasurementprocessor/go.mod index 18f0c39d9..68c747e13 100644 --- a/processor/throughputmeasurementprocessor/go.mod +++ b/processor/throughputmeasurementprocessor/go.mod @@ -3,6 +3,7 @@ module github.com/observiq/bindplane-agent/processor/throughputmeasurementproces go 1.21.9 require ( + github.com/observiq/bindplane-agent/internal/measurements v1.59.0 github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden v0.107.0 github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.107.0 github.com/stretchr/testify v1.9.0 @@ -43,3 +44,5 @@ require ( google.golang.org/protobuf v1.34.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace github.com/observiq/bindplane-agent/internal/measurements => ../../internal/measurements diff --git a/processor/throughputmeasurementprocessor/ocb_registry.go b/processor/throughputmeasurementprocessor/ocb_registry.go new file mode 100644 index 000000000..2a7ecceb3 --- /dev/null +++ b/processor/throughputmeasurementprocessor/ocb_registry.go @@ -0,0 +1,46 @@ +// Copyright observIQ, Inc. +// +// 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. + +//go:build !bindplane + +package throughputmeasurementprocessor + +import ( + "fmt" + + "github.com/observiq/bindplane-agent/internal/measurements" + "go.opentelemetry.io/collector/component" +) + +// GetThroughputRegistry returns the throughput registry that should be registered to based on the component ID. +// nil, nil may be returned by this function. In this case, the processor should not register it's throughput measurements anywhere. +func GetThroughputRegistry(host component.Host, bindplane component.ID) (measurements.ThroughputMeasurementsRegistry, error) { + var emptyComponentID component.ID + if bindplane == emptyComponentID { + // No bindplane component referenced, so we won't register our measurements anywhere. + return nil, nil + } + + ext, ok := host.GetExtensions()[bindplane] + if !ok { + return nil, fmt.Errorf("bindplane extension %q does not exist", bindplane) + } + + registry, ok := ext.(measurements.ThroughputMeasurementsRegistry) + if !ok { + return nil, fmt.Errorf("extension %q is not an throughput message registry", bindplane) + } + + return registry, nil +} diff --git a/processor/throughputmeasurementprocessor/processor.go b/processor/throughputmeasurementprocessor/processor.go index 8e61b8c13..4f57a19d2 100644 --- a/processor/throughputmeasurementprocessor/processor.go +++ b/processor/throughputmeasurementprocessor/processor.go @@ -19,108 +19,59 @@ import ( "fmt" "math/rand" + "github.com/observiq/bindplane-agent/internal/measurements" + "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/pdata/plog" "go.opentelemetry.io/collector/pdata/pmetric" "go.opentelemetry.io/collector/pdata/ptrace" - "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" "go.uber.org/zap" ) -const processorAttributeName = "processor" - type throughputMeasurementProcessor struct { - logger *zap.Logger - enabled bool - samplingCutOffRatio float64 - attrSet attribute.Set - logSize, metricSize, traceSize metric.Int64Counter - logCount, datapointCount, spanCount metric.Int64Counter - tracesSizer ptrace.Sizer - metricsSizer pmetric.Sizer - logsSizer plog.Sizer + logger *zap.Logger + enabled bool + measurements *measurements.ThroughputMeasurements + samplingCutOffRatio float64 + processorID string + bindplane component.ID } func newThroughputMeasurementProcessor(logger *zap.Logger, mp metric.MeterProvider, cfg *Config, processorID string) (*throughputMeasurementProcessor, error) { - meter := mp.Meter("github.com/observiq/bindplane-agent/processor/throughputmeasurementprocessor") - - logSize, err := meter.Int64Counter( - metricName("log_data_size"), - metric.WithDescription("Size of the log package passed to the processor"), - metric.WithUnit("By"), - ) - if err != nil { - return nil, fmt.Errorf("create log_data_size counter: %w", err) - } - - metricSize, err := meter.Int64Counter( - metricName("metric_data_size"), - metric.WithDescription("Size of the metric package passed to the processor"), - metric.WithUnit("By"), - ) + measurements, err := measurements.NewThroughputMeasurements(mp, processorID, cfg.ExtraLabels) if err != nil { - return nil, fmt.Errorf("create metric_data_size counter: %w", err) + return nil, fmt.Errorf("create throughput measurements: %w", err) } - traceSize, err := meter.Int64Counter( - metricName("trace_data_size"), - metric.WithDescription("Size of the trace package passed to the processor"), - metric.WithUnit("By"), - ) - if err != nil { - return nil, fmt.Errorf("create trace_data_size counter: %w", err) - } + return &throughputMeasurementProcessor{ + logger: logger, + enabled: cfg.Enabled, + measurements: measurements, + samplingCutOffRatio: cfg.SamplingRatio, + processorID: processorID, + bindplane: cfg.BindplaneExtension, + }, nil +} - logCount, err := meter.Int64Counter( - metricName("log_count"), - metric.WithDescription("Count of the number log records passed to the processor"), - metric.WithUnit("{logs}"), - ) - if err != nil { - return nil, fmt.Errorf("create log_count counter: %w", err) - } +func (tmp *throughputMeasurementProcessor) start(_ context.Context, host component.Host) error { - datapointCount, err := meter.Int64Counter( - metricName("metric_count"), - metric.WithDescription("Count of the number datapoints passed to the processor"), - metric.WithUnit("{datapoints}"), - ) + registry, err := GetThroughputRegistry(host, tmp.bindplane) if err != nil { - return nil, fmt.Errorf("create metric_count counter: %w", err) + return fmt.Errorf("get throughput registry: %w", err) } - spanCount, err := meter.Int64Counter( - metricName("trace_count"), - metric.WithDescription("Count of the number spans passed to the processor"), - metric.WithUnit("{spans}"), - ) - if err != nil { - return nil, fmt.Errorf("create trace_count counter: %w", err) + if registry != nil { + registry.RegisterThroughputMeasurements(tmp.processorID, tmp.measurements) } - return &throughputMeasurementProcessor{ - logger: logger, - enabled: cfg.Enabled, - samplingCutOffRatio: cfg.SamplingRatio, - logSize: logSize, - metricSize: metricSize, - traceSize: traceSize, - logCount: logCount, - datapointCount: datapointCount, - spanCount: spanCount, - attrSet: attribute.NewSet(attribute.String(processorAttributeName, processorID)), - tracesSizer: &ptrace.ProtoMarshaler{}, - metricsSizer: &pmetric.ProtoMarshaler{}, - logsSizer: &plog.ProtoMarshaler{}, - }, nil + return nil } func (tmp *throughputMeasurementProcessor) processTraces(ctx context.Context, td ptrace.Traces) (ptrace.Traces, error) { if tmp.enabled { //#nosec G404 -- randomly generated number is not used for security purposes. It's ok if it's weak if rand.Float64() <= tmp.samplingCutOffRatio { - tmp.traceSize.Add(ctx, int64(tmp.tracesSizer.TracesSize(td)), metric.WithAttributeSet(tmp.attrSet)) - tmp.spanCount.Add(ctx, int64(td.SpanCount()), metric.WithAttributeSet(tmp.attrSet)) + tmp.measurements.AddTraces(ctx, td) } } @@ -131,8 +82,7 @@ func (tmp *throughputMeasurementProcessor) processLogs(ctx context.Context, ld p if tmp.enabled { //#nosec G404 -- randomly generated number is not used for security purposes. It's ok if it's weak if rand.Float64() <= tmp.samplingCutOffRatio { - tmp.logSize.Add(ctx, int64(tmp.logsSizer.LogsSize(ld)), metric.WithAttributeSet(tmp.attrSet)) - tmp.logCount.Add(ctx, int64(ld.LogRecordCount()), metric.WithAttributeSet(tmp.attrSet)) + tmp.measurements.AddLogs(ctx, ld) } } @@ -143,14 +93,9 @@ func (tmp *throughputMeasurementProcessor) processMetrics(ctx context.Context, m if tmp.enabled { //#nosec G404 -- randomly generated number is not used for security purposes. It's ok if it's weak if rand.Float64() <= tmp.samplingCutOffRatio { - tmp.metricSize.Add(ctx, int64(tmp.metricsSizer.MetricsSize(md)), metric.WithAttributeSet(tmp.attrSet)) - tmp.datapointCount.Add(ctx, int64(md.DataPointCount()), metric.WithAttributeSet(tmp.attrSet)) + tmp.measurements.AddMetrics(ctx, md) } } return md, nil } - -func metricName(metric string) string { - return fmt.Sprintf("otelcol_processor_throughputmeasurement_%s", metric) -} diff --git a/processor/throughputmeasurementprocessor/processor_test.go b/processor/throughputmeasurementprocessor/processor_test.go index 364ec07ef..9e30a1f27 100644 --- a/processor/throughputmeasurementprocessor/processor_test.go +++ b/processor/throughputmeasurementprocessor/processor_test.go @@ -69,7 +69,7 @@ func TestProcessor_Logs(t *testing.T) { sum := metric.Data.(metricdata.Sum[int64]) require.Equal(t, 1, len(sum.DataPoints)) - processorAttr, ok := sum.DataPoints[0].Attributes.Value(attribute.Key(processorAttributeName)) + processorAttr, ok := sum.DataPoints[0].Attributes.Value(attribute.Key("processor")) require.True(t, ok, "processor attribute was not found") require.Equal(t, processorID, processorAttr.AsString()) @@ -79,7 +79,7 @@ func TestProcessor_Logs(t *testing.T) { sum := metric.Data.(metricdata.Sum[int64]) require.Equal(t, 1, len(sum.DataPoints)) - processorAttr, ok := sum.DataPoints[0].Attributes.Value(attribute.Key(processorAttributeName)) + processorAttr, ok := sum.DataPoints[0].Attributes.Value(attribute.Key("processor")) require.True(t, ok, "processor attribute was not found") require.Equal(t, processorID, processorAttr.AsString()) @@ -132,7 +132,7 @@ func TestProcessor_Metrics(t *testing.T) { sum := metric.Data.(metricdata.Sum[int64]) require.Equal(t, 1, len(sum.DataPoints)) - processorAttr, ok := sum.DataPoints[0].Attributes.Value(attribute.Key(processorAttributeName)) + processorAttr, ok := sum.DataPoints[0].Attributes.Value(attribute.Key("processor")) require.True(t, ok, "processor attribute was not found") require.Equal(t, processorID, processorAttr.AsString()) @@ -142,7 +142,7 @@ func TestProcessor_Metrics(t *testing.T) { sum := metric.Data.(metricdata.Sum[int64]) require.Equal(t, 1, len(sum.DataPoints)) - processorAttr, ok := sum.DataPoints[0].Attributes.Value(attribute.Key(processorAttributeName)) + processorAttr, ok := sum.DataPoints[0].Attributes.Value(attribute.Key("processor")) require.True(t, ok, "processor attribute was not found") require.Equal(t, processorID, processorAttr.AsString()) @@ -195,7 +195,7 @@ func TestProcessor_Traces(t *testing.T) { sum := metric.Data.(metricdata.Sum[int64]) require.Equal(t, 1, len(sum.DataPoints)) - processorAttr, ok := sum.DataPoints[0].Attributes.Value(attribute.Key(processorAttributeName)) + processorAttr, ok := sum.DataPoints[0].Attributes.Value(attribute.Key("processor")) require.True(t, ok, "processor attribute was not found") require.Equal(t, processorID, processorAttr.AsString()) @@ -205,7 +205,7 @@ func TestProcessor_Traces(t *testing.T) { sum := metric.Data.(metricdata.Sum[int64]) require.Equal(t, 1, len(sum.DataPoints)) - processorAttr, ok := sum.DataPoints[0].Attributes.Value(attribute.Key(processorAttributeName)) + processorAttr, ok := sum.DataPoints[0].Attributes.Value(attribute.Key("processor")) require.True(t, ok, "processor attribute was not found") require.Equal(t, processorID, processorAttr.AsString()) @@ -265,7 +265,7 @@ func TestProcessor_Logs_TwoInstancesSameID(t *testing.T) { sum := metric.Data.(metricdata.Sum[int64]) require.Equal(t, 1, len(sum.DataPoints)) - processorAttr, ok := sum.DataPoints[0].Attributes.Value(attribute.Key(processorAttributeName)) + processorAttr, ok := sum.DataPoints[0].Attributes.Value(attribute.Key("processor")) require.True(t, ok, "processor attribute was not found") require.Equal(t, processorID, processorAttr.AsString()) @@ -275,7 +275,7 @@ func TestProcessor_Logs_TwoInstancesSameID(t *testing.T) { sum := metric.Data.(metricdata.Sum[int64]) require.Equal(t, 1, len(sum.DataPoints)) - processorAttr, ok := sum.DataPoints[0].Attributes.Value(attribute.Key(processorAttributeName)) + processorAttr, ok := sum.DataPoints[0].Attributes.Value(attribute.Key("processor")) require.True(t, ok, "processor attribute was not found") require.Equal(t, processorID, processorAttr.AsString()) @@ -340,7 +340,7 @@ func TestProcessor_Logs_TwoInstancesDifferentID(t *testing.T) { require.Equal(t, 2, len(sum.DataPoints)) for _, dp := range sum.DataPoints { - processorAttr, ok := dp.Attributes.Value(attribute.Key(processorAttributeName)) + processorAttr, ok := dp.Attributes.Value(attribute.Key("processor")) require.True(t, ok, "processor attribute was not found") switch processorAttr.AsString() { @@ -358,7 +358,7 @@ func TestProcessor_Logs_TwoInstancesDifferentID(t *testing.T) { require.Equal(t, 2, len(sum.DataPoints)) for _, dp := range sum.DataPoints { - processorAttr, ok := dp.Attributes.Value(attribute.Key(processorAttributeName)) + processorAttr, ok := dp.Attributes.Value(attribute.Key("processor")) require.True(t, ok, "processor attribute was not found") switch processorAttr.AsString() {