Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prototype disabling scope across traces, metrics, and logs #6201

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion docs/apidiffs/current_vs_latest/opentelemetry-sdk-common.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,17 @@
Comparing source compatibility of against
No changes.
+++ NEW CLASS: PUBLIC(+) ABSTRACT(+) io.opentelemetry.sdk.common.ScopeSelector (not serializable)
+++ CLASS FILE FORMAT VERSION: 52.0 <- n.a.
+++ NEW SUPERCLASS: java.lang.Object
+++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.common.ScopeSelectorBuilder builder()
+++ NEW METHOD: PUBLIC(+) ABSTRACT(+) java.lang.String getScopeName()
+++ NEW METHOD: PUBLIC(+) ABSTRACT(+) java.lang.String getScopeVersion()
+++ NEW ANNOTATION: javax.annotation.Nullable
+++ NEW METHOD: PUBLIC(+) boolean matchesScope(io.opentelemetry.sdk.common.InstrumentationScopeInfo)
+++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.common.ScopeSelector named(java.lang.String)
+++ NEW METHOD: PUBLIC(+) FINAL(+) java.lang.String toString()
+++ NEW CLASS: PUBLIC(+) FINAL(+) io.opentelemetry.sdk.common.ScopeSelectorBuilder (not serializable)
+++ CLASS FILE FORMAT VERSION: 52.0 <- n.a.
+++ NEW SUPERCLASS: java.lang.Object
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.common.ScopeSelector build()
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.common.ScopeSelectorBuilder setScopeName(java.lang.String)
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.common.ScopeSelectorBuilder setScopeVersion(java.lang.String)
10 changes: 9 additions & 1 deletion docs/apidiffs/current_vs_latest/opentelemetry-sdk-logs.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,10 @@
Comparing source compatibility of against
No changes.
+++ NEW CLASS: PUBLIC(+) ABSTRACT(+) io.opentelemetry.sdk.logs.LoggerConfig (not serializable)
+++ CLASS FILE FORMAT VERSION: 52.0 <- n.a.
+++ NEW SUPERCLASS: java.lang.Object
+++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.logs.LoggerConfig defaultConfig()
+++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.logs.LoggerConfig disabled()
+++ NEW METHOD: PUBLIC(+) ABSTRACT(+) boolean isEnabled()
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.logs.SdkLoggerProviderBuilder (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.logs.SdkLoggerProviderBuilder addScopeConfig(io.opentelemetry.sdk.common.ScopeSelector, io.opentelemetry.sdk.logs.LoggerConfig)
Original file line number Diff line number Diff line change
@@ -1,2 +1,10 @@
Comparing source compatibility of against
No changes.
+++ NEW CLASS: PUBLIC(+) ABSTRACT(+) io.opentelemetry.sdk.metrics.MeterConfig (not serializable)
+++ CLASS FILE FORMAT VERSION: 52.0 <- n.a.
+++ NEW SUPERCLASS: java.lang.Object
+++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.metrics.MeterConfig defaultConfig()
+++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.metrics.MeterConfig disabled()
+++ NEW METHOD: PUBLIC(+) ABSTRACT(+) boolean isEnabled()
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder addScopeConfig(io.opentelemetry.sdk.common.ScopeSelector, io.opentelemetry.sdk.metrics.MeterConfig)
10 changes: 9 additions & 1 deletion docs/apidiffs/current_vs_latest/opentelemetry-sdk-trace.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,10 @@
Comparing source compatibility of against
No changes.
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.trace.SdkTracerProviderBuilder (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.trace.SdkTracerProviderBuilder addScopeConfig(io.opentelemetry.sdk.common.ScopeSelector, io.opentelemetry.sdk.trace.TracerConfig)
+++ NEW CLASS: PUBLIC(+) ABSTRACT(+) io.opentelemetry.sdk.trace.TracerConfig (not serializable)
+++ CLASS FILE FORMAT VERSION: 52.0 <- n.a.
+++ NEW SUPERCLASS: java.lang.Object
+++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.trace.TracerConfig defaultConfig()
+++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.trace.TracerConfig disabled()
+++ NEW METHOD: PUBLIC(+) ABSTRACT(+) boolean isEnabled()
140 changes: 140 additions & 0 deletions sdk/all/src/test/java/io/opentelemetry/sdk/ScopeConfigTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.sdk;

import static io.opentelemetry.sdk.common.ScopeSelector.named;
import static org.assertj.core.api.Assertions.assertThat;

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.LoggerConfig;
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
import io.opentelemetry.sdk.logs.data.LogRecordData;
import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor;
import io.opentelemetry.sdk.metrics.MeterConfig;
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
import io.opentelemetry.sdk.metrics.data.MetricData;
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.TracerConfig;
import io.opentelemetry.sdk.trace.data.SpanData;
import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;

class ScopeConfigTest {

/**
* Emit spans, metrics and logs in a hierarchy of 3 scopes: scopeA -> scopeB -> scopeC. Exercise
* the scope config which is common across all signals and verify telemetry is as expected.
*/
@Test
void disableScopeAllSignals() {
InMemoryLogRecordExporter logRecordExporter = InMemoryLogRecordExporter.create();
InMemoryMetricReader metricReader = InMemoryMetricReader.create();
InMemorySpanExporter spanExporter = InMemorySpanExporter.create();
OpenTelemetrySdk sdk =
OpenTelemetrySdk.builder()
.setTracerProvider(
SdkTracerProvider.builder()
.addSpanProcessor(SimpleSpanProcessor.create(spanExporter))
.addScopeConfig(named("scopeB"), TracerConfig.disabled())
.build())
.setMeterProvider(
SdkMeterProvider.builder()
.registerMetricReader(metricReader)
.addScopeConfig(named("scopeB"), MeterConfig.disabled())
.build())
.setLoggerProvider(
SdkLoggerProvider.builder()
.addLogRecordProcessor(SimpleLogRecordProcessor.create(logRecordExporter))
.addScopeConfig(named("scopeB"), LoggerConfig.disabled())
.build())
.build();

// Start scopeA
Tracer scopeATracer = sdk.getTracer("scopeA");
Meter scopeAMeter = sdk.getMeter("scopeA");
Logger scopeALogger = sdk.getSdkLoggerProvider().get("scopeA");
Span spanA = scopeATracer.spanBuilder("spanA").startSpan();
try (Scope spanAScope = spanA.makeCurrent()) {
scopeALogger.logRecordBuilder().setBody("scopeA log message").emit();

// Start scopeB
Tracer scopeBTracer = sdk.getTracer("scopeB");
Meter scopeBMeter = sdk.getMeter("scopeB");
Logger scopeBLogger = sdk.getSdkLoggerProvider().get("scopeB");
Span spanB = scopeBTracer.spanBuilder("spanB").startSpan();
try (Scope spanBScope = spanB.makeCurrent()) {
scopeBLogger.logRecordBuilder().setBody("scopeB log message").emit();

// Start scopeC
Tracer scopeCTracer = sdk.getTracer("scopeC");
Meter scopeCMeter = sdk.getMeter("scopeC");
Logger scopeCLogger = sdk.getSdkLoggerProvider().get("scopeC");
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

// 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(InstrumentationScopeInfo.create("scopeA"))).hasSize(1);
assertThat(spansByScope.get(InstrumentationScopeInfo.create("scopeB"))).isNull();
assertThat(spansByScope.get(InstrumentationScopeInfo.create("scopeC"))).hasSize(1);
});
assertThat(metricReader.collectAllMetrics())
.satisfies(
metrics -> {
Map<InstrumentationScopeInfo, List<MetricData>> metricsByScope =
metrics.stream()
.collect(Collectors.groupingBy(MetricData::getInstrumentationScopeInfo));
assertThat(metricsByScope.get(InstrumentationScopeInfo.create("scopeA"))).hasSize(1);
assertThat(metricsByScope.get(InstrumentationScopeInfo.create("scopeB"))).isNull();
assertThat(metricsByScope.get(InstrumentationScopeInfo.create("scopeC"))).hasSize(1);
});
assertThat(logRecordExporter.getFinishedLogRecordItems())
.satisfies(
logs -> {
Map<InstrumentationScopeInfo, List<LogRecordData>> logsByScope =
logs.stream()
.collect(Collectors.groupingBy(LogRecordData::getInstrumentationScopeInfo));
assertThat(logsByScope.get(InstrumentationScopeInfo.create("scopeA"))).hasSize(1);
assertThat(logsByScope.get(InstrumentationScopeInfo.create("scopeB"))).isNull();
assertThat(logsByScope.get(InstrumentationScopeInfo.create("scopeC"))).hasSize(1);
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.sdk.common;

import com.google.auto.value.AutoValue;
import io.opentelemetry.sdk.internal.GlobUtil;
import java.util.Objects;
import java.util.StringJoiner;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;

@AutoValue
@Immutable
public abstract class ScopeSelector {

private final AtomicReference<Predicate<InstrumentationScopeInfo>> selectorPredicate =
new AtomicReference<>();

/** Returns a new {@link ScopeSelectorBuilder} for {@link ScopeSelector}. */
public static ScopeSelectorBuilder builder() {
return new ScopeSelectorBuilder();
}

/**
* Returns a {@link ScopeSelector} selecting scopes with the {@code scopeName}.
*
* <p>Scope name 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 ScopeSelector named(String scopeName) {
return builder().setScopeName(scopeName).build();
}

static ScopeSelector create(String scopeName, @Nullable String scopeVersion) {
ScopeSelector selector = new AutoValue_ScopeSelector(scopeName, scopeVersion);
// Compute and cache the predicate because we need to check if a scope matches any registered
// scope selector whenever a scope is created
selector.selectorPredicate.set(matchesScopePredicate(selector));
return selector;
}

ScopeSelector() {}

/** Determine if the {@code scopeInfo} matches the criteria of this {@link ScopeSelector}. */
public boolean matchesScope(InstrumentationScopeInfo scopeInfo) {
return Objects.requireNonNull(selectorPredicate.get()).test(scopeInfo);
}

private static Predicate<InstrumentationScopeInfo> matchesScopePredicate(
ScopeSelector scopeSelector) {
Predicate<String> scopeNamePredicate =
GlobUtil.toGlobPatternPredicate(scopeSelector.getScopeName());
return scopeInfo -> {
String scopeVersionCriteria = scopeSelector.getScopeVersion();
return scopeNamePredicate.test(scopeInfo.getName())
&& (scopeVersionCriteria == null || scopeVersionCriteria.equals(scopeInfo.getVersion()));
};
}

/**
* Returns the selected scope name.
*
* <p>Scope name 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 abstract String getScopeName();

/** Returns the selected scope version, or null of this selects all scope versions. */
@Nullable
public abstract String getScopeVersion();

@Override
public final String toString() {
StringJoiner joiner = new StringJoiner(", ", "ScopeSelector{", "}");
joiner.add("scopeName=" + getScopeName());
if (getScopeVersion() != null) {
joiner.add("scopeVersion=" + getScopeVersion());
}
return joiner.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.sdk.common;

import static java.util.Objects.requireNonNull;

import javax.annotation.Nullable;

/** Builder for {@link ScopeSelector}. */
public final class ScopeSelectorBuilder {

private String scopeName = "*";
@Nullable private String scopeVersion;

ScopeSelectorBuilder() {}

/**
* Select scopes with the given {@code scopeName}.
*
* <p>Scope name 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 ScopeSelectorBuilder setScopeName(String scopeName) {
requireNonNull(scopeName, "scopeName");
this.scopeName = scopeName;
return this;
}

public ScopeSelectorBuilder setScopeVersion(String scopeVersion) {
requireNonNull(scopeVersion, "scopeVersion");
this.scopeVersion = scopeVersion;
return this;
}

/** Returns an {@link ScopeSelector} with the configuration of this builder. */
public ScopeSelector build() {
return ScopeSelector.create(scopeName, scopeVersion);
}
}
Loading
Loading