Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/opentelemetry/trace/__init__.py: 53%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

147 statements  

1# Copyright The OpenTelemetry Authors 

2# SPDX-License-Identifier: Apache-2.0 

3 

4""" 

5The OpenTelemetry tracing API describes the classes used to generate 

6distributed traces. 

7 

8The :class:`.Tracer` class controls access to the execution context, and 

9manages span creation. Each operation in a trace is represented by a 

10:class:`.Span`, which records the start, end time, and metadata associated with 

11the operation. 

12 

13This module provides abstract (i.e. unimplemented) classes required for 

14tracing, and a concrete no-op :class:`.NonRecordingSpan` that allows applications 

15to use the API package alone without a supporting implementation. 

16 

17To get a tracer, you need to provide the package name from which you are 

18calling the tracer APIs to OpenTelemetry by calling `TracerProvider.get_tracer` 

19with the calling module name and the version of your package. 

20 

21The tracer supports creating spans that are "attached" or "detached" from the 

22context. New spans are "attached" to the context in that they are 

23created as children of the currently active span, and the newly-created span 

24can optionally become the new active span:: 

25 

26 from opentelemetry import trace 

27 

28 tracer = trace.get_tracer(__name__) 

29 

30 # Create a new root span, set it as the current span in context 

31 with tracer.start_as_current_span("parent"): 

32 # Attach a new child and update the current span 

33 with tracer.start_as_current_span("child"): 

34 do_work(): 

35 # Close child span, set parent as current 

36 # Close parent span, set default span as current 

37 

38When creating a span that's "detached" from the context the active span doesn't 

39change, and the caller is responsible for managing the span's lifetime:: 

40 

41 # Explicit parent span assignment is done via the Context 

42 from opentelemetry.trace import set_span_in_context 

43 

44 context = set_span_in_context(parent) 

45 child = tracer.start_span("child", context=context) 

46 

47 try: 

48 do_work(span=child) 

49 finally: 

50 child.end() 

51 

52Applications should generally use a single global TracerProvider, and use 

53either implicit or explicit context propagation consistently throughout. 

54 

55.. versionadded:: 0.1.0 

56.. versionchanged:: 0.3.0 

57 `TracerProvider` was introduced and the global ``tracer`` getter was 

58 replaced by ``tracer_provider``. 

59.. versionchanged:: 0.5.0 

60 ``tracer_provider`` was replaced by `get_tracer_provider`, 

61 ``set_preferred_tracer_provider_implementation`` was replaced by 

62 `set_tracer_provider`. 

63""" 

64 

65import os 

66from abc import ABC, abstractmethod 

67from collections.abc import Iterator, Sequence 

68from enum import Enum 

69from logging import getLogger 

70from typing import cast 

71 

72from typing_extensions import deprecated 

73 

74from opentelemetry import context as context_api 

75from opentelemetry.attributes import BoundedAttributes 

76from opentelemetry.context.context import Context 

77from opentelemetry.environment_variables import OTEL_PYTHON_TRACER_PROVIDER 

78from opentelemetry.trace.propagation import ( 

79 _SPAN_KEY, 

80 get_current_span, 

81 set_span_in_context, 

82) 

83from opentelemetry.trace.span import ( 

84 DEFAULT_TRACE_OPTIONS, 

85 DEFAULT_TRACE_STATE, 

86 INVALID_SPAN, 

87 INVALID_SPAN_CONTEXT, 

88 INVALID_SPAN_ID, 

89 INVALID_TRACE_ID, 

90 NonRecordingSpan, 

91 Span, 

92 SpanContext, 

93 TraceFlags, 

94 TraceState, 

95 format_span_id, 

96 format_trace_id, 

97) 

98from opentelemetry.trace.status import Status, StatusCode 

99from opentelemetry.util import types 

100from opentelemetry.util._decorator import _agnosticcontextmanager 

101from opentelemetry.util._once import Once 

