You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardexpand all lines: python/lesson02/README.md
+14-24
Original file line number
Diff line number
Diff line change
@@ -121,52 +121,42 @@ If we find this trace in the UI, it will show a proper parent-child relationship
121
121
122
122
You may have noticed one unpleasant side effect of our recent changes - we had to pass the Span object
123
123
as the first argument to each function. Unlike Go, languages like Java and Python support thread-local
124
-
storage, which is convenient for storing such request-scoped data like the current span. Unfortunately,
125
-
unlike in Java, the OpenTracing API for Python currently does not expose a standard mechanism of accessing
126
-
such thread-local storage (or its equivalents in async frameworks like `tornado` or `gevent`). The new
127
-
API is curently a work in progress and should be available soon.
124
+
storage, which is convenient for storing such request-scoped data like the current span. As of v2 the OpenTracing API for Python supports a standard mechanism for passing and accessing the current span using the concepts of Scope Manager and Scope, which are using either plain thread-local storage or more specific mechanisms provided by various async frameworks like `tornado` or `gevent`.
128
125
129
-
For the purpose of this tutorial, we will use an alternative API available in module `opentracing_instrumentation`,
130
-
which is already included in the `requirements.txt`. This modue provides an in-memory context propagation
131
-
facility suitable for multi-threaded apps and Tornado apps, via its `request_context` submodule.
132
-
133
-
When we create the top level `root_span`, we can store it in the context like this:
126
+
Instead of calling `start_span` on the tracer, we can call `start_active_span` that invokes that mechanism and makes the span "active" and accessible via `tracer.active_span`. The return value of the `start_active_span` is a `Scope` object that needs to be closed in order to restore the previously active span on the stack.
134
127
135
128
```python
136
-
from opentracing_instrumentation.request_context import get_current_span, span_in_context
137
-
138
129
defsay_hello(hello_to):
139
-
with tracer.start_span('say-hello') as span:
140
-
span.set_tag('hello-to', hello_to)
141
-
with span_in_context(span):
142
-
hello_str = format_string(hello_to)
143
-
print_hello(hello_str)
130
+
with tracer.start_active_span('say-hello') as scope:
131
+
scope.span.set_tag('hello-to', hello_to)
132
+
hello_str = format_string(hello_to)
133
+
print_hello(hello_str)
144
134
```
145
135
146
136
Notice that we're no longer passing the span as argument to the functions, because they can now
147
-
retrieve the span with `get_current_span` function:
137
+
retrieve the span with `tracer.active_span` function. However, because creating a child span of a currently active span is such a common pattern, this now happens automatically, so that we do not need to pass the `child_of` reference to the parent span anymore:
148
138
149
139
```python
150
140
defformat_string(hello_to):
151
-
root_span = get_current_span()
152
-
with tracer.start_span('format', child_of=root_span) as span:
with tracer.start_span('println', child_of=root_span) as span:
147
+
with tracer.start_active_span('println') as scope:
160
148
print(hello_str)
161
-
span.log_kv({'event': 'println'})
149
+
scope.span.log_kv({'event': 'println'})
162
150
```
163
151
152
+
Note that because `start_active_span` function returns a `Scope` instead of a `Span`, we use `scope.span` property to access the span when we want to annotate it with tags or logs. We could also use `tracer.active_span` property with the same effect.
153
+
164
154
If we run this modified program, we will see that all three reported spans still have the same trace ID.
165
155
166
156
### What's the Big Deal?
167
157
168
158
The last change we made may not seem particularly useful. But imagine that your program is
169
-
much larger with many functions calling each other. By using the `request_context` mechanism we can access
159
+
much larger with many functions calling each other. By using the Scope Manager mechanism we can access
170
160
the current span from any place in the program without having to pass the span object as the argument to
171
161
all the function calls. This is especially useful if we are using instrumented RPC frameworks that perform
172
162
tracing functions automatically - they have a stable way of finding the current span.
Copy file name to clipboardexpand all lines: python/lesson03/README.md
+9-13
Original file line number
Diff line number
Diff line change
@@ -91,18 +91,14 @@ The tracing instrumentation uses `inject` and `extract` to pass the span context
91
91
92
92
### Instrumenting the Client
93
93
94
-
In the `format_string` function we already create a child span. In order to pass its context over the HTTP
95
-
request we need to call `tracer.inject` before building the HTTP request. But since we're building the HTTP
96
-
requerst inside a helper function `http_get`, we need to make sure that function has access to the new
97
-
child span we started. We can use `span_in_context()` again:
94
+
In the `format_string` function we already create a child span. Since we call `tracer.start_active_span()`, this child span will be available inside the helper function `http_get`.
98
95
99
96
```python
100
97
defformat_string(hello_to):
101
-
with tracer.start_span('format', child_of=get_current_span()) as span:
with tracer.start_span('format', child_of=span_ctx, tags=span_tags):
166
+
with tracer.start_active_span('format', child_of=span_ctx, tags=span_tags):
171
167
hello_to = request.args.get('helloTo')
172
168
return'Hello, %s!'% hello_to
173
169
```
174
170
175
-
Make a similar change in `publisher.py`.
171
+
Make a similar change in `publisher.py`. Note that we are still using `start_active_span` for consistency. In this example it does not make much of a difference, but if the logic in the formatter and publisher was more involved, we could benefit from propagating the span through thread locals.
Copy file name to clipboardexpand all lines: python/lesson04/README.md
+14-32
Original file line number
Diff line number
Diff line change
@@ -7,23 +7,16 @@
7
7
8
8
### Walkthrough
9
9
10
-
In Lesson 3 we have seen how span context is propagated over the wire between different applications.
11
-
It is not hard to see that this process can be generalized to passing more than just the tracing context.
12
-
With OpenTracing instrumentation in place, we can support general purpose _distributed context propagation_
13
-
where we associate some metadata with the transaction and make that metadata available anywhere in the
14
-
distributed call graph. In OpenTracing this metadata is called _baggage_, to highlight the fact that
15
-
it is carried over in-band with all RPC requests, just like baggage.
10
+
In Lesson 3 we have seen how span context is propagated over the wire between different applications. It is not hard to see that this process can be generalized to passing more than just the tracing context. With OpenTracing instrumentation in place, we can support general purpose _distributed context propagation_ where we associate some metadata with the transaction and make that metadata available anywhere in the distributed call graph. In OpenTracing this metadata is called _baggage_, to highlight the fact that it is carried over in-band with all RPC requests, just like baggage.
16
11
17
-
To see how it works in OpenTracing, let's take the application we built in Lesson 3. You can copy the source
18
-
code from [../lesson03/solution](../lesson03/solution) package:
12
+
To see how it works in OpenTracing, let's take the application we built in Lesson 3. You can copy the source code from [../lesson03/solution](../lesson03/solution) package:
19
13
20
14
```
21
15
mkdir lesson04/exercise
22
16
cp -r lesson03/solution/*py lesson04/exercise/
23
17
```
24
18
25
-
The `formatter` service takes the `helloTo` parameter and returns a string `Hello, {helloTo}!`. Let's modify
26
-
it so that we can customize the greeting too, but without modifying the public API of that service.
19
+
The `formatter` service takes the `helloTo` parameter and returns a string `Hello, {helloTo}!`. Let's modify it so that we can customize the greeting too, but without modifying the public API of that service.
27
20
28
21
### Set Baggage in the Client
29
22
@@ -43,23 +36,22 @@ And update `sayHello`:
43
36
44
37
```python
45
38
defsay_hello(hello_to, greeting):
46
-
with tracer.start_span('say-hello') as span:
47
-
span.set_tag('hello-to', hello_to)
48
-
span.set_baggage_item('greeting', greeting)
49
-
with span_in_context(span):
50
-
hello_str = format_string(hello_to)
51
-
print_hello(hello_str)
39
+
with tracer.start_active_span('say-hello') as scope:
40
+
scope.span.set_tag('hello-to', hello_to)
41
+
scope.span.set_baggage_item('greeting', greeting)
42
+
hello_str = format_string(hello_to)
43
+
print_hello(hello_str)
52
44
```
53
45
54
-
By doing this we read a second command line argument as a "greeting" and store it in the baggage under `'greeting'` key.
46
+
By doing this we read a second command line argument as a "greeting" and store it in the baggage under the `'greeting'` key.
55
47
56
48
### Read Baggage in Formatter
57
49
58
50
Change the following code in the `formatter`'s HTTP handler:
59
51
60
52
```python
61
-
with tracer.start_span('format', child_of=span_ctx, tags=span_tags) asspan:
62
-
greeting = span.get_baggage_item('greeting')
53
+
with tracer.start_active_span('format', child_of=span_ctx, tags=span_tags) asscope:
54
+
greeting =scope.span.get_baggage_item('greeting')
63
55
ifnot greeting:
64
56
greeting ='Hello'
65
57
hello_to = request.args.get('helloTo')
@@ -68,8 +60,7 @@ with tracer.start_span('format', child_of=span_ctx, tags=span_tags) as span:
68
60
69
61
### Run it
70
62
71
-
As in Lesson 3, first start the `formatter` and `publisher` in separate terminals, then run the client
72
-
with two arguments, e.g. `Bryan Bonjour`. The `publisher` should print `Bonjour, Bryan!`.
63
+
As in Lesson 3, first start the `formatter` and `publisher` in separate terminals, then run the client with two arguments, e.g. `Bryan Bonjour`. The `publisher` should print `Bonjour, Bryan!`.
We may ask - so what, we could've done the same thing by passing the `greeting` as an HTTP request parameter.
111
-
However, that is exactly the point of this exercise - we did not have to change any APIs on the path from
112
-
the root span in `hello.py` all the way to the server-side span in `formatter`, three levels down.
113
-
If we had a much larger application with much deeper call tree, say the `formatter` was 10 levels down,
114
-
the exact code changes we made here would have worked, despite 8 more services being in the path.
115
-
If changing the API was the only way to pass the data, we would have needed to modify 8 more services
116
-
to get the same effect.
101
+
We may ask - so what, we could've done the same thing by passing the `greeting` as an HTTP request parameter. However, that is exactly the point of this exercise - we did not have to change any APIs on the path from the root span in `hello.py` all the way to the server-side span in `formatter`, three levels down. If we had a much larger application with much deeper call tree, say the `formatter` was 10 levels down, the exact code changes we made here would have worked, despite 8 more services being in the path. If changing the API was the only way to pass the data, we would have needed to modify 8 more services to get the same effect.
117
102
118
103
Some of the possible applications of baggage include:
119
104
@@ -125,10 +110,7 @@ Some of the possible applications of baggage include:
125
110
126
111
### Now, a Warning... NOW a Warning?
127
112
128
-
Of course, while baggage is an extermely powerful mechanism, it is also dangerous. If we store a 1Mb value/string
129
-
in baggage, every request in the call graph below that point will have to carry that 1Mb of data. So baggage
130
-
must be used with caution. In fact, Jaeger client libraries implement centrally controlled baggage restrictions,
131
-
so that only blessed services can put blessed keys in the baggage, with possible restrictions on the value length.
113
+
Of course, while baggage is an extremely powerful mechanism, it is also dangerous. If we store a 1Mb value/string in baggage, every request in the call graph below that point will have to carry that 1Mb of data. So baggage must be used with caution. In fact, Jaeger client libraries implement centrally controlled baggage restrictions, so that only blessed services can put blessed keys in the baggage, with possible restrictions on the value length.
0 commit comments