Polish "Add support for OTel-specific environment variables"

See gh-44394
This commit is contained in:
Moritz Halbritter 2025-02-28 14:35:12 +01:00
parent bf015bfeb9
commit 8f4e0518f3
5 changed files with 34 additions and 23 deletions

View File

@ -16,6 +16,7 @@
package org.springframework.boot.actuate.autoconfigure.metrics.export.otlp;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@ -27,6 +28,7 @@ import org.springframework.boot.actuate.autoconfigure.metrics.export.properties.
import org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryProperties;
import org.springframework.boot.actuate.autoconfigure.opentelemetry.OpenTelemetryResourceAttributes;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
/**
* Adapter to convert {@link OtlpMetricsProperties} to an {@link OtlpConfig}.
@ -80,7 +82,7 @@ class OtlpMetricsPropertiesConfigAdapter extends StepRegistryPropertiesConfigAda
.asMap();
attributes.computeIfAbsent("service.name", (key) -> getApplicationName());
attributes.computeIfAbsent("service.group", (key) -> getApplicationGroup());
return attributes;
return Collections.unmodifiableMap(attributes);
}
private String getApplicationName() {
@ -88,7 +90,8 @@ class OtlpMetricsPropertiesConfigAdapter extends StepRegistryPropertiesConfigAda
}
private String getApplicationGroup() {
return this.environment.getProperty("spring.application.group");
String applicationGroup = this.environment.getProperty("spring.application.group");
return (StringUtils.hasLength(applicationGroup)) ? applicationGroup : null;
}
@Override

View File

@ -36,6 +36,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
/**
* {@link EnableAutoConfiguration Auto-configuration} for OpenTelemetry.
@ -88,7 +89,8 @@ public class OpenTelemetryAutoConfiguration {
}
private String getApplicationGroup(Environment environment) {
return environment.getProperty("spring.application.group");
String applicationGroup = environment.getProperty("spring.application.group");
return (StringUtils.hasLength(applicationGroup)) ? applicationGroup : null;
}
}

View File

@ -130,21 +130,21 @@ public final class OpenTelemetryResourceAttributes {
ByteArrayOutputStream bos = new ByteArrayOutputStream(bytes.length);
for (int i = 0; i < bytes.length; i++) {
byte b = bytes[i];
if (b == '%') {
int u = decodeHex(bytes, ++i);
int l = decodeHex(bytes, ++i);
if (u >= 0 && l >= 0) {
bos.write((u << 4) + l);
}
else {
throw new IllegalArgumentException(
"Failed to decode percent-encoded characters at index %d in the value: '%s'"
.formatted(i - 2, value));
}
if (b != '%') {
bos.write(b);
continue;
}
int u = decodeHex(bytes, i + 1);
int l = decodeHex(bytes, i + 2);
if (u >= 0 && l >= 0) {
bos.write((u << 4) + l);
}
else {
bos.write(b);
throw new IllegalArgumentException(
"Failed to decode percent-encoded characters at index %d in the value: '%s'".formatted(i,
value));
}
i += 2;
}
return bos.toString(StandardCharsets.UTF_8);
}

View File

@ -24,6 +24,7 @@ import java.util.stream.Stream;
import io.opentelemetry.api.internal.PercentEscaper;
import org.assertj.core.api.InstanceOfAssertFactories;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
@ -36,7 +37,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
*/
class OpenTelemetryResourceAttributesTests {
private static final Random random = new Random();
private static Random random;
private static final PercentEscaper escaper = PercentEscaper.create();
@ -44,11 +45,17 @@ class OpenTelemetryResourceAttributesTests {
private final Map<String, String> resourceAttributes = new LinkedHashMap<>();
@BeforeAll
static void beforeAll() {
long seed = new Random().nextLong();
System.out.println("Seed: " + seed);
random = new Random(seed);
}
@Test
void otelServiceNameShouldTakePrecedenceOverOtelResourceAttributes() {
this.environmentVariables.put("OTEL_RESOURCE_ATTRIBUTES", "service.name=ignored");
this.environmentVariables.put("OTEL_SERVICE_NAME", "otel-service");
OpenTelemetryResourceAttributes attributes = getAttributes();
assertThat(attributes.asMap()).hasSize(1).containsEntry("service.name", "otel-service");
}
@ -57,7 +64,6 @@ class OpenTelemetryResourceAttributesTests {
void otelServiceNameWhenEmptyShouldTakePrecedenceOverOtelResourceAttributes() {
this.environmentVariables.put("OTEL_RESOURCE_ATTRIBUTES", "service.name=ignored");
this.environmentVariables.put("OTEL_SERVICE_NAME", "");
OpenTelemetryResourceAttributes attributes = getAttributes();
assertThat(attributes.asMap()).hasSize(1).containsEntry("service.name", "");
}
@ -66,7 +72,6 @@ class OpenTelemetryResourceAttributesTests {
void otelResourceAttributesShouldBeUsed() {
this.environmentVariables.put("OTEL_RESOURCE_ATTRIBUTES",
", ,,key1=value1,key2= value2, key3=value3,key4=,=value5,key6,=,key7=spring+boot,key8=ś");
OpenTelemetryResourceAttributes attributes = getAttributes();
assertThat(attributes.asMap()).hasSize(6)
.containsEntry("key1", "value1")
@ -83,7 +88,6 @@ class OpenTelemetryResourceAttributesTests {
this.resourceAttributes.put("key2", "");
this.environmentVariables.put("OTEL_SERVICE_NAME", "custom-service");
this.environmentVariables.put("OTEL_RESOURCE_ATTRIBUTES", "key1=value1,key2=value2");
OpenTelemetryResourceAttributes attributes = getAttributes();
assertThat(attributes.asMap()).hasSize(4)
.containsEntry("service.name", "custom-service")
@ -99,7 +103,6 @@ class OpenTelemetryResourceAttributesTests {
this.resourceAttributes.put(null, "value");
this.environmentVariables.put("OTEL_SERVICE_NAME", "custom-service");
this.environmentVariables.put("OTEL_RESOURCE_ATTRIBUTES", "key1=value1,key2=value2");
OpenTelemetryResourceAttributes attributes = getAttributes();
assertThat(attributes.asMap()).hasSize(3)
.containsEntry("service.name", "custom-service")

View File

@ -95,7 +95,8 @@ Spring Boot's actuator module includes basic support for OpenTelemetry.
It provides a bean of type javadoc:io.opentelemetry.api.OpenTelemetry[], and if there are beans of type javadoc:io.opentelemetry.sdk.trace.SdkTracerProvider[], javadoc:io.opentelemetry.context.propagation.ContextPropagators[], javadoc:io.opentelemetry.sdk.logs.SdkLoggerProvider[] or javadoc:io.opentelemetry.sdk.metrics.SdkMeterProvider[] in the application context, they automatically get registered.
Additionally, it provides a javadoc:io.opentelemetry.sdk.resources.Resource[] bean.
The attributes of the auto-configured javadoc:io.opentelemetry.sdk.resources.Resource[] can be configured via the configprop:management.opentelemetry.resource-attributes[] configuration property. Auto-configured attributes will be merged with attributes from the `OTEL_RESOURCE_ATTRIBUTES` and `OTEL_SERVICE_NAME` environment variables, with attributes configured through the configuration property taking precedence over those from the environment variables.
The attributes of the auto-configured javadoc:io.opentelemetry.sdk.resources.Resource[] can be configured via the configprop:management.opentelemetry.resource-attributes[] configuration property.
Auto-configured attributes will be merged with attributes from the `OTEL_RESOURCE_ATTRIBUTES` and `OTEL_SERVICE_NAME` environment variables, with attributes configured through the configuration property taking precedence over those from the environment variables.
If you have defined your own javadoc:io.opentelemetry.sdk.resources.Resource[] bean, this will no longer be the case.
@ -103,7 +104,9 @@ If you have defined your own javadoc:io.opentelemetry.sdk.resources.Resource[] b
NOTE: Spring Boot does not provide auto-configuration for OpenTelemetry metrics or logging.
OpenTelemetry tracing is only auto-configured when used together with xref:actuator/tracing.adoc[Micrometer Tracing].
NOTE: The `OTEL_RESOURCE_ATTRIBUTES` environment variable consist of a list of key-value pairs. For example: `key1=value1,key2=value2,key3=spring%20boot`. All attribute values are treated as strings, and any characters outside the baggage-octet range must be **percent-encoded**.
NOTE: The `OTEL_RESOURCE_ATTRIBUTES` environment variable consist of a list of key-value pairs.
For example: `key1=value1,key2=value2,key3=spring%20boot`.
All attribute values are treated as strings, and any characters outside the baggage-octet range must be **percent-encoded**.
The next sections will provide more details about logging, metrics and traces.