Skip to content
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
a202a21
swap handle async
fandreuz May 12, 2025
c83804d
static
fandreuz May 12, 2025
f4a4093
atomicref
fandreuz May 12, 2025
7039b42
set meter config
fandreuz May 12, 2025
0bd9d86
null checks
fandreuz May 12, 2025
73a42da
generic drop agg
fandreuz May 12, 2025
3e4ef5c
setEnabled
fandreuz May 12, 2025
9136311
disable storages
fandreuz May 12, 2025
6de7e98
ops
fandreuz May 12, 2025
b44530c
move down
fandreuz May 12, 2025
d9e39f1
unit tests
fandreuz May 12, 2025
c6c8f30
fix stuff and order
fandreuz May 12, 2025
b42e68a
Merge branch 'main' into 7051-metric-config
fandreuz May 16, 2025
3b6f149
fix cmpl
fandreuz May 16, 2025
285d43c
optimize setEnabled
fandreuz May 21, 2025
e656bd9
optimize setEnabled
fandreuz May 21, 2025
21e188e
fix test name
fandreuz May 21, 2025
03d159d
nonnnull
fandreuz May 21, 2025
bd141f6
new test
fandreuz May 21, 2025
b00d726
fix isEnabled
fandreuz May 21, 2025
fb846dc
new tests
fandreuz May 21, 2025
30d81f5
new tests
fandreuz May 21, 2025
21d36d5
memory mode
fandreuz May 21, 2025
4196b17
cc
fandreuz May 21, 2025
3c10154
aggregator reset tests
fandreuz May 21, 2025
9b12918
more tests
fandreuz May 21, 2025
c8e0c85
Merge remote-tracking branch 'fandreuz/7051-metric-config' into 7051-…
fandreuz May 21, 2025
b21fa7b
ops
fandreuz May 21, 2025
e58c13c
nn
fandreuz May 22, 2025
d747d66
clear
fandreuz May 22, 2025
6ca257d
no order
fandreuz May 22, 2025
5f33bee
Update sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/interna…
fandreuz May 22, 2025
e5de344
Update sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/interna…
fandreuz May 23, 2025
7ab9b3a
spotless
fandreuz May 23, 2025
f3e3b50
Simplify dynamic meter config mechanics
jack-berg Jun 16, 2025
f46b35e
Fix tests
jack-berg Sep 16, 2025
fbfc03c
Merge branch 'main' of https://github.com/open-telemetry/opentelemetr…
jack-berg Sep 16, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ final class SdkMeter implements Meter {
private final MeterProviderSharedState meterProviderSharedState;
private final InstrumentationScopeInfo instrumentationScopeInfo;
private final Map<RegisteredReader, MetricStorageRegistry> readerStorageRegistries;
private final boolean meterEnabled;

private boolean meterEnabled;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect this is going to need some sort of memory barrier around it. Either make it an AtomicBoolean, or provide some kind of synchronization around the update to it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jack-berg should this field be marked volatile ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In other similar situations we've punted on the volatile keyword with the argument that even the small performance impact of reading a volatile field vs a non-volatile field is too high a price to pay the dynamism we get from this feature, which will only be leveraged by a few users. I know that in theory not marking it volatile may mean that the changes are never seen by readers, but I'd like to see complaints of that in practice before paying the performance penalty.

In this particular instance, adding volatile isn't terribly consequential because the field is only read on collection. The read work happens down in DefaultSynchronousMetricStorage, which has an enabled field which is also marked non-volatile and which is read each and every time a measurement is recorded. So we could update SdkMeter#meterEnabled to be volatile, but I don't think we should make DefaultSynchronousMetricStorage#enabled volatile. So the question is, should we be mark SdkMeter#meterEnabled because we can, even though its not really consequential? Or leave it be for consistency with SdkTracer, SdkLogger, DefaultSynchronousMetricStorage#enabled?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair enough. I'm ok with it, if you're ok with it.


SdkMeter(
MeterProviderSharedState meterProviderSharedState,
Expand All @@ -103,6 +104,18 @@ final class SdkMeter implements Meter {
this.meterEnabled = meterConfig.isEnabled();
}

void updateMeterConfig(MeterConfig meterConfig) {
meterEnabled = meterConfig.isEnabled();

for (RegisteredReader registeredReader : readerStorageRegistries.keySet()) {
Collection<MetricStorage> storages =
Objects.requireNonNull(readerStorageRegistries.get(registeredReader)).getStorages();
for (MetricStorage storage : storages) {
storage.setEnabled(meterEnabled);
}
}
}

// Visible for testing
InstrumentationScopeInfo getInstrumentationScopeInfo() {
return instrumentationScopeInfo;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,10 @@ public final class SdkMeterProvider implements MeterProvider, Closeable {
private final List<MetricProducer> metricProducers;
private final MeterProviderSharedState sharedState;
private final ComponentRegistry<SdkMeter> registry;
private final ScopeConfigurator<MeterConfig> meterConfigurator;
private final AtomicBoolean isClosed = new AtomicBoolean(false);

private ScopeConfigurator<MeterConfig> meterConfigurator;

/** Returns a new {@link SdkMeterProviderBuilder} for {@link SdkMeterProvider}. */
public static SdkMeterProviderBuilder builder() {
return new SdkMeterProviderBuilder();
Expand Down Expand Up @@ -105,6 +106,15 @@ private MeterConfig getMeterConfig(InstrumentationScopeInfo instrumentationScope
return meterConfig == null ? MeterConfig.defaultConfig() : meterConfig;
}

void setMeterConfigurator(ScopeConfigurator<MeterConfig> meterConfigurator) {
this.meterConfigurator = meterConfigurator;
this.registry
.getComponents()
.forEach(
sdkMeter ->
sdkMeter.updateMeterConfig(getMeterConfig(sdkMeter.getInstrumentationScopeInfo())));
}

@Override
public MeterBuilder meterBuilder(String instrumentationScopeName) {
if (registeredReaders.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,19 @@
return sdkMeterProviderBuilder;
}

/** Reflectively set the {@link ScopeConfigurator} to the {@link SdkMeterProvider}. */
public static void setMeterConfigurator(
SdkMeterProvider sdkMeterProvider, ScopeConfigurator<MeterConfig> scopeConfigurator) {
try {
Method method =
SdkMeterProvider.class.getDeclaredMethod("setMeterConfigurator", ScopeConfigurator.class);
method.setAccessible(true);
method.invoke(sdkMeterProvider, scopeConfigurator);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
throw new IllegalStateException("Error calling setMeterConfigurator on SdkMeterProvider", e);

Check warning on line 61 in sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/SdkMeterProviderUtil.java

View check run for this annotation

Codecov / codecov/patch

sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/SdkMeterProviderUtil.java#L60-L61

Added lines #L60 - L61 were not covered by tests
}
}

/** Reflectively set the {@link ScopeConfigurator} to the {@link SdkMeterProviderBuilder}. */
public static SdkMeterProviderBuilder setMeterConfigurator(
SdkMeterProviderBuilder sdkMeterProviderBuilder,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import io.opentelemetry.sdk.metrics.data.AggregationTemporality;
import io.opentelemetry.sdk.metrics.data.DoubleExemplarData;
import io.opentelemetry.sdk.metrics.data.ExemplarData;
import io.opentelemetry.sdk.metrics.data.MetricData;
import io.opentelemetry.sdk.metrics.data.MetricDataType;
Expand All @@ -27,8 +26,9 @@
@Immutable
public interface Aggregator<T extends PointData, U extends ExemplarData> {
/** Returns the drop aggregator, an aggregator that drops measurements. */
static Aggregator<?, DoubleExemplarData> drop() {
return DropAggregator.INSTANCE;
@SuppressWarnings("unchecked")
static <T extends PointData, U extends ExemplarData> Aggregator<T, U> drop() {
return (Aggregator<T, U>) DropAggregator.INSTANCE;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import io.opentelemetry.sdk.metrics.data.AggregationTemporality;
import io.opentelemetry.sdk.metrics.data.DoubleExemplarData;
import io.opentelemetry.sdk.metrics.data.ExemplarData;
import io.opentelemetry.sdk.metrics.data.MetricData;
import io.opentelemetry.sdk.metrics.data.PointData;
Expand All @@ -25,7 +24,7 @@
* <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 DropAggregator implements Aggregator<PointData, DoubleExemplarData> {
public final class DropAggregator implements Aggregator<PointData, ExemplarData> {

private static final PointData POINT_DATA =
new PointData() {
Expand All @@ -50,16 +49,16 @@ public List<? extends ExemplarData> getExemplars() {
}
};

public static final Aggregator<PointData, DoubleExemplarData> INSTANCE = new DropAggregator();
public static final Aggregator<PointData, ExemplarData> INSTANCE = new DropAggregator();

private static final AggregatorHandle<PointData, DoubleExemplarData> HANDLE =
new AggregatorHandle<PointData, DoubleExemplarData>(ExemplarReservoir.doubleNoSamples()) {
private static final AggregatorHandle<PointData, ExemplarData> HANDLE =
new AggregatorHandle<PointData, ExemplarData>(ExemplarReservoir.anyNoSamples()) {
@Override
protected PointData doAggregateThenMaybeReset(
long startEpochNanos,
long epochNanos,
Attributes attributes,
List<DoubleExemplarData> exemplars,
List<ExemplarData> exemplars,
boolean reset) {
return POINT_DATA;
}
Expand All @@ -74,7 +73,7 @@ protected void doRecordDouble(double value) {}
private DropAggregator() {}

@Override
public AggregatorHandle<PointData, DoubleExemplarData> createHandle() {
public AggregatorHandle<PointData, ExemplarData> createHandle() {
return HANDLE;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ static ExemplarReservoir<LongExemplarData> longNoSamples() {
return NoopExemplarReservoir.LONG_INSTANCE;
}

/** An exemplar reservoir that stores no exemplars. */
static ExemplarReservoir<ExemplarData> anyNoSamples() {
return NoopExemplarReservoir.ANY_INSTANCE;
}

/**
* A double reservoir with fixed size that stores the given number of exemplars.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class NoopExemplarReservoir<T extends ExemplarData> implements ExemplarReservoir
new NoopExemplarReservoir<>();
static final NoopExemplarReservoir<DoubleExemplarData> DOUBLE_INSTANCE =
new NoopExemplarReservoir<>();
static final NoopExemplarReservoir<ExemplarData> ANY_INSTANCE = new NoopExemplarReservoir<>();

private NoopExemplarReservoir() {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,11 @@ public final class AsynchronousMetricStorage<T extends PointData, U extends Exem
private final RegisteredReader registeredReader;
private final MetricDescriptor metricDescriptor;
private final AggregationTemporality aggregationTemporality;
private final Aggregator<T, U> aggregator;
private final AttributesProcessor attributesProcessor;
private final MemoryMode memoryMode;
private final Aggregator<T, U> originalAggregator;

private Aggregator<T, U> aggregator;

/**
* This field is set to 1 less than the actual intended cardinality limit, allowing the last slot
Expand Down Expand Up @@ -100,6 +102,7 @@ private AsynchronousMetricStorage(
.getAggregationTemporality(metricDescriptor.getSourceInstrument().getType());
this.memoryMode = registeredReader.getReader().getMemoryMode();
this.aggregator = aggregator;
this.originalAggregator = aggregator;
this.attributesProcessor = attributesProcessor;
this.maxCardinality = maxCardinality - 1;
this.reusablePointsPool = new ObjectPool<>(aggregator::createReusablePoint);
Expand Down Expand Up @@ -309,6 +312,20 @@ private Collection<T> collectWithCumulativeAggregationTemporality() {
return currentPoints;
}

@Override
public void setEnabled(boolean enabled) {
if (enabled) {
if (aggregator == Aggregator.drop()) {
aggregator = originalAggregator;
}
} else {
aggregator = Aggregator.drop();

aggregatorHandles.clear();
lastPoints.clear();
}
}

@Override
public boolean isEmpty() {
return aggregator == Aggregator.drop();
Expand Down
Loading
Loading