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 = 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 = None, 

231 ) -> "Tracer": 

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

233 return NoOpTracer() 

234 

235 

236@deprecated( 

237 "You should use NoOpTracerProvider. Deprecated since version 1.9.0." 

238) 

239class _DefaultTracerProvider(NoOpTracerProvider): 

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

241 

242 All operations are no-op. 

243 """ 

244 

245 

246class ProxyTracerProvider(TracerProvider): 

247 def get_tracer( 

248 self, 

249 instrumenting_module_name: str, 

250 instrumenting_library_version: str | None = None, 

251 schema_url: str | None = None, 

252 attributes: types.Attributes | None = None, 

253 ) -> "Tracer": 

254 if _TRACER_PROVIDER: 

255 return _TRACER_PROVIDER.get_tracer( 

256 instrumenting_module_name, 

257 instrumenting_library_version, 

258 schema_url, 

259 attributes, 

260 ) 

261 return ProxyTracer( 

262 instrumenting_module_name, 

263 instrumenting_library_version, 

264 schema_url, 

265 attributes, 

266 ) 

267 

268 

269class Tracer(ABC): 

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

271 

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

273 and controlling spans' lifecycles. 

274 """ 

275 

276 @abstractmethod 

277 def start_span( 

278 self, 

279 name: str, 

280 context: Context | None = None, 

281 kind: SpanKind = SpanKind.INTERNAL, 

282 attributes: types.Attributes = None, 

283 links: _Links = None, 

284 start_time: int | None = None, 

285 record_exception: bool = True, 

286 set_status_on_exception: bool = True, 

287 ) -> "Span": 

288 """Starts a span. 

289 

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

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

292 method, see :meth:`start_as_current_span`. 

293 

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

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

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

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

298 

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

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

301 

302 Example:: 

303 

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

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

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

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

308 

309 Args: 

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

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

312 global context. 

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

314 meaningful even if there is no parent. 

315 attributes: The span's attributes. 

316 links: Links span to other spans 

317 start_time: Sets the start time of a span 

318 record_exception: Whether to record any exceptions raised within the 

319 context as error event on the span. 

320 set_status_on_exception: Only relevant if the returned span is used 

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

322 be automatically set to ERROR when an uncaught exception is 

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

324 this mechanism if it was previously set manually. 

325 

326 Returns: 

327 The newly-created span. 

328 """ 

329 

330 @_agnosticcontextmanager 

331 @abstractmethod 

332 def start_as_current_span( 

333 self, 

334 name: str, 

335 context: Context | None = None, 

336 kind: SpanKind = SpanKind.INTERNAL, 

337 attributes: types.Attributes = None, 

338 links: _Links = None, 

339 start_time: int | None = None, 

340 record_exception: bool = True, 

341 set_status_on_exception: bool = True, 

342 end_on_exit: bool = True, 

343 ) -> Iterator["Span"]: 

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

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

346 

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

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

349 returning to the previous context. 

350 

351 Example:: 

352 

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

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

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

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

357 trace.get_current_span() # returns child 

358 trace.get_current_span() # returns parent 

359 trace.get_current_span() # returns previously active span 

360 

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

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

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

364 

365 with tracer.start_as_current_span(name) as span: 

366 do_work() 

367 

368 is equivalent to:: 

369 

370 span = tracer.start_span(name) 

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

372 do_work() 

373 

374 This can also be used as a decorator:: 

375 

376 @tracer.start_as_current_span("name") 

377 def function(): 

378 ... 

379 

380 function() 

381 

382 Args: 

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

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

385 global context. 

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

387 meaningful even if there is no parent. 

388 attributes: The span's attributes. 

389 links: Links span to other spans 

390 start_time: Sets the start time of a span 

391 record_exception: Whether to record any exceptions raised within the 

392 context as error event on the span. 

393 set_status_on_exception: Only relevant if the returned span is used 

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

395 be automatically set to ERROR when an uncaught exception is 

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

397 this mechanism if it was previously set manually. 

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

399 context manager. 

400 

401 Yields: 

402 The newly-created span. 

403 """ 

404 

405 

406class ProxyTracer(Tracer): 

407 # pylint: disable=W0222,signature-differs 

408 def __init__( 

409 self, 

410 instrumenting_module_name: str, 

411 instrumenting_library_version: str | None = None, 

412 schema_url: str | None = None, 

413 attributes: types.Attributes | None = None, 

414 ): 

415 self._instrumenting_module_name = instrumenting_module_name 

416 self._instrumenting_library_version = instrumenting_library_version 

417 self._schema_url = schema_url 

418 self._attributes = attributes 

419 self._real_tracer: Tracer | None = None 

420 self._noop_tracer = NoOpTracer() 

421 

422 @property 

423 def _tracer(self) -> Tracer: 

424 if self._real_tracer: 

425 return self._real_tracer 

426 

427 if _TRACER_PROVIDER: 

428 self._real_tracer = _TRACER_PROVIDER.get_tracer( 

429 self._instrumenting_module_name, 

430 self._instrumenting_library_version, 

431 self._schema_url, 

432 self._attributes, 

433 ) 

434 return self._real_tracer 

435 return self._noop_tracer 

436 

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

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

439 

440 @_agnosticcontextmanager # type: ignore 

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

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

443 yield span 

444 

445 

446class NoOpTracer(Tracer): 

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

448 

449 All operations are no-op. 

450 """ 

451 

452 def start_span( 

453 self, 

454 name: str, 

455 context: Context | None = None, 

456 kind: SpanKind = SpanKind.INTERNAL, 

457 attributes: types.Attributes = None, 

458 links: _Links = None, 

459 start_time: int | None = None, 

460 record_exception: bool = True, 

461 set_status_on_exception: bool = True, 

462 ) -> "Span": 

