From bbd347235b2c2bfdf29290e46545de74b7131d0f Mon Sep 17 00:00:00 2001 From: Alex Boten <223565+codeboten@users.noreply.github.com> Date: Thu, 30 May 2024 13:51:32 -0700 Subject: [PATCH] [service] use mdatagen for service metrics This reverts the reverts https://github.com/open-telemetry/opentelemetry-collector/pull/10271 and adds a mechanism to skip adding a create settings method for the service package component test. Will need to figure out if servicetelemetry.TelemetrySettings should be renamed to fit w/ the other CreateSettings structs before removing this check. Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com> --- cmd/mdatagen/loader.go | 5 +- .../component_telemetry_test.go.tmpl | 3 + service/generated_component_telemetry_test.go | 64 +++++++++++++++++++ .../internal/metadata/generated_telemetry.go | 44 ++++++++++--- service/metadata.yaml | 1 + 5 files changed, 106 insertions(+), 11 deletions(-) create mode 100644 service/generated_component_telemetry_test.go diff --git a/cmd/mdatagen/loader.go b/cmd/mdatagen/loader.go index a2f1de68419..ffc9d9644df 100644 --- a/cmd/mdatagen/loader.go +++ b/cmd/mdatagen/loader.go @@ -228,8 +228,9 @@ type tests struct { } type telemetry struct { - Level configtelemetry.Level `mapstructure:"level"` - Metrics map[metricName]metric `mapstructure:"metrics"` + SkipCreateSettings bool `mapstructure:"skip_create_settings"` + Level configtelemetry.Level `mapstructure:"level"` + Metrics map[metricName]metric `mapstructure:"metrics"` } type metadata struct { diff --git a/cmd/mdatagen/templates/component_telemetry_test.go.tmpl b/cmd/mdatagen/templates/component_telemetry_test.go.tmpl index adba6355149..951bec98820 100644 --- a/cmd/mdatagen/templates/component_telemetry_test.go.tmpl +++ b/cmd/mdatagen/templates/component_telemetry_test.go.tmpl @@ -21,6 +21,7 @@ type componentTestTelemetry struct { meterProvider *sdkmetric.MeterProvider } +{{- if not .Telemetry.SkipCreateSettings }} func (tt *componentTestTelemetry) NewCreateSettings() {{ .Status.Class }}.CreateSettings { settings := {{ .Status.Class }}test.NewNopCreateSettings() settings.MeterProvider = tt.meterProvider @@ -29,6 +30,8 @@ func (tt *componentTestTelemetry) NewCreateSettings() {{ .Status.Class }}.Create return settings } +{{- end }} + func setupTestTelemetry() componentTestTelemetry { reader := sdkmetric.NewManualReader() return componentTestTelemetry{ diff --git a/service/generated_component_telemetry_test.go b/service/generated_component_telemetry_test.go new file mode 100644 index 00000000000..49df62f1b8b --- /dev/null +++ b/service/generated_component_telemetry_test.go @@ -0,0 +1,64 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package service + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + sdkmetric "go.opentelemetry.io/otel/sdk/metric" + "go.opentelemetry.io/otel/sdk/metric/metricdata" + "go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest" +) + +type componentTestTelemetry struct { + reader *sdkmetric.ManualReader + meterProvider *sdkmetric.MeterProvider +} + +func setupTestTelemetry() componentTestTelemetry { + reader := sdkmetric.NewManualReader() + return componentTestTelemetry{ + reader: reader, + meterProvider: sdkmetric.NewMeterProvider(sdkmetric.WithReader(reader)), + } +} + +func (tt *componentTestTelemetry) assertMetrics(t *testing.T, expected []metricdata.Metrics) { + var md metricdata.ResourceMetrics + require.NoError(t, tt.reader.Collect(context.Background(), &md)) + // ensure all required metrics are present + for _, want := range expected { + got := tt.getMetric(want.Name, md) + metricdatatest.AssertEqual(t, want, got, metricdatatest.IgnoreTimestamp()) + } + + // ensure no additional metrics are emitted + require.Equal(t, len(expected), tt.len(md)) +} + +func (tt *componentTestTelemetry) getMetric(name string, got metricdata.ResourceMetrics) metricdata.Metrics { + for _, sm := range got.ScopeMetrics { + for _, m := range sm.Metrics { + if m.Name == name { + return m + } + } + } + + return metricdata.Metrics{} +} + +func (tt *componentTestTelemetry) len(got metricdata.ResourceMetrics) int { + metricsCount := 0 + for _, sm := range got.ScopeMetrics { + metricsCount += len(sm.Metrics) + } + + return metricsCount +} + +func (tt *componentTestTelemetry) Shutdown(ctx context.Context) error { + return tt.meterProvider.Shutdown(ctx) +} diff --git a/service/internal/metadata/generated_telemetry.go b/service/internal/metadata/generated_telemetry.go index 3d1c7b37a04..f8d4ae4513a 100644 --- a/service/internal/metadata/generated_telemetry.go +++ b/service/internal/metadata/generated_telemetry.go @@ -6,10 +6,13 @@ import ( "context" "errors" + "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/metric" + "go.opentelemetry.io/otel/metric/noop" "go.opentelemetry.io/otel/trace" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/config/configtelemetry" ) func Meter(settings component.TelemetrySettings) metric.Meter { @@ -35,11 +38,27 @@ type TelemetryBuilder struct { observeProcessRuntimeTotalSysMemoryBytes func() int64 ProcessUptime metric.Float64ObservableCounter observeProcessUptime func() float64 + level configtelemetry.Level + attributeSet attribute.Set } // telemetryBuilderOption applies changes to default builder. type telemetryBuilderOption func(*TelemetryBuilder) +// WithLevel sets the current telemetry level for the component. +func WithLevel(lvl configtelemetry.Level) telemetryBuilderOption { + return func(builder *TelemetryBuilder) { + builder.level = lvl + } +} + +// WithAttributeSet applies a set of attributes for asynchronous instruments. +func WithAttributeSet(set attribute.Set) telemetryBuilderOption { + return func(builder *TelemetryBuilder) { + builder.attributeSet = set + } +} + // WithProcessCPUSecondsCallback sets callback for observable ProcessCPUSeconds metric. func WithProcessCPUSecondsCallback(cb func() float64) telemetryBuilderOption { return func(builder *TelemetryBuilder) { @@ -85,18 +104,25 @@ func WithProcessUptimeCallback(cb func() float64) telemetryBuilderOption { // NewTelemetryBuilder provides a struct with methods to update all internal telemetry // for a component func NewTelemetryBuilder(settings component.TelemetrySettings, options ...telemetryBuilderOption) (*TelemetryBuilder, error) { - builder := TelemetryBuilder{} + builder := TelemetryBuilder{level: configtelemetry.LevelBasic} for _, op := range options { op(&builder) } - var err, errs error - meter := Meter(settings) + var ( + err, errs error + meter metric.Meter + ) + if builder.level >= configtelemetry.LevelBasic { + meter = Meter(settings) + } else { + meter = noop.Meter{} + } builder.ProcessCPUSeconds, err = meter.Float64ObservableCounter( "process_cpu_seconds", metric.WithDescription("Total CPU user and system time in seconds"), metric.WithUnit("s"), metric.WithFloat64Callback(func(_ context.Context, o metric.Float64Observer) error { - o.Observe(builder.observeProcessCPUSeconds()) + o.Observe(builder.observeProcessCPUSeconds(), metric.WithAttributeSet(builder.attributeSet)) return nil }), ) @@ -106,7 +132,7 @@ func NewTelemetryBuilder(settings component.TelemetrySettings, options ...teleme metric.WithDescription("Total physical memory (resident set size)"), metric.WithUnit("By"), metric.WithInt64Callback(func(_ context.Context, o metric.Int64Observer) error { - o.Observe(builder.observeProcessMemoryRss()) + o.Observe(builder.observeProcessMemoryRss(), metric.WithAttributeSet(builder.attributeSet)) return nil }), ) @@ -116,7 +142,7 @@ func NewTelemetryBuilder(settings component.TelemetrySettings, options ...teleme metric.WithDescription("Bytes of allocated heap objects (see 'go doc runtime.MemStats.HeapAlloc')"), metric.WithUnit("By"), metric.WithInt64Callback(func(_ context.Context, o metric.Int64Observer) error { - o.Observe(builder.observeProcessRuntimeHeapAllocBytes()) + o.Observe(builder.observeProcessRuntimeHeapAllocBytes(), metric.WithAttributeSet(builder.attributeSet)) return nil }), ) @@ -126,7 +152,7 @@ func NewTelemetryBuilder(settings component.TelemetrySettings, options ...teleme metric.WithDescription("Cumulative bytes allocated for heap objects (see 'go doc runtime.MemStats.TotalAlloc')"), metric.WithUnit("By"), metric.WithInt64Callback(func(_ context.Context, o metric.Int64Observer) error { - o.Observe(builder.observeProcessRuntimeTotalAllocBytes()) + o.Observe(builder.observeProcessRuntimeTotalAllocBytes(), metric.WithAttributeSet(builder.attributeSet)) return nil }), ) @@ -136,7 +162,7 @@ func NewTelemetryBuilder(settings component.TelemetrySettings, options ...teleme metric.WithDescription("Total bytes of memory obtained from the OS (see 'go doc runtime.MemStats.Sys')"), metric.WithUnit("By"), metric.WithInt64Callback(func(_ context.Context, o metric.Int64Observer) error { - o.Observe(builder.observeProcessRuntimeTotalSysMemoryBytes()) + o.Observe(builder.observeProcessRuntimeTotalSysMemoryBytes(), metric.WithAttributeSet(builder.attributeSet)) return nil }), ) @@ -146,7 +172,7 @@ func NewTelemetryBuilder(settings component.TelemetrySettings, options ...teleme metric.WithDescription("Uptime of the process"), metric.WithUnit("s"), metric.WithFloat64Callback(func(_ context.Context, o metric.Float64Observer) error { - o.Observe(builder.observeProcessUptime()) + o.Observe(builder.observeProcessUptime(), metric.WithAttributeSet(builder.attributeSet)) return nil }), ) diff --git a/service/metadata.yaml b/service/metadata.yaml index d5417740d57..0f320450d70 100644 --- a/service/metadata.yaml +++ b/service/metadata.yaml @@ -15,6 +15,7 @@ tests: - "go.opentelemetry.io/collector/service/internal/proctelemetry.InitPrometheusServer.func1" telemetry: + skip_create_settings: true metrics: process_uptime: enabled: true