102from opentelemetry.util._providers import _load_provider 

103 

104logger = getLogger(__name__) 

105 

106 

107class _LinkBase(ABC): 

108 def __init__(self, context: "SpanContext") -> None: 

109 self._context = context 

110 

111 @property 

112 def context(self) -> "SpanContext": 

113 return self._context 

114 

115 @property 

116 @abstractmethod 

117 def attributes(self) -> types.Attributes: 

118 pass 

119 

120 

121class Link(_LinkBase): 

122 """A link to a `Span`. The attributes of a Link are immutable. 

123 

124 Args: 

125 context: `SpanContext` of the `Span` to link to. 

126 attributes: Link's attributes. 

127 """ 

128 

129 def __init__( 

130 self, 

131 context: "SpanContext", 

132 attributes: types.Attributes = None, 

133 ) -> None: 

134 super().__init__(context) 

135 self._attributes = attributes 

136 

137 @property 

138 def attributes(self) -> types.Attributes: 

139 return self._attributes 

140 

141 @property 

142 def dropped_attributes(self) -> int: 

143 if isinstance(self._attributes, BoundedAttributes): 

144 return self._attributes.dropped 

145 return 0 

146 

147 

148_Links = Sequence[Link] | None 

149 

150 

151class SpanKind(Enum): 

152 """Specifies additional details on how this span relates to its parent span. 

153 

154 Note that this enumeration is experimental and likely to change. See 

155 https://github.com/open-telemetry/opentelemetry-specification/pull/226. 

156 """ 

157 

158 #: Default value. Indicates that the span is used internally in the 

159 # application. 

160 INTERNAL = 0 

161 

162 #: Indicates that the span describes an operation that handles a remote 

163 # request. 

164 SERVER = 1 

165 

166 #: Indicates that the span describes a request to some remote service. 

167 CLIENT = 2 

168 

169 #: Indicates that the span describes a producer sending a message to a 

170 #: broker. Unlike client and server, there is usually no direct critical 

171 #: path latency relationship between producer and consumer spans. 

172 PRODUCER = 3 

173 

174 #: Indicates that the span describes a consumer receiving a message from a 

175 #: broker. Unlike client and server, there is usually no direct critical 

176 #: path latency relationship between producer and consumer spans. 

177 CONSUMER = 4 

178 

179 

180class TracerProvider(ABC): 

181 @abstractmethod 

182 def get_tracer( 

183 self, 

184 instrumenting_module_name: str, 

185 instrumenting_library_version: str | None = None, 

186 schema_url: str | None = None, 

187 attributes: types.Attributes = None, 

188 ) -> "Tracer": 

189 """Returns a `Tracer` for use by the given instrumentation library. 

190 

191 For any two calls it is undefined whether the same or different 

192 `Tracer` instances are returned, even for different library names. 

193 

194 This function may return different `Tracer` types (e.g. a no-op tracer 

195 vs. a functional tracer). 

196 

197 Args: 

198 instrumenting_module_name: The uniquely identifiable name for instrumentation 

199 scope, such as instrumentation library, package, module or class name. 

200 ``__name__`` should be avoided as this can result in 

201 different tracer names if the tracers are in different files. 

202 It is better to use a fixed string that can be imported where 

203 needed and used consistently as the name of the tracer. 

204 

205 This should *not* be the name of the module that is 

206 instrumented but the name of the module doing the instrumentation. 

207 E.g., instead of ``"requests"``, use 

208 ``"opentelemetry.instrumentation.requests"``. 

209 

210 instrumenting_library_version: Optional. The version string of the 

211 instrumenting library. Usually this should be the same as 

212 ``importlib.metadata.version(instrumenting_library_name)``. 

213 

214 schema_url: Optional. Specifies the Schema URL of the emitted telemetry. 

215 attributes: Optional. Specifies the attributes of the emitted telemetry. 

216 """ 

217 

218 

219class NoOpTracerProvider(TracerProvider): 

