15
15
import logging
16
16
17
17
import wrapt
18
+ from opencensus .trace import execution_context
18
19
from opencensus .trace .blank_span import BlankSpan
20
+ from opencensus .trace .span_context import SpanContext
19
21
from opencensus .trace .tracers .base import Tracer as BaseTracer
22
+ from opencensus .trace .tracestate import Tracestate
20
23
21
24
from opentelemetry import context , trace
22
25
from opentelemetry .shim .opencensus ._shim_span import ShimSpan
23
26
24
27
_logger = logging .getLogger (__name__ )
25
28
26
29
_SHIM_SPAN_KEY = context .create_key ("opencensus-shim-span-key" )
30
+ _SAMPLED = trace .TraceFlags (trace .TraceFlags .SAMPLED )
27
31
28
32
29
33
def set_shim_span_in_context (
@@ -36,12 +40,57 @@ def get_shim_span_in_context() -> ShimSpan:
36
40
return context .get_value (_SHIM_SPAN_KEY )
37
41
38
42
43
+ def set_oc_span_in_context (
44
+ oc_span_context : SpanContext , ctx : context .Context
45
+ ) -> context .Context :
46
+ """Returns a new OTel context based on ctx with oc_span_context set as the current span"""
47
+
48
+ # If no SpanContext is passed to the opencensus.trace.tracer.Tracer, it creates a new one
49
+ # with a random trace ID and a None span ID to be the parent:
50
+ # https://github.com/census-instrumentation/opencensus-python/blob/2e08df591b507612b3968be8c2538dedbf8fab37/opencensus/trace/tracer.py#L47.
51
+ #
52
+ # OpenTelemetry considers this an invalid SpanContext and will ignore it, so we can just
53
+ # return early
54
+ if oc_span_context .span_id is None :
55
+ return ctx
56
+
57
+ trace_id = int (oc_span_context .trace_id , 16 )
58
+ span_id = int (oc_span_context .span_id , 16 )
59
+ is_remote = oc_span_context .from_header
60
+ trace_flags = (
61
+ _SAMPLED if oc_span_context .trace_options .get_enabled () else None
62
+ )
63
+ trace_state = (
64
+ trace .TraceState (tuple (oc_span_context .tracestate .items ()))
65
+ # OC SpanContext does not validate this type
66
+ if isinstance (oc_span_context .tracestate , Tracestate )
67
+ else None
68
+ )
69
+
70
+ return trace .set_span_in_context (
71
+ trace .NonRecordingSpan (
72
+ trace .SpanContext (
73
+ trace_id = trace_id ,
74
+ span_id = span_id ,
75
+ is_remote = is_remote ,
76
+ trace_flags = trace_flags ,
77
+ trace_state = trace_state ,
78
+ )
79
+ )
80
+ )
81
+
82
+
39
83
# pylint: disable=abstract-method
40
84
class ShimTracer (wrapt .ObjectProxy ):
41
85
def __init__ (
42
- self , wrapped : BaseTracer , * , otel_tracer : trace .Tracer
86
+ self ,
87
+ wrapped : BaseTracer ,
88
+ * ,
89
+ oc_span_context : SpanContext ,
90
+ otel_tracer : trace .Tracer
43
91
) -> None :
44
92
super ().__init__ (wrapped )
93
+ self ._self_oc_span_context = oc_span_context
45
94
self ._self_otel_tracer = otel_tracer
46
95
47
96
# For now, finish() is not implemented by the shim. It would require keeping a list of all
@@ -53,7 +102,15 @@ def span(self, name="span"):
53
102
return self .start_span (name = name )
54
103
55
104
def start_span (self , name = "span" ):
56
- span = self ._self_otel_tracer .start_span (name )
105
+ parent_ctx = context .get_current ()
106
+ # If there is no current span in context, use the one provided to the OC Tracer at
107
+ # creation time
108
+ if trace .get_current_span (parent_ctx ) is trace .INVALID_SPAN :
109
+ parent_ctx = set_oc_span_in_context (
110
+ self ._self_oc_span_context , parent_ctx
111
+ )
112
+
113
+ span = self ._self_otel_tracer .start_span (name , context = parent_ctx )
57
114
shim_span = ShimSpan (
58
115
BlankSpan (name = name , context_tracer = self ),
59
116
otel_span = span ,
@@ -67,20 +124,27 @@ def start_span(self, name="span"):
67
124
# equivalent to the below. This can cause context to leak but is equivalent.
68
125
# pylint: disable=protected-access
69
126
shim_span ._self_token = context .attach (ctx )
127
+ # Also set it in OC's context, equivalent to
128
+ # https://github.com/census-instrumentation/opencensus-python/blob/2e08df591b507612b3968be8c2538dedbf8fab37/opencensus/trace/tracers/context_tracer.py#L94
129
+ execution_context .set_current_span (shim_span )
70
130
return shim_span
71
131
72
132
def end_span (self ):
73
- """Finishes the current span in the context and pops restores the context from before
74
- the span was started.
133
+ """Finishes the current span in the context and restores the context from before the
134
+ span was started.
75
135
"""
76
136
span = self .current_span ()
77
137
if not span :
78
138
_logger .warning ("No active span, cannot do end_span." )
79
139
return
80
140
81
141
span .finish ()
142
+
82
143
# pylint: disable=protected-access
83
144
context .detach (span ._self_token )
145
+ # Also reset the OC execution_context, equivalent to
146
+ # https://github.com/census-instrumentation/opencensus-python/blob/2e08df591b507612b3968be8c2538dedbf8fab37/opencensus/trace/tracers/context_tracer.py#L114-L117
147
+ execution_context .set_current_span (self .current_span ())
84
148
85
149
# pylint: disable=no-self-use
86
150
def current_span (self ):
0 commit comments