30
30
31
31
.. code:: python
32
32
33
+ from opentelemetry import trace
34
+ from opentelemetry.sdk.trace import TracerProvider
35
+ from opentelemetry.sdk.trace.export import BatchExportSpanProcessor
33
36
from opentelemetry.instrumentation.celery import CeleryInstrumentor
34
37
35
- CeleryInstrumentor().instrument()
36
-
37
38
from celery import Celery
39
+ from celery.signals import worker_process_init
40
+
41
+ @worker_process_init.connect(weak=False)
42
+ def init_celery_tracing(*args, **kwargs):
43
+ trace.set_tracer_provider(TracerProvider())
44
+ span_processor = BatchExportSpanProcessor(ConsoleSpanExporter())
45
+ trace.get_tracer_provider().add_span_processor(span_processor)
46
+ CeleryInstrumentor().instrument()
38
47
39
48
app = Celery("tasks", broker="amqp://localhost")
40
49
@@ -50,13 +59,15 @@ def add(x, y):
50
59
51
60
import logging
52
61
import signal
62
+ from collections .abc import Iterable
53
63
54
64
from celery import signals # pylint: disable=no-name-in-module
55
65
56
- from opentelemetry import trace
66
+ from opentelemetry import propagators , trace
57
67
from opentelemetry .instrumentation .celery import utils
58
68
from opentelemetry .instrumentation .celery .version import __version__
59
69
from opentelemetry .instrumentation .instrumentor import BaseInstrumentor
70
+ from opentelemetry .trace .propagation import get_current_span
60
71
from opentelemetry .trace .status import Status , StatusCanonicalCode
61
72
62
73
logger = logging .getLogger (__name__ )
@@ -106,9 +117,16 @@ def _trace_prerun(self, *args, **kwargs):
106
117
if task is None or task_id is None :
107
118
return
108
119
120
+ request = task .request
121
+ tracectx = propagators .extract (carrier_extractor , request ) or {}
122
+ parent = get_current_span (tracectx )
123
+
109
124
logger .debug ("prerun signal start task_id=%s" , task_id )
110
125
111
- span = self ._tracer .start_span (task .name , kind = trace .SpanKind .CONSUMER )
126
+ operation_name = "{0}/{1}" .format (_TASK_RUN , task .name )
127
+ span = self ._tracer .start_span (
128
+ operation_name , parent = parent , kind = trace .SpanKind .CONSUMER
129
+ )
112
130
113
131
activation = self ._tracer .use_span (span , end_on_exit = True )
114
132
activation .__enter__ ()
@@ -146,7 +164,10 @@ def _trace_before_publish(self, *args, **kwargs):
146
164
if task is None or task_id is None :
147
165
return
148
166
149
- span = self ._tracer .start_span (task .name , kind = trace .SpanKind .PRODUCER )
167
+ operation_name = "{0}/{1}" .format (_TASK_APPLY_ASYNC , task .name )
168
+ span = self ._tracer .start_span (
169
+ operation_name , kind = trace .SpanKind .PRODUCER
170
+ )
150
171
151
172
# apply some attributes here because most of the data is not available
152
173
span .set_attribute (_TASK_TAG_KEY , _TASK_APPLY_ASYNC )
@@ -158,6 +179,10 @@ def _trace_before_publish(self, *args, **kwargs):
158
179
activation .__enter__ ()
159
180
utils .attach_span (task , task_id , (span , activation ), is_publish = True )
160
181
182
+ headers = kwargs .get ("headers" )
183
+ if headers :
184
+ propagators .inject (type (headers ).__setitem__ , headers )
185
+
161
186
@staticmethod
162
187
def _trace_after_publish (* args , ** kwargs ):
163
188
task = utils .retrieve_task_from_sender (kwargs )
@@ -221,3 +246,10 @@ def _trace_retry(*args, **kwargs):
221
246
# Use `str(reason)` instead of `reason.message` in case we get
222
247
# something that isn't an `Exception`
223
248
span .set_attribute (_TASK_RETRY_REASON_KEY , str (reason ))
249
+
250
+
251
+ def carrier_extractor (carrier , key ):
252
+ value = getattr (carrier , key , [])
253
+ if isinstance (value , str ) or not isinstance (value , Iterable ):
254
+ value = (value ,)
255
+ return value
0 commit comments