463 current_span = get_current_span(context) 

464 if isinstance(current_span, NonRecordingSpan): 

465 return current_span 

466 parent_span_context = current_span.get_span_context() 

467 if parent_span_context is not None and not isinstance( 

468 parent_span_context, SpanContext 

469 ): 

470 logger.warning( 

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

472 current_span, 

473 parent_span_context, 

474 ) 

475 return INVALID_SPAN 

476 

477 return NonRecordingSpan(context=parent_span_context) 

478 

479 @_agnosticcontextmanager 

480 def start_as_current_span( 

481 self, 

482 name: str, 

483 context: Context | None = None, 

484 kind: SpanKind = SpanKind.INTERNAL, 

485 attributes: types.Attributes = None, 

486 links: _Links = None, 

487 start_time: int | None = None, 

488 record_exception: bool = True, 

489 set_status_on_exception: bool = True, 

490 end_on_exit: bool = True, 

491 ) -> Iterator["Span"]: 

492 span = self.start_span( 

493 name=name, 

494 context=context, 

495 kind=kind, 

496 attributes=attributes, 

497 links=links, 

498 start_time=start_time, 

499 record_exception=record_exception, 

500 set_status_on_exception=set_status_on_exception, 

501 ) 

502 with use_span( 

503 span, 

504 end_on_exit=end_on_exit, 

505 record_exception=record_exception, 

506 set_status_on_exception=set_status_on_exception, 

507 ) as span: 

508 yield span 

509 

510 

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

512class _DefaultTracer(NoOpTracer): 

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

514 

515 All operations are no-op. 

516 """ 

517 

518 

519_TRACER_PROVIDER_SET_ONCE = Once() 

520_TRACER_PROVIDER: TracerProvider | None = None 

521_PROXY_TRACER_PROVIDER = ProxyTracerProvider() 

522 

523 

524def get_tracer( 

525 instrumenting_module_name: str, 

526 instrumenting_library_version: str | None = None, 

527 tracer_provider: TracerProvider | None = None, 

528 schema_url: str | None = None, 

529 attributes: types.Attributes | None = None, 

530) -> "Tracer": 

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

532 

533 This function is a convenience wrapper for 

534 opentelemetry.trace.TracerProvider.get_tracer. 

535 

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

537 """ 

538 if tracer_provider is None: 

539 tracer_provider = get_tracer_provider() 

540 return tracer_provider.get_tracer( 

541 instrumenting_module_name, 

542 instrumenting_library_version, 

543 schema_url, 

544 attributes, 

545 ) 

546 

547 

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

549 def set_tp() -> None: 

550 global _TRACER_PROVIDER # pylint: disable=global-statement 

551 _TRACER_PROVIDER = tracer_provider 

552 

553 did_set = _TRACER_PROVIDER_SET_ONCE.do_once(set_tp) 

554 

555 if log and not did_set: 

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

557 

558 

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

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

561 

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

563 is made. 

564 """ 

565 _set_tracer_provider(tracer_provider, log=True) 

566 

567 

568def get_tracer_provider() -> TracerProvider: 

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

570 if _TRACER_PROVIDER is None: 

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

572 # vars, return a proxy tracer provider 

573 if OTEL_PYTHON_TRACER_PROVIDER not in os.environ: 

574 return _PROXY_TRACER_PROVIDER 

575 

576 tracer_provider: TracerProvider = _load_provider( 

577 OTEL_PYTHON_TRACER_PROVIDER, "tracer_provider" 

578 ) 

579 _set_tracer_provider(tracer_provider, log=False) 

580 # _TRACER_PROVIDER will have been set by one thread 

581 return cast("TracerProvider", _TRACER_PROVIDER) 

582 

583 

584@_agnosticcontextmanager 

585def use_span( 

586 span: Span, 

587 end_on_exit: bool = False, 

588 record_exception: bool = True, 

589 set_status_on_exception: bool = True, 

590) -> Iterator[Span]: 

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

592 

593 Args: 

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

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

596 context manager scope. 

597 record_exception: Whether to record any exceptions raised within the 

598 context as error event on the span. 

599 set_status_on_exception: Only relevant if the returned span is used 

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

601 be automatically set to ERROR when an uncaught exception is 

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

603 this mechanism if it was previously set manually. 

604 """ 

605 try: 

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

607 try: 

608 yield span 

609 finally: 

610 context_api.detach(token) 

611 

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

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

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

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

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

617 # Record the exception as an event 

618 if record_exception: 

619 span.record_exception(exc) 

620 

621 # Set status in case exception was raised 

622 if set_status_on_exception: 

623 span.set_status( 

624 Status( 

625 status_code=StatusCode.ERROR, 

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

627 ) 

628 ) 

629 

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

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

632 # such child span was started with both record_exception and 

633 # set_status_on_exception attributes set to False. 

634 raise 

635 

636 finally: 

637 if end_on_exit: 

638 span.end() 

639 

640 

641__all__ = [ 

642 "DEFAULT_TRACE_OPTIONS", 

643 "DEFAULT_TRACE_STATE", 

644 "INVALID_SPAN", 

645 "INVALID_SPAN_CONTEXT", 

646 "INVALID_SPAN_ID", 

647 "INVALID_TRACE_ID", 

648 "NonRecordingSpan", 

649 "Link", 

650 "Span", 

651 "SpanContext", 

652 "SpanKind", 

653 "TraceFlags", 

654 "TraceState", 

655 "TracerProvider", 

656 "Tracer", 

657 "format_span_id", 

658 "format_trace_id", 

659 "get_current_span", 

660 "get_tracer", 

661 "get_tracer_provider", 

662 "set_tracer_provider", 

663 "set_span_in_context", 

664 "use_span", 

665 "Status", 

666 "StatusCode", 

667]