Skip to content

Commit

Permalink
Extract componenttest.Telemetry as generic struct for telemetry testing
Browse files Browse the repository at this point in the history
Signed-off-by: Bogdan Drutu <[email protected]>
  • Loading branch information
bogdandrutu committed Jan 22, 2025
1 parent b09a65b commit a97ea13
Show file tree
Hide file tree
Showing 18 changed files with 144 additions and 371 deletions.
25 changes: 25 additions & 0 deletions .chloggen/extracttesttel.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Use this changelog template to create an entry for release notes.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver)
component: componenttest

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Extract componenttest.Telemetry as generic struct for telemetry testing

# One or more tracking issues or pull requests related to the change
issues: [12151]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:

# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: [api]
4 changes: 2 additions & 2 deletions cmd/mdatagen/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,9 @@ require (
go.opentelemetry.io/collector/scraper/scrapertest v0.118.0
go.opentelemetry.io/collector/semconv v0.118.0
go.opentelemetry.io/otel/metric v1.34.0
go.opentelemetry.io/otel/sdk v1.34.0
go.opentelemetry.io/otel/sdk/metric v1.34.0
go.opentelemetry.io/otel/trace v1.34.0
go.uber.org/goleak v1.3.0
go.uber.org/multierr v1.11.0
go.uber.org/zap v1.27.0
golang.org/x/text v0.21.0
)
Expand Down Expand Up @@ -60,6 +58,8 @@ require (
go.opentelemetry.io/collector/processor/xprocessor v0.118.0 // indirect
go.opentelemetry.io/collector/receiver/xreceiver v0.118.0 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect
go.opentelemetry.io/otel/sdk v1.34.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/net v0.33.0 // indirect
golang.org/x/sys v0.29.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241104194629-dd2ea8efbc28 // indirect
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 4 additions & 34 deletions cmd/mdatagen/internal/templates/telemetrytest.go.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -7,40 +7,25 @@ import (
"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"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/sdk/trace/tracetest"
"go.uber.org/multierr"

{{- if or isConnector isExporter isExtension isProcessor isReceiver isScraper }}
"go.opentelemetry.io/collector/component"
{{- end }}
"go.opentelemetry.io/collector/component/componenttest"
"go.opentelemetry.io/collector/config/configtelemetry"
{{- if or isConnector isExporter isExtension isProcessor isReceiver isScraper }}
"go.opentelemetry.io/collector/{{ .Status.Class }}"
"go.opentelemetry.io/collector/{{ .Status.Class }}/{{ .Status.Class }}test"
{{- end }}
)

type Telemetry struct {
Reader *sdkmetric.ManualReader
SpanRecorder *tracetest.SpanRecorder

meterProvider *sdkmetric.MeterProvider
traceProvider *sdktrace.TracerProvider
componenttest.Telemetry
}

func SetupTelemetry() Telemetry {
reader := sdkmetric.NewManualReader()
spanRecorder := new(tracetest.SpanRecorder)
return Telemetry{
Reader: reader,
SpanRecorder: spanRecorder,

meterProvider: sdkmetric.NewMeterProvider(sdkmetric.WithReader(reader)),
traceProvider: sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(spanRecorder)),
}
return Telemetry{ Telemetry: componenttest.NewTelemetry() }
}

{{- if or isConnector isExporter isExtension isProcessor isReceiver isScraper }}
Expand All @@ -52,14 +37,6 @@ func (tt *Telemetry) NewSettings() {{ .Status.Class }}.Settings {
}
{{- end }}

func (tt *Telemetry) NewTelemetrySettings() component.TelemetrySettings {
set := componenttest.NewNopTelemetrySettings()
set.MeterProvider = tt.meterProvider
set.MetricsLevel = configtelemetry.LevelDetailed
set.TracerProvider = tt.traceProvider
return set
}

