Skip to content

Commit

Permalink
Add helper to get a metric for componentest.Telemetry (#12215)
Browse files Browse the repository at this point in the history
Signed-off-by: Bogdan Drutu <[email protected]>
  • Loading branch information
bogdandrutu authored Jan 30, 2025
1 parent c119b2a commit fb8bb93
Show file tree
Hide file tree
Showing 13 changed files with 173 additions and 156 deletions.
25 changes: 25 additions & 0 deletions .chloggen/add-get-metrics.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: Add helper to get a metric for componentest.Telemetry

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

# (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]

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

9 changes: 2 additions & 7 deletions cmd/mdatagen/internal/templates/telemetrytest.go.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -67,18 +67,13 @@ func AssertEqual{{ $name.Render }}(t *testing.T, tt componenttest.Telemetry, dps
DataPoints: dps,
},
}
got := getMetric(t, tt, "otelcol_{{ $name }}")
got, err := tt.GetMetric("otelcol_{{ $name }}")
require.NoError(t, err)
metricdatatest.AssertEqual(t, want, got, opts...)
}

{{- end }}

func getMetric(t *testing.T, tt componenttest.Telemetry, name string) metricdata.Metrics {
var md metricdata.ResourceMetrics
require.NoError(t, tt.Reader.Collect(context.Background(), &md))
return getMetricFromResource(name, md)
}

func getMetricFromResource(name string, got metricdata.ResourceMetrics) metricdata.Metrics {
for _, sm := range got.ScopeMetrics {
for _, m := range sm.Metrics {
Expand Down
20 changes: 10 additions & 10 deletions component/componenttest/obsreporttest.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,55 +33,55 @@ type TestTelemetry struct {
// Deprecated: [v0.119.0] use the metadatatest.AssertEqualMetric series of functions instead.
// 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.Telemetry, tts.id, sentSpans, sendFailedSpans)
}

// Deprecated: [v0.119.0] use the metadatatest.AssertEqualMetric series of functions instead.
// 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.Telemetry, tts.id, sentMetricsPoints, sendFailedMetricsPoints)
}

// Deprecated: [v0.119.0] use the metadatatest.AssertEqualMetric series of functions instead.
func (tts *TestTelemetry) CheckExporterEnqueueFailedMetrics(enqueueFailed int64) error {
return checkExporterEnqueueFailed(tts.Reader, tts.id, "metric_points", enqueueFailed)
return checkExporterEnqueueFailed(&tts.Telemetry, tts.id, "metric_points", enqueueFailed)
}

// Deprecated: [v0.119.0] use the metadatatest.AssertEqualMetric series of functions instead.
func (tts *TestTelemetry) CheckExporterEnqueueFailedTraces(enqueueFailed int64) error {
return checkExporterEnqueueFailed(tts.Reader, tts.id, "spans", enqueueFailed)
return checkExporterEnqueueFailed(&tts.Telemetry, tts.id, "spans", enqueueFailed)
}

// Deprecated: [v0.119.0] use the metadatatest.AssertEqualMetric series of functions instead.
func (tts *TestTelemetry) CheckExporterEnqueueFailedLogs(enqueueFailed int64) error {
return checkExporterEnqueueFailed(tts.Reader, tts.id, "log_records", enqueueFailed)
return checkExporterEnqueueFailed(&tts.Telemetry, tts.id, "log_records", enqueueFailed)
}

// Deprecated: [v0.119.0] use the metadatatest.AssertEqualMetric series of functions instead.
// 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.Telemetry, tts.id, sentLogRecords, sendFailedLogRecords)
}

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.Telemetry, metric, val, attrs)
}

// 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.Telemetry, tts.id, protocol, acceptedSpans, droppedSpans)
}

// Deprecated: [v0.119.0] use the metadatatest.AssertEqualMetric series of functions instead.
// 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.Telemetry, tts.id, protocol, acceptedLogRecords, droppedLogRecords)
}

// 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.Telemetry, tts.id, protocol, acceptedMetricPoints, droppedMetricPoints)
}

// TelemetrySettings returns the TestTelemetry's TelemetrySettings
Expand Down
74 changes: 28 additions & 46 deletions component/componenttest/otelchecker.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,68 +4,66 @@
package componenttest // import "go.opentelemetry.io/collector/component/componenttest"

