Skip to content

Prometheus attribute conversion should merge values when collisions occur #8236

@jack-berg

Description

@jack-berg

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:

@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);
}

Found while reviewing open-telemetry/opentelemetry-specification#4963

cc @zeitlinger, @ArthurSens, @dashpole

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions