|
8 | 8 | from sentry_sdk._compat import with_metaclass
|
9 | 9 | from sentry_sdk.scope import Scope
|
10 | 10 | from sentry_sdk.client import Client
|
11 |
| -from sentry_sdk.tracing import Span |
| 11 | +from sentry_sdk.tracing import Span, Transaction |
12 | 12 | from sentry_sdk.sessions import Session
|
13 | 13 | from sentry_sdk.utils import (
|
14 | 14 | exc_info_from_error,
|
@@ -441,38 +441,138 @@ def start_span(
|
441 | 441 | ):
|
442 | 442 | # type: (...) -> Span
|
443 | 443 | """
|
444 |
| - Create a new span whose parent span is the currently active |
445 |
| - span, if any. The return value is the span object that can |
446 |
| - be used as a context manager to start and stop timing. |
447 |
| -
|
448 |
| - Note that you will not see any span that is not contained |
449 |
| - within a transaction. Create a transaction with |
450 |
| - ``start_span(transaction="my transaction")`` if an |
451 |
| - integration doesn't already do this for you. |
| 444 | + Create and start timing a new span whose parent is the currently active |
| 445 | + span or transaction, if any. The return value is a span instance, |
| 446 | + typically used as a context manager to start and stop timing in a `with` |
| 447 | + block. |
| 448 | +
|
| 449 | + Only spans contained in a transaction are sent to Sentry. Most |
| 450 | + integrations start a transaction at the appropriate time, for example |
| 451 | + for every incoming HTTP request. Use `start_transaction` to start a new |
| 452 | + transaction when one is not already in progress. |
452 | 453 | """
|
453 |
| - |
454 |
| - client, scope = self._stack[-1] |
| 454 | + # XXX: if the only way to use start_span correctly is when there |
| 455 | + # is already an existing transaction/span in the scope, then |
| 456 | + # this is a hard to use API. We don't document nor support |
| 457 | + # appending an existing span to a new transaction created to |
| 458 | + # contain the span. |
| 459 | + |
| 460 | + # TODO: consider removing this in a future release. |
| 461 | + # This is for backwards compatibility with releases before |
| 462 | + # start_transaction existed, to allow for a smoother transition. |
| 463 | + if isinstance(span, Transaction) or "transaction" in kwargs: |
| 464 | + deprecation_msg = ( |
| 465 | + "Deprecated: use start_transaction to start transactions and " |
| 466 | + "Transaction.start_child to start spans." |
| 467 | + ) |
| 468 | + if isinstance(span, Transaction): |
| 469 | + logger.warning(deprecation_msg) |
| 470 | + return self.start_transaction(span) |
| 471 | + if "transaction" in kwargs: |
| 472 | + logger.warning(deprecation_msg) |
| 473 | + name = kwargs.pop("transaction") |
| 474 | + return self.start_transaction(name=name, **kwargs) |
| 475 | + |
| 476 | + # XXX: this is not very useful because |
| 477 | + # |
| 478 | + # with hub.start_span(Span(...)): |
| 479 | + # pass |
| 480 | + # |
| 481 | + # is equivalent to the simpler |
| 482 | + # |
| 483 | + # with Span(...): |
| 484 | + # pass |
| 485 | + if span is not None: |
| 486 | + return span |
455 | 487 |
|
456 | 488 | kwargs.setdefault("hub", self)
|
457 | 489 |
|
458 |
| - if span is None: |
459 |
| - span = scope.span |
460 |
| - if span is not None: |
461 |
| - span = span.new_span(**kwargs) |
462 |
| - else: |
463 |
| - span = Span(**kwargs) |
| 490 | + span = self.scope.span |
| 491 | + if span is not None: |
| 492 | + return span.start_child(**kwargs) |
| 493 | + |
| 494 | + # XXX: returning a detached span here means whatever span tree built |
| 495 | + # from it will be eventually discarded. |
| 496 | + # |
| 497 | + # This is also not very useful because |
| 498 | + # |
| 499 | + # with hub.start_span(op="..."): |
| 500 | + # pass |
| 501 | + # |
| 502 | + # is equivalent, when there is no span in the scope, to the simpler |
| 503 | + # |
| 504 | + # with Span(op="..."): |
| 505 | + # pass |
| 506 | + return Span(**kwargs) |
| 507 | + |
| 508 | + def start_transaction( |
| 509 | + self, |
| 510 | + transaction=None, # type: Optional[Transaction] |
| 511 | + **kwargs # type: Any |
| 512 | + ): |
| 513 | + # type: (...) -> Transaction |
| 514 | + """ |
| 515 | + Start and return a transaction. |
| 516 | +
|
| 517 | + Start an existing transaction if given, otherwise create and start a new |
| 518 | + transaction with kwargs. |
| 519 | +
|
| 520 | + This is the entry point to manual tracing instrumentation. |
| 521 | +
|
| 522 | + A tree structure can be built by adding child spans to the transaction, |
| 523 | + and child spans to other spans. To start a new child span within the |
| 524 | + transaction or any span, call the respective `.start_child()` method. |
| 525 | +
|
| 526 | + Every child span must be finished before the transaction is finished, |
| 527 | + otherwise the unfinished spans are discarded. |
| 528 | +
|
| 529 | + When used as context managers, spans and transactions are automatically |
| 530 | + finished at the end of the `with` block. If not using context managers, |
| 531 | + call the `.finish()` method. |
| 532 | +
|
| 533 | + When the transaction is finished, it will be sent to Sentry with all its |
| 534 | + finished child spans. |
| 535 | + """ |
| 536 | + # XXX: should we always set transaction.hub = self? |
| 537 | + # In a multi-hub program, what does it mean to write |
| 538 | + # hub1.start_transaction(Transaction(hub=hub2)) |
| 539 | + # ? Should the transaction be on hub1 or hub2? |
| 540 | + |
| 541 | + # XXX: it is strange that both start_span and start_transaction take |
| 542 | + # kwargs, but those are ignored if span/transaction are not None. |
| 543 | + # Code such as: |
| 544 | + # hub.start_transaction(Transaction(name="foo"), name="bar") |
| 545 | + # |
| 546 | + # is clearly weird, but not so weird if we intentionally want to rename |
| 547 | + # a transaction we got from somewhere else: |
| 548 | + # hub.start_transaction(transaction, name="new_name") |
| 549 | + # |
| 550 | + # Would be clearer if Hub was not involved: |
| 551 | + # transaction.name = "new_name" |
| 552 | + # with transaction: # __enter__ => start, __exit__ => finish |
| 553 | + # with transaction.start_child(...): |
| 554 | + # pass |
| 555 | + # # alternatively, rely on transaction being in the current scope |
| 556 | + # with Span(...): |
| 557 | + # pass |
| 558 | + |
| 559 | + if transaction is None: |
| 560 | + kwargs.setdefault("hub", self) |
| 561 | + transaction = Transaction(**kwargs) |
| 562 | + |
| 563 | + client, scope = self._stack[-1] |
464 | 564 |
|
465 |
| - if span.sampled is None and span.transaction is not None: |
| 565 | + if transaction.sampled is None: |
466 | 566 | sample_rate = client and client.options["traces_sample_rate"] or 0
|
467 |
| - span.sampled = random.random() < sample_rate |
| 567 | + transaction.sampled = random.random() < sample_rate |
468 | 568 |
|
469 |
| - if span.sampled: |
| 569 | + if transaction.sampled: |
470 | 570 | max_spans = (
|
471 | 571 | client and client.options["_experiments"].get("max_spans") or 1000
|
472 | 572 | )
|
473 |
| - span.init_finished_spans(maxlen=max_spans) |
| 573 | + transaction.init_span_recorder(maxlen=max_spans) |
474 | 574 |
|
475 |
| - return span |
| 575 | + return transaction |
476 | 576 |
|
477 | 577 | @overload # noqa
|
478 | 578 | def push_scope(
|
|
0 commit comments