Implement ScopeManager for in-process propagation#63
Implement ScopeManager for in-process propagation#63palazzem wants to merge 8 commits intoopentracing:masterfrom
Conversation
…opeManager propagation
e529e27 to
9f406cf
Compare
| """ | ||
| return True | ||
|
|
||
| def is_parent(self, parent, span): |
There was a problem hiding this comment.
don't know if it's really needed, but it would be great that tests available in the api_check.py tests also the parenting so that a ScopeManager can be tested automatically when a new Tracer implementation is created.
| def __init__(self): | ||
| # TODO: `tracer` should not be None, but we don't have a reference; | ||
| # should we move the NOOP SpanContext, Span, Scope to somewhere | ||
| # else so that they're globally reachable? |
There was a problem hiding this comment.
What I mean here is that maybe everywhere in the repo we can simply:
from opentracing.ext import noop # or something similar
self._noop_span = noop.SPAN
self._noop_scope = noop.SCOPEThe noop module may be initialized when the Tracer is created (so in the __init__.py at the moment?) so we have references of Tracer and ScopeManager, avoiding creating every time a no-op of something.
Let me know if I'm missing something!
| with self.assertRaises(AssertionError): | ||
| api_check.test_context_baggage() | ||
|
|
||
| def test_scope_manager_check_works(self): |
There was a problem hiding this comment.
Not sure we need that (other than coverage?). I've followed the approach above.
|
@carlosalberto @tedsuo it would be great having a pass on this PR as a follow-up of the Java API. Currently this API is good enough for us since it grants the flexibility we need for in-process propagation. I'm going to publish the reference implementation PR too, so I can update the PR description with the link. Thanks in advance for spending time reviewing the PR! EDIT: link to the reference implementation -> opentracing/basictracer-python#24 |
| self._manager = manager | ||
| self._span = span | ||
|
|
||
| def span(self): |
There was a problem hiding this comment.
I would prefer this one as a @property so scope.span instead of scope.span() but it's something we can discuss.
There was a problem hiding this comment.
I'd say we keep a property as well - I know some people don't like it, but then again, we already have Span's context and tracer as properties ;)
| """ScopeManager accessor""" | ||
| return self._scope_manager | ||
|
|
||
| def start_active(self, |
There was a problem hiding this comment.
nomenclature nitpick: i don't like the naming of start_active vs start_manual. start_active will be used 95% of the time, so i think it should just be called start and then there can be start_{whatever_you_choose} for the rest.
There was a problem hiding this comment.
that's an interesting point since the most common API may be the shortest possible, let's see what others are thinking!
There was a problem hiding this comment.
I would vote to keep the api as close to the other language implementations as possible. On a similar note, should something about in process span management be added to the opentracing specification?
There was a problem hiding this comment.
@wkiser I agree on the uniformity point.
About the standard inclusion: I think it will be added eventually, after the implementation proves to be sufficient and tracer authors are fine with it (for reference: for the Java api it took a pair of iterations to get it right ;) )
There was a problem hiding this comment.
+1, "manual" was removed from the Java API.
Even though start_active_span will be used more often, it's worth keeping the full name to be in sync with other languages and emphasize the side effects. It also doesn't return a Span the way start_span does.
There was a problem hiding this comment.
It also doesn't return a Span the way start_span does.
Ah - I overlooked that. Seems like it should beTracer.start_active_scope().
I've submitted a PR to @palazzem's branch with these naming updates (palazzem#1).
There was a problem hiding this comment.
I think it should be start_active_span, since Span is the main thing that's being created here, it's just wrapped in a Scope for management.
There was a problem hiding this comment.
I don't have a strong opinion on _active_, so I'm happy to adjust it.
Here's the difference in practice:
with tracer.start_active_span('...') as scope:
scope.span().set_tag('http.method', 'GET')
do_some_work()
vs.
with tracer.start_active_scope('...') as scope:
There was a problem hiding this comment.
Sure thing! I definitely think that start_manual* must be out of the Python API. Let's make this change.
| :param finish_on_close: whether span should automatically be | ||
| finished when `Scope#close()` is called | ||
| """ | ||
| self._manager = manager |
There was a problem hiding this comment.
If you are saving manager, why not expose it too (through a property)? Else, well, don't save it (and let implementations do on that ;) )
There was a problem hiding this comment.
I think it may be better to not expose the manager since the source of truth for the ScopeManager should be the Tracer (right?). If that makes sense, I'd say to remove that line and let the implementation do that.
There was a problem hiding this comment.
Though we need to keep it in the constructor signature since implementations need (in 99% of cases) that.
| # TODO: `tracer` should not be None, but we don't have a reference; | ||
| # should we move the NOOP SpanContext, Span, Scope to somewhere | ||
| # else so that they're globally reachable? | ||
| self._noop_span = Span(tracer=None, context=SpanContext()) |
There was a problem hiding this comment.
One idea: creating a Tracer and use its noop_span is an alternative, even if not a perfect one (as then we would end up with two noop Tracer objects).
| _supported_formats = [Format.TEXT_MAP, Format.BINARY, Format.HTTP_HEADERS] | ||
|
|
||
| def __init__(self): | ||
| self._scope_manager = ScopeManager() |
There was a problem hiding this comment.
Why not call it _noop_scope_manager?
|
Upon further review when working on the
Let me know. Hopefully it helps! I will be pushing soon an update with the |
|
@carlosalberto since my work has been already merged, I think we should close this PR because it has been superseded by #64 ? |
|
@palazzem Yes. Closing it now. Thanks ;) |
Overview
This PR aims to solve in-process context propagation for Python. It has two aims:
start_active()andstart_manual())ScopeManager) that allows tracing async operations with all the common frameworks - threads, asyncio, tornado, etcImplementation details are available in another PR (opentracing/basictracer-python#24).
Details
With this PR,
Scopeis implemented as a wrapper ofSpanclass. No changes have been made to the currentSpanandSpanContext, making the propagation something built on top of current data models.A
Scopeformalizes the activation and deactivation of aSpan, that is handled using aScopeManageravailable in theTraceritself. It can be used automatically using tracing methods, or manually if you need to retrieve the current activeSpan. Simple examples are:Opened questions
get_something/set_somethinginstead of justsomething). In Python such approach is not really idiomatic, though properties are. Because of that I suggest to have some internal attributes "protected" via a property like:Continuation(and so the counter) is not part of this API, though it could be acontribmodule in case we "really" need it in the future.References