Scope config (#6375)
This commit is contained in:
parent
1623a80d4c
commit
c33febbea6
|
@ -0,0 +1,237 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.sdk;
|
||||||
|
|
||||||
|
import static io.opentelemetry.sdk.internal.ScopeConfiguratorBuilder.nameEquals;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import io.opentelemetry.api.OpenTelemetry;
|
||||||
|
import io.opentelemetry.api.logs.Logger;
|
||||||
|
import io.opentelemetry.api.metrics.Meter;
|
||||||
|
import io.opentelemetry.api.trace.Span;
|
||||||
|
import io.opentelemetry.api.trace.Tracer;
|
||||||
|
import io.opentelemetry.context.Scope;
|
||||||
|
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
|
||||||
|
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
|
||||||
|
import io.opentelemetry.sdk.logs.SdkLoggerProviderBuilder;
|
||||||
|
import io.opentelemetry.sdk.logs.data.LogRecordData;
|
||||||
|
import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor;
|
||||||
|
import io.opentelemetry.sdk.logs.internal.LoggerConfig;
|
||||||
|
import io.opentelemetry.sdk.logs.internal.SdkLoggerProviderUtil;
|
||||||
|
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
|
||||||
|
import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
|
||||||
|
import io.opentelemetry.sdk.metrics.data.MetricData;
|
||||||
|
import io.opentelemetry.sdk.metrics.internal.MeterConfig;
|
||||||
|
import io.opentelemetry.sdk.metrics.internal.SdkMeterProviderUtil;
|
||||||
|
import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter;
|
||||||
|
import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader;
|
||||||
|
import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter;
|
||||||
|
import io.opentelemetry.sdk.trace.SdkTracerProvider;
|
||||||
|
import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder;
|
||||||
|
import io.opentelemetry.sdk.trace.data.SpanData;
|
||||||
|
import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor;
|
||||||
|
import io.opentelemetry.sdk.trace.internal.SdkTracerProviderUtil;
|
||||||
|
import io.opentelemetry.sdk.trace.internal.TracerConfig;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class ScopeConfiguratorTest {
|
||||||
|
|
||||||
|
private final InMemoryLogRecordExporter logRecordExporter = InMemoryLogRecordExporter.create();
|
||||||
|
private final InMemoryMetricReader metricReader = InMemoryMetricReader.create();
|
||||||
|
private final InMemorySpanExporter spanExporter = InMemorySpanExporter.create();
|
||||||
|
|
||||||
|
private static final InstrumentationScopeInfo scopeA = InstrumentationScopeInfo.create("scopeA");
|
||||||
|
private static final InstrumentationScopeInfo scopeB = InstrumentationScopeInfo.create("scopeB");
|
||||||
|
private static final InstrumentationScopeInfo scopeC = InstrumentationScopeInfo.create("scopeC");
|
||||||
|
|
||||||
|
/** Disable "scopeB". All other scopes are enabled by default. */
|
||||||
|
@Test
|
||||||
|
void disableScopeB() {
|
||||||
|
// Configuration ergonomics will improve after APIs stabilize
|
||||||
|
SdkTracerProviderBuilder tracerProviderBuilder = SdkTracerProvider.builder();
|
||||||
|
SdkTracerProviderUtil.addTracerConfiguratorCondition(
|
||||||
|
tracerProviderBuilder, nameEquals(scopeB.getName()), TracerConfig.disabled());
|
||||||
|
SdkMeterProviderBuilder meterProviderBuilder = SdkMeterProvider.builder();
|
||||||
|
SdkMeterProviderUtil.addMeterConfiguratorCondition(
|
||||||
|
meterProviderBuilder, nameEquals(scopeB.getName()), MeterConfig.disabled());
|
||||||
|
SdkLoggerProviderBuilder loggerProviderBuilder = SdkLoggerProvider.builder();
|
||||||
|
SdkLoggerProviderUtil.addLoggerConfiguratorCondition(
|
||||||
|
loggerProviderBuilder, nameEquals(scopeB.getName()), LoggerConfig.disabled());
|
||||||
|
|
||||||
|
OpenTelemetrySdk sdk =
|
||||||
|
OpenTelemetrySdk.builder()
|
||||||
|
.setTracerProvider(
|
||||||
|
tracerProviderBuilder
|
||||||
|
.addSpanProcessor(SimpleSpanProcessor.create(spanExporter))
|
||||||
|
.build())
|
||||||
|
.setMeterProvider(meterProviderBuilder.registerMetricReader(metricReader).build())
|
||||||
|
.setLoggerProvider(
|
||||||
|
loggerProviderBuilder
|
||||||
|
.addLogRecordProcessor(SimpleLogRecordProcessor.create(logRecordExporter))
|
||||||
|
.build())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
simulateInstrumentation(sdk);
|
||||||
|
|
||||||
|
// Collect all the telemetry. Ensure we don't see any from scopeB, and that the telemetry from
|
||||||
|
// scopeA and scopeC is valid.
|
||||||
|
assertThat(spanExporter.getFinishedSpanItems())
|
||||||
|
.satisfies(
|
||||||
|
spans -> {
|
||||||
|
Map<InstrumentationScopeInfo, List<SpanData>> spansByScope =
|
||||||
|
spans.stream()
|
||||||
|
.collect(Collectors.groupingBy(SpanData::getInstrumentationScopeInfo));
|
||||||
|
assertThat(spansByScope.get(scopeA)).hasSize(1);
|
||||||
|
assertThat(spansByScope.get(scopeB)).isNull();
|
||||||
|
assertThat(spansByScope.get(scopeC)).hasSize(1);
|
||||||
|
});
|
||||||
|
assertThat(metricReader.collectAllMetrics())
|
||||||
|
.satisfies(
|
||||||
|
metrics -> {
|
||||||
|
Map<InstrumentationScopeInfo, List<MetricData>> metricsByScope =
|
||||||
|
metrics.stream()
|
||||||
|
.collect(Collectors.groupingBy(MetricData::getInstrumentationScopeInfo));
|
||||||
|
assertThat(metricsByScope.get(scopeA)).hasSize(1);
|
||||||
|
assertThat(metricsByScope.get(scopeB)).isNull();
|
||||||
|
assertThat(metricsByScope.get(scopeC)).hasSize(1);
|
||||||
|
});
|
||||||
|
assertThat(logRecordExporter.getFinishedLogRecordItems())
|
||||||
|
.satisfies(
|
||||||
|
logs -> {
|
||||||
|
Map<InstrumentationScopeInfo, List<LogRecordData>> logsByScope =
|
||||||
|
logs.stream()
|
||||||
|
.collect(Collectors.groupingBy(LogRecordData::getInstrumentationScopeInfo));
|
||||||
|
assertThat(logsByScope.get(scopeA)).hasSize(1);
|
||||||
|
assertThat(logsByScope.get(scopeB)).isNull();
|
||||||
|
assertThat(logsByScope.get(scopeC)).hasSize(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Disable all scopes by default and enable a single scope. */
|
||||||
|
@Test
|
||||||
|
void disableAllScopesExceptB() {
|
||||||
|
// Configuration ergonomics will improve after APIs stabilize
|
||||||
|
SdkTracerProviderBuilder tracerProviderBuilder = SdkTracerProvider.builder();
|
||||||
|
SdkTracerProviderUtil.setTracerConfigurator(
|
||||||
|
tracerProviderBuilder,
|
||||||
|
TracerConfig.configuratorBuilder()
|
||||||
|
.setDefault(TracerConfig.disabled())
|
||||||
|
.addCondition(nameEquals(scopeB.getName()), TracerConfig.enabled())
|
||||||
|
.build());
|
||||||
|
SdkMeterProviderBuilder meterProviderBuilder = SdkMeterProvider.builder();
|
||||||
|
SdkMeterProviderUtil.setMeterConfigurator(
|
||||||
|
meterProviderBuilder,
|
||||||
|
MeterConfig.configuratorBuilder()
|
||||||
|
.setDefault(MeterConfig.disabled())
|
||||||
|
.addCondition(nameEquals(scopeB.getName()), MeterConfig.enabled())
|
||||||
|
.build());
|
||||||
|
SdkLoggerProviderBuilder loggerProviderBuilder = SdkLoggerProvider.builder();
|
||||||
|
SdkLoggerProviderUtil.setLoggerConfigurator(
|
||||||
|
loggerProviderBuilder,
|
||||||
|
LoggerConfig.configuratorBuilder()
|
||||||
|
.setDefault(LoggerConfig.disabled())
|
||||||
|
.addCondition(nameEquals(scopeB.getName()), LoggerConfig.enabled())
|
||||||
|
.build());
|
||||||
|
|
||||||
|
OpenTelemetrySdk sdk =
|
||||||
|
OpenTelemetrySdk.builder()
|
||||||
|
.setTracerProvider(
|
||||||
|
tracerProviderBuilder
|
||||||
|
.addSpanProcessor(SimpleSpanProcessor.create(spanExporter))
|
||||||
|
.build())
|
||||||
|
.setMeterProvider(meterProviderBuilder.registerMetricReader(metricReader).build())
|
||||||
|
.setLoggerProvider(
|
||||||
|
loggerProviderBuilder
|
||||||
|
.addLogRecordProcessor(SimpleLogRecordProcessor.create(logRecordExporter))
|
||||||
|
.build())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
simulateInstrumentation(sdk);
|
||||||
|
|
||||||
|
// Collect all the telemetry. Ensure we only see telemetry from scopeB, since other scopes have
|
||||||
|
// been disabled by default.
|
||||||
|
assertThat(spanExporter.getFinishedSpanItems())
|
||||||
|
.satisfies(
|
||||||
|
spans -> {
|
||||||
|
Map<InstrumentationScopeInfo, List<SpanData>> spansByScope =
|
||||||
|
spans.stream()
|
||||||
|
.collect(Collectors.groupingBy(SpanData::getInstrumentationScopeInfo));
|
||||||
|
assertThat(spansByScope.get(scopeA)).isNull();
|
||||||
|
assertThat(spansByScope.get(scopeB)).hasSize(1);
|
||||||
|
assertThat(spansByScope.get(scopeC)).isNull();
|
||||||
|
});
|
||||||
|
assertThat(metricReader.collectAllMetrics())
|
||||||
|
.satisfies(
|
||||||
|
metrics -> {
|
||||||
|
Map<InstrumentationScopeInfo, List<MetricData>> metricsByScope =
|
||||||
|
metrics.stream()
|
||||||
|
.collect(Collectors.groupingBy(MetricData::getInstrumentationScopeInfo));
|
||||||
|
assertThat(metricsByScope.get(scopeA)).isNull();
|
||||||
|
assertThat(metricsByScope.get(scopeB)).hasSize(1);
|
||||||
|
assertThat(metricsByScope.get(scopeC)).isNull();
|
||||||
|
});
|
||||||
|
assertThat(logRecordExporter.getFinishedLogRecordItems())
|
||||||
|
.satisfies(
|
||||||
|
logs -> {
|
||||||
|
Map<InstrumentationScopeInfo, List<LogRecordData>> logsByScope =
|
||||||
|
logs.stream()
|
||||||
|
.collect(Collectors.groupingBy(LogRecordData::getInstrumentationScopeInfo));
|
||||||
|
assertThat(logsByScope.get(scopeA)).isNull();
|
||||||
|
assertThat(logsByScope.get(scopeB)).hasSize(1);
|
||||||
|
assertThat(logsByScope.get(scopeC)).isNull();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emit spans, metrics and logs in a hierarchy of 3 scopes: scopeA -> scopeB -> scopeC. Exercise
|
||||||
|
* the scope config which is common across all signals.
|
||||||
|
*/
|
||||||
|
private static void simulateInstrumentation(OpenTelemetry openTelemetry) {
|
||||||
|
// Start scopeA
|
||||||
|
Tracer scopeATracer = openTelemetry.getTracer(scopeA.getName());
|
||||||
|
Meter scopeAMeter = openTelemetry.getMeter(scopeA.getName());
|
||||||
|
Logger scopeALogger = openTelemetry.getLogsBridge().get(scopeA.getName());
|
||||||
|
Span spanA = scopeATracer.spanBuilder("spanA").startSpan();
|
||||||
|
try (Scope spanAScope = spanA.makeCurrent()) {
|
||||||
|
scopeALogger.logRecordBuilder().setBody("scopeA log message").emit();
|
||||||
|
|
||||||
|
// Start scopeB
|
||||||
|
Tracer scopeBTracer = openTelemetry.getTracer(scopeB.getName());
|
||||||
|
Meter scopeBMeter = openTelemetry.getMeter(scopeB.getName());
|
||||||
|
Logger scopeBLogger = openTelemetry.getLogsBridge().get(scopeB.getName());
|
||||||
|
Span spanB = scopeBTracer.spanBuilder("spanB").startSpan();
|
||||||
|
try (Scope spanBScope = spanB.makeCurrent()) {
|
||||||
|
scopeBLogger.logRecordBuilder().setBody("scopeB log message").emit();
|
||||||
|
|
||||||
|
// Start scopeC
|
||||||
|
Tracer scopeCTracer = openTelemetry.getTracer(scopeC.getName());
|
||||||
|
Meter scopeCMeter = openTelemetry.getMeter(scopeC.getName());
|
||||||
|
Logger scopeCLogger = openTelemetry.getLogsBridge().get(scopeC.getName());
|
||||||
|
Span spanC = scopeCTracer.spanBuilder("spanC").startSpan();
|
||||||
|
try (Scope spanCScope = spanB.makeCurrent()) {
|
||||||
|
scopeCLogger.logRecordBuilder().setBody("scopeC log message").emit();
|
||||||
|
} finally {
|
||||||
|
spanC.end();
|
||||||
|
scopeCMeter.counterBuilder("scopeCCounter").build().add(1);
|
||||||
|
}
|
||||||
|
// End scopeC
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
spanB.end();
|
||||||
|
scopeBMeter.counterBuilder("scopeBCounter").build().add(1);
|
||||||
|
}
|
||||||
|
// End scopeB
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
spanA.end();
|
||||||
|
scopeAMeter.counterBuilder("scopeACounter").build().add(1);
|
||||||
|
}
|
||||||
|
// End scopeA
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.sdk.internal;
|
||||||
|
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utilities for glob pattern matching.
|
||||||
|
*
|
||||||
|
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
|
||||||
|
* at any time.
|
||||||
|
*/
|
||||||
|
public final class GlobUtil {
|
||||||
|
|
||||||
|
private GlobUtil() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a predicate that returns {@code true} if a string matches the {@code globPattern}.
|
||||||
|
*
|
||||||
|
* <p>{@code globPattern} may contain the wildcard characters {@code *} and {@code ?} with the
|
||||||
|
* following matching criteria:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code *} matches 0 or more instances of any character
|
||||||
|
* <li>{@code ?} matches exactly one instance of any character
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public static Predicate<String> toGlobPatternPredicate(String globPattern) {
|
||||||
|
// Match all
|
||||||
|
if (globPattern.equals("*")) {
|
||||||
|
return unused -> true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If globPattern contains '*' or '?', convert it to a regex and return corresponding predicate
|
||||||
|
for (int i = 0; i < globPattern.length(); i++) {
|
||||||
|
char c = globPattern.charAt(i);
|
||||||
|
if (c == '*' || c == '?') {
|
||||||
|
Pattern pattern = toRegexPattern(globPattern);
|
||||||
|
return string -> pattern.matcher(string).matches();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exact match, ignoring case
|
||||||
|
return globPattern::equalsIgnoreCase;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform the {@code globPattern} to a regex by converting {@code *} to {@code .*}, {@code ?}
|
||||||
|
* to {@code .}, and escaping other regex special characters.
|
||||||
|
*/
|
||||||
|
private static Pattern toRegexPattern(String globPattern) {
|
||||||
|
int tokenStart = -1;
|
||||||
|
StringBuilder patternBuilder = new StringBuilder();
|
||||||
|
for (int i = 0; i < globPattern.length(); i++) {
|
||||||
|
char c = globPattern.charAt(i);
|
||||||
|
if (c == '*' || c == '?') {
|
||||||
|
if (tokenStart != -1) {
|
||||||
|
patternBuilder.append(Pattern.quote(globPattern.substring(tokenStart, i)));
|
||||||
|
tokenStart = -1;
|
||||||
|
}
|
||||||
|
if (c == '*') {
|
||||||
|
patternBuilder.append(".*");
|
||||||
|
} else {
|
||||||
|
// c == '?'
|
||||||
|
patternBuilder.append(".");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (tokenStart == -1) {
|
||||||
|
tokenStart = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (tokenStart != -1) {
|
||||||
|
patternBuilder.append(Pattern.quote(globPattern.substring(tokenStart)));
|
||||||
|
}
|
||||||
|
return Pattern.compile(patternBuilder.toString());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.sdk.internal;
|
||||||
|
|
||||||
|
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link ScopeConfigurator} computes configuration for a given {@link InstrumentationScopeInfo}.
|
||||||
|
*
|
||||||
|
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
|
||||||
|
* at any time.
|
||||||
|
*/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface ScopeConfigurator<T> extends Function<InstrumentationScopeInfo, T> {
|
||||||
|
|
||||||
|
/** Create a new builder. */
|
||||||
|
static <T> ScopeConfiguratorBuilder<T> builder() {
|
||||||
|
return new ScopeConfiguratorBuilder<>(unused -> null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert this {@link ScopeConfigurator} to a builder. Additional added matchers only apply when
|
||||||
|
* {@link #apply(Object)} returns {@code null}. If this configurator contains {@link
|
||||||
|
* ScopeConfiguratorBuilder#setDefault(Object)}, additional matchers are never applied.
|
||||||
|
*/
|
||||||
|
default ScopeConfiguratorBuilder<T> toBuilder() {
|
||||||
|
return new ScopeConfiguratorBuilder<>(this);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,114 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.sdk.internal;
|
||||||
|
|
||||||
|
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder for {@link ScopeConfigurator}.
|
||||||
|
*
|
||||||
|
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
|
||||||
|
* at any time.
|
||||||
|
*
|
||||||
|
* @param <T> The scope configuration object, e.g. {@code TracerConfig}, {@code LoggerConfig},
|
||||||
|
* {@code MeterConfig}.
|
||||||
|
*/
|
||||||
|
public final class ScopeConfiguratorBuilder<T> {
|
||||||
|
|
||||||
|
private final ScopeConfigurator<T> baseScopeConfigurator;
|
||||||
|
@Nullable private T defaultScopeConfig;
|
||||||
|
private final List<Condition<T>> conditions = new ArrayList<>();
|
||||||
|
|
||||||
|
ScopeConfiguratorBuilder(ScopeConfigurator<T> baseScopeConfigurator) {
|
||||||
|
this.baseScopeConfigurator = baseScopeConfigurator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the default scope config, which is returned by {@link ScopeConfigurator#apply(Object)} if a
|
||||||
|
* {@link InstrumentationScopeInfo} does not match any {@link #addCondition(Predicate, Object)
|
||||||
|
* conditions}. If a default is not set, an SDK defined default is used.
|
||||||
|
*/
|
||||||
|
public ScopeConfiguratorBuilder<T> setDefault(T defaultScopeConfig) {
|
||||||
|
this.defaultScopeConfig = defaultScopeConfig;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a condition. Conditions are evaluated in order. The {@code scopeConfig} for the first match
|
||||||
|
* is returned by {@link ScopeConfigurator#apply(Object)}.
|
||||||
|
*
|
||||||
|
* @param scopePredicate predicate that {@link InstrumentationScopeInfo}s are evaluated against
|
||||||
|
* @param scopeConfig the scope config to use when this condition is the first matching {@code
|
||||||
|
* scopePredicate}
|
||||||
|
* @see #nameMatchesGlob(String)
|
||||||
|
* @see #nameEquals(String)
|
||||||
|
*/
|
||||||
|
public ScopeConfiguratorBuilder<T> addCondition(
|
||||||
|
Predicate<InstrumentationScopeInfo> scopePredicate, T scopeConfig) {
|
||||||
|
conditions.add(new Condition<>(scopePredicate, scopeConfig));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function for pattern matching {@link InstrumentationScopeInfo#getName()} against the
|
||||||
|
* {@code globPattern}.
|
||||||
|
*
|
||||||
|
* <p>{@code globPattern} may contain the wildcard characters {@code *} and {@code ?} with the
|
||||||
|
* following matching criteria:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>{@code *} matches 0 or more instances of any character
|
||||||
|
* <li>{@code ?} matches exactly one instance of any character
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @see #addCondition(Predicate, Object)
|
||||||
|
*/
|
||||||
|
public static Predicate<InstrumentationScopeInfo> nameMatchesGlob(String globPattern) {
|
||||||
|
Predicate<String> globPredicate = GlobUtil.toGlobPatternPredicate(globPattern);
|
||||||
|
return scopeInfo -> globPredicate.test(scopeInfo.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function for exact matching {@link InstrumentationScopeInfo#getName()} against the
|
||||||
|
* {@code scopeName}.
|
||||||
|
*
|
||||||
|
* @see #addCondition(Predicate, Object)
|
||||||
|
*/
|
||||||
|
public static Predicate<InstrumentationScopeInfo> nameEquals(String scopeName) {
|
||||||
|
return scopeInfo -> scopeInfo.getName().equals(scopeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Build a {@link ScopeConfigurator} with the configuration of this builder. */
|
||||||
|
public ScopeConfigurator<T> build() {
|
||||||
|
// TODO: return an instance with toString implementation which self describes rules
|
||||||
|
return scopeInfo -> {
|
||||||
|
T scopeConfig = baseScopeConfigurator.apply(scopeInfo);
|
||||||
|
if (scopeConfig != null) {
|
||||||
|
return scopeConfig;
|
||||||
|
}
|
||||||
|
for (Condition<T> condition : conditions) {
|
||||||
|
if (condition.scopeMatcher.test(scopeInfo)) {
|
||||||
|
return condition.scopeConfig;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultScopeConfig;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class Condition<T> {
|
||||||
|
private final Predicate<InstrumentationScopeInfo> scopeMatcher;
|
||||||
|
private final T scopeConfig;
|
||||||
|
|
||||||
|
private Condition(Predicate<InstrumentationScopeInfo> scopeMatcher, T scopeConfig) {
|
||||||
|
this.scopeMatcher = scopeMatcher;
|
||||||
|
this.scopeConfig = scopeConfig;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.sdk.internal;
|
||||||
|
|
||||||
|
import static io.opentelemetry.sdk.internal.GlobUtil.toGlobPatternPredicate;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
class GlobUtilTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void matchesName() {
|
||||||
|
assertThat(toGlobPatternPredicate("foo").test("foo")).isTrue();
|
||||||
|
assertThat(toGlobPatternPredicate("foo").test("Foo")).isTrue();
|
||||||
|
assertThat(toGlobPatternPredicate("foo").test("bar")).isFalse();
|
||||||
|
assertThat(toGlobPatternPredicate("fo?").test("foo")).isTrue();
|
||||||
|
assertThat(toGlobPatternPredicate("fo??").test("fooo")).isTrue();
|
||||||
|
assertThat(toGlobPatternPredicate("fo?").test("fob")).isTrue();
|
||||||
|
assertThat(toGlobPatternPredicate("fo?").test("fooo")).isFalse();
|
||||||
|
assertThat(toGlobPatternPredicate("*").test("foo")).isTrue();
|
||||||
|
assertThat(toGlobPatternPredicate("*").test("bar")).isTrue();
|
||||||
|
assertThat(toGlobPatternPredicate("*").test("baz")).isTrue();
|
||||||
|
assertThat(toGlobPatternPredicate("*").test("foo.bar.baz")).isTrue();
|
||||||
|
assertThat(toGlobPatternPredicate("*").test(null)).isTrue();
|
||||||
|
assertThat(toGlobPatternPredicate("*").test("")).isTrue();
|
||||||
|
assertThat(toGlobPatternPredicate("fo*").test("fo")).isTrue();
|
||||||
|
assertThat(toGlobPatternPredicate("fo*").test("foo")).isTrue();
|
||||||
|
assertThat(toGlobPatternPredicate("fo*").test("fooo")).isTrue();
|
||||||
|
assertThat(toGlobPatternPredicate("fo*").test("foo.bar.baz")).isTrue();
|
||||||
|
assertThat(toGlobPatternPredicate("*bar").test("sandbar")).isTrue();
|
||||||
|
assertThat(toGlobPatternPredicate("fo*b*").test("foobar")).isTrue();
|
||||||
|
assertThat(toGlobPatternPredicate("fo*b*").test("foob")).isTrue();
|
||||||
|
assertThat(toGlobPatternPredicate("fo*b*").test("foo bar")).isTrue();
|
||||||
|
assertThat(toGlobPatternPredicate("fo? b??").test("foo bar")).isTrue();
|
||||||
|
assertThat(toGlobPatternPredicate("fo? b??").test("fooo bar")).isFalse();
|
||||||
|
assertThat(toGlobPatternPredicate("fo* ba?").test("foo is not bar")).isTrue();
|
||||||
|
assertThat(toGlobPatternPredicate("fo? b*").test("fox beetles for lunch")).isTrue();
|
||||||
|
assertThat(toGlobPatternPredicate("f()[]$^.{}|").test("f()[]$^.{}|")).isTrue();
|
||||||
|
assertThat(toGlobPatternPredicate("f()[]$^.{}|?").test("f()[]$^.{}|o")).isTrue();
|
||||||
|
assertThat(toGlobPatternPredicate("f()[]$^.{}|*").test("f()[]$^.{}|ooo")).isTrue();
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,23 +7,34 @@ package io.opentelemetry.sdk.logs;
|
||||||
|
|
||||||
import io.opentelemetry.api.logs.LogRecordBuilder;
|
import io.opentelemetry.api.logs.LogRecordBuilder;
|
||||||
import io.opentelemetry.api.logs.Logger;
|
import io.opentelemetry.api.logs.Logger;
|
||||||
|
import io.opentelemetry.api.logs.LoggerProvider;
|
||||||
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
|
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
|
||||||
|
import io.opentelemetry.sdk.logs.internal.LoggerConfig;
|
||||||
|
|
||||||
/** SDK implementation of {@link Logger}. */
|
/** SDK implementation of {@link Logger}. */
|
||||||
final class SdkLogger implements Logger {
|
final class SdkLogger implements Logger {
|
||||||
|
|
||||||
|
private static final Logger NOOP_LOGGER = LoggerProvider.noop().get("noop");
|
||||||
|
|
||||||
private final LoggerSharedState loggerSharedState;
|
private final LoggerSharedState loggerSharedState;
|
||||||
private final InstrumentationScopeInfo instrumentationScopeInfo;
|
private final InstrumentationScopeInfo instrumentationScopeInfo;
|
||||||
|
private final LoggerConfig loggerConfig;
|
||||||
|
|
||||||
SdkLogger(
|
SdkLogger(
|
||||||
LoggerSharedState loggerSharedState, InstrumentationScopeInfo instrumentationScopeInfo) {
|
LoggerSharedState loggerSharedState,
|
||||||
|
InstrumentationScopeInfo instrumentationScopeInfo,
|
||||||
|
LoggerConfig loggerConfig) {
|
||||||
this.loggerSharedState = loggerSharedState;
|
this.loggerSharedState = loggerSharedState;
|
||||||
this.instrumentationScopeInfo = instrumentationScopeInfo;
|
this.instrumentationScopeInfo = instrumentationScopeInfo;
|
||||||
|
this.loggerConfig = loggerConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LogRecordBuilder logRecordBuilder() {
|
public LogRecordBuilder logRecordBuilder() {
|
||||||
return new SdkLogRecordBuilder(loggerSharedState, instrumentationScopeInfo);
|
if (loggerConfig.isEnabled()) {
|
||||||
|
return new SdkLogRecordBuilder(loggerSharedState, instrumentationScopeInfo);
|
||||||
|
}
|
||||||
|
return NOOP_LOGGER.logRecordBuilder();
|
||||||
}
|
}
|
||||||
|
|
||||||
// VisibleForTesting
|
// VisibleForTesting
|
||||||
|
|
|
@ -11,7 +11,10 @@ import io.opentelemetry.api.logs.LoggerBuilder;
|
||||||
import io.opentelemetry.api.logs.LoggerProvider;
|
import io.opentelemetry.api.logs.LoggerProvider;
|
||||||
import io.opentelemetry.sdk.common.Clock;
|
import io.opentelemetry.sdk.common.Clock;
|
||||||
import io.opentelemetry.sdk.common.CompletableResultCode;
|
import io.opentelemetry.sdk.common.CompletableResultCode;
|
||||||
|
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
|
||||||
import io.opentelemetry.sdk.internal.ComponentRegistry;
|
import io.opentelemetry.sdk.internal.ComponentRegistry;
|
||||||
|
import io.opentelemetry.sdk.internal.ScopeConfigurator;
|
||||||
|
import io.opentelemetry.sdk.logs.internal.LoggerConfig;
|
||||||
import io.opentelemetry.sdk.resources.Resource;
|
import io.opentelemetry.sdk.resources.Resource;
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -33,6 +36,7 @@ public final class SdkLoggerProvider implements LoggerProvider, Closeable {
|
||||||
|
|
||||||
private final LoggerSharedState sharedState;
|
private final LoggerSharedState sharedState;
|
||||||
private final ComponentRegistry<SdkLogger> loggerComponentRegistry;
|
private final ComponentRegistry<SdkLogger> loggerComponentRegistry;
|
||||||
|
private final ScopeConfigurator<LoggerConfig> loggerConfigurator;
|
||||||
private final boolean isNoopLogRecordProcessor;
|
private final boolean isNoopLogRecordProcessor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -48,16 +52,27 @@ public final class SdkLoggerProvider implements LoggerProvider, Closeable {
|
||||||
Resource resource,
|
Resource resource,
|
||||||
Supplier<LogLimits> logLimitsSupplier,
|
Supplier<LogLimits> logLimitsSupplier,
|
||||||
List<LogRecordProcessor> processors,
|
List<LogRecordProcessor> processors,
|
||||||
Clock clock) {
|
Clock clock,
|
||||||
|
ScopeConfigurator<LoggerConfig> loggerConfigurator) {
|
||||||
LogRecordProcessor logRecordProcessor = LogRecordProcessor.composite(processors);
|
LogRecordProcessor logRecordProcessor = LogRecordProcessor.composite(processors);
|
||||||
this.sharedState =
|
this.sharedState =
|
||||||
new LoggerSharedState(resource, logLimitsSupplier, logRecordProcessor, clock);
|
new LoggerSharedState(resource, logLimitsSupplier, logRecordProcessor, clock);
|
||||||
this.loggerComponentRegistry =
|
this.loggerComponentRegistry =
|
||||||
new ComponentRegistry<>(
|
new ComponentRegistry<>(
|
||||||
instrumentationScopeInfo -> new SdkLogger(sharedState, instrumentationScopeInfo));
|
instrumentationScopeInfo ->
|
||||||
|
new SdkLogger(
|
||||||
|
sharedState,
|
||||||
|
instrumentationScopeInfo,
|
||||||
|
getLoggerConfig(instrumentationScopeInfo)));
|
||||||
|
this.loggerConfigurator = loggerConfigurator;
|
||||||
this.isNoopLogRecordProcessor = logRecordProcessor instanceof NoopLogRecordProcessor;
|
this.isNoopLogRecordProcessor = logRecordProcessor instanceof NoopLogRecordProcessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private LoggerConfig getLoggerConfig(InstrumentationScopeInfo instrumentationScopeInfo) {
|
||||||
|
LoggerConfig loggerConfig = loggerConfigurator.apply(instrumentationScopeInfo);
|
||||||
|
return loggerConfig == null ? LoggerConfig.defaultConfig() : loggerConfig;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Logger get(String instrumentationScopeName) {
|
public Logger get(String instrumentationScopeName) {
|
||||||
return loggerComponentRegistry.get(
|
return loggerComponentRegistry.get(
|
||||||
|
|
|
@ -11,11 +11,16 @@ import io.opentelemetry.api.logs.LogRecordBuilder;
|
||||||
import io.opentelemetry.api.logs.Logger;
|
import io.opentelemetry.api.logs.Logger;
|
||||||
import io.opentelemetry.context.Context;
|
import io.opentelemetry.context.Context;
|
||||||
import io.opentelemetry.sdk.common.Clock;
|
import io.opentelemetry.sdk.common.Clock;
|
||||||
|
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
|
||||||
|
import io.opentelemetry.sdk.internal.ScopeConfigurator;
|
||||||
|
import io.opentelemetry.sdk.internal.ScopeConfiguratorBuilder;
|
||||||
import io.opentelemetry.sdk.logs.data.LogRecordData;
|
import io.opentelemetry.sdk.logs.data.LogRecordData;
|
||||||
|
import io.opentelemetry.sdk.logs.internal.LoggerConfig;
|
||||||
import io.opentelemetry.sdk.resources.Resource;
|
import io.opentelemetry.sdk.resources.Resource;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.function.Predicate;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,6 +34,8 @@ public final class SdkLoggerProviderBuilder {
|
||||||
private Resource resource = Resource.getDefault();
|
private Resource resource = Resource.getDefault();
|
||||||
private Supplier<LogLimits> logLimitsSupplier = LogLimits::getDefault;
|
private Supplier<LogLimits> logLimitsSupplier = LogLimits::getDefault;
|
||||||
private Clock clock = Clock.getDefault();
|
private Clock clock = Clock.getDefault();
|
||||||
|
private ScopeConfiguratorBuilder<LoggerConfig> loggerConfiguratorBuilder =
|
||||||
|
LoggerConfig.configuratorBuilder();
|
||||||
|
|
||||||
SdkLoggerProviderBuilder() {}
|
SdkLoggerProviderBuilder() {}
|
||||||
|
|
||||||
|
@ -100,12 +107,47 @@ public final class SdkLoggerProviderBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the logger configurator, which computes {@link LoggerConfig} for each {@link
|
||||||
|
* InstrumentationScopeInfo}.
|
||||||
|
*
|
||||||
|
* <p>Overrides any matchers added via {@link #addLoggerConfiguratorCondition(Predicate,
|
||||||
|
* LoggerConfig)}.
|
||||||
|
*
|
||||||
|
* @see LoggerConfig#configuratorBuilder()
|
||||||
|
*/
|
||||||
|
SdkLoggerProviderBuilder setLoggerConfigurator(
|
||||||
|
ScopeConfigurator<LoggerConfig> loggerConfigurator) {
|
||||||
|
this.loggerConfiguratorBuilder = loggerConfigurator.toBuilder();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a condition to the logger configurator, which computes {@link LoggerConfig} for each
|
||||||
|
* {@link InstrumentationScopeInfo}.
|
||||||
|
*
|
||||||
|
* <p>Applies after any previously added conditions.
|
||||||
|
*
|
||||||
|
* <p>If {@link #setLoggerConfigurator(ScopeConfigurator)} was previously called, this condition
|
||||||
|
* will only be applied if the {@link ScopeConfigurator#apply(Object)} returns null for the
|
||||||
|
* matched {@link InstrumentationScopeInfo}(s).
|
||||||
|
*
|
||||||
|
* @see ScopeConfiguratorBuilder#nameEquals(String)
|
||||||
|
* @see ScopeConfiguratorBuilder#nameMatchesGlob(String)
|
||||||
|
*/
|
||||||
|
SdkLoggerProviderBuilder addLoggerConfiguratorCondition(
|
||||||
|
Predicate<InstrumentationScopeInfo> scopeMatcher, LoggerConfig loggerConfig) {
|
||||||
|
this.loggerConfiguratorBuilder.addCondition(scopeMatcher, loggerConfig);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a {@link SdkLoggerProvider} instance.
|
* Create a {@link SdkLoggerProvider} instance.
|
||||||
*
|
*
|
||||||
* @return an instance configured with the provided options
|
* @return an instance configured with the provided options
|
||||||
*/
|
*/
|
||||||
public SdkLoggerProvider build() {
|
public SdkLoggerProvider build() {
|
||||||
return new SdkLoggerProvider(resource, logLimitsSupplier, logRecordProcessors, clock);
|
return new SdkLoggerProvider(
|
||||||
|
resource, logLimitsSupplier, logRecordProcessors, clock, loggerConfiguratorBuilder.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.sdk.logs.internal;
|
||||||
|
|
||||||
|
import com.google.auto.value.AutoValue;
|
||||||
|
import io.opentelemetry.api.logs.Logger;
|
||||||
|
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
|
||||||
|
import io.opentelemetry.sdk.internal.ScopeConfigurator;
|
||||||
|
import io.opentelemetry.sdk.internal.ScopeConfiguratorBuilder;
|
||||||
|
import io.opentelemetry.sdk.logs.SdkLoggerProviderBuilder;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A collection of configuration options which define the behavior of a {@link Logger}.
|
||||||
|
*
|
||||||
|
* @see SdkLoggerProviderUtil#setLoggerConfigurator(SdkLoggerProviderBuilder, ScopeConfigurator)
|
||||||
|
* @see SdkLoggerProviderUtil#addLoggerConfiguratorCondition(SdkLoggerProviderBuilder, Predicate,
|
||||||
|
* LoggerConfig)
|
||||||
|
*/
|
||||||
|
@AutoValue
|
||||||
|
@Immutable
|
||||||
|
public abstract class LoggerConfig {
|
||||||
|
|
||||||
|
private static final LoggerConfig DEFAULT_CONFIG =
|
||||||
|
new AutoValue_LoggerConfig(/* enabled= */ true);
|
||||||
|
private static final LoggerConfig DISABLED_CONFIG =
|
||||||
|
new AutoValue_LoggerConfig(/* enabled= */ false);
|
||||||
|
|
||||||
|
/** Returns a disabled {@link LoggerConfig}. */
|
||||||
|
public static LoggerConfig disabled() {
|
||||||
|
return DISABLED_CONFIG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns an enabled {@link LoggerConfig}. */
|
||||||
|
public static LoggerConfig enabled() {
|
||||||
|
return DEFAULT_CONFIG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default {@link LoggerConfig}, which is used when no configurator is set or when the
|
||||||
|
* logger configurator returns {@code null} for a {@link InstrumentationScopeInfo}.
|
||||||
|
*/
|
||||||
|
public static LoggerConfig defaultConfig() {
|
||||||
|
return DEFAULT_CONFIG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a {@link ScopeConfiguratorBuilder} for configuring {@link
|
||||||
|
* SdkLoggerProviderUtil#setLoggerConfigurator(SdkLoggerProviderBuilder, ScopeConfigurator)}.
|
||||||
|
*/
|
||||||
|
public static ScopeConfiguratorBuilder<LoggerConfig> configuratorBuilder() {
|
||||||
|
return ScopeConfigurator.builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
LoggerConfig() {}
|
||||||
|
|
||||||
|
/** Returns {@code true} if this logger is enabled. Defaults to {@code true}. */
|
||||||
|
public abstract boolean isEnabled();
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.sdk.logs.internal;
|
||||||
|
|
||||||
|
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
|
||||||
|
import io.opentelemetry.sdk.internal.ScopeConfigurator;
|
||||||
|
import io.opentelemetry.sdk.logs.SdkLoggerProviderBuilder;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A collection of methods that allow use of experimental features prior to availability in public
|
||||||
|
* APIs.
|
||||||
|
*
|
||||||
|
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
|
||||||
|
* at any time.
|
||||||
|
*/
|
||||||
|
public final class SdkLoggerProviderUtil {
|
||||||
|
|
||||||
|
private SdkLoggerProviderUtil() {}
|
||||||
|
|
||||||
|
/** Reflectively set the {@link ScopeConfigurator} to the {@link SdkLoggerProviderBuilder}. */
|
||||||
|
public static void setLoggerConfigurator(
|
||||||
|
SdkLoggerProviderBuilder sdkLoggerProviderBuilder,
|
||||||
|
ScopeConfigurator<LoggerConfig> loggerConfigurator) {
|
||||||
|
try {
|
||||||
|
Method method =
|
||||||
|
SdkLoggerProviderBuilder.class.getDeclaredMethod(
|
||||||
|
"setLoggerConfigurator", ScopeConfigurator.class);
|
||||||
|
method.setAccessible(true);
|
||||||
|
method.invoke(sdkLoggerProviderBuilder, loggerConfigurator);
|
||||||
|
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Error calling setLoggerConfigurator on SdkLoggerProviderBuilder", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Reflectively add a logger configurator condition to the {@link SdkLoggerProviderBuilder}. */
|
||||||
|
public static void addLoggerConfiguratorCondition(
|
||||||
|
SdkLoggerProviderBuilder sdkLoggerProviderBuilder,
|
||||||
|
Predicate<InstrumentationScopeInfo> scopeMatcher,
|
||||||
|
LoggerConfig loggerConfig) {
|
||||||
|
try {
|
||||||
|
Method method =
|
||||||
|
SdkLoggerProviderBuilder.class.getDeclaredMethod(
|
||||||
|
"addLoggerConfiguratorCondition", Predicate.class, LoggerConfig.class);
|
||||||
|
method.setAccessible(true);
|
||||||
|
method.invoke(sdkLoggerProviderBuilder, scopeMatcher, loggerConfig);
|
||||||
|
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Error calling addLoggerConfiguratorCondition on SdkLoggerProviderBuilder", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.sdk.logs;
|
||||||
|
|
||||||
|
import static io.opentelemetry.sdk.internal.ScopeConfiguratorBuilder.nameEquals;
|
||||||
|
import static io.opentelemetry.sdk.internal.ScopeConfiguratorBuilder.nameMatchesGlob;
|
||||||
|
import static io.opentelemetry.sdk.logs.internal.LoggerConfig.defaultConfig;
|
||||||
|
import static io.opentelemetry.sdk.logs.internal.LoggerConfig.enabled;
|
||||||
|
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
|
||||||
|
|
||||||
|
import io.opentelemetry.api.logs.Logger;
|
||||||
|
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
|
||||||
|
import io.opentelemetry.sdk.internal.ScopeConfigurator;
|
||||||
|
import io.opentelemetry.sdk.logs.data.LogRecordData;
|
||||||
|
import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor;
|
||||||
|
import io.opentelemetry.sdk.logs.internal.LoggerConfig;
|
||||||
|
import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
|
||||||
|
class LoggerConfigTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void disableScopes() {
|
||||||
|
InMemoryLogRecordExporter exporter = InMemoryLogRecordExporter.create();
|
||||||
|
SdkLoggerProvider loggerProvider =
|
||||||
|
SdkLoggerProvider.builder()
|
||||||
|
// Disable loggerB. Since loggers are enabled by default, loggerA and loggerC are
|
||||||
|
// enabled.
|
||||||
|
.addLoggerConfiguratorCondition(nameEquals("loggerB"), LoggerConfig.disabled())
|
||||||
|
.addLogRecordProcessor(SimpleLogRecordProcessor.create(exporter))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Logger loggerA = loggerProvider.get("loggerA");
|
||||||
|
Logger loggerB = loggerProvider.get("loggerB");
|
||||||
|
Logger loggerC = loggerProvider.get("loggerC");
|
||||||
|
|
||||||
|
loggerA.logRecordBuilder().setBody("messageA").emit();
|
||||||
|
loggerB.logRecordBuilder().setBody("messageB").emit();
|
||||||
|
loggerC.logRecordBuilder().setBody("messageC").emit();
|
||||||
|
|
||||||
|
// Only logs from loggerA and loggerC should be seen
|
||||||
|
assertThat(exporter.getFinishedLogRecordItems())
|
||||||
|
.satisfies(
|
||||||
|
metrics -> {
|
||||||
|
Map<InstrumentationScopeInfo, List<LogRecordData>> logsByScope =
|
||||||
|
metrics.stream()
|
||||||
|
.collect(Collectors.groupingBy(LogRecordData::getInstrumentationScopeInfo));
|
||||||
|
assertThat(logsByScope.get(InstrumentationScopeInfo.create("loggerA"))).hasSize(1);
|
||||||
|
assertThat(logsByScope.get(InstrumentationScopeInfo.create("loggerB"))).isNull();
|
||||||
|
assertThat(logsByScope.get(InstrumentationScopeInfo.create("loggerC"))).hasSize(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("loggerConfiguratorArgs")
|
||||||
|
void loggerConfigurator(
|
||||||
|
ScopeConfigurator<LoggerConfig> loggerConfigurator,
|
||||||
|
InstrumentationScopeInfo scope,
|
||||||
|
LoggerConfig expectedLoggerConfig) {
|
||||||
|
LoggerConfig loggerConfig = loggerConfigurator.apply(scope);
|
||||||
|
loggerConfig = loggerConfig == null ? defaultConfig() : loggerConfig;
|
||||||
|
assertThat(loggerConfig).isEqualTo(expectedLoggerConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final InstrumentationScopeInfo scopeCat = InstrumentationScopeInfo.create("cat");
|
||||||
|
private static final InstrumentationScopeInfo scopeDog = InstrumentationScopeInfo.create("dog");
|
||||||
|
private static final InstrumentationScopeInfo scopeDuck = InstrumentationScopeInfo.create("duck");
|
||||||
|
|
||||||
|
private static Stream<Arguments> loggerConfiguratorArgs() {
|
||||||
|
ScopeConfigurator<LoggerConfig> defaultConfigurator =
|
||||||
|
LoggerConfig.configuratorBuilder().build();
|
||||||
|
ScopeConfigurator<LoggerConfig> disableCat =
|
||||||
|
LoggerConfig.configuratorBuilder()
|
||||||
|
.addCondition(nameEquals("cat"), LoggerConfig.disabled())
|
||||||
|
// Second matching rule for cat should be ignored
|
||||||
|
.addCondition(nameEquals("cat"), enabled())
|
||||||
|
.build();
|
||||||
|
ScopeConfigurator<LoggerConfig> disableStartsWithD =
|
||||||
|
LoggerConfig.configuratorBuilder()
|
||||||
|
.addCondition(nameMatchesGlob("d*"), LoggerConfig.disabled())
|
||||||
|
.build();
|
||||||
|
ScopeConfigurator<LoggerConfig> enableCat =
|
||||||
|
LoggerConfig.configuratorBuilder()
|
||||||
|
.setDefault(LoggerConfig.disabled())
|
||||||
|
.addCondition(nameEquals("cat"), enabled())
|
||||||
|
// Second matching rule for cat should be ignored
|
||||||
|
.addCondition(nameEquals("cat"), LoggerConfig.disabled())
|
||||||
|
.build();
|
||||||
|
ScopeConfigurator<LoggerConfig> enableStartsWithD =
|
||||||
|
LoggerConfig.configuratorBuilder()
|
||||||
|
.setDefault(LoggerConfig.disabled())
|
||||||
|
.addCondition(nameMatchesGlob("d*"), LoggerConfig.enabled())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return Stream.of(
|
||||||
|
// default
|
||||||
|
Arguments.of(defaultConfigurator, scopeCat, defaultConfig()),
|
||||||
|
Arguments.of(defaultConfigurator, scopeDog, defaultConfig()),
|
||||||
|
Arguments.of(defaultConfigurator, scopeDuck, defaultConfig()),
|
||||||
|
// default enabled, disable cat
|
||||||
|
Arguments.of(disableCat, scopeCat, LoggerConfig.disabled()),
|
||||||
|
Arguments.of(disableCat, scopeDog, enabled()),
|
||||||
|
Arguments.of(disableCat, scopeDuck, enabled()),
|
||||||
|
// default enabled, disable pattern
|
||||||
|
Arguments.of(disableStartsWithD, scopeCat, enabled()),
|
||||||
|
Arguments.of(disableStartsWithD, scopeDog, LoggerConfig.disabled()),
|
||||||
|
Arguments.of(disableStartsWithD, scopeDuck, LoggerConfig.disabled()),
|
||||||
|
// default disabled, enable cat
|
||||||
|
Arguments.of(enableCat, scopeCat, enabled()),
|
||||||
|
Arguments.of(enableCat, scopeDog, LoggerConfig.disabled()),
|
||||||
|
Arguments.of(enableCat, scopeDuck, LoggerConfig.disabled()),
|
||||||
|
// default disabled, enable pattern
|
||||||
|
Arguments.of(enableStartsWithD, scopeCat, LoggerConfig.disabled()),
|
||||||
|
Arguments.of(enableStartsWithD, scopeDog, enabled()),
|
||||||
|
Arguments.of(enableStartsWithD, scopeDuck, enabled()));
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,6 +24,7 @@ import io.opentelemetry.api.logs.LogRecordBuilder;
|
||||||
import io.opentelemetry.sdk.common.Clock;
|
import io.opentelemetry.sdk.common.Clock;
|
||||||
import io.opentelemetry.sdk.common.CompletableResultCode;
|
import io.opentelemetry.sdk.common.CompletableResultCode;
|
||||||
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
|
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
|
||||||
|
import io.opentelemetry.sdk.logs.internal.LoggerConfig;
|
||||||
import io.opentelemetry.sdk.resources.Resource;
|
import io.opentelemetry.sdk.resources.Resource;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
@ -44,7 +45,7 @@ class SdkLoggerTest {
|
||||||
when(state.getLogRecordProcessor()).thenReturn(logRecordProcessor);
|
when(state.getLogRecordProcessor()).thenReturn(logRecordProcessor);
|
||||||
when(state.getClock()).thenReturn(clock);
|
when(state.getClock()).thenReturn(clock);
|
||||||
|
|
||||||
SdkLogger logger = new SdkLogger(state, info);
|
SdkLogger logger = new SdkLogger(state, info, LoggerConfig.defaultConfig());
|
||||||
LogRecordBuilder logRecordBuilder = logger.logRecordBuilder();
|
LogRecordBuilder logRecordBuilder = logger.logRecordBuilder();
|
||||||
logRecordBuilder.setBody("foo");
|
logRecordBuilder.setBody("foo");
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ import io.opentelemetry.api.metrics.MeterProvider;
|
||||||
import io.opentelemetry.api.metrics.ObservableMeasurement;
|
import io.opentelemetry.api.metrics.ObservableMeasurement;
|
||||||
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
|
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
|
||||||
import io.opentelemetry.sdk.metrics.data.MetricData;
|
import io.opentelemetry.sdk.metrics.data.MetricData;
|
||||||
|
import io.opentelemetry.sdk.metrics.internal.MeterConfig;
|
||||||
import io.opentelemetry.sdk.metrics.internal.export.RegisteredReader;
|
import io.opentelemetry.sdk.metrics.internal.export.RegisteredReader;
|
||||||
import io.opentelemetry.sdk.metrics.internal.state.CallbackRegistration;
|
import io.opentelemetry.sdk.metrics.internal.state.CallbackRegistration;
|
||||||
import io.opentelemetry.sdk.metrics.internal.state.MeterProviderSharedState;
|
import io.opentelemetry.sdk.metrics.internal.state.MeterProviderSharedState;
|
||||||
|
@ -55,14 +56,17 @@ final class SdkMeter implements Meter {
|
||||||
private final InstrumentationScopeInfo instrumentationScopeInfo;
|
private final InstrumentationScopeInfo instrumentationScopeInfo;
|
||||||
private final MeterProviderSharedState meterProviderSharedState;
|
private final MeterProviderSharedState meterProviderSharedState;
|
||||||
private final MeterSharedState meterSharedState;
|
private final MeterSharedState meterSharedState;
|
||||||
|
private final MeterConfig meterConfig;
|
||||||
|
|
||||||
SdkMeter(
|
SdkMeter(
|
||||||
MeterProviderSharedState meterProviderSharedState,
|
MeterProviderSharedState meterProviderSharedState,
|
||||||
InstrumentationScopeInfo instrumentationScopeInfo,
|
InstrumentationScopeInfo instrumentationScopeInfo,
|
||||||
List<RegisteredReader> registeredReaders) {
|
List<RegisteredReader> registeredReaders,
|
||||||
|
MeterConfig meterConfig) {
|
||||||
this.instrumentationScopeInfo = instrumentationScopeInfo;
|
this.instrumentationScopeInfo = instrumentationScopeInfo;
|
||||||
this.meterProviderSharedState = meterProviderSharedState;
|
this.meterProviderSharedState = meterProviderSharedState;
|
||||||
this.meterSharedState = MeterSharedState.create(instrumentationScopeInfo, registeredReaders);
|
this.meterSharedState = MeterSharedState.create(instrumentationScopeInfo, registeredReaders);
|
||||||
|
this.meterConfig = meterConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Visible for testing
|
// Visible for testing
|
||||||
|
@ -82,34 +86,32 @@ final class SdkMeter implements Meter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LongCounterBuilder counterBuilder(String name) {
|
public LongCounterBuilder counterBuilder(String name) {
|
||||||
return !checkValidInstrumentName(name)
|
return meterConfig.isEnabled() && checkValidInstrumentName(name)
|
||||||
? NOOP_METER.counterBuilder(NOOP_INSTRUMENT_NAME)
|
? new SdkLongCounter.SdkLongCounterBuilder(meterProviderSharedState, meterSharedState, name)
|
||||||
: new SdkLongCounter.SdkLongCounterBuilder(
|
: NOOP_METER.counterBuilder(NOOP_INSTRUMENT_NAME);
|
||||||
meterProviderSharedState, meterSharedState, name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LongUpDownCounterBuilder upDownCounterBuilder(String name) {
|
public LongUpDownCounterBuilder upDownCounterBuilder(String name) {
|
||||||
return !checkValidInstrumentName(name)
|
return meterConfig.isEnabled() && checkValidInstrumentName(name)
|
||||||
? NOOP_METER.upDownCounterBuilder(NOOP_INSTRUMENT_NAME)
|
? new SdkLongUpDownCounter.SdkLongUpDownCounterBuilder(
|
||||||
: new SdkLongUpDownCounter.SdkLongUpDownCounterBuilder(
|
meterProviderSharedState, meterSharedState, name)
|
||||||
meterProviderSharedState, meterSharedState, name);
|
: NOOP_METER.upDownCounterBuilder(NOOP_INSTRUMENT_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DoubleHistogramBuilder histogramBuilder(String name) {
|
public DoubleHistogramBuilder histogramBuilder(String name) {
|
||||||
return !checkValidInstrumentName(name)
|
return meterConfig.isEnabled() && checkValidInstrumentName(name)
|
||||||
? NOOP_METER.histogramBuilder(NOOP_INSTRUMENT_NAME)
|
? new SdkDoubleHistogram.SdkDoubleHistogramBuilder(
|
||||||
: new SdkDoubleHistogram.SdkDoubleHistogramBuilder(
|
meterProviderSharedState, meterSharedState, name)
|
||||||
meterProviderSharedState, meterSharedState, name);
|
: NOOP_METER.histogramBuilder(NOOP_INSTRUMENT_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DoubleGaugeBuilder gaugeBuilder(String name) {
|
public DoubleGaugeBuilder gaugeBuilder(String name) {
|
||||||
return !checkValidInstrumentName(name)
|
return meterConfig.isEnabled() && checkValidInstrumentName(name)
|
||||||
? NOOP_METER.gaugeBuilder(NOOP_INSTRUMENT_NAME)
|
? new SdkDoubleGauge.SdkDoubleGaugeBuilder(meterProviderSharedState, meterSharedState, name)
|
||||||
: new SdkDoubleGauge.SdkDoubleGaugeBuilder(
|
: NOOP_METER.gaugeBuilder(NOOP_INSTRUMENT_NAME);
|
||||||
meterProviderSharedState, meterSharedState, name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -117,6 +119,9 @@ final class SdkMeter implements Meter {
|
||||||
Runnable callback,
|
Runnable callback,
|
||||||
ObservableMeasurement observableMeasurement,
|
ObservableMeasurement observableMeasurement,
|
||||||
ObservableMeasurement... additionalMeasurements) {
|
ObservableMeasurement... additionalMeasurements) {
|
||||||
|
if (!meterConfig.isEnabled()) {
|
||||||
|
return NOOP_METER.batchCallback(callback, observableMeasurement, additionalMeasurements);
|
||||||
|
}
|
||||||
Set<ObservableMeasurement> measurements = new HashSet<>();
|
Set<ObservableMeasurement> measurements = new HashSet<>();
|
||||||
measurements.add(observableMeasurement);
|
measurements.add(observableMeasurement);
|
||||||
Collections.addAll(measurements, additionalMeasurements);
|
Collections.addAll(measurements, additionalMeasurements);
|
||||||
|
|
|
@ -11,11 +11,14 @@ import io.opentelemetry.api.metrics.MeterBuilder;
|
||||||
import io.opentelemetry.api.metrics.MeterProvider;
|
import io.opentelemetry.api.metrics.MeterProvider;
|
||||||
import io.opentelemetry.sdk.common.Clock;
|
import io.opentelemetry.sdk.common.Clock;
|
||||||
import io.opentelemetry.sdk.common.CompletableResultCode;
|
import io.opentelemetry.sdk.common.CompletableResultCode;
|
||||||
|
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
|
||||||
import io.opentelemetry.sdk.internal.ComponentRegistry;
|
import io.opentelemetry.sdk.internal.ComponentRegistry;
|
||||||
|
import io.opentelemetry.sdk.internal.ScopeConfigurator;
|
||||||
import io.opentelemetry.sdk.metrics.data.MetricData;
|
import io.opentelemetry.sdk.metrics.data.MetricData;
|
||||||
import io.opentelemetry.sdk.metrics.export.CollectionRegistration;
|
import io.opentelemetry.sdk.metrics.export.CollectionRegistration;
|
||||||
import io.opentelemetry.sdk.metrics.export.MetricProducer;
|
import io.opentelemetry.sdk.metrics.export.MetricProducer;
|
||||||
import io.opentelemetry.sdk.metrics.export.MetricReader;
|
import io.opentelemetry.sdk.metrics.export.MetricReader;
|
||||||
|
import io.opentelemetry.sdk.metrics.internal.MeterConfig;
|
||||||
import io.opentelemetry.sdk.metrics.internal.SdkMeterProviderUtil;
|
import io.opentelemetry.sdk.metrics.internal.SdkMeterProviderUtil;
|
||||||
import io.opentelemetry.sdk.metrics.internal.exemplar.ExemplarFilter;
|
import io.opentelemetry.sdk.metrics.internal.exemplar.ExemplarFilter;
|
||||||
import io.opentelemetry.sdk.metrics.internal.export.CardinalityLimitSelector;
|
import io.opentelemetry.sdk.metrics.internal.export.CardinalityLimitSelector;
|
||||||
|
@ -49,6 +52,7 @@ public final class SdkMeterProvider implements MeterProvider, Closeable {
|
||||||
private final List<MetricProducer> metricProducers;
|
private final List<MetricProducer> metricProducers;
|
||||||
private final MeterProviderSharedState sharedState;
|
private final MeterProviderSharedState sharedState;
|
||||||
private final ComponentRegistry<SdkMeter> registry;
|
private final ComponentRegistry<SdkMeter> registry;
|
||||||
|
private final ScopeConfigurator<MeterConfig> meterConfigurator;
|
||||||
private final AtomicBoolean isClosed = new AtomicBoolean(false);
|
private final AtomicBoolean isClosed = new AtomicBoolean(false);
|
||||||
|
|
||||||
/** Returns a new {@link SdkMeterProviderBuilder} for {@link SdkMeterProvider}. */
|
/** Returns a new {@link SdkMeterProviderBuilder} for {@link SdkMeterProvider}. */
|
||||||
|
@ -62,7 +66,8 @@ public final class SdkMeterProvider implements MeterProvider, Closeable {
|
||||||
List<MetricProducer> metricProducers,
|
List<MetricProducer> metricProducers,
|
||||||
Clock clock,
|
Clock clock,
|
||||||
Resource resource,
|
Resource resource,
|
||||||
ExemplarFilter exemplarFilter) {
|
ExemplarFilter exemplarFilter,
|
||||||
|
ScopeConfigurator<MeterConfig> meterConfigurator) {
|
||||||
long startEpochNanos = clock.now();
|
long startEpochNanos = clock.now();
|
||||||
this.registeredViews = registeredViews;
|
this.registeredViews = registeredViews;
|
||||||
this.registeredReaders =
|
this.registeredReaders =
|
||||||
|
@ -79,7 +84,12 @@ public final class SdkMeterProvider implements MeterProvider, Closeable {
|
||||||
this.registry =
|
this.registry =
|
||||||
new ComponentRegistry<>(
|
new ComponentRegistry<>(
|
||||||
instrumentationLibraryInfo ->
|
instrumentationLibraryInfo ->
|
||||||
new SdkMeter(sharedState, instrumentationLibraryInfo, registeredReaders));
|
new SdkMeter(
|
||||||
|
sharedState,
|
||||||
|
instrumentationLibraryInfo,
|
||||||
|
registeredReaders,
|
||||||
|
getMeterConfig(instrumentationLibraryInfo)));
|
||||||
|
this.meterConfigurator = meterConfigurator;
|
||||||
for (RegisteredReader registeredReader : registeredReaders) {
|
for (RegisteredReader registeredReader : registeredReaders) {
|
||||||
List<MetricProducer> readerMetricProducers = new ArrayList<>(metricProducers);
|
List<MetricProducer> readerMetricProducers = new ArrayList<>(metricProducers);
|
||||||
readerMetricProducers.add(new LeasedMetricProducer(registry, sharedState, registeredReader));
|
readerMetricProducers.add(new LeasedMetricProducer(registry, sharedState, registeredReader));
|
||||||
|
@ -90,6 +100,11 @@ public final class SdkMeterProvider implements MeterProvider, Closeable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MeterConfig getMeterConfig(InstrumentationScopeInfo instrumentationScopeInfo) {
|
||||||
|
MeterConfig meterConfig = meterConfigurator.apply(instrumentationScopeInfo);
|
||||||
|
return meterConfig == null ? MeterConfig.defaultConfig() : meterConfig;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MeterBuilder meterBuilder(String instrumentationScopeName) {
|
public MeterBuilder meterBuilder(String instrumentationScopeName) {
|
||||||
if (registeredReaders.isEmpty()) {
|
if (registeredReaders.isEmpty()) {
|
||||||
|
|
|
@ -6,8 +6,12 @@
|
||||||
package io.opentelemetry.sdk.metrics;
|
package io.opentelemetry.sdk.metrics;
|
||||||
|
|
||||||
import io.opentelemetry.sdk.common.Clock;
|
import io.opentelemetry.sdk.common.Clock;
|
||||||
|
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
|
||||||
|
import io.opentelemetry.sdk.internal.ScopeConfigurator;
|
||||||
|
import io.opentelemetry.sdk.internal.ScopeConfiguratorBuilder;
|
||||||
import io.opentelemetry.sdk.metrics.export.MetricProducer;
|
import io.opentelemetry.sdk.metrics.export.MetricProducer;
|
||||||
import io.opentelemetry.sdk.metrics.export.MetricReader;
|
import io.opentelemetry.sdk.metrics.export.MetricReader;
|
||||||
|
import io.opentelemetry.sdk.metrics.internal.MeterConfig;
|
||||||
import io.opentelemetry.sdk.metrics.internal.SdkMeterProviderUtil;
|
import io.opentelemetry.sdk.metrics.internal.SdkMeterProviderUtil;
|
||||||
import io.opentelemetry.sdk.metrics.internal.debug.SourceInfo;
|
import io.opentelemetry.sdk.metrics.internal.debug.SourceInfo;
|
||||||
import io.opentelemetry.sdk.metrics.internal.exemplar.ExemplarFilter;
|
import io.opentelemetry.sdk.metrics.internal.exemplar.ExemplarFilter;
|
||||||
|
@ -18,6 +22,7 @@ import java.util.ArrayList;
|
||||||
import java.util.IdentityHashMap;
|
import java.util.IdentityHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builder class for the {@link SdkMeterProvider}.
|
* Builder class for the {@link SdkMeterProvider}.
|
||||||
|
@ -40,6 +45,8 @@ public final class SdkMeterProviderBuilder {
|
||||||
private final List<MetricProducer> metricProducers = new ArrayList<>();
|
private final List<MetricProducer> metricProducers = new ArrayList<>();
|
||||||
private final List<RegisteredView> registeredViews = new ArrayList<>();
|
private final List<RegisteredView> registeredViews = new ArrayList<>();
|
||||||
private ExemplarFilter exemplarFilter = DEFAULT_EXEMPLAR_FILTER;
|
private ExemplarFilter exemplarFilter = DEFAULT_EXEMPLAR_FILTER;
|
||||||
|
private ScopeConfiguratorBuilder<MeterConfig> meterConfiguratorBuilder =
|
||||||
|
MeterConfig.configuratorBuilder();
|
||||||
|
|
||||||
SdkMeterProviderBuilder() {}
|
SdkMeterProviderBuilder() {}
|
||||||
|
|
||||||
|
@ -150,9 +157,48 @@ public final class SdkMeterProviderBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the meter configurator, which computes {@link MeterConfig} for each {@link
|
||||||
|
* InstrumentationScopeInfo}.
|
||||||
|
*
|
||||||
|
* <p>Overrides any matchers added via {@link #addMeterConfiguratorCondition(Predicate,
|
||||||
|
* MeterConfig)}.
|
||||||
|
*
|
||||||
|
* @see MeterConfig#configuratorBuilder()
|
||||||
|
*/
|
||||||
|
SdkMeterProviderBuilder setMeterConfigurator(ScopeConfigurator<MeterConfig> meterConfigurator) {
|
||||||
|
this.meterConfiguratorBuilder = meterConfigurator.toBuilder();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a condition to the meter configurator, which computes {@link MeterConfig} for each {@link
|
||||||
|
* InstrumentationScopeInfo}.
|
||||||
|
*
|
||||||
|
* <p>Applies after any previously added conditions.
|
||||||
|
*
|
||||||
|
* <p>If {@link #setMeterConfigurator(ScopeConfigurator)} was previously called, this condition
|
||||||
|
* will only be applied if the {@link ScopeConfigurator#apply(Object)} returns null for the
|
||||||
|
* matched {@link InstrumentationScopeInfo}(s).
|
||||||
|
*
|
||||||
|
* @see ScopeConfiguratorBuilder#nameEquals(String)
|
||||||
|
* @see ScopeConfiguratorBuilder#nameMatchesGlob(String)
|
||||||
|
*/
|
||||||
|
SdkMeterProviderBuilder addMeterConfiguratorCondition(
|
||||||
|
Predicate<InstrumentationScopeInfo> scopeMatcher, MeterConfig meterConfig) {
|
||||||
|
this.meterConfiguratorBuilder.addCondition(scopeMatcher, meterConfig);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns an {@link SdkMeterProvider} built with the configuration of this builder. */
|
/** Returns an {@link SdkMeterProvider} built with the configuration of this builder. */
|
||||||
public SdkMeterProvider build() {
|
public SdkMeterProvider build() {
|
||||||
return new SdkMeterProvider(
|
return new SdkMeterProvider(
|
||||||
registeredViews, metricReaders, metricProducers, clock, resource, exemplarFilter);
|
registeredViews,
|
||||||
|
metricReaders,
|
||||||
|
metricProducers,
|
||||||
|
clock,
|
||||||
|
resource,
|
||||||
|
exemplarFilter,
|
||||||
|
meterConfiguratorBuilder.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.sdk.metrics.internal;
|
||||||
|
|
||||||
|
import com.google.auto.value.AutoValue;
|
||||||
|
import io.opentelemetry.api.metrics.Meter;
|
||||||
|
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
|
||||||
|
import io.opentelemetry.sdk.internal.ScopeConfigurator;
|
||||||
|
import io.opentelemetry.sdk.internal.ScopeConfiguratorBuilder;
|
||||||
|
import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A collection of configuration options which define the behavior of a {@link Meter}.
|
||||||
|
*
|
||||||
|
* @see SdkMeterProviderUtil#setMeterConfigurator(SdkMeterProviderBuilder, ScopeConfigurator)
|
||||||
|
* @see SdkMeterProviderUtil#addMeterConfiguratorCondition(SdkMeterProviderBuilder, Predicate,
|
||||||
|
* MeterConfig)
|
||||||
|
*/
|
||||||
|
@AutoValue
|
||||||
|
@Immutable
|
||||||
|
public abstract class MeterConfig {
|
||||||
|
|
||||||
|
private static final MeterConfig DEFAULT_CONFIG = new AutoValue_MeterConfig(/* enabled= */ true);
|
||||||
|
private static final MeterConfig DISABLED_CONFIG =
|
||||||
|
new AutoValue_MeterConfig(/* enabled= */ false);
|
||||||
|
|
||||||
|
/** Returns a disabled {@link MeterConfig}. */
|
||||||
|
public static MeterConfig disabled() {
|
||||||
|
return DISABLED_CONFIG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns an enabled {@link MeterConfig}. */
|
||||||
|
public static MeterConfig enabled() {
|
||||||
|
return DEFAULT_CONFIG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default {@link MeterConfig}, which is used when no configurator is set or when the
|
||||||
|
* meter configurator returns {@code null} for a {@link InstrumentationScopeInfo}.
|
||||||
|
*/
|
||||||
|
public static MeterConfig defaultConfig() {
|
||||||
|
return DEFAULT_CONFIG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a {@link ScopeConfiguratorBuilder} for configuring {@link
|
||||||
|
* SdkMeterProviderUtil#setMeterConfigurator(SdkMeterProviderBuilder, ScopeConfigurator)}.
|
||||||
|
*/
|
||||||
|
public static ScopeConfiguratorBuilder<MeterConfig> configuratorBuilder() {
|
||||||
|
return ScopeConfigurator.builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
MeterConfig() {}
|
||||||
|
|
||||||
|
/** Returns {@code true} if this meter is enabled. Defaults to {@code true}. */
|
||||||
|
public abstract boolean isEnabled();
|
||||||
|
}
|
|
@ -5,6 +5,8 @@
|
||||||
|
|
||||||
package io.opentelemetry.sdk.metrics.internal;
|
package io.opentelemetry.sdk.metrics.internal;
|
||||||
|
|
||||||
|
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
|
||||||
|
import io.opentelemetry.sdk.internal.ScopeConfigurator;
|
||||||
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
|
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
|
||||||
import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
|
import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
|
||||||
import io.opentelemetry.sdk.metrics.ViewBuilder;
|
import io.opentelemetry.sdk.metrics.ViewBuilder;
|
||||||
|
@ -18,8 +20,11 @@ import java.lang.reflect.Method;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
|
* A collection of methods that allow use of experimental features prior to availability in public
|
||||||
* any time.
|
* APIs.
|
||||||
|
*
|
||||||
|
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
|
||||||
|
* at any time.
|
||||||
*/
|
*/
|
||||||
public final class SdkMeterProviderUtil {
|
public final class SdkMeterProviderUtil {
|
||||||
|
|
||||||
|
@ -66,6 +71,39 @@ public final class SdkMeterProviderUtil {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Reflectively set the {@link ScopeConfigurator} to the {@link SdkMeterProviderBuilder}. */
|
||||||
|
public static void setMeterConfigurator(
|
||||||
|
SdkMeterProviderBuilder sdkMeterProviderBuilder,
|
||||||
|
ScopeConfigurator<MeterConfig> meterConfigurator) {
|
||||||
|
try {
|
||||||
|
Method method =
|
||||||
|
SdkMeterProviderBuilder.class.getDeclaredMethod(
|
||||||
|
"setMeterConfigurator", ScopeConfigurator.class);
|
||||||
|
method.setAccessible(true);
|
||||||
|
method.invoke(sdkMeterProviderBuilder, meterConfigurator);
|
||||||
|
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Error calling setMeterConfigurator on SdkMeterProviderBuilder", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Reflectively add a tracer configurator condition to the {@link SdkMeterProviderBuilder}. */
|
||||||
|
public static void addMeterConfiguratorCondition(
|
||||||
|
SdkMeterProviderBuilder sdkMeterProviderBuilder,
|
||||||
|
Predicate<InstrumentationScopeInfo> scopeMatcher,
|
||||||
|
MeterConfig meterConfig) {
|
||||||
|
try {
|
||||||
|
Method method =
|
||||||
|
SdkMeterProviderBuilder.class.getDeclaredMethod(
|
||||||
|
"addMeterConfiguratorCondition", Predicate.class, MeterConfig.class);
|
||||||
|
method.setAccessible(true);
|
||||||
|
method.invoke(sdkMeterProviderBuilder, scopeMatcher, meterConfig);
|
||||||
|
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Error calling addMeterConfiguratorCondition on SdkMeterProviderBuilder", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reflectively add an {@link AttributesProcessor} to the {@link ViewBuilder} which appends
|
* Reflectively add an {@link AttributesProcessor} to the {@link ViewBuilder} which appends
|
||||||
* key-values from baggage to all measurements.
|
* key-values from baggage to all measurements.
|
||||||
|
|
|
@ -21,9 +21,12 @@ import javax.annotation.concurrent.Immutable;
|
||||||
@AutoValue
|
@AutoValue
|
||||||
@Immutable
|
@Immutable
|
||||||
public abstract class MeterProviderSharedState {
|
public abstract class MeterProviderSharedState {
|
||||||
|
|
||||||
public static MeterProviderSharedState create(
|
public static MeterProviderSharedState create(
|
||||||
Clock clock, Resource resource, ExemplarFilter exemplarFilter, long startEpochNanos) {
|
Clock clock, Resource resource, ExemplarFilter exemplarFilter, long startEpochNanos) {
|
||||||
return new AutoValue_MeterProviderSharedState(clock, resource, startEpochNanos, exemplarFilter);
|
MeterProviderSharedState sharedState =
|
||||||
|
new AutoValue_MeterProviderSharedState(clock, resource, startEpochNanos, exemplarFilter);
|
||||||
|
return sharedState;
|
||||||
}
|
}
|
||||||
|
|
||||||
MeterProviderSharedState() {}
|
MeterProviderSharedState() {}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import static io.opentelemetry.sdk.metrics.internal.view.NoopAttributesProcessor
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
|
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
|
||||||
|
import io.opentelemetry.sdk.internal.GlobUtil;
|
||||||
import io.opentelemetry.sdk.metrics.Aggregation;
|
import io.opentelemetry.sdk.metrics.Aggregation;
|
||||||
import io.opentelemetry.sdk.metrics.InstrumentSelector;
|
import io.opentelemetry.sdk.metrics.InstrumentSelector;
|
||||||
import io.opentelemetry.sdk.metrics.InstrumentType;
|
import io.opentelemetry.sdk.metrics.InstrumentType;
|
||||||
|
@ -27,10 +28,8 @@ import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Predicate;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.regex.Pattern;
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -170,7 +169,8 @@ public final class ViewRegistry {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (selector.getInstrumentName() != null
|
if (selector.getInstrumentName() != null
|
||||||
&& !toGlobPatternPredicate(selector.getInstrumentName()).test(descriptor.getName())) {
|
&& !GlobUtil.toGlobPatternPredicate(selector.getInstrumentName())
|
||||||
|
.test(descriptor.getName())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return matchesMeter(selector, meterScope);
|
return matchesMeter(selector, meterScope);
|
||||||
|
@ -190,69 +190,6 @@ public final class ViewRegistry {
|
||||||
|| selector.getMeterSchemaUrl().equals(meterScope.getSchemaUrl());
|
|| selector.getMeterSchemaUrl().equals(meterScope.getSchemaUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a predicate that returns {@code true} if a string matches the {@code globPattern}.
|
|
||||||
*
|
|
||||||
* <p>{@code globPattern} may contain the wildcard characters {@code *} and {@code ?} with the
|
|
||||||
* following matching criteria:
|
|
||||||
*
|
|
||||||
* <ul>
|
|
||||||
* <li>{@code *} matches 0 or more instances of any character
|
|
||||||
* <li>{@code ?} matches exactly one instance of any character
|
|
||||||
* </ul>
|
|
||||||
*/
|
|
||||||
// Visible for testing
|
|
||||||
static Predicate<String> toGlobPatternPredicate(String globPattern) {
|
|
||||||
// Match all
|
|
||||||
if (globPattern.equals("*")) {
|
|
||||||
return unused -> true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If globPattern contains '*' or '?', convert it to a regex and return corresponding predicate
|
|
||||||
for (int i = 0; i < globPattern.length(); i++) {
|
|
||||||
char c = globPattern.charAt(i);
|
|
||||||
if (c == '*' || c == '?') {
|
|
||||||
Pattern pattern = toRegexPattern(globPattern);
|
|
||||||
return string -> pattern.matcher(string).matches();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exact match, ignoring case
|
|
||||||
return globPattern::equalsIgnoreCase;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transform the {@code globPattern} to a regex by converting {@code *} to {@code .*}, {@code ?}
|
|
||||||
* to {@code .}, and escaping other regex special characters.
|
|
||||||
*/
|
|
||||||
private static Pattern toRegexPattern(String globPattern) {
|
|
||||||
int tokenStart = -1;
|
|
||||||
StringBuilder patternBuilder = new StringBuilder();
|
|
||||||
for (int i = 0; i < globPattern.length(); i++) {
|
|
||||||
char c = globPattern.charAt(i);
|
|
||||||
if (c == '*' || c == '?') {
|
|
||||||
if (tokenStart != -1) {
|
|
||||||
patternBuilder.append(Pattern.quote(globPattern.substring(tokenStart, i)));
|
|
||||||
tokenStart = -1;
|
|
||||||
}
|
|
||||||
if (c == '*') {
|
|
||||||
patternBuilder.append(".*");
|
|
||||||
} else {
|
|
||||||
// c == '?'
|
|
||||||
patternBuilder.append(".");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (tokenStart == -1) {
|
|
||||||
tokenStart = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (tokenStart != -1) {
|
|
||||||
patternBuilder.append(Pattern.quote(globPattern.substring(tokenStart)));
|
|
||||||
}
|
|
||||||
return Pattern.compile(patternBuilder.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static RegisteredView applyAdviceToDefaultView(
|
private static RegisteredView applyAdviceToDefaultView(
|
||||||
RegisteredView instrumentDefaultView, Advice advice) {
|
RegisteredView instrumentDefaultView, Advice advice) {
|
||||||
return RegisteredView.create(
|
return RegisteredView.create(
|
||||||
|
|
|
@ -0,0 +1,148 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.sdk.metrics;
|
||||||
|
|
||||||
|
import static io.opentelemetry.sdk.internal.ScopeConfiguratorBuilder.nameEquals;
|
||||||
|
import static io.opentelemetry.sdk.internal.ScopeConfiguratorBuilder.nameMatchesGlob;
|
||||||
|
import static io.opentelemetry.sdk.metrics.internal.MeterConfig.defaultConfig;
|
||||||
|
import static io.opentelemetry.sdk.metrics.internal.MeterConfig.disabled;
|
||||||
|
import static io.opentelemetry.sdk.metrics.internal.MeterConfig.enabled;
|
||||||
|
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
|
||||||
|
|
||||||
|
import io.opentelemetry.api.metrics.Meter;
|
||||||
|
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
|
||||||
|
import io.opentelemetry.sdk.internal.ScopeConfigurator;
|
||||||
|
import io.opentelemetry.sdk.metrics.data.MetricData;
|
||||||
|
import io.opentelemetry.sdk.metrics.internal.MeterConfig;
|
||||||
|
import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
|
||||||
|
class MeterConfigTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void disableScopes() {
|
||||||
|
InMemoryMetricReader reader = InMemoryMetricReader.create();
|
||||||
|
SdkMeterProvider meterProvider =
|
||||||
|
SdkMeterProvider.builder()
|
||||||
|
// Disable meterB. Since meters are enabled by default, meterA and meterC are enabled.
|
||||||
|
.addMeterConfiguratorCondition(nameEquals("meterB"), disabled())
|
||||||
|
.registerMetricReader(reader)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Meter meterA = meterProvider.get("meterA");
|
||||||
|
Meter meterB = meterProvider.get("meterB");
|
||||||
|
Meter meterC = meterProvider.get("meterC");
|
||||||
|
|
||||||
|
meterA.counterBuilder("counterA").build().add(1);
|
||||||
|
meterA.counterBuilder("asyncCounterA").buildWithCallback(observable -> observable.record(1));
|
||||||
|
meterA.upDownCounterBuilder("upDownCounterA").build().add(1);
|
||||||
|
meterA
|
||||||
|
.upDownCounterBuilder("asyncUpDownCounterA")
|
||||||
|
.buildWithCallback(observable -> observable.record(1));
|
||||||
|
meterA.histogramBuilder("histogramA").build().record(1.0);
|
||||||
|
meterA.gaugeBuilder("gaugeA").buildWithCallback(observable -> observable.record(1.0));
|
||||||
|
|
||||||
|
meterB.counterBuilder("counterB").build().add(1);
|
||||||
|
meterB.counterBuilder("asyncCounterB").buildWithCallback(observable -> observable.record(1));
|
||||||
|
meterB.upDownCounterBuilder("upDownCounterB").build().add(1);
|
||||||
|
meterB
|
||||||
|
.upDownCounterBuilder("asyncUpDownCounterB")
|
||||||
|
.buildWithCallback(observable -> observable.record(1));
|
||||||
|
meterB.histogramBuilder("histogramB").build().record(1.0);
|
||||||
|
meterB.gaugeBuilder("gaugeB").buildWithCallback(observable -> observable.record(1.0));
|
||||||
|
|
||||||
|
meterC.counterBuilder("counterC").build().add(1);
|
||||||
|
meterC.counterBuilder("asyncCounterC").buildWithCallback(observable -> observable.record(1));
|
||||||
|
meterC.upDownCounterBuilder("upDownCounterC").build().add(1);
|
||||||
|
meterC
|
||||||
|
.upDownCounterBuilder("asyncUpDownCounterC")
|
||||||
|
.buildWithCallback(observable -> observable.record(1));
|
||||||
|
meterC.histogramBuilder("histogramC").build().record(1.0);
|
||||||
|
meterC.gaugeBuilder("gaugeC").buildWithCallback(observable -> observable.record(1.0));
|
||||||
|
|
||||||
|
// Only metrics from meterA and meterC should be seen
|
||||||
|
assertThat(reader.collectAllMetrics())
|
||||||
|
.satisfies(
|
||||||
|
metrics -> {
|
||||||
|
Map<InstrumentationScopeInfo, List<MetricData>> metricsByScope =
|
||||||
|
metrics.stream()
|
||||||
|
.collect(Collectors.groupingBy(MetricData::getInstrumentationScopeInfo));
|
||||||
|
assertThat(metricsByScope.get(InstrumentationScopeInfo.create("meterA"))).hasSize(6);
|
||||||
|
assertThat(metricsByScope.get(InstrumentationScopeInfo.create("meterB"))).isNull();
|
||||||
|
assertThat(metricsByScope.get(InstrumentationScopeInfo.create("meterC"))).hasSize(6);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("meterConfiguratorArgs")
|
||||||
|
void meterConfigurator(
|
||||||
|
ScopeConfigurator<MeterConfig> meterConfigurator,
|
||||||
|
InstrumentationScopeInfo scope,
|
||||||
|
MeterConfig expectedMeterConfig) {
|
||||||
|
MeterConfig meterConfig = meterConfigurator.apply(scope);
|
||||||
|
meterConfig = meterConfig == null ? defaultConfig() : meterConfig;
|
||||||
|
assertThat(meterConfig).isEqualTo(expectedMeterConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final InstrumentationScopeInfo scopeCat = InstrumentationScopeInfo.create("cat");
|
||||||
|
private static final InstrumentationScopeInfo scopeDog = InstrumentationScopeInfo.create("dog");
|
||||||
|
private static final InstrumentationScopeInfo scopeDuck = InstrumentationScopeInfo.create("duck");
|
||||||
|
|
||||||
|
private static Stream<Arguments> meterConfiguratorArgs() {
|
||||||
|
ScopeConfigurator<MeterConfig> defaultConfigurator = MeterConfig.configuratorBuilder().build();
|
||||||
|
ScopeConfigurator<MeterConfig> disableCat =
|
||||||
|
MeterConfig.configuratorBuilder()
|
||||||
|
.addCondition(nameEquals("cat"), MeterConfig.disabled())
|
||||||
|
// Second matching rule for cat should be ignored
|
||||||
|
.addCondition(nameEquals("cat"), enabled())
|
||||||
|
.build();
|
||||||
|
ScopeConfigurator<MeterConfig> disableStartsWithD =
|
||||||
|
MeterConfig.configuratorBuilder()
|
||||||
|
.addCondition(nameMatchesGlob("d*"), MeterConfig.disabled())
|
||||||
|
.build();
|
||||||
|
ScopeConfigurator<MeterConfig> enableCat =
|
||||||
|
MeterConfig.configuratorBuilder()
|
||||||
|
.setDefault(MeterConfig.disabled())
|
||||||
|
.addCondition(nameEquals("cat"), enabled())
|
||||||
|
// Second matching rule for cat should be ignored
|
||||||
|
.addCondition(nameEquals("cat"), MeterConfig.disabled())
|
||||||
|
.build();
|
||||||
|
ScopeConfigurator<MeterConfig> enableStartsWithD =
|
||||||
|
MeterConfig.configuratorBuilder()
|
||||||
|
.setDefault(MeterConfig.disabled())
|
||||||
|
.addCondition(nameMatchesGlob("d*"), MeterConfig.enabled())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return Stream.of(
|
||||||
|
// default
|
||||||
|
Arguments.of(defaultConfigurator, scopeCat, defaultConfig()),
|
||||||
|
Arguments.of(defaultConfigurator, scopeDog, defaultConfig()),
|
||||||
|
Arguments.of(defaultConfigurator, scopeDuck, defaultConfig()),
|
||||||
|
// default enabled, disable cat
|
||||||
|
Arguments.of(disableCat, scopeCat, MeterConfig.disabled()),
|
||||||
|
Arguments.of(disableCat, scopeDog, enabled()),
|
||||||
|
Arguments.of(disableCat, scopeDuck, enabled()),
|
||||||
|
// default enabled, disable pattern
|
||||||
|
Arguments.of(disableStartsWithD, scopeCat, enabled()),
|
||||||
|
Arguments.of(disableStartsWithD, scopeDog, MeterConfig.disabled()),
|
||||||
|
Arguments.of(disableStartsWithD, scopeDuck, MeterConfig.disabled()),
|
||||||
|
// default disabled, enable cat
|
||||||
|
Arguments.of(enableCat, scopeCat, enabled()),
|
||||||
|
Arguments.of(enableCat, scopeDog, MeterConfig.disabled()),
|
||||||
|
Arguments.of(enableCat, scopeDuck, MeterConfig.disabled()),
|
||||||
|
// default disabled, enable pattern
|
||||||
|
Arguments.of(enableStartsWithD, scopeCat, MeterConfig.disabled()),
|
||||||
|
Arguments.of(enableStartsWithD, scopeDog, enabled()),
|
||||||
|
Arguments.of(enableStartsWithD, scopeDuck, enabled()));
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,7 +7,6 @@ package io.opentelemetry.sdk.metrics.internal.view;
|
||||||
|
|
||||||
import static io.opentelemetry.api.common.AttributeKey.stringKey;
|
import static io.opentelemetry.api.common.AttributeKey.stringKey;
|
||||||
import static io.opentelemetry.sdk.metrics.internal.view.ViewRegistry.DEFAULT_REGISTERED_VIEW;
|
import static io.opentelemetry.sdk.metrics.internal.view.ViewRegistry.DEFAULT_REGISTERED_VIEW;
|
||||||
import static io.opentelemetry.sdk.metrics.internal.view.ViewRegistry.toGlobPatternPredicate;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
import io.github.netmikey.logunit.api.LogCapturer;
|
import io.github.netmikey.logunit.api.LogCapturer;
|
||||||
|
@ -544,26 +543,4 @@ class ViewRegistryTest {
|
||||||
INSTRUMENTATION_SCOPE_INFO))
|
INSTRUMENTATION_SCOPE_INFO))
|
||||||
.isEqualTo(Collections.singletonList(DEFAULT_REGISTERED_VIEW));
|
.isEqualTo(Collections.singletonList(DEFAULT_REGISTERED_VIEW));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void matchesName() {
|
|
||||||
assertThat(toGlobPatternPredicate("foo").test("foo")).isTrue();
|
|
||||||
assertThat(toGlobPatternPredicate("foo").test("Foo")).isTrue();
|
|
||||||
assertThat(toGlobPatternPredicate("foo").test("bar")).isFalse();
|
|
||||||
assertThat(toGlobPatternPredicate("fo?").test("foo")).isTrue();
|
|
||||||
assertThat(toGlobPatternPredicate("fo??").test("fooo")).isTrue();
|
|
||||||
assertThat(toGlobPatternPredicate("fo?").test("fob")).isTrue();
|
|
||||||
assertThat(toGlobPatternPredicate("fo?").test("fooo")).isFalse();
|
|
||||||
assertThat(toGlobPatternPredicate("*").test("foo")).isTrue();
|
|
||||||
assertThat(toGlobPatternPredicate("*").test("bar")).isTrue();
|
|
||||||
assertThat(toGlobPatternPredicate("*").test("baz")).isTrue();
|
|
||||||
assertThat(toGlobPatternPredicate("*").test("foo.bar.baz")).isTrue();
|
|
||||||
assertThat(toGlobPatternPredicate("fo*").test("fo")).isTrue();
|
|
||||||
assertThat(toGlobPatternPredicate("fo*").test("foo")).isTrue();
|
|
||||||
assertThat(toGlobPatternPredicate("fo*").test("fooo")).isTrue();
|
|
||||||
assertThat(toGlobPatternPredicate("fo*").test("foo.bar.baz")).isTrue();
|
|
||||||
assertThat(toGlobPatternPredicate("f()[]$^.{}|").test("f()[]$^.{}|")).isTrue();
|
|
||||||
assertThat(toGlobPatternPredicate("f()[]$^.{}|?").test("f()[]$^.{}|o")).isTrue();
|
|
||||||
assertThat(toGlobPatternPredicate("f()[]$^.{}|*").test("f()[]$^.{}|ooo")).isTrue();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,21 +9,31 @@ import io.opentelemetry.api.trace.SpanBuilder;
|
||||||
import io.opentelemetry.api.trace.Tracer;
|
import io.opentelemetry.api.trace.Tracer;
|
||||||
import io.opentelemetry.api.trace.TracerProvider;
|
import io.opentelemetry.api.trace.TracerProvider;
|
||||||
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
|
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
|
||||||
|
import io.opentelemetry.sdk.trace.internal.TracerConfig;
|
||||||
|
|
||||||
/** {@link SdkTracer} is SDK implementation of {@link Tracer}. */
|
/** {@link SdkTracer} is SDK implementation of {@link Tracer}. */
|
||||||
final class SdkTracer implements Tracer {
|
final class SdkTracer implements Tracer {
|
||||||
static final String FALLBACK_SPAN_NAME = "<unspecified span name>";
|
static final String FALLBACK_SPAN_NAME = "<unspecified span name>";
|
||||||
|
private static final Tracer NOOP_TRACER = TracerProvider.noop().get("noop");
|
||||||
|
|
||||||
private final TracerSharedState sharedState;
|
private final TracerSharedState sharedState;
|
||||||
private final InstrumentationScopeInfo instrumentationScopeInfo;
|
private final InstrumentationScopeInfo instrumentationScopeInfo;
|
||||||
|
private final TracerConfig tracerConfig;
|
||||||
|
|
||||||
SdkTracer(TracerSharedState sharedState, InstrumentationScopeInfo instrumentationScopeInfo) {
|
SdkTracer(
|
||||||
|
TracerSharedState sharedState,
|
||||||
|
InstrumentationScopeInfo instrumentationScopeInfo,
|
||||||
|
TracerConfig tracerConfig) {
|
||||||
this.sharedState = sharedState;
|
this.sharedState = sharedState;
|
||||||
this.instrumentationScopeInfo = instrumentationScopeInfo;
|
this.instrumentationScopeInfo = instrumentationScopeInfo;
|
||||||
|
this.tracerConfig = tracerConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpanBuilder spanBuilder(String spanName) {
|
public SpanBuilder spanBuilder(String spanName) {
|
||||||
|
if (!tracerConfig.isEnabled()) {
|
||||||
|
return NOOP_TRACER.spanBuilder(spanName);
|
||||||
|
}
|
||||||
if (spanName == null || spanName.trim().isEmpty()) {
|
if (spanName == null || spanName.trim().isEmpty()) {
|
||||||
spanName = FALLBACK_SPAN_NAME;
|
spanName = FALLBACK_SPAN_NAME;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,11 @@ import io.opentelemetry.api.trace.TracerBuilder;
|
||||||
import io.opentelemetry.api.trace.TracerProvider;
|
import io.opentelemetry.api.trace.TracerProvider;
|
||||||
import io.opentelemetry.sdk.common.Clock;
|
import io.opentelemetry.sdk.common.Clock;
|
||||||
import io.opentelemetry.sdk.common.CompletableResultCode;
|
import io.opentelemetry.sdk.common.CompletableResultCode;
|
||||||
|
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
|
||||||
import io.opentelemetry.sdk.internal.ComponentRegistry;
|
import io.opentelemetry.sdk.internal.ComponentRegistry;
|
||||||
|
import io.opentelemetry.sdk.internal.ScopeConfigurator;
|
||||||
import io.opentelemetry.sdk.resources.Resource;
|
import io.opentelemetry.sdk.resources.Resource;
|
||||||
|
import io.opentelemetry.sdk.trace.internal.TracerConfig;
|
||||||
import io.opentelemetry.sdk.trace.samplers.Sampler;
|
import io.opentelemetry.sdk.trace.samplers.Sampler;
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -27,6 +30,7 @@ public final class SdkTracerProvider implements TracerProvider, Closeable {
|
||||||
static final String DEFAULT_TRACER_NAME = "";
|
static final String DEFAULT_TRACER_NAME = "";
|
||||||
private final TracerSharedState sharedState;
|
private final TracerSharedState sharedState;
|
||||||
private final ComponentRegistry<SdkTracer> tracerSdkComponentRegistry;
|
private final ComponentRegistry<SdkTracer> tracerSdkComponentRegistry;
|
||||||
|
private final ScopeConfigurator<TracerConfig> tracerConfigurator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new {@link SdkTracerProviderBuilder} for {@link SdkTracerProvider}.
|
* Returns a new {@link SdkTracerProviderBuilder} for {@link SdkTracerProvider}.
|
||||||
|
@ -37,19 +41,31 @@ public final class SdkTracerProvider implements TracerProvider, Closeable {
|
||||||
return new SdkTracerProviderBuilder();
|
return new SdkTracerProviderBuilder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("NonApiType")
|
||||||
SdkTracerProvider(
|
SdkTracerProvider(
|
||||||
Clock clock,
|
Clock clock,
|
||||||
IdGenerator idsGenerator,
|
IdGenerator idsGenerator,
|
||||||
Resource resource,
|
Resource resource,
|
||||||
Supplier<SpanLimits> spanLimitsSupplier,
|
Supplier<SpanLimits> spanLimitsSupplier,
|
||||||
Sampler sampler,
|
Sampler sampler,
|
||||||
List<SpanProcessor> spanProcessors) {
|
List<SpanProcessor> spanProcessors,
|
||||||
|
ScopeConfigurator<TracerConfig> tracerConfigurator) {
|
||||||
this.sharedState =
|
this.sharedState =
|
||||||
new TracerSharedState(
|
new TracerSharedState(
|
||||||
clock, idsGenerator, resource, spanLimitsSupplier, sampler, spanProcessors);
|
clock, idsGenerator, resource, spanLimitsSupplier, sampler, spanProcessors);
|
||||||
this.tracerSdkComponentRegistry =
|
this.tracerSdkComponentRegistry =
|
||||||
new ComponentRegistry<>(
|
new ComponentRegistry<>(
|
||||||
instrumentationScopeInfo -> new SdkTracer(sharedState, instrumentationScopeInfo));
|
instrumentationScopeInfo ->
|
||||||
|
new SdkTracer(
|
||||||
|
sharedState,
|
||||||
|
instrumentationScopeInfo,
|
||||||
|
getTracerConfig(instrumentationScopeInfo)));
|
||||||
|
this.tracerConfigurator = tracerConfigurator;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TracerConfig getTracerConfig(InstrumentationScopeInfo instrumentationScopeInfo) {
|
||||||
|
TracerConfig tracerConfig = tracerConfigurator.apply(instrumentationScopeInfo);
|
||||||
|
return tracerConfig == null ? TracerConfig.defaultConfig() : tracerConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -8,11 +8,16 @@ package io.opentelemetry.sdk.trace;
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
import io.opentelemetry.sdk.common.Clock;
|
import io.opentelemetry.sdk.common.Clock;
|
||||||
|
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
|
||||||
|
import io.opentelemetry.sdk.internal.ScopeConfigurator;
|
||||||
|
import io.opentelemetry.sdk.internal.ScopeConfiguratorBuilder;
|
||||||
import io.opentelemetry.sdk.resources.Resource;
|
import io.opentelemetry.sdk.resources.Resource;
|
||||||
|
import io.opentelemetry.sdk.trace.internal.TracerConfig;
|
||||||
import io.opentelemetry.sdk.trace.samplers.Sampler;
|
import io.opentelemetry.sdk.trace.samplers.Sampler;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.function.Predicate;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/** Builder of {@link SdkTracerProvider}. */
|
/** Builder of {@link SdkTracerProvider}. */
|
||||||
|
@ -26,6 +31,8 @@ public final class SdkTracerProviderBuilder {
|
||||||
private Resource resource = Resource.getDefault();
|
private Resource resource = Resource.getDefault();
|
||||||
private Supplier<SpanLimits> spanLimitsSupplier = SpanLimits::getDefault;
|
private Supplier<SpanLimits> spanLimitsSupplier = SpanLimits::getDefault;
|
||||||
private Sampler sampler = DEFAULT_SAMPLER;
|
private Sampler sampler = DEFAULT_SAMPLER;
|
||||||
|
private ScopeConfiguratorBuilder<TracerConfig> tracerConfiguratorBuilder =
|
||||||
|
TracerConfig.configuratorBuilder();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Assign a {@link Clock}. {@link Clock} will be used each time a {@link
|
* Assign a {@link Clock}. {@link Clock} will be used each time a {@link
|
||||||
|
@ -147,6 +154,40 @@ public final class SdkTracerProviderBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the tracer configurator, which computes {@link TracerConfig} for each {@link
|
||||||
|
* InstrumentationScopeInfo}.
|
||||||
|
*
|
||||||
|
* <p>Overrides any matchers added via {@link #addTracerConfiguratorCondition(Predicate,
|
||||||
|
* TracerConfig)}.
|
||||||
|
*
|
||||||
|
* @see TracerConfig#configuratorBuilder()
|
||||||
|
*/
|
||||||
|
SdkTracerProviderBuilder setTracerConfigurator(
|
||||||
|
ScopeConfigurator<TracerConfig> tracerConfigurator) {
|
||||||
|
this.tracerConfiguratorBuilder = tracerConfigurator.toBuilder();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a condition to the tracer configurator, which computes {@link TracerConfig} for each
|
||||||
|
* {@link InstrumentationScopeInfo}.
|
||||||
|
*
|
||||||
|
* <p>Applies after any previously added conditions.
|
||||||
|
*
|
||||||
|
* <p>If {@link #setTracerConfigurator(ScopeConfigurator)} was previously called, this condition
|
||||||
|
* will only be applied if the {@link ScopeConfigurator#apply(Object)} returns null for the
|
||||||
|
* matched {@link InstrumentationScopeInfo}(s).
|
||||||
|
*
|
||||||
|
* @see ScopeConfiguratorBuilder#nameEquals(String)
|
||||||
|
* @see ScopeConfiguratorBuilder#nameMatchesGlob(String)
|
||||||
|
*/
|
||||||
|
SdkTracerProviderBuilder addTracerConfiguratorCondition(
|
||||||
|
Predicate<InstrumentationScopeInfo> scopeMatcher, TracerConfig tracerConfig) {
|
||||||
|
this.tracerConfiguratorBuilder.addCondition(scopeMatcher, tracerConfig);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new {@link SdkTracerProvider} instance with the configuration.
|
* Create a new {@link SdkTracerProvider} instance with the configuration.
|
||||||
*
|
*
|
||||||
|
@ -154,7 +195,13 @@ public final class SdkTracerProviderBuilder {
|
||||||
*/
|
*/
|
||||||
public SdkTracerProvider build() {
|
public SdkTracerProvider build() {
|
||||||
return new SdkTracerProvider(
|
return new SdkTracerProvider(
|
||||||
clock, idsGenerator, resource, spanLimitsSupplier, sampler, spanProcessors);
|
clock,
|
||||||
|
idsGenerator,
|
||||||
|
resource,
|
||||||
|
spanLimitsSupplier,
|
||||||
|
sampler,
|
||||||
|
spanProcessors,
|
||||||
|
tracerConfiguratorBuilder.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
SdkTracerProviderBuilder() {}
|
SdkTracerProviderBuilder() {}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import javax.annotation.Nullable;
|
||||||
|
|
||||||
// Represents the shared state/config between all Tracers created by the same TracerProvider.
|
// Represents the shared state/config between all Tracers created by the same TracerProvider.
|
||||||
final class TracerSharedState {
|
final class TracerSharedState {
|
||||||
|
|
||||||
private final Object lock = new Object();
|
private final Object lock = new Object();
|
||||||
private final Clock clock;
|
private final Clock clock;
|
||||||
private final IdGenerator idGenerator;
|
private final IdGenerator idGenerator;
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.sdk.trace.internal;
|
||||||
|
|
||||||
|
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
|
||||||
|
import io.opentelemetry.sdk.internal.ScopeConfigurator;
|
||||||
|
import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A collection of methods that allow use of experimental features prior to availability in public
|
||||||
|
* APIs.
|
||||||
|
*
|
||||||
|
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
|
||||||
|
* at any time.
|
||||||
|
*/
|
||||||
|
public final class SdkTracerProviderUtil {
|
||||||
|
|
||||||
|
private SdkTracerProviderUtil() {}
|
||||||
|
|
||||||
|
/** Reflectively set the {@link ScopeConfigurator} to the {@link SdkTracerProviderBuilder}. */
|
||||||
|
public static void setTracerConfigurator(
|
||||||
|
SdkTracerProviderBuilder sdkTracerProviderBuilder,
|
||||||
|
ScopeConfigurator<TracerConfig> tracerConfigurator) {
|
||||||
|
try {
|
||||||
|
Method method =
|
||||||
|
SdkTracerProviderBuilder.class.getDeclaredMethod(
|
||||||
|
"setTracerConfigurator", ScopeConfigurator.class);
|
||||||
|
method.setAccessible(true);
|
||||||
|
method.invoke(sdkTracerProviderBuilder, tracerConfigurator);
|
||||||
|
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Error calling setTracerConfigurator on SdkTracerProviderBuilder", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Reflectively add a tracer configurator condition to the {@link SdkTracerProviderBuilder}. */
|
||||||
|
public static void addTracerConfiguratorCondition(
|
||||||
|
SdkTracerProviderBuilder sdkTracerProviderBuilder,
|
||||||
|
Predicate<InstrumentationScopeInfo> scopeMatcher,
|
||||||
|
TracerConfig tracerConfig) {
|
||||||
|
try {
|
||||||
|
Method method =
|
||||||
|
SdkTracerProviderBuilder.class.getDeclaredMethod(
|
||||||
|
"addTracerConfiguratorCondition", Predicate.class, TracerConfig.class);
|
||||||
|
method.setAccessible(true);
|
||||||
|
method.invoke(sdkTracerProviderBuilder, scopeMatcher, tracerConfig);
|
||||||
|
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Error calling addTracerConfiguratorCondition on SdkTracerProviderBuilder", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.sdk.trace.internal;
|
||||||
|
|
||||||
|
import com.google.auto.value.AutoValue;
|
||||||
|
import io.opentelemetry.api.trace.Tracer;
|
||||||
|
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
|
||||||
|
import io.opentelemetry.sdk.internal.ScopeConfigurator;
|
||||||
|
import io.opentelemetry.sdk.internal.ScopeConfiguratorBuilder;
|
||||||
|
import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A collection of configuration options which define the behavior of a {@link Tracer}.
|
||||||
|
*
|
||||||
|
* @see SdkTracerProviderUtil#setTracerConfigurator(SdkTracerProviderBuilder, ScopeConfigurator)
|
||||||
|
* @see SdkTracerProviderUtil#addTracerConfiguratorCondition(SdkTracerProviderBuilder, Predicate,
|
||||||
|
* TracerConfig)
|
||||||
|
*/
|
||||||
|
@AutoValue
|
||||||
|
@Immutable
|
||||||
|
public abstract class TracerConfig {
|
||||||
|
|
||||||
|
private static final TracerConfig DEFAULT_CONFIG =
|
||||||
|
new AutoValue_TracerConfig(/* enabled= */ true);
|
||||||
|
private static final TracerConfig DISABLED_CONFIG =
|
||||||
|
new AutoValue_TracerConfig(/* enabled= */ false);
|
||||||
|
|
||||||
|
/** Returns a disabled {@link TracerConfig}. */
|
||||||
|
public static TracerConfig disabled() {
|
||||||
|
return DISABLED_CONFIG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns an enabled {@link TracerConfig}. */
|
||||||
|
public static TracerConfig enabled() {
|
||||||
|
return DEFAULT_CONFIG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the default {@link TracerConfig}, which is used when no configurator is set or when the
|
||||||
|
* tracer configurator returns {@code null} for a {@link InstrumentationScopeInfo}.
|
||||||
|
*/
|
||||||
|
public static TracerConfig defaultConfig() {
|
||||||
|
return DEFAULT_CONFIG;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a {@link ScopeConfiguratorBuilder} for configuring {@link
|
||||||
|
* SdkTracerProviderUtil#setTracerConfigurator(SdkTracerProviderBuilder, ScopeConfigurator)}.
|
||||||
|
*/
|
||||||
|
public static ScopeConfiguratorBuilder<TracerConfig> configuratorBuilder() {
|
||||||
|
return ScopeConfigurator.builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
TracerConfig() {}
|
||||||
|
|
||||||
|
/** Returns {@code true} if this tracer is enabled. Defaults to {@code true}. */
|
||||||
|
public abstract boolean isEnabled();
|
||||||
|
}
|
|
@ -0,0 +1,156 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.sdk.trace;
|
||||||
|
|
||||||
|
import static io.opentelemetry.sdk.internal.ScopeConfiguratorBuilder.nameEquals;
|
||||||
|
import static io.opentelemetry.sdk.internal.ScopeConfiguratorBuilder.nameMatchesGlob;
|
||||||
|
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
|
||||||
|
import static io.opentelemetry.sdk.trace.internal.TracerConfig.defaultConfig;
|
||||||
|
import static io.opentelemetry.sdk.trace.internal.TracerConfig.disabled;
|
||||||
|
import static io.opentelemetry.sdk.trace.internal.TracerConfig.enabled;
|
||||||
|
|
||||||
|
import io.opentelemetry.api.common.Attributes;
|
||||||
|
import io.opentelemetry.api.trace.Span;
|
||||||
|
import io.opentelemetry.api.trace.SpanId;
|
||||||
|
import io.opentelemetry.api.trace.Tracer;
|
||||||
|
import io.opentelemetry.context.Scope;
|
||||||
|
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
|
||||||
|
import io.opentelemetry.sdk.internal.ScopeConfigurator;
|
||||||
|
import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter;
|
||||||
|
import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor;
|
||||||
|
import io.opentelemetry.sdk.trace.internal.TracerConfig;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
|
||||||
|
class TracerConfigTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void disableScopes() throws InterruptedException {
|
||||||
|
InMemorySpanExporter exporter = InMemorySpanExporter.create();
|
||||||
|
SdkTracerProvider tracerProvider =
|
||||||
|
SdkTracerProvider.builder()
|
||||||
|
// Disable tracerB. Since tracers are enabled by default, tracerA and tracerC are
|
||||||
|
// enabled.
|
||||||
|
.addTracerConfiguratorCondition(nameEquals("tracerB"), disabled())
|
||||||
|
.addSpanProcessor(SimpleSpanProcessor.create(exporter))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Tracer tracerA = tracerProvider.get("tracerA");
|
||||||
|
Tracer tracerB = tracerProvider.get("tracerB");
|
||||||
|
Tracer tracerC = tracerProvider.get("tracerC");
|
||||||
|
|
||||||
|
Span parent;
|
||||||
|
Span child;
|
||||||
|
Span grandchild;
|
||||||
|
|
||||||
|
parent = tracerA.spanBuilder("parent").startSpan();
|
||||||
|
try (Scope parentScope = parent.makeCurrent()) {
|
||||||
|
parent.setAttribute("a", "1");
|
||||||
|
child = tracerB.spanBuilder("child").startSpan();
|
||||||
|
// tracerB is disabled and should behave the same as noop tracer
|
||||||
|
assertThat(child.getSpanContext()).isEqualTo(parent.getSpanContext());
|
||||||
|
assertThat(child.isRecording()).isFalse();
|
||||||
|
try (Scope childScope = child.makeCurrent()) {
|
||||||
|
child.setAttribute("b", "1");
|
||||||
|
grandchild = tracerC.spanBuilder("grandchild").startSpan();
|
||||||
|
try (Scope grandchildScope = grandchild.makeCurrent()) {
|
||||||
|
grandchild.setAttribute("c", "1");
|
||||||
|
Thread.sleep(100);
|
||||||
|
} finally {
|
||||||
|
grandchild.end();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
child.end();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
parent.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only contain tracerA:parent and tracerC:child should be seen
|
||||||
|
// tracerC:grandchild should list tracerA:parent as its parent
|
||||||
|
assertThat(exporter.getFinishedSpanItems())
|
||||||
|
.satisfiesExactlyInAnyOrder(
|
||||||
|
spanData ->
|
||||||
|
assertThat(spanData)
|
||||||
|
.hasInstrumentationScopeInfo(InstrumentationScopeInfo.create("tracerA"))
|
||||||
|
.hasName("parent")
|
||||||
|
.hasSpanId(parent.getSpanContext().getSpanId())
|
||||||
|
.hasParentSpanId(SpanId.getInvalid())
|
||||||
|
.hasAttributes(Attributes.builder().put("a", "1").build()),
|
||||||
|
spanData ->
|
||||||
|
assertThat(spanData)
|
||||||
|
.hasInstrumentationScopeInfo(InstrumentationScopeInfo.create("tracerC"))
|
||||||
|
.hasName("grandchild")
|
||||||
|
.hasSpanId(grandchild.getSpanContext().getSpanId())
|
||||||
|
.hasParentSpanId(parent.getSpanContext().getSpanId())
|
||||||
|
.hasAttributes(Attributes.builder().put("c", "1").build()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("tracerConfiguratorArgs")
|
||||||
|
void tracerConfigurator(
|
||||||
|
ScopeConfigurator<TracerConfig> tracerConfigurator,
|
||||||
|
InstrumentationScopeInfo scope,
|
||||||
|
TracerConfig expectedTracerConfig) {
|
||||||
|
TracerConfig tracerConfig = tracerConfigurator.apply(scope);
|
||||||
|
tracerConfig = tracerConfig == null ? defaultConfig() : tracerConfig;
|
||||||
|
assertThat(tracerConfig).isEqualTo(expectedTracerConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final InstrumentationScopeInfo scopeCat = InstrumentationScopeInfo.create("cat");
|
||||||
|
private static final InstrumentationScopeInfo scopeDog = InstrumentationScopeInfo.create("dog");
|
||||||
|
private static final InstrumentationScopeInfo scopeDuck = InstrumentationScopeInfo.create("duck");
|
||||||
|
|
||||||
|
private static Stream<Arguments> tracerConfiguratorArgs() {
|
||||||
|
ScopeConfigurator<TracerConfig> defaultConfigurator =
|
||||||
|
TracerConfig.configuratorBuilder().build();
|
||||||
|
ScopeConfigurator<TracerConfig> disableCat =
|
||||||
|
TracerConfig.configuratorBuilder()
|
||||||
|
.addCondition(nameEquals("cat"), disabled())
|
||||||
|
// Second matching rule for cat should be ignored
|
||||||
|
.addCondition(nameEquals("cat"), enabled())
|
||||||
|
.build();
|
||||||
|
ScopeConfigurator<TracerConfig> disableStartsWithD =
|
||||||
|
TracerConfig.configuratorBuilder().addCondition(nameMatchesGlob("d*"), disabled()).build();
|
||||||
|
ScopeConfigurator<TracerConfig> enableCat =
|
||||||
|
TracerConfig.configuratorBuilder()
|
||||||
|
.setDefault(disabled())
|
||||||
|
.addCondition(nameEquals("cat"), enabled())
|
||||||
|
// Second matching rule for cat should be ignored
|
||||||
|
.addCondition(nameEquals("cat"), disabled())
|
||||||
|
.build();
|
||||||
|
ScopeConfigurator<TracerConfig> enableStartsWithD =
|
||||||
|
TracerConfig.configuratorBuilder()
|
||||||
|
.setDefault(disabled())
|
||||||
|
.addCondition(nameMatchesGlob("d*"), TracerConfig.enabled())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
return Stream.of(
|
||||||
|
// default
|
||||||
|
Arguments.of(defaultConfigurator, scopeCat, defaultConfig()),
|
||||||
|
Arguments.of(defaultConfigurator, scopeDog, defaultConfig()),
|
||||||
|
Arguments.of(defaultConfigurator, scopeDuck, defaultConfig()),
|
||||||
|
// default enabled, disable cat
|
||||||
|
Arguments.of(disableCat, scopeCat, disabled()),
|
||||||
|
Arguments.of(disableCat, scopeDog, enabled()),
|
||||||
|
Arguments.of(disableCat, scopeDuck, enabled()),
|
||||||
|
// default enabled, disable pattern
|
||||||
|
Arguments.of(disableStartsWithD, scopeCat, enabled()),
|
||||||
|
Arguments.of(disableStartsWithD, scopeDog, disabled()),
|
||||||
|
Arguments.of(disableStartsWithD, scopeDuck, disabled()),
|
||||||
|
// default disabled, enable cat
|
||||||
|
Arguments.of(enableCat, scopeCat, enabled()),
|
||||||
|
Arguments.of(enableCat, scopeDog, disabled()),
|
||||||
|
Arguments.of(enableCat, scopeDuck, disabled()),
|
||||||
|
// default disabled, enable pattern
|
||||||
|
Arguments.of(enableStartsWithD, scopeCat, disabled()),
|
||||||
|
Arguments.of(enableStartsWithD, scopeDog, enabled()),
|
||||||
|
Arguments.of(enableStartsWithD, scopeDuck, enabled()));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue