diff --git a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/Otel2PrometheusConverter.java b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/Otel2PrometheusConverter.java index d80abc69209..15d3ab1b2a3 100644 --- a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/Otel2PrometheusConverter.java +++ b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/Otel2PrometheusConverter.java @@ -540,9 +540,9 @@ private Labels convertAttributes( Map labelNameToValue = buildAttributeLabels(attributes); + // Additional attributes (exemplar trace_id and span_id) take precedence over attributes. for (int i = 0; i < additionalAttributes.length; i += 2) { - labelNameToValue.putIfAbsent( - requireNonNull(additionalAttributes[i]), additionalAttributes[i + 1]); + labelNameToValue.put(requireNonNull(additionalAttributes[i]), additionalAttributes[i + 1]); } if (scope != null && otelScopeLabelsEnabled) { diff --git a/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/Otel2PrometheusConverterTest.java b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/Otel2PrometheusConverterTest.java index 63971865ff4..023616f2e0a 100644 --- a/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/Otel2PrometheusConverterTest.java +++ b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/Otel2PrometheusConverterTest.java @@ -1408,6 +1408,48 @@ private static Stream exemplarLabelLimitArgs() { new String[] {})); } + @ParameterizedTest + @MethodSource("exemplarSpanContextLabelsTakePrecedenceArgs") + void exemplarSpanContextLabelsTakePrecedence(Attributes filteredAttributes) { + SpanContext spanContext = + SpanContext.create( + "00000000000000000000000000000001", + "0000000000000001", + TraceFlags.getSampled(), + TraceState.getDefault()); + ImmutableDoubleExemplarData exemplar = + (ImmutableDoubleExemplarData) + ImmutableDoubleExemplarData.create(filteredAttributes, 1000L, spanContext, 1.0); + + MetricData metricData = + ImmutableMetricData.createDoubleGauge( + Resource.getDefault(), + InstrumentationScopeInfo.create("test"), + "my.gauge", + "desc", + "unit", + ImmutableGaugeData.create( + Collections.singletonList( + ImmutableDoublePointData.create( + 0, 1000, Attributes.empty(), 1.0, Collections.singletonList(exemplar))))); + + MetricSnapshots snapshots = converter.convert(Collections.singletonList(metricData)); + GaugeDataPointSnapshot point = (GaugeDataPointSnapshot) snapshots.get(0).getDataPoints().get(0); + Labels exemplarLabels = point.getExemplar().getLabels(); + assertThat(exemplarLabels.get("trace_id")).isEqualTo("00000000000000000000000000000001"); + assertThat(exemplarLabels.get("span_id")).isEqualTo("0000000000000001"); + } + + private static Stream exemplarSpanContextLabelsTakePrecedenceArgs() { + return Stream.of( + Arguments.argumentSet( + "sameLabelName", + Attributes.of(stringKey("trace_id"), "bogus", stringKey("span_id"), "bogus")), + Arguments.argumentSet( + "sameLabelNameAfterEscaping", + Attributes.of(stringKey("trace.id"), "bogus", stringKey("span.id"), "bogus"))); + } + private static InfoSnapshot makeInfoSnapshot(String id) { return new InfoSnapshot( MetricMetadata.builder().name("target").build(),