Skip to content

Commit 8ea6895

Browse files
fandreuzjack-berg
andauthored
Add setMeterConfigurator support to MeterProvider (#7346)
Co-authored-by: Jack Berg <[email protected]> Co-authored-by: Jack Berg <[email protected]>
1 parent 1d8fa83 commit 8ea6895

File tree

14 files changed

+364
-48
lines changed

14 files changed

+364
-48
lines changed

sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/SdkMeter.java

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ final class SdkMeter implements Meter {
8888
private final MeterProviderSharedState meterProviderSharedState;
8989
private final InstrumentationScopeInfo instrumentationScopeInfo;
9090
private final Map<RegisteredReader, MetricStorageRegistry> readerStorageRegistries;
91-
private final boolean meterEnabled;
91+
92+
private boolean meterEnabled;
9293

9394
SdkMeter(
9495
MeterProviderSharedState meterProviderSharedState,
@@ -103,28 +104,41 @@ final class SdkMeter implements Meter {
103104
this.meterEnabled = meterConfig.isEnabled();
104105
}
105106

107+
void updateMeterConfig(MeterConfig meterConfig) {
108+
meterEnabled = meterConfig.isEnabled();
109+
110+
for (RegisteredReader registeredReader : readerStorageRegistries.keySet()) {
111+
Collection<MetricStorage> storages =
112+
Objects.requireNonNull(readerStorageRegistries.get(registeredReader)).getStorages();
113+
for (MetricStorage storage : storages) {
114+
storage.setEnabled(meterEnabled);
115+
}
116+
}
117+
}
118+
106119
// Visible for testing
107120
InstrumentationScopeInfo getInstrumentationScopeInfo() {
108121
return instrumentationScopeInfo;
109122
}
110123

111124
/** Collect all metrics for the meter. */
112125
Collection<MetricData> collectAll(RegisteredReader registeredReader, long epochNanos) {
113-
// Short circuit collection process if meter is disabled
114-
if (!meterEnabled) {
115-
return Collections.emptyList();
116-
}
117126
List<CallbackRegistration> currentRegisteredCallbacks;
118127
synchronized (callbackLock) {
119128
currentRegisteredCallbacks = new ArrayList<>(callbackRegistrations);
120129
}
121130
// Collections across all readers are sequential
122131
synchronized (collectLock) {
123-
for (CallbackRegistration callbackRegistration : currentRegisteredCallbacks) {
124-
callbackRegistration.invokeCallback(
125-
registeredReader, meterProviderSharedState.getStartEpochNanos(), epochNanos);
132+
// Only invoke callbacks if meter is enabled
133+
if (meterEnabled) {
134+
for (CallbackRegistration callbackRegistration : currentRegisteredCallbacks) {
135+
callbackRegistration.invokeCallback(
136+
registeredReader, meterProviderSharedState.getStartEpochNanos(), epochNanos);
137+
}
126138
}
127139

140+
// Collect even if meter is disabled. Storage is responsible for managing state and returning
141+
// empty metric if disabled.
128142
Collection<MetricStorage> storages =
129143
Objects.requireNonNull(readerStorageRegistries.get(registeredReader)).getStorages();
130144
List<MetricData> result = new ArrayList<>(storages.size());
@@ -275,7 +289,8 @@ WriteableMetricStorage registerSynchronousMetricStorage(InstrumentDescriptor ins
275289
reader,
276290
registeredView,
277291
instrument,
278-
meterProviderSharedState.getExemplarFilter())));
292+
meterProviderSharedState.getExemplarFilter(),
293+
meterEnabled)));
279294
}
280295
}
281296

@@ -301,7 +316,8 @@ SdkObservableMeasurement registerObservableMeasurement(
301316
}
302317
registeredStorages.add(
303318
registry.register(
304-
AsynchronousMetricStorage.create(reader, registeredView, instrumentDescriptor)));
319+
AsynchronousMetricStorage.create(
320+
reader, registeredView, instrumentDescriptor, meterEnabled)));
305321
}
306322
}
307323

sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/SdkMeterProvider.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,10 @@ public final class SdkMeterProvider implements MeterProvider, Closeable {
5252
private final List<MetricProducer> metricProducers;
5353
private final MeterProviderSharedState sharedState;
5454
private final ComponentRegistry<SdkMeter> registry;
55-
private final ScopeConfigurator<MeterConfig> meterConfigurator;
5655
private final AtomicBoolean isClosed = new AtomicBoolean(false);
5756

57+
private ScopeConfigurator<MeterConfig> meterConfigurator;
58+
5859
/** Returns a new {@link SdkMeterProviderBuilder} for {@link SdkMeterProvider}. */
5960
public static SdkMeterProviderBuilder builder() {
6061
return new SdkMeterProviderBuilder();
@@ -105,6 +106,15 @@ private MeterConfig getMeterConfig(InstrumentationScopeInfo instrumentationScope
105106
return meterConfig == null ? MeterConfig.defaultConfig() : meterConfig;
106107
}
107108

109+
void setMeterConfigurator(ScopeConfigurator<MeterConfig> meterConfigurator) {
110+
this.meterConfigurator = meterConfigurator;
111+
this.registry
112+
.getComponents()
113+
.forEach(
114+
sdkMeter ->
115+
sdkMeter.updateMeterConfig(getMeterConfig(sdkMeter.getInstrumentationScopeInfo())));
116+
}
117+
108118
@Override
109119
public MeterBuilder meterBuilder(String instrumentationScopeName) {
110120
if (registeredReaders.isEmpty()) {

sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/SdkMeterProviderUtil.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,19 @@ public static SdkMeterProviderBuilder setExemplarFilter(
4949
return sdkMeterProviderBuilder;
5050
}
5151

52+
/** Reflectively set the {@link ScopeConfigurator} to the {@link SdkMeterProvider}. */
53+
public static void setMeterConfigurator(
54+
SdkMeterProvider sdkMeterProvider, ScopeConfigurator<MeterConfig> scopeConfigurator) {
55+
try {
56+
Method method =
57+
SdkMeterProvider.class.getDeclaredMethod("setMeterConfigurator", ScopeConfigurator.class);
58+
method.setAccessible(true);
59+
method.invoke(sdkMeterProvider, scopeConfigurator);
60+
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
61+
throw new IllegalStateException("Error calling setMeterConfigurator on SdkMeterProvider", e);
62+
}
63+
}
64+
5265
/** Reflectively set the {@link ScopeConfigurator} to the {@link SdkMeterProviderBuilder}. */
5366
public static SdkMeterProviderBuilder setMeterConfigurator(
5467
SdkMeterProviderBuilder sdkMeterProviderBuilder,

sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/state/AsynchronousMetricStorage.java

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import io.opentelemetry.sdk.metrics.internal.aggregator.Aggregator;
2323
import io.opentelemetry.sdk.metrics.internal.aggregator.AggregatorFactory;
2424
import io.opentelemetry.sdk.metrics.internal.aggregator.AggregatorHandle;
25+
import io.opentelemetry.sdk.metrics.internal.aggregator.EmptyMetricData;
2526
import io.opentelemetry.sdk.metrics.internal.descriptor.InstrumentDescriptor;
2627
import io.opentelemetry.sdk.metrics.internal.descriptor.MetricDescriptor;
2728
import io.opentelemetry.sdk.metrics.internal.exemplar.ExemplarFilter;
@@ -86,12 +87,15 @@ public final class AsynchronousMetricStorage<T extends PointData, U extends Exem
8687
private long startEpochNanos;
8788
private long epochNanos;
8889

90+
private boolean enabled;
91+
8992
private AsynchronousMetricStorage(
9093
RegisteredReader registeredReader,
9194
MetricDescriptor metricDescriptor,
9295
Aggregator<T, U> aggregator,
9396
AttributesProcessor attributesProcessor,
94-
int maxCardinality) {
97+
int maxCardinality,
98+
boolean enabled) {
9599
this.registeredReader = registeredReader;
96100
this.metricDescriptor = metricDescriptor;
97101
this.aggregationTemporality =
@@ -102,6 +106,7 @@ private AsynchronousMetricStorage(
102106
this.aggregator = aggregator;
103107
this.attributesProcessor = attributesProcessor;
104108
this.maxCardinality = maxCardinality - 1;
109+
this.enabled = enabled;
105110
this.reusablePointsPool = new ObjectPool<>(aggregator::createReusablePoint);
106111
this.reusableHandlesPool = new ObjectPool<>(aggregator::createHandle);
107112
this.handleBuilder = ignored -> reusableHandlesPool.borrowObject();
@@ -125,7 +130,8 @@ private AsynchronousMetricStorage(
125130
AsynchronousMetricStorage<T, U> create(
126131
RegisteredReader registeredReader,
127132
RegisteredView registeredView,
128-
InstrumentDescriptor instrumentDescriptor) {
133+
InstrumentDescriptor instrumentDescriptor,
134+
boolean enabled) {
129135
View view = registeredView.getView();
130136
MetricDescriptor metricDescriptor =
131137
MetricDescriptor.create(view, registeredView.getViewSourceInfo(), instrumentDescriptor);
@@ -140,7 +146,8 @@ AsynchronousMetricStorage<T, U> create(
140146
metricDescriptor,
141147
aggregator,
142148
registeredView.getViewAttributesProcessor(),
143-
registeredView.getCardinalityLimit());
149+
registeredView.getCardinalityLimit(),
150+
enabled);
144151
}
145152

146153
/** Record callback measurement from {@link ObservableLongMeasurement}. */
@@ -207,8 +214,10 @@ public MetricData collect(
207214
aggregatorHandles.forEach(handleReleaser);
208215
aggregatorHandles.clear();
209216

210-
return aggregator.toMetricData(
211-
resource, instrumentationScopeInfo, metricDescriptor, result, aggregationTemporality);
217+
return enabled
218+
? aggregator.toMetricData(
219+
resource, instrumentationScopeInfo, metricDescriptor, result, aggregationTemporality)
220+
: EmptyMetricData.getInstance();
212221
}
213222

214223
private Collection<T> collectWithDeltaAggregationTemporality() {
@@ -310,7 +319,7 @@ private Collection<T> collectWithCumulativeAggregationTemporality() {
310319
}
311320

312321
@Override
313-
public boolean isEmpty() {
314-
return aggregator == Aggregator.drop();
322+
public void setEnabled(boolean enabled) {
323+
this.enabled = enabled;
315324
}
316325
}

sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/state/DefaultSynchronousMetricStorage.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,15 @@ public final class DefaultSynchronousMetricStorage<T extends PointData, U extend
7474
private final ConcurrentLinkedQueue<AggregatorHandle<T, U>> aggregatorHandlePool =
7575
new ConcurrentLinkedQueue<>();
7676

77+
private boolean enabled;
78+
7779
DefaultSynchronousMetricStorage(
7880
RegisteredReader registeredReader,
7981
MetricDescriptor metricDescriptor,
8082
Aggregator<T, U> aggregator,
8183
AttributesProcessor attributesProcessor,
82-
int maxCardinality) {
84+
int maxCardinality,
85+
boolean enabled) {
8386
this.registeredReader = registeredReader;
8487
this.metricDescriptor = metricDescriptor;
8588
this.aggregationTemporality =
@@ -90,6 +93,7 @@ public final class DefaultSynchronousMetricStorage<T extends PointData, U extend
9093
this.attributesProcessor = attributesProcessor;
9194
this.maxCardinality = maxCardinality - 1;
9295
this.memoryMode = registeredReader.getReader().getMemoryMode();
96+
this.enabled = enabled;
9397
}
9498

9599
// Visible for testing
@@ -99,6 +103,9 @@ Queue<AggregatorHandle<T, U>> getAggregatorHandlePool() {
99103

100104
@Override
101105
public void recordLong(long value, Attributes attributes, Context context) {
106+
if (!enabled) {
107+
return;
108+
}
102109
AggregatorHolder<T, U> aggregatorHolder = getHolderForRecord();
103110
try {
104111
AggregatorHandle<T, U> handle =
@@ -111,6 +118,9 @@ public void recordLong(long value, Attributes attributes, Context context) {
111118

112119
@Override
113120
public void recordDouble(double value, Attributes attributes, Context context) {
121+
if (!enabled) {
122+
return;
123+
}
114124
if (Double.isNaN(value)) {
115125
logger.log(
116126
Level.FINE,
@@ -131,9 +141,14 @@ public void recordDouble(double value, Attributes attributes, Context context) {
131141
}
132142
}
133143

144+
@Override
145+
public void setEnabled(boolean enabled) {
146+
this.enabled = enabled;
147+
}
148+
134149
@Override
135150
public boolean isEnabled() {
136-
return true;
151+
return enabled;
137152
}
138153

139154
/**
@@ -299,7 +314,7 @@ public MetricData collect(
299314
previousCollectionAggregatorHandles = aggregatorHandles;
300315
}
301316

302-
if (points.isEmpty()) {
317+
if (points.isEmpty() || !enabled) {
303318
return EmptyMetricData.getInstance();
304319
}
305320

sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/state/EmptyMetricStorage.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,9 @@ public void recordDouble(double value, Attributes attributes, Context context) {
4444
public boolean isEnabled() {
4545
return false;
4646
}
47+
48+
@Override
49+
public void setEnabled(boolean enabled) {
50+
// do nothing
51+
}
4752
}

sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/state/MetricStorage.java

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,5 @@ MetricData collect(
4848
long startEpochNanos,
4949
long epochNanos);
5050

51-
/**
52-
* Determines whether this storage is an empty metric storage.
53-
*
54-
* <p>Uses the reference comparison since {@link EmptyMetricStorage} is singleton.
55-
*
56-
* @return true if is empty.
57-
*/
58-
default boolean isEmpty() {
59-
return this == EmptyMetricStorage.INSTANCE;
60-
}
51+
void setEnabled(boolean enabled);
6152
}

sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/state/SynchronousMetricStorage.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ static <T extends PointData, U extends ExemplarData> SynchronousMetricStorage cr
4040
RegisteredReader registeredReader,
4141
RegisteredView registeredView,
4242
InstrumentDescriptor instrumentDescriptor,
43-
ExemplarFilter exemplarFilter) {
43+
ExemplarFilter exemplarFilter,
44+
boolean enabled) {
4445
View view = registeredView.getView();
4546
MetricDescriptor metricDescriptor =
4647
MetricDescriptor.create(view, registeredView.getViewSourceInfo(), instrumentDescriptor);
@@ -57,6 +58,7 @@ static <T extends PointData, U extends ExemplarData> SynchronousMetricStorage cr
5758
metricDescriptor,
5859
aggregator,
5960
registeredView.getViewAttributesProcessor(),
60-
registeredView.getCardinalityLimit());
61+
registeredView.getCardinalityLimit(),
62+
enabled);
6163
}
6264
}

sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/SdkMeterProviderTest.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@
2828
import io.opentelemetry.context.Scope;
2929
import io.opentelemetry.sdk.common.CompletableResultCode;
3030
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
31+
import io.opentelemetry.sdk.internal.ScopeConfigurator;
3132
import io.opentelemetry.sdk.metrics.data.MetricData;
3233
import io.opentelemetry.sdk.metrics.export.MetricReader;
34+
import io.opentelemetry.sdk.metrics.internal.MeterConfig;
3335
import io.opentelemetry.sdk.metrics.internal.SdkMeterProviderUtil;
3436
import io.opentelemetry.sdk.metrics.internal.view.ViewRegistry;
3537
import io.opentelemetry.sdk.resources.Resource;
@@ -47,6 +49,7 @@
4749
import java.util.concurrent.TimeoutException;
4850
import java.util.concurrent.atomic.AtomicBoolean;
4951
import java.util.function.Consumer;
52+
import org.assertj.core.api.Assertions;
5053
import org.junit.jupiter.api.Test;
5154
import org.junit.jupiter.api.extension.ExtendWith;
5255
import org.junit.jupiter.api.extension.RegisterExtension;
@@ -1030,6 +1033,34 @@ void resetForTest() {
10301033
sum -> sum.isCumulative().hasPointsSatisfying(point -> point.hasValue(1))));
10311034
}
10321035

1036+
private static ScopeConfigurator<MeterConfig> flipConfigurator(boolean enabled) {
1037+
return scopeInfo -> enabled ? MeterConfig.disabled() : MeterConfig.enabled();
1038+
}
1039+
1040+
@Test
1041+
void propagatesEnablementToMeterDirectly() {
1042+
SdkMeterProvider meterProvider =
1043+
SdkMeterProvider.builder().registerMetricReader(InMemoryMetricReader.create()).build();
1044+
SdkMeter meter = (SdkMeter) meterProvider.get("test");
1045+
boolean isEnabled = meter.isMeterEnabled();
1046+
1047+
meterProvider.setMeterConfigurator(flipConfigurator(isEnabled));
1048+
1049+
Assertions.assertThat(meter.isMeterEnabled()).isEqualTo(!isEnabled);
1050+
}
1051+
1052+
@Test
1053+
void propagatesEnablementToMeterByUtil() {
1054+
SdkMeterProvider sdkMeterProvider =
1055+
SdkMeterProvider.builder().registerMetricReader(InMemoryMetricReader.create()).build();
1056+
SdkMeter sdkMeter = (SdkMeter) sdkMeterProvider.get("test");
1057+
boolean isEnabled = sdkMeter.isMeterEnabled();
1058+
1059+
SdkMeterProviderUtil.setMeterConfigurator(sdkMeterProvider, flipConfigurator(isEnabled));
1060+
1061+
Assertions.assertThat(sdkMeter.isMeterEnabled()).isEqualTo(!isEnabled);
1062+
}
1063+
10331064
private static void registerViewForAllTypes(
10341065
SdkMeterProviderBuilder meterProviderBuilder, Aggregation aggregation) {
10351066
for (InstrumentType instrumentType : InstrumentType.values()) {

sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/SdkMeterTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import io.opentelemetry.api.metrics.Meter;
1919
import io.opentelemetry.api.metrics.MeterProvider;
2020
import io.opentelemetry.internal.testing.slf4j.SuppressLogger;
21+
import io.opentelemetry.sdk.metrics.internal.MeterConfig;
2122
import io.opentelemetry.sdk.metrics.internal.state.MetricStorageRegistry;
2223
import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader;
2324
import java.util.Locale;
@@ -481,4 +482,16 @@ void stringRepresentation() {
481482
+ "attributes={}"
482483
+ "}}");
483484
}
485+
486+
@Test
487+
void updateEnabled() {
488+
SdkMeterProvider sdkMeterProvider =
489+
SdkMeterProvider.builder().registerMetricReader(InMemoryMetricReader.create()).build();
490+
SdkMeter meter = (SdkMeter) sdkMeterProvider.get("test");
491+
492+
meter.updateMeterConfig(MeterConfig.disabled());
493+
assertThat(meter.isMeterEnabled()).isFalse();
494+
meter.updateMeterConfig(MeterConfig.enabled());
495+
assertThat(meter.isMeterEnabled()).isTrue();
496+
}
484497
}

0 commit comments

Comments
 (0)