__init__.py 23 KB

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