import (
"context"
"fmt"

"go.opentelemetry.io/otel/attribute"
sdkmetric "go.opentelemetry.io/otel/sdk/metric"
"go.opentelemetry.io/otel/sdk/metric/metricdata"
"go.uber.org/multierr"

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

func checkReceiverTraces(reader *sdkmetric.ManualReader, receiver component.ID, protocol string, accepted, dropped int64) error {
return checkReceiver(reader, receiver, "spans", protocol, accepted, dropped)
func checkReceiverTraces(tel *Telemetry, receiver component.ID, protocol string, accepted, dropped int64) error {
return checkReceiver(tel, receiver, "spans", protocol, accepted, dropped)
}

func checkReceiverLogs(reader *sdkmetric.ManualReader, receiver component.ID, protocol string, accepted, dropped int64) error {
return checkReceiver(reader, receiver, "log_records", protocol, accepted, dropped)
func checkReceiverLogs(tel *Telemetry, receiver component.ID, protocol string, accepted, dropped int64) error {
return checkReceiver(tel, receiver, "log_records", protocol, accepted, dropped)
}

func checkReceiverMetrics(reader *sdkmetric.ManualReader, receiver component.ID, protocol string, accepted, dropped int64) error {
return checkReceiver(reader, receiver, "metric_points", protocol, accepted, dropped)
func checkReceiverMetrics(tel *Telemetry, receiver component.ID, protocol string, accepted, dropped int64) error {
return checkReceiver(tel, receiver, "metric_points", protocol, accepted, dropped)
}

func checkReceiver(reader *sdkmetric.ManualReader, receiver component.ID, datatype, protocol string, acceptedMetricPoints, droppedMetricPoints int64) error {
func checkReceiver(tel *Telemetry, receiver component.ID, datatype, protocol string, acceptedMetricPoints, droppedMetricPoints int64) error {
receiverAttrs := attributesForReceiverMetrics(receiver, protocol)
return multierr.Combine(
checkIntSum(reader, "otelcol_receiver_accepted_"+datatype, acceptedMetricPoints, receiverAttrs),
checkIntSum(reader, "otelcol_receiver_refused_"+datatype, droppedMetricPoints, receiverAttrs))
checkIntSum(tel, "otelcol_receiver_accepted_"+datatype, acceptedMetricPoints, receiverAttrs),
checkIntSum(tel, "otelcol_receiver_refused_"+datatype, droppedMetricPoints, receiverAttrs))
}

func checkExporterTraces(reader *sdkmetric.ManualReader, exporter component.ID, sent, sendFailed int64) error {
return checkExporter(reader, exporter, "spans", sent, sendFailed)
func checkExporterTraces(tel *Telemetry, exporter component.ID, sent, sendFailed int64) error {
return checkExporter(tel, exporter, "spans", sent, sendFailed)
}

func checkExporterLogs(reader *sdkmetric.ManualReader, exporter component.ID, sent, sendFailed int64) error {
return checkExporter(reader, exporter, "log_records", sent, sendFailed)
func checkExporterLogs(tel *Telemetry, exporter component.ID, sent, sendFailed int64) error {
return checkExporter(tel, exporter, "log_records", sent, sendFailed)
}

func checkExporterMetrics(reader *sdkmetric.ManualReader, exporter component.ID, sent, sendFailed int64) error {
return checkExporter(reader, exporter, "metric_points", sent, sendFailed)
func checkExporterMetrics(tel *Telemetry, exporter component.ID, sent, sendFailed int64) error {
return checkExporter(tel, exporter, "metric_points", sent, sendFailed)
}

func checkExporter(reader *sdkmetric.ManualReader, exporter component.ID, datatype string, sent, sendFailed int64) error {
func checkExporter(tel *Telemetry, exporter component.ID, datatype string, sent, sendFailed int64) error {
exporterAttrs := attributesForExporterMetrics(exporter)
errs := checkIntSum(reader, "otelcol_exporter_sent_"+datatype, sent, exporterAttrs)
errs := checkIntSum(tel, "otelcol_exporter_sent_"+datatype, sent, exporterAttrs)
if sendFailed > 0 {
errs = multierr.Append(errs,
checkIntSum(reader, "otelcol_exporter_send_failed_"+datatype, sendFailed, exporterAttrs))
checkIntSum(tel, "otelcol_exporter_send_failed_"+datatype, sendFailed, exporterAttrs))
}
return errs
}

func checkExporterEnqueueFailed(reader *sdkmetric.ManualReader, exporter component.ID, datatype string, enqueueFailed int64) error {
func checkExporterEnqueueFailed(tel *Telemetry, exporter component.ID, datatype string, enqueueFailed int64) error {
if enqueueFailed == 0 {
return nil
}
exporterAttrs := attributesForExporterMetrics(exporter)
return checkIntSum(reader, "otelcol_exporter_enqueue_failed_"+datatype, enqueueFailed, exporterAttrs)
return checkIntSum(tel, "otelcol_exporter_enqueue_failed_"+datatype, enqueueFailed, exporterAttrs)
}

func checkIntGauge(reader *sdkmetric.ManualReader, metric string, expected int64, expectedAttrs attribute.Set) error {
dp, err := getGaugeDataPoint[int64](reader, metric, expectedAttrs)
func checkIntGauge(tel *Telemetry, metric string, expected int64, expectedAttrs attribute.Set) error {
dp, err := getGaugeDataPoint[int64](tel, metric, expectedAttrs)
if err != nil {
return err
}
Expand All @@ -77,8 +75,8 @@ func checkIntGauge(reader *sdkmetric.ManualReader, metric string, expected int64
return nil
}

func checkIntSum(reader *sdkmetric.ManualReader, expectedMetric string, expected int64, expectedAttrs attribute.Set) error {
dp, err := getSumDataPoint[int64](reader, expectedMetric, expectedAttrs)
func checkIntSum(tel *Telemetry, expectedMetric string, expected int64, expectedAttrs attribute.Set) error {
dp, err := getSumDataPoint[int64](tel, expectedMetric, expectedAttrs)
if err != nil {
return err
}
Expand All @@ -90,8 +88,8 @@ func checkIntSum(reader *sdkmetric.ManualReader, expectedMetric string, expected
return nil
}

func getSumDataPoint[N int64 | float64](reader *sdkmetric.ManualReader, expectedName string, expectedAttrs attribute.Set) (metricdata.DataPoint[N], error) {
m, err := getMetric(reader, expectedName)
func getSumDataPoint[N int64 | float64](tel *Telemetry, expectedName string, expectedAttrs attribute.Set) (metricdata.DataPoint[N], error) {
m, err := tel.GetMetric(expectedName)
if err != nil {
return metricdata.DataPoint[N]{}, err
}
Expand All @@ -104,8 +102,8 @@ func getSumDataPoint[N int64 | float64](reader *sdkmetric.ManualReader, expected
}
}

func getGaugeDataPoint[N int64 | float64](reader *sdkmetric.ManualReader, expectedName string, expectedAttrs attribute.Set) (metricdata.DataPoint[N], error) {
m, err := getMetric(reader, expectedName)
func getGaugeDataPoint[N int64 | float64](tel *Telemetry, expectedName string, expectedAttrs attribute.Set) (metricdata.DataPoint[N], error) {
m, err := tel.GetMetric(expectedName)
if err != nil {
return metricdata.DataPoint[N]{}, err
}
Expand All @@ -127,22 +125,6 @@ func getDataPoint[N int64 | float64](dps []metricdata.DataPoint[N], expectedName
return metricdata.DataPoint[N]{}, fmt.Errorf("metric '%s' doesn't have a data point with the given attributes: %s", expectedName, expectedAttrs.Encoded(attribute.DefaultEncoder()))
}

func getMetric(reader *sdkmetric.ManualReader, expectedName string) (metricdata.Metrics, error) {
var rm metricdata.ResourceMetrics
if err := reader.Collect(context.Background(), &rm); err != nil {
return metricdata.Metrics{}, err
}

for _, sm := range rm.ScopeMetrics {
for _, m := range sm.Metrics {
if m.Name == expectedName {
return m, nil
}
}
}
return metricdata.Metrics{}, fmt.Errorf("metric '%s' not found", expectedName)
}

// attributesForReceiverMetrics returns the attributes that are needed for the receiver metrics.
func attributesForReceiverMetrics(receiver component.ID, transport string) attribute.Set {
return attribute.NewSet(
Expand Down
18 changes: 18 additions & 0 deletions component/componenttest/telemetry.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ package componenttest // import "go.opentelemetry.io/collector/component/compone
import (
"context"
"errors"
"fmt"

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

Expand Down Expand Up @@ -73,6 +75,22 @@ func (tt *Telemetry) NewTelemetrySettings() component.TelemetrySettings {
return set
}

func (tt *Telemetry) GetMetric(name string) (metricdata.Metrics, error) {
var rm metricdata.ResourceMetrics
if err := tt.Reader.Collect(context.Background(), &rm); err != nil {
return metricdata.Metrics{}, err
}

for _, sm := range rm.ScopeMetrics {
for _, m := range sm.Metrics {
if m.Name == name {
return m, nil
}
}
}
return metricdata.Metrics{}, fmt.Errorf("metric '%s' not found", name)
}

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

0 comments on commit fb8bb93

Please sign in to comment.