Add APIs to determine if tracer, logger, instruments are enabled (#6502)

This commit is contained in:
jack-berg 2024-07-03 15:34:18 -05:00 committed by GitHub
parent 9fd6bcae9e
commit 1f7d6a507e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
37 changed files with 773 additions and 81 deletions

View File

@ -14,6 +14,7 @@ See [EventApiUsageTest](./src/test/java/io/opentelemetry/api/incubator/events/Ev
Features:
* Check if logger is enabled before emitting logs to avoid uneccessary computation
* Set AnyValue log record body with arbitrarily complex data
See [ExtendedLogsBridgeApiUsageTest](./src/test/java/io/opentelemetry/api/incubator/logs/ExtendedLogsBridgeApiUsageTest.java).
@ -30,6 +31,7 @@ See [ExtendedMetricsApiUsageTest](./src/test/java/io/opentelemetry/api/incubator
Features:
* Check if instrument is enabled before recording measurements to avoid uneccessary computation
* Simplified injection / extraction of context
See [ExtendedContextPropagatorsUsageTest](./src/test/java/io/opentelemetry/api/incubator/propagation/ExtendedContextPropagatorsUsageTest.java).
@ -38,6 +40,7 @@ See [ExtendedContextPropagatorsUsageTest](./src/test/java/io/opentelemetry/api/i
Features:
* Check if tracer is enabled before starting spans to avoid uneccessary computation
* Utility methods to reduce boilerplace using span API, including extracting context, and wrapping runnables / callables with spans
See [ExtendedTraceApiUsageTest](./src/test/java/io/opentelemetry/api/incubator/trace/ExtendedTraceApiUsageTest.java).

View File

@ -0,0 +1,23 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.api.incubator.logs;
import io.opentelemetry.api.logs.Logger;
/** Extended {@link Logger} with experimental APIs. */
public interface ExtendedLogger extends Logger {
/**
* Returns {@code true} if the logger is enabled.
*
* <p>This allows callers to avoid unnecessary compute when nothing is consuming the data. Because
* the response is subject to change over the application, callers should call this before each
* call to {@link #logRecordBuilder()}.
*/
default boolean isEnabled() {
return true;
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.api.incubator.metrics;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.DoubleCounter;
import io.opentelemetry.context.Context;
/** Extended {@link DoubleCounter} with experimental APIs. */
public interface ExtendedDoubleCounter extends DoubleCounter {
/**
* Returns {@code true} if the counter is enabled.
*
* <p>This allows callers to avoid unnecessary compute when nothing is consuming the data. Because
* the response is subject to change over the application, callers should call this before each
* call to {@link #add(double)}, {@link #add(double, Attributes)}, or {@link #add(double,
* Attributes, Context)}.
*/
default boolean isEnabled() {
return true;
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.api.incubator.metrics;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.DoubleGauge;
import io.opentelemetry.context.Context;
/** Extended {@link DoubleGauge} with experimental APIs. */
public interface ExtendedDoubleGauge extends DoubleGauge {
/**
* Returns {@code true} if the gauge is enabled.
*
* <p>This allows callers to avoid unnecessary compute when nothing is consuming the data. Because
* the response is subject to change over the application, callers should call this before each
* call to {@link #set(double)}, {@link #set(double, Attributes)}, or {@link #set(double,
* Attributes, Context)}.
*/
default boolean isEnabled() {
return true;
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.api.incubator.metrics;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.DoubleHistogram;
import io.opentelemetry.context.Context;
/** Extended {@link DoubleHistogram} with experimental APIs. */
public interface ExtendedDoubleHistogram extends DoubleHistogram {
/**
* Returns {@code true} if the histogram is enabled.
*
* <p>This allows callers to avoid unnecessary compute when nothing is consuming the data. Because
* the response is subject to change over the application, callers should call this before each
* call to {@link #record(double)}, {@link #record(double, Attributes)}, or {@link #record(double,
* Attributes, Context)}.
*/
default boolean isEnabled() {
return true;
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.api.incubator.metrics;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.DoubleUpDownCounter;
import io.opentelemetry.context.Context;
/** Extended {@link DoubleUpDownCounter} with experimental APIs. */
public interface ExtendedDoubleUpDownCounter extends DoubleUpDownCounter {
/**
* Returns {@code true} if the up down counter is enabled.
*
* <p>This allows callers to avoid unnecessary compute when nothing is consuming the data. Because
* the response is subject to change over the application, callers should call this before each
* call to {@link #add(double)}, {@link #add(double, Attributes)}, or {@link #add(double,
* Attributes, Context)}.
*/
default boolean isEnabled() {
return true;
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.api.incubator.metrics;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.DoubleCounter;
import io.opentelemetry.api.metrics.LongCounter;
import io.opentelemetry.context.Context;
/** Extended {@link DoubleCounter} with experimental APIs. */
public interface ExtendedLongCounter extends LongCounter {
/**
* Returns {@code true} if the counter is enabled.
*
* <p>This allows callers to avoid unnecessary compute when nothing is consuming the data. Because
* the response is subject to change over the application, callers should call this before each
* call to {@link #add(long)}, {@link #add(long, Attributes)}, or {@link #add(long, Attributes,
* Context)}.
*/
default boolean isEnabled() {
return true;
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.api.incubator.metrics;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.LongGauge;
import io.opentelemetry.context.Context;
/** Extended {@link LongGauge} with experimental APIs. */
public interface ExtendedLongGauge extends LongGauge {
/**
* Returns {@code true} if the gauge is enabled.
*
* <p>This allows callers to avoid unnecessary compute when nothing is consuming the data. Because
* the response is subject to change over the application, callers should call this before each
* call to {@link #set(long)}, {@link #set(long, Attributes)}, or {@link #set(long, Attributes,
* Context)}.
*/
default boolean isEnabled() {
return true;
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.api.incubator.metrics;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.LongHistogram;
import io.opentelemetry.context.Context;
/** Extended {@link LongHistogram} with experimental APIs. */
public interface ExtendedLongHistogram extends LongHistogram {
/**
* Returns {@code true} if the histogram is enabled.
*
* <p>This allows callers to avoid unnecessary compute when nothing is consuming the data. Because
* the response is subject to change over the application, callers should call this before each
* call to {@link #record(long)}, {@link #record(long, Attributes)}, or {@link #record(long,
* Attributes, Context)}.
*/
default boolean isEnabled() {
return true;
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.api.incubator.metrics;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.LongUpDownCounter;
import io.opentelemetry.context.Context;
/** Extended {@link LongUpDownCounter} with experimental APIs. */
public interface ExtendedLongUpDownCounter extends LongUpDownCounter {
/**
* Returns {@code true} if the up down counter is enabled.
*
* <p>This allows callers to avoid unnecessary compute when nothing is consuming the data. Because
* the response is subject to change over the application, callers should call this before each
* call to {@link #add(long)}, {@link #add(long, Attributes)}, or {@link #add(long, Attributes,
* Context)}.
*/
default boolean isEnabled() {
return true;
}
}

View File

@ -0,0 +1,23 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.api.incubator.trace;
import io.opentelemetry.api.trace.Tracer;
/** Extended {@link Tracer} with experimental APIs. */
public interface ExtendedTracer extends Tracer {
/**
* Returns {@code true} if the tracer is enabled.
*
* <p>This allows callers to avoid unnecessary compute when nothing is consuming the data. Because
* the response is subject to change over the application, callers should call this before each
* call to {@link #spanBuilder(String)}.
*/
default boolean isEnabled() {
return true;
}
}

View File

@ -5,21 +5,82 @@
package io.opentelemetry.api.incubator.logs;
import static io.opentelemetry.sdk.internal.ScopeConfiguratorBuilder.nameEquals;
import static io.opentelemetry.sdk.logs.internal.LoggerConfig.disabled;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import com.google.common.collect.ImmutableMap;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.logs.Logger;
import io.opentelemetry.sdk.logs.SdkLoggerProvider;
import io.opentelemetry.sdk.logs.SdkLoggerProviderBuilder;
import io.opentelemetry.sdk.logs.export.SimpleLogRecordProcessor;
import io.opentelemetry.sdk.logs.internal.AnyValueBody;
import io.opentelemetry.sdk.logs.internal.SdkLoggerProviderUtil;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.testing.exporter.InMemoryLogRecordExporter;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;
/** Demonstrating usage of extended Logs Bridge API. */
class ExtendedLogsBridgeApiUsageTest {
@Test
void loggerEnabled() {
// Setup SdkLoggerProvider
InMemoryLogRecordExporter exporter = InMemoryLogRecordExporter.create();
SdkLoggerProviderBuilder loggerProviderBuilder =
SdkLoggerProvider.builder()
// Default resource used for demonstration purposes
.setResource(Resource.getDefault())
// In-memory exporter used for demonstration purposes
.addLogRecordProcessor(SimpleLogRecordProcessor.create(exporter));
// Disable loggerB
SdkLoggerProviderUtil.addLoggerConfiguratorCondition(
loggerProviderBuilder, nameEquals("loggerB"), disabled());
SdkLoggerProvider loggerProvider = loggerProviderBuilder.build();
// Create loggerA and loggerB
ExtendedLogger loggerA = (ExtendedLogger) loggerProvider.get("loggerA");
ExtendedLogger loggerB = (ExtendedLogger) loggerProvider.get("loggerB");
// Check if logger is enabled before emitting log and avoid unnecessary computation
if (loggerA.isEnabled()) {
loggerA
.logRecordBuilder()
.setBody("hello world!")
.setAllAttributes(Attributes.builder().put("result", flipCoin()).build())
.emit();
}
if (loggerB.isEnabled()) {
loggerB
.logRecordBuilder()
.setBody("hello world!")
.setAllAttributes(Attributes.builder().put("result", flipCoin()).build())
.emit();
}
// loggerA is enabled, loggerB is disabled
assertThat(loggerA.isEnabled()).isTrue();
assertThat(loggerB.isEnabled()).isFalse();
// Collected data only consists of logs from loggerA. Note, loggerB's logs would be
// omitted from the results even if logs were emitted. The check if enabled simply avoids
// unnecessary computation.
assertThat(exporter.getFinishedLogRecordItems())
.allSatisfy(
logRecordData ->
assertThat(logRecordData.getInstrumentationScopeInfo().getName())
.isEqualTo("loggerA"));
}
private static final Random random = new Random();
private static String flipCoin() {
return random.nextBoolean() ? "heads" : "tails";
}
@Test
void extendedLogRecordBuilderUsage() {
// Setup SdkLoggerProvider

View File

@ -5,6 +5,8 @@
package io.opentelemetry.api.incubator.metrics;
import static io.opentelemetry.sdk.internal.ScopeConfiguratorBuilder.nameEquals;
import static io.opentelemetry.sdk.metrics.internal.MeterConfig.disabled;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import com.google.common.collect.ImmutableList;
@ -15,14 +17,68 @@ import io.opentelemetry.api.metrics.LongCounter;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.sdk.metrics.InstrumentSelector;
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
import io.opentelemetry.sdk.metrics.View;
import io.opentelemetry.sdk.metrics.internal.SdkMeterProviderUtil;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader;
import java.util.Random;
import org.junit.jupiter.api.Test;
/** Demonstrating usage of extended Metrics API. */
class ExtendedMetricsApiUsageTest {
@Test
void meterEnabled() {
// Setup SdkMeterProvider
InMemoryMetricReader reader = InMemoryMetricReader.create();
SdkMeterProviderBuilder meterProviderBuilder =
SdkMeterProvider.builder()
// Default resource used for demonstration purposes
.setResource(Resource.getDefault())
// In-memory reader used for demonstration purposes
.registerMetricReader(reader);
// Disable meterB
SdkMeterProviderUtil.addMeterConfiguratorCondition(
meterProviderBuilder, nameEquals("meterB"), disabled());
SdkMeterProvider meterProvider = meterProviderBuilder.build();
// Create meterA and meterB, and corresponding instruments
Meter meterA = meterProvider.get("meterA");
Meter meterB = meterProvider.get("meterB");
ExtendedDoubleHistogram histogramA =
(ExtendedDoubleHistogram) meterA.histogramBuilder("histogramA").build();
ExtendedDoubleHistogram histogramB =
(ExtendedDoubleHistogram) meterB.histogramBuilder("histogramB").build();
// Check if instrument is enabled before recording measurement and avoid unnecessary computation
if (histogramA.isEnabled()) {
histogramA.record(1.0, Attributes.builder().put("result", flipCoin()).build());
}
if (histogramB.isEnabled()) {
histogramA.record(1.0, Attributes.builder().put("result", flipCoin()).build());
}
// histogramA is enabled since meterA is enabled, histogramB is disabled since meterB is
// disabled
assertThat(histogramA.isEnabled()).isTrue();
assertThat(histogramB.isEnabled()).isFalse();
// Collected data only consists of metrics from meterA. Note, meterB's histogramB would be
// omitted from the results even if values were recorded. The check if enabled simply avoids
// unnecessary computation.
assertThat(reader.collectAllMetrics())
.allSatisfy(
metric ->
assertThat(metric.getInstrumentationScopeInfo().getName()).isEqualTo("meterA"));
}
private static final Random random = new Random();
private static String flipCoin() {
return random.nextBoolean() ? "heads" : "tails";
}
@Test
void attributesAdvice() {
// Setup SdkMeterProvider

View File

@ -5,9 +5,12 @@
package io.opentelemetry.api.incubator.trace;
import static io.opentelemetry.sdk.internal.ScopeConfiguratorBuilder.nameEquals;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static io.opentelemetry.sdk.trace.internal.TracerConfig.disabled;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.api.trace.TraceFlags;
@ -21,16 +24,73 @@ import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.testing.exporter.InMemorySpanExporter;
import io.opentelemetry.sdk.trace.IdGenerator;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder;
import io.opentelemetry.sdk.trace.data.StatusData;
import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor;
import io.opentelemetry.sdk.trace.internal.SdkTracerProviderUtil;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.function.BiConsumer;
import org.junit.jupiter.api.Test;
/** Demonstrating usage of extended Trace API. */
class ExtendedTraceApiUsageTest {
@Test
void tracerEnabled() {
// Setup SdkTracerProvider
InMemorySpanExporter exporter = InMemorySpanExporter.create();
SdkTracerProviderBuilder tracerProviderBuilder =
SdkTracerProvider.builder()
// Default resource used for demonstration purposes
.setResource(Resource.getDefault())
// In-memory exporter used for demonstration purposes
.addSpanProcessor(SimpleSpanProcessor.create(exporter));
// Disable tracerB
SdkTracerProviderUtil.addTracerConfiguratorCondition(
tracerProviderBuilder, nameEquals("tracerB"), disabled());
SdkTracerProvider tracerProvider = tracerProviderBuilder.build();
// Create tracerA and tracerB
ExtendedTracer tracerA = (ExtendedTracer) tracerProvider.get("tracerA");
ExtendedTracer tracerB = (ExtendedTracer) tracerProvider.get("tracerB");
// Check if tracer is enabled before recording span and avoid unnecessary computation
if (tracerA.isEnabled()) {
tracerA
.spanBuilder("span name")
.startSpan()
.setAllAttributes(Attributes.builder().put("result", flipCoin()).build())
.end();
}
if (tracerB.isEnabled()) {
tracerB
.spanBuilder("span name")
.startSpan()
.setAllAttributes(Attributes.builder().put("result", flipCoin()).build())
.end();
}
// tracerA is enabled, tracerB is disabled
assertThat(tracerA.isEnabled()).isTrue();
assertThat(tracerB.isEnabled()).isFalse();
// Collected data only consists of spans from tracerA. Note, tracerB's spans would be
// omitted from the results even if spans were recorded. The check if enabled simply avoids
// unnecessary computation.
assertThat(exporter.getFinishedSpanItems())
.allSatisfy(
spanData ->
assertThat(spanData.getInstrumentationScopeInfo().getName()).isEqualTo("tracerA"));
}
private static final Random random = new Random();
private static String flipCoin() {
return random.nextBoolean() ? "heads" : "tails";
}
/** Demonstrates {@link ExtendedSpanBuilder#setParentFrom(ContextPropagators, Map)}. */
@Test
void setParentFrom() {

View File

@ -5,6 +5,7 @@
package io.opentelemetry.sdk.logs;
import io.opentelemetry.api.incubator.logs.ExtendedLogger;
import io.opentelemetry.api.logs.LogRecordBuilder;
import io.opentelemetry.api.logs.Logger;
import io.opentelemetry.api.logs.LoggerProvider;
@ -12,13 +13,13 @@ import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import io.opentelemetry.sdk.logs.internal.LoggerConfig;
/** SDK implementation of {@link Logger}. */
final class SdkLogger implements Logger {
final class SdkLogger implements ExtendedLogger {
private static final Logger NOOP_LOGGER = LoggerProvider.noop().get("noop");
private final LoggerSharedState loggerSharedState;
private final InstrumentationScopeInfo instrumentationScopeInfo;
private final LoggerConfig loggerConfig;
private final boolean loggerEnabled;
SdkLogger(
LoggerSharedState loggerSharedState,
@ -26,12 +27,12 @@ final class SdkLogger implements Logger {
LoggerConfig loggerConfig) {
this.loggerSharedState = loggerSharedState;
this.instrumentationScopeInfo = instrumentationScopeInfo;
this.loggerConfig = loggerConfig;
this.loggerEnabled = loggerConfig.isEnabled();
}
@Override
public LogRecordBuilder logRecordBuilder() {
if (loggerConfig.isEnabled()) {
if (loggerEnabled) {
return new SdkLogRecordBuilder(loggerSharedState, instrumentationScopeInfo);
}
return NOOP_LOGGER.logRecordBuilder();
@ -41,4 +42,9 @@ final class SdkLogger implements Logger {
InstrumentationScopeInfo getInstrumentationScopeInfo() {
return instrumentationScopeInfo;
}
@Override
public boolean isEnabled() {
return loggerEnabled;
}
}

View File

@ -11,6 +11,7 @@ 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.incubator.logs.ExtendedLogger;
import io.opentelemetry.api.logs.Logger;
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import io.opentelemetry.sdk.internal.ScopeConfigurator;
@ -59,6 +60,10 @@ class LoggerConfigTest {
assertThat(logsByScope.get(InstrumentationScopeInfo.create("loggerB"))).isNull();
assertThat(logsByScope.get(InstrumentationScopeInfo.create("loggerC"))).hasSize(1);
});
// loggerA and loggerC are enabled, loggerB is disabled.
assertThat(((ExtendedLogger) loggerA).isEnabled()).isTrue();
assertThat(((ExtendedLogger) loggerB).isEnabled()).isFalse();
assertThat(((ExtendedLogger) loggerC).isEnabled()).isTrue();
}
@ParameterizedTest

View File

@ -17,7 +17,6 @@ import io.opentelemetry.sdk.metrics.internal.state.SdkObservableMeasurement;
import io.opentelemetry.sdk.metrics.internal.state.WriteableMetricStorage;
import java.util.Collections;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Consumer;
/** Helper to make implementing builders easier. */
@ -65,12 +64,21 @@ final class InstrumentBuilder {
meterProviderSharedState, meterSharedState, name, description, unit, adviceBuilder);
}
@FunctionalInterface
interface SynchronousInstrumentConstructor<I extends AbstractInstrument> {
I createInstrument(
InstrumentDescriptor instrumentDescriptor,
MeterSharedState meterSharedState,
WriteableMetricStorage storage);
}
<I extends AbstractInstrument> I buildSynchronousInstrument(
BiFunction<InstrumentDescriptor, WriteableMetricStorage, I> instrumentFactory) {
SynchronousInstrumentConstructor<I> instrumentFactory) {
InstrumentDescriptor descriptor = newDescriptor();
WriteableMetricStorage storage =
meterSharedState.registerSynchronousMetricStorage(descriptor, meterProviderSharedState);
return instrumentFactory.apply(descriptor, storage);
return instrumentFactory.createInstrument(descriptor, meterSharedState, storage);
}
SdkObservableInstrument buildDoubleAsynchronousInstrument(

View File

@ -7,8 +7,8 @@ package io.opentelemetry.sdk.metrics;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.incubator.metrics.ExtendedDoubleCounter;
import io.opentelemetry.api.incubator.metrics.ExtendedDoubleCounterBuilder;
import io.opentelemetry.api.metrics.DoubleCounter;
import io.opentelemetry.api.metrics.DoubleCounterBuilder;
import io.opentelemetry.api.metrics.ObservableDoubleCounter;
import io.opentelemetry.api.metrics.ObservableDoubleMeasurement;
@ -24,14 +24,19 @@ import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
final class SdkDoubleCounter extends AbstractInstrument implements DoubleCounter {
final class SdkDoubleCounter extends AbstractInstrument implements ExtendedDoubleCounter {
private static final Logger logger = Logger.getLogger(SdkDoubleCounter.class.getName());
private final ThrottlingLogger throttlingLogger = new ThrottlingLogger(logger);
private final MeterSharedState meterSharedState;
private final WriteableMetricStorage storage;
private SdkDoubleCounter(InstrumentDescriptor descriptor, WriteableMetricStorage storage) {
private SdkDoubleCounter(
InstrumentDescriptor descriptor,
MeterSharedState meterSharedState,
WriteableMetricStorage storage) {
super(descriptor);
this.meterSharedState = meterSharedState;
this.storage = storage;
}
@ -58,6 +63,11 @@ final class SdkDoubleCounter extends AbstractInstrument implements DoubleCounter
add(increment, Attributes.empty());
}
@Override
public boolean isEnabled() {
return meterSharedState.isMeterEnabled() && storage.isEnabled();
}
static final class SdkDoubleCounterBuilder implements ExtendedDoubleCounterBuilder {
private final InstrumentBuilder builder;

View File

@ -7,8 +7,8 @@ package io.opentelemetry.sdk.metrics;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.incubator.metrics.ExtendedDoubleGauge;
import io.opentelemetry.api.incubator.metrics.ExtendedDoubleGaugeBuilder;
import io.opentelemetry.api.metrics.DoubleGauge;
import io.opentelemetry.api.metrics.DoubleGaugeBuilder;
import io.opentelemetry.api.metrics.LongGaugeBuilder;
import io.opentelemetry.api.metrics.ObservableDoubleGauge;
@ -21,12 +21,17 @@ import io.opentelemetry.sdk.metrics.internal.state.WriteableMetricStorage;
import java.util.List;
import java.util.function.Consumer;
final class SdkDoubleGauge extends AbstractInstrument implements DoubleGauge {
final class SdkDoubleGauge extends AbstractInstrument implements ExtendedDoubleGauge {
private final MeterSharedState meterSharedState;
private final WriteableMetricStorage storage;
private SdkDoubleGauge(InstrumentDescriptor descriptor, WriteableMetricStorage storage) {
private SdkDoubleGauge(
InstrumentDescriptor descriptor,
MeterSharedState meterSharedState,
WriteableMetricStorage storage) {
super(descriptor);
this.meterSharedState = meterSharedState;
this.storage = storage;
}
@ -45,6 +50,11 @@ final class SdkDoubleGauge extends AbstractInstrument implements DoubleGauge {
set(increment, Attributes.empty());
}
@Override
public boolean isEnabled() {
return meterSharedState.isMeterEnabled() && storage.isEnabled();
}
static final class SdkDoubleGaugeBuilder implements ExtendedDoubleGaugeBuilder {
private final InstrumentBuilder builder;

View File

@ -7,8 +7,8 @@ package io.opentelemetry.sdk.metrics;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.incubator.metrics.ExtendedDoubleHistogram;
import io.opentelemetry.api.incubator.metrics.ExtendedDoubleHistogramBuilder;
import io.opentelemetry.api.metrics.DoubleHistogram;
import io.opentelemetry.api.metrics.DoubleHistogramBuilder;
import io.opentelemetry.api.metrics.LongHistogramBuilder;
import io.opentelemetry.context.Context;
@ -23,14 +23,19 @@ import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;
final class SdkDoubleHistogram extends AbstractInstrument implements DoubleHistogram {
final class SdkDoubleHistogram extends AbstractInstrument implements ExtendedDoubleHistogram {
private static final Logger logger = Logger.getLogger(SdkDoubleHistogram.class.getName());
private final ThrottlingLogger throttlingLogger = new ThrottlingLogger(logger);
private final MeterSharedState meterSharedState;
private final WriteableMetricStorage storage;
private SdkDoubleHistogram(InstrumentDescriptor descriptor, WriteableMetricStorage storage) {
private SdkDoubleHistogram(
InstrumentDescriptor descriptor,
MeterSharedState meterSharedState,
WriteableMetricStorage storage) {
super(descriptor);
this.meterSharedState = meterSharedState;
this.storage = storage;
}
@ -57,6 +62,11 @@ final class SdkDoubleHistogram extends AbstractInstrument implements DoubleHisto
record(value, Attributes.empty());
}
@Override
public boolean isEnabled() {
return meterSharedState.isMeterEnabled() && storage.isEnabled();
}
static final class SdkDoubleHistogramBuilder implements ExtendedDoubleHistogramBuilder {
private final InstrumentBuilder builder;

View File

@ -7,6 +7,7 @@ package io.opentelemetry.sdk.metrics;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.incubator.metrics.ExtendedDoubleUpDownCounter;
import io.opentelemetry.api.incubator.metrics.ExtendedDoubleUpDownCounterBuilder;
import io.opentelemetry.api.metrics.DoubleUpDownCounter;
import io.opentelemetry.api.metrics.DoubleUpDownCounterBuilder;
@ -21,12 +22,18 @@ import io.opentelemetry.sdk.metrics.internal.state.WriteableMetricStorage;
import java.util.List;
import java.util.function.Consumer;
final class SdkDoubleUpDownCounter extends AbstractInstrument implements DoubleUpDownCounter {
final class SdkDoubleUpDownCounter extends AbstractInstrument
implements ExtendedDoubleUpDownCounter {
private final MeterSharedState meterSharedState;
private final WriteableMetricStorage storage;
private SdkDoubleUpDownCounter(InstrumentDescriptor descriptor, WriteableMetricStorage storage) {
private SdkDoubleUpDownCounter(
InstrumentDescriptor descriptor,
MeterSharedState meterSharedState,
WriteableMetricStorage storage) {
super(descriptor);
this.meterSharedState = meterSharedState;
this.storage = storage;
}
@ -45,6 +52,11 @@ final class SdkDoubleUpDownCounter extends AbstractInstrument implements DoubleU
add(increment, Attributes.empty());
}
@Override
public boolean isEnabled() {
return meterSharedState.isMeterEnabled() && storage.isEnabled();
}
static final class SdkDoubleUpDownCounterBuilder implements ExtendedDoubleUpDownCounterBuilder {
private final InstrumentBuilder builder;

View File

@ -7,9 +7,9 @@ package io.opentelemetry.sdk.metrics;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.incubator.metrics.ExtendedLongCounter;
import io.opentelemetry.api.incubator.metrics.ExtendedLongCounterBuilder;
import io.opentelemetry.api.metrics.DoubleCounterBuilder;
import io.opentelemetry.api.metrics.LongCounter;
import io.opentelemetry.api.metrics.LongCounterBuilder;
import io.opentelemetry.api.metrics.ObservableLongCounter;
import io.opentelemetry.api.metrics.ObservableLongMeasurement;
@ -24,15 +24,20 @@ import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
final class SdkLongCounter extends AbstractInstrument implements LongCounter {
final class SdkLongCounter extends AbstractInstrument implements ExtendedLongCounter {
private static final Logger logger = Logger.getLogger(SdkLongCounter.class.getName());
private final ThrottlingLogger throttlingLogger = new ThrottlingLogger(logger);
private final MeterSharedState meterSharedState;
private final WriteableMetricStorage storage;
private SdkLongCounter(InstrumentDescriptor descriptor, WriteableMetricStorage storage) {
private SdkLongCounter(
InstrumentDescriptor descriptor,
MeterSharedState meterSharedState,
WriteableMetricStorage storage) {
super(descriptor);
this.meterSharedState = meterSharedState;
this.storage = storage;
}
@ -59,6 +64,11 @@ final class SdkLongCounter extends AbstractInstrument implements LongCounter {
add(increment, Attributes.empty());
}
@Override
public boolean isEnabled() {
return meterSharedState.isMeterEnabled() && storage.isEnabled();
}
static final class SdkLongCounterBuilder implements ExtendedLongCounterBuilder {
private final InstrumentBuilder builder;

View File

@ -7,8 +7,8 @@ package io.opentelemetry.sdk.metrics;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.incubator.metrics.ExtendedLongGauge;
import io.opentelemetry.api.incubator.metrics.ExtendedLongGaugeBuilder;
import io.opentelemetry.api.metrics.LongGauge;
import io.opentelemetry.api.metrics.LongGaugeBuilder;
import io.opentelemetry.api.metrics.ObservableLongGauge;
import io.opentelemetry.api.metrics.ObservableLongMeasurement;
@ -21,12 +21,17 @@ import io.opentelemetry.sdk.metrics.internal.state.WriteableMetricStorage;
import java.util.List;
import java.util.function.Consumer;
final class SdkLongGauge extends AbstractInstrument implements LongGauge {
final class SdkLongGauge extends AbstractInstrument implements ExtendedLongGauge {
private final MeterSharedState meterSharedState;
private final WriteableMetricStorage storage;
private SdkLongGauge(InstrumentDescriptor descriptor, WriteableMetricStorage storage) {
private SdkLongGauge(
InstrumentDescriptor descriptor,
MeterSharedState meterSharedState,
WriteableMetricStorage storage) {
super(descriptor);
this.meterSharedState = meterSharedState;
this.storage = storage;
}
@ -45,6 +50,11 @@ final class SdkLongGauge extends AbstractInstrument implements LongGauge {
set(increment, Attributes.empty());
}
@Override
public boolean isEnabled() {
return meterSharedState.isMeterEnabled() && storage.isEnabled();
}
static final class SdkLongGaugeBuilder implements ExtendedLongGaugeBuilder {
private final InstrumentBuilder builder;

View File

@ -7,8 +7,8 @@ package io.opentelemetry.sdk.metrics;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.incubator.metrics.ExtendedLongHistogram;
import io.opentelemetry.api.incubator.metrics.ExtendedLongHistogramBuilder;
import io.opentelemetry.api.metrics.LongHistogram;
import io.opentelemetry.api.metrics.LongHistogramBuilder;
import io.opentelemetry.context.Context;
import io.opentelemetry.sdk.internal.ThrottlingLogger;
@ -24,14 +24,19 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
final class SdkLongHistogram extends AbstractInstrument implements LongHistogram {
final class SdkLongHistogram extends AbstractInstrument implements ExtendedLongHistogram {
private static final Logger logger = Logger.getLogger(SdkLongHistogram.class.getName());
private final ThrottlingLogger throttlingLogger = new ThrottlingLogger(logger);
private final MeterSharedState meterSharedState;
private final WriteableMetricStorage storage;
private SdkLongHistogram(InstrumentDescriptor descriptor, WriteableMetricStorage storage) {
private SdkLongHistogram(
InstrumentDescriptor descriptor,
MeterSharedState meterSharedState,
WriteableMetricStorage storage) {
super(descriptor);
this.meterSharedState = meterSharedState;
this.storage = storage;
}
@ -58,6 +63,11 @@ final class SdkLongHistogram extends AbstractInstrument implements LongHistogram
record(value, Attributes.empty());
}
@Override
public boolean isEnabled() {
return meterSharedState.isMeterEnabled() && storage.isEnabled();
}
static final class SdkLongHistogramBuilder implements ExtendedLongHistogramBuilder {
private final InstrumentBuilder builder;

View File

@ -7,6 +7,7 @@ package io.opentelemetry.sdk.metrics;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.incubator.metrics.ExtendedLongUpDownCounter;
import io.opentelemetry.api.incubator.metrics.ExtendedLongUpDownCounterBuilder;
import io.opentelemetry.api.metrics.DoubleUpDownCounterBuilder;
import io.opentelemetry.api.metrics.LongUpDownCounter;
@ -21,12 +22,17 @@ import io.opentelemetry.sdk.metrics.internal.state.WriteableMetricStorage;
import java.util.List;
import java.util.function.Consumer;
final class SdkLongUpDownCounter extends AbstractInstrument implements LongUpDownCounter {
final class SdkLongUpDownCounter extends AbstractInstrument implements ExtendedLongUpDownCounter {
private final MeterSharedState meterSharedState;
private final WriteableMetricStorage storage;
private SdkLongUpDownCounter(InstrumentDescriptor descriptor, WriteableMetricStorage storage) {
private SdkLongUpDownCounter(
InstrumentDescriptor descriptor,
MeterSharedState meterSharedState,
WriteableMetricStorage storage) {
super(descriptor);
this.meterSharedState = meterSharedState;
this.storage = storage;
}
@ -45,6 +51,11 @@ final class SdkLongUpDownCounter extends AbstractInstrument implements LongUpDow
add(increment, Attributes.empty());
}
@Override
public boolean isEnabled() {
return meterSharedState.isMeterEnabled() && storage.isEnabled();
}
static final class SdkLongUpDownCounterBuilder implements ExtendedLongUpDownCounterBuilder {
private final InstrumentBuilder builder;

View File

@ -56,7 +56,6 @@ final class SdkMeter implements Meter {
private final InstrumentationScopeInfo instrumentationScopeInfo;
private final MeterProviderSharedState meterProviderSharedState;
private final MeterSharedState meterSharedState;
private final MeterConfig meterConfig;
SdkMeter(
MeterProviderSharedState meterProviderSharedState,
@ -65,8 +64,8 @@ final class SdkMeter implements Meter {
MeterConfig meterConfig) {
this.instrumentationScopeInfo = instrumentationScopeInfo;
this.meterProviderSharedState = meterProviderSharedState;
this.meterSharedState = MeterSharedState.create(instrumentationScopeInfo, registeredReaders);
this.meterConfig = meterConfig;
this.meterSharedState =
MeterSharedState.create(instrumentationScopeInfo, registeredReaders, meterConfig);
}
// Visible for testing
@ -86,14 +85,14 @@ final class SdkMeter implements Meter {
@Override
public LongCounterBuilder counterBuilder(String name) {
return meterConfig.isEnabled() && checkValidInstrumentName(name)
return checkValidInstrumentName(name)
? new SdkLongCounter.SdkLongCounterBuilder(meterProviderSharedState, meterSharedState, name)
: NOOP_METER.counterBuilder(NOOP_INSTRUMENT_NAME);
}
@Override
public LongUpDownCounterBuilder upDownCounterBuilder(String name) {
return meterConfig.isEnabled() && checkValidInstrumentName(name)
return checkValidInstrumentName(name)
? new SdkLongUpDownCounter.SdkLongUpDownCounterBuilder(
meterProviderSharedState, meterSharedState, name)
: NOOP_METER.upDownCounterBuilder(NOOP_INSTRUMENT_NAME);
@ -101,7 +100,7 @@ final class SdkMeter implements Meter {
@Override
public DoubleHistogramBuilder histogramBuilder(String name) {
return meterConfig.isEnabled() && checkValidInstrumentName(name)
return checkValidInstrumentName(name)
? new SdkDoubleHistogram.SdkDoubleHistogramBuilder(
meterProviderSharedState, meterSharedState, name)
: NOOP_METER.histogramBuilder(NOOP_INSTRUMENT_NAME);
@ -109,7 +108,7 @@ final class SdkMeter implements Meter {
@Override
public DoubleGaugeBuilder gaugeBuilder(String name) {
return meterConfig.isEnabled() && checkValidInstrumentName(name)
return checkValidInstrumentName(name)
? new SdkDoubleGauge.SdkDoubleGaugeBuilder(meterProviderSharedState, meterSharedState, name)
: NOOP_METER.gaugeBuilder(NOOP_INSTRUMENT_NAME);
}
@ -119,9 +118,6 @@ final class SdkMeter implements Meter {
Runnable callback,
ObservableMeasurement observableMeasurement,
ObservableMeasurement... additionalMeasurements) {
if (!meterConfig.isEnabled()) {
return NOOP_METER.batchCallback(callback, observableMeasurement, additionalMeasurements);
}
Set<ObservableMeasurement> measurements = new HashSet<>();
measurements.add(observableMeasurement);
Collections.addAll(measurements, additionalMeasurements);

View File

@ -131,6 +131,11 @@ public final class DefaultSynchronousMetricStorage<T extends PointData, U extend
}
}
@Override
public boolean isEnabled() {
return true;
}
/**
* Obtain the AggregatorHolder for recording measurements, re-reading the volatile
* this.aggregatorHolder until we access one where recordsInProgress is even. Collect sets

View File

@ -39,4 +39,9 @@ final class EmptyMetricStorage implements SynchronousMetricStorage {
@Override
public void recordDouble(double value, Attributes attributes, Context context) {}
@Override
public boolean isEnabled() {
return false;
}
}

View File

@ -11,11 +11,13 @@ import io.opentelemetry.api.internal.GuardedBy;
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import io.opentelemetry.sdk.metrics.Aggregation;
import io.opentelemetry.sdk.metrics.data.MetricData;
import io.opentelemetry.sdk.metrics.internal.MeterConfig;
import io.opentelemetry.sdk.metrics.internal.descriptor.InstrumentDescriptor;
import io.opentelemetry.sdk.metrics.internal.export.RegisteredReader;
import io.opentelemetry.sdk.metrics.internal.view.RegisteredView;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@ -36,20 +38,25 @@ public class MeterSharedState {
private final List<CallbackRegistration> callbackRegistrations = new ArrayList<>();
private final Map<RegisteredReader, MetricStorageRegistry> readerStorageRegistries;
private final InstrumentationScopeInfo instrumentationScopeInfo;
private final boolean meterEnabled;
private MeterSharedState(
InstrumentationScopeInfo instrumentationScopeInfo, List<RegisteredReader> registeredReaders) {
InstrumentationScopeInfo instrumentationScopeInfo,
List<RegisteredReader> registeredReaders,
MeterConfig meterConfig) {
this.instrumentationScopeInfo = instrumentationScopeInfo;
this.readerStorageRegistries =
registeredReaders.stream()
.collect(toMap(Function.identity(), unused -> new MetricStorageRegistry()));
this.meterEnabled = meterConfig.isEnabled();
}
public static MeterSharedState create(
InstrumentationScopeInfo instrumentationScopeInfo, List<RegisteredReader> registeredReaders) {
return new MeterSharedState(instrumentationScopeInfo, registeredReaders);
InstrumentationScopeInfo instrumentationScopeInfo,
List<RegisteredReader> registeredReaders,
MeterConfig meterConfig) {
return new MeterSharedState(instrumentationScopeInfo, registeredReaders, meterConfig);
}
/**
@ -81,11 +88,20 @@ public class MeterSharedState {
return instrumentationScopeInfo;
}
/** Returns {@code true} if the {@link MeterConfig#enabled()} of the meter is {@code true}. */
public boolean isMeterEnabled() {
return meterEnabled;
}
/** Collects all metrics. */
public List<MetricData> collectAll(
RegisteredReader registeredReader,
MeterProviderSharedState meterProviderSharedState,
long epochNanos) {
// Short circuit collection process if meter is disabled
if (!meterEnabled) {
return Collections.emptyList();
}
List<CallbackRegistration> currentRegisteredCallbacks;
synchronized (callbackLock) {
currentRegisteredCallbacks = new ArrayList<>(callbackRegistrations);
@ -113,7 +129,7 @@ public class MeterSharedState {
result.add(current);
}
}
return result;
return Collections.unmodifiableList(result);
}
}

View File

@ -29,4 +29,14 @@ class MultiWritableMetricStorage implements WriteableMetricStorage {
storage.recordDouble(value, attributes, context);
}
}
@Override
public boolean isEnabled() {
for (WriteableMetricStorage storage : storages) {
if (storage.isEnabled()) {
return true;
}
}
return false;
}
}

View File

@ -22,4 +22,10 @@ public interface WriteableMetricStorage {
/** Records a measurement. */
void recordDouble(double value, Attributes attributes, Context context);
/**
* Returns {@code true} if the storage is actively recording measurements, and {@code false}
* otherwise (i.e. noop / empty metric storage is installed).
*/
boolean isEnabled();
}

View File

@ -8,6 +8,7 @@ package io.opentelemetry.sdk.metrics;
import static org.assertj.core.api.Assertions.assertThat;
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import io.opentelemetry.sdk.metrics.internal.MeterConfig;
import io.opentelemetry.sdk.metrics.internal.descriptor.Advice;
import io.opentelemetry.sdk.metrics.internal.exemplar.ExemplarFilter;
import io.opentelemetry.sdk.metrics.internal.state.MeterProviderSharedState;
@ -24,7 +25,7 @@ class InstrumentBuilderTest {
TestClock.create(), Resource.getDefault(), ExemplarFilter.alwaysOff(), 0);
static final InstrumentationScopeInfo SCOPE = InstrumentationScopeInfo.create("scope-name");
public static final MeterSharedState METER_SHARED_STATE =
MeterSharedState.create(SCOPE, Collections.emptyList());
MeterSharedState.create(SCOPE, Collections.emptyList(), MeterConfig.defaultConfig());
@Test
void stringRepresentation() {

View File

@ -11,7 +11,16 @@ 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 static java.util.stream.Collectors.groupingBy;
import io.opentelemetry.api.incubator.metrics.ExtendedDoubleCounter;
import io.opentelemetry.api.incubator.metrics.ExtendedDoubleGauge;
import io.opentelemetry.api.incubator.metrics.ExtendedDoubleHistogram;
import io.opentelemetry.api.incubator.metrics.ExtendedDoubleUpDownCounter;
import io.opentelemetry.api.incubator.metrics.ExtendedLongCounter;
import io.opentelemetry.api.incubator.metrics.ExtendedLongGauge;
import io.opentelemetry.api.incubator.metrics.ExtendedLongHistogram;
import io.opentelemetry.api.incubator.metrics.ExtendedLongUpDownCounter;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import io.opentelemetry.sdk.internal.ScopeConfigurator;
@ -20,7 +29,7 @@ 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.concurrent.atomic.AtomicLong;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
@ -37,50 +46,136 @@ class MeterConfigTest {
// Disable meterB. Since meters are enabled by default, meterA and meterC are enabled.
.addMeterConfiguratorCondition(nameEquals("meterB"), disabled())
.registerMetricReader(reader)
// Register drop aggregation for all instruments of meterD. Instruments are disabled if
// their relevant MeterConfig is disabled, or if there are no resolved views which
// consume the measurements.
.registerView(
InstrumentSelector.builder().setMeterName("meterD").build(),
View.builder().setAggregation(Aggregation.drop()).build())
.build();
Meter meterA = meterProvider.get("meterA");
Meter meterB = meterProvider.get("meterB");
Meter meterC = meterProvider.get("meterC");
Meter meterD = meterProvider.get("meterD");
AtomicLong meterAInvocations = new AtomicLong();
AtomicLong meterBInvocations = new AtomicLong();
AtomicLong meterCInvocations = new AtomicLong();
AtomicLong meterDInvocations = new AtomicLong();
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));
// Record measurements to each instrument type
recordToMeterInstruments(meterA, meterAInvocations);
recordToMeterInstruments(meterB, meterBInvocations);
recordToMeterInstruments(meterC, meterCInvocations);
recordToMeterInstruments(meterD, meterDInvocations);
// 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);
metrics.stream().collect(groupingBy(MetricData::getInstrumentationScopeInfo));
assertThat(metricsByScope.get(InstrumentationScopeInfo.create("meterA"))).hasSize(14);
assertThat(metricsByScope.get(InstrumentationScopeInfo.create("meterB"))).isNull();
assertThat(metricsByScope.get(InstrumentationScopeInfo.create("meterC"))).hasSize(6);
assertThat(metricsByScope.get(InstrumentationScopeInfo.create("meterC"))).hasSize(14);
assertThat(metricsByScope.get(InstrumentationScopeInfo.create("meterD"))).isNull();
});
// Only async callbacks from meterA and meterC should be invoked
assertThat(meterAInvocations.get()).isPositive();
assertThat(meterBInvocations.get()).isZero();
assertThat(meterCInvocations.get()).isPositive();
assertThat(meterDInvocations.get()).isZero();
// Instruments from meterA and meterC are enabled, meterC is not enabled
assertMeterInstrumentsEnabled(meterA, /* expectedEnabled= */ true);
assertMeterInstrumentsEnabled(meterB, /* expectedEnabled= */ false);
assertMeterInstrumentsEnabled(meterC, /* expectedEnabled= */ true);
assertMeterInstrumentsEnabled(meterD, /* expectedEnabled= */ false);
}
private static void recordToMeterInstruments(Meter meter, AtomicLong asyncInvocationsCount) {
meter.counterBuilder("longCounter").build().add(1);
meter.counterBuilder("doubleCounter").ofDoubles().build().add(1);
meter
.counterBuilder("asyncLongCounter")
.buildWithCallback(
observable -> {
asyncInvocationsCount.incrementAndGet();
observable.record(1);
});
meter
.counterBuilder("asyncDoubleCounter")
.ofDoubles()
.buildWithCallback(
observable -> {
asyncInvocationsCount.incrementAndGet();
observable.record(1);
});
meter.upDownCounterBuilder("longUpDownCounter").build().add(1);
meter.upDownCounterBuilder("doubleUpDownCounter").ofDoubles().build().add(1);
meter
.upDownCounterBuilder("asyncLongUpDownCounter")
.buildWithCallback(
observable -> {
asyncInvocationsCount.incrementAndGet();
observable.record(1);
});
meter
.upDownCounterBuilder("asyncDoubleUpDownCounter")
.ofDoubles()
.buildWithCallback(
observable -> {
asyncInvocationsCount.incrementAndGet();
observable.record(1);
});
meter.histogramBuilder("doubleHistogram").build().record(1.0);
meter.histogramBuilder("longHistogram").ofLongs().build().record(1);
meter.gaugeBuilder("doubleGauge").build().set(1);
meter.gaugeBuilder("longGauge").ofLongs().build().set(1);
meter
.gaugeBuilder("asyncDoubleGauge")
.buildWithCallback(
observable -> {
asyncInvocationsCount.incrementAndGet();
observable.record(1.0);
});
meter
.gaugeBuilder("asyncLongGauge")
.ofLongs()
.buildWithCallback(
observable -> {
asyncInvocationsCount.incrementAndGet();
observable.record(1);
});
}
private static void assertMeterInstrumentsEnabled(Meter meter, boolean expectedEnabled) {
assertThat(
((ExtendedDoubleCounter) meter.counterBuilder("doubleCounter").ofDoubles().build())
.isEnabled())
.isEqualTo(expectedEnabled);
assertThat(((ExtendedLongCounter) meter.counterBuilder("longCounter").build()).isEnabled())
.isEqualTo(expectedEnabled);
assertThat(
((ExtendedDoubleUpDownCounter)
meter.upDownCounterBuilder("doubleUpDownCounter").ofDoubles().build())
.isEnabled())
.isEqualTo(expectedEnabled);
assertThat(
((ExtendedLongUpDownCounter) meter.upDownCounterBuilder("longUpDownCounter").build())
.isEnabled())
.isEqualTo(expectedEnabled);
assertThat(
((ExtendedDoubleHistogram) meter.histogramBuilder("doubleHistogram").build())
.isEnabled())
.isEqualTo(expectedEnabled);
assertThat(
((ExtendedLongHistogram) meter.histogramBuilder("longHistogram").ofLongs().build())
.isEnabled())
.isEqualTo(expectedEnabled);
assertThat(((ExtendedDoubleGauge) meter.gaugeBuilder("doubleGauge").build()).isEnabled())
.isEqualTo(expectedEnabled);
assertThat(((ExtendedLongGauge) meter.gaugeBuilder("longGauge").ofLongs().build()).isEnabled())
.isEqualTo(expectedEnabled);
}
@ParameterizedTest

View File

@ -13,6 +13,7 @@ import static org.mockito.Mockito.verify;
import io.github.netmikey.logunit.api.LogCapturer;
import io.opentelemetry.internal.testing.slf4j.SuppressLogger;
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import io.opentelemetry.sdk.metrics.internal.MeterConfig;
import io.opentelemetry.sdk.metrics.internal.descriptor.Advice;
import io.opentelemetry.sdk.metrics.internal.descriptor.InstrumentDescriptor;
import io.opentelemetry.sdk.metrics.internal.state.CallbackRegistration;
@ -36,7 +37,11 @@ class SdkObservableInstrumentTest {
@BeforeEach
void setup() {
meterSharedState =
spy(MeterSharedState.create(InstrumentationScopeInfo.empty(), Collections.emptyList()));
spy(
MeterSharedState.create(
InstrumentationScopeInfo.empty(),
Collections.emptyList(),
MeterConfig.defaultConfig()));
callbackRegistration =
CallbackRegistration.create(
Collections.singletonList(

View File

@ -114,5 +114,10 @@ class MetricStorageRegistryTest {
@Override
public void recordDouble(double value, Attributes attributes, Context context) {}
@Override
public boolean isEnabled() {
return true;
}
}
}

View File

@ -5,6 +5,7 @@
package io.opentelemetry.sdk.trace;
import io.opentelemetry.api.incubator.trace.ExtendedTracer;
import io.opentelemetry.api.trace.SpanBuilder;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.api.trace.TracerProvider;
@ -12,13 +13,13 @@ import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import io.opentelemetry.sdk.trace.internal.TracerConfig;
/** {@link SdkTracer} is SDK implementation of {@link Tracer}. */
final class SdkTracer implements Tracer {
final class SdkTracer implements ExtendedTracer {
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 InstrumentationScopeInfo instrumentationScopeInfo;
private final TracerConfig tracerConfig;
private final boolean tracerEnabled;
SdkTracer(
TracerSharedState sharedState,
@ -26,12 +27,12 @@ final class SdkTracer implements Tracer {
TracerConfig tracerConfig) {
this.sharedState = sharedState;
this.instrumentationScopeInfo = instrumentationScopeInfo;
this.tracerConfig = tracerConfig;
this.tracerEnabled = tracerConfig.isEnabled();
}
@Override
public SpanBuilder spanBuilder(String spanName) {
if (!tracerConfig.isEnabled()) {
if (!tracerEnabled) {
return NOOP_TRACER.spanBuilder(spanName);
}
if (spanName == null || spanName.trim().isEmpty()) {
@ -49,4 +50,9 @@ final class SdkTracer implements Tracer {
InstrumentationScopeInfo getInstrumentationScopeInfo() {
return instrumentationScopeInfo;
}
@Override
public boolean isEnabled() {
return tracerEnabled;
}
}

View File

@ -13,6 +13,7 @@ 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.incubator.trace.ExtendedTracer;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanId;
import io.opentelemetry.api.trace.Tracer;
@ -90,6 +91,10 @@ class TracerConfigTest {
.hasSpanId(grandchild.getSpanContext().getSpanId())
.hasParentSpanId(parent.getSpanContext().getSpanId())
.hasAttributes(Attributes.builder().put("c", "1").build()));
// tracerA and tracerC are enabled, tracerB is disabled.
assertThat(((ExtendedTracer) tracerA).isEnabled()).isTrue();
assertThat(((ExtendedTracer) tracerB).isEnabled()).isFalse();
assertThat(((ExtendedTracer) tracerA).isEnabled()).isTrue();
}
@ParameterizedTest