-
Notifications
You must be signed in to change notification settings - Fork 849
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
Fix metric sdk when multiple readers are present #4436
Changes from 3 commits
3602eb4
148cc51
ec9f1b8
ab11d74
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,24 +13,18 @@ | |
import io.opentelemetry.sdk.metrics.data.MetricData; | ||
import io.opentelemetry.sdk.metrics.export.MetricReader; | ||
import io.opentelemetry.sdk.metrics.internal.exemplar.ExemplarFilter; | ||
import io.opentelemetry.sdk.metrics.internal.export.CollectionHandle; | ||
import io.opentelemetry.sdk.metrics.internal.export.CollectionInfo; | ||
import io.opentelemetry.sdk.metrics.internal.export.MetricProducer; | ||
import io.opentelemetry.sdk.metrics.internal.export.RegisteredReader; | ||
import io.opentelemetry.sdk.metrics.internal.state.MeterProviderSharedState; | ||
import io.opentelemetry.sdk.metrics.internal.view.ViewRegistry; | ||
import io.opentelemetry.sdk.resources.Resource; | ||
import java.io.Closeable; | ||
import java.util.ArrayList; | ||
import java.util.Collection; | ||
import java.util.Collections; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Set; | ||
import java.util.concurrent.TimeUnit; | ||
import java.util.concurrent.atomic.AtomicBoolean; | ||
import java.util.concurrent.atomic.AtomicLong; | ||
import java.util.function.Supplier; | ||
import java.util.logging.Logger; | ||
|
||
/** SDK implementation for {@link MeterProvider}. */ | ||
|
@@ -41,10 +35,8 @@ public final class SdkMeterProvider implements MeterProvider, Closeable { | |
|
||
private final ComponentRegistry<SdkMeter> registry; | ||
private final MeterProviderSharedState sharedState; | ||
private final Map<CollectionHandle, CollectionInfo> collectionInfoMap; | ||
private final List<RegisteredReader> registeredReaders; | ||
private final AtomicBoolean isClosed = new AtomicBoolean(false); | ||
private final AtomicLong lastCollectionTimestamp; | ||
private final long minimumCollectionIntervalNanos; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The The refactor negates the need for it and assists in reducing complexity. |
||
|
||
/** | ||
* Returns a new {@link SdkMeterProviderBuilder} for {@link SdkMeterProvider}. | ||
|
@@ -56,38 +48,26 @@ public static SdkMeterProviderBuilder builder() { | |
} | ||
|
||
SdkMeterProvider( | ||
List<MetricReader> metricReaders, | ||
List<RegisteredReader> registeredReaders, | ||
Clock clock, | ||
Resource resource, | ||
ViewRegistry viewRegistry, | ||
ExemplarFilter exemplarFilter, | ||
long minimumCollectionIntervalNanos) { | ||
ExemplarFilter exemplarFilter) { | ||
this.sharedState = | ||
MeterProviderSharedState.create(clock, resource, viewRegistry, exemplarFilter); | ||
this.registeredReaders = registeredReaders; | ||
for (RegisteredReader registeredReader : registeredReaders) { | ||
registeredReader.getReader().register(new LeasedMetricProducer(registeredReader)); | ||
jack-berg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
this.registry = | ||
new ComponentRegistry<>( | ||
instrumentationLibraryInfo -> new SdkMeter(sharedState, instrumentationLibraryInfo)); | ||
this.lastCollectionTimestamp = | ||
new AtomicLong(clock.nanoTime() - minimumCollectionIntervalNanos); | ||
this.minimumCollectionIntervalNanos = minimumCollectionIntervalNanos; | ||
|
||
// Here we construct our own unique handle ids for this SDK. | ||
// These are guaranteed to be unique per-reader for this SDK, and only this SDK. | ||
// These are *only* mutated in our constructor, and safe to use concurrently after construction. | ||
Set<CollectionHandle> collectors = CollectionHandle.mutableSet(); | ||
collectionInfoMap = new HashMap<>(); | ||
Supplier<CollectionHandle> handleSupplier = CollectionHandle.createSupplier(); | ||
for (MetricReader metricReader : metricReaders) { | ||
CollectionHandle handle = handleSupplier.get(); | ||
collectionInfoMap.put(handle, CollectionInfo.create(handle, collectors, metricReader)); | ||
metricReader.register(new LeasedMetricProducer(handle)); | ||
collectors.add(handle); | ||
} | ||
instrumentationLibraryInfo -> | ||
new SdkMeter(sharedState, instrumentationLibraryInfo, registeredReaders)); | ||
} | ||
|
||
@Override | ||
public MeterBuilder meterBuilder(String instrumentationScopeName) { | ||
if (collectionInfoMap.isEmpty()) { | ||
if (registeredReaders.isEmpty()) { | ||
return MeterProvider.noop().meterBuilder(instrumentationScopeName); | ||
} | ||
if (instrumentationScopeName == null || instrumentationScopeName.isEmpty()) { | ||
|
@@ -102,12 +82,12 @@ public MeterBuilder meterBuilder(String instrumentationScopeName) { | |
* resulting {@link CompletableResultCode} completes when all complete. | ||
*/ | ||
public CompletableResultCode forceFlush() { | ||
if (collectionInfoMap.isEmpty()) { | ||
if (registeredReaders.isEmpty()) { | ||
return CompletableResultCode.ofSuccess(); | ||
} | ||
List<CompletableResultCode> results = new ArrayList<>(); | ||
for (CollectionInfo collectionInfo : collectionInfoMap.values()) { | ||
results.add(collectionInfo.getReader().forceFlush()); | ||
for (RegisteredReader registeredReader : registeredReaders) { | ||
results.add(registeredReader.getReader().forceFlush()); | ||
} | ||
return CompletableResultCode.ofAll(results); | ||
} | ||
|
@@ -121,11 +101,11 @@ public CompletableResultCode shutdown() { | |
LOGGER.info("Multiple close calls"); | ||
return CompletableResultCode.ofSuccess(); | ||
} | ||
if (collectionInfoMap.isEmpty()) { | ||
if (registeredReaders.isEmpty()) { | ||
return CompletableResultCode.ofSuccess(); | ||
} | ||
List<CompletableResultCode> results = new ArrayList<>(); | ||
for (CollectionInfo info : collectionInfoMap.values()) { | ||
for (RegisteredReader info : registeredReaders) { | ||
results.add(info.getReader().shutdown()); | ||
} | ||
return CompletableResultCode.ofAll(results); | ||
|
@@ -139,37 +119,19 @@ public void close() { | |
|
||
/** Helper class to expose registered metric exports. */ | ||
private class LeasedMetricProducer implements MetricProducer { | ||
private final CollectionHandle handle; | ||
|
||
LeasedMetricProducer(CollectionHandle handle) { | ||
this.handle = handle; | ||
private final RegisteredReader registeredReader; | ||
|
||
LeasedMetricProducer(RegisteredReader registeredReader) { | ||
this.registeredReader = registeredReader; | ||
} | ||
|
||
@Override | ||
public Collection<MetricData> collectAllMetrics() { | ||
Collection<SdkMeter> meters = registry.getComponents(); | ||
// Suppress too-frequent-collection. | ||
long currentNanoTime = sharedState.getClock().nanoTime(); | ||
long pastNanoTime = lastCollectionTimestamp.get(); | ||
// It hasn't been long enough since the last collection. | ||
boolean disableSynchronousCollection = | ||
(currentNanoTime - pastNanoTime) < minimumCollectionIntervalNanos; | ||
// If we're not disabling metrics, write the current collection time. | ||
// We don't care if this happens in more than one thread, suppression is optimistic, and the | ||
// interval is small enough some jitter isn't important. | ||
if (!disableSynchronousCollection) { | ||
lastCollectionTimestamp.lazySet(currentNanoTime); | ||
} | ||
CollectionInfo info = collectionInfoMap.get(handle); | ||
if (info == null) { | ||
throw new IllegalStateException( | ||
"No collection info for handle, this is a bug in the OpenTelemetry SDK."); | ||
} | ||
|
||
List<MetricData> result = new ArrayList<>(); | ||
for (SdkMeter meter : meters) { | ||
result.addAll( | ||
meter.collectAll(info, sharedState.getClock().now(), disableSynchronousCollection)); | ||
result.addAll(meter.collectAll(registeredReader, sharedState.getClock().now())); | ||
} | ||
return Collections.unmodifiableCollection(result); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CollectionHandle and CollectionInfo had overlapping responsibilities. I've replaced them with a single class with a name which more clearly conveys its responsibilities:
RegisteredReader
.