|
@SuppressWarnings({"rawtypes", "unchecked"}) |
|
private Labels convertAttributes( |
|
@Nullable Resource resource, |
|
@Nullable InstrumentationScopeInfo scope, |
|
Attributes attributes, |
|
String... additionalAttributes) { |
|
|
|
List<AttributeKey<?>> allowedAttributeKeys = |
|
allowedResourceAttributesFilter != null |
|
? filterAllowedResourceAttributeKeys(resource) |
|
: Collections.emptyList(); |
|
|
|
Map<String, String> labelNameToValue = new HashMap<>(); |
|
attributes.forEach( |
|
(key, value) -> |
|
labelNameToValue.put( |
|
convertLabelName(key.getKey()), toLabelValue(key.getType(), value))); |
|
|
|
for (int i = 0; i < additionalAttributes.length; i += 2) { |
|
labelNameToValue.putIfAbsent( |
|
requireNonNull(additionalAttributes[i]), additionalAttributes[i + 1]); |
|
} |
|
|
|
if (scope != null && otelScopeLabelsEnabled) { |
|
labelNameToValue.putIfAbsent(OTEL_SCOPE_NAME, scope.getName()); |
|
if (scope.getVersion() != null) { |
|
labelNameToValue.putIfAbsent(OTEL_SCOPE_VERSION, scope.getVersion()); |
|
} |
|
String schemaUrl = scope.getSchemaUrl(); |
|
if (schemaUrl != null) { |
|
labelNameToValue.putIfAbsent(OTEL_SCOPE_SCHEMA_URL, schemaUrl); |
|
} |
|
scope |
|
.getAttributes() |
|
.forEach( |
|
(key, value) -> |
|
labelNameToValue.putIfAbsent( |
|
OTEL_SCOPE_ATTRIBUTE_PREFIX + key.getKey(), value.toString())); |
|
} |
|
|
|
if (resource != null) { |
|
Attributes resourceAttributes = resource.getAttributes(); |
|
for (AttributeKey attributeKey : allowedAttributeKeys) { |
|
Object attributeValue = resourceAttributes.get(attributeKey); |
|
if (attributeValue != null) { |
|
labelNameToValue.putIfAbsent( |
|
convertLabelName(attributeKey.getKey()), attributeValue.toString()); |
|
} |
|
} |
|
} |
|
|
|
String[] names = new String[labelNameToValue.size()]; |
|
String[] values = new String[labelNameToValue.size()]; |
|
int[] pos = new int[] {0}; |
|
labelNameToValue.forEach( |
|
(name, value) -> { |
|
names[pos[0]] = name; |
|
values[pos[0]] = value; |
|
pos[0] += 1; |
|
}); |
|
|
|
return Labels.of(names, values); |
|
} |
According to the prometheus compatibility spec, when converting attributes, if normalizing creates label conflicts the values should be merged together with a
;delimeter and sorted lexigraphically according to original attribute key name.https://github.com/open-telemetry/opentelemetry-specification/blame/3a4cba26572558609bdcb51dfcbb2d8259085387/specification/compatibility/prometheus_and_openmetrics.md#L534-L537
We don't do this, instead using a naive "first wins" implementation:
opentelemetry-java/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/Otel2PrometheusConverter.java
Lines 462 to 524 in 9b602eb
Found while reviewing open-telemetry/opentelemetry-specification#4963
cc @zeitlinger, @ArthurSens, @dashpole