220 """The default TracerProvider, used when no implementation is available. 

221 

222 All operations are no-op. 

223 """ 

224 

225 def get_tracer( 

226 self, 

227 instrumenting_module_name: str, 

228 instrumenting_library_version: str | None = None, 

229 schema_url: str | None = None, 

230 attributes: types.Attributes = None, 

231 ) -> "Tracer": 

232 # pylint:disable=no-self-use,unused-argument 

233 return NoOpTracer() 

234 

235 

236@deprecated("You should use NoOpTracerProvider. Deprecated since version 1.9.0.") 

237class _DefaultTracerProvider(NoOpTracerProvider): 

238 """The default TracerProvider, used when no implementation is available. 

239 

240 All operations are no-op. 

241 """ 

242 

243 

244class ProxyTracerProvider(TracerProvider): 

245 def get_tracer( 

246 self, 

247 instrumenting_module_name: str, 

248 instrumenting_library_version: str | None = None, 

249 schema_url: str | None = None, 

250 attributes: types.Attributes = None, 

251 ) -> "Tracer": 

252 if _TRACER_PROVIDER: 

253 return _TRACER_PROVIDER.get_tracer( 

254 instrumenting_module_name, 

255 instrumenting_library_version, 

256 schema_url, 

257 attributes, 

258 ) 

259 return ProxyTracer( 

260 instrumenting_module_name, 

261 instrumenting_library_version, 

262 schema_url, 

263 attributes, 

264 ) 

265 

266 

267class Tracer(ABC): 

268 """Handles span creation and in-process context propagation. 

269 

270 This class provides methods for manipulating the context, creating spans, 

271 and controlling spans' lifecycles. 

272 """ 

273 

274 @abstractmethod 

275 def start_span( 

276 self, 

277 name: str, 

278 context: Context | None = None, 

279 kind: SpanKind = SpanKind.INTERNAL, 

280 attributes: types.Attributes = None, 

281 links: _Links = None, 

282 start_time: int | None = None, 

283 record_exception: bool = True, 

284 set_status_on_exception: bool = True, 

285 ) -> "Span": 

286 """Starts a span. 

287 

288 Create a new span. Start the span without setting it as the current 

289 span in the context. To start the span and use the context in a single 

290 method, see :meth:`start_as_current_span`. 

291 

292 By default the current span in the context will be used as parent, but an 

293 explicit context can also be specified, by passing in a `Context` containing 

294 a current `Span`. If there is no current span in the global `Context` or in 

295 the specified context, the created span will be a root span. 

296 

297 The span can be used as a context manager. On exiting the context manager, 

298 the span's end() method will be called. 

299 

300 Example:: 

301 

302 # trace.get_current_span() will be used as the implicit parent. 

303 # If none is found, the created span will be a root instance. 

304 with tracer.start_span("one") as child: 

305 child.add_event("child's event") 

306 

307 Args: 

308 name: The name of the span to be created. 

309 context: An optional Context containing the span's parent. Defaults to the 

310 global context. 

311 kind: The span's kind (relationship to parent). Note that is 

312 meaningful even if there is no parent. 

313 attributes: The span's attributes. 

314 links: Links span to other spans 

315 start_time: Sets the start time of a span 

316 record_exception: Whether to record any exceptions raised within the 

317 context as error event on the span. 

318 set_status_on_exception: Only relevant if the returned span is used 

319 in a with/context manager. Defines whether the span status will 

320 be automatically set to ERROR when an uncaught exception is 

321 raised in the span with block. The span status won't be set by 

322 this mechanism if it was previously set manually. 

323 

324 Returns: 

325 The newly-created span. 

326 """ 

327 

328 @_agnosticcontextmanager 

329 @abstractmethod 

