-
Notifications
You must be signed in to change notification settings - Fork 666
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
Adding Zipkin exporter #320
Conversation
Signed-off-by: Alex Boten <aboten@lightstep.com>
Codecov Report
@@ Coverage Diff @@
## master #320 +/- ##
==========================================
+ Coverage 84.73% 84.82% +0.09%
==========================================
Files 35 38 +3
Lines 1716 1839 +123
Branches 200 217 +17
==========================================
+ Hits 1454 1560 +106
- Misses 204 214 +10
- Partials 58 65 +7
Continue to review full report at Codecov.
|
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 looks great, some minor comments only.
ext/opentelemetry-ext-zipkin/src/opentelemetry/ext/zipkin/__init__.py
Outdated
Show resolved
Hide resolved
ext/opentelemetry-ext-zipkin/src/opentelemetry/ext/zipkin/__init__.py
Outdated
Show resolved
Hide resolved
Signed-off-by: Alex Boten <aboten@lightstep.com>
Signed-off-by: Alex Boten <aboten@lightstep.com>
Signed-off-by: Alex Boten <aboten@lightstep.com>
Recent changes LGTM too, cc @open-telemetry/python-approvers to get another review on this. |
Would be good to add README with installation/usage instructions like other ext packages |
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.
Nice! looks great. some nice to haves to fix, but it looks good to me functionally.
=src | ||
packages=find_namespace: | ||
install_requires = | ||
requests~=2.7 |
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 enforce this on the package? I have a feeling it will make it hard for people to adopt.
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 would you recommend here?
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.
nevermind, I think I forgot the usage of the compatible release qualifier. This looks good.
self.ipv4 = ipv4 | ||
self.ipv6 = ipv6 | ||
|
||
def export(self, spans: Sequence[Span]) -> "SpanExportResult": |
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.
any need to reference this by a string vs class directly?
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 is not, updated.
|
||
def export(self, spans: Sequence[Span]) -> "SpanExportResult": | ||
zipkin_spans = self._translate_to_zipkin(spans) | ||
result = requests.post( |
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.
general question: does the SpanExporterer have any handling for when an export takes too long?
I was going to suggest putting a timeout on this request, but it seems like, to keep the export working, we would need to provide a more general timeout mechanism, potentially in the caller to export.
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 point, i don't know that the exporter currently does anything here. From some quick testing, it looks like the exporter could hang indefinitely waiting for an export to complete.
SpanKind.CONSUMER: "CONSUMER", | ||
} | ||
|
||
SUCCESS_STATUS_CODE = (200, 202) |
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.
nitpick: "SUCCESS_STATUS_CODE" implies a single int. would be nice to callout a plural (CODES) or a CODE_LIST.
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, updated to CODES
result.status_code, | ||
result.text, | ||
) | ||
# TODO: should we retry here? |
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.
maybe it's good as a configuration variable?
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 idea, i added a retry
option to configure the behaviour
pass | ||
|
||
|
||
def _format(unformatted_id): |
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 seems like a fairly small method. is it worth keeping?
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.
removed it
return None | ||
tags = {} | ||
for attribute_key, attribute_value in attr.items(): | ||
if isinstance(attribute_value, (int, bool, float)): |
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.
is this case allowed? it seems like we should do some enforcement on the creation of attributes spans, rather than try to catch it on the flush side here.
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.
Attributes are assigned one of the following values:
AttributeValue = typing.Union[str, bool, float] |
Looks like int is not one of them though, i'll remove it.
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.
Although that isn't enforced at this point... I can create a separate ticket for this
self.attributes[key] = value |
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 in issues anyway: #347. Thanks!
if isinstance(attribute_value, (int, bool, float)): | ||
value = str(attribute_value) | ||
elif isinstance(attribute_value, str): | ||
res = attribute_value[:128] |
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.
is the "res" variable needed?
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. good catch. removed
def _extract_annotations_from_events(events): | ||
if not events: | ||
return None | ||
annotations = [] |
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.
nitpick: this could be a fairly intuitive list comprehension.
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
start_timestamp_mus = _nsec_to_usec_round(span.start_time) | ||
duration_mus = _nsec_to_usec_round(span.end_time - span.start_time) | ||
|
||
zipkin_span = { |
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 looks like there's no handling of the "debug" field here, which could be extracted from the boolean flags in the TraceOptions.
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.
theoretically we could include debug as a bit in trace_options, or use the sampled bit and consider that debug: https://www.w3.org/TR/trace-context-1/#trace-flags
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 didn't see anything referencing a debug field in the zipkin API https://zipkin.io/zipkin-api/#/default/post_spans. can you elaborate on 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.
Documentation is in that page under Models - Span
https://zipkin.io/zipkin-api/#/default/post_spans
debug | boolean True is a request to store this span even if it overrides sampling policy.This is true when the X-B3-Flags header has a value of 1. |
---|
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.
Easy enough to use the sampling bit.
Signed-off-by: Alex Boten <aboten@lightstep.com>
Added |
Signed-off-by: Alex Boten <aboten@lightstep.com>
Signed-off-by: Alex Boten <aboten@lightstep.com>
- adding retry configuration option - adding link to zipkin documentation for microseconds Signed-off-by: Alex Boten <aboten@lightstep.com>
Signed-off-by: Alex Boten <aboten@lightstep.com>
Amazing! Thanks. |
Fixes #318
Pulled some of the code from the OC implementation and looked at the javascript implementation
Not addressed yet:
Zipkin Spec:
https://github.com/openzipkin/zipkin-api/blob/master/zipkin2-api.yaml
Signed-off-by: Alex Boten aboten@lightstep.com