1# Copyright The OpenTelemetry Authors
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""
16The OpenTelemetry tracing API describes the classes used to generate
17distributed traces.
18
19The :class:`.Tracer` class controls access to the execution context, and
20manages span creation. Each operation in a trace is represented by a
21:class:`.Span`, which records the start, end time, and metadata associated with
22the operation.
23
24This module provides abstract (i.e. unimplemented) classes required for
25tracing, and a concrete no-op :class:`.NonRecordingSpan` that allows applications
26to use the API package alone without a supporting implementation.
27
28To get a tracer, you need to provide the package name from which you are
29calling the tracer APIs to OpenTelemetry by calling `TracerProvider.get_tracer`
30with the calling module name and the version of your package.
31
32The tracer supports creating spans that are "attached" or "detached" from the
33context. New spans are "attached" to the context in that they are
34created as children of the currently active span, and the newly-created span
35can optionally become the new active span::
36
37 from opentelemetry import trace
38
39 tracer = trace.get_tracer(__name__)
40
41 # Create a new root span, set it as the current span in context
42 with tracer.start_as_current_span("parent"):
43 # Attach a new child and update the current span
44 with tracer.start_as_current_span("child"):
45 do_work():
46 # Close child span, set parent as current
47 # Close parent span, set default span as current
48
49When creating a span that's "detached" from the context the active span doesn't
50change, and the caller is responsible for managing the span's lifetime::
51
52 # Explicit parent span assignment is done via the Context
53 from opentelemetry.trace import set_span_in_context
54
55 context = set_span_in_context(parent)
56 child = tracer.start_span("child", context=context)
57
58 try:
59 do_work(span=child)
60 finally:
61 child.end()
62
63Applications should generally use a single global TracerProvider, and use
64either implicit or explicit context propagation consistently throughout.
65
66.. versionadded:: 0.1.0
67.. versionchanged:: 0.3.0
68 `TracerProvider` was introduced and the global ``tracer`` getter was
69 replaced by ``tracer_provider``.
70.. versionchanged:: 0.5.0
71 ``tracer_provider`` was replaced by `get_tracer_provider`,
72 ``set_preferred_tracer_provider_implementation`` was replaced by
73 `set_tracer_provider`.
74"""
75
76import os
77import typing
78from abc import ABC, abstractmethod
79from enum import Enum
80from logging import getLogger
81from typing import Iterator, Optional, Sequence, cast
82
83from deprecated import deprecated
84
85from opentelemetry import context as context_api
86from opentelemetry.attributes import BoundedAttributes
87from opentelemetry.context.context import Context
88from opentelemetry.environment_variables import OTEL_PYTHON_TRACER_PROVIDER
89from opentelemetry.trace.propagation import (
90 _SPAN_KEY,
91 get_current_span,
92 set_span_in_context,
93)
94from opentelemetry.trace.span import (
95 DEFAULT_TRACE_OPTIONS,
96 DEFAULT_TRACE_STATE,
97 INVALID_SPAN,
98 INVALID_SPAN_CONTEXT,
99 INVALID_SPAN_ID,
100 INVALID_TRACE_ID,
101 NonRecordingSpan,
102 Span,
103 SpanContext,
104 TraceFlags,
105 TraceState,
106 format_span_id,
107 format_trace_id,
108)
109from opentelemetry.trace.status import Status, StatusCode
110from opentelemetry.util import types
111from opentelemetry.util._decorator import _agnosticcontextmanager
112from opentelemetry.util._once import Once
113from opentelemetry.util._providers import _load_provider
114
115logger = getLogger(__name__)
116
117
118class _LinkBase(ABC):
119 def __init__(self, context: "SpanContext") -> None:
120 self._context = context
121
122 @property
123 def context(self) -> "SpanContext":
124 return self._context
125
126 @property
127 @abstractmethod
128 def attributes(self) -> types.Attributes:
129 pass
130
131
132class Link(_LinkBase):
133 """A link to a `Span`. The attributes of a Link are immutable.
134
135 Args:
136 context: `SpanContext` of the `Span` to link to.
137 attributes: Link's attributes.
138 """
139
140 def __init__(
141 self,
142 context: "SpanContext",
143 attributes: types.Attributes = None,
144 ) -> None:
145 super().__init__(context)
146 self._attributes = attributes
147
148 @property
149 def attributes(self) -> types.Attributes:
150 return self._attributes
151
152 @property
153 def dropped_attributes(self) -> int:
154 if isinstance(self._attributes, BoundedAttributes):
155 return self._attributes.dropped
156 return 0
157
158
159_Links = Optional[Sequence[Link]]
160
161
162class SpanKind(Enum):
163 """Specifies additional details on how this span relates to its parent span.
164
165 Note that this enumeration is experimental and likely to change. See
166 https://github.com/open-telemetry/opentelemetry-specification/pull/226.
167 """
168
169 #: Default value. Indicates that the span is used internally in the
170 # application.
171 INTERNAL = 0
172
173 #: Indicates that the span describes an operation that handles a remote
174 # request.
175 SERVER = 1
176
177 #: Indicates that the span describes a request to some remote service.
178 CLIENT = 2
179
180 #: Indicates that the span describes a producer sending a message to a
181 #: broker. Unlike client and server, there is usually no direct critical
182 #: path latency relationship between producer and consumer spans.
183 PRODUCER = 3
184
185 #: Indicates that the span describes a consumer receiving a message from a
186 #: broker. Unlike client and server, there is usually no direct critical
187 #: path latency relationship between producer and consumer spans.
188 CONSUMER = 4
189
190
191class TracerProvider(ABC):
192 @abstractmethod
193 def get_tracer(
194 self,
195 instrumenting_module_name: str,
196 instrumenting_library_version: typing.Optional[str] = None,
197 schema_url: typing.Optional[str] = None,
198 attributes: typing.Optional[types.Attributes] = None,
199 ) -> "Tracer":
200 """Returns a `Tracer` for use by the given instrumentation library.
201
202 For any two calls it is undefined whether the same or different
203 `Tracer` instances are returned, even for different library names.
204
205 This function may return different `Tracer` types (e.g. a no-op tracer
206 vs. a functional tracer).
207
208 Args:
209 instrumenting_module_name: The uniquely identifiable name for instrumentation
210 scope, such as instrumentation library, package, module or class name.
211 ``__name__`` may not be used as this can result in
212 different tracer names if the tracers are in different files.
213 It is better to use a fixed string that can be imported where
214 needed and used consistently as the name of the tracer.
215
216 This should *not* be the name of the module that is
217 instrumented but the name of the module doing the instrumentation.
218 E.g., instead of ``"requests"``, use
219 ``"opentelemetry.instrumentation.requests"``.
220
221 instrumenting_library_version: Optional. The version string of the
222 instrumenting library. Usually this should be the same as
223 ``importlib.metadata.version(instrumenting_library_name)``.
224
225 schema_url: Optional. Specifies the Schema URL of the emitted telemetry.
226 attributes: Optional. Specifies the attributes of the emitted telemetry.
227 """
228
229
230class NoOpTracerProvider(TracerProvider):
231 """The default TracerProvider, used when no implementation is available.
232
233 All operations are no-op.
234 """
235
236 def get_tracer(
237 self,
238 instrumenting_module_name: str,
239 instrumenting_library_version: typing.Optional[str] = None,
240 schema_url: typing.Optional[str] = None,
241 attributes: typing.Optional[types.Attributes] = None,
242 ) -> "Tracer":
243 # pylint:disable=no-self-use,unused-argument
244 return NoOpTracer()
245
246
247@deprecated(version="1.9.0", reason="You should use NoOpTracerProvider") # type: ignore
248class _DefaultTracerProvider(NoOpTracerProvider):
249 """The default TracerProvider, used when no implementation is available.
250
251 All operations are no-op.
252 """
253
254
255class ProxyTracerProvider(TracerProvider):
256 def get_tracer(
257 self,
258 instrumenting_module_name: str,
259 instrumenting_library_version: typing.Optional[str] = None,
260 schema_url: typing.Optional[str] = None,
261 attributes: typing.Optional[types.Attributes] = None,
262 ) -> "Tracer":
263 if _TRACER_PROVIDER:
264 return _TRACER_PROVIDER.get_tracer(
265 instrumenting_module_name,
266 instrumenting_library_version,
267 schema_url,
268 attributes,
269 )
270 return ProxyTracer(
271 instrumenting_module_name,
272 instrumenting_library_version,
273 schema_url,
274 attributes,
275 )
276
277
278class Tracer(ABC):
279 """Handles span creation and in-process context propagation.
280
281 This class provides methods for manipulating the context, creating spans,
282 and controlling spans' lifecycles.
283 """
284
285 @abstractmethod
286 def start_span(
287 self,
288 name: str,
289 context: Optional[Context] = None,
290 kind: SpanKind = SpanKind.INTERNAL,
291 attributes: types.Attributes = None,
292 links: _Links = None,
293 start_time: Optional[int] = None,
294 record_exception: bool = True,
295 set_status_on_exception: bool = True,
296 ) -> "Span":
297 """Starts a span.
298
299 Create a new span. Start the span without setting it as the current
300 span in the context. To start the span and use the context in a single
301 method, see :meth:`start_as_current_span`.
302
303 By default the current span in the context will be used as parent, but an
304 explicit context can also be specified, by passing in a `Context` containing
305 a current `Span`. If there is no current span in the global `Context` or in
306 the specified context, the created span will be a root span.
307
308 The span can be used as a context manager. On exiting the context manager,
309 the span's end() method will be called.
310
311 Example::
312
313 # trace.get_current_span() will be used as the implicit parent.
314 # If none is found, the created span will be a root instance.
315 with tracer.start_span("one") as child:
316 child.add_event("child's event")
317
318 Args:
319 name: The name of the span to be created.
320 context: An optional Context containing the span's parent. Defaults to the
321 global context.
322 kind: The span's kind (relationship to parent). Note that is
323 meaningful even if there is no parent.
324 attributes: The span's attributes.
325 links: Links span to other spans
326 start_time: Sets the start time of a span
327 record_exception: Whether to record any exceptions raised within the
328 context as error event on the span.
329 set_status_on_exception: Only relevant if the returned span is used
330 in a with/context manager. Defines whether the span status will
331 be automatically set to ERROR when an uncaught exception is
332 raised in the span with block. The span status won't be set by
333 this mechanism if it was previously set manually.
334
335 Returns:
336 The newly-created span.
337 """
338
339 @_agnosticcontextmanager
340 @abstractmethod
341 def start_as_current_span(
342 self,
343 name: str,
344 context: Optional[Context] = None,
345 kind: SpanKind = SpanKind.INTERNAL,
346 attributes: types.Attributes = None,
347 links: _Links = None,
348 start_time: Optional[int] = None,
349 record_exception: bool = True,
350 set_status_on_exception: bool = True,
351 end_on_exit: bool = True,
352 ) -> Iterator["Span"]:
353 """Context manager for creating a new span and set it
354 as the current span in this tracer's context.
355
356 Exiting the context manager will call the span's end method,
357 as well as return the current span to its previous value by
358 returning to the previous context.
359
360 Example::
361
362 with tracer.start_as_current_span("one") as parent:
363 parent.add_event("parent's event")
364 with tracer.start_as_current_span("two") as child:
365 child.add_event("child's event")
366 trace.get_current_span() # returns child
367 trace.get_current_span() # returns parent
368 trace.get_current_span() # returns previously active span
369
370 This is a convenience method for creating spans attached to the
371 tracer's context. Applications that need more control over the span
372 lifetime should use :meth:`start_span` instead. For example::
373
374 with tracer.start_as_current_span(name) as span:
375 do_work()
376
377 is equivalent to::
378
379 span = tracer.start_span(name)
380 with opentelemetry.trace.use_span(span, end_on_exit=True):
381 do_work()
382
383 This can also be used as a decorator::
384
385 @tracer.start_as_current_span("name")
386 def function():
387 ...
388
389 function()
390
391 Args:
392 name: The name of the span to be created.
393 context: An optional Context containing the span's parent. Defaults to the
394 global context.
395 kind: The span's kind (relationship to parent). Note that is
396 meaningful even if there is no parent.
397 attributes: The span's attributes.
398 links: Links span to other spans
399 start_time: Sets the start time of a span
400 record_exception: Whether to record any exceptions raised within the
401 context as error event on the span.
402 set_status_on_exception: Only relevant if the returned span is used
403 in a with/context manager. Defines whether the span status will
404 be automatically set to ERROR when an uncaught exception is
405 raised in the span with block. The span status won't be set by
406 this mechanism if it was previously set manually.
407 end_on_exit: Whether to end the span automatically when leaving the
408 context manager.
409
410 Yields:
411 The newly-created span.
412 """
413
414
415class ProxyTracer(Tracer):
416 # pylint: disable=W0222,signature-differs
417 def __init__(
418 self,
419 instrumenting_module_name: str,
420 instrumenting_library_version: typing.Optional[str] = None,
421 schema_url: typing.Optional[str] = None,
422 attributes: typing.Optional[types.Attributes] = None,
423 ):
424 self._instrumenting_module_name = instrumenting_module_name
425 self._instrumenting_library_version = instrumenting_library_version
426 self._schema_url = schema_url
427 self._attributes = attributes
428 self._real_tracer: Optional[Tracer] = None
429 self._noop_tracer = NoOpTracer()
430
431 @property
432 def _tracer(self) -> Tracer:
433 if self._real_tracer:
434 return self._real_tracer
435
436 if _TRACER_PROVIDER:
437 self._real_tracer = _TRACER_PROVIDER.get_tracer(
438 self._instrumenting_module_name,
439 self._instrumenting_library_version,
440 self._schema_url,
441 self._attributes,
442 )
443 return self._real_tracer
444 return self._noop_tracer
445
446 def start_span(self, *args, **kwargs) -> Span: # type: ignore
447 return self._tracer.start_span(*args, **kwargs) # type: ignore
448
449 @_agnosticcontextmanager # type: ignore
450 def start_as_current_span(self, *args, **kwargs) -> Iterator[Span]:
451 with self._tracer.start_as_current_span(*args, **kwargs) as span: # type: ignore
452 yield span
453
454
455class NoOpTracer(Tracer):
456 """The default Tracer, used when no Tracer implementation is available.
457
458 All operations are no-op.
459 """
460
461 def start_span(
462 self,
463 name: str,
464 context: Optional[Context] = None,
465 kind: SpanKind = SpanKind.INTERNAL,
466 attributes: types.Attributes = None,
467 links: _Links = None,
468 start_time: Optional[int] = None,
469 record_exception: bool = True,
470 set_status_on_exception: bool = True,
471 ) -> "Span":
472 return INVALID_SPAN
473
474 @_agnosticcontextmanager
475 def start_as_current_span(
476 self,
477 name: str,
478 context: Optional[Context] = None,
479 kind: SpanKind = SpanKind.INTERNAL,
480 attributes: types.Attributes = None,
481 links: _Links = None,
482 start_time: Optional[int] = None,
483 record_exception: bool = True,
484 set_status_on_exception: bool = True,
485 end_on_exit: bool = True,
486 ) -> Iterator["Span"]:
487 yield INVALID_SPAN
488
489
490@deprecated(version="1.9.0", reason="You should use NoOpTracer") # type: ignore
491class _DefaultTracer(NoOpTracer):
492 """The default Tracer, used when no Tracer implementation is available.
493
494 All operations are no-op.
495 """
496
497
498_TRACER_PROVIDER_SET_ONCE = Once()
499_TRACER_PROVIDER: Optional[TracerProvider] = None
500_PROXY_TRACER_PROVIDER = ProxyTracerProvider()
501
502
503def get_tracer(
504 instrumenting_module_name: str,
505 instrumenting_library_version: typing.Optional[str] = None,
506 tracer_provider: Optional[TracerProvider] = None,
507 schema_url: typing.Optional[str] = None,
508 attributes: typing.Optional[types.Attributes] = None,
509) -> "Tracer":
510 """Returns a `Tracer` for use by the given instrumentation library.
511
512 This function is a convenience wrapper for
513 opentelemetry.trace.TracerProvider.get_tracer.
514
515 If tracer_provider is omitted the current configured one is used.
516 """
517 if tracer_provider is None:
518 tracer_provider = get_tracer_provider()
519 return tracer_provider.get_tracer(
520 instrumenting_module_name,
521 instrumenting_library_version,
522 schema_url,
523 attributes,
524 )
525
526
527def _set_tracer_provider(tracer_provider: TracerProvider, log: bool) -> None:
528 def set_tp() -> None:
529 global _TRACER_PROVIDER # pylint: disable=global-statement
530 _TRACER_PROVIDER = tracer_provider
531
532 did_set = _TRACER_PROVIDER_SET_ONCE.do_once(set_tp)
533
534 if log and not did_set:
535 logger.warning("Overriding of current TracerProvider is not allowed")
536
537
538def set_tracer_provider(tracer_provider: TracerProvider) -> None:
539 """Sets the current global :class:`~.TracerProvider` object.
540
541 This can only be done once, a warning will be logged if any further attempt
542 is made.
543 """
544 _set_tracer_provider(tracer_provider, log=True)
545
546
547def get_tracer_provider() -> TracerProvider:
548 """Gets the current global :class:`~.TracerProvider` object."""
549 if _TRACER_PROVIDER is None:
550 # if a global tracer provider has not been set either via code or env
551 # vars, return a proxy tracer provider
552 if OTEL_PYTHON_TRACER_PROVIDER not in os.environ:
553 return _PROXY_TRACER_PROVIDER
554
555 tracer_provider: TracerProvider = _load_provider(
556 OTEL_PYTHON_TRACER_PROVIDER, "tracer_provider"
557 )
558 _set_tracer_provider(tracer_provider, log=False)
559 # _TRACER_PROVIDER will have been set by one thread
560 return cast("TracerProvider", _TRACER_PROVIDER)
561
562
563@_agnosticcontextmanager
564def use_span(
565 span: Span,
566 end_on_exit: bool = False,
567 record_exception: bool = True,
568 set_status_on_exception: bool = True,
569) -> Iterator[Span]:
570 """Takes a non-active span and activates it in the current context.
571
572 Args:
573 span: The span that should be activated in the current context.
574 end_on_exit: Whether to end the span automatically when leaving the
575 context manager scope.
576 record_exception: Whether to record any exceptions raised within the
577 context as error event on the span.
578 set_status_on_exception: Only relevant if the returned span is used
579 in a with/context manager. Defines whether the span status will
580 be automatically set to ERROR when an uncaught exception is
581 raised in the span with block. The span status won't be set by
582 this mechanism if it was previously set manually.
583 """
584 try:
585 token = context_api.attach(context_api.set_value(_SPAN_KEY, span))
586 try:
587 yield span
588 finally:
589 context_api.detach(token)
590
591 except BaseException as exc: # pylint: disable=broad-exception-caught
592 if isinstance(span, Span) and span.is_recording():
593 # Record the exception as an event
594 if record_exception:
595 span.record_exception(exc)
596
597 # Set status in case exception was raised
598 if set_status_on_exception:
599 span.set_status(
600 Status(
601 status_code=StatusCode.ERROR,
602 description=f"{type(exc).__name__}: {exc}",
603 )
604 )
605
606 # This causes parent spans to set their status to ERROR and to record
607 # an exception as an event if a child span raises an exception even if
608 # such child span was started with both record_exception and
609 # set_status_on_exception attributes set to False.
610 raise
611
612 finally:
613 if end_on_exit:
614 span.end()
615
616
617__all__ = [
618 "DEFAULT_TRACE_OPTIONS",
619 "DEFAULT_TRACE_STATE",
620 "INVALID_SPAN",
621 "INVALID_SPAN_CONTEXT",
622 "INVALID_SPAN_ID",
623 "INVALID_TRACE_ID",
624 "NonRecordingSpan",
625 "Link",
626 "Span",
627 "SpanContext",
628 "SpanKind",
629 "TraceFlags",
630 "TraceState",
631 "TracerProvider",
632 "Tracer",
633 "format_span_id",
634 "format_trace_id",
635 "get_current_span",
636 "get_tracer",
637 "get_tracer_provider",
638 "set_tracer_provider",
639 "set_span_in_context",
640 "use_span",
641 "Status",
642 "StatusCode",
643]