-
Notifications
You must be signed in to change notification settings - Fork 250
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
feat: Rework Meter and MeterProvider API #1442
feat: Rework Meter and MeterProvider API #1442
Conversation
|
||
Naming/VariableNumber: | ||
Enabled: false |
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 happy to revert any of these style changes, just let me know
nit: For this one, I think that naming variables with something_1
and something_2
in tests is easier to read
|
||
Style/IfUnlessModifier: | ||
Enabled: false |
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: When this is enabled, we can be forced into writing long lines with if/unless
at the end which I think may not be easy to read
raise InstrumentNameError if name.nil? | ||
raise InstrumentNameError if name.empty? | ||
raise InstrumentNameError unless NAME_REGEX.match?(name) | ||
raise InstrumentUnitError if unit && (!unit.ascii_only? || unit.size > 63) | ||
raise InstrumentDescriptionError if description && (description.size > 1023 || !utf8mb3_encoding?(description.dup)) |
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.
After reading Synchronous Instrument API I noticed the following
The API SHOULD be documented in a way to communicate to users that the
name
parameter needs to conform to the instrument name syntax.
The API SHOULD NOT validate thename
; that is left to
implementations of the API.
and
The
unit
parameter needs to support the instrument unit rule.
Meaning, the API MUST accept a case-sensitive string that supports
ASCII character encoding and can hold at least 63 characters.
The API SHOULD NOT validate theunit
.
Similar for name
and unit
in Asynchronous Instrument API
So, I think we should remove these validations from here and add yard doc comments instead. What do you think?
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 think we should remove these validations from here and add yard doc comments instead. What do you think?
I think that's reasonable, the API spec is explicit in that it should perform any validations against the name field, so that will be the responsibility of the SDK implementation. Yard docs seem like a good solution for this.
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.
Also in the "Metrics No-Op API Implementation" docs it makes clear No-Op Meters must not validate. For example:
The Meter MUST accept these parameters. However, the Meter MUST NOT validate any argument it receives.
https://opentelemetry.io/docs/specs/otel/metrics/noop/#counter-creation
raise InstrumentNameError unless NAME_REGEX.match?(name) | ||
raise InstrumentUnitError if unit && (!unit.ascii_only? || unit.size > 63) | ||
raise InstrumentDescriptionError if description && (description.size > 1023 || !utf8mb3_encoding?(description.dup)) | ||
name = name.downcase |
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.
This is new. From the Metrics' Meter API specification for the Instrument Name Syntax it says:
They are case-insensitive, ASCII strings.
So here I'm normalizing the name with downcase
before using it in @instrument_registry[name]
# @param callback [Proc, Array<Proc>] | ||
# Callback functions should: | ||
# - be reentrant safe; | ||
# - not take an indefinite amount of time; | ||
# - not make duplicate observations (more than one Measurement with the same attributes) | ||
# across all registered callbacks; | ||
def register_callback(callback) | ||
@callback.concat(Array(callback)) | ||
end | ||
|
||
# @param callback [Proc, Array<Proc>] | ||
# Callback functions should: | ||
# - be reentrant safe; | ||
# - not take an indefinite amount of time; | ||
# - not make duplicate observations (more than one Measurement with the same attributes) | ||
# across all registered callbacks; | ||
def unregister_callback(callback) | ||
@callback -= Array(callback) | ||
end |
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.
This is how I imagine register/unregister callback functions would look like in Ruby. Feel free to suggest any changes
https://opentelemetry.io/docs/specs/otel/metrics/api/#asynchronous-instrument-api
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 just realized that, with this, people are able to register callbacks to No-Op instances of Asynchronous Instruments. I think I should delete these methods' bodies and leave them to be implemented by the SDK
Updated in b0ba487
attr_reader :name, :unit, :description | ||
|
||
# @api private | ||
def initialize(name, unit: nil, description: nil) |
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.
- advice for implementations.
Users can provide advice, but its up to their discretion. Therefore, this API needs to be structured to accept advice, but MUST NOT obligate the user to provide it.
advice needs to be structured as described in instrument advice, with parameters that are general and specific to a particular instrument kind. The API SHOULD NOT validate advice.
Did you want to work this in as a follow up PR? https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/api.md#synchronous-instrument-api
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 that advice
has been added recently. I can include it in this 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.
Updated in 7cce415
I'm not sure how advice
will be exactly used, but I think of it as an "options" Hash with recommended values. For example in docs, the recommended bucket boundaries for a histogram. I imagine they will add standard names to be used in advice
raise InstrumentNameError if name.nil? | ||
raise InstrumentNameError if name.empty? | ||
raise InstrumentNameError unless NAME_REGEX.match?(name) | ||
raise InstrumentUnitError if unit && (!unit.ascii_only? || unit.size > 63) | ||
raise InstrumentDescriptionError if description && (description.size > 1023 || !utf8mb3_encoding?(description.dup)) |
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 think we should remove these validations from here and add yard doc comments instead. What do you think?
I think that's reasonable, the API spec is explicit in that it should perform any validations against the name field, so that will be the responsibility of the SDK implementation. Yard docs seem like a good solution for this.
Co-authored-by: Robert <[email protected]>
…thoughtbot/opentelemetry-ruby into elias/meter-and-instrument-creation-api
If you squash&merge, feel free to use the PR's description in the commit message (and add/edit anything). Otherwise, let me know and I can re-create commits with better messages :D |
@elias19r I'm about ready to merge, there's some branch conflicts that need to be resolved first though. |
module OpenTelemetry | ||
module Metrics | ||
module Instrument | ||
# https://opentelemetry.io/docs/reference/specification/metrics/api/#asynchronous-instrument-api |
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.
This link didn't work for me. I found the doc at:
# https://opentelemetry.io/docs/reference/specification/metrics/api/#asynchronous-instrument-api | |
# https://opentelemetry.io/docs/specs/otel/metrics/api/#asynchronous-instrument-api |
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 catch! I think they changed it. https://opentelemetry.io/docs/reference/specification/metrics/api/#asynchronous-instrument-api is redirecting to https://opentelemetry.io/docs/specs/otel/metrics/api/#asynchronous-instrument-api with status 301. I'll update them!
Updated in e4cd5c2
@name = name | ||
@unit = unit || '' | ||
@description = description || '' | ||
@callback = callback ? Array(callback) : [] |
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: since this is an array of functions, can we pluralize the name as callbacks
?
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.
Sure, I can pluralize them!
I think the reason I initially didn't do that was to keep the same "symbol same" from the specification although this @callback
instance variable is internal.
But in Meter
API, in metrics_api/lib/opentelemetry/metrics/meter.rb
, should I pluralize the keyword arg callback:
as well in the create_observable_counter
, create_observable_gauge
, and create_observable_up_down_counter
methods?
Updated in 443d35e
# - not take an indefinite amount of time; | ||
# - not make duplicate observations (more than one Measurement with the same attributes) | ||
# across all registered callbacks; | ||
def register_callback(callback); end |
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.
Similarly, the plural form is preferable. I'd also like to use the "splat" form:
def register_callback(callback); end | |
def register_callbacks(*callback); end |
This allows calling as:
instr.register_callbacks(cb1)
instr.register_callbacks(cb2, cb3)
instead of
instr.register_callback(cb1)
instr.register_callback([cb2, cb3])
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 like the splat form, but I've been thinking if should actually support all those cases later in the implementation:
instr.register_callback(cb1)
instr.register_callback(cb2, cb3)
instr.register_callback([cb4, cb5]) # And support this as well?
# Just so it is consistent with `callbacks:` from the `Meter#create_observable_counter` API
In the implementation, I think we could support that with
def register_callbacks(*callbacks)
@callbacks.concat(callbacks.flatten)
end
Because otherwise it would feel odd to be able to pass an array of procs when creating an instrument, like
meter = MeterProvider.meter('test')
instr = meter.create_observable_counter('test', callbacks: [cb1, cb2])
but not be able to pass an array of procs when registering callbacks later
meter = MeterProvider.meter('test')
instr = meter.create_observable_counter('test')
instr.register_callbacks([cb1, cb2]) # ??
Let me know what you think! Updated in 9d9ad67
|
||
assert(instrument.register_callbacks(-> {}).nil?) | ||
assert(instrument.register_callbacks(-> {}, -> {}).nil?) | ||
assert(instrument.register_callbacks([-> {}, -> {}]).nil?) |
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.
assert(instrument.register_callbacks([-> {}, -> {}]).nil?) | |
assert(instrument.register_callbacks(-> {}, -> {}).nil?) |
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.
What do you think of allowing all these use cases:
instr.register_callback(cb1)
instr.register_callback(cb2, cb3)
instr.register_callback([cb4, cb5]) # And support this as well?
# Just so it is consistent with `callbacks:` from the `Meter#create_observable_counter` API
See #1442 (comment)
|
||
assert(instrument.unregister_callbacks(-> {}).nil?) | ||
assert(instrument.unregister_callbacks(-> {}, -> {}).nil?) | ||
assert(instrument.unregister_callbacks([-> {}, -> {}]).nil?) |
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.
assert(instrument.unregister_callbacks([-> {}, -> {}]).nil?) | |
assert(instrument.unregister_callbacks(-> {}, -> {}).nil?) |
@callbacks = callbacks ? Array(callbacks) : [] | ||
end | ||
|
||
# @param callbacks [Proc, Array<Proc>] |
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.
Technically, callbacks
is an Array<Proc>
now, always - I'm not sure how to document a "splat" param.
# across all registered callbacks; | ||
def register_callbacks(*callbacks); end | ||
|
||
# @param callbacks [Proc, Array<Proc>] |
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.
Same here.
# @param advice [optional Hash] Set of recommendations aimed at assisting | ||
# implementations in providing useful output with minimal configuration |
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.
Do we have a better description of advice
? I'm working through the spec, and I haven't found anything useful yet. I have no idea how to use it - Hash
is very generic - what are allowed key & value types?
I would have expected advice
to be a simple String
, with set
here not meaning an actual data structure, but I feel like I'm missing a lot of context.
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.
It seems advice
has been added to the specification recently. I'm not sure how it will be exactly used. For now, I'm thinking of it as an "options" Hash with recommended values. For example in docs, the recommended bucket boundaries for a histogram. I imagine they will define standard names to be used in advice
, maybe:
advice: {
histogram: {
explicit_bucket_boundaries: [0.1, 0.5, 1.0, 5.0, 10.0, 25.0, Float::INFINITY]
}
}
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.
Reading the SDK page, I found another reference to advice
in Default Aggregation:
The Default Aggregation informs the SDK to use the Instrument
kind
to select an aggregation andadvice
to influence aggregation configuration parameters (as noted in the "Selected Aggregation" column).
Instrument Kind Selected Aggregation [...] [...] Histogram Explicit Bucket Histogram Aggregation, with ExplicitBucketBoundaries
fromadvice
if provided
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 don't think this should block the current PR from being merged, but it seems that advice
has been renamed to advisory parameters
.
- API: https://opentelemetry.io/docs/specs/otel/metrics/api/#instrument-advisory-parameters
- SDK: https://opentelemetry.io/docs/specs/otel/metrics/sdk/#instrument-advisory-parameters
It has a mixed status in the API and is experimental in the SDK.
@robertlaurin @fbogsany Hi, I haven't had a lot of time to work on this, but I could start a follow-up PR adjusting the existing SDK code to the changes I'm making to the API here. thoughtbot#1 It is mainly about making the Proxy Instruments and SDK Instruments inherit from the API Instrument classes. If you get a chance to review it, let me know if that makes sense :) After that, I wonder if we could complete the implementation of one instrument (maybe Counter). I don't quite see the big picture yet, but I'm starting to understand the concepts of the data model for metrics. |
We interested in getting suport for metrics at least no auto instrumentation for ruby, it's shame that it still not have is maintainers can implement it? |
…trument-creation-api
I think I won't be able to keep up with those PRs, it's been a while since I read the docs. I'll close them but keep the branches in case the changes are useful. |
validation should be performed by the implementation of the API
SynchronousInstrument
andAsynchronousInstrument
name
required forMeter
NOOP_
to no-op constant instrumentsbecause their name should be case-insensitive
advice
to Synchronous Instruments