330 def start_as_current_span( 

331 self, 

332 name: str, 

333 context: Context | None = None, 

334 kind: SpanKind = SpanKind.INTERNAL, 

335 attributes: types.Attributes = None, 

336 links: _Links = None, 

337 start_time: int | None = None, 

338 record_exception: bool = True, 

339 set_status_on_exception: bool = True, 

340 end_on_exit: bool = True, 

341 ) -> Iterator["Span"]: 

342 """Context manager for creating a new span and set it 

343 as the current span in this tracer's context. 

344 

345 Exiting the context manager will call the span's end method, 

346 as well as return the current span to its previous value by 

347 returning to the previous context. 

348 

349 Example:: 

350 

351 with tracer.start_as_current_span("one") as parent: 

352 parent.add_event("parent's event") 

353 with tracer.start_as_current_span("two") as child: 

354 child.add_event("child's event") 

355 trace.get_current_span() # returns child 

356 trace.get_current_span() # returns parent 

357 trace.get_current_span() # returns previously active span 

358 

359 This is a convenience method for creating spans attached to the 

360 tracer's context. Applications that need more control over the span 

361 lifetime should use :meth:`start_span` instead. For example:: 

362 

363 with tracer.start_as_current_span(name) as span: 

364 do_work() 

365 

366 is equivalent to:: 

367 

368 span = tracer.start_span(name) 

369 with opentelemetry.trace.use_span(span, end_on_exit=True): 

370 do_work() 

371 

372 This can also be used as a decorator:: 

373 

374 @tracer.start_as_current_span("name") 

375 def function(): ... 

376 

377 

378 function() 

379 

380 Args: 

381 name: The name of the span to be created. 

382 context: An optional Context containing the span's parent. Defaults to the 

383 global context. 

384 kind: The span's kind (relationship to parent). Note that is 

385 meaningful even if there is no parent. 

386 attributes: The span's attributes. 

387 links: Links span to other spans 

388 start_time: Sets the start time of a span 

389 record_exception: Whether to record any exceptions raised within the 

390 context as error event on the span. 

391 set_status_on_exception: Only relevant if the returned span is used 

392 in a with/context manager. Defines whether the span status will 

393 be automatically set to ERROR when an uncaught exception is 

394 raised in the span with block. The span status won't be set by 

395 this mechanism if it was previously set manually. 

396 end_on_exit: Whether to end the span automatically when leaving the 

397 context manager. 

398 

399 Yields: 

400 The newly-created span. 

401 """ 

402 

403 

404class ProxyTracer(Tracer): 

405 # pylint: disable=W0222,signature-differs 

406 def __init__( 

407 self, 

408 instrumenting_module_name: str, 

409 instrumenting_library_version: str | None = None, 

410 schema_url: str | None = None, 

411 attributes: types.Attributes = None, 

412 ): 

413 self._instrumenting_module_name = instrumenting_module_name 

414 self._instrumenting_library_version = instrumenting_library_version 

415 self._schema_url = schema_url 

416 self._attributes = attributes 

417 self._real_tracer: Tracer | None = None 

418 self._noop_tracer = NoOpTracer() 

419 

420 @property 

421 def _tracer(self) -> Tracer: 

422 if self._real_tracer: 

423 return self._real_tracer 

424 

425 if _TRACER_PROVIDER: 

426 self._real_tracer = _TRACER_PROVIDER.get_tracer( 

427 self._instrumenting_module_name, 

428 self._instrumenting_library_version, 

429 self._schema_url, 

430 self._attributes, 

431 ) 

432 return self._real_tracer 

433 return self._noop_tracer 

434 

435 def start_span(self, *args, **kwargs) -> Span: # type: ignore 

436 return self._tracer.start_span(*args, **kwargs) # type: ignore 

437 

438 @_agnosticcontextmanager # type: ignore 

439 def start_as_current_span(self, *args, **kwargs) -> Iterator[Span]: 

440 with self._tracer.start_as_current_span(*args, **kwargs) as span: # type: ignore 

441 yield span 

442 

443 

444class NoOpTracer(Tracer): 

