-
Notifications
You must be signed in to change notification settings - Fork 839
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
Allow multiple views per instrument #3541
Conversation
- Split writeable api from readable api for storage - Create multi-writeable APIs for when multiple views are defined per instrument. - Add test to ensure multiple metrics can be exported - Fix minor issues in registry + sdk meter provider limiting multi-view registration.
Does not update tests.
...c/main/java/io/opentelemetry/sdk/metrics/internal/state/DuplicateMetricStorageException.java
Outdated
Show resolved
Hide resolved
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.
one little gripe & a corresponding nit, but this looks pretty solid to me.
meterProviderSharedState.getStartEpochNanos(), | ||
metricUpdater)); | ||
} catch (DuplicateMetricStorageException e) { | ||
logger.log(Level.WARNING, e, () -> "Failed to register metric."); |
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.
#nit: This message isn't expensive to compute so let's omit the supplier. Same applies to the other catch
log messages in this class.
logger.log(Level.WARNING, e, () -> "Failed to register metric."); | |
logger.log(Level.WARNING, "Failed to register metric.", e); |
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.
@jsuereth something for a follow-on PR
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.
Good point, the expensive calculation is in a downstream branch. Once I fix the error messages for better provenance we can account for this.
* @return The list of {@link View}s for this instrument in registered order, or a default | ||
* aggregation view. | ||
*/ | ||
public List<View> findViews(InstrumentDescriptor descriptor, InstrumentationLibraryInfo meter) { |
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.
The only remaining usages of the singular version of this method above (public View findView(InstrumentDescriptor descriptor, InstrumentationLibraryInfo meter)
) are in tests, so I believe it can be removed.
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.
Great catch, will remove in next cleanup CL. Thought I had removed it in this branch, but apparently missed it.
} | ||
|
||
/** Registers new asynchronous storage associated with a given {@code long} instrument. */ | ||
public final MetricStorage registerLongAsynchronousInstrument( | ||
public final void registerLongAsynchronousInstrument( |
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.
These register methods are fairly repetitive. Could DRY it up with something like:
public final WriteableMetricStorage registerSynchronousMetricStorage(
InstrumentDescriptor instrument, MeterProviderSharedState meterProviderSharedState) {
List<SynchronousMetricStorage<?>> storage =
register(
instrument,
meterProviderSharedState,
view ->
SynchronousMetricStorage.create(
view,
instrument,
meterProviderSharedState.getResource(),
getInstrumentationLibraryInfo(),
meterProviderSharedState.getStartEpochNanos()));
if (storage.size() == 1) {
return storage.get(0);
}
// If the size is 0, we return an, effectively, no-op writer.
return new MultiWritableMetricStorage(storage);
}
public final void registerLongAsynchronousInstrument(
InstrumentDescriptor instrument,
MeterProviderSharedState meterProviderSharedState,
Consumer<ObservableLongMeasurement> metricUpdater) {
register(
instrument,
meterProviderSharedState,
view ->
AsynchronousMetricStorage.longAsynchronousAccumulator(
view,
instrument,
meterProviderSharedState.getResource(),
getInstrumentationLibraryInfo(),
meterProviderSharedState.getStartEpochNanos(),
metricUpdater));
}
..
private <T extends MetricStorage> List<T> register(
InstrumentDescriptor instrument,
MeterProviderSharedState meterProviderSharedState,
Function<View, T> storageFunction) {
List<View> views =
meterProviderSharedState
.getViewRegistry()
.findViews(instrument, getInstrumentationLibraryInfo());
List<T> storage = new ArrayList<>(views.size());
for (View view : views) {
try {
storage.add(getMetricStorageRegistry().register(storageFunction.apply(view)));
} catch (DuplicateMetricStorageException e) {
logger.log(Level.WARNING, e, () -> "Failed to register metric.");
}
}
return storage;
}
If you want to do this, you'd also have to change the constructor of MultiWritableMetricsStorage
:
MultiWritableMetricStorage(List<? extends WriteableMetricStorage> metrics)
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.
There's cleanup/fixes coming here in future PRs. TL;DR; we likely need a different registration mechanism for sync vs. async. Happy to discuss the details if you're interested, but expect these to diverge when we fix the issue that async instruments collect multiple times per collection period.
This does NOT