diff --git a/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/DeclarativeConfigProperties.java b/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/DeclarativeConfigProperties.java index 425a3d768be..ac3cef17c99 100644 --- a/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/DeclarativeConfigProperties.java +++ b/api/incubator/src/main/java/io/opentelemetry/api/incubator/config/DeclarativeConfigProperties.java @@ -46,8 +46,8 @@ static Map toMap(DeclarativeConfigProperties declarativeConfigPr /** * Returns a {@link String} configuration property. * - * @return null if the property has not been configured - * @throws DeclarativeConfigException if the property is not a valid scalar string + * @return null if the property has not been configured or if the property is not a valid scalar + * string */ @Nullable String getString(String name); @@ -56,8 +56,7 @@ static Map toMap(DeclarativeConfigProperties declarativeConfigPr * Returns a {@link String} configuration property. * * @return a {@link String} configuration property or {@code defaultValue} if a property with - * {@code name} has not been configured - * @throws DeclarativeConfigException if the property is not a valid scalar string + * {@code name} has not been configured or is not a valid scalar string */ default String getString(String name, String defaultValue) { return defaultIfNull(getString(name), defaultValue); @@ -67,8 +66,8 @@ default String getString(String name, String defaultValue) { * Returns a {@link Boolean} configuration property. Implementations should use the same rules as * {@link Boolean#parseBoolean(String)} for handling the values. * - * @return null if the property has not been configured - * @throws DeclarativeConfigException if the property is not a valid scalar boolean + * @return null if the property has not been configured or if the property is not a valid scalar + * boolean */ @Nullable Boolean getBoolean(String name); @@ -77,8 +76,7 @@ default String getString(String name, String defaultValue) { * Returns a {@link Boolean} configuration property. * * @return a {@link Boolean} configuration property or {@code defaultValue} if a property with - * {@code name} has not been configured - * @throws DeclarativeConfigException if the property is not a valid scalar boolean + * {@code name} has not been configured or is not a valid scalar boolean */ default boolean getBoolean(String name, boolean defaultValue) { return defaultIfNull(getBoolean(name), defaultValue); @@ -90,8 +88,8 @@ default boolean getBoolean(String name, boolean defaultValue) { *

If the underlying config property is {@link Long}, it is converted to {@link Integer} with * {@link Long#intValue()} which may result in loss of precision. * - * @return null if the property has not been configured - * @throws DeclarativeConfigException if the property is not a valid scalar integer + * @return null if the property has not been configured or if the property is not a valid scalar + * integer */ @Nullable Integer getInt(String name); @@ -103,8 +101,7 @@ default boolean getBoolean(String name, boolean defaultValue) { * {@link Long#intValue()} which may result in loss of precision. * * @return a {@link Integer} configuration property or {@code defaultValue} if a property with - * {@code name} has not been configured - * @throws DeclarativeConfigException if the property is not a valid scalar integer + * {@code name} has not been configured or is not a valid scalar integer */ default int getInt(String name, int defaultValue) { return defaultIfNull(getInt(name), defaultValue); @@ -113,8 +110,8 @@ default int getInt(String name, int defaultValue) { /** * Returns a {@link Long} configuration property. * - * @return null if the property has not been configured - * @throws DeclarativeConfigException if the property is not a valid scalar long + * @return null if the property has not been configured or if the property is not a valid scalar + * long */ @Nullable Long getLong(String name); @@ -123,8 +120,7 @@ default int getInt(String name, int defaultValue) { * Returns a {@link Long} configuration property. * * @return a {@link Long} configuration property or {@code defaultValue} if a property with {@code - * name} has not been configured - * @throws DeclarativeConfigException if the property is not a valid scalar long + * name} has not been configured or is not a valid scalar long */ default long getLong(String name, long defaultValue) { return defaultIfNull(getLong(name), defaultValue); @@ -133,8 +129,8 @@ default long getLong(String name, long defaultValue) { /** * Returns a {@link Double} configuration property. * - * @return null if the property has not been configured - * @throws DeclarativeConfigException if the property is not a valid scalar double + * @return null if the property has not been configured or if the property is not a valid scalar + * double */ @Nullable Double getDouble(String name); @@ -143,8 +139,7 @@ default long getLong(String name, long defaultValue) { * Returns a {@link Double} configuration property. * * @return a {@link Double} configuration property or {@code defaultValue} if a property with - * {@code name} has not been configured - * @throws DeclarativeConfigException if the property is not a valid scalar double + * {@code name} has not been configured or is not a valid scalar double */ default double getDouble(String name, double defaultValue) { return defaultIfNull(getDouble(name), defaultValue); @@ -158,8 +153,8 @@ default double getDouble(String name, double defaultValue) { * @param scalarType the scalar type, one of {@link String}, {@link Boolean}, {@link Long} or * {@link Double} * @return a {@link List} configuration property, or null if the property has not been configured - * @throws DeclarativeConfigException if the property is not a valid sequence of scalars, or if - * {@code scalarType} is not supported + * or if the property is not a valid sequence of scalars + * @throws DeclarativeConfigException if {@code scalarType} is not supported */ @Nullable List getScalarList(String name, Class scalarType); @@ -172,8 +167,7 @@ default double getDouble(String name, double defaultValue) { * @param scalarType the scalar type, one of {@link String}, {@link Boolean}, {@link Long} or * {@link Double} * @return a {@link List} configuration property or {@code defaultValue} if a property with {@code - * name} has not been configured - * @throws DeclarativeConfigException if the property is not a valid sequence of scalars + * name} has not been configured or is not a valid sequence of scalars */ default List getScalarList(String name, Class scalarType, List defaultValue) { return defaultIfNull(getScalarList(name, scalarType), defaultValue); @@ -183,8 +177,7 @@ default List getScalarList(String name, Class scalarType, List defa * Returns a {@link DeclarativeConfigProperties} configuration property. * * @return a map-valued configuration property, or {@code null} if {@code name} has not been - * configured - * @throws DeclarativeConfigException if the property is not a mapping + * configured or is not a mapping */ @Nullable DeclarativeConfigProperties getStructured(String name); @@ -193,8 +186,7 @@ default List getScalarList(String name, Class scalarType, List defa * Returns a list of {@link DeclarativeConfigProperties} configuration property. * * @return a map-valued configuration property, or {@code defaultValue} if {@code name} has not - * been configured - * @throws DeclarativeConfigException if the property is not a mapping + * been configured or is not a mapping */ default DeclarativeConfigProperties getStructured( String name, DeclarativeConfigProperties defaultValue) { @@ -210,8 +202,7 @@ default DeclarativeConfigProperties getStructured( * but empty vs. not set, use {@link #getStructured(String)}. * * @return a map-valued configuration property, or an empty {@link DeclarativeConfigProperties} - * instance if {@code name} has not been configured - * @throws DeclarativeConfigException if the property is not a mapping + * instance if {@code name} has not been configured or is not a mapping */ default DeclarativeConfigProperties get(String name) { return getStructured(name, empty()); @@ -221,8 +212,7 @@ default DeclarativeConfigProperties get(String name) { * Returns a list of {@link DeclarativeConfigProperties} configuration property. * * @return a list of map-valued configuration property, or {@code null} if {@code name} has not - * been configured - * @throws DeclarativeConfigException if the property is not a sequence of mappings + * been configured or is not a sequence of mappings */ @Nullable List getStructuredList(String name); @@ -231,8 +221,7 @@ default DeclarativeConfigProperties get(String name) { * Returns a list of {@link DeclarativeConfigProperties} configuration property. * * @return a list of map-valued configuration property, or {@code defaultValue} if {@code name} - * has not been configured - * @throws DeclarativeConfigException if the property is not a sequence of mappings + * has not been configured or is not a sequence of mappings */ default List getStructuredList( String name, List defaultValue) { diff --git a/api/incubator/src/main/java/io/opentelemetry/api/incubator/propagation/EnvironmentGetter.java b/api/incubator/src/main/java/io/opentelemetry/api/incubator/propagation/EnvironmentGetter.java new file mode 100644 index 00000000000..5dfae10392f --- /dev/null +++ b/api/incubator/src/main/java/io/opentelemetry/api/incubator/propagation/EnvironmentGetter.java @@ -0,0 +1,47 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.api.incubator.propagation; + +import io.opentelemetry.context.propagation.TextMapGetter; +import java.util.Collections; +import java.util.Locale; +import java.util.Map; +import javax.annotation.Nullable; + +/** + * A {@link TextMapGetter} that extracts context from a map carrier, intended for use with + * environment variables. + * + *

Standard environment variable names are uppercase (e.g., {@code TRACEPARENT}, {@code + * TRACESTATE}, {@code BAGGAGE}). This getter translates keys to uppercase before looking them up in + * the carrier. + */ +public enum EnvironmentGetter implements TextMapGetter> { + INSTANCE; + + @Override + public Iterable keys(Map carrier) { + if (carrier == null) { + return Collections.emptyList(); + } + return carrier.keySet(); + } + + @Nullable + @Override + public String get(@Nullable Map carrier, String key) { + if (carrier == null || key == null) { + return null; + } + // Spec recommends using uppercase for environment variable names. + return carrier.get(key.toUpperCase(Locale.ROOT)); + } + + @Override + public String toString() { + return "EnvironmentGetter"; + } +} diff --git a/api/incubator/src/main/java/io/opentelemetry/api/incubator/propagation/EnvironmentSetter.java b/api/incubator/src/main/java/io/opentelemetry/api/incubator/propagation/EnvironmentSetter.java new file mode 100644 index 00000000000..bc94a12af23 --- /dev/null +++ b/api/incubator/src/main/java/io/opentelemetry/api/incubator/propagation/EnvironmentSetter.java @@ -0,0 +1,37 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.api.incubator.propagation; + +import io.opentelemetry.context.propagation.TextMapSetter; +import java.util.Locale; +import java.util.Map; +import javax.annotation.Nullable; + +/** + * A {@link TextMapSetter} that injects context into a map carrier, intended for use with + * environment variables. + * + *

Standard environment variable names are uppercase (e.g., {@code TRACEPARENT}, {@code + * TRACESTATE}, {@code BAGGAGE}). This setter translates keys to uppercase before inserting them + * into the carrier. + */ +public enum EnvironmentSetter implements TextMapSetter> { + INSTANCE; + + @Override + public void set(@Nullable Map carrier, String key, String value) { + if (carrier == null || key == null || value == null) { + return; + } + // Spec recommends using uppercase for environment variable names. + carrier.put(key.toUpperCase(Locale.ROOT), value); + } + + @Override + public String toString() { + return "EnvironmentSetter"; + } +} diff --git a/api/incubator/src/test/java/io/opentelemetry/api/incubator/propagation/EnvironmentGetterTest.java b/api/incubator/src/test/java/io/opentelemetry/api/incubator/propagation/EnvironmentGetterTest.java new file mode 100644 index 00000000000..0370beb885b --- /dev/null +++ b/api/incubator/src/test/java/io/opentelemetry/api/incubator/propagation/EnvironmentGetterTest.java @@ -0,0 +1,51 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.api.incubator.propagation; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.Test; + +class EnvironmentGetterTest { + + @Test + void get() { + Map carrier = new HashMap<>(); + carrier.put("TRACEPARENT", "val1"); + carrier.put("TRACESTATE", "val2"); + carrier.put("BAGGAGE", "val3"); + carrier.put("OTHER", "val4"); + + assertThat(EnvironmentGetter.INSTANCE.get(carrier, "traceparent")).isEqualTo("val1"); + assertThat(EnvironmentGetter.INSTANCE.get(carrier, "TRACESTATE")).isEqualTo("val2"); + assertThat(EnvironmentGetter.INSTANCE.get(carrier, "Baggage")).isEqualTo("val3"); + assertThat(EnvironmentGetter.INSTANCE.get(carrier, "other")).isEqualTo("val4"); + } + + @Test + void get_null() { + assertThat(EnvironmentGetter.INSTANCE.get(null, "key")).isNull(); + assertThat(EnvironmentGetter.INSTANCE.get(Collections.emptyMap(), null)).isNull(); + } + + @Test + void keys() { + Map carrier = new HashMap<>(); + carrier.put("K1", "V1"); + carrier.put("K2", "V2"); + + assertThat(EnvironmentGetter.INSTANCE.keys(carrier)).containsExactlyInAnyOrder("K1", "K2"); + assertThat(EnvironmentGetter.INSTANCE.keys(null)).isEmpty(); + } + + @Test + void testToString() { + assertThat(EnvironmentGetter.INSTANCE.toString()).isEqualTo("EnvironmentGetter"); + } +} diff --git a/api/incubator/src/test/java/io/opentelemetry/api/incubator/propagation/EnvironmentSetterTest.java b/api/incubator/src/test/java/io/opentelemetry/api/incubator/propagation/EnvironmentSetterTest.java new file mode 100644 index 00000000000..02308a7b544 --- /dev/null +++ b/api/incubator/src/test/java/io/opentelemetry/api/incubator/propagation/EnvironmentSetterTest.java @@ -0,0 +1,41 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.api.incubator.propagation; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; + +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.Test; + +class EnvironmentSetterTest { + + @Test + void set() { + Map carrier = new HashMap<>(); + EnvironmentSetter.INSTANCE.set(carrier, "traceparent", "val1"); + EnvironmentSetter.INSTANCE.set(carrier, "TRACESTATE", "val2"); + EnvironmentSetter.INSTANCE.set(carrier, "Baggage", "val3"); + + assertThat(carrier).containsEntry("TRACEPARENT", "val1"); + assertThat(carrier).containsEntry("TRACESTATE", "val2"); + assertThat(carrier).containsEntry("BAGGAGE", "val3"); + } + + @Test + void set_null() { + Map carrier = new HashMap<>(); + EnvironmentSetter.INSTANCE.set(null, "key", "val"); + EnvironmentSetter.INSTANCE.set(carrier, null, "val"); + EnvironmentSetter.INSTANCE.set(carrier, "key", null); + assertThat(carrier).isEmpty(); + } + + @Test + void testToString() { + assertThat(EnvironmentSetter.INSTANCE.toString()).isEqualTo("EnvironmentSetter"); + } +} diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/YamlDeclarativeConfigPropertiesTest.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/YamlDeclarativeConfigPropertiesTest.java index 63ece0a0687..18611e901d1 100644 --- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/YamlDeclarativeConfigPropertiesTest.java +++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/YamlDeclarativeConfigPropertiesTest.java @@ -230,8 +230,11 @@ void wrongType() { assertThat(otherProps.getDouble("str_key")).isNull(); assertThat(otherProps.getBoolean("str_key")).isNull(); assertThat(otherProps.getScalarList("str_key", String.class)).isNull(); + assertThat(otherProps.getScalarList("str_list_key", Long.class)).isNull(); assertThat(otherProps.getStructured("str_key")).isNull(); assertThat(otherProps.getStructuredList("str_key")).isNull(); + assertThat(otherProps.getStructured("str_list_key")).isNull(); + assertThat(otherProps.getStructuredList("map_key")).isNull(); assertWarning("Ignoring value for key [int_key] because it is Integer instead of String: 1"); assertWarning( @@ -240,6 +243,8 @@ void wrongType() { "Ignoring value for key [str_key] because it is String instead of Double: str_value"); assertWarning( "Ignoring value for key [str_key] because it is String instead of Boolean: str_value"); + assertWarning( + "Ignoring value for key [str_list_key] because it is String instead of Long: val1"); } private void assertWarning(String message) {