Skip to content

Add support for B3 headers #80

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

Merged
merged 1 commit into from
Nov 1, 2019
Merged

Add support for B3 headers #80

merged 1 commit into from
Nov 1, 2019

Conversation

ocelotl
Copy link
Contributor

@ocelotl ocelotl commented Oct 17, 2019

@carlosalberto The example added here is failing because apparently I should use this instead of this. I am using the latter because it (correct me if I'm wrong, please) more closely matches the OpenTracing specification for the Context, as shown here (the latter lacks a sampled attribute).

If I should use the former context class, should I use its sampled attribute to store the value of the very close X-B3-Sampled value?

@ocelotl ocelotl requested a review from carlosalberto October 17, 2019 17:11
@ocelotl ocelotl force-pushed the b3_format branch 2 times, most recently from 6de2e14 to 2d2516b Compare October 17, 2019 17:38
import sys
import threading

try:
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for the example. For the next iterations we will have most likely to update it, as we need to comply with a few proposed changes about how to allow the user to specify this ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, it is basically the same as the previous HTTP example. I can refactor it completely or create one new from scratch too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The example can be executed now as it is. Please let me know when we need to update it.

Comment on lines 4 to 5
from opentracing.mocktracer.propagator import Propagator
from opentracing.mocktracer.context import SpanContext
Copy link
Contributor

Choose a reason for hiding this comment

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

We cannot use these components as they are a mock component, and as such, they are not meant to be used in production code. SpanContext needs to be used as an interface only, and in our internal code, we extend the basictracer package, so we internally end up using basictracer.SpanContext: https://github.com/opentracing/basictracer-python/blob/master/basictracer/context.py

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed!

sampled = baggage.get(self._SAMPLED, None)
if sampled is not None:
if flags == 1:
warn(
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd rather use a log here only, as this is not considered under OT a 'big' error.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, changing to logger.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed!

)
)
else:
if traceid is None or spanid is None:
Copy link
Contributor

Choose a reason for hiding this comment

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

We don't want to raise an Exception here. And here we can totally use warn to signal the user about this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, will downgrade to warn.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed too

parentspanid = carrier.get(_PARENTSPANID, None)
sampled = carrier.get(_SAMPLED, None)
flags = carrier.get(_FLAGS, None)
if parentspanid is sampled is flags is None:
Copy link
Contributor

Choose a reason for hiding this comment

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

if parentspanid is sampled doesn't look/read right. Could you confirm this is the expected check?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I'm checking that all three of parentspanid, sampled and flags are None. I am using is instead of == because None is a singleton.

)


__all__ = ['LightStepB3Propagator']
Copy link
Contributor

Choose a reason for hiding this comment

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

We don't use this convention, so we don't need it ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, I'll remove it. How do you specify public interfaces, then?

Copy link
Contributor

Choose a reason for hiding this comment

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

We simply allow users to explicitly load them, i.e.

from lightstep.propagators import B3Propagator

(This is a convention we have also kept from OpenTracing)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

tracer.inject(span.context, LightStepFormat.LIGHTSTEP_BINARY, lightstep_carrier)
tracer.inject(
span.context, LightStepFormat.LIGHTSTEP_BINARY, lightstep_carrier
)
Copy link
Contributor

Choose a reason for hiding this comment

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

This style change is not needed (usually, when we want to make style changes, they can come in a separated PR)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, rolling it back.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed


