-
Notifications
You must be signed in to change notification settings - Fork 255
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!: refactor propagators to add #fields #638
Conversation
I think this is ready for review. The outstanding CI failures are in the Jaeger propagator, which I (or @fhwang) still need to update. |
I'm not sure how to square the B3 propagator requirements with the configuration options for
and
while the latter only allows configuring EITHER I can add a |
It seems like there should be three formats for b3 in the environment variable specification. I opened open-telemetry/opentelemetry-specification#1562 to discuss options. |
We discussed this at the maintainers meeting today and I opened a PR based on the discusson: open-telemetry/opentelemetry-specification#1570. tl; dr:
|
Looks like the PR was updated to always prefer |
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.
Didn't have a lot to add, just some questions around symmetry and "food for thought"
# @param [optional Setter] setter If the optional setter is provided, it | ||
# will be used to write context into the carrier, otherwise the default | ||
# text map setter will be used. | ||
def inject(carrier, context: Context.current, setter: Context::Propagation.text_map_setter) |
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.
How would you feel about using keyword
args for all of the parameters to inject/extract
?
I know for some it is idiomatic to have treated some arguments as options
hash but I personally prefer keeping method arguments symmetric.
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 general, we've opted to use keyword args for optional args or where a method has "many" args. In this case, the most common usage is inject(carrier)
. inject(carrier: carrier)
doesn't increase clarity, but increases verbosity.
def inject(carrier, context: Context.current, setter: Context::Propagation.text_map_setter) | ||
baggage = context[ContextKeys.baggage_key] | ||
|
||
return if baggage.nil? || baggage.empty? |
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.
return if baggage.nil? || baggage.empty? | |
return if baggage&.empty? |
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 results of those expressions differ:
irb(main):001:0> b = ''
=> ""
irb(main):002:0> b.nil? || b.empty?
=> true
irb(main):003:0> b&.empty?
=> true
irb(main):004:0> b = nil
=> nil
irb(main):005:0> b.nil? || b.empty?
=> true
irb(main):006:0> b&.empty?
=> 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.
!!baggage&.empty?
does look sad doesn't it. Oh falsey
Nil
s!
end | ||
|
||
context.set_value(ContextKeys.baggage_key, baggage) | ||
rescue StandardError |
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.
Would we want to log errors in inject/extract
to aid in debugging?
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's a good question. The concern is that it could become very noisy. We could log at a debug
level. I think we're also using exceptions for control flow in some propagators (traceparent
perhaps?), which is not ideal. Here we're just being defensive, so logging is probably the right thing to do.
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.
👍🏼
# @param [Array<#inject, #extract, #fields>] propagators An array of | ||
# text map propagators | ||
def compose_propagators(propagators) | ||
raise ArgumentError, 'propagators must be a non-nil array' unless propagators.is_a?(Array) |
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.
Should we treat Empty arrays the same as 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.
It is not technically wrong to pass an empty array here, and there are ways this could happen that result in configuration warning messages already. It might be useful instead to recognize the empty case and return a NoopTextMapPropagator
.
injectors = @injectors || @propagators | ||
injectors.each do |injector| | ||
injector.inject(carrier, context: context, setter: setter) | ||
rescue => e # rubocop:disable Style/RescueStandardError |
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.
May I ask that we qualify the exception type (here and in other places)?
rescue => e # rubocop:disable Style/RescueStandardError | |
rescue StandardError => 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.
I'll do some digging to see why we do this. I feel like we had a good reason, but I don't recall what it was.
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 was no good reason that I can see - fixing.
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 only fixing the propagator-related ones in this PR. The others can be addressed in a separate PR.
injectors.each do |injector| | ||
injector.inject(carrier, context: context, setter: setter) | ||
rescue => e # rubocop:disable Style/RescueStandardError | ||
OpenTelemetry.logger.warn "Error in CompositePropagator#inject #{e.message}" |
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 believe that Logger#warn
will return true
. This should return nil
correct?
irb(main):003:0> l = Logger.new("/dev/null")
=> #<Logger:0x00007fb55c02f738 @level=0, @progname=nil, @default_formatter=#<Logger::Formatter:0x00007fb55c02f6e8 @datetime_format=nil>, @formatter=nil, @logdev=#<Logger::LogDevice:0x00007fb55c02f300 @shift_period_suffix="%Y%m%d", @shift_size=1048576, @shift_age=0, @filename="/dev/null", @dev=#<File:/dev/null>, @mon_mutex=#<Thread::Mutex:0x00007fb55c02f0f8>, @mon_mutex_owner_object_id=70208454752640, @mon_owner=nil, @mon_count=0>>
irb(main):004:0> l.warn "afds"
=> true
after { instrumentation.instance_variable_set(:@installed, false) } | ||
after do | ||
# Force re-install of instrumentation | ||
instrumentation.instance_variable_set(:@installed, 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 realize this is not introduced in the PR but what is force re-reinstall doing? Should there be a reset
method similar to that of exporter
?
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.
reset
is only defined for the InMemorySpanExporter
- it isn't part of the Exporter
API - arguably that exporter should be moved out of the SDK to the common package (or somewhere else).
The force-reinstall is used in instrumentation tests (only) to avoid state leakage between tests. Re-installing instrumentation is not a supported operation in general.
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.
👍🏼
Status: I've merged the baggage changes & resolved conflicts. The outstanding piece is updating the |
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.
Thanks for the huge refactor @fbogsany!
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.
🥇
Fixes #613
This is a large refactor of propagators. Part of the goal is to add the
#fields
method, however this also uncovered quite a few bugs, discrepancies, incorrect documentation, and deviation from the spec.Important changes:
TextMapInjector
s andTextMapExtractor
s with unifiedTextMapPropagator
scarrier
passed to#inject
is expected to be mutable - inject now returnsnil
instead of the modifiedcarrier
- the spec states that "Carriers used at Inject are expected to be mutable." (although there is some later inconsistency)#fields
for eachTextMapPropagator
TextMapPropagator
rather thanPropagator
- the latter is an abstract concept in the spec, with only the former currently defined - the spec makes it clear that "A binary Propagator type will be added in the future"TextMapPropagator
in the API: a wrapper for aTextMapInjector
and aTextMapExtractor
pair, aCompositeTextMapPropagator
that composes either a list of propagators or a list of TextMapInjectors and a list of
TextMapExtractor`s, and a default no-op text map propagatorTODO