Skip to content
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

Metrics API : Provider , MeterProvider, Meter, SynchronousInstrument #1033

Merged
merged 20 commits into from
Nov 11, 2021

Conversation

lalitb
Copy link
Member

@lalitb lalitb commented Oct 25, 2021

Fixes # (issue)

Changes

Please provide a brief description of the changes here.

For significant contributions please make sure you have completed the following items:

  • CHANGELOG.md updated for non-trivial changes
  • Unit tests have been added
  • Changes in public API reviewed

@lalitb lalitb requested a review from a team October 25, 2021 15:53
@lalitb lalitb marked this pull request as draft October 25, 2021 15:53
@lalitb lalitb changed the title Metrics API : Provider , MeterProvider, Meter, SynchronousIntrument [WIP] Metrics API : Provider , MeterProvider, Meter, SynchronousIntrument Oct 25, 2021
@codecov
Copy link

codecov bot commented Oct 25, 2021

Codecov Report

Merging #1033 (5736834) into main (3807726) will decrease coverage by 0.44%.
The diff coverage is 69.82%.

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1033      +/-   ##
==========================================
- Coverage   94.85%   94.41%   -0.43%     
==========================================
  Files         151      158       +7     
  Lines        5971     6077     +106     
==========================================
+ Hits         5663     5737      +74     
- Misses        308      340      +32     
Impacted Files Coverage Δ
api/include/opentelemetry/metrics/noop.h 28.89% <28.89%> (ø)
api/include/opentelemetry/metrics/meter.h 100.00% <100.00%> (ø)
api/include/opentelemetry/metrics/meter_provider.h 100.00% <100.00%> (ø)
api/include/opentelemetry/metrics/provider.h 100.00% <100.00%> (ø)
...i/include/opentelemetry/metrics/sync_instruments.h 100.00% <100.00%> (ø)
api/test/metrics/meter_provider_test.cc 100.00% <100.00%> (ø)
api/test/metrics/noop_sync_instrument_test.cc 100.00% <100.00%> (ø)

@lalitb lalitb changed the title [WIP] Metrics API : Provider , MeterProvider, Meter, SynchronousIntrument [WIP] Metrics API : Provider , MeterProvider, Meter, SynchronousInstrument Oct 25, 2021
api/include/opentelemetry/metrics_new/meter.h Outdated Show resolved Hide resolved
api/include/opentelemetry/metrics_new/meter.h Outdated Show resolved Hide resolved
nostd::string_view description = "",
nostd::string_view unit = "",
const bool enabled = true,
void (*callback)(ObserverResult<float>)) noexcept = 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: You may want to have a variant of this that takes a void* state parameter and passes that to the callback (or hide that with templates).

I understand you need this API to be bin-compat so likely std::function is out. However, it's entirely likely these observables need to track their own internal state.

Copy link
Member Author

Choose a reason for hiding this comment

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

Does it mean having these two variants:
CreateFloatObservableCounter( .., .., .., callback ) // existing
and
CreateFloatObservableCounter(.., .., .., callback, void *)

Passing state through lambda/std::function would be the best way, but you are correct on issues with ABI-compat. I initially thought this was in the scope of SDK implementation ( as ObserverResult implementation is provided by SDK , and state can be passed through its constructor) but let me give it a thought. Probably we can add it separately from this PR if needed?

Copy link
Member Author

@lalitb lalitb Oct 28, 2021

Choose a reason for hiding this comment

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

Have modified the callback to contain the state. Passing void * or va_args has potential danger of run-time crash, so used KeyValueIterable for it as below:
void (*callback)(ObserverResult<double> &result, const common::KeyValueIterable &state)

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure this is what I meant.

Specifically, what you really want is to pass an instance of a class and its state, vs. a key-value iterable (which is immutable and you cannot change its state).

Maybe try an implementation of one of these methods against some kind of stateful backend where you pull values and see what works best.

I agree void* is unsafe, I'm not sure of a bin-compat alternative....

Copy link
Contributor

Choose a reason for hiding this comment

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

I did some of my own research and likely my understanding of best-practices around callbacks is off in C++. Stateful callbacks apparently commonly use a lambda to capture a local variable that contains state, and likely works here.

Specifically, just want to make sure you can do something like the following:

const MyCustomMetrics& LatestMetrics() {
   static std::unique_ptr<Storage> lastMetricPull;
   static long lastMetricPullTime = 0;
   if (lastMetricPullTime < someThreasholdAgainstCurrentTime) {
      delete lastMetricPull;
      lastMetricPull = expensiveCallForMetrics();
   }
}


void someNormalMethod() {
   meterProivder->CreateFloatObservable(..., ..., ..., [&](ObservableMeasuremnet m) {
      m.observe( LatestMetrics().someValue);
  });
}

I.e. as long as we have a mechanism to store long-running state in "raw" form, we're fine. I forgot lambdas do all sorts of things for you know, so I think your original signature was fine, sorry for the noise!

* @param value The increment amount. May be positive, negative or zero.
* @param attributes A set of attributes to associate with the count.
*/
virtual void Record(T value, const common::KeyValueIterable &attributes) noexcept = 0;
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: You need a variant of this method (in every synchronous instrument) that also takes "Context".

By default these value/attributes need to pull Context.current or the C++ equivalent.

Copy link
Member Author

Choose a reason for hiding this comment

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

Do we need to support explicit context at the instrumentation/api level? I was thinking of these measurements to be associated with the current context. As it's not there in specs too ( https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#record )

Copy link
Contributor

Choose a reason for hiding this comment

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

It's up to you. If you provide any explicit context-api, then I'd say you should also allow it here (we do in java).

The key is that context CAN be used in measurements to do exemplar sampling.

@jsuereth
Copy link
Contributor

Great progress @lalitb! Excited to see this!!!!

@lalitb lalitb marked this pull request as ready for review November 2, 2021 17:30
@lalitb lalitb changed the title [WIP] Metrics API : Provider , MeterProvider, Meter, SynchronousInstrument Metrics API : Provider , MeterProvider, Meter, SynchronousInstrument Nov 2, 2021
Copy link
Contributor

@jsuereth jsuereth left a comment

Choose a reason for hiding this comment

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

Looks good!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants