-
Notifications
You must be signed in to change notification settings - Fork 834
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
28 changed files
with
1,533 additions
and
122 deletions.
There are no files selected for viewing
237 changes: 237 additions & 0 deletions
237
sdk/all/src/test/java/io/opentelemetry/sdk/ScopeConfiguratorTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} | ||
} |
82 changes: 82 additions & 0 deletions
82
sdk/common/src/main/java/io/opentelemetry/sdk/internal/GlobUtil.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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()); | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
sdk/common/src/main/java/io/opentelemetry/sdk/internal/ScopeConfigurator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
} |
Oops, something went wrong.