Don't pass configuration to SDK autoconfigure through system props (#3866)
* Don't pass configuration to SDK autoconfigure through system props * suppress CanonicalDuration * checkstyle
This commit is contained in:
parent
31f22eb4f7
commit
ca8a119e01
|
@ -1,44 +0,0 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.api.config;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
final class CollectionParsers {
|
||||
private static final Logger logger = LoggerFactory.getLogger(CollectionParsers.class);
|
||||
|
||||
static List<String> parseList(String value) {
|
||||
String[] tokens = value.split(",", -1);
|
||||
// Remove whitespace from each item.
|
||||
for (int i = 0; i < tokens.length; i++) {
|
||||
tokens[i] = tokens[i].trim();
|
||||
}
|
||||
return Collections.unmodifiableList(Arrays.asList(tokens));
|
||||
}
|
||||
|
||||
static Map<String, String> parseMap(String value) {
|
||||
Map<String, String> result = new LinkedHashMap<>();
|
||||
for (String token : value.split(",", -1)) {
|
||||
token = token.trim();
|
||||
String[] parts = token.split("=", -1);
|
||||
if (parts.length != 2) {
|
||||
logger.warn(
|
||||
"Invalid map config part, should be formatted key1=value1,key2=value2: {}", value);
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
result.put(parts[0], parts[1]);
|
||||
}
|
||||
return Collections.unmodifiableMap(result);
|
||||
}
|
||||
|
||||
private CollectionParsers() {}
|
||||
}
|
|
@ -8,6 +8,7 @@ package io.opentelemetry.instrumentation.api.config;
|
|||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import com.google.auto.value.AutoValue;
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -17,6 +18,17 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Represents the global agent configuration consisting of system properties, environment variables,
|
||||
* contents of the agent configuration file and properties defined by the {@code
|
||||
* ConfigPropertySource} SPI (see {@code ConfigInitializer} and {@link ConfigBuilder}).
|
||||
*
|
||||
* <p>In case any {@code get*()} method variant gets called for the same property more than once
|
||||
* (e.g. each time an advice class executes) it is suggested to cache the result instead of
|
||||
* repeatedly calling {@link Config}. Agent configuration does not change during the runtime so
|
||||
* retrieving the property once and storing its result in e.g. static final field allows JIT to do
|
||||
* its magic and remove some code branches.
|
||||
*/
|
||||
@AutoValue
|
||||
public abstract class Config {
|
||||
private static final Logger logger = LoggerFactory.getLogger(Config.class);
|
||||
|
@ -34,6 +46,9 @@ public abstract class Config {
|
|||
return new AutoValue_Config(allProperties);
|
||||
}
|
||||
|
||||
// package protected constructor to make extending this class impossible
|
||||
Config() {}
|
||||
|
||||
/**
|
||||
* Sets the agent configuration singleton. This method is only supposed to be called once, from
|
||||
* the agent classloader just before the first instrumentation is loaded (and before {@link
|
||||
|
@ -47,6 +62,7 @@ public abstract class Config {
|
|||
instance = requireNonNull(config);
|
||||
}
|
||||
|
||||
/** Returns the global agent configuration. */
|
||||
public static Config get() {
|
||||
if (instance == null) {
|
||||
// this should only happen in library instrumentation
|
||||
|
@ -61,98 +77,186 @@ public abstract class Config {
|
|||
public abstract Map<String, String> getAllProperties();
|
||||
|
||||
/**
|
||||
* Returns a string property value or null if a property with name {@code name} did not exist.
|
||||
*
|
||||
* @see #getProperty(String, String)
|
||||
* Returns a string-valued configuration property or {@code null} if a property with name {@code
|
||||
* name} has not been configured.
|
||||
*/
|
||||
@Nullable
|
||||
public String getProperty(String name) {
|
||||
return getProperty(name, null);
|
||||
return getRawProperty(name, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a property value from the agent configuration consisting of system properties,
|
||||
* environment variables and contents of the agent configuration file (see {@code
|
||||
* ConfigInitializer} and {@code ConfigBuilder}).
|
||||
*
|
||||
* <p>In case any {@code get*Property()} method variant gets called for the same property more
|
||||
* than once (e.g. each time an advice executes) it is suggested to cache the result instead of
|
||||
* repeatedly calling {@link Config}. Agent configuration does not change during the runtime so
|
||||
* retrieving the property once and storing its result in e.g. static final field allows JIT to do
|
||||
* its magic and remove some code branches.
|
||||
*
|
||||
* @return A string property value or {@code defaultValue} if a property with name {@code name}
|
||||
* did not exist.
|
||||
* Returns a string-valued configuration property or {@code defaultValue} if a property with name
|
||||
* {@code name} has not been configured.
|
||||
*/
|
||||
public String getProperty(String name, String defaultValue) {
|
||||
return getAllProperties().getOrDefault(NamingConvention.DOT.normalize(name), defaultValue);
|
||||
return getRawProperty(name, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a boolean property value or {@code defaultValue} if a property with name {@code name}
|
||||
* did not exist.
|
||||
*
|
||||
* @see #getProperty(String, String)
|
||||
* Returns a boolean-valued configuration property or {@code null} if a property with name {@code
|
||||
* name} has not been configured.
|
||||
*/
|
||||
@Nullable
|
||||
public Boolean getBoolean(String name) {
|
||||
return getTypedProperty(name, Boolean::parseBoolean, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a boolean-valued configuration property or {@code defaultValue} if a property with name
|
||||
* {@code name} has not been configured.
|
||||
*/
|
||||
public boolean getBooleanProperty(String name, boolean defaultValue) {
|
||||
return getTypedProperty(name, Boolean::parseBoolean, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a long property value or {@code defaultValue} if a property with name {@code name} did
|
||||
* not exist.
|
||||
*
|
||||
* <p>This property may be used by vendor distributions to get numerical values.
|
||||
*
|
||||
* @see #getProperty(String, String)
|
||||
* Returns a integer-valued configuration property or {@code null} if a property with name {@code
|
||||
* name} has not been configured.
|
||||
*/
|
||||
public long getLongProperty(String name, long defaultValue) {
|
||||
@Nullable
|
||||
public Integer getInt(String name) {
|
||||
return getTypedProperty(name, Integer::parseInt, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a integer-valued configuration property or {@code defaultValue} if a property with name
|
||||
* {@code name} has not been configured.
|
||||
*/
|
||||
public int getInt(String name, int defaultValue) {
|
||||
return getTypedProperty(name, Integer::parseInt, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a long-valued configuration property or {@code null} if a property with name {@code
|
||||
* name} has not been configured.
|
||||
*/
|
||||
@Nullable
|
||||
public Long getLong(String name) {
|
||||
return getTypedProperty(name, Long::parseLong, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a long-valued configuration property or {@code defaultValue} if a property with name
|
||||
* {@code name} has not been configured.
|
||||
*/
|
||||
public long getLong(String name, long defaultValue) {
|
||||
return getTypedProperty(name, Long::parseLong, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list-of-strings property value or empty list if a property with name {@code name} did
|
||||
* not exist.
|
||||
* Returns a double-valued configuration property or {@code null} if a property with name {@code
|
||||
* name} has not been configured.
|
||||
*/
|
||||
@Nullable
|
||||
public Double getDouble(String name) {
|
||||
return getTypedProperty(name, Double::parseDouble, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a double-valued configuration property or {@code defaultValue} if a property with name
|
||||
* {@code name} has not been configured.
|
||||
*/
|
||||
public double getDouble(String name, double defaultValue) {
|
||||
return getTypedProperty(name, Double::parseDouble, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a duration-valued configuration property or {@code null} if a property with name {@code
|
||||
* name} has not been configured.
|
||||
*
|
||||
* @see #getProperty(String, String)
|
||||
* <p>Durations can be of the form "{number}{unit}", where unit is one of:
|
||||
*
|
||||
* <ul>
|
||||
* <li>ms
|
||||
* <li>s
|
||||
* <li>m
|
||||
* <li>h
|
||||
* <li>d
|
||||
* </ul>
|
||||
*
|
||||
* <p>If no unit is specified, milliseconds is the assumed duration unit.
|
||||
*/
|
||||
@Nullable
|
||||
public Duration getDuration(String name) {
|
||||
return getTypedProperty(name, ConfigValueParsers::parseDuration, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a duration-valued configuration property or {@code defaultValue} if a property with
|
||||
* name {@code name} has not been configured.
|
||||
*
|
||||
* <p>Durations can be of the form "{number}{unit}", where unit is one of:
|
||||
*
|
||||
* <ul>
|
||||
* <li>ms
|
||||
* <li>s
|
||||
* <li>m
|
||||
* <li>h
|
||||
* <li>d
|
||||
* </ul>
|
||||
*
|
||||
* <p>If no unit is specified, milliseconds is the assumed duration unit.
|
||||
*/
|
||||
public Duration getDuration(String name, Duration defaultValue) {
|
||||
return getTypedProperty(name, ConfigValueParsers::parseDuration, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list-valued configuration property or an empty list if a property with name {@code
|
||||
* name} has not been configured. The format of the original value must be comma-separated, e.g.
|
||||
* {@code one,two,three}.
|
||||
*/
|
||||
public List<String> getListProperty(String name) {
|
||||
return getListProperty(name, Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list-of-strings property value or {@code defaultValue} if a property with name {@code
|
||||
* name} did not exist.
|
||||
*
|
||||
* @see #getProperty(String, String)
|
||||
* Returns a list-valued configuration property or {@code defaultValue} if a property with name
|
||||
* {@code name} has not been configured. The format of the original value must be comma-separated,
|
||||
* e.g. {@code one,two,three}.
|
||||
*/
|
||||
public List<String> getListProperty(String name, List<String> defaultValue) {
|
||||
return getTypedProperty(name, CollectionParsers::parseList, defaultValue);
|
||||
return getTypedProperty(name, ConfigValueParsers::parseList, defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map-of-strings property value or empty map if a property with name {@code name} did
|
||||
* not exist.
|
||||
*
|
||||
* @see #getProperty(String, String)
|
||||
* Returns a map-valued configuration property or an empty map if a property with name {@code
|
||||
* name} has not been configured. The format of the original value must be comma-separated for
|
||||
* each key, with an '=' separating the key and value, e.g. {@code
|
||||
* key=value,anotherKey=anotherValue}.
|
||||
*/
|
||||
public Map<String, String> getMapProperty(String name) {
|
||||
return getTypedProperty(name, CollectionParsers::parseMap, Collections.emptyMap());
|
||||
return getMapProperty(name, Collections.emptyMap());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a map-valued configuration property or {@code defaultValue} if a property with name
|
||||
* {@code name} has not been configured. The format of the original value must be comma-separated
|
||||
* for each key, with an '=' separating the key and value, e.g. {@code
|
||||
* key=value,anotherKey=anotherValue}.
|
||||
*/
|
||||
public Map<String, String> getMapProperty(String name, Map<String, String> defaultValue) {
|
||||
return getTypedProperty(name, ConfigValueParsers::parseMap, defaultValue);
|
||||
}
|
||||
|
||||
private <T> T getTypedProperty(String name, Function<String, T> parser, T defaultValue) {
|
||||
String value = getProperty(name);
|
||||
String value = getRawProperty(name, null);
|
||||
if (value == null || value.trim().isEmpty()) {
|
||||
return defaultValue;
|
||||
}
|
||||
try {
|
||||
return parser.apply(value);
|
||||
} catch (Throwable t) {
|
||||
} catch (RuntimeException t) {
|
||||
logger.debug("Cannot parse {}", value, t);
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
private String getRawProperty(String name, String defaultValue) {
|
||||
return getAllProperties().getOrDefault(NamingConvention.DOT.normalize(name), defaultValue);
|
||||
}
|
||||
|
||||
public boolean isInstrumentationEnabled(
|
||||
Iterable<String> instrumentationNames, boolean defaultEnabled) {
|
||||
return isInstrumentationPropertyEnabled(instrumentationNames, "enabled", defaultEnabled);
|
||||
|
@ -176,6 +280,10 @@ public abstract class Config {
|
|||
return anyEnabled;
|
||||
}
|
||||
|
||||
public boolean isAgentDebugEnabled() {
|
||||
return getBooleanProperty("otel.javaagent.debug", false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts this config instance to Java {@link Properties}.
|
||||
*
|
||||
|
@ -187,8 +295,4 @@ public abstract class Config {
|
|||
properties.putAll(getAllProperties());
|
||||
return properties;
|
||||
}
|
||||
|
||||
public boolean isAgentDebugEnabled() {
|
||||
return getBooleanProperty("otel.javaagent.debug", false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,11 @@ public final class ConfigBuilder {
|
|||
|
||||
private final Map<String, String> allProperties = new HashMap<>();
|
||||
|
||||
public ConfigBuilder addProperty(String name, String value) {
|
||||
allProperties.put(NamingConvention.DOT.normalize(name), value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ConfigBuilder readProperties(Properties properties) {
|
||||
for (String name : properties.stringPropertyNames()) {
|
||||
allProperties.put(NamingConvention.DOT.normalize(name), properties.getProperty(name));
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.api.config;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
final class ConfigValueParsers {
|
||||
|
||||
static List<String> parseList(String value) {
|
||||
String[] tokens = value.split(",", -1);
|
||||
// Remove whitespace from each item.
|
||||
for (int i = 0; i < tokens.length; i++) {
|
||||
tokens[i] = tokens[i].trim();
|
||||
}
|
||||
return Collections.unmodifiableList(Arrays.asList(tokens));
|
||||
}
|
||||
|
||||
static Map<String, String> parseMap(String value) {
|
||||
Map<String, String> result = new LinkedHashMap<>();
|
||||
for (String token : value.split(",", -1)) {
|
||||
token = token.trim();
|
||||
String[] parts = token.split("=", -1);
|
||||
if (parts.length != 2) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid map config part, should be formatted key1=value1,key2=value2: " + value);
|
||||
}
|
||||
result.put(parts[0], parts[1]);
|
||||
}
|
||||
return Collections.unmodifiableMap(result);
|
||||
}
|
||||
|
||||
static Duration parseDuration(String value) {
|
||||
String unitString = getUnitString(value);
|
||||
String numberString = value.substring(0, value.length() - unitString.length());
|
||||
long rawNumber = Long.parseLong(numberString.trim());
|
||||
TimeUnit unit = getDurationUnit(unitString.trim());
|
||||
return Duration.ofMillis(TimeUnit.MILLISECONDS.convert(rawNumber, unit));
|
||||
}
|
||||
|
||||
/** Returns the TimeUnit associated with a unit string. Defaults to milliseconds. */
|
||||
private static TimeUnit getDurationUnit(String unitString) {
|
||||
switch (unitString) {
|
||||
case "": // Fallthrough expected
|
||||
case "ms":
|
||||
return TimeUnit.MILLISECONDS;
|
||||
case "s":
|
||||
return TimeUnit.SECONDS;
|
||||
case "m":
|
||||
return TimeUnit.MINUTES;
|
||||
case "h":
|
||||
return TimeUnit.HOURS;
|
||||
case "d":
|
||||
return TimeUnit.DAYS;
|
||||
default:
|
||||
throw new IllegalArgumentException("Invalid duration string, found: " + unitString);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fragments the 'units' portion of a config value from the 'value' portion.
|
||||
*
|
||||
* <p>E.g. "1ms" would return the string "ms".
|
||||
*/
|
||||
private static String getUnitString(String rawValue) {
|
||||
int lastDigitIndex = rawValue.length() - 1;
|
||||
while (lastDigitIndex >= 0) {
|
||||
char c = rawValue.charAt(lastDigitIndex);
|
||||
if (Character.isDigit(c)) {
|
||||
break;
|
||||
}
|
||||
lastDigitIndex -= 1;
|
||||
}
|
||||
// Pull everything after the last digit.
|
||||
return rawValue.substring(lastDigitIndex + 1);
|
||||
}
|
||||
|
||||
private ConfigValueParsers() {}
|
||||
}
|
|
@ -7,7 +7,8 @@ package io.opentelemetry.instrumentation.api.config
|
|||
|
||||
import spock.lang.Specification
|
||||
|
||||
class ConfigTest extends Specification {
|
||||
// TODO: rewrite to Java & JUnit
|
||||
class ConfigSpockTest extends Specification {
|
||||
def "verify instrumentation config"() {
|
||||
setup:
|
||||
def config = new ConfigBuilder().readProperties([
|
||||
|
@ -42,64 +43,6 @@ class ConfigTest extends Specification {
|
|||
instrumentationNames = new TreeSet<String>(names)
|
||||
}
|
||||
|
||||
def "should get string property"() {
|
||||
given:
|
||||
def config = new ConfigBuilder().readProperties([
|
||||
"property.string": "whatever"
|
||||
]).build()
|
||||
|
||||
expect:
|
||||
config.getProperty("property.string") == "whatever"
|
||||
config.getProperty("property.string", "default") == "whatever"
|
||||
config.getProperty("does-not-exist") == null
|
||||
config.getProperty("does-not-exist", "default") == "default"
|
||||
}
|
||||
|
||||
def "should get boolean property"() {
|
||||
given:
|
||||
def config = new ConfigBuilder().readProperties([
|
||||
"property.bool": "false"
|
||||
]).build()
|
||||
|
||||
expect:
|
||||
!config.getBooleanProperty("property.bool", true)
|
||||
config.getBooleanProperty("does-not-exist", true)
|
||||
}
|
||||
|
||||
def "should get list property"() {
|
||||
given:
|
||||
def config = new ConfigBuilder().readProperties([
|
||||
"property.list": "one, two, three"
|
||||
]).build()
|
||||
|
||||
expect:
|
||||
config.getListProperty("property.list") == ["one", "two", "three"]
|
||||
config.getListProperty("property.list", ["four"]) == ["one", "two", "three"]
|
||||
config.getListProperty("does-not-exist") == []
|
||||
config.getListProperty("does-not-exist", ["four"]) == ["four"]
|
||||
}
|
||||
|
||||
def "should get map property"() {
|
||||
given:
|
||||
def config = new ConfigBuilder().readProperties([
|
||||
"property.map": "one=1, two=2"
|
||||
]).build()
|
||||
|
||||
expect:
|
||||
config.getMapProperty("property.map") == ["one": "1", "two": "2"]
|
||||
config.getMapProperty("does-not-exist").isEmpty()
|
||||
}
|
||||
|
||||
def "should return empty map when map property value is invalid"() {
|
||||
given:
|
||||
def config = new ConfigBuilder().readProperties([
|
||||
"property.map": "one=1, broken!"
|
||||
]).build()
|
||||
|
||||
expect:
|
||||
config.getMapProperty("property.map").isEmpty()
|
||||
}
|
||||
|
||||
def "should expose if agent debug is enabled"() {
|
||||
given:
|
||||
def config = new ConfigBuilder().readProperties([
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.instrumentation.api.config;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
// suppress duration unit check, e.g. ofMillis(5000) -> ofSeconds(5)
|
||||
@SuppressWarnings("CanonicalDuration")
|
||||
class ConfigTest {
|
||||
@Test
|
||||
void shouldGetString() {
|
||||
Config config = Config.newBuilder().addProperty("prop.string", "some text").build();
|
||||
|
||||
assertEquals("some text", config.getProperty("prop.string"));
|
||||
assertEquals("some text", config.getProperty("prop.string", "default"));
|
||||
assertNull(config.getProperty("prop.missing"));
|
||||
assertEquals("default", config.getProperty("prop.missing", "default"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGetBoolean() {
|
||||
Config config = Config.newBuilder().addProperty("prop.boolean", "true").build();
|
||||
|
||||
assertTrue(config.getBoolean("prop.boolean"));
|
||||
assertTrue(config.getBooleanProperty("prop.boolean", false));
|
||||
assertNull(config.getBoolean("prop.missing"));
|
||||
assertFalse(config.getBooleanProperty("prop.missing", false));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGetInt() {
|
||||
Config config =
|
||||
Config.newBuilder()
|
||||
.addProperty("prop.int", "12")
|
||||
.addProperty("prop.wrong", "twelve")
|
||||
.build();
|
||||
|
||||
assertEquals(12, config.getInt("prop.int"));
|
||||
assertEquals(12, config.getInt("prop.int", 1000));
|
||||
assertNull(config.getInt("prop.wrong"));
|
||||
assertEquals(1000, config.getInt("prop.wrong", 1000));
|
||||
assertNull(config.getInt("prop.missing"));
|
||||
assertEquals(1000, config.getInt("prop.missing", 1000));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGetLong() {
|
||||
Config config =
|
||||
Config.newBuilder()
|
||||
.addProperty("prop.long", "12")
|
||||
.addProperty("prop.wrong", "twelve")
|
||||
.build();
|
||||
|
||||
assertEquals(12, config.getLong("prop.long"));
|
||||
assertEquals(12, config.getLong("prop.long", 1000));
|
||||
assertNull(config.getLong("prop.wrong"));
|
||||
assertEquals(1000, config.getLong("prop.wrong", 1000));
|
||||
assertNull(config.getLong("prop.missing"));
|
||||
assertEquals(1000, config.getLong("prop.missing", 1000));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGetDouble() {
|
||||
Config config =
|
||||
Config.newBuilder()
|
||||
.addProperty("prop.double", "12.345")
|
||||
.addProperty("prop.wrong", "twelve point something")
|
||||
.build();
|
||||
|
||||
assertEquals(12.345, config.getDouble("prop.double"));
|
||||
assertEquals(12.345, config.getDouble("prop.double", 99.99));
|
||||
assertNull(config.getDouble("prop.wrong"));
|
||||
assertEquals(99.99, config.getDouble("prop.wrong", 99.99));
|
||||
assertNull(config.getDouble("prop.missing"));
|
||||
assertEquals(99.99, config.getDouble("prop.missing", 99.99));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGetDuration_defaultUnit() {
|
||||
Config config =
|
||||
Config.newBuilder()
|
||||
.addProperty("prop.duration", "5000")
|
||||
.addProperty("prop.wrong", "hundred days")
|
||||
.build();
|
||||
|
||||
assertEquals(Duration.ofMillis(5000), config.getDuration("prop.duration"));
|
||||
assertEquals(Duration.ofMillis(5000), config.getDuration("prop.duration", Duration.ZERO));
|
||||
assertNull(config.getDuration("prop.wrong"));
|
||||
assertEquals(Duration.ZERO, config.getDuration("prop.wrong", Duration.ZERO));
|
||||
assertNull(config.getDuration("prop.missing"));
|
||||
assertEquals(Duration.ZERO, config.getDuration("prop.missing", Duration.ZERO));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGetDuration_variousUnits() {
|
||||
Config config = Config.newBuilder().addProperty("prop.duration", "100ms").build();
|
||||
assertEquals(Duration.ofMillis(100), config.getDuration("prop.duration"));
|
||||
|
||||
config = Config.newBuilder().addProperty("prop.duration", "100s").build();
|
||||
assertEquals(Duration.ofSeconds(100), config.getDuration("prop.duration"));
|
||||
|
||||
config = Config.newBuilder().addProperty("prop.duration", "100m").build();
|
||||
assertEquals(Duration.ofMinutes(100), config.getDuration("prop.duration"));
|
||||
|
||||
config = Config.newBuilder().addProperty("prop.duration", "100h").build();
|
||||
assertEquals(Duration.ofHours(100), config.getDuration("prop.duration"));
|
||||
|
||||
config = Config.newBuilder().addProperty("prop.duration", "100d").build();
|
||||
assertEquals(Duration.ofDays(100), config.getDuration("prop.duration"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGetList() {
|
||||
Config config = Config.newBuilder().addProperty("prop.list", "one, two ,three").build();
|
||||
|
||||
assertEquals(asList("one", "two", "three"), config.getListProperty("prop.list"));
|
||||
assertEquals(
|
||||
asList("one", "two", "three"),
|
||||
config.getListProperty("prop.list", singletonList("default")));
|
||||
assertTrue(config.getListProperty("prop.missing").isEmpty());
|
||||
assertEquals(
|
||||
singletonList("default"), config.getListProperty("prop.missing", singletonList("default")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGetMap() {
|
||||
Config config =
|
||||
Config.newBuilder()
|
||||
.addProperty("prop.map", "one=1, two=2")
|
||||
.addProperty("prop.wrong", "one=1, but not two!")
|
||||
.build();
|
||||
|
||||
assertEquals(map("one", "1", "two", "2"), config.getMapProperty("prop.map"));
|
||||
assertEquals(
|
||||
map("one", "1", "two", "2"), config.getMapProperty("prop.map", singletonMap("three", "3")));
|
||||
assertTrue(config.getMapProperty("prop.wrong").isEmpty());
|
||||
assertEquals(
|
||||
singletonMap("three", "3"),
|
||||
config.getMapProperty("prop.wrong", singletonMap("three", "3")));
|
||||
assertTrue(config.getMapProperty("prop.missing").isEmpty());
|
||||
assertEquals(
|
||||
singletonMap("three", "3"),
|
||||
config.getMapProperty("prop.missing", singletonMap("three", "3")));
|
||||
}
|
||||
|
||||
public static Map<String, String> map(String k1, String v1, String k2, String v2) {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put(k1, v1);
|
||||
map.put(k2, v2);
|
||||
return map;
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ import io.opentelemetry.extension.noopapi.NoopOpenTelemetry;
|
|||
import io.opentelemetry.instrumentation.api.config.Config;
|
||||
import io.opentelemetry.javaagent.extension.AgentListener;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.OpenTelemetrySdkAccess;
|
||||
import io.opentelemetry.javaagent.tooling.config.ConfigPropertiesAdapter;
|
||||
import io.opentelemetry.sdk.OpenTelemetrySdk;
|
||||
import io.opentelemetry.sdk.autoconfigure.OpenTelemetrySdkAutoConfiguration;
|
||||
import io.opentelemetry.sdk.autoconfigure.spi.SdkMeterProviderConfigurer;
|
||||
|
@ -23,7 +24,6 @@ import io.opentelemetry.sdk.metrics.export.IntervalMetricReader;
|
|||
import io.opentelemetry.sdk.metrics.view.InstrumentSelector;
|
||||
import io.opentelemetry.sdk.metrics.view.View;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -48,14 +48,13 @@ public class OpenTelemetryInstaller implements AgentListener {
|
|||
public static synchronized void installAgentTracer(Config config) {
|
||||
if (config.getBooleanProperty(JAVAAGENT_ENABLED_CONFIG, true)) {
|
||||
|
||||
copySystemProperties(config);
|
||||
|
||||
if (config.getBooleanProperty(JAVAAGENT_NOOP_CONFIG, false)) {
|
||||
GlobalOpenTelemetry.set(NoopOpenTelemetry.getInstance());
|
||||
} else {
|
||||
System.setProperty("io.opentelemetry.context.contextStorageProvider", "default");
|
||||
|
||||
OpenTelemetrySdk sdk = OpenTelemetrySdkAutoConfiguration.initialize();
|
||||
OpenTelemetrySdk sdk =
|
||||
OpenTelemetrySdkAutoConfiguration.initialize(true, new ConfigPropertiesAdapter(config));
|
||||
OpenTelemetrySdkAccess.internalSetForceFlush(
|
||||
(timeout, unit) -> {
|
||||
CompletableResultCode traceResult = sdk.getSdkTracerProvider().forceFlush();
|
||||
|
@ -70,28 +69,6 @@ public class OpenTelemetryInstaller implements AgentListener {
|
|||
}
|
||||
}
|
||||
|
||||
// OpenTelemetrySdkAutoConfiguration currently only supports configuration from environment. We
|
||||
// massage any properties we have that aren't in the environment to system properties.
|
||||
// TODO(anuraaga): Make this less hacky
|
||||
private static void copySystemProperties(Config config) {
|
||||
Map<String, String> allProperties = config.getAllProperties();
|
||||
Map<String, String> environmentProperties =
|
||||
Config.newBuilder()
|
||||
.readEnvironmentVariables()
|
||||
.readSystemProperties()
|
||||
.build()
|
||||
.getAllProperties();
|
||||
|
||||
allProperties.forEach(
|
||||
(key, value) -> {
|
||||
if (!environmentProperties.containsKey(key)
|
||||
&& key.startsWith("otel.")
|
||||
&& !key.startsWith("otel.instrumentation")) {
|
||||
System.setProperty(key, value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Configure histogram metrics similarly to how the SDK will default in 1.5.0 for early feedback.
|
||||
@AutoService(SdkMeterProviderConfigurer.class)
|
||||
public static final class OpenTelemetryMetricsConfigurer implements SdkMeterProviderConfigurer {
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.tooling.config;
|
||||
|
||||
import io.opentelemetry.instrumentation.api.config.Config;
|
||||
import io.opentelemetry.sdk.autoconfigure.ConfigProperties;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public final class ConfigPropertiesAdapter implements ConfigProperties {
|
||||
private final Config config;
|
||||
|
||||
public ConfigPropertiesAdapter(Config config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getString(String name) {
|
||||
return config.getProperty(name);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Boolean getBoolean(String name) {
|
||||
return config.getBoolean(name);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Integer getInt(String name) {
|
||||
return config.getInt(name);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Long getLong(String name) {
|
||||
return config.getLong(name);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Double getDouble(String name) {
|
||||
return config.getDouble(name);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Duration getDuration(String name) {
|
||||
return config.getDuration(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getCommaSeparatedValues(String name) {
|
||||
return config.getListProperty(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getCommaSeparatedMap(String name) {
|
||||
return config.getMapProperty(name);
|
||||
}
|
||||
}
|
|
@ -6,8 +6,8 @@
|
|||
package io.opentelemetry.javaagent.tooling
|
||||
|
||||
import io.opentelemetry.api.GlobalOpenTelemetry
|
||||
import io.opentelemetry.extension.noopapi.NoopOpenTelemetry
|
||||
import io.opentelemetry.instrumentation.api.config.Config
|
||||
import io.opentelemetry.extension.noopapi.NoopOpenTelemetry;
|
||||
import spock.lang.Specification
|
||||
|
||||
class OpenTelemetryInstallerTest extends Specification {
|
||||
|
|
Loading…
Reference in New Issue