codekingpro/portable-devtools
114k
1# Copyright The OpenTelemetry Authors2#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 at6#7# http://www.apache.org/licenses/LICENSE-2.08#9# Unless required by applicable law or agreed to in writing, software10# 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 and13# limitations under the License.14 15"""16The OpenTelemetry tracing API describes the classes used to generate17distributed traces.18 19The :class:`.Tracer` class controls access to the execution context, and20manages span creation. Each operation in a trace is represented by a21:class:`.Span`, which records the start, end time, and metadata associated with22the operation.23 24This module provides abstract (i.e. unimplemented) classes required for25tracing, and a concrete no-op :class:`.NonRecordingSpan` that allows applications26to use the API package alone without a supporting implementation.27 28To get a tracer, you need to provide the package name from which you are29calling 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 the33context. New spans are "attached" to the context in that they are34created as children of the currently active span, and the newly-created span35can optionally become the new active span::36 37 from opentelemetry import trace38 39 tracer = trace.get_tracer(__name__)40 41 # Create a new root span, set it as the current span in context42 with tracer.start_as_current_span("parent"):43 # Attach a new child and update the current span44 with tracer.start_as_current_span("child"):45 do_work():46 # Close child span, set parent as current47 # Close parent span, set default span as current48 49When creating a span that's "detached" from the context the active span doesn't50change, and the caller is responsible for managing the span's lifetime::51 52 # Explicit parent span assignment is done via the Context53 from opentelemetry.trace import set_span_in_context54 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 use64either implicit or explicit context propagation consistently throughout.65 66.. versionadded:: 0.1.067.. versionchanged:: 0.3.068 `TracerProvider` was introduced and the global ``tracer`` getter was69 replaced by ``tracer_provider``.70.. versionchanged:: 0.5.071 ``tracer_provider`` was replaced by `get_tracer_provider`,72 ``set_preferred_tracer_provider_implementation`` was replaced by73 `set_tracer_provider`.74"""75 76import os77import typing78from abc import ABC, abstractmethod79from enum import Enum80from logging import getLogger81from typing import Iterator, Optional, Sequence, cast82 83from typing_extensions import deprecated84 85from opentelemetry import context as context_api86from opentelemetry.attributes import BoundedAttributes87from opentelemetry.context.context import Context88from opentelemetry.environment_variables import OTEL_PYTHON_TRACER_PROVIDER89from 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, StatusCode110from opentelemetry.util import types111from opentelemetry.util._decorator import _agnosticcontextmanager112from opentelemetry.util._once import Once113from opentelemetry.util._providers import _load_provider114 115logger = getLogger(__name__)116 117 118class _LinkBase(ABC):119 def __init__(self, context: "SpanContext") -> None:120 self._context = context121 122 @property123 def context(self) -> "SpanContext":124 return self._context125 126 @property127 @abstractmethod128 def attributes(self) -> types.Attributes:129 pass130 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 = attributes147 148 @property149 def attributes(self) -> types.Attributes:150 return self._attributes151 152 @property153 def dropped_attributes(self) -> int:154 if isinstance(self._attributes, BoundedAttributes):155 return self._attributes.dropped156 return 0157 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. See166 https://github.com/open-telemetry/opentelemetry-specification/pull/226.167 """168 169 #: Default value. Indicates that the span is used internally in the170 # application.171 INTERNAL = 0172 173 #: Indicates that the span describes an operation that handles a remote174 # request.175 SERVER = 1176 177 #: Indicates that the span describes a request to some remote service.178 CLIENT = 2179 180 #: Indicates that the span describes a producer sending a message to a181 #: broker. Unlike client and server, there is usually no direct critical182 #: path latency relationship between producer and consumer spans.183 PRODUCER = 3184 185 #: Indicates that the span describes a consumer receiving a message from a186 #: broker. Unlike client and server, there is usually no direct critical187 #: path latency relationship between producer and consumer spans.188 CONSUMER = 4189 190 191class TracerProvider(ABC):192 @abstractmethod193 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 different203 `Tracer` instances are returned, even for different library names.204 205 This function may return different `Tracer` types (e.g. a no-op tracer206 vs. a functional tracer).207 208 Args:209 instrumenting_module_name: The uniquely identifiable name for instrumentation210 scope, such as instrumentation library, package, module or class name.211 ``__name__`` should be avoided as this can result in212 different tracer names if the tracers are in different files.213 It is better to use a fixed string that can be imported where214 needed and used consistently as the name of the tracer.215 216 This should *not* be the name of the module that is217 instrumented but the name of the module doing the instrumentation.218 E.g., instead of ``"requests"``, use219 ``"opentelemetry.instrumentation.requests"``.220 221 instrumenting_library_version: Optional. The version string of the222 instrumenting library. Usually this should be the same as223 ``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-argument244 return NoOpTracer()245 246 247@deprecated(248 "You should use NoOpTracerProvider. Deprecated since version 1.9.0."249)250class _DefaultTracerProvider(NoOpTracerProvider):251 """The default TracerProvider, used when no implementation is available.252 253 All operations are no-op.254 """255 256 257class ProxyTracerProvider(TracerProvider):258 def get_tracer(259 self,260 instrumenting_module_name: str,261 instrumenting_library_version: typing.Optional[str] = None,262 schema_url: typing.Optional[str] = None,263 attributes: typing.Optional[types.Attributes] = None,264 ) -> "Tracer":265 if _TRACER_PROVIDER:266 return _TRACER_PROVIDER.get_tracer(267 instrumenting_module_name,268 instrumenting_library_version,269 schema_url,270 attributes,271 )272 return ProxyTracer(273 instrumenting_module_name,274 instrumenting_library_version,275 schema_url,276 attributes,277 )278 279 280class Tracer(ABC):281 """Handles span creation and in-process context propagation.282 283 This class provides methods for manipulating the context, creating spans,284 and controlling spans' lifecycles.285 """286 287 @abstractmethod288 def start_span(289 self,290 name: str,291 context: Optional[Context] = None,292 kind: SpanKind = SpanKind.INTERNAL,293 attributes: types.Attributes = None,294 links: _Links = None,295 start_time: Optional[int] = None,296 record_exception: bool = True,297 set_status_on_exception: bool = True,298 ) -> "Span":299 """Starts a span.300 301 Create a new span. Start the span without setting it as the current302 span in the context. To start the span and use the context in a single303 method, see :meth:`start_as_current_span`.304 305 By default the current span in the context will be used as parent, but an306 explicit context can also be specified, by passing in a `Context` containing307 a current `Span`. If there is no current span in the global `Context` or in308 the specified context, the created span will be a root span.309 310 The span can be used as a context manager. On exiting the context manager,311 the span's end() method will be called.312 313 Example::314 315 # trace.get_current_span() will be used as the implicit parent.316 # If none is found, the created span will be a root instance.317 with tracer.start_span("one") as child:318 child.add_event("child's event")319 320 Args:321 name: The name of the span to be created.322 context: An optional Context containing the span's parent. Defaults to the323 global context.324 kind: The span's kind (relationship to parent). Note that is325 meaningful even if there is no parent.326 attributes: The span's attributes.327 links: Links span to other spans328 start_time: Sets the start time of a span329 record_exception: Whether to record any exceptions raised within the330 context as error event on the span.331 set_status_on_exception: Only relevant if the returned span is used332 in a with/context manager. Defines whether the span status will333 be automatically set to ERROR when an uncaught exception is334 raised in the span with block. The span status won't be set by335 this mechanism if it was previously set manually.336 337 Returns:338 The newly-created span.339 """340 341 @_agnosticcontextmanager342 @abstractmethod343 def start_as_current_span(344 self,345 name: str,346 context: Optional[Context] = None,347 kind: SpanKind = SpanKind.INTERNAL,348 attributes: types.Attributes = None,349 links: _Links = None,350 start_time: Optional[int] = None,351 record_exception: bool = True,352 set_status_on_exception: bool = True,353 end_on_exit: bool = True,354 ) -> Iterator["Span"]:355 """Context manager for creating a new span and set it356 as the current span in this tracer's context.357 358 Exiting the context manager will call the span's end method,359 as well as return the current span to its previous value by360 returning to the previous context.361 362 Example::363 364 with tracer.start_as_current_span("one") as parent:365 parent.add_event("parent's event")366 with tracer.start_as_current_span("two") as child:367 child.add_event("child's event")368 trace.get_current_span() # returns child369 trace.get_current_span() # returns parent370 trace.get_current_span() # returns previously active span371 372 This is a convenience method for creating spans attached to the373 tracer's context. Applications that need more control over the span374 lifetime should use :meth:`start_span` instead. For example::375 376 with tracer.start_as_current_span(name) as span:377 do_work()378 379 is equivalent to::380 381 span = tracer.start_span(name)382 with opentelemetry.trace.use_span(span, end_on_exit=True):383 do_work()384 385 This can also be used as a decorator::386 387 @tracer.start_as_current_span("name")388 def function():389 ...390 391 function()392 393 Args:394 name: The name of the span to be created.395 context: An optional Context containing the span's parent. Defaults to the396 global context.397 kind: The span's kind (relationship to parent). Note that is398 meaningful even if there is no parent.399 attributes: The span's attributes.400 links: Links span to other spans401 start_time: Sets the start time of a span402 record_exception: Whether to record any exceptions raised within the403 context as error event on the span.404 set_status_on_exception: Only relevant if the returned span is used405 in a with/context manager. Defines whether the span status will406 be automatically set to ERROR when an uncaught exception is407 raised in the span with block. The span status won't be set by408 this mechanism if it was previously set manually.409 end_on_exit: Whether to end the span automatically when leaving the410 context manager.411 412 Yields:413 The newly-created span.414 """415 416 417class ProxyTracer(Tracer):418 # pylint: disable=W0222,signature-differs419 def __init__(420 self,421 instrumenting_module_name: str,422 instrumenting_library_version: typing.Optional[str] = None,423 schema_url: typing.Optional[str] = None,424 attributes: typing.Optional[types.Attributes] = None,425 ):426 self._instrumenting_module_name = instrumenting_module_name427 self._instrumenting_library_version = instrumenting_library_version428 self._schema_url = schema_url429 self._attributes = attributes430 self._real_tracer: Optional[Tracer] = None431 self._noop_tracer = NoOpTracer()432 433 @property434 def _tracer(self) -> Tracer:435 if self._real_tracer:436 return self._real_tracer437 438 if _TRACER_PROVIDER:439 self._real_tracer = _TRACER_PROVIDER.get_tracer(440 self._instrumenting_module_name,441 self._instrumenting_library_version,442 self._schema_url,443 self._attributes,444 )445 return self._real_tracer446 return self._noop_tracer447 448 def start_span(self, *args, **kwargs) -> Span: # type: ignore449 return self._tracer.start_span(*args, **kwargs) # type: ignore450 451 @_agnosticcontextmanager # type: ignore452 def start_as_current_span(self, *args, **kwargs) -> Iterator[Span]:453 with self._tracer.start_as_current_span(*args, **kwargs) as span: # type: ignore454 yield span455 456 457class NoOpTracer(Tracer):458 """The default Tracer, used when no Tracer implementation is available.459 460 All operations are no-op.461 """462 463 def start_span(464 self,465 name: str,466 context: Optional[Context] = None,467 kind: SpanKind = SpanKind.INTERNAL,468 attributes: types.Attributes = None,469 links: _Links = None,470 start_time: Optional[int] = None,471 record_exception: bool = True,472 set_status_on_exception: bool = True,473 ) -> "Span":474 current_span = get_current_span(context)475 if isinstance(current_span, NonRecordingSpan):476 return current_span477 parent_span_context = current_span.get_span_context()478 if parent_span_context is not None and not isinstance(479 parent_span_context, SpanContext480 ):481 logger.warning(482 "Invalid span context for %s: %s",483 current_span,484 parent_span_context,485 )486 return INVALID_SPAN487 488 return NonRecordingSpan(context=parent_span_context)489 490 @_agnosticcontextmanager491 def start_as_current_span(492 self,493 name: str,494 context: Optional[Context] = None,495 kind: SpanKind = SpanKind.INTERNAL,496 attributes: types.Attributes = None,497 links: _Links = None,498 start_time: Optional[int] = None,499 record_exception: bool = True,500 set_status_on_exception: bool = True,501 end_on_exit: bool = True,502 ) -> Iterator["Span"]:503 span = self.start_span(504 name=name,505 context=context,506 kind=kind,507 attributes=attributes,508 links=links,509 start_time=start_time,510 record_exception=record_exception,511 set_status_on_exception=set_status_on_exception,512 )513 with use_span(514 span,515 end_on_exit=end_on_exit,516 record_exception=record_exception,517 set_status_on_exception=set_status_on_exception,518 ) as span:519 yield span520 521 522@deprecated("You should use NoOpTracer. Deprecated since version 1.9.0.")523class _DefaultTracer(NoOpTracer):524 """The default Tracer, used when no Tracer implementation is available.525 526 All operations are no-op.527 """528 529 530_TRACER_PROVIDER_SET_ONCE = Once()531_TRACER_PROVIDER: Optional[TracerProvider] = None532_PROXY_TRACER_PROVIDER = ProxyTracerProvider()533 534 535def get_tracer(536 instrumenting_module_name: str,537 instrumenting_library_version: typing.Optional[str] = None,538 tracer_provider: Optional[TracerProvider] = None,539 schema_url: typing.Optional[str] = None,540 attributes: typing.Optional[types.Attributes] = None,541) -> "Tracer":542 """Returns a `Tracer` for use by the given instrumentation library.543 544 This function is a convenience wrapper for545 opentelemetry.trace.TracerProvider.get_tracer.546 547 If tracer_provider is omitted the current configured one is used.548 """549 if tracer_provider is None:550 tracer_provider = get_tracer_provider()551 return tracer_provider.get_tracer(552 instrumenting_module_name,553 instrumenting_library_version,554 schema_url,555 attributes,556 )557 558 559def _set_tracer_provider(tracer_provider: TracerProvider, log: bool) -> None:560 def set_tp() -> None:561 global _TRACER_PROVIDER # pylint: disable=global-statement562 _TRACER_PROVIDER = tracer_provider563 564 did_set = _TRACER_PROVIDER_SET_ONCE.do_once(set_tp)565 566 if log and not did_set:567 logger.warning("Overriding of current TracerProvider is not allowed")568 569 570def set_tracer_provider(tracer_provider: TracerProvider) -> None:571 """Sets the current global :class:`~.TracerProvider` object.572 573 This can only be done once, a warning will be logged if any further attempt574 is made.575 """576 _set_tracer_provider(tracer_provider, log=True)577 578 579def get_tracer_provider() -> TracerProvider:580 """Gets the current global :class:`~.TracerProvider` object."""581 if _TRACER_PROVIDER is None:582 # if a global tracer provider has not been set either via code or env583 # vars, return a proxy tracer provider584 if OTEL_PYTHON_TRACER_PROVIDER not in os.environ:585 return _PROXY_TRACER_PROVIDER586 587 tracer_provider: TracerProvider = _load_provider(588 OTEL_PYTHON_TRACER_PROVIDER, "tracer_provider"589 )590 _set_tracer_provider(tracer_provider, log=False)591 # _TRACER_PROVIDER will have been set by one thread592 return cast("TracerProvider", _TRACER_PROVIDER)593 594 595@_agnosticcontextmanager596def use_span(597 span: Span,598 end_on_exit: bool = False,599 record_exception: bool = True,600 set_status_on_exception: bool = True,601) -> Iterator[Span]:602 """Takes a non-active span and activates it in the current context.603 604 Args:605 span: The span that should be activated in the current context.606 end_on_exit: Whether to end the span automatically when leaving the607 context manager scope.608 record_exception: Whether to record any exceptions raised within the609 context as error event on the span.610 set_status_on_exception: Only relevant if the returned span is used611 in a with/context manager. Defines whether the span status will612 be automatically set to ERROR when an uncaught exception is613 raised in the span with block. The span status won't be set by614 this mechanism if it was previously set manually.615 """616 try:617 token = context_api.attach(context_api.set_value(_SPAN_KEY, span))618 try:619 yield span620 finally:621 context_api.detach(token)622 623 # Record only exceptions that inherit Exception class but not BaseException, because624 # classes that directly inherit BaseException are not technically errors, e.g. GeneratorExit.625 # See https://github.com/open-telemetry/opentelemetry-python/issues/4484626 except Exception as exc: # pylint: disable=broad-exception-caught627 if isinstance(span, Span) and span.is_recording():628 # Record the exception as an event629 if record_exception:630 span.record_exception(exc)631 632 # Set status in case exception was raised633 if set_status_on_exception:634 span.set_status(635 Status(636 status_code=StatusCode.ERROR,637 description=f"{type(exc).__name__}: {exc}",638 )639 )640 641 # This causes parent spans to set their status to ERROR and to record642 # an exception as an event if a child span raises an exception even if643 # such child span was started with both record_exception and644 # set_status_on_exception attributes set to False.645 raise646 647 finally:648 if end_on_exit:649 span.end()650 651 652__all__ = [653 "DEFAULT_TRACE_OPTIONS",654 "DEFAULT_TRACE_STATE",655 "INVALID_SPAN",656 "INVALID_SPAN_CONTEXT",657 "INVALID_SPAN_ID",658 "INVALID_TRACE_ID",659 "NonRecordingSpan",660 "Link",661 "Span",662 "SpanContext",663 "SpanKind",664 "TraceFlags",665 "TraceState",666 "TracerProvider",667 "Tracer",668 "format_span_id",669 "format_trace_id",670 "get_current_span",671 "get_tracer",672 "get_tracer_provider",673 "set_tracer_provider",674 "set_span_in_context",675 "use_span",676 "Status",677 "StatusCode",678]679 