Skip to content

Commit

Permalink
Initialize file configuration model interpreting
Browse files Browse the repository at this point in the history
  • Loading branch information
jack-berg committed Aug 7, 2023
1 parent 72c1780 commit 448c7d3
Show file tree
Hide file tree
Showing 19 changed files with 1,414 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.sdk.autoconfigure.internal;

import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.Ordered;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;

/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
public final class ServiceLoaderHelper {

private final ClassLoader classLoader;
private final ServiceLoaderFinder serviceLoaderFinder;

// Visible for testing
ServiceLoaderHelper(ClassLoader classLoader, ServiceLoaderFinder serviceLoaderFinder) {
this.classLoader = classLoader;
this.serviceLoaderFinder = serviceLoaderFinder;
}

Check warning on line 33 in sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/ServiceLoaderHelper.java

View check run for this annotation

Codecov / codecov/patch

sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/ServiceLoaderHelper.java#L30-L33

Added lines #L30 - L33 were not covered by tests

/** Create a {@link ServiceLoaderHelper} which loads SPIs using the {@code classLoader}. */
public static ServiceLoaderHelper create(ClassLoader classLoader) {
return new ServiceLoaderHelper(classLoader, ServiceLoader::load);

Check warning on line 37 in sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/ServiceLoaderHelper.java

View check run for this annotation

Codecov / codecov/patch

sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/ServiceLoaderHelper.java#L37

Added line #L37 was not covered by tests
}

/**
* Load implementations of an SPI which are configurable (i.e. they accept {@link
* ConfigProperties}.
*
* @param spiClass the SPI class
* @param getName function returning the name of an SPI implementation
* @param getConfigurable function returning a configured instance
* @param config the configuration to pass to invocations of {@code #getConfigurable}
* @param <T> the configurable type
* @param <S> the SPI type
* @return a {@link NamedSpiManager} used to access configured instances of the SPI by name
*/
public <T, S> NamedSpiManager<T> loadConfigurable(
Class<S> spiClass,
Function<S, String> getName,
BiFunction<S, ConfigProperties, T> getConfigurable,
ConfigProperties config) {
Map<String, Supplier<T>> nameToProvider = new HashMap<>();

Check warning on line 57 in sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/ServiceLoaderHelper.java

View check run for this annotation

Codecov / codecov/patch

sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/ServiceLoaderHelper.java#L57

Added line #L57 was not covered by tests
for (S provider : load(spiClass)) {
String name = getName.apply(provider);
nameToProvider.put(name, () -> getConfigurable.apply(provider, config));
}
return NamedSpiManager.create(nameToProvider);

Check warning on line 62 in sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/ServiceLoaderHelper.java

View check run for this annotation

Codecov / codecov/patch

sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/ServiceLoaderHelper.java#L59-L62

Added lines #L59 - L62 were not covered by tests
}

/**
* Load implementations of an ordered SPI (i.e. implements {@link Ordered}).
*
* @param spiClass the SPI class
* @param <T> the SPI type
* @return list of SPI implementations, in order
*/
public <T extends Ordered> List<T> loadOrdered(Class<T> spiClass) {
List<T> result = load(spiClass);
result.sort(Comparator.comparing(Ordered::order));
return result;

Check warning on line 75 in sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/ServiceLoaderHelper.java

View check run for this annotation

Codecov / codecov/patch

sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/ServiceLoaderHelper.java#L73-L75

Added lines #L73 - L75 were not covered by tests
}

/**
* Load implementations of an SPI.
*
* @param spiClass the SPI class
* @param <T> the SPI type
* @return list of SPI implementations
*/
public <T> List<T> load(Class<T> spiClass) {
List<T> result = new ArrayList<>();

Check warning on line 86 in sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/ServiceLoaderHelper.java

View check run for this annotation

Codecov / codecov/patch

sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/ServiceLoaderHelper.java#L86

Added line #L86 was not covered by tests
for (T service : serviceLoaderFinder.load(spiClass, classLoader)) {
result.add(service);
}
return result;

Check warning on line 90 in sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/ServiceLoaderHelper.java

View check run for this annotation

Codecov / codecov/patch

sdk-extensions/autoconfigure/src/main/java/io/opentelemetry/sdk/autoconfigure/internal/ServiceLoaderHelper.java#L88-L90

Added lines #L88 - L90 were not covered by tests
}

// Visible for testing
interface ServiceLoaderFinder {
<T> Iterable<T> load(Class<T> spiClass, ClassLoader classLoader);
}
}
15 changes: 13 additions & 2 deletions sdk-extensions/incubator/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,12 @@ dependencies {
implementation("com.fasterxml.jackson.core:jackson-databind")
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-yaml")
implementation("org.yaml:snakeyaml:2.1")
implementation(project(":sdk-extensions:autoconfigure"))

testImplementation(project(":sdk:testing"))
testImplementation(project(":sdk-extensions:autoconfigure"))
testImplementation(project(":exporters:otlp:all"))
testImplementation("com.linecorp.armeria:armeria-junit5")

testImplementation("com.google.guava:guava-testlib")
}
Expand Down Expand Up @@ -75,6 +78,10 @@ jsonSchema2Pojo {
// Clear old source files to avoid contaminated source dir when updating
removeOldOutput = true

// Include @Nullable annotation. Note: jsonSchmea2Pojo will not add @Nullable annotations on getters
// so we perform some steps in jsonSchema2PojoPostProcessing to add these.
includeJsr305Annotations = true

// Prefer builders to setters
includeSetters = false
generateBuilders = true
Expand All @@ -97,10 +104,14 @@ val jsonSchema2PojoPostProcessing by tasks.registering(Copy::class) {
into("$buildDir/generated/sources/js2p-tmp")
filter {
it
// Replace java 9+ @Generated annotation with java 8 version
.replace("import javax.annotation.processing.Generated", "import javax.annotation.Generated")
// Remove @Nullable annotation so it can be deterministically added later
.replace("import javax.annotation.Nullable;\n", "")
// Replace java 9+ @Generated annotation with java 8 version, add @Nullable annotation
.replace("import javax.annotation.processing.Generated;", "import javax.annotation.Nullable;\nimport javax.annotation.Generated;")
// Add @SuppressWarnings("rawtypes") annotation to address raw types used in jsonschema2pojo builders
.replace("@Generated(\"jsonschema2pojo\")", "@Generated(\"jsonschema2pojo\")\n@SuppressWarnings(\"rawtypes\")")
// Add @Nullable annotations to all getters
.replace("( *)public ([a-zA-Z]*) get([a-zA-Z]*)".toRegex(), "$1@Nullable\n$1public $2 get$3")
}
}
val overwriteJs2p by tasks.registering(Copy::class) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.sdk.extension.incubator.fileconfig;

import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.internal.ServiceLoaderHelper;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfiguration;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

/**
* Parses YAML configuration files conforming to the schema in <a
* href="https://github.com/open-telemetry/opentelemetry-configuration">open-telemetry/opentelemetry-configuration</a>
* to a {@link OpenTelemetryConfiguration} in-memory representation. Interprets the in-memory
* representation to produce an {@link OpenTelemetrySdk}.
*
* @see #parseAndInterpret(InputStream)
*/
public final class ConfigurationFactory {

private static final Logger logger = Logger.getLogger(ConfigurationFactory.class.getName());

private ConfigurationFactory() {}

/**
* Parse the {@code inputStream} YAML to {@link OpenTelemetryConfiguration} and interpret the
* model to create {@link OpenTelemetrySdk} instance corresponding to the configuration.
*
* @param inputStream the configuration YAML
* @return the {@link OpenTelemetrySdk}
*/
public static OpenTelemetrySdk parseAndInterpret(InputStream inputStream) {
OpenTelemetryConfiguration model;
try {
model = ConfigurationReader.parse(inputStream);
} catch (RuntimeException e) {
throw new ConfigurationException("Unable to parse inputStream", e);
}

List<Closeable> closeables = new ArrayList<>();
try {
return OpenTelemetryConfigurationFactory.getInstance()
.create(
model,
ServiceLoaderHelper.create(ConfigurationFactory.class.getClassLoader()),
closeables);
} catch (RuntimeException e) {
logger.info(
"Error encountered interpreting configuration. Closing partially configured components.");
for (Closeable closeable : closeables) {
try {
logger.fine("Closing " + closeable.getClass().getName());
closeable.close();
} catch (IOException ex) {
logger.warning(
"Error closing " + closeable.getClass().getName() + ": " + ex.getMessage());
}
}
if (e instanceof ConfigurationException) {
throw e;
}
throw new ConfigurationException("Unexpected configuration error", e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import java.io.InputStream;
import org.yaml.snakeyaml.Yaml;

class ConfigurationReader {
final class ConfigurationReader {

private static final ObjectMapper MAPPER = new ObjectMapper();
private static final Yaml YAML = new Yaml();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.sdk.extension.incubator.fileconfig;

import io.opentelemetry.sdk.autoconfigure.internal.ServiceLoaderHelper;
import java.io.Closeable;
import java.util.List;
import javax.annotation.Nullable;

interface Factory<ModelT, ResultT> {

/**
* Interpret the model and create {@link ResultT} with corresponding configuration.
*
* @param model the configuration model
* @param serviceLoaderHelper the service loader helper
* @param closeables mutable list of closeables created
* @return the {@link ResultT}
*/
ResultT create(
@Nullable ModelT model, ServiceLoaderHelper serviceLoaderHelper, List<Closeable> closeables);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.sdk.extension.incubator.fileconfig;

import java.io.Closeable;
import java.util.List;
import javax.annotation.Nullable;

final class FileConfigUtil {

private FileConfigUtil() {}

/** Add the {@code closeable} to the {@code closeables} and return it. */
static <T extends Closeable> T addAndReturn(List<Closeable> closeables, T closeable) {
closeables.add(closeable);
return closeable;
}

static <T> T assertNotNull(@Nullable T object, String description) {
if (object == null) {
throw new NullPointerException(description + " is null");
}
return object;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.sdk.extension.incubator.fileconfig;

import io.opentelemetry.sdk.autoconfigure.internal.ServiceLoaderHelper;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.LogRecordLimits;
import io.opentelemetry.sdk.logs.LogLimits;
import io.opentelemetry.sdk.logs.LogLimitsBuilder;
import java.io.Closeable;
import java.util.List;
import javax.annotation.Nullable;

final class LogLimitsFactory implements Factory<LogRecordLimits, LogLimits> {

private static final LogLimitsFactory INSTANCE = new LogLimitsFactory();

private LogLimitsFactory() {}

static LogLimitsFactory getInstance() {
return INSTANCE;
}

@Override
public LogLimits create(
@Nullable LogRecordLimits model,
ServiceLoaderHelper serviceLoaderHelper,
List<Closeable> closeables) {
if (model == null) {
return LogLimits.getDefault();
}

LogLimitsBuilder builder = LogLimits.builder();
if (model.getAttributeCountLimit() != null) {
builder.setMaxNumberOfAttributes(model.getAttributeCountLimit());
}
if (model.getAttributeValueLengthLimit() != null) {
builder.setMaxAttributeValueLength(model.getAttributeValueLengthLimit());
}
return builder.build();
}
}
Loading

0 comments on commit 448c7d3

Please sign in to comment.