@@ -162,6 +162,7 @@ def set_fates(clotho, lachesis, atropos, father="Zues", mother="Themis"):
162
162
than one caller? Will all of those calling functions have be in a context
163
163
with an active span?
164
164
"""
165
+ from abc import ABC
165
166
import contextlib
166
167
import enum
167
168
import inspect
@@ -204,7 +205,7 @@ def set_fates(clotho, lachesis, atropos, father="Zues", mother="Themis"):
204
205
205
206
# Helper class
206
207
207
- # Always returns the fixed value given for any accessed property
208
+ # This will always returns the fixed value given for any accessed property
208
209
class _DummyLookup (object ):
209
210
def __init__ (self , value ):
210
211
self .value = value
@@ -213,6 +214,21 @@ def __getattribute__(self, name):
213
214
return object .__getattribute__ (self , "value" )
214
215
215
216
217
+ class DummyLink (ABC ):
218
+ def __init__ (self ):
219
+ self .not_implemented_message = (
220
+ "opentelemetry wasn't imported so this is just a dummy link placeholder"
221
+ )
222
+
223
+ @property
224
+ def context (self ):
225
+ raise NotImplementedError (self .not_implemented_message )
226
+
227
+ @property
228
+ def context (self ):
229
+ raise NotImplementedError (self .not_implemented_message )
230
+
231
+
216
232
# These dependencies are optional so they can fail to import
217
233
# and we
218
234
try :
@@ -229,12 +245,13 @@ def __getattribute__(self, name):
229
245
SpanKind = opentelemetry .trace .SpanKind
230
246
SpanAttributes = opentelemetry .semconv .trace .SpanAttributes
231
247
StatusCode = opentelemetry .trace .StatusCode
232
-
248
+ Link = opentelemetry . trace . Link
233
249
except ImportError :
234
250
opentelemetry = None # type: ignore[assignment]
235
251
SpanKind = _DummyLookup (0 )
236
252
SpanAttributes = _DummyLookup ("fake-attribute" )
237
253
StatusCode = _DummyLookup (0 )
254
+ Link = DummyLink
238
255
239
256
240
257
logger = logging .getLogger (__name__ )
@@ -435,6 +452,15 @@ def whitelisted_homeserver(destination: str) -> bool:
435
452
# Start spans and scopes
436
453
437
454
455
+ def create_non_recording_span ():
456
+ if opentelemetry is None :
457
+ return contextlib .nullcontext () # type: ignore[unreachable]
458
+
459
+ return opentelemetry .trace .NonRecordingSpan (
460
+ opentelemetry .trace .INVALID_SPAN_CONTEXT
461
+ )
462
+
463
+
438
464
def start_active_span (
439
465
name : str ,
440
466
* ,
@@ -446,11 +472,15 @@ def start_active_span(
446
472
record_exception : bool = True ,
447
473
set_status_on_exception : bool = True ,
448
474
end_on_exit : bool = True ,
475
+ # For testing only
476
+ tracer : Optional ["opentelemetry.sdk.trace.TracerProvider" ] = None ,
449
477
) -> Iterator ["opentelemetry.trace.span.Span" ]:
450
478
if opentelemetry is None :
451
479
return contextlib .nullcontext () # type: ignore[unreachable]
452
480
453
- tracer = opentelemetry .trace .get_tracer (__name__ )
481
+ if tracer is None :
482
+ tracer = opentelemetry .trace .get_tracer (__name__ )
483
+
454
484
return tracer .start_as_current_span (
455
485
name = name ,
456
486
context = context ,
@@ -464,6 +494,8 @@ def start_active_span(
464
494
)
465
495
466
496
497
+ # TODO: I don't think we even need this function over the normal `start_active_span`.
498
+ # The only difference is the `inherit_force_tracing` stuff.
467
499
def start_active_span_follows_from (
468
500
operation_name : str ,
469
501
contexts : Collection ,
@@ -491,8 +523,21 @@ def start_active_span_follows_from(
491
523
forced, the new span will also have tracing forced.
492
524
tracer: override the opentracing tracer. By default the global tracer is used.
493
525
"""
494
- # TODO
495
- pass
526
+ if opentelemetry is None :
527
+ return contextlib .nullcontext () # type: ignore[unreachable]
528
+
529
+ links = [opentelemetry .trace .Link (context ) for context in contexts ]
530
+ span = start_active_span (
531
+ name = operation_name ,
532
+ links = links ,
533
+ )
534
+
535
+ if inherit_force_tracing and any (
536
+ is_context_forced_tracing (ctx ) for ctx in contexts
537
+ ):
538
+ force_tracing (span )
539
+
540
+ return span
496
541
497
542
498
543
def start_active_span_from_edu (
@@ -529,7 +574,7 @@ def set_attribute(key: str, value: Union[str, bool, int, float]) -> None:
529
574
530
575
@ensure_active_span ("set the status" )
531
576
def set_status (
532
- status : "opentelemetry.trace.StatusCode" , exc : Optional ( Exception )
577
+ status : "opentelemetry.trace.StatusCode" , exc : Optional [ Exception ]
533
578
) -> None :
534
579
"""Sets a tag on the active span"""
535
580
active_span = get_active_span ()
@@ -825,15 +870,16 @@ def trace_servlet(
825
870
}
826
871
827
872
request_name = request .request_metrics .name
828
- context = span_context_from_request (request ) if extract_context else None
873
+ span_context = span_context_from_request (request ) if extract_context else None
829
874
830
875
# we configure the scope not to finish the span immediately on exit, and instead
831
876
# pass the span into the SynapseRequest, which will finish it once we've finished
832
877
# sending the response to the client.
878
+
833
879
with start_active_span (
834
880
request_name ,
835
881
kind = SpanKind .SERVER ,
836
- context = context ,
882
+ context = span_context ,
837
883
end_on_exit = False ,
838
884
) as span :
839
885
request .set_tracing_span (span )
0 commit comments