Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@
import io.opentelemetry.common.ComponentLoader;
import io.opentelemetry.sdk.autoconfigure.internal.SpiHelper;
import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider;
import io.opentelemetry.sdk.logs.export.LogRecordExporter;
import io.opentelemetry.sdk.metrics.export.MetricExporter;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import java.io.Closeable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import javax.annotation.Nullable;

Expand All @@ -28,6 +32,13 @@ class DeclarativeConfigContext {
@Nullable private Resource resource = null;
@Nullable private List<ComponentProvider> componentProviders = null;

private BiFunction<String, SpanExporter, SpanExporter> spanExporterCustomizer =
(name, exporter) -> exporter;
private BiFunction<String, MetricExporter, MetricExporter> metricExporterCustomizer =
(name, exporter) -> exporter;
private BiFunction<String, LogRecordExporter, LogRecordExporter> logRecordExporterCustomizer =
(name, exporter) -> exporter;

// Visible for testing
DeclarativeConfigContext(SpiHelper spiHelper) {
this.spiHelper = spiHelper;
Expand Down Expand Up @@ -71,6 +82,31 @@ void setResource(Resource resource) {
this.resource = resource;
}

void setSpanExporterCustomizer(BiFunction<String, SpanExporter, SpanExporter> customizer) {
this.spanExporterCustomizer = customizer;
}

BiFunction<String, SpanExporter, SpanExporter> getSpanExporterCustomizer() {
return spanExporterCustomizer;
}

void setMetricExporterCustomizer(BiFunction<String, MetricExporter, MetricExporter> customizer) {
this.metricExporterCustomizer = customizer;
}

BiFunction<String, MetricExporter, MetricExporter> getMetricExporterCustomizer() {
return metricExporterCustomizer;
}

void setLogRecordExporterCustomizer(
BiFunction<String, LogRecordExporter, LogRecordExporter> customizer) {
this.logRecordExporterCustomizer = customizer;
}

BiFunction<String, LogRecordExporter, LogRecordExporter> getLogRecordExporterCustomizer() {
return logRecordExporterCustomizer;
}

SpiHelper getSpiHelper() {
return spiHelper;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ private static ExtendedOpenTelemetrySdk create(
provider.customize(builder);
}

// Pass exporter customizers to context
context.setSpanExporterCustomizer(builder.getSpanExporterCustomizer());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can just pass the builder to DeclarativeConfigContext to avoid the noise of extra getters / setter for each customizer?

context.setMetricExporterCustomizer(builder.getMetricExporterCustomizer());
context.setLogRecordExporterCustomizer(builder.getLogRecordExporterCustomizer());

ExtendedOpenTelemetrySdk sdk =
createAndMaybeCleanup(
OpenTelemetryConfigurationFactory.getInstance(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,48 @@
package io.opentelemetry.sdk.extension.incubator.fileconfig;

import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel;
import io.opentelemetry.sdk.logs.export.LogRecordExporter;
import io.opentelemetry.sdk.metrics.export.MetricExporter;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import java.util.function.BiFunction;
import java.util.function.Function;

/** Builder for the declarative configuration. */
public class DeclarativeConfigurationBuilder implements DeclarativeConfigurationCustomizer {
private Function<OpenTelemetryConfigurationModel, OpenTelemetryConfigurationModel>
modelCustomizer = Function.identity();

private BiFunction<String, SpanExporter, SpanExporter> spanExporterCustomizer =
(name, exporter) -> exporter;
private BiFunction<String, MetricExporter, MetricExporter> metricExporterCustomizer =
(name, exporter) -> exporter;
private BiFunction<String, LogRecordExporter, LogRecordExporter> logRecordExporterCustomizer =
(name, exporter) -> exporter;

@Override
public void addModelCustomizer(
Function<OpenTelemetryConfigurationModel, OpenTelemetryConfigurationModel> customizer) {
modelCustomizer = mergeCustomizer(modelCustomizer, customizer);
}

@Override
public void addSpanExporterCustomizer(BiFunction<String, SpanExporter, SpanExporter> customizer) {
spanExporterCustomizer = mergeBiFunctionCustomizer(spanExporterCustomizer, customizer);
}

@Override
public void addMetricExporterCustomizer(
BiFunction<String, MetricExporter, MetricExporter> customizer) {
metricExporterCustomizer = mergeBiFunctionCustomizer(metricExporterCustomizer, customizer);
}

@Override
public void addLogRecordExporterCustomizer(
BiFunction<String, LogRecordExporter, LogRecordExporter> customizer) {
logRecordExporterCustomizer =
mergeBiFunctionCustomizer(logRecordExporterCustomizer, customizer);
}

private static <I, O1, O2> Function<I, O2> mergeCustomizer(
Function<? super I, ? extends O1> first, Function<? super O1, ? extends O2> second) {
return (I configured) -> {
Expand All @@ -27,9 +56,29 @@ private static <I, O1, O2> Function<I, O2> mergeCustomizer(
};
}

private static <K, V> BiFunction<K, V, V> mergeBiFunctionCustomizer(
BiFunction<K, V, V> first, BiFunction<K, V, V> second) {
return (K key, V value) -> {
V firstResult = first.apply(key, value);
return second.apply(key, firstResult);
};
}

/** Customize the configuration model. */
public OpenTelemetryConfigurationModel customizeModel(
OpenTelemetryConfigurationModel configurationModel) {
return modelCustomizer.apply(configurationModel);
}

BiFunction<String, SpanExporter, SpanExporter> getSpanExporterCustomizer() {
return spanExporterCustomizer;
}

BiFunction<String, MetricExporter, MetricExporter> getMetricExporterCustomizer() {
return metricExporterCustomizer;
}

BiFunction<String, LogRecordExporter, LogRecordExporter> getLogRecordExporterCustomizer() {
return logRecordExporterCustomizer;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
package io.opentelemetry.sdk.extension.incubator.fileconfig;

import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.OpenTelemetryConfigurationModel;
import io.opentelemetry.sdk.logs.export.LogRecordExporter;
import io.opentelemetry.sdk.metrics.export.MetricExporter;
import io.opentelemetry.sdk.trace.export.SpanExporter;
import java.util.function.BiFunction;
import java.util.function.Function;

/** A service provider interface (SPI) for customizing declarative configuration. */
Expand All @@ -18,4 +22,32 @@ public interface DeclarativeConfigurationCustomizer {
*/
void addModelCustomizer(
Function<OpenTelemetryConfigurationModel, OpenTelemetryConfigurationModel> customizer);

/**
* Add customizer for {@link SpanExporter} instances created from declarative configuration.
* Multiple customizers compose in registration order.
*
* @param customizer function receiving (exporterName, exporter) and returning customized exporter
*/
void addSpanExporterCustomizer(BiFunction<String, SpanExporter, SpanExporter> customizer);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This contract is different than the env var / system prop equivalent:

AutoConfigurationCustomizer addSpanExporterCustomizer(BiFunction<SpanExporter, ConfigProperties, SpanExporter>)

Comments:

  • There are mechanisms for a customizer to identify the exporter and decide whether to participate: the exporterName and the class type of the exporter. We don't technically need both. What's the advantage (if any) of the exporter name in addition to the type?
  • The customizer doesn't have access to the DeclarativeConfigProperties for the exporter. We need to add this.
  • One annoying part about this and the existing AutoConfigurationCustomizer pattern is that the customizer implementation has to filter all span exporter and only operate on the instances they care about. I wonder if we could improve on this by decomposing it into a predicate and a customizer: addSpanExporterCustomizer(Predicate<SpanExporter>, BiFunction<SpanExporter, DeclarativeConfigProperties, SpanExporter>, or even something like: <T extends SpanExporter> addSpanExporterCustomizer(Class<T>, BiFunction<T, DeclarativeConfigProperties, T>) to remove the need to do this filtering and type casting entirely.


/**
* Add customizer for {@link MetricExporter} instances created from declarative configuration.
* Multiple customizers compose in registration order.
*
* @param customizer function receiving (exporterName, exporter) and returning customized exporter
*/
void addMetricExporterCustomizer(BiFunction<String, MetricExporter, MetricExporter> customizer);

/**
* Add customizer for {@link LogRecordExporter} instances created from declarative configuration.
* Multiple customizers compose in registration order.
*
* <p>Important: Customizers must not return null. If the customizer wraps the exporter in a new
* {@link java.io.Closeable} instance, the customizer is responsible for resource cleanup.
*
* @param customizer function receiving (exporterName, exporter) and returning customized exporter
*/
void addLogRecordExporterCustomizer(
BiFunction<String, LogRecordExporter, LogRecordExporter> customizer);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

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

import io.opentelemetry.api.incubator.config.DeclarativeConfigException;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.LogRecordExporterModel;
import io.opentelemetry.sdk.logs.export.LogRecordExporter;

Expand All @@ -21,6 +22,18 @@ static LogRecordExporterFactory getInstance() {
public LogRecordExporter create(LogRecordExporterModel model, DeclarativeConfigContext context) {
ConfigKeyValue logRecordExporterKeyValue =
FileConfigUtil.validateSingleKeyValue(context, model, "log record exporter");
return context.loadComponent(LogRecordExporter.class, logRecordExporterKeyValue);

String exporterName = logRecordExporterKeyValue.getKey();
LogRecordExporter exporter =
context.loadComponent(LogRecordExporter.class, logRecordExporterKeyValue);

// Apply customizer
LogRecordExporter customized =
context.getLogRecordExporterCustomizer().apply(exporterName, exporter);
if (customized == null) {
throw new DeclarativeConfigException(
"Log record exporter customizer returned null for exporter: " + exporterName);
}
return customized;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

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

import io.opentelemetry.api.incubator.config.DeclarativeConfigException;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.PushMetricExporterModel;
import io.opentelemetry.sdk.metrics.export.MetricExporter;

Expand All @@ -21,6 +22,16 @@ static MetricExporterFactory getInstance() {
public MetricExporter create(PushMetricExporterModel model, DeclarativeConfigContext context) {
ConfigKeyValue metricExporterKeyValue =
FileConfigUtil.validateSingleKeyValue(context, model, "metric exporter");
return context.loadComponent(MetricExporter.class, metricExporterKeyValue);

String exporterName = metricExporterKeyValue.getKey();
MetricExporter exporter = context.loadComponent(MetricExporter.class, metricExporterKeyValue);

// Apply customizer
MetricExporter customized = context.getMetricExporterCustomizer().apply(exporterName, exporter);
if (customized == null) {
throw new DeclarativeConfigException(
"Metric exporter customizer returned null for exporter: " + exporterName);
}
return customized;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

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

import io.opentelemetry.api.incubator.config.DeclarativeConfigException;
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SpanExporterModel;
import io.opentelemetry.sdk.trace.export.SpanExporter;

Expand All @@ -22,6 +23,16 @@ static SpanExporterFactory getInstance() {
public SpanExporter create(SpanExporterModel model, DeclarativeConfigContext context) {
ConfigKeyValue spanExporterKeyValue =
FileConfigUtil.validateSingleKeyValue(context, model, "span exporter");
return context.loadComponent(SpanExporter.class, spanExporterKeyValue);

String exporterName = spanExporterKeyValue.getKey();
SpanExporter exporter = context.loadComponent(SpanExporter.class, spanExporterKeyValue);

// Apply customizer
SpanExporter customized = context.getSpanExporterCustomizer().apply(exporterName, exporter);
if (customized == null) {
throw new DeclarativeConfigException(
"Span exporter customizer returned null for exporter: " + exporterName);
}
return customized;
}
}
Loading
Loading