diff options
Diffstat (limited to '.venv/lib/python3.12/site-packages/opentelemetry/trace/__init__.py')
-rw-r--r-- | .venv/lib/python3.12/site-packages/opentelemetry/trace/__init__.py | 646 |
1 files changed, 646 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/opentelemetry/trace/__init__.py b/.venv/lib/python3.12/site-packages/opentelemetry/trace/__init__.py new file mode 100644 index 00000000..73087e95 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/opentelemetry/trace/__init__.py @@ -0,0 +1,646 @@ +# Copyright The OpenTelemetry Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +The OpenTelemetry tracing API describes the classes used to generate +distributed traces. + +The :class:`.Tracer` class controls access to the execution context, and +manages span creation. Each operation in a trace is represented by a +:class:`.Span`, which records the start, end time, and metadata associated with +the operation. + +This module provides abstract (i.e. unimplemented) classes required for +tracing, and a concrete no-op :class:`.NonRecordingSpan` that allows applications +to use the API package alone without a supporting implementation. + +To get a tracer, you need to provide the package name from which you are +calling the tracer APIs to OpenTelemetry by calling `TracerProvider.get_tracer` +with the calling module name and the version of your package. + +The tracer supports creating spans that are "attached" or "detached" from the +context. New spans are "attached" to the context in that they are +created as children of the currently active span, and the newly-created span +can optionally become the new active span:: + + from opentelemetry import trace + + tracer = trace.get_tracer(__name__) + + # Create a new root span, set it as the current span in context + with tracer.start_as_current_span("parent"): + # Attach a new child and update the current span + with tracer.start_as_current_span("child"): + do_work(): + # Close child span, set parent as current + # Close parent span, set default span as current + +When creating a span that's "detached" from the context the active span doesn't +change, and the caller is responsible for managing the span's lifetime:: + + # Explicit parent span assignment is done via the Context + from opentelemetry.trace import set_span_in_context + + context = set_span_in_context(parent) + child = tracer.start_span("child", context=context) + + try: + do_work(span=child) + finally: + child.end() + +Applications should generally use a single global TracerProvider, and use +either implicit or explicit context propagation consistently throughout. + +.. versionadded:: 0.1.0 +.. versionchanged:: 0.3.0 + `TracerProvider` was introduced and the global ``tracer`` getter was + replaced by ``tracer_provider``. +.. versionchanged:: 0.5.0 + ``tracer_provider`` was replaced by `get_tracer_provider`, + ``set_preferred_tracer_provider_implementation`` was replaced by + `set_tracer_provider`. +""" + +import os +import typing +from abc import ABC, abstractmethod +from enum import Enum +from logging import getLogger +from typing import Iterator, Optional, Sequence, cast + +from deprecated import deprecated + +from opentelemetry import context as context_api +from opentelemetry.attributes import BoundedAttributes +from opentelemetry.context.context import Context +from opentelemetry.environment_variables import OTEL_PYTHON_TRACER_PROVIDER +from opentelemetry.trace.propagation import ( + _SPAN_KEY, + get_current_span, + set_span_in_context, +) +from opentelemetry.trace.span import ( + DEFAULT_TRACE_OPTIONS, + DEFAULT_TRACE_STATE, + INVALID_SPAN, + INVALID_SPAN_CONTEXT, + INVALID_SPAN_ID, + INVALID_TRACE_ID, + NonRecordingSpan, + Span, + SpanContext, + TraceFlags, + TraceState, + format_span_id, + format_trace_id, +) +from opentelemetry.trace.status import Status, StatusCode +from opentelemetry.util import types +from opentelemetry.util._decorator import _agnosticcontextmanager +from opentelemetry.util._once import Once +from opentelemetry.util._providers import _load_provider + +logger = getLogger(__name__) + + +class _LinkBase(ABC): + def __init__(self, context: "SpanContext") -> None: + self._context = context + + @property + def context(self) -> "SpanContext": + return self._context + + @property + @abstractmethod + def attributes(self) -> types.Attributes: + pass + + +class Link(_LinkBase): + """A link to a `Span`. The attributes of a Link are immutable. + + Args: + context: `SpanContext` of the `Span` to link to. + attributes: Link's attributes. + """ + + def __init__( + self, + context: "SpanContext", + attributes: types.Attributes = None, + ) -> None: + super().__init__(context) + self._attributes = attributes + + @property + def attributes(self) -> types.Attributes: + return self._attributes + + @property + def dropped_attributes(self) -> int: + if isinstance(self._attributes, BoundedAttributes): + return self._attributes.dropped + return 0 + + +_Links = Optional[Sequence[Link]] + + +class SpanKind(Enum): + """Specifies additional details on how this span relates to its parent span. + + Note that this enumeration is experimental and likely to change. See + https://github.com/open-telemetry/opentelemetry-specification/pull/226. + """ + + #: Default value. Indicates that the span is used internally in the + # application. + INTERNAL = 0 + + #: Indicates that the span describes an operation that handles a remote + # request. + SERVER = 1 + + #: Indicates that the span describes a request to some remote service. + CLIENT = 2 + + #: Indicates that the span describes a producer sending a message to a + #: broker. Unlike client and server, there is usually no direct critical + #: path latency relationship between producer and consumer spans. + PRODUCER = 3 + + #: Indicates that the span describes a consumer receiving a message from a + #: broker. Unlike client and server, there is usually no direct critical + #: path latency relationship between producer and consumer spans. + CONSUMER = 4 + + +class TracerProvider(ABC): + @abstractmethod + def get_tracer( + self, + instrumenting_module_name: str, + instrumenting_library_version: typing.Optional[str] = None, + schema_url: typing.Optional[str] = None, + attributes: typing.Optional[types.Attributes] = None, + ) -> "Tracer": + """Returns a `Tracer` for use by the given instrumentation library. + + For any two calls it is undefined whether the same or different + `Tracer` instances are returned, even for different library names. + + This function may return different `Tracer` types (e.g. a no-op tracer + vs. a functional tracer). + + Args: + instrumenting_module_name: The uniquely identifiable name for instrumentation + scope, such as instrumentation library, package, module or class name. + ``__name__`` may not be used as this can result in + different tracer names if the tracers are in different files. + It is better to use a fixed string that can be imported where + needed and used consistently as the name of the tracer. + + This should *not* be the name of the module that is + instrumented but the name of the module doing the instrumentation. + E.g., instead of ``"requests"``, use + ``"opentelemetry.instrumentation.requests"``. + + instrumenting_library_version: Optional. The version string of the + instrumenting library. Usually this should be the same as + ``importlib.metadata.version(instrumenting_library_name)``. + + schema_url: Optional. Specifies the Schema URL of the emitted telemetry. + attributes: Optional. Specifies the attributes of the emitted telemetry. + """ + + +class NoOpTracerProvider(TracerProvider): + """The default TracerProvider, used when no implementation is available. + + All operations are no-op. + """ + + def get_tracer( + self, + instrumenting_module_name: str, + instrumenting_library_version: typing.Optional[str] = None, + schema_url: typing.Optional[str] = None, + attributes: typing.Optional[types.Attributes] = None, + ) -> "Tracer": + # pylint:disable=no-self-use,unused-argument + return NoOpTracer() + + +@deprecated(version="1.9.0", reason="You should use NoOpTracerProvider") # type: ignore +class _DefaultTracerProvider(NoOpTracerProvider): + """The default TracerProvider, used when no implementation is available. + + All operations are no-op. + """ + + +class ProxyTracerProvider(TracerProvider): + def get_tracer( + self, + instrumenting_module_name: str, + instrumenting_library_version: typing.Optional[str] = None, + schema_url: typing.Optional[str] = None, + attributes: typing.Optional[types.Attributes] = None, + ) -> "Tracer": + if _TRACER_PROVIDER: + return _TRACER_PROVIDER.get_tracer( + instrumenting_module_name, + instrumenting_library_version, + schema_url, + attributes, + ) + return ProxyTracer( + instrumenting_module_name, + instrumenting_library_version, + schema_url, + attributes, + ) + + +class Tracer(ABC): + """Handles span creation and in-process context propagation. + + This class provides methods for manipulating the context, creating spans, + and controlling spans' lifecycles. + """ + + @abstractmethod + def start_span( + self, + name: str, + context: Optional[Context] = None, + kind: SpanKind = SpanKind.INTERNAL, + attributes: types.Attributes = None, + links: _Links = None, + start_time: Optional[int] = None, + record_exception: bool = True, + set_status_on_exception: bool = True, + ) -> "Span": + """Starts a span. + + Create a new span. Start the span without setting it as the current + span in the context. To start the span and use the context in a single + method, see :meth:`start_as_current_span`. + + By default the current span in the context will be used as parent, but an + explicit context can also be specified, by passing in a `Context` containing + a current `Span`. If there is no current span in the global `Context` or in + the specified context, the created span will be a root span. + + The span can be used as a context manager. On exiting the context manager, + the span's end() method will be called. + + Example:: + + # trace.get_current_span() will be used as the implicit parent. + # If none is found, the created span will be a root instance. + with tracer.start_span("one") as child: + child.add_event("child's event") + + Args: + name: The name of the span to be created. + context: An optional Context containing the span's parent. Defaults to the + global context. + kind: The span's kind (relationship to parent). Note that is + meaningful even if there is no parent. + attributes: The span's attributes. + links: Links span to other spans + start_time: Sets the start time of a span + record_exception: Whether to record any exceptions raised within the + context as error event on the span. + set_status_on_exception: Only relevant if the returned span is used + in a with/context manager. Defines whether the span status will + be automatically set to ERROR when an uncaught exception is + raised in the span with block. The span status won't be set by + this mechanism if it was previously set manually. + + Returns: + The newly-created span. + """ + + @_agnosticcontextmanager + @abstractmethod + def start_as_current_span( + self, + name: str, + context: Optional[Context] = None, + kind: SpanKind = SpanKind.INTERNAL, + attributes: types.Attributes = None, + links: _Links = None, + start_time: Optional[int] = None, + record_exception: bool = True, + set_status_on_exception: bool = True, + end_on_exit: bool = True, + ) -> Iterator["Span"]: + """Context manager for creating a new span and set it + as the current span in this tracer's context. + + Exiting the context manager will call the span's end method, + as well as return the current span to its previous value by + returning to the previous context. + + Example:: + + with tracer.start_as_current_span("one") as parent: + parent.add_event("parent's event") + with tracer.start_as_current_span("two") as child: + child.add_event("child's event") + trace.get_current_span() # returns child + trace.get_current_span() # returns parent + trace.get_current_span() # returns previously active span + + This is a convenience method for creating spans attached to the + tracer's context. Applications that need more control over the span + lifetime should use :meth:`start_span` instead. For example:: + + with tracer.start_as_current_span(name) as span: + do_work() + + is equivalent to:: + + span = tracer.start_span(name) + with opentelemetry.trace.use_span(span, end_on_exit=True): + do_work() + + This can also be used as a decorator:: + + @tracer.start_as_current_span("name") + def function(): + ... + + function() + + Args: + name: The name of the span to be created. + context: An optional Context containing the span's parent. Defaults to the + global context. + kind: The span's kind (relationship to parent). Note that is + meaningful even if there is no parent. + attributes: The span's attributes. + links: Links span to other spans + start_time: Sets the start time of a span + record_exception: Whether to record any exceptions raised within the + context as error event on the span. + set_status_on_exception: Only relevant if the returned span is used + in a with/context manager. Defines whether the span status will + be automatically set to ERROR when an uncaught exception is + raised in the span with block. The span status won't be set by + this mechanism if it was previously set manually. + end_on_exit: Whether to end the span automatically when leaving the + context manager. + + Yields: + The newly-created span. + """ + + +class ProxyTracer(Tracer): + # pylint: disable=W0222,signature-differs + def __init__( + self, + instrumenting_module_name: str, + instrumenting_library_version: typing.Optional[str] = None, + schema_url: typing.Optional[str] = None, + attributes: typing.Optional[types.Attributes] = None, + ): + self._instrumenting_module_name = instrumenting_module_name + self._instrumenting_library_version = instrumenting_library_version + self._schema_url = schema_url + self._attributes = attributes + self._real_tracer: Optional[Tracer] = None + self._noop_tracer = NoOpTracer() + + @property + def _tracer(self) -> Tracer: + if self._real_tracer: + return self._real_tracer + + if _TRACER_PROVIDER: + self._real_tracer = _TRACER_PROVIDER.get_tracer( + self._instrumenting_module_name, + self._instrumenting_library_version, + self._schema_url, + self._attributes, + ) + return self._real_tracer + return self._noop_tracer + + def start_span(self, *args, **kwargs) -> Span: # type: ignore + return self._tracer.start_span(*args, **kwargs) # type: ignore + + @_agnosticcontextmanager # type: ignore + def start_as_current_span(self, *args, **kwargs) -> Iterator[Span]: + with self._tracer.start_as_current_span(*args, **kwargs) as span: # type: ignore + yield span + + +class NoOpTracer(Tracer): + """The default Tracer, used when no Tracer implementation is available. + + All operations are no-op. + """ + + def start_span( + self, + name: str, + context: Optional[Context] = None, + kind: SpanKind = SpanKind.INTERNAL, + attributes: types.Attributes = None, + links: _Links = None, + start_time: Optional[int] = None, + record_exception: bool = True, + set_status_on_exception: bool = True, + ) -> "Span": + return INVALID_SPAN + + @_agnosticcontextmanager + def start_as_current_span( + self, + name: str, + context: Optional[Context] = None, + kind: SpanKind = SpanKind.INTERNAL, + attributes: types.Attributes = None, + links: _Links = None, + start_time: Optional[int] = None, + record_exception: bool = True, + set_status_on_exception: bool = True, + end_on_exit: bool = True, + ) -> Iterator["Span"]: + yield INVALID_SPAN + + +@deprecated(version="1.9.0", reason="You should use NoOpTracer") # type: ignore +class _DefaultTracer(NoOpTracer): + """The default Tracer, used when no Tracer implementation is available. + + All operations are no-op. + """ + + +_TRACER_PROVIDER_SET_ONCE = Once() +_TRACER_PROVIDER: Optional[TracerProvider] = None +_PROXY_TRACER_PROVIDER = ProxyTracerProvider() + + +def get_tracer( + instrumenting_module_name: str, + instrumenting_library_version: typing.Optional[str] = None, + tracer_provider: Optional[TracerProvider] = None, + schema_url: typing.Optional[str] = None, + attributes: typing.Optional[types.Attributes] = None, +) -> "Tracer": + """Returns a `Tracer` for use by the given instrumentation library. + + This function is a convenience wrapper for + opentelemetry.trace.TracerProvider.get_tracer. + + If tracer_provider is omitted the current configured one is used. + """ + if tracer_provider is None: + tracer_provider = get_tracer_provider() + return tracer_provider.get_tracer( + instrumenting_module_name, + instrumenting_library_version, + schema_url, + attributes, + ) + + +def _set_tracer_provider(tracer_provider: TracerProvider, log: bool) -> None: + def set_tp() -> None: + global _TRACER_PROVIDER # pylint: disable=global-statement + _TRACER_PROVIDER = tracer_provider + + did_set = _TRACER_PROVIDER_SET_ONCE.do_once(set_tp) + + if log and not did_set: + logger.warning("Overriding of current TracerProvider is not allowed") + + +def set_tracer_provider(tracer_provider: TracerProvider) -> None: + """Sets the current global :class:`~.TracerProvider` object. + + This can only be done once, a warning will be logged if any further attempt + is made. + """ + _set_tracer_provider(tracer_provider, log=True) + + +def get_tracer_provider() -> TracerProvider: + """Gets the current global :class:`~.TracerProvider` object.""" + if _TRACER_PROVIDER is None: + # if a global tracer provider has not been set either via code or env + # vars, return a proxy tracer provider + if OTEL_PYTHON_TRACER_PROVIDER not in os.environ: + return _PROXY_TRACER_PROVIDER + + tracer_provider: TracerProvider = _load_provider( + OTEL_PYTHON_TRACER_PROVIDER, "tracer_provider" + ) + _set_tracer_provider(tracer_provider, log=False) + # _TRACER_PROVIDER will have been set by one thread + return cast("TracerProvider", _TRACER_PROVIDER) + + +@_agnosticcontextmanager +def use_span( + span: Span, + end_on_exit: bool = False, + record_exception: bool = True, + set_status_on_exception: bool = True, +) -> Iterator[Span]: + """Takes a non-active span and activates it in the current context. + + Args: + span: The span that should be activated in the current context. + end_on_exit: Whether to end the span automatically when leaving the + context manager scope. + record_exception: Whether to record any exceptions raised within the + context as error event on the span. + set_status_on_exception: Only relevant if the returned span is used + in a with/context manager. Defines whether the span status will + be automatically set to ERROR when an uncaught exception is + raised in the span with block. The span status won't be set by + this mechanism if it was previously set manually. + """ + try: + token = context_api.attach(context_api.set_value(_SPAN_KEY, span)) + try: + yield span + finally: + context_api.detach(token) + + # Record only exceptions that inherit Exception class but not BaseException, because + # classes that directly inherit BaseException are not technically errors, e.g. GeneratorExit. + # See https://github.com/open-telemetry/opentelemetry-python/issues/4484 + except Exception as exc: # pylint: disable=broad-exception-caught + if isinstance(span, Span) and span.is_recording(): + # Record the exception as an event + if record_exception: + span.record_exception(exc) + + # Set status in case exception was raised + if set_status_on_exception: + span.set_status( + Status( + status_code=StatusCode.ERROR, + description=f"{type(exc).__name__}: {exc}", + ) + ) + + # This causes parent spans to set their status to ERROR and to record + # an exception as an event if a child span raises an exception even if + # such child span was started with both record_exception and + # set_status_on_exception attributes set to False. + raise + + finally: + if end_on_exit: + span.end() + + +__all__ = [ + "DEFAULT_TRACE_OPTIONS", + "DEFAULT_TRACE_STATE", + "INVALID_SPAN", + "INVALID_SPAN_CONTEXT", + "INVALID_SPAN_ID", + "INVALID_TRACE_ID", + "NonRecordingSpan", + "Link", + "Span", + "SpanContext", + "SpanKind", + "TraceFlags", + "TraceState", + "TracerProvider", + "Tracer", + "format_span_id", + "format_trace_id", + "get_current_span", + "get_tracer", + "get_tracer_provider", + "set_tracer_provider", + "set_span_in_context", + "use_span", + "Status", + "StatusCode", +] |