"""

# The LIGHTSTEP_BINARY format represents SpanContexts in byte array format.
# https://github.com/lightstep/lightstep-tracer-common/lightstep_carrier.proto
LIGHTSTEP_BINARY = 'lightstep_binary'
# The LIGHTSTEP_B3 format represents SpanContexts in B3 format.
# https://github.com/openzipkin/b3-propagation
LIGHTSTEP_B3 = 'lightstep_b3'
Copy link
Contributor

Choose a reason for hiding this comment

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

This can be called B3 (LIGHSTEP_BINARY has this name because it's a binary specific format that only us use ;) )

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I removed it completely from this class since it is specified to be only for LightStep-specific carrier formats. The module says it is also for this kind of carrier formats. Should the B3 specification identifier declaration (B3 = "b3") be on a new file?

@carlosalberto
Copy link
Contributor

Hey, I left some reviews. Let me come back to you on the sampled part (as we don't filter out based on that flag, so I need to confirm our handling in the future).

Two things:

  1. It would be nice to rename lightstep_b3_propagator.py to b3_propagator.py, as B3 is not really LightStep specific ;)
  2. We need tests. For some inspiration, check the ones here (in Java): https://github.com/lightstep/lightstep-tracer-java-common/blob/master/common/src/test/java/com/lightstep/tracer/shared/B3PropagatorTest.java

@ocelotl ocelotl force-pushed the b3_format branch 5 times, most recently from 37a6a33 to 04eaa42 Compare October 21, 2019 23:07
if baggage == OTSpanContext.EMPTY_BAGGAGE:
baggage = None
return SpanContext(
# traceid and spanid are encoded in hex, so thet must be encoded
Copy link
Contributor

Choose a reason for hiding this comment

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

If no valid traceid/spanid were encoded, we need to report it as done here: https://github.com/opentracing/basictracer-python/blob/master/basictracer/text_propagator.py#L51

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done, raising SpanContextCorruptedException.

@@ -4,6 +4,10 @@

from __future__ import absolute_import

# The B3 format represents SpanContexts in B3 format.
# https://github.com/openzipkin/b3-propagation
B3 = "b3"
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we really need it here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well, it can be placed somewhere else if you prefer, of course. I just added it there since that module was holding the register identifier for the other propagator in the package, LightStepBinaryPropagator.

@@ -75,6 +76,7 @@ def __init__(self, enable_binary_format, recorder, scope_manager):
super(_LightstepTracer, self).__init__(recorder, scope_manager=scope_manager)
self.register_propagator(Format.TEXT_MAP, TextPropagator())
self.register_propagator(Format.HTTP_HEADERS, TextPropagator())
self.register_propagator(B3, B3Propagator())
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's leave this as an optional step (so by default only TextMapPropagator is registered by default).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done, removed B3 as a default propagator.

@ocelotl ocelotl force-pushed the b3_format branch 2 times, most recently from c54f957 to 1641c23 Compare October 22, 2019 21:53
@ocelotl
Copy link
Contributor Author

ocelotl commented Oct 22, 2019

@carlosalberto I added several more test cases, and fixed all but one of your comments since I need a bit more info still.

Thank you!

@chadbrad
Copy link
Contributor

@carlosalberto and / or @codeboten - are we good to merge?

@carlosalberto
Copy link
Contributor

Hey @ocelotl

Thanks. Actually we need a last iteration, which I hadn't realize. The way injection/extraction works is to specify one of the formats in opentracing.Format, and not 'create' a new one to use B3 (or later TraceContext), but use B3 when either TEXT_MAP or HTTP_HEADERS are specified:

# replaces the default propagator with the B3 one
tracer.register_propagator(b3_propagator)
...
# we use the text map format, with the b3 propagator
tracer.inject(span.context, opentracing.Format.TEXT_MAP, carrier)

For this you will need to also update the example ;) Once this is done, we will be able to merge.

@ocelotl
Copy link
Contributor Author

ocelotl commented Oct 28, 2019

@codeboten @carlosalberto I implemented the truncation of the MSb in traceid in the output of extract until traceid is 64b long. I still added FIXME comments to highlight the pending question of padding/truncating on the MS or LS side. I'll squash all this into a single before merging if you prefer.

@ocelotl
Copy link
Contributor Author

ocelotl commented Oct 28, 2019

@codeboten @carlosalberto I implemented the truncation of the MSb in traceid in the output of extract until traceid is 64b long. I still added FIXME comments to highlight the pending question of padding/truncating on the MS or LS side. I'll squash all this into a single before merging if you prefer.

Hey @ocelotl

Thanks. Actually we need a last iteration, which I hadn't realize. The way injection/extraction works is to specify one of the formats in opentracing.Format, and not 'create' a new one to use B3 (or later TraceContext), but use B3 when either TEXT_MAP or HTTP_HEADERS are specified:

# replaces the default propagator with the B3 one
tracer.register_propagator(b3_propagator)
...
# we use the text map format, with the b3 propagator
tracer.inject(span.context, opentracing.Format.TEXT_MAP, carrier)

For this you will need to also update the example ;) Once this is done, we will be able to merge.

Ok! implementing...

@ocelotl
Copy link
Contributor Author

ocelotl commented Oct 28, 2019

@codeboten @carlosalberto I implemented the truncation of the MSb in traceid in the output of extract until traceid is 64b long. I still added FIXME comments to highlight the pending question of padding/truncating on the MS or LS side. I'll squash all this into a single before merging if you prefer.

Hey @ocelotl
Thanks. Actually we need a last iteration, which I hadn't realize. The way injection/extraction works is to specify one of the formats in opentracing.Format, and not 'create' a new one to use B3 (or later TraceContext), but use B3 when either TEXT_MAP or HTTP_HEADERS are specified:

# replaces the default propagator with the B3 one
tracer.register_propagator(b3_propagator)
...
# we use the text map format, with the b3 propagator
tracer.inject(span.context, opentracing.Format.TEXT_MAP, carrier)

For this you will need to also update the example ;) Once this is done, we will be able to merge.

Ok! implementing...

@carlosalberto Done!

carrier.update(baggage)

if traceid is not None:
# FIXME This adds zeroes on the MS side until traceid is 128b long.
Copy link
Contributor

Choose a reason for hiding this comment

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

padding must be on the least significant bits

if baggage == OTSpanContext.EMPTY_BAGGAGE:
baggage = None

# FIXME This truncates the MSb of traceid until only 64b are left.
Copy link
Contributor

Choose a reason for hiding this comment

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

the implementation here should leave the trace ID as is when it's propagated to ensure it matches what was passed in. the truncation should only occur when reporting to lightstep

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, I'll change it. What do you refer to with when reporting to lightstep?

Copy link
Contributor

Choose a reason for hiding this comment

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

A good example of this is in the ruby implementation, where a separate field stores the 128-bit vs the 64-bit trace IDs, to remain compatible with the current propagators: https://github.com/lightstep/lightstep-tracer-ruby/pull/86/files#diff-d87db7512b8e96c0a8d9463d52de94beR13-R14. This ensures existing propagation continues to work as is.

@ocelotl
Copy link
Contributor Author

ocelotl commented Oct 29, 2019

@codeboten Changes added, please confirm 👍

self.register_propagator(Format.TEXT_MAP, TextPropagator())
self.register_propagator(Format.HTTP_HEADERS, TextPropagator())
self.register_propagator(Format.TEXT_MAP, B3Propagator())
self.register_propagator(Format.HTTP_HEADERS, B3Propagator())
Copy link
Contributor

Choose a reason for hiding this comment

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

Think we wanted the default here to continue being the TextPropagator

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was requested by @carlosalberto before:

Hey @ocelotl

Thanks. Actually we need a last iteration, which I hadn't realize. The way injection/extraction works is to specify one of the formats in opentracing.Format, and not 'create' a new one to use B3 (or later TraceContext), but use B3 when either TEXT_MAP or HTTP_HEADERS are specified:

# replaces the default propagator with the B3 one
tracer.register_propagator(b3_propagator)
...
# we use the text map format, with the b3 propagator
tracer.inject(span.context, opentracing.Format.TEXT_MAP, carrier)

For this you will need to also update the example ;) Once this is done, we will be able to merge.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, my understanding is that both TEXT_MAP and HTTP_HEADERS should default to TextPropagator, unless configured otherwise when configuring the Tracer.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@carlosalberto can you confirm, please?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, we want TextPropagator being the default.

not 'create' a new one to use B3 (or later TraceContext), but use B3 when either TEXT_MAP or HTTP_HEADERS are specified

Correct, we want B3 to completely replace TextPropagator when registered explicitly (instead of having it as a separate format).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I reverted the changes in tracer.py. I also updated the example to explicitly call register_propagator twice to register the B3 propagator for TEXT_MAP and HTTP_HEADERS respectively.

Just to be sure, @carlosalberto, you mentioned this in your previous comment:

# replaces the default propagator with the B3 one
tracer.register_propagator(b3_propagator)

But is not possible to call register_propagator with only one argument, the format name and propagator instance are needed.

Please let me know if this is the right implementation.

Thanks!

Copy link
Contributor

Choose a reason for hiding this comment

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

But is not possible to call register_propagator with only one argument, the format name and propagator instance are needed.

Yeah, this is fine ;)

@codeboten
Copy link
Contributor

LGTM 👍

@codeboten codeboten merged commit 719e21a into master Nov 1, 2019
@ocelotl ocelotl deleted the b3_format branch November 1, 2019 17:41
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.

4 participants