445 """The default Tracer, used when no Tracer implementation is available. 

446 

447 All operations are no-op. 

448 """ 

449 

450 def start_span( 

451 self, 

452 name: str, 

453 context: Context | None = None, 

454 kind: SpanKind = SpanKind.INTERNAL, 

455 attributes: types.Attributes = None, 

456 links: _Links = None, 

457 start_time: int | None = None, 

458 record_exception: bool = True, 

459 set_status_on_exception: bool = True, 

460 ) -> "Span": 

461 current_span = get_current_span(context) 

462 if isinstance(current_span, NonRecordingSpan): 

463 return current_span 

464 parent_span_context = current_span.get_span_context() 

465 if parent_span_context is not None and not isinstance(parent_span_context, SpanContext): 

466 logger.warning( 

467 "Invalid span context for %s: %s", 

468 current_span, 

469 parent_span_context, 

470 ) 

471 return INVALID_SPAN 

472 

473 return NonRecordingSpan(context=parent_span_context) 

474 

475 @_agnosticcontextmanager 

476 def start_as_current_span( 

477 self, 

478 name: str, 

479 context: Context | None = None, 

480 kind: SpanKind = SpanKind.INTERNAL, 

481 attributes: types.Attributes = None, 

482 links: _Links = None, 

483 start_time: int | None = None, 

484 record_exception: bool = True, 

485 set_status_on_exception: bool = True, 

486 end_on_exit: bool = True, 

487 ) -> Iterator["Span"]: 

488 span = self.start_span( 

489 name=name, 

490 context=context, 

491 kind=kind, 

492 attributes=attributes, 

493 links=links, 

494 start_time=start_time, 

495 record_exception=record_exception, 

496 set_status_on_exception=set_status_on_exception, 

497 ) 

498 with use_span( 

499 span, 

500 end_on_exit=end_on_exit, 

501 record_exception=record_exception, 

502 set_status_on_exception=set_status_on_exception, 

503 ) as span: 

504 yield span 

505 

506 

507@deprecated("You should use NoOpTracer. Deprecated since version 1.9.0.") 

508class _DefaultTracer(NoOpTracer): 

509 """The default Tracer, used when no Tracer implementation is available. 

510 

511 All operations are no-op. 

512 """ 

513 

514 

515_TRACER_PROVIDER_SET_ONCE = Once() 

516_TRACER_PROVIDER: TracerProvider | None = None 

517_PROXY_TRACER_PROVIDER = ProxyTracerProvider() 

518 

519 

520def get_tracer( 

521 instrumenting_module_name: str, 

522 instrumenting_library_version: str | None = None, 

523 tracer_provider: TracerProvider | None = None, 

524 schema_url: str | None = None, 

525 attributes: types.Attributes = None, 

526) -> "Tracer": 

527 """Returns a `Tracer` for use by the given instrumentation library. 

528 

529 This function is a convenience wrapper for 

530 opentelemetry.trace.TracerProvider.get_tracer. 

531 

532 If tracer_provider is omitted the current configured one is used. 

533 """ 

534 if tracer_provider is None: 

535 tracer_provider = get_tracer_provider() 

536 return tracer_provider.get_tracer( 

537 instrumenting_module_name, 

538 instrumenting_library_version, 

539 schema_url, 

540 attributes, 

541 ) 

542 

543 

544def _set_tracer_provider(tracer_provider: TracerProvider, log: bool) -> None: 

545 def set_tp() -> None: 

546 global _TRACER_PROVIDER # pylint: disable=global-statement 

547 _TRACER_PROVIDER = tracer_provider 

548 

549 did_set = _TRACER_PROVIDER_SET_ONCE.do_once(set_tp) 

550 

551 if log and not did_set: 

552 logger.warning("Overriding of current TracerProvider is not allowed") 

553 

554 

555def set_tracer_provider(tracer_provider: TracerProvider) -> None: 

