diff --git a/CHANGELOG.md b/CHANGELOG.md index c399e74acfa..846880ec62f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ ## Unreleased +### SDK + +#### Exporters + +* Prometheus: Fix serialization of array-valued scope and resource attributes to JSON strings + ([#XXXX](https://github.com/open-telemetry/opentelemetry-java/pull/XXXX)) + ## Version 1.63.0 (2026-06-05) ### API 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 b7e1957a356..64544e3aa7b 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 @@ -561,7 +561,8 @@ private Labels convertAttributes( .forEach( (key, value) -> labelNameToValue.putIfAbsent( - OTEL_SCOPE_ATTRIBUTE_PREFIX + key.getKey(), value.toString())); + OTEL_SCOPE_ATTRIBUTE_PREFIX + key.getKey(), + toLabelValue(key.getType(), value))); } if (resource != null) { @@ -570,7 +571,8 @@ private Labels convertAttributes( Object attributeValue = resourceAttributes.get(attributeKey); if (attributeValue != null) { labelNameToValue.putIfAbsent( - convertLabelName(attributeKey.getKey()), attributeValue.toString()); + convertLabelName(attributeKey.getKey()), + toLabelValue(attributeKey.getType(), attributeValue)); } } } 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 891967111b9..41fbe299a50 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 @@ -681,9 +681,56 @@ private static Stream resourceAttributesAdditionArgs() { "my_metric_units", "cluster=\"mycluster\",otel_scope_foo=\"bar\",otel_scope_name=\"scope\",otel_scope_schema_url=\"schemaUrl\",otel_scope_version=\"version\"")); + // Array-valued resource attribute is serialized as a JSON string, matching the point attribute + // path + arguments.add( + Arguments.argumentSet( + "array-valued resource attribute serialized as json", + createSampleMetricData( + "my.metric", + "units", + MetricDataType.LONG_SUM, + Attributes.empty(), + Resource.create( + Attributes.of(stringArrayKey("clusters"), Arrays.asList("a", "b")))), + /* allowedResourceAttributesFilter= */ Predicates.startsWith("clu"), + "my_metric_units", + "clusters=\"[\\\"a\\\",\\\"b\\\"]\",otel_scope_foo=\"bar\",otel_scope_name=\"scope\",otel_scope_schema_url=\"schemaUrl\",otel_scope_version=\"version\"")); + return arguments.stream(); } + @Test + void arrayValuedScopeAttributeSerializedAsJson() { + // Array-valued scope attribute is serialized as a JSON string, matching the point attribute + // path + InstrumentationScopeInfo scope = + InstrumentationScopeInfo.builder("scope") + .setAttributes(Attributes.of(stringArrayKey("foo"), Arrays.asList("a", "b"))) + .build(); + MetricData metricData = + ImmutableMetricData.createLongSum( + Resource.getDefault(), + scope, + "sample", + "description", + "1", + ImmutableSumData.create( + /* isMonotonic= */ true, + AggregationTemporality.CUMULATIVE, + Collections.singletonList( + ImmutableLongPointData.create(0, 1, Attributes.empty(), 1L)))); + + MetricSnapshots snapshots = converter.convert(Collections.singletonList(metricData)); + + Optional metricSnapshot = + snapshots.stream().filter(snapshot -> snapshot instanceof CounterSnapshot).findFirst(); + assertThat(metricSnapshot).isPresent(); + + Labels labels = metricSnapshot.get().getDataPoints().get(0).getLabels(); + assertThat(labels.get("otel_scope_foo")).isEqualTo("[\"a\",\"b\"]"); + } + @Test void metricNameCollisionTest_Issue6277() { // NOTE: Metrics with the same resolved prometheus name should merge. However,