From d498416109911fc272aff1090cdc6f6f09af0606 Mon Sep 17 00:00:00 2001 From: AstonKrss <3223957215@qq.com> Date: Fri, 16 Oct 2026 16:27:08 +0800 Subject: [PATCH] Include config document path in declarative config error messages (#7949) Errors thrown while interpreting declarative configuration now include the path to the offending node in the configuration document, e.g. ".tracer_provider.processors[0].batch exporter is required but is null". DeclarativeConfigContext tracks the path of the node currently being interpreted. Factories push/pop path segments as they descend into children, and FileConfigUtil.requireNonNull / validateSingleKeyValue prefix messages with the current path. --- .../AttributeListFactory.java | 16 ++- .../ComposableRuleBasedSamplerFactory.java | 102 ++++++++++-------- .../ComposableSamplerFactory.java | 18 ++-- .../DeclarativeConfigContext.java | 39 +++++++ .../declarativeconfig/FileConfigUtil.java | 29 ++++- .../LogRecordProcessorFactory.java | 17 +-- .../LoggerProviderFactory.java | 21 ++-- .../MeterProviderFactory.java | 59 +++++----- .../MetricReaderFactory.java | 19 ++-- .../OpenTelemetryConfigurationFactory.java | 81 +++++++++----- .../declarativeconfig/SamplerFactory.java | 13 ++- .../SpanProcessorFactory.java | 15 +-- .../TracerProviderFactory.java | 36 +++++-- .../DeclarativeConfigurationCreateTest.java | 18 ++++ .../LogRecordProcessorFactoryTest.java | 10 +- .../MetricReaderFactoryTest.java | 12 ++- .../declarativeconfig/SamplerFactoryTest.java | 10 +- .../SpanProcessorFactoryTest.java | 10 +- 18 files changed, 358 insertions(+), 167 deletions(-) diff --git a/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/AttributeListFactory.java b/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/AttributeListFactory.java index 8d4313b36d3..1fb77359e92 100644 --- a/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/AttributeListFactory.java +++ b/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/AttributeListFactory.java @@ -30,16 +30,24 @@ static AttributeListFactory getInstance() { public Attributes create(List 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 diff --git a/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/ComposableRuleBasedSamplerFactory.java b/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/ComposableRuleBasedSamplerFactory.java index 9b4f9b9a63c..314f618b077 100644 --- a/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/ComposableRuleBasedSamplerFactory.java +++ b/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/ComposableRuleBasedSamplerFactory.java @@ -51,29 +51,36 @@ public ComposableSampler create( List rules = model.getRules(); if (rules != null) { - rules.forEach( - rule -> { - AttributeMatcher valueMatcher = attributeValuesMatcher(rule.getAttributeValues()); - AttributeMatcher patternMatcher = attributePatternsMatcher(rule.getAttributePatterns()); - Set matchingParents = - rule.getParent() != null && !rule.getParent().isEmpty() - ? new HashSet<>(rule.getParent()) - : null; - Set 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 matchingParents = + rule.getParent() != null && !rule.getParent().isEmpty() + ? new HashSet<>(rule.getParent()) + : null; + Set 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(); @@ -81,38 +88,49 @@ public ComposableSampler create( @Nullable private static AttributeMatcher attributeValuesMatcher( - @Nullable - ExperimentalComposableRuleBasedSamplerRuleAttributeValuesModel attributeValuesModel) { + @Nullable ExperimentalComposableRuleBasedSamplerRuleAttributeValuesModel attributeValuesModel, + DeclarativeConfigContext context) { if (attributeValuesModel == null) { return null; } - List 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 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 included = attributePatternsModel.getIncluded(); - if (included != null && included.isEmpty()) { - throw new DeclarativeConfigException("included must not be empty"); - } - List excluded = attributePatternsModel.getExcluded(); - if (excluded != null && excluded.isEmpty()) { - throw new DeclarativeConfigException("excluded must not be empty"); + context.pushConfigPath("attribute_patterns"); + try { + List included = attributePatternsModel.getIncluded(); + if (included != null && included.isEmpty()) { + throw new DeclarativeConfigException("included must not be empty"); + } + List 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 diff --git a/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/ComposableSamplerFactory.java b/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/ComposableSamplerFactory.java index 0ce055c0ddd..34a15a091c5 100644 --- a/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/ComposableSamplerFactory.java +++ b/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/ComposableSamplerFactory.java @@ -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; @@ -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); @@ -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); } diff --git a/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/DeclarativeConfigContext.java b/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/DeclarativeConfigContext.java index 5508d87f577..85c9bbbd533 100644 --- a/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/DeclarativeConfigContext.java +++ b/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/DeclarativeConfigContext.java @@ -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; @@ -40,6 +42,7 @@ class DeclarativeConfigContext implements ComponentLoader { private final Set listeners = Collections.newSetFromMap(new IdentityHashMap<>()); private final List closeables = new ArrayList<>(); + private final Deque configPath = new ArrayDeque<>(); @Nullable private volatile MeterProvider meterProvider; @Nullable private ConfigProvider configProvider; @Nullable private List componentProviders = null; @@ -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 withConfigPath(String pathSegment, Supplier 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 diff --git a/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/FileConfigUtil.java b/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/FileConfigUtil.java index b5a8fe572d5..c6990f6b5c7 100644 --- a/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/FileConfigUtil.java +++ b/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/FileConfigUtil.java @@ -16,13 +16,31 @@ final class FileConfigUtil { private FileConfigUtil() {} - static 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 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 = @@ -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); diff --git a/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/LogRecordProcessorFactory.java b/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/LogRecordProcessorFactory.java index 81cfde81235..d1f20612cd9 100644 --- a/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/LogRecordProcessorFactory.java +++ b/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/LogRecordProcessorFactory.java @@ -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(); @@ -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); @@ -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); diff --git a/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/LoggerProviderFactory.java b/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/LoggerProviderFactory.java index 4cceee77991..28ab794e88c 100644 --- a/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/LoggerProviderFactory.java +++ b/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/LoggerProviderFactory.java @@ -58,10 +58,13 @@ public SdkLoggerProviderBuilder create( List 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 = @@ -76,9 +79,13 @@ public SdkLoggerProviderBuilder create( List 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; diff --git a/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/MeterProviderFactory.java b/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/MeterProviderFactory.java index 4eb8a061b01..851f7cc1084 100644 --- a/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/MeterProviderFactory.java +++ b/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/MeterProviderFactory.java @@ -43,31 +43,38 @@ public SdkMeterProviderBuilder create( List readerModels = model.getReaders(); if (readerModels != null) { - readerModels.forEach( - readerModel -> { - MetricReaderAndCardinalityLimits readerAndCardinalityLimits = - MetricReaderFactory.getInstance().create(readerModel, context); - CardinalityLimitSelector cardinalityLimits = - readerAndCardinalityLimits.getCardinalityLimitsSelector(); - if (cardinalityLimits == null) { - builder.registerMetricReader(readerAndCardinalityLimits.getMetricReader()); - } else { - builder.registerMetricReader( - readerAndCardinalityLimits.getMetricReader(), cardinalityLimits); - } - }); + for (int i = 0; i < readerModels.size(); i++) { + MetricReaderModel readerModel = readerModels.get(i); + MetricReaderAndCardinalityLimits readerAndCardinalityLimits = + context.withConfigPath( + "readers[" + i + "]", + () -> MetricReaderFactory.getInstance().create(readerModel, context)); + CardinalityLimitSelector cardinalityLimits = + readerAndCardinalityLimits.getCardinalityLimitsSelector(); + if (cardinalityLimits == null) { + builder.registerMetricReader(readerAndCardinalityLimits.getMetricReader()); + } else { + builder.registerMetricReader( + readerAndCardinalityLimits.getMetricReader(), cardinalityLimits); + } + } } List viewModels = model.getViews(); if (viewModels != null) { - viewModels.forEach( - viewModel -> { - ViewSelectorModel selector = requireNonNull(viewModel.getSelector(), "view selector"); - ViewStreamModel stream = requireNonNull(viewModel.getStream(), "view stream"); - builder.registerView( - InstrumentSelectorFactory.getInstance().create(selector, context), - ViewFactory.getInstance().create(stream, context)); - }); + for (int i = 0; i < viewModels.size(); i++) { + ViewModel viewModel = viewModels.get(i); + context.pushConfigPath("views[" + i + "]"); + try { + ViewSelectorModel selector = requireNonNull(context, viewModel.getSelector(), "selector"); + ViewStreamModel stream = requireNonNull(context, viewModel.getStream(), "stream"); + builder.registerView( + InstrumentSelectorFactory.getInstance().create(selector, context), + ViewFactory.getInstance().create(stream, context)); + } finally { + context.popConfigPath(); + } + } } ExperimentalMeterConfiguratorModel meterConfiguratorModel = @@ -82,9 +89,13 @@ public SdkMeterProviderBuilder create( List meterMatcherAndConfigs = meterConfiguratorModel.getMeters(); if (meterMatcherAndConfigs != null) { - for (ExperimentalMeterMatcherAndConfigModel meterMatcherAndConfig : - meterMatcherAndConfigs) { - String name = requireNonNull(meterMatcherAndConfig.getName(), "meter matcher name"); + for (int i = 0; i < meterMatcherAndConfigs.size(); i++) { + ExperimentalMeterMatcherAndConfigModel meterMatcherAndConfig = + meterMatcherAndConfigs.get(i); + String name = + context.withConfigPath( + "meter_configurator/development.meters[" + i + "]", + () -> requireNonNull(context, meterMatcherAndConfig.getName(), "name")); ExperimentalMeterConfigModel config = meterMatcherAndConfig.getConfig(); if (name == null || config == null) { continue; diff --git a/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/MetricReaderFactory.java b/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/MetricReaderFactory.java index 687b64131b0..c63604bfd51 100644 --- a/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/MetricReaderFactory.java +++ b/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/MetricReaderFactory.java @@ -37,14 +37,19 @@ public MetricReaderAndCardinalityLimits create( MetricReaderModel model, DeclarativeConfigContext context) { FileConfigUtil.validateSingleKeyValue(context, model, "metric reader"); - if (model.getPeriodic() != null) { - return PeriodicMetricReaderFactory.INSTANCE.create(model.getPeriodic(), context); + PeriodicMetricReaderModel periodic = model.getPeriodic(); + if (periodic != null) { + return context.withConfigPath( + "periodic", () -> PeriodicMetricReaderFactory.INSTANCE.create(periodic, context)); } - if (model.getPull() != null) { - return PullMetricReaderFactory.INSTANCE.create(model.getPull(), context); + PullMetricReaderModel pull = model.getPull(); + if (pull != null) { + return context.withConfigPath( + "pull", () -> PullMetricReaderFactory.INSTANCE.create(pull, context)); } - throw new DeclarativeConfigException("reader must be set"); + throw new DeclarativeConfigException( + FileConfigUtil.withConfigPath(context, "reader must be set")); } private static class PeriodicMetricReaderFactory @@ -58,7 +63,7 @@ private PeriodicMetricReaderFactory() {} public MetricReaderAndCardinalityLimits create( PeriodicMetricReaderModel model, DeclarativeConfigContext context) { PushMetricExporterModel exporterModel = - requireNonNull(model.getExporter(), "periodic metric reader exporter"); + requireNonNull(context, model.getExporter(), "exporter"); MetricExporter metricExporter = MetricExporterFactory.getInstance().create(exporterModel, context); @@ -95,7 +100,7 @@ private PullMetricReaderFactory() {} public MetricReaderAndCardinalityLimits create( PullMetricReaderModel model, DeclarativeConfigContext context) { PullMetricExporterModel exporterModel = - requireNonNull(model.getExporter(), "pull metric reader exporter"); + requireNonNull(context, model.getExporter(), "exporter"); CardinalityLimitSelector cardinalityLimitSelector = null; if (model.getCardinalityLimits() != null) { cardinalityLimitSelector = diff --git a/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/OpenTelemetryConfigurationFactory.java b/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/OpenTelemetryConfigurationFactory.java index 9ceaa3a4cdf..5a38881fbc4 100644 --- a/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/OpenTelemetryConfigurationFactory.java +++ b/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/OpenTelemetryConfigurationFactory.java @@ -12,6 +12,8 @@ import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.LoggerProviderModel; import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.MeterProviderModel; import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.OpenTelemetryConfigurationModel; +import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.PropagatorModel; +import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.ResourceModel; import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.TracerProviderModel; import io.opentelemetry.sdk.internal.ExtendedOpenTelemetrySdk; import io.opentelemetry.sdk.internal.OpenTelemetrySdkBuilderUtil; @@ -69,55 +71,76 @@ public DeclarativeConfigResult create( (ExtendedOpenTelemetrySdk) builder.build(), Resource.getDefault()); } - if (model.getPropagator() != null) { + PropagatorModel propagatorModel = model.getPropagator(); + if (propagatorModel != null) { builder.setPropagators( - PropagatorFactory.getInstance().create(model.getPropagator(), context)); + context.withConfigPath( + "propagator", + () -> PropagatorFactory.getInstance().create(propagatorModel, context))); } Resource resource = Resource.getDefault(); - if (model.getResource() != null) { - resource = ResourceFactory.getInstance().create(model.getResource(), context); + ResourceModel resourceModel = model.getResource(); + if (resourceModel != null) { + resource = + context.withConfigPath( + "resource", () -> ResourceFactory.getInstance().create(resourceModel, context)); } MeterProviderModel meterProviderModel = model.getMeterProvider(); if (meterProviderModel == null) { meterProviderModel = new MeterProviderModel(); } - SdkMeterProvider meterProvider = - MeterProviderFactory.getInstance() - .create(meterProviderModel, context) - .setResource(resource) - .build(); - context.setMeterProvider(meterProvider); - builder.setMeterProvider(context.addCloseable(meterProvider)); + context.pushConfigPath("meter_provider"); + try { + SdkMeterProvider meterProvider = + MeterProviderFactory.getInstance() + .create(meterProviderModel, context) + .setResource(resource) + .build(); + context.setMeterProvider(meterProvider); + builder.setMeterProvider(context.addCloseable(meterProvider)); + } finally { + context.popConfigPath(); + } LoggerProviderModel loggerProviderModel = model.getLoggerProvider(); if (loggerProviderModel == null) { loggerProviderModel = new LoggerProviderModel(); } - builder.setLoggerProvider( - context.addCloseable( - LoggerProviderFactory.getInstance() - .create( - LoggerProviderAndAttributeLimits.create( - model.getAttributeLimits(), loggerProviderModel), - context) - .setResource(resource) - .build())); + context.pushConfigPath("logger_provider"); + try { + builder.setLoggerProvider( + context.addCloseable( + LoggerProviderFactory.getInstance() + .create( + LoggerProviderAndAttributeLimits.create( + model.getAttributeLimits(), loggerProviderModel), + context) + .setResource(resource) + .build())); + } finally { + context.popConfigPath(); + } TracerProviderModel tracerProviderModel = model.getTracerProvider(); if (tracerProviderModel == null) { tracerProviderModel = new TracerProviderModel(); } - builder.setTracerProvider( - context.addCloseable( - TracerProviderFactory.getInstance() - .create( - TracerProviderAndAttributeLimits.create( - model.getAttributeLimits(), tracerProviderModel), - context) - .setResource(resource) - .build())); + context.pushConfigPath("tracer_provider"); + try { + builder.setTracerProvider( + context.addCloseable( + TracerProviderFactory.getInstance() + .create( + TracerProviderAndAttributeLimits.create( + model.getAttributeLimits(), tracerProviderModel), + context) + .setResource(resource) + .build())); + } finally { + context.popConfigPath(); + } return new DeclarativeConfigResult( (ExtendedOpenTelemetrySdk) context.addCloseable(builder.build()), resource); diff --git a/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/SamplerFactory.java b/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/SamplerFactory.java index ed6f80e5a71..c3d2918c81c 100644 --- a/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/SamplerFactory.java +++ b/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/SamplerFactory.java @@ -5,7 +5,6 @@ package io.opentelemetry.sdk.autoconfigure.declarativeconfig; -import io.opentelemetry.api.incubator.config.DeclarativeConfigException; import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.AlwaysRecordSamplerModel; import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.ParentBasedSamplerModel; import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.SamplerModel; @@ -48,8 +47,10 @@ public Sampler create(SamplerModel model, DeclarativeConfigContext context) { if (model.getParentBased() != null) { return createParedBasedSampler(model.getParentBased(), context); } - if (model.getAlwaysRecord() != null) { - return createAlwaysRecordSampler(model.getAlwaysRecord(), context); + AlwaysRecordSamplerModel alwaysRecord = model.getAlwaysRecord(); + if (alwaysRecord != null) { + return context.withConfigPath( + "always_record", () -> createAlwaysRecordSampler(alwaysRecord, context)); } ExperimentalProbabilitySamplerModel probabilityDevelopment = SamplerModelAccessor.getProbability(model); @@ -102,10 +103,8 @@ private static Sampler createParedBasedSampler( private static Sampler createAlwaysRecordSampler( AlwaysRecordSamplerModel alwaysRecordModel, DeclarativeConfigContext context) { - SamplerModel rootModel = alwaysRecordModel.getRoot(); - if (rootModel == null) { - throw new DeclarativeConfigException("always_record sampler .root is required"); - } + SamplerModel rootModel = + FileConfigUtil.requireNonNull(context, alwaysRecordModel.getRoot(), "root"); return AlwaysRecordSampler.create(INSTANCE.create(rootModel, context)); } diff --git a/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/SpanProcessorFactory.java b/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/SpanProcessorFactory.java index aeb1ba12103..18278d94796 100644 --- a/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/SpanProcessorFactory.java +++ b/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/SpanProcessorFactory.java @@ -34,11 +34,14 @@ public SpanProcessor create(SpanProcessorModel model, DeclarativeConfigContext c ConfigKeyValue processorKeyValue = FileConfigUtil.validateSingleKeyValue(context, model, "span processor"); - if (model.getBatch() != null) { - return createBatchLogRecordProcessor(model.getBatch(), context); + BatchSpanProcessorModel batch = model.getBatch(); + if (batch != null) { + return context.withConfigPath("batch", () -> createBatchLogRecordProcessor(batch, context)); } - if (model.getSimple() != null) { - return createSimpleLogRecordProcessor(model.getSimple(), context); + SimpleSpanProcessorModel simple = model.getSimple(); + if (simple != null) { + return context.withConfigPath( + "simple", () -> createSimpleLogRecordProcessor(simple, context)); } return context.loadComponent(SpanProcessor.class, processorKeyValue); @@ -47,7 +50,7 @@ public SpanProcessor create(SpanProcessorModel model, DeclarativeConfigContext c private static SpanProcessor createBatchLogRecordProcessor( BatchSpanProcessorModel batchModel, DeclarativeConfigContext context) { SpanExporterModel exporterModel = - FileConfigUtil.requireNonNull(batchModel.getExporter(), "batch span processor exporter"); + FileConfigUtil.requireNonNull(context, batchModel.getExporter(), "exporter"); SpanExporter spanExporter = SpanExporterFactory.getInstance().create(exporterModel, context); BatchSpanProcessorBuilder builder = BatchSpanProcessor.builder(spanExporter); if (batchModel.getExportTimeout() != null) { @@ -70,7 +73,7 @@ private static SpanProcessor createBatchLogRecordProcessor( private static SpanProcessor createSimpleLogRecordProcessor( SimpleSpanProcessorModel simpleModel, DeclarativeConfigContext context) { SpanExporterModel exporterModel = - FileConfigUtil.requireNonNull(simpleModel.getExporter(), "simple span processor exporter"); + FileConfigUtil.requireNonNull(context, simpleModel.getExporter(), "exporter"); SpanExporter spanExporter = SpanExporterFactory.getInstance().create(exporterModel, context); SimpleSpanProcessorBuilder builder = SimpleSpanProcessor.builder(spanExporter); context.setInternalTelemetry(builder::setMeterProvider); diff --git a/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/TracerProviderFactory.java b/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/TracerProviderFactory.java index 961ef5c893f..37a9ae0a9d3 100644 --- a/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/TracerProviderFactory.java +++ b/sdk-extensions/declarative-config/src/main/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/TracerProviderFactory.java @@ -7,6 +7,8 @@ import static io.opentelemetry.sdk.autoconfigure.declarativeconfig.FileConfigUtil.requireNonNull; +import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.IdGeneratorModel; +import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.SamplerModel; import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.SpanProcessorModel; import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.TracerProviderModel; import io.opentelemetry.sdk.autoconfigure.declarativeconfig.model.internal.ExperimentalTracerConfigModel; @@ -53,23 +55,31 @@ public SdkTracerProviderBuilder create( context); builder.setSpanLimits(spanLimits); - if (tracerProviderModel.getSampler() != null) { + SamplerModel samplerModel = tracerProviderModel.getSampler(); + if (samplerModel != null) { Sampler sampler = - SamplerFactory.getInstance().create(tracerProviderModel.getSampler(), context); + context.withConfigPath( + "sampler", () -> SamplerFactory.getInstance().create(samplerModel, context)); builder.setSampler(sampler); } List processors = tracerProviderModel.getProcessors(); if (processors != null) { - processors.forEach( - processor -> - builder.addSpanProcessor( - SpanProcessorFactory.getInstance().create(processor, context))); + for (int i = 0; i < processors.size(); i++) { + SpanProcessorModel processor = processors.get(i); + builder.addSpanProcessor( + context.withConfigPath( + "processors[" + i + "]", + () -> SpanProcessorFactory.getInstance().create(processor, context))); + } } - if (tracerProviderModel.getIdGenerator() != null) { + IdGeneratorModel idGeneratorModel = tracerProviderModel.getIdGenerator(); + if (idGeneratorModel != null) { builder.setIdGenerator( - IdGeneratorFactory.getInstance().create(tracerProviderModel.getIdGenerator(), context)); + context.withConfigPath( + "id_generator", + () -> IdGeneratorFactory.getInstance().create(idGeneratorModel, context))); } ExperimentalTracerConfiguratorModel tracerConfiguratorModel = @@ -84,9 +94,13 @@ public SdkTracerProviderBuilder create( List tracerMatcherAndConfigs = tracerConfiguratorModel.getTracers(); if (tracerMatcherAndConfigs != null) { - for (ExperimentalTracerMatcherAndConfigModel tracerMatcherAndConfig : - tracerMatcherAndConfigs) { - String name = requireNonNull(tracerMatcherAndConfig.getName(), "tracer matcher name"); + for (int i = 0; i < tracerMatcherAndConfigs.size(); i++) { + ExperimentalTracerMatcherAndConfigModel tracerMatcherAndConfig = + tracerMatcherAndConfigs.get(i); + String name = + context.withConfigPath( + "tracer_configurator/development.tracers[" + i + "]", + () -> requireNonNull(context, tracerMatcherAndConfig.getName(), "name")); ExperimentalTracerConfigModel config = tracerMatcherAndConfig.getConfig(); if (name == null || config == null) { continue; diff --git a/sdk-extensions/declarative-config/src/test/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/DeclarativeConfigurationCreateTest.java b/sdk-extensions/declarative-config/src/test/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/DeclarativeConfigurationCreateTest.java index 86b8f3608a3..050d5820c6a 100644 --- a/sdk-extensions/declarative-config/src/test/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/DeclarativeConfigurationCreateTest.java +++ b/sdk-extensions/declarative-config/src/test/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/DeclarativeConfigurationCreateTest.java @@ -174,6 +174,24 @@ void parseAndCreate_EmptyComponentProviderConfig() { .doesNotThrowAnyException(); } + @Test + void parseAndCreate_ErrorIncludesDocumentPath() { + String yaml = + "file_format: \"1.2\"\n" + + "tracer_provider:\n" + + " processors:\n" + + " - batch:\n" + + " schedule_delay: 100\n"; + + assertThatThrownBy( + () -> + DeclarativeConfiguration.parseAndCreate( + new ByteArrayInputStream(yaml.getBytes(StandardCharsets.UTF_8)))) + .isInstanceOf(DeclarativeConfigException.class) + .hasMessageContaining( + ".tracer_provider.processors[0].batch exporter is required but is null"); + } + @Test void create_ModelCustomizer() { OpenTelemetryConfigurationModel model = new OpenTelemetryConfigurationModel(); diff --git a/sdk-extensions/declarative-config/src/test/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/LogRecordProcessorFactoryTest.java b/sdk-extensions/declarative-config/src/test/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/LogRecordProcessorFactoryTest.java index 1470d392600..0815c43a9d5 100644 --- a/sdk-extensions/declarative-config/src/test/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/LogRecordProcessorFactoryTest.java +++ b/sdk-extensions/declarative-config/src/test/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/LogRecordProcessorFactoryTest.java @@ -107,7 +107,11 @@ private static Stream createTestCases() { @ParameterizedTest @MethodSource("createInvalidTestCases") void create_Invalid(LogRecordProcessorModel model, String expectedMessage) { - assertThatThrownBy(() -> LogRecordProcessorFactory.getInstance().create(model, context)) + assertThatThrownBy( + () -> + context.withConfigPath( + "logger_provider.processors[0]", + () -> LogRecordProcessorFactory.getInstance().create(model, context))) .isInstanceOf(DeclarativeConfigException.class) .hasMessage(expectedMessage); } @@ -117,11 +121,11 @@ private static Stream createInvalidTestCases() { Arguments.argumentSet( "batch missing exporter", new LogRecordProcessorModel().setBatch(new BatchLogRecordProcessorModel()), - "batch log record processor exporter is required but is null"), + ".logger_provider.processors[0].batch exporter is required but is null"), Arguments.argumentSet( "simple missing exporter", new LogRecordProcessorModel().setSimple(new SimpleLogRecordProcessorModel()), - "simple log record processor exporter is required but is null"), + ".logger_provider.processors[0].simple exporter is required but is null"), Arguments.argumentSet( "unknown component provider", new LogRecordProcessorModel() diff --git a/sdk-extensions/declarative-config/src/test/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/MetricReaderFactoryTest.java b/sdk-extensions/declarative-config/src/test/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/MetricReaderFactoryTest.java index 0ce35451c3e..4a5a38637ce 100644 --- a/sdk-extensions/declarative-config/src/test/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/MetricReaderFactoryTest.java +++ b/sdk-extensions/declarative-config/src/test/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/MetricReaderFactoryTest.java @@ -227,7 +227,11 @@ static Stream createTestCases() throws IOException { @ParameterizedTest @MethodSource("createInvalidTestCases") void create_Invalid(MetricReaderModel model, String expectedMessage) { - assertThatThrownBy(() -> MetricReaderFactory.getInstance().create(model, context)) + assertThatThrownBy( + () -> + context.withConfigPath( + "meter_provider.readers[0]", + () -> MetricReaderFactory.getInstance().create(model, context))) .isInstanceOf(DeclarativeConfigException.class) .hasMessage(expectedMessage); } @@ -237,16 +241,16 @@ static Stream createInvalidTestCases() { Arguments.argumentSet( "null periodic reader exporter", new MetricReaderModel().setPeriodic(new PeriodicMetricReaderModel()), - "periodic metric reader exporter is required but is null"), + ".meter_provider.readers[0].periodic exporter is required but is null"), Arguments.argumentSet( "null pull reader", new MetricReaderModel().setPull(new PullMetricReaderModel()), - "pull metric reader exporter is required but is null"), + ".meter_provider.readers[0].pull exporter is required but is null"), Arguments.argumentSet( "null pull reader exporter", new MetricReaderModel() .setPull(new PullMetricReaderModel().setExporter(new PullMetricExporterModel())), - "metric reader must have exactly one entry but has 0")); + ".meter_provider.readers[0].pull metric reader must have exactly one entry but has 0")); } private static int randomAvailablePort() throws IOException { diff --git a/sdk-extensions/declarative-config/src/test/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/SamplerFactoryTest.java b/sdk-extensions/declarative-config/src/test/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/SamplerFactoryTest.java index 5fa15122572..d3a7cd2a753 100644 --- a/sdk-extensions/declarative-config/src/test/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/SamplerFactoryTest.java +++ b/sdk-extensions/declarative-config/src/test/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/SamplerFactoryTest.java @@ -196,7 +196,11 @@ private static Stream createArguments() { @ParameterizedTest @MethodSource("createInvalidArguments") void createInvalid(SamplerModel model, String expectedMessage) { - assertThatThrownBy(() -> SamplerFactory.getInstance().create(model, context)) + assertThatThrownBy( + () -> + context.withConfigPath( + "tracer_provider.sampler", + () -> SamplerFactory.getInstance().create(model, context))) .isInstanceOf(DeclarativeConfigException.class) .extracting(throwable -> throwable.getCause() == null ? throwable : throwable.getCause()) .extracting(Throwable::getMessage) @@ -223,11 +227,11 @@ private static Stream createInvalidArguments() { new SamplerModel(), new ExperimentalComposableSamplerModel() .setParentThreshold(new ExperimentalComposableParentThresholdSamplerModel())), - "parent threshold sampler root is required but is null"), + ".tracer_provider.sampler.parent_threshold root is required but is null"), Arguments.argumentSet( "always_record missing root", new SamplerModel().setAlwaysRecord(new AlwaysRecordSamplerModel()), - "always_record sampler .root is required"), + ".tracer_provider.sampler.always_record root is required but is null"), Arguments.argumentSet( "unknown component provider", new SamplerModel() diff --git a/sdk-extensions/declarative-config/src/test/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/SpanProcessorFactoryTest.java b/sdk-extensions/declarative-config/src/test/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/SpanProcessorFactoryTest.java index a6893432179..491c2160d1f 100644 --- a/sdk-extensions/declarative-config/src/test/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/SpanProcessorFactoryTest.java +++ b/sdk-extensions/declarative-config/src/test/java/io/opentelemetry/sdk/autoconfigure/declarativeconfig/SpanProcessorFactoryTest.java @@ -95,7 +95,11 @@ private static Stream createTestCases() { @ParameterizedTest @MethodSource("createInvalidTestCases") void create_Invalid(SpanProcessorModel model, String expectedMessage) { - assertThatThrownBy(() -> SpanProcessorFactory.getInstance().create(model, context)) + assertThatThrownBy( + () -> + context.withConfigPath( + "tracer_provider.processors[0]", + () -> SpanProcessorFactory.getInstance().create(model, context))) .isInstanceOf(DeclarativeConfigException.class) .hasMessage(expectedMessage); } @@ -105,11 +109,11 @@ private static Stream createInvalidTestCases() { Arguments.argumentSet( "batch missing exporter", new SpanProcessorModel().setBatch(new BatchSpanProcessorModel()), - "batch span processor exporter is required but is null"), + ".tracer_provider.processors[0].batch exporter is required but is null"), Arguments.argumentSet( "simple missing exporter", new SpanProcessorModel().setSimple(new SimpleSpanProcessorModel()), - "simple span processor exporter is required but is null"), + ".tracer_provider.processors[0].simple exporter is required but is null"), Arguments.argumentSet( "unknown component provider", new SpanProcessorModel()