1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2026-06-19 21:45:50 +02:00

fix counting of spans/logs in self-observability metrics in otlp trace and log exporters (#8254)

Fixes https://github.com/open-telemetry/opentelemetry-go/issues/8253
This commit is contained in:
David Ashpole
2026-04-24 03:20:58 -04:00
committed by GitHub
parent b4e15cd1c7
commit 4dd8821c94
9 changed files with 81 additions and 30 deletions
+1
View File
@@ -75,6 +75,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Fix stale status code reporting on self-observability metrics in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp` and `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#8226) - Fix stale status code reporting on self-observability metrics in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp` and `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#8226)
- Fix a concurrent `Collect` data race and potential panic in `go.opentelemetry.io/otel/exporters/prometheus` when `WithResourceAsConstantLabels` option is used. (#8227) - Fix a concurrent `Collect` data race and potential panic in `go.opentelemetry.io/otel/exporters/prometheus` when `WithResourceAsConstantLabels` option is used. (#8227)
- Fix race condition in `FixedSizeReservoir` in `go.opentelemetry.io/otel/sdk/metric/exemplar` by reverting #7447. (#8249) - Fix race condition in `FixedSizeReservoir` in `go.opentelemetry.io/otel/sdk/metric/exemplar` by reverting #7447. (#8249)
- Fix counting of spans and logs in self-observability metrics in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc`, `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`, `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc`, and `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#8254)
<!-- Released section --> <!-- Released section -->
<!-- Don't change this section unless doing release --> <!-- Don't change this section unless doing release -->
+6 -1
View File
@@ -151,8 +151,13 @@ func (c *client) UploadLogs(ctx context.Context, rl []*logpb.ResourceLogs) (uplo
defer cancel() defer cancel()
pbRequest := &collogpb.ExportLogsServiceRequest{ResourceLogs: rl} pbRequest := &collogpb.ExportLogsServiceRequest{ResourceLogs: rl}
count := int64(len(rl))
if c.instrumentation != nil { if c.instrumentation != nil {
var count int64
for _, resLogs := range rl {
for _, scopeLogs := range resLogs.ScopeLogs {
count += int64(len(scopeLogs.LogRecords))
}
}
eo := c.instrumentation.ExportLogs(ctx, count) eo := c.instrumentation.ExportLogs(ctx, count)
defer func() { defer func() {
eo.End(uploadErr) eo.End(uploadErr)
@@ -705,7 +705,7 @@ func TestClientObservability(t *testing.T) {
serverAddrAttrs[0], serverAddrAttrs[0],
serverAddrAttrs[1], serverAddrAttrs[1],
), ),
Value: int64(len(resourceLogs)), Value: int64(len(logRecords)),
}, },
}, },
}, },
@@ -831,7 +831,7 @@ func TestClientObservability(t *testing.T) {
serverAddrAttrs[0], serverAddrAttrs[0],
serverAddrAttrs[1], serverAddrAttrs[1],
), ),
Value: 0, Value: 2,
}, },
{ {
Attributes: attribute.NewSet( Attributes: attribute.NewSet(
@@ -843,7 +843,7 @@ func TestClientObservability(t *testing.T) {
serverAddrAttrs[1], serverAddrAttrs[1],
semconv.ErrorType(wantErr), semconv.ErrorType(wantErr),
), ),
Value: 1, Value: 2,
}, },
}, },
}, },
@@ -976,7 +976,7 @@ func TestClientObservability(t *testing.T) {
serverAddrAttrs[1], serverAddrAttrs[1],
wantErrTypeAttr, wantErrTypeAttr,
), ),
Value: 1, Value: int64(len(logRecords)),
}, },
}, },
}, },
@@ -1144,7 +1144,7 @@ func TestClientObservabilityWithRetry(t *testing.T) {
serverAddrAttrs[0], serverAddrAttrs[0],
serverAddrAttrs[1], serverAddrAttrs[1],
), ),
Value: int64(len(resourceLogs)) - n, Value: int64(len(logRecords)) - n,
}, },
{ {
Attributes: attribute.NewSet( Attributes: attribute.NewSet(
+7 -1
View File
@@ -164,7 +164,13 @@ func (c *httpClient) uploadLogs(ctx context.Context, data []*logpb.ResourceLogs)
var statusCode int var statusCode int
if c.inst != nil { if c.inst != nil {
op := c.inst.ExportLogs(ctx, int64(len(data))) var count int64
for _, resLogs := range data {
for _, scopeLogs := range resLogs.ScopeLogs {
count += int64(len(scopeLogs.LogRecords))
}
}
op := c.inst.ExportLogs(ctx, count)
defer func() { op.End(uploadErr, statusCode) }() defer func() { op.End(uploadErr, statusCode) }()
} }
@@ -1038,7 +1038,7 @@ func TestClientInstrumentation(t *testing.T) {
Unit: otelconv.SDKExporterLogInflight{}.Unit(), Unit: otelconv.SDKExporterLogInflight{}.Unit(),
Data: metricdata.Sum[int64]{ Data: metricdata.Sum[int64]{
DataPoints: []metricdata.DataPoint[int64]{ DataPoints: []metricdata.DataPoint[int64]{
{Attributes: attribute.NewSet(baseAttrs...)}, {Attributes: attribute.NewSet(baseAttrs...), Value: 0},
}, },
Temporality: metricdata.CumulativeTemporality, Temporality: metricdata.CumulativeTemporality,
}, },
@@ -1049,11 +1049,11 @@ func TestClientInstrumentation(t *testing.T) {
Unit: otelconv.SDKExporterLogExported{}.Unit(), Unit: otelconv.SDKExporterLogExported{}.Unit(),
Data: metricdata.Sum[int64]{ Data: metricdata.Sum[int64]{
DataPoints: []metricdata.DataPoint[int64]{ DataPoints: []metricdata.DataPoint[int64]{
{Attributes: attribute.NewSet(baseAttrs...)}, {Attributes: attribute.NewSet(baseAttrs...), Value: 2},
{Attributes: attribute.NewSet(append( {Attributes: attribute.NewSet(append(
baseAttrs, baseAttrs,
otelconv.SDKExporterLogExported{}.AttrErrorType("*errors.joinError"), otelconv.SDKExporterLogExported{}.AttrErrorType("*errors.joinError"),
)...)}, )...), Value: 2},
}, },
Temporality: 0x1, Temporality: 0x1,
IsMonotonic: true, IsMonotonic: true,
@@ -1078,12 +1078,25 @@ func TestClientInstrumentation(t *testing.T) {
} }
require.Len(t, got.ScopeMetrics, 1) require.Len(t, got.ScopeMetrics, 1)
opt := []metricdatatest.Option{
gotMetrics := got.ScopeMetrics[0].Metrics
require.Len(t, gotMetrics, 3, "expected 3 metrics")
// Assert counters without IgnoreValue
optCounters := []metricdatatest.Option{
metricdatatest.IgnoreTimestamp(),
metricdatatest.IgnoreExemplars(),
}
metricdatatest.AssertEqual(t, want.Metrics[0], gotMetrics[0], optCounters...)
metricdatatest.AssertEqual(t, want.Metrics[1], gotMetrics[1], optCounters...)
// Assert duration with IgnoreValue
optDuration := []metricdatatest.Option{
metricdatatest.IgnoreTimestamp(), metricdatatest.IgnoreTimestamp(),
metricdatatest.IgnoreExemplars(), metricdatatest.IgnoreExemplars(),
metricdatatest.IgnoreValue(), metricdatatest.IgnoreValue(),
} }
metricdatatest.AssertEqual(t, want, got.ScopeMetrics[0], opt...) metricdatatest.AssertEqual(t, want.Metrics[2], gotMetrics[2], optDuration...)
} }
func TestResponseBodySizeLimit(t *testing.T) { func TestResponseBodySizeLimit(t *testing.T) {
@@ -215,7 +215,13 @@ func (c *client) UploadTraces(ctx context.Context, protoSpans []*tracepb.Resourc
code := codes.Unknown code := codes.Unknown
if c.inst != nil { if c.inst != nil {
op := c.inst.ExportSpans(ctx, len(protoSpans)) var spanCount int
for _, rs := range protoSpans {
for _, ss := range rs.ScopeSpans {
spanCount += len(ss.Spans)
}
}
op := c.inst.ExportSpans(ctx, spanCount)
defer func() { op.End(uploadErr, code) }() defer func() { op.End(uploadErr, code) }()
} }
@@ -491,7 +491,8 @@ func TestClientInstrumentation(t *testing.T) {
t.Cleanup(func() { require.NoError(t, mc.stop()) }) t.Cleanup(func() { require.NoError(t, mc.stop()) })
exp := newGRPCExporter(t, t.Context(), mc.endpoint) exp := newGRPCExporter(t, t.Context(), mc.endpoint)
err := exp.ExportSpans(t.Context(), roSpans) localSpans := tracetest.SpanStubs{{Name: "Span 0"}, {Name: "Span 1"}}.Snapshots()
err := exp.ExportSpans(t.Context(), localSpans)
assert.ErrorIs(t, err, internal.TracePartialSuccessError(n, msg)) assert.ErrorIs(t, err, internal.TracePartialSuccessError(n, msg))
require.NoError(t, exp.Shutdown(t.Context())) require.NoError(t, exp.Shutdown(t.Context()))
@@ -512,7 +513,7 @@ func TestClientInstrumentation(t *testing.T) {
Unit: otelconv.SDKExporterSpanInflight{}.Unit(), Unit: otelconv.SDKExporterSpanInflight{}.Unit(),
Data: metricdata.Sum[int64]{ Data: metricdata.Sum[int64]{
DataPoints: []metricdata.DataPoint[int64]{ DataPoints: []metricdata.DataPoint[int64]{
{Attributes: attribute.NewSet(attrs...)}, {Attributes: attribute.NewSet(attrs...), Value: 0},
}, },
Temporality: metricdata.CumulativeTemporality, Temporality: metricdata.CumulativeTemporality,
}, },
@@ -523,11 +524,11 @@ func TestClientInstrumentation(t *testing.T) {
Unit: otelconv.SDKExporterSpanExported{}.Unit(), Unit: otelconv.SDKExporterSpanExported{}.Unit(),
Data: metricdata.Sum[int64]{ Data: metricdata.Sum[int64]{
DataPoints: []metricdata.DataPoint[int64]{ DataPoints: []metricdata.DataPoint[int64]{
{Attributes: attribute.NewSet(attrs...)}, {Attributes: attribute.NewSet(attrs...), Value: 0},
{Attributes: attribute.NewSet(append( {Attributes: attribute.NewSet(append(
attrs, attrs,
otelconv.SDKExporterSpanExported{}.AttrErrorType("*errors.joinError"), otelconv.SDKExporterSpanExported{}.AttrErrorType("*errors.joinError"),
)...)}, )...), Value: 2},
}, },
Temporality: 0x1, Temporality: 0x1,
IsMonotonic: true, IsMonotonic: true,
@@ -553,12 +554,18 @@ func TestClientInstrumentation(t *testing.T) {
}, },
} }
require.Len(t, got.ScopeMetrics, 1) require.Len(t, got.ScopeMetrics, 1)
opt := []metricdatatest.Option{ gotMetrics := got.ScopeMetrics[0].Metrics
require.Len(t, gotMetrics, 3)
metricdatatest.AssertEqual(t, want.Metrics[0], gotMetrics[0], metricdatatest.IgnoreTimestamp())
metricdatatest.AssertEqual(t, want.Metrics[1], gotMetrics[1], metricdatatest.IgnoreTimestamp())
metricdatatest.AssertEqual(
t,
want.Metrics[2],
gotMetrics[2],
metricdatatest.IgnoreTimestamp(), metricdatatest.IgnoreTimestamp(),
metricdatatest.IgnoreExemplars(),
metricdatatest.IgnoreValue(), metricdatatest.IgnoreValue(),
} )
metricdatatest.AssertEqual(t, want, got.ScopeMetrics[0], opt...)
} }
func canonical(t *testing.T, endpoint string) string { func canonical(t *testing.T, endpoint string) string {
@@ -179,7 +179,13 @@ func (d *client) UploadTraces(ctx context.Context, protoSpans []*tracepb.Resourc
var statusCode int var statusCode int
if d.inst != nil { if d.inst != nil {
op := d.inst.ExportSpans(ctx, len(protoSpans)) var spanCount int
for _, rs := range protoSpans {
for _, ss := range rs.ScopeSpans {
spanCount += len(ss.Spans)
}
}
op := d.inst.ExportSpans(ctx, spanCount)
defer func() { op.End(uploadErr, statusCode) }() defer func() { op.End(uploadErr, statusCode) }()
} }
@@ -531,7 +531,8 @@ func TestClientInstrumentation(t *testing.T) {
exporter, err := otlptrace.New(t.Context(), driver) exporter, err := otlptrace.New(t.Context(), driver)
require.NoError(t, err) require.NoError(t, err)
err = exporter.ExportSpans(t.Context(), otlptracetest.SingleReadOnlySpan()) localSpans := tracetest.SpanStubs{{Name: "Span 0"}, {Name: "Span 1"}}.Snapshots()
err = exporter.ExportSpans(t.Context(), localSpans)
assert.Error(t, err) assert.Error(t, err)
require.NoError(t, exporter.Shutdown(t.Context())) require.NoError(t, exporter.Shutdown(t.Context()))
@@ -553,7 +554,7 @@ func TestClientInstrumentation(t *testing.T) {
Unit: otelconv.SDKExporterSpanInflight{}.Unit(), Unit: otelconv.SDKExporterSpanInflight{}.Unit(),
Data: metricdata.Sum[int64]{ Data: metricdata.Sum[int64]{
DataPoints: []metricdata.DataPoint[int64]{ DataPoints: []metricdata.DataPoint[int64]{
{Attributes: attribute.NewSet(attrs...)}, {Attributes: attribute.NewSet(attrs...), Value: 0},
}, },
Temporality: metricdata.CumulativeTemporality, Temporality: metricdata.CumulativeTemporality,
}, },
@@ -564,11 +565,11 @@ func TestClientInstrumentation(t *testing.T) {
Unit: otelconv.SDKExporterSpanExported{}.Unit(), Unit: otelconv.SDKExporterSpanExported{}.Unit(),
Data: metricdata.Sum[int64]{ Data: metricdata.Sum[int64]{
DataPoints: []metricdata.DataPoint[int64]{ DataPoints: []metricdata.DataPoint[int64]{
{Attributes: attribute.NewSet(attrs...)}, {Attributes: attribute.NewSet(attrs...), Value: 0},
{Attributes: attribute.NewSet(append( {Attributes: attribute.NewSet(append(
attrs, attrs,
otelconv.SDKExporterSpanExported{}.AttrErrorType("*errors.joinError"), otelconv.SDKExporterSpanExported{}.AttrErrorType("*errors.joinError"),
)...)}, )...), Value: 2},
}, },
Temporality: 0x1, Temporality: 0x1,
IsMonotonic: true, IsMonotonic: true,
@@ -592,12 +593,18 @@ func TestClientInstrumentation(t *testing.T) {
}, },
} }
require.Len(t, got.ScopeMetrics, 1) require.Len(t, got.ScopeMetrics, 1)
opt := []metricdatatest.Option{ gotMetrics := got.ScopeMetrics[0].Metrics
require.Len(t, gotMetrics, 3)
metricdatatest.AssertEqual(t, want.Metrics[0], gotMetrics[0], metricdatatest.IgnoreTimestamp())
metricdatatest.AssertEqual(t, want.Metrics[1], gotMetrics[1], metricdatatest.IgnoreTimestamp())
metricdatatest.AssertEqual(
t,
want.Metrics[2],
gotMetrics[2],
metricdatatest.IgnoreTimestamp(), metricdatatest.IgnoreTimestamp(),
metricdatatest.IgnoreExemplars(),
metricdatatest.IgnoreValue(), metricdatatest.IgnoreValue(),
} )
metricdatatest.AssertEqual(t, want, got.ScopeMetrics[0], opt...)
} }
func TestResponseBodySizeLimit(t *testing.T) { func TestResponseBodySizeLimit(t *testing.T) {