556 """Sets the current global :class:`~.TracerProvider` object. 

557 

558 This can only be done once, a warning will be logged if any further attempt 

559 is made. 

560 """ 

561 _set_tracer_provider(tracer_provider, log=True) 

562 

563 

564def get_tracer_provider() -> TracerProvider: 

565 """Gets the current global :class:`~.TracerProvider` object.""" 

566 if _TRACER_PROVIDER is None: 

567 # if a global tracer provider has not been set either via code or env 

568 # vars, return a proxy tracer provider 

569 if OTEL_PYTHON_TRACER_PROVIDER not in os.environ: 

570 return _PROXY_TRACER_PROVIDER 

571 

572 tracer_provider: TracerProvider = _load_provider(OTEL_PYTHON_TRACER_PROVIDER, "tracer_provider") 

573 _set_tracer_provider(tracer_provider, log=False) 

574 # _TRACER_PROVIDER will have been set by one thread 

575 return cast("TracerProvider", _TRACER_PROVIDER) 

576 

577 

578@_agnosticcontextmanager 

579def use_span( 

580 span: Span, 

581 end_on_exit: bool = False, 

582 record_exception: bool = True, 

583 set_status_on_exception: bool = True, 

584) -> Iterator[Span]: 

585 """Takes a non-active span and activates it in the current context. 

586 

587 Args: 

588 span: The span that should be activated in the current context. 

589 end_on_exit: Whether to end the span automatically when leaving the 

590 context manager scope. 

591 record_exception: Whether to record any exceptions raised within the 

592 context as error event on the span. 

593 set_status_on_exception: Only relevant if the returned span is used 

594 in a with/context manager. Defines whether the span status will 

595 be automatically set to ERROR when an uncaught exception is 

596 raised in the span with block. The span status won't be set by 

597 this mechanism if it was previously set manually. 

598 """ 

599 try: 

600 token = context_api.attach(context_api.set_value(_SPAN_KEY, span)) 

601 try: 

602 yield span 

603 finally: 

604 context_api.detach(token) 

605 

606 # Record only exceptions that inherit Exception class but not BaseException, because 

607 # classes that directly inherit BaseException are not technically errors, e.g. GeneratorExit. 

608 # See https://github.com/open-telemetry/opentelemetry-python/issues/4484 

609 except Exception as exc: # pylint: disable=broad-exception-caught 

610 if isinstance(span, Span) and span.is_recording(): 

611 # Record the exception as an event 

612 if record_exception: 

613 span.record_exception(exc) 

614 

615 # Set status in case exception was raised 

616 if set_status_on_exception: 

617 span.set_status( 

618 Status( 

619 status_code=StatusCode.ERROR, 

620 description=f"{type(exc).__name__}: {exc}", 

621 ) 

622 ) 

623 

624 # This causes parent spans to set their status to ERROR and to record 

625 # an exception as an event if a child span raises an exception even if 

626 # such child span was started with both record_exception and 

627 # set_status_on_exception attributes set to False. 

628 raise 

629 

630 finally: 

631 if end_on_exit: 

632 span.end() 

633 

634 

635__all__ = [ 

636 "DEFAULT_TRACE_OPTIONS", 

637 "DEFAULT_TRACE_STATE", 

638 "INVALID_SPAN", 

639 "INVALID_SPAN_CONTEXT", 

640 "INVALID_SPAN_ID", 

641 "INVALID_TRACE_ID", 

642 "Link", 

643 "NonRecordingSpan", 

644 "Span", 

645 "SpanContext", 

646 "SpanKind", 

647 "Status", 

648 "StatusCode", 

649 "TraceFlags", 

650 "TraceState", 

651 "Tracer", 

652 "TracerProvider", 

653 "format_span_id", 

654 "format_trace_id", 

655 "get_current_span", 

656 "get_tracer", 

657 "get_tracer_provider", 

658 "set_span_in_context", 

659 "set_tracer_provider", 

660 "use_span", 

661]