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 @@ -30,16 +30,24 @@ static AttributeListFactory getInstance() {
public Attributes create(List<AttributeNameValueModel> model, DeclarativeConfigContext context) {
AttributesBuilder builder = Attributes.builder();

for (AttributeNameValueModel nameValueModel : model) {
addToBuilder(nameValueModel, builder);
for (int i = 0; i < model.size(); i++) {
AttributeNameValueModel nameValueModel = model.get(i);
context.pushConfigPath("attributes[" + i + "]");
try {
addToBuilder(nameValueModel, builder, context);
} finally {
context.popConfigPath();
}
}

return builder.build();
}

private static void addToBuilder(
AttributeNameValueModel nameValueModel, AttributesBuilder builder) {
String name = FileConfigUtil.requireNonNull(nameValueModel.getName(), "attribute name");
AttributeNameValueModel nameValueModel,
AttributesBuilder builder,
DeclarativeConfigContext context) {
String name = FileConfigUtil.requireNonNull(context, nameValueModel.getName(), "name");
Object value = nameValueModel.getValue();
if (value == null) {
// Per the configuration schema, an entry with a null value is ignored
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,68 +51,86 @@ public ComposableSampler create(

List<ExperimentalComposableRuleBasedSamplerRuleModel> rules = model.getRules();
if (rules != null) {
rules.forEach(
rule -> {
AttributeMatcher valueMatcher = attributeValuesMatcher(rule.getAttributeValues());
AttributeMatcher patternMatcher = attributePatternsMatcher(rule.getAttributePatterns());
Set<ExperimentalSpanParentModel> matchingParents =
rule.getParent() != null && !rule.getParent().isEmpty()
? new HashSet<>(rule.getParent())
: null;
Set<SpanKind> matchingSpanKinds =
rule.getSpanKinds() != null && !rule.getSpanKinds().isEmpty()
? rule.getSpanKinds().stream()
.map(DeclarativeConfigSamplingPredicate::toSpanKind)
.collect(toSet())
: null;

SamplingPredicate predicate =
new DeclarativeConfigSamplingPredicate(
valueMatcher, patternMatcher, matchingParents, matchingSpanKinds);
ComposableSampler sampler =
ComposableSamplerFactory.getInstance()
.create(requireNonNull(rule.getSampler(), "rule sampler"), context);
builder.add(predicate, sampler);
});
for (int i = 0; i < rules.size(); i++) {
ExperimentalComposableRuleBasedSamplerRuleModel rule = rules.get(i);
context.pushConfigPath("rules[" + i + "]");
try {
AttributeMatcher valueMatcher =
attributeValuesMatcher(rule.getAttributeValues(), context);
AttributeMatcher patternMatcher =
attributePatternsMatcher(rule.getAttributePatterns(), context);
Set<ExperimentalSpanParentModel> matchingParents =
rule.getParent() != null && !rule.getParent().isEmpty()
? new HashSet<>(rule.getParent())
: null;
Set<SpanKind> matchingSpanKinds =
rule.getSpanKinds() != null && !rule.getSpanKinds().isEmpty()
? rule.getSpanKinds().stream()
.map(DeclarativeConfigSamplingPredicate::toSpanKind)
.collect(toSet())
: null;

SamplingPredicate predicate =
new DeclarativeConfigSamplingPredicate(
valueMatcher, patternMatcher, matchingParents, matchingSpanKinds);
ComposableSampler sampler =
ComposableSamplerFactory.getInstance()
.create(requireNonNull(context, rule.getSampler(), "sampler"), context);
builder.add(predicate, sampler);
} finally {
context.popConfigPath();
}
}
}

return builder.build();
}

@Nullable
private static AttributeMatcher attributeValuesMatcher(
@Nullable
ExperimentalComposableRuleBasedSamplerRuleAttributeValuesModel attributeValuesModel) {
@Nullable ExperimentalComposableRuleBasedSamplerRuleAttributeValuesModel attributeValuesModel,
DeclarativeConfigContext context) {
if (attributeValuesModel == null) {
return null;
}
List<String> values = attributeValuesModel.getValues();
if (values == null || values.isEmpty()) {
throw new DeclarativeConfigException(".values is required and must be non-empty");
context.pushConfigPath("attribute_values");
try {
List<String> values = attributeValuesModel.getValues();
if (values == null || values.isEmpty()) {
throw new DeclarativeConfigException(".values is required and must be non-empty");
}
return new AttributeMatcher(
requireNonNull(context, attributeValuesModel.getKey(), "key"),
IncludeExcludePredicate.createExactMatching(values, null));
} finally {
context.popConfigPath();
}
return new AttributeMatcher(
requireNonNull(attributeValuesModel.getKey(), "attribute_values key"),
IncludeExcludePredicate.createExactMatching(values, null));
}

@Nullable
private static AttributeMatcher attributePatternsMatcher(
@Nullable
ExperimentalComposableRuleBasedSamplerRuleAttributePatternsModel attributePatternsModel) {
ExperimentalComposableRuleBasedSamplerRuleAttributePatternsModel attributePatternsModel,
DeclarativeConfigContext context) {
if (attributePatternsModel == null) {
return null;
}
List<String> included = attributePatternsModel.getIncluded();
if (included != null && included.isEmpty()) {
throw new DeclarativeConfigException("included must not be empty");
}
List<String> excluded = attributePatternsModel.getExcluded();
if (excluded != null && excluded.isEmpty()) {
throw new DeclarativeConfigException("excluded must not be empty");
context.pushConfigPath("attribute_patterns");
try {
List<String> included = attributePatternsModel.getIncluded();
if (included != null && included.isEmpty()) {
throw new DeclarativeConfigException("included must not be empty");
}
List<String> excluded = attributePatternsModel.getExcluded();
if (excluded != null && excluded.isEmpty()) {
throw new DeclarativeConfigException("excluded must not be empty");
}
return new AttributeMatcher(
requireNonNull(context, attributePatternsModel.getKey(), "key"),
IncludeExcludePredicate.createPatternMatching(included, excluded));
} finally {
context.popConfigPath();
}
return new AttributeMatcher(
requireNonNull(attributePatternsModel.getKey(), "attribute_patterns key"),
IncludeExcludePredicate.createPatternMatching(included, excluded));
}

// Visible for testing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.internal.ExperimentalComposableParentThresholdSamplerModel;
import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.internal.ExperimentalComposableProbabilitySamplerModel;
import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.internal.ExperimentalComposableRuleBasedSamplerModel;
import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.internal.ExperimentalComposableSamplerModel;
import io.opentelemetry.sdk.extension.incubator.trace.samplers.ComposableSampler;

Expand Down Expand Up @@ -38,11 +39,17 @@ public ComposableSampler create(
if (model.getProbability() != null) {
return createProbabilitySampler(model.getProbability());
}
if (model.getRuleBased() != null) {
return ComposableRuleBasedSamplerFactory.getInstance().create(model.getRuleBased(), context);
ExperimentalComposableRuleBasedSamplerModel ruleBasedModel = model.getRuleBased();
if (ruleBasedModel != null) {
return context.withConfigPath(
"rule_based",
() -> ComposableRuleBasedSamplerFactory.getInstance().create(ruleBasedModel, context));
}
if (model.getParentThreshold() != null) {
return createParentThresholdSampler(model.getParentThreshold(), context);
ExperimentalComposableParentThresholdSamplerModel parentThresholdModel =
model.getParentThreshold();
if (parentThresholdModel != null) {
return context.withConfigPath(
"parent_threshold", () -> createParentThresholdSampler(parentThresholdModel, context));
}

return context.loadComponent(ComposableSampler.class, samplerKeyValue);
Expand All @@ -61,8 +68,7 @@ private static ComposableSampler createParentThresholdSampler(
ExperimentalComposableParentThresholdSamplerModel parentThresholdModel,
DeclarativeConfigContext context) {
ExperimentalComposableSamplerModel rootModel =
FileConfigUtil.requireNonNull(
parentThresholdModel.getRoot(), "parent threshold sampler root");
FileConfigUtil.requireNonNull(context, parentThresholdModel.getRoot(), "root");
ComposableSampler rootSampler = INSTANCE.create(rootModel, context);
return ComposableSampler.parentThreshold(rootSampler);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
import io.opentelemetry.sdk.autoconfigure.spi.internal.ExtendedDeclarativeConfigProperties;
import io.opentelemetry.sdk.common.InternalTelemetryVersion;
import java.io.Closeable;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Locale;
Expand All @@ -40,6 +42,7 @@ class DeclarativeConfigContext implements ComponentLoader {
private final Set<AutoConfigureListener> listeners =
Collections.newSetFromMap(new IdentityHashMap<>());
private final List<Closeable> closeables = new ArrayList<>();
private final Deque<String> configPath = new ArrayDeque<>();
@Nullable private volatile MeterProvider meterProvider;
@Nullable private ConfigProvider configProvider;
@Nullable private List<ComponentProvider> componentProviders = null;
Expand Down Expand Up @@ -83,6 +86,42 @@ DeclarativeConfigurationBuilder getBuilder() {
return Objects.requireNonNull(builder, "builder has not been set");
}

/**
* Returns the path of the node in the declarative configuration document which is currently being
* interpreted, for example {@code .tracer_provider.processors[0].batch}. Returns the empty string
* when the root node is being interpreted.
*/
String getConfigPath() {
return configPath.isEmpty() ? "" : "." + String.join(".", configPath);
}

/**
* Interpret {@code supplier} as if the currently interpreted node was the child of the current
* node identified by {@code pathSegment}, restoring the previous {@linkplain #getConfigPath()
* configuration path} afterwards.
*/
<T> T withConfigPath(String pathSegment, Supplier<T> supplier) {
configPath.addLast(pathSegment);
try {
return supplier.get();
} finally {
configPath.removeLast();
}
}

/**
* Descend into the child node identified by {@code pathSegment}. Callers must ensure that {@link
* #popConfigPath()} is called, typically from a {@code finally} block.
*/
void pushConfigPath(String pathSegment) {
configPath.addLast(pathSegment);
}

/** Ascend from the node entered by {@link #pushConfigPath(String)}. */
void popConfigPath() {
configPath.removeLast();
}

/**
* Overload of {@link #setInternalTelemetry(Consumer, Consumer)} for components which do not
* support setting {@link InternalTelemetryVersion} because they only support {@link
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,31 @@ final class FileConfigUtil {

private FileConfigUtil() {}

static <T> T requireNonNull(@Nullable T object, String description) {
/**
* Returns {@code object} if it is non-null, otherwise throw a {@link DeclarativeConfigException}
* describing the missing {@code description}. The description is prefixed with the {@linkplain
* DeclarativeConfigContext#getConfigPath() path} of the node currently being interpreted, so that
* the error points to its location in the configuration document.
*/
static <T> T requireNonNull(
DeclarativeConfigContext context, @Nullable T object, String description) {
if (object == null) {
throw new DeclarativeConfigException(description + " is required but is null");
throw new DeclarativeConfigException(
withConfigPath(context, description + " is required but is null"));
}
return object;
}

/**
* Prefix {@code message} with the {@linkplain DeclarativeConfigContext#getConfigPath() path} of
* the node currently being interpreted, so that errors point to their location in the
* configuration document.
*/
static String withConfigPath(DeclarativeConfigContext context, String message) {
String path = context.getConfigPath();
return path.isEmpty() ? message : path + " " + message;
}

static ConfigKeyValue validateSingleKeyValue(
DeclarativeConfigContext context, Object model, String resourceName) {
DeclarativeConfigProperties modelConfigProperties =
Expand All @@ -34,7 +52,12 @@ static ConfigKeyValue validateSingleKeyValue(
? ""
: ": " + propertyKeys.stream().collect(joining(",", "[", "]"));
throw new DeclarativeConfigException(
resourceName + " must have exactly one entry but has " + propertyKeys.size() + suffix);
withConfigPath(
context,
resourceName
+ " must have exactly one entry but has "
+ propertyKeys.size()
+ suffix));
}
String key = propertyKeys.iterator().next();
DeclarativeConfigProperties value = modelConfigProperties.getStructured(key);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,14 @@ public LogRecordProcessor create(
ConfigKeyValue processorKeyValue =
FileConfigUtil.validateSingleKeyValue(context, model, "log record processor");

if (model.getBatch() != null) {
return createBatchLogRecordProcessor(model.getBatch(), context);
BatchLogRecordProcessorModel batch = model.getBatch();
if (batch != null) {
return context.withConfigPath("batch", () -> createBatchLogRecordProcessor(batch, context));
}
if (model.getSimple() != null) {
return createSimpleLogRecordProcessor(model.getSimple(), context);
SimpleLogRecordProcessorModel simple = model.getSimple();
if (simple != null) {
return context.withConfigPath(
"simple", () -> createSimpleLogRecordProcessor(simple, context));
}
if (LogRecordProcessorModelAccessor.getEventToSpanEventBridge(model) != null) {
return EventToSpanEventBridge.create();
Expand All @@ -54,8 +57,7 @@ public LogRecordProcessor create(
private static LogRecordProcessor createBatchLogRecordProcessor(
BatchLogRecordProcessorModel batchModel, DeclarativeConfigContext context) {
LogRecordExporterModel exporterModel =
FileConfigUtil.requireNonNull(
batchModel.getExporter(), "batch log record processor exporter");
FileConfigUtil.requireNonNull(context, batchModel.getExporter(), "exporter");

LogRecordExporter logRecordExporter =
LogRecordExporterFactory.getInstance().create(exporterModel, context);
Expand All @@ -80,8 +82,7 @@ private static LogRecordProcessor createBatchLogRecordProcessor(
private static LogRecordProcessor createSimpleLogRecordProcessor(
SimpleLogRecordProcessorModel simpleModel, DeclarativeConfigContext context) {
LogRecordExporterModel exporterModel =
FileConfigUtil.requireNonNull(
simpleModel.getExporter(), "simple log record processor exporter");
FileConfigUtil.requireNonNull(context, simpleModel.getExporter(), "exporter");
LogRecordExporter logRecordExporter =
LogRecordExporterFactory.getInstance().create(exporterModel, context);
SimpleLogRecordProcessorBuilder builder = SimpleLogRecordProcessor.builder(logRecordExporter);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,13 @@ public SdkLoggerProviderBuilder create(

List<LogRecordProcessorModel> processors = loggerProviderModel.getProcessors();
if (processors != null) {
processors.forEach(
processor ->
builder.addLogRecordProcessor(
LogRecordProcessorFactory.getInstance().create(processor, context)));
for (int i = 0; i < processors.size(); i++) {
LogRecordProcessorModel processor = processors.get(i);
builder.addLogRecordProcessor(
context.withConfigPath(
"processors[" + i + "]",
() -> LogRecordProcessorFactory.getInstance().create(processor, context)));
}
}

ExperimentalLoggerConfiguratorModel loggerConfiguratorModel =
Expand All @@ -76,9 +79,13 @@ public SdkLoggerProviderBuilder create(
List<ExperimentalLoggerMatcherAndConfigModel> loggerMatcherAndConfigs =
loggerConfiguratorModel.getLoggers();
if (loggerMatcherAndConfigs != null) {
for (ExperimentalLoggerMatcherAndConfigModel loggerMatcherAndConfig :
loggerMatcherAndConfigs) {
String name = requireNonNull(loggerMatcherAndConfig.getName(), "logger matcher name");
for (int i = 0; i < loggerMatcherAndConfigs.size(); i++) {
ExperimentalLoggerMatcherAndConfigModel loggerMatcherAndConfig =
loggerMatcherAndConfigs.get(i);
String name =
context.withConfigPath(
"logger_configurator/development.loggers[" + i + "]",
() -> requireNonNull(context, loggerMatcherAndConfig.getName(), "name"));
ExperimentalLoggerConfigModel config = loggerMatcherAndConfig.getConfig();
if (name == null || config == null) {
continue;
Expand Down
Loading
Loading