Skip to content

Commit bec5b30

Browse files
committed
Decode plus sign in resource attributes
1 parent 6893380 commit bec5b30

2 files changed

Lines changed: 59 additions & 14 deletions

File tree

sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/ResourceConfiguration.java

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@
1717
import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties;
1818
import io.opentelemetry.sdk.resources.Resource;
1919
import io.opentelemetry.sdk.resources.ResourceBuilder;
20-
import java.io.UnsupportedEncodingException;
21-
import java.net.URLDecoder;
2220
import java.nio.charset.StandardCharsets;
2321
import java.util.Collections;
2422
import java.util.HashSet;
@@ -67,18 +65,14 @@ public static Resource createEnvironmentResource() {
6765
*/
6866
public static Resource createEnvironmentResource(ConfigProperties config) {
6967
AttributesBuilder resourceAttributes = Attributes.builder();
70-
try {
71-
for (Map.Entry<String, String> entry : config.getMap(ATTRIBUTE_PROPERTY).entrySet()) {
72-
resourceAttributes.put(
73-
entry.getKey(),
74-
// Attributes specified via otel.resource.attributes follow the W3C Baggage spec and
75-
// characters outside the baggage-octet range are percent encoded
76-
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/sdk.md#specifying-resource-information-via-an-environment-variable
77-
URLDecoder.decode(entry.getValue(), StandardCharsets.UTF_8.displayName()));
78-
}
79-
} catch (UnsupportedEncodingException e) {
80-
// Should not happen since always using standard charset
81-
throw new ConfigurationException("Unable to decode resource attributes.", e);
68+
for (Map.Entry<String, String> entry : config.getMap(ATTRIBUTE_PROPERTY).entrySet()) {
69+
resourceAttributes.put(
70+
entry.getKey(),
71+
// Attributes specified via otel.resource.attributes follow the W3C Baggage spec and
72+
// characters outside the baggage-octet range are percent encoded
73+
// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/sdk.md#specifying-resource-information-via-an-environment-variable
74+
75+
decodeResourceAttributes(entry.getValue()));
8276
}
8377
String serviceName = config.getString(SERVICE_NAME_PROPERTY);
8478
if (serviceName != null) {
@@ -132,5 +126,35 @@ static Resource filterAttributes(Resource resource, ConfigProperties configPrope
132126
return builder.build();
133127
}
134128

129+
private static String decodeResourceAttributes(String value) {
130+
try {
131+
if (value.indexOf('%') < 0) {
132+
return value;
133+
}
134+
135+
int n = value.length();
136+
byte[] bytes = new byte[n];
137+
int pos = 0;
138+
139+
for (int i = 0; i < n; i++) {
140+
char c = value.charAt(i);
141+
if (c == '%' && i + 2 < n) {
142+
int d1 = Character.digit(value.charAt(i + 1), 16);
143+
int d2 = Character.digit(value.charAt(i + 2), 16);
144+
if (d1 != -1 && d2 != -1) {
145+
bytes[pos++] = (byte) ((d1 << 4) + d2);
146+
i += 2;
147+
continue;
148+
}
149+
}
150+
// Keep '+' as '+' and any other non-encoded chars
151+
bytes[pos++] = (byte) c;
152+
}
153+
return new String(bytes, 0, pos, StandardCharsets.UTF_8);
154+
} catch (RuntimeException e) {
155+
throw new ConfigurationException("Failed to decode resource attributes: " + value, e);
156+
}
157+
}
158+
135159
private ResourceConfiguration() {}
136160
}

sdk-extensions/autoconfigure/src/test/java/io/opentelemetry/sdk/autoconfigure/ResourceConfigurationTest.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,27 @@ void customConfigResourceWithDisabledKeys() {
5050
.build());
5151
}
5252

53+
@Test
54+
void decodePlusSignInCustomConfigResource() {
55+
Map<String, String> props = new HashMap<>();
56+
props.put("otel.service.name", "my-app");
57+
props.put(
58+
"otel.resource.attributes", "food=cheese+cake,drink=juice,animal= ,color=,shape=square");
59+
60+
assertThat(
61+
ResourceConfiguration.configureResource(
62+
DefaultConfigProperties.create(props, componentLoader),
63+
SpiHelper.create(ResourceConfigurationTest.class.getClassLoader()),
64+
(r, c) -> r))
65+
.isEqualTo(
66+
Resource.getDefault().toBuilder()
67+
.put(stringKey("service.name"), "my-app")
68+
.put("food", "cheese+cake")
69+
.put("drink", "juice")
70+
.put("shape", "square")
71+
.build());
72+
}
73+
5374
@Test
5475
void createEnvironmentResource_Empty() {
5576
Attributes attributes = ResourceConfiguration.createEnvironmentResource().getAttributes();

0 commit comments

Comments
 (0)