Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce ConfigProvider API #6549

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
5 changes: 5 additions & 0 deletions api/incubator/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ dependencies {

annotationProcessor("com.google.auto.value:auto-value")

// To use parsed config file as input for YamlStructuredConfigPropertiesTest
testImplementation(project(":sdk-extensions:incubator"))
// TODO (jack-berg): Why is this dependency not brought in as transitive dependency of :sdk-extensions:incubator?
testImplementation("com.fasterxml.jackson.core:jackson-databind")

testImplementation(project(":sdk:testing"))

testImplementation("io.opentelemetry.semconv:opentelemetry-semconv-incubating")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.api.incubator.config;

import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;

/**
* A registry for accessing declarative configuration.
*
* <p>The name <i>Provider</i> is for consistency with other languages and it is <b>NOT</b> loaded
* using reflection.
*
* <p>See {@link InstrumentationConfigUtil} for convenience methods for extracting config from
* {@link ConfigProvider}.
*/
@ThreadSafe
public interface ConfigProvider {

/**
* Returns the {@link DeclarativeConfigProperties} corresponding to <a
* href="https://github.com/open-telemetry/opentelemetry-configuration/blob/main/schema/instrumentation.json">instrumentation
* config</a>, or {@code null} if unavailable.
*
* @return the instrumentation {@link DeclarativeConfigProperties}
*/
@Nullable
DeclarativeConfigProperties getInstrumentationConfig();

/** Returns a no-op {@link ConfigProvider}. */
static ConfigProvider noop() {
jack-berg marked this conversation as resolved.
Show resolved Hide resolved
return () -> null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.api.incubator.config;

/** An exception that is thrown when errors occur with declarative configuration. */
public final class DeclarativeConfigException extends RuntimeException {

private static final long serialVersionUID = 3036584181551130522L;

/** Create a new configuration exception with specified {@code message} and without a cause. */
public DeclarativeConfigException(String message) {
super(message);
}

/** Create a new configuration exception with specified {@code message} and {@code cause}. */
public DeclarativeConfigException(String message, Throwable cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,30 @@
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.sdk.autoconfigure.spi.internal;
package io.opentelemetry.api.incubator.config;

import static io.opentelemetry.api.internal.ConfigUtil.defaultIfNull;

import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;

/**
* An interface for accessing structured configuration data.
* An interface for accessing declarative configuration data.
*
* <p>An instance of {@link StructuredConfigProperties} is equivalent to a <a
* <p>An instance of {@link DeclarativeConfigProperties} is equivalent to a <a
* href="https://yaml.org/spec/1.2.2/#3211-nodes">YAML mapping node</a>. It has accessors for
* reading scalar properties, {@link #getStructured(String)} for reading children which are
* themselves mappings, and {@link #getStructuredList(String)} for reading children which are
* sequences of mappings.
*/
public interface StructuredConfigProperties {
public interface DeclarativeConfigProperties {

/**
* Returns a {@link String} configuration property.
*
* @return null if the property has not been configured
* @throws ConfigurationException if the property is not a valid scalar string
* @throws DeclarativeConfigException if the property is not a valid scalar string
*/
@Nullable
String getString(String name);
Expand All @@ -38,7 +36,7 @@ public interface StructuredConfigProperties {
*
* @return a {@link String} configuration property or {@code defaultValue} if a property with
* {@code name} has not been configured
* @throws ConfigurationException if the property is not a valid scalar string
* @throws DeclarativeConfigException if the property is not a valid scalar string
*/
default String getString(String name, String defaultValue) {
return defaultIfNull(getString(name), defaultValue);
Expand All @@ -49,7 +47,7 @@ default String getString(String name, String defaultValue) {
* {@link Boolean#parseBoolean(String)} for handling the values.
*
* @return null if the property has not been configured
* @throws ConfigurationException if the property is not a valid scalar boolean
* @throws DeclarativeConfigException if the property is not a valid scalar boolean
*/
@Nullable
Boolean getBoolean(String name);
Expand All @@ -59,7 +57,7 @@ default String getString(String name, String defaultValue) {
*
* @return a {@link Boolean} configuration property or {@code defaultValue} if a property with
* {@code name} has not been configured
* @throws ConfigurationException if the property is not a valid scalar boolean
* @throws DeclarativeConfigException if the property is not a valid scalar boolean
*/
default boolean getBoolean(String name, boolean defaultValue) {
return defaultIfNull(getBoolean(name), defaultValue);
Expand All @@ -72,7 +70,7 @@ default boolean getBoolean(String name, boolean defaultValue) {
* {@link Long#intValue()} which may result in loss of precision.
*
* @return null if the property has not been configured
* @throws ConfigurationException if the property is not a valid scalar integer
* @throws DeclarativeConfigException if the property is not a valid scalar integer
*/
@Nullable
Integer getInt(String name);
Expand All @@ -85,7 +83,7 @@ default boolean getBoolean(String name, boolean defaultValue) {
*
* @return a {@link Integer} configuration property or {@code defaultValue} if a property with
* {@code name} has not been configured
* @throws ConfigurationException if the property is not a valid scalar integer
* @throws DeclarativeConfigException if the property is not a valid scalar integer
*/
default int getInt(String name, int defaultValue) {
return defaultIfNull(getInt(name), defaultValue);
Expand All @@ -95,7 +93,7 @@ default int getInt(String name, int defaultValue) {
* Returns a {@link Long} configuration property.
*
* @return null if the property has not been configured
* @throws ConfigurationException if the property is not a valid scalar long
* @throws DeclarativeConfigException if the property is not a valid scalar long
*/
@Nullable
Long getLong(String name);
Expand All @@ -105,7 +103,7 @@ default int getInt(String name, int defaultValue) {
*
* @return a {@link Long} configuration property or {@code defaultValue} if a property with {@code
* name} has not been configured
* @throws ConfigurationException if the property is not a valid scalar long
* @throws DeclarativeConfigException if the property is not a valid scalar long
*/
default long getLong(String name, long defaultValue) {
return defaultIfNull(getLong(name), defaultValue);
Expand All @@ -115,7 +113,7 @@ default long getLong(String name, long defaultValue) {
* Returns a {@link Double} configuration property.
*
* @return null if the property has not been configured
* @throws ConfigurationException if the property is not a valid scalar double
* @throws DeclarativeConfigException if the property is not a valid scalar double
*/
@Nullable
Double getDouble(String name);
Expand All @@ -125,7 +123,7 @@ default long getLong(String name, long defaultValue) {
*
* @return a {@link Double} configuration property or {@code defaultValue} if a property with
* {@code name} has not been configured
* @throws ConfigurationException if the property is not a valid scalar double
* @throws DeclarativeConfigException if the property is not a valid scalar double
*/
default double getDouble(String name, double defaultValue) {
return defaultIfNull(getDouble(name), defaultValue);
Expand All @@ -139,8 +137,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 ConfigurationException if the property is not a valid sequence of scalars, or if {@code
* scalarType} is not supported
* @throws DeclarativeConfigException if the property is not a valid sequence of scalars, or if
* {@code scalarType} is not supported
*/
@Nullable
<T> List<T> getScalarList(String name, Class<T> scalarType);
Expand All @@ -149,34 +147,36 @@ default double getDouble(String name, double defaultValue) {
* Returns a {@link List} configuration property. Entries which are not strings are converted to
* their string representation.
*
* @see ConfigProperties#getList(String name)
* @param name the property name
* @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 ConfigurationException if the property is not a valid sequence of scalars
* @throws DeclarativeConfigException if the property is not a valid sequence of scalars
*/
default <T> List<T> getScalarList(String name, Class<T> scalarType, List<T> defaultValue) {
return defaultIfNull(getScalarList(name, scalarType), defaultValue);
}

/**
* Returns a {@link StructuredConfigProperties} configuration property.
* Returns a {@link DeclarativeConfigProperties} configuration property.
*
* @return a map-valued configuration property, or {@code null} if {@code name} has not been
* configured
* @throws ConfigurationException if the property is not a mapping
* @throws DeclarativeConfigException if the property is not a mapping
*/
@Nullable
StructuredConfigProperties getStructured(String name);
DeclarativeConfigProperties getStructured(String name);

/**
* Returns a list of {@link StructuredConfigProperties} configuration property.
* 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 ConfigurationException if the property is not a sequence of mappings
* @throws DeclarativeConfigException if the property is not a sequence of mappings
*/
@Nullable
List<StructuredConfigProperties> getStructuredList(String name);
List<DeclarativeConfigProperties> getStructuredList(String name);

/**
* Returns a set of all configuration property keys.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.api.incubator.config;

import io.opentelemetry.api.GlobalOpenTelemetry;
import java.util.concurrent.atomic.AtomicReference;
import javax.annotation.Nullable;

/**
* This class provides a temporary global accessor for {@link ConfigProvider} until the
* instrumentation config API is marked stable. It will eventually be merged into {@link
* GlobalOpenTelemetry}.
*/
// We intentionally assign to be used for error reporting.
@SuppressWarnings("StaticAssignmentOfThrowable")
public final class GlobalConfigProvider {

private static final AtomicReference<ConfigProvider> instance =
new AtomicReference<>(ConfigProvider.noop());

@SuppressWarnings("NonFinalStaticField")
@Nullable
private static volatile Throwable setInstanceCaller;

private GlobalConfigProvider() {}

/** Returns the globally registered {@link ConfigProvider}. */
// instance cannot be set to null
@SuppressWarnings("NullAway")
public static ConfigProvider get() {
return instance.get();
}

/**
* Sets the global {@link ConfigProvider}. Future calls to {@link #get()} will return the provided
* {@link ConfigProvider} instance. This should be called once as early as possible in your
* application initialization logic.
jack-berg marked this conversation as resolved.
Show resolved Hide resolved
*
* @throws IllegalStateException when called more than once
*/
public static void set(ConfigProvider configProvider) {
boolean changed = instance.compareAndSet(ConfigProvider.noop(), configProvider);
if (!changed && (configProvider != ConfigProvider.noop())) {
throw new IllegalStateException(
"GlobalConfigProvider.set has already been called. GlobalConfigProvider.set "
+ "must be called only once before any calls to GlobalConfigProvider.get. "
+ "Previous invocation set to cause of this exception.",
setInstanceCaller);
}
setInstanceCaller = new Throwable();
}

/**
* Unsets the global {@link ConfigProvider}. This is only meant to be used from tests which need
* to reconfigure {@link ConfigProvider}.
*/
public static void resetForTest() {
jack-berg marked this conversation as resolved.
Show resolved Hide resolved
instance.set(ConfigProvider.noop());
}
}
Loading
Loading