func (tt *Telemetry) AssertMetrics(t *testing.T, expected []metricdata.Metrics, opts ...metricdatatest.Option) {
var md metricdata.ResourceMetrics
require.NoError(t, tt.Reader.Collect(context.Background(), &md))
Expand All @@ -73,13 +50,6 @@ func (tt *Telemetry) AssertMetrics(t *testing.T, expected []metricdata.Metrics,
require.Equal(t, len(expected), lenMetrics(md))
}

func (tt *Telemetry) Shutdown(ctx context.Context) error {
return multierr.Combine(
tt.meterProvider.Shutdown(ctx),
tt.traceProvider.Shutdown(ctx),
)
}

func getMetric(name string, got metricdata.ResourceMetrics) metricdata.Metrics {
for _, sm := range got.ScopeMetrics {
for _, m := range sm.Metrics {
Expand Down
71 changes: 18 additions & 53 deletions component/componenttest/obsreporttest.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,9 @@
package componenttest // import "go.opentelemetry.io/collector/component/componenttest"

import (
"context"
"errors"

"go.opentelemetry.io/otel/attribute"
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/sdk/trace/tracetest"
"go.uber.org/zap"

"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/config/configtelemetry"
"go.opentelemetry.io/collector/pdata/pcommon"
)

const (
Expand All @@ -33,99 +23,74 @@ const (
)

type TestTelemetry struct {
id component.ID
ts component.TelemetrySettings
SpanRecorder *tracetest.SpanRecorder
reader *sdkmetric.ManualReader
Telemetry
id component.ID
}

// CheckExporterTraces checks that for the current exported values for trace exporter metrics match given values.
func (tts *TestTelemetry) CheckExporterTraces(sentSpans, sendFailedSpans int64) error {
return checkExporterTraces(tts.reader, tts.id, sentSpans, sendFailedSpans)
return checkExporterTraces(tts.Reader, tts.id, sentSpans, sendFailedSpans)

Check warning on line 32 in component/componenttest/obsreporttest.go

View check run for this annotation

Codecov / codecov/patch

component/componenttest/obsreporttest.go#L32

Added line #L32 was not covered by tests
}

// CheckExporterMetrics checks that for the current exported values for metrics exporter metrics match given values.
func (tts *TestTelemetry) CheckExporterMetrics(sentMetricsPoints, sendFailedMetricsPoints int64) error {
return checkExporterMetrics(tts.reader, tts.id, sentMetricsPoints, sendFailedMetricsPoints)
return checkExporterMetrics(tts.Reader, tts.id, sentMetricsPoints, sendFailedMetricsPoints)

Check warning on line 37 in component/componenttest/obsreporttest.go

View check run for this annotation

Codecov / codecov/patch

component/componenttest/obsreporttest.go#L37

Added line #L37 was not covered by tests
}

func (tts *TestTelemetry) CheckExporterEnqueueFailedMetrics(enqueueFailed int64) error {
return checkExporterEnqueueFailed(tts.reader, tts.id, "metric_points", enqueueFailed)
return checkExporterEnqueueFailed(tts.Reader, tts.id, "metric_points", enqueueFailed)

Check warning on line 41 in component/componenttest/obsreporttest.go

View check run for this annotation

Codecov / codecov/patch

component/componenttest/obsreporttest.go#L41

Added line #L41 was not covered by tests
}

func (tts *TestTelemetry) CheckExporterEnqueueFailedTraces(enqueueFailed int64) error {
return checkExporterEnqueueFailed(tts.reader, tts.id, "spans", enqueueFailed)
return checkExporterEnqueueFailed(tts.Reader, tts.id, "spans", enqueueFailed)

Check warning on line 45 in component/componenttest/obsreporttest.go

View check run for this annotation

Codecov / codecov/patch

component/componenttest/obsreporttest.go#L45

Added line #L45 was not covered by tests
}

func (tts *TestTelemetry) CheckExporterEnqueueFailedLogs(enqueueFailed int64) error {
return checkExporterEnqueueFailed(tts.reader, tts.id, "log_records", enqueueFailed)
return checkExporterEnqueueFailed(tts.Reader, tts.id, "log_records", enqueueFailed)

Check warning on line 49 in component/componenttest/obsreporttest.go

View check run for this annotation

Codecov / codecov/patch

component/componenttest/obsreporttest.go#L49

Added line #L49 was not covered by tests
}

// CheckExporterLogs checks that for the current exported values for logs exporter metrics match given values.
func (tts *TestTelemetry) CheckExporterLogs(sentLogRecords, sendFailedLogRecords int64) error {
return checkExporterLogs(tts.reader, tts.id, sentLogRecords, sendFailedLogRecords)
return checkExporterLogs(tts.Reader, tts.id, sentLogRecords, sendFailedLogRecords)

Check warning on line 54 in component/componenttest/obsreporttest.go

View check run for this annotation

Codecov / codecov/patch

component/componenttest/obsreporttest.go#L54

Added line #L54 was not covered by tests
}

func (tts *TestTelemetry) CheckExporterMetricGauge(metric string, val int64, extraAttrs ...attribute.KeyValue) error {
attrs := attributesForExporterMetrics(tts.id, extraAttrs...)
return checkIntGauge(tts.reader, metric, val, attrs)
return checkIntGauge(tts.Reader, metric, val, attrs)

Check warning on line 59 in component/componenttest/obsreporttest.go

View check run for this annotation

Codecov / codecov/patch

component/componenttest/obsreporttest.go#L59

Added line #L59 was not covered by tests
}

// CheckReceiverTraces checks that for the current exported values for trace receiver metrics match given values.
func (tts *TestTelemetry) CheckReceiverTraces(protocol string, acceptedSpans, droppedSpans int64) error {
return checkReceiverTraces(tts.reader, tts.id, protocol, acceptedSpans, droppedSpans)
return checkReceiverTraces(tts.Reader, tts.id, protocol, acceptedSpans, droppedSpans)

Check warning on line 64 in component/componenttest/obsreporttest.go

View check run for this annotation

Codecov / codecov/patch

component/componenttest/obsreporttest.go#L64

Added line #L64 was not covered by tests
}

// CheckReceiverLogs checks that for the current exported values for logs receiver metrics match given values.
func (tts *TestTelemetry) CheckReceiverLogs(protocol string, acceptedLogRecords, droppedLogRecords int64) error {
return checkReceiverLogs(tts.reader, tts.id, protocol, acceptedLogRecords, droppedLogRecords)
return checkReceiverLogs(tts.Reader, tts.id, protocol, acceptedLogRecords, droppedLogRecords)

Check warning on line 69 in component/componenttest/obsreporttest.go

View check run for this annotation

Codecov / codecov/patch

component/componenttest/obsreporttest.go#L69

Added line #L69 was not covered by tests
}

// CheckReceiverMetrics checks that for the current exported values for metrics receiver metrics match given values.
func (tts *TestTelemetry) CheckReceiverMetrics(protocol string, acceptedMetricPoints, droppedMetricPoints int64) error {
return checkReceiverMetrics(tts.reader, tts.id, protocol, acceptedMetricPoints, droppedMetricPoints)
return checkReceiverMetrics(tts.Reader, tts.id, protocol, acceptedMetricPoints, droppedMetricPoints)

Check warning on line 74 in component/componenttest/obsreporttest.go

View check run for this annotation

Codecov / codecov/patch

component/componenttest/obsreporttest.go#L74

Added line #L74 was not covered by tests
}

// Deprecated: [v0.118.0] use metadatatest.AssertMetrics instead.
// CheckScraperMetrics checks that for the current exported values for metrics scraper metrics match given values.
func (tts *TestTelemetry) CheckScraperMetrics(receiver component.ID, scraper component.ID, scrapedMetricPoints, erroredMetricPoints int64) error {
return checkScraperMetrics(tts.reader, receiver, scraper, scrapedMetricPoints, erroredMetricPoints)
}

// Shutdown unregisters any views and shuts down the SpanRecorder
func (tts *TestTelemetry) Shutdown(ctx context.Context) error {
return errors.Join(
tts.ts.TracerProvider.(*sdktrace.TracerProvider).Shutdown(ctx),
tts.ts.MeterProvider.(*sdkmetric.MeterProvider).Shutdown(ctx))
return checkScraperMetrics(tts.Reader, receiver, scraper, scrapedMetricPoints, erroredMetricPoints)

Check warning on line 80 in component/componenttest/obsreporttest.go

View check run for this annotation

Codecov / codecov/patch

component/componenttest/obsreporttest.go#L80

Added line #L80 was not covered by tests
}

// TelemetrySettings returns the TestTelemetry's TelemetrySettings
func (tts *TestTelemetry) TelemetrySettings() component.TelemetrySettings {
return tts.ts
return tts.NewTelemetrySettings()

Check warning on line 85 in component/componenttest/obsreporttest.go

View check run for this annotation

Codecov / codecov/patch

component/componenttest/obsreporttest.go#L85

Added line #L85 was not covered by tests
}

// SetupTelemetry sets up the testing environment to check the metrics recorded by receivers, producers, or exporters.
// The caller must pass the ID of the component being tested. The ID will be used by the CreateSettings and Check methods.
// The caller must defer a call to `Shutdown` on the returned TestTelemetry.
func SetupTelemetry(id component.ID) (TestTelemetry, error) {
settings := TestTelemetry{
id: id,
reader: sdkmetric.NewManualReader(),
SpanRecorder: new(tracetest.SpanRecorder),
}

mp := sdkmetric.NewMeterProvider(
sdkmetric.WithResource(resource.Empty()),
sdkmetric.WithReader(settings.reader),
)

settings.ts = component.TelemetrySettings{
Logger: zap.NewNop(),
TracerProvider: sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(settings.SpanRecorder)),
MeterProvider: mp,
MetricsLevel: configtelemetry.LevelDetailed,
Resource: pcommon.NewResource(),
}

return settings, nil
return TestTelemetry{
Telemetry: NewTelemetry(),
id: id,
}, nil

Check warning on line 95 in component/componenttest/obsreporttest.go

View check run for this annotation

Codecov / codecov/patch

component/componenttest/obsreporttest.go#L92-L95

Added lines #L92 - L95 were not covered by tests
}
47 changes: 47 additions & 0 deletions component/componenttest/telemetry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package componenttest

import (
"context"
"errors"

sdkmetric "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/resource"
sdktrace "go.opentelemetry.io/otel/sdk/trace"
"go.opentelemetry.io/otel/sdk/trace/tracetest"

"go.opentelemetry.io/collector/component"
"go.opentelemetry.io/collector/config/configtelemetry"
)

type Telemetry struct {
Reader *sdkmetric.ManualReader
SpanRecorder *tracetest.SpanRecorder
meterProvider *sdkmetric.MeterProvider
traceProvider *sdktrace.TracerProvider
}

func NewTelemetry() Telemetry {
reader := sdkmetric.NewManualReader()
spanRecorder := new(tracetest.SpanRecorder)
return Telemetry{
Reader: reader,
SpanRecorder: spanRecorder,
meterProvider: sdkmetric.NewMeterProvider(sdkmetric.WithResource(resource.Empty()), sdkmetric.WithReader(reader)),
traceProvider: sdktrace.NewTracerProvider(sdktrace.WithResource(resource.Empty()), sdktrace.WithSpanProcessor(spanRecorder)),
}
}

func (tt *Telemetry) NewTelemetrySettings() component.TelemetrySettings {
set := NewNopTelemetrySettings()
set.MeterProvider = tt.meterProvider
set.MetricsLevel = configtelemetry.LevelDetailed
set.TracerProvider = tt.traceProvider
return set
}

func (tt *Telemetry) Shutdown(ctx context.Context) error {
return errors.Join(
tt.meterProvider.Shutdown(ctx),
tt.traceProvider.Shutdown(ctx),
)
}
Loading

0 comments on commit a97ea13

Please sign in to comment.