Skip to content

Commit 43cad9b

Browse files
committed
fix: Align environment carriers with spec requirements
1 parent 6eed41c commit 43cad9b

File tree

4 files changed

+84
-28
lines changed

4 files changed

+84
-28
lines changed

api/incubator/src/main/java/io/opentelemetry/api/incubator/propagation/EnvironmentGetter.java

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,32 @@
1010
import java.util.Locale;
1111
import java.util.Map;
1212
import javax.annotation.Nullable;
13+
import javax.annotation.concurrent.Immutable;
1314

1415
/**
1516
* A {@link TextMapGetter} that extracts context from a map carrier, intended for use with
1617
* environment variables.
1718
*
1819
* <p>Standard environment variable names are uppercase (e.g., {@code TRACEPARENT}, {@code
19-
* TRACESTATE}, {@code BAGGAGE}). This getter translates keys to uppercase before looking them up in
20-
* the carrier.
20+
* TRACESTATE}, {@code BAGGAGE}). This getter translates keys to uppercase and replaces characters
21+
* not allowed in environment variables (e.g., {@code .} and {@code -}) with underscores before
22+
* looking them up in the carrier.
23+
24+
* @see <a href=
25+
* "https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/context/env-carriers.md#format-restrictions">Environment
26+
* Variable Format Restrictions</a>
2127
*/
22-
public enum EnvironmentGetter implements TextMapGetter<Map<String, String>> {
23-
INSTANCE;
28+
@Immutable
29+
public final class EnvironmentGetter implements TextMapGetter<Map<String, String>> {
30+
31+
private static final EnvironmentGetter INSTANCE = new EnvironmentGetter();
32+
33+
private EnvironmentGetter() {}
34+
35+
/** Returns the singleton instance of {@link EnvironmentGetter}. */
36+
public static EnvironmentGetter getInstance() {
37+
return INSTANCE;
38+
}
2439

2540
@Override
2641
public Iterable<String> keys(Map<String, String> carrier) {
@@ -36,8 +51,11 @@ public String get(@Nullable Map<String, String> carrier, String key) {
3651
if (carrier == null || key == null) {
3752
return null;
3853
}
39-
// Spec recommends using uppercase for environment variable names.
40-
return carrier.get(key.toUpperCase(Locale.ROOT));
54+
// Spec recommends using uppercase and underscores for environment variable
55+
// names for maximum
56+
// cross-platform compatibility.
57+
String sanitizedKey = key.replace('.', '_').replace('-', '_').toUpperCase(Locale.ROOT);
58+
return carrier.get(sanitizedKey);
4159
}
4260

4361
@Override

api/incubator/src/main/java/io/opentelemetry/api/incubator/propagation/EnvironmentSetter.java

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,43 @@
99
import java.util.Locale;
1010
import java.util.Map;
1111
import javax.annotation.Nullable;
12+
import javax.annotation.concurrent.Immutable;
1213

1314
/**
1415
* A {@link TextMapSetter} that injects context into a map carrier, intended for use with
1516
* environment variables.
1617
*
1718
* <p>Standard environment variable names are uppercase (e.g., {@code TRACEPARENT}, {@code
18-
* TRACESTATE}, {@code BAGGAGE}). This setter translates keys to uppercase before inserting them
19-
* into the carrier.
19+
* TRACESTATE}, {@code BAGGAGE}). This setter translates keys to uppercase and replaces characters
20+
* not allowed in environment variables (e.g., {@code .} and {@code -}) with underscores before
21+
* inserting them into the carrier.
22+
*
23+
* @see <a href=
24+
* "https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/context/env-carriers.md#format-restrictions">Environment
25+
* Variable Format Restrictions</a>
2026
*/
21-
public enum EnvironmentSetter implements TextMapSetter<Map<String, String>> {
22-
INSTANCE;
27+
@Immutable
28+
public final class EnvironmentSetter implements TextMapSetter<Map<String, String>> {
29+
30+
private static final EnvironmentSetter INSTANCE = new EnvironmentSetter();
31+
32+
private EnvironmentSetter() {}
33+
34+
/** Returns the singleton instance of {@link EnvironmentSetter}. */
35+
public static EnvironmentSetter getInstance() {
36+
return INSTANCE;
37+
}
2338

2439
@Override
2540
public void set(@Nullable Map<String, String> carrier, String key, String value) {
2641
if (carrier == null || key == null || value == null) {
2742
return;
2843
}
29-
// Spec recommends using uppercase for environment variable names.
30-
carrier.put(key.toUpperCase(Locale.ROOT), value);
44+
// Spec recommends using uppercase and underscores for environment variable
45+
// names for maximum
46+
// cross-platform compatibility.
47+
String sanitizedKey = key.replace('.', '_').replace('-', '_').toUpperCase(Locale.ROOT);
48+
carrier.put(sanitizedKey, value);
3149
}
3250

3351
@Override

api/incubator/src/test/java/io/opentelemetry/api/incubator/propagation/EnvironmentGetterTest.java

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,26 @@ void get() {
2222
carrier.put("BAGGAGE", "val3");
2323
carrier.put("OTHER", "val4");
2424

25-
assertThat(EnvironmentGetter.INSTANCE.get(carrier, "traceparent")).isEqualTo("val1");
26-
assertThat(EnvironmentGetter.INSTANCE.get(carrier, "TRACESTATE")).isEqualTo("val2");
27-
assertThat(EnvironmentGetter.INSTANCE.get(carrier, "Baggage")).isEqualTo("val3");
28-
assertThat(EnvironmentGetter.INSTANCE.get(carrier, "other")).isEqualTo("val4");
25+
assertThat(EnvironmentGetter.getInstance().get(carrier, "traceparent")).isEqualTo("val1");
26+
assertThat(EnvironmentGetter.getInstance().get(carrier, "TRACESTATE")).isEqualTo("val2");
27+
assertThat(EnvironmentGetter.getInstance().get(carrier, "Baggage")).isEqualTo("val3");
28+
assertThat(EnvironmentGetter.getInstance().get(carrier, "other")).isEqualTo("val4");
29+
}
30+
31+
@Test
32+
void get_sanitization() {
33+
Map<String, String> carrier = new HashMap<>();
34+
carrier.put("OTEL_TRACE_ID", "val1");
35+
carrier.put("OTEL_BAGGAGE_KEY", "val2");
36+
37+
assertThat(EnvironmentGetter.getInstance().get(carrier, "otel.trace.id")).isEqualTo("val1");
38+
assertThat(EnvironmentGetter.getInstance().get(carrier, "otel-baggage-key")).isEqualTo("val2");
2939
}
3040

3141
@Test
3242
void get_null() {
33-
assertThat(EnvironmentGetter.INSTANCE.get(null, "key")).isNull();
34-
assertThat(EnvironmentGetter.INSTANCE.get(Collections.emptyMap(), null)).isNull();
43+
assertThat(EnvironmentGetter.getInstance().get(null, "key")).isNull();
44+
assertThat(EnvironmentGetter.getInstance().get(Collections.emptyMap(), null)).isNull();
3545
}
3646

3747
@Test
@@ -40,12 +50,12 @@ void keys() {
4050
carrier.put("K1", "V1");
4151
carrier.put("K2", "V2");
4252

43-
assertThat(EnvironmentGetter.INSTANCE.keys(carrier)).containsExactlyInAnyOrder("K1", "K2");
44-
assertThat(EnvironmentGetter.INSTANCE.keys(null)).isEmpty();
53+
assertThat(EnvironmentGetter.getInstance().keys(carrier)).containsExactlyInAnyOrder("K1", "K2");
54+
assertThat(EnvironmentGetter.getInstance().keys(null)).isEmpty();
4555
}
4656

4757
@Test
4858
void testToString() {
49-
assertThat(EnvironmentGetter.INSTANCE.toString()).isEqualTo("EnvironmentGetter");
59+
assertThat(EnvironmentGetter.getInstance().toString()).isEqualTo("EnvironmentGetter");
5060
}
5161
}

api/incubator/src/test/java/io/opentelemetry/api/incubator/propagation/EnvironmentSetterTest.java

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,26 +16,36 @@ class EnvironmentSetterTest {
1616
@Test
1717
void set() {
1818
Map<String, String> carrier = new HashMap<>();
19-
EnvironmentSetter.INSTANCE.set(carrier, "traceparent", "val1");
20-
EnvironmentSetter.INSTANCE.set(carrier, "TRACESTATE", "val2");
21-
EnvironmentSetter.INSTANCE.set(carrier, "Baggage", "val3");
19+
EnvironmentSetter.getInstance().set(carrier, "traceparent", "val1");
20+
EnvironmentSetter.getInstance().set(carrier, "TRACESTATE", "val2");
21+
EnvironmentSetter.getInstance().set(carrier, "Baggage", "val3");
2222

2323
assertThat(carrier).containsEntry("TRACEPARENT", "val1");
2424
assertThat(carrier).containsEntry("TRACESTATE", "val2");
2525
assertThat(carrier).containsEntry("BAGGAGE", "val3");
2626
}
2727

28+
@Test
29+
void set_sanitization() {
30+
Map<String, String> carrier = new HashMap<>();
31+
EnvironmentSetter.getInstance().set(carrier, "otel.trace.id", "val1");
32+
EnvironmentSetter.getInstance().set(carrier, "otel-baggage-key", "val2");
33+
34+
assertThat(carrier).containsEntry("OTEL_TRACE_ID", "val1");
35+
assertThat(carrier).containsEntry("OTEL_BAGGAGE_KEY", "val2");
36+
}
37+
2838
@Test
2939
void set_null() {
3040
Map<String, String> carrier = new HashMap<>();
31-
EnvironmentSetter.INSTANCE.set(null, "key", "val");
32-
EnvironmentSetter.INSTANCE.set(carrier, null, "val");
33-
EnvironmentSetter.INSTANCE.set(carrier, "key", null);
41+
EnvironmentSetter.getInstance().set(null, "key", "val");
42+
EnvironmentSetter.getInstance().set(carrier, null, "val");
43+
EnvironmentSetter.getInstance().set(carrier, "key", null);
3444
assertThat(carrier).isEmpty();
3545
}
3646

3747
@Test
3848
void testToString() {
39-
assertThat(EnvironmentSetter.INSTANCE.toString()).isEqualTo("EnvironmentSetter");
49+
assertThat(EnvironmentSetter.getInstance().toString()).isEqualTo("EnvironmentSetter");
4050
}
4151
}

0 commit comments

Comments
 (0)