Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -681,9 +681,56 @@ private static Stream<Arguments> 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> 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,
Expand Down
Loading