-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
[API Epic 1/4] Metric api prototype #2557
Conversation
Looks good 👍
Nice, I haven't seen this trick before 💥
This raises a flag or two--depends on how the global package is structured. I think there have been complaints that Noop implementations clutter the documentation, but I don't object.
Looks good (no error, 👍).
Yeah, I admit I don't like having two ways to do this, and I'd rather have batch observations than singleton observations if I am forced to choose. The existing API's support for both also sickens me, thus my support for changing the specification. (open-telemetry/opentelemetry-specification#2281) The I have weakly mixed feelings at this point. Providing singleton observation APIs seems to add more surface area while admittedly making the user's life easier. (Public service announcement: I'm working on a change to the SDK to meet @MadVikingGod in the middle here, in parallel. Thanks @MadVikingGod for taking on the API-side effort and really digging in!) |
This has been open for a bit, I'm bumping to see if I could get any feedback. I know it's a lot of removed code, and I'm sorry. Where I would especially like feedback is:
|
@MadVikingGod for your question 3 specifically,
I'm looking at whether there could be an option to do this that is semantic sugar for creating a meter-level callback and binding it in a simpler API. This leads back to a topic about generics, since we have both integer and floating-point cases. There's a question of whether the instrument options need to become int64/float64 type-aware. If so, we could have a type-specific option meant for use with asynchronous instruments. (A caveat is that if we come to a discussion about unregistering callbacks at the OTel specification level, we'll have to craft something that lets you unregister them too.) For example, in the instrument package:
(for convenience, I have also used:)
An adapter type:
The semantic sugar happens here :
and it would be used like:
|
For the ability to register a single callback what about this API: Callback API ? Could that be implemented by the SDK? I would assume that at instrument creation time the meter would check if config.callback is empty and the instrument is async (maybe we need to split async and sync configs?), and if there is a callback just wrap with something akin to
|
My first sense is that it's a lot of typing for the API author 😀 . I like the idea that the instrument is the observer, instead of the
Yes, as you described. It sounds like the
and the SDK would be required to internally register a callback and provide the simple adapters needed. 👍 |
So I forgot to include a My hangup on this approach is that instrument Options are now split between the package |
Another idea: have each asynchronous instrument support an additional
by multiple single-instrument callbacks:
|
TL;DR I still approve this PR. There's discussion above on how to make the instruments a little more convenient for singleton use. The specification proposal in open-telemetry/opentelemetry-specification#2317 suggests that callbacks could be unregistered, and gives the languages freedom on how to make this idiomatic. If accepted, I'd argue the |
So I don't see an appreciable difference between
I wonder why is it the |
I think the answer is no, you can't compare functions (compile time) and attempts to hash them at runtime panic. https://go.dev/play/p/r_lE0t_-CEs I don't care whether |
On more thought on the topic of Unregister, what if we had a different method for registering a callback that can be canceled? This solves the use case of Unregistering callbacks, and also has the benefit that most users won't have to deal with the callback token unless they need to use the feature. So my proposal is to have:
|
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package asyncfloat64 // import "go.opentelemetry.io/otel/metric/instrument/asyncfloat64" |
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.
I think this is not a good package name for the functionality. It does not reflect any "metric" functionality. @rakyll would like your input here, since I know you have good input for these cases.
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.
Bogdan, do you think splitting/organizing packages into type based units like here is a problem? Even though the package names don't represent what's in them, I don't have a good suggestion. asyncfloat64instrument would be too verbose and putting all instruments in the same package might be too big. I think the choice here is a fair compromise if we need to split instruments into multiple packages. Disclaimer: I didn't have much time to think to come up with a better structure.
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.
In golang the type name is not very important since majority of the times you don't use that in the assignments or declaration. That is one of the main reason I believe that the split in the package base on the input type does not make sense.
I think something like 'instrument.Float64Counter' and 'instrument.Float64ObserverCounter' is better because of that
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.
@bogdandrutu There are 12 instruments in total, (int64, float64) x (6 instruments). This prototype has them in 4 packages, 3 instruments each. How many packages would you propose having?
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.
So I created a change that explored this approach. Here.
There are pros and cons to both approaches. There is also a takeaway that I will be incorporating into this patch.
The pros for grouping everything into the instrument
package is that when looking for the kinds of instruments they are all documented together. This is a nice feature if we can have minimal other things in the package.
The downside of that approach is that the names become very unwieldy.
What is now a
asyncfloat64.UpDownCounter
Becomes a
insturment.AsyncFloat64UpDownCounter
The other pro is that we could get rid of the namespacing we do, the Meter Interface would then become the 12 Create functions + register. This was discussed in #2526, and this API in the PR is made this way because of that discussion.
The one takeaway I have is that the nonrecording implementations can be extracted into a separate package. That I will incorporate regardless of this discussion.
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.
Personally I prefer the one package approach, and that package can either be "metric" where Meter and MeterProvider are defined.
I also believe that "Sync" can be removed from the names to shorter the common case:
+ Meter
- ObservableInt64Counter(string, ...InstrumentOption) ObservableInt64Counter
- ObservableInt64UpDownCounter(string, ...InstrumentOption) ObservableInt64UpDownCounter
- ObservableInt64Gauge(string, ...InstrumentOption) ObservableInt64Gauge
- ObservableFloat64Counter(string, ...InstrumentOption) ObservableFloat64Counter
- ObservableFloat64UpDownCounter(string, ...InstrumentOption) ObservableFloat64UpDownCounter
- ObservableFloat64Gauge(string, ...InstrumentOption) ObservableFloat64Gauge
- Int64Counter(string, ...InstrumentOption) Int64Counter
- Int64UpDownCounter(string, ...InstrumentOption) Int64UpDownCounter
- Int64Histogram(string, ...InstrumentOption) Int64Histogram
- Float64Counter(string, ...InstrumentOption) Float64Counter
- Float64UpDownCounter(string, ...InstrumentOption) Float64UpDownCounter
- Float64Histogram(string, ...InstrumentOption) Float64Histogram
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.
I slightly prefer the one package approach. End-users can find all instrument
s in the same place. When the generics feature is ready, we can have intuitive naming for generics functions without creating a new package:
I have no opinion for Async/Observable naming.
+ Meter
- AsyncCounter(T, ...InstrumentOption) AsyncCounter
- AsyncUpDownCounter(T, ...InstrumentOption) AsyncUpDownCounter
- AsyncGauge(T, ...InstrumentOption) AsyncGauge
- Counter(T, ...InstrumentOption) Counter
- UpDownCounter(T, ...InstrumentOption) UpDownCounter
- Histogram(T, ...InstrumentOption) Histogram
When reviewing this there is one place we do stand out from the spec. The spec suggests (MAY) using I think using |
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.
Non-blocking feedback. Looks clean.
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.
I'm with @dashpole on slightly prefering InstrumentProvider
, but not blocking.
With the two preferences and no preference the other way I'll change it to InstrumentProvider |
Agree with your reasoning for the async, but I see very small value (I think it hurts actually) in having "sync" prefix, so because of that I think going with the recommendation "nothing for sync, and use observable for async" makes more sense, and shorten the common case. |
This is a series of patches to update the metrics API.
This change is ONLY the changes in the API package. It leaves portions of the repository[1] in a broken state
This is designed to capture feedback around #2526.
How is this different from the original proposal:
metric/instrument
package. This was done to provide more hints to what is and is not an insturmentAsynchronous
andSynchronous
are now unexported methods. This means that ALL instruments must embed these interfaces, but it also reduces the API surface area. This was done in anticipation of questions like "What doesSynchronous()
do?" "Nothing it is used to group instruments"NewCallback
, nowRegisterCallback
When doing this work I also found that this API would not be compliant with the spec. The spec requires that Asynchronous instruments (all flavors) accept a Callback Function as a creation parameter. Right now callbacks can only be registered via the
Meter.RegisterCallback
, and these callback will have a different shape from one registered at creation. The reason is a closure at creation won't be able to close over the instrument, so it will have to take the instrument as a parameter. For example:[1] Portions of the SDK broken by this change
// Shouldn't require the sdk
bridge/opencensus
internal/metric // Probably will be removed
// Actual SDK changes
sdk/metric
sdk/export/metric
// Requires the sdk/metric Exporter interface
exporters/prometheus
exporters/otlp/otlpmetric
exporters/stdout/stdoutmetric
exporters/otlp/otlpmetric/otlpmetrichttp
exporters/otlp/otlpmetric/otlpmetricgrpc
// Requires an exporter
example/opencensus
example/prometheus