-
Notifications
You must be signed in to change notification settings - Fork 544
feat: Introduce Transaction and Hub.start_transaction #747
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
c877854
to
ecafc4a
Compare
I don't know what's wrong with the docs generation job https://travis-ci.com/github/getsentry/sentry-python/jobs/354722815 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Figured I would start reviewing these PRs to get more familiar with this SDK, but I'll leave Markus to give the final ✅
# This is for backwards compatibility with releases before | ||
# start_transaction existed, to allow for a smoother transition. | ||
if isinstance(span, Transaction) or "transaction" in kwargs: | ||
deprecation_msg = ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need the redundant check here for isinstance(span, Transaction) or "transaction" in kwargs
, we should just extract deprecation_msg
to be a top level constant
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The intention here was to have a self-contained block with the TODO note, so it is clearer what it applies to.
# appending an existing span to a new transaction created to | ||
# contain the span. | ||
|
||
# TODO: consider removing this in a future release. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As an aside for another PR: we should have a standard deprecated comment somewhere like we do in JS, so we can easily see what to remove in each major version.
sentry_sdk/hub.py
Outdated
# | ||
# Would be clearer if Hub was not involved: | ||
# transaction.name = "new_name" | ||
# with transaction: # __enter__ => start, __exit__ => finish |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't we still have to attach the transaction to the current hub, even with with transaction:
? That way, I'd much prefer to be explicit using hub.start_transaction
then hide what it's doing here.
sentry_sdk/hub.py
Outdated
# XXX: should we always set transaction.hub = self? | ||
# In a multi-hub program, what does it mean to write | ||
# hub1.start_transaction(Transaction(hub=hub2)) | ||
# ? Should the transaction be on hub1 or hub2? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, this is tricky. I think if we keep our current implementation, the transaction should be on hub1
and we should not allow setting hub through kwargs
. If we decide to change the API, we can revisit this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would shift each of those discussions into github issues if you want to continue them
sentry_sdk/hub.py
Outdated
name = kwargs.pop("transaction") | ||
return self.start_transaction(name=name, **kwargs) | ||
|
||
# XXX: this is not very useful because |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is there to be consistent with start_transaction I suppose.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, start_transaction
doesn't exist outside this PR. In one of its usages, the previous start_span
would also just return the input (sampling decision work moved to transactions only).
Keeping it for backwards-compat.
sentry_sdk/hub.py
Outdated
if span is not None: | ||
return span.start_child(**kwargs) | ||
|
||
# XXX: returning a detached span here means whatever span tree built |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes the API simpler to use as the caller does not have to check whether they are in a transaction. I don't really see why you call this not useful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can hide an entire instrumented span tree if one is not careful to guarantee a transaction is in the scope when calling hub.start_span(op="...")
.
You told me today about a use case for intentionally omitting an entire instrumented span tree, I still keeping wrap my head around that. In other parts of the Unified API we explicitly prefer to avoid doing things when not necessary -- e.g. no-op transport/client when SDK has no DSN, callbacks are not called, etc.
I'll move these discussions into issues.
sentry_sdk/hub.py
Outdated
# hub1.start_transaction(Transaction(hub=hub2)) | ||
# ? Should the transaction be on hub1 or hub2? | ||
|
||
# XXX: it is strange that both start_span and start_transaction take |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
btw i think those discussions should not be done as comments in the sourcecode
Circular imports. Removed the diff --git a/sentry_sdk/_types.py b/sentry_sdk/_types.py
index 7c1aaa6..8e5427c 100644
--- a/sentry_sdk/_types.py
+++ b/sentry_sdk/_types.py
@@ -12,7 +12,7 @@ if MYPY:
from typing import Optional
from typing import Tuple
from typing import Type
- from typing import Union
+
from typing_extensions import Literal
ExcInfo = Tuple[
@@ -37,7 +37,3 @@ if MYPY:
]
SessionStatus = Literal["ok", "exited", "crashed", "abnormal"]
EndpointType = Literal["store", "envelope"]
-
- from sentry_sdk.tracing import Span, Transaction # noqa
-
- SpanLike = Union[Span, Transaction]
diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py
index 0c082c4..f928063 100644
--- a/sentry_sdk/scope.py
+++ b/sentry_sdk/scope.py
@@ -26,7 +26,7 @@ if MYPY:
Type,
)
- from sentry_sdk.tracing import SpanLike
+ from sentry_sdk.tracing import Span
from sentry_sdk.sessions import Session
F = TypeVar("F", bound=Callable[..., Any])
@@ -114,7 +114,7 @@ class Scope(object):
self.clear_breadcrumbs()
self._should_capture = True
- self._span = None # type: Optional[SpanLike]
+ self._span = None # type: Optional[Span]
self._session = None # type: Optional[Session]
self._force_auto_session_tracking = None # type: Optional[bool]
@@ -181,13 +181,13 @@ class Scope(object):
@property
def span(self):
- # type: () -> Optional[SpanLike]
+ # type: () -> Optional[Span]
"""Get/set current tracing span or transaction."""
return self._span
@span.setter
def span(self, span):
- # type: (Optional[SpanLike]) -> None
+ # type: (Optional[Span]) -> None
self._span = span
# XXX: this differs from the implementation in JS, there Scope.setSpan
# does not set Scope._transactionName.
diff --git a/sentry_sdk/tracing.py b/sentry_sdk/tracing.py
index 05abcf6..d390c6b 100644
--- a/sentry_sdk/tracing.py
+++ b/sentry_sdk/tracing.py
@@ -25,9 +25,6 @@ if MYPY:
from typing import Dict
from typing import List
from typing import Tuple
- from typing import Type
-
- from sentry_sdk._types import SpanLike
_traceparent_header_format_re = re.compile(
"^[ \t]*" # whitespace
@@ -225,7 +222,7 @@ class Span(object):
@classmethod
def continue_from_environ(
- cls, # type: Type[SpanLike]
+ cls,
environ, # type: typing.Mapping[str, str]
**kwargs # type: Any
):
@@ -239,7 +236,7 @@ class Span(object):
@classmethod
def continue_from_headers(
- cls, # type: Type[SpanLike]
+ cls,
headers, # type: typing.Mapping[str, str]
**kwargs # type: Any
):
@@ -261,7 +258,7 @@ class Span(object):
@classmethod
def from_traceparent(
- cls, # type: Type[SpanLike]
+ cls,
traceparent, # type: Optional[str]
**kwargs # type: Any
): |
This aligns the tracing implementation with the current JS tracing implementation, up to a certain extent. Hub.start_transaction or start_transaction are meant to be used when starting transactions, replacing most uses of Hub.start_span / start_span. Spans are typically created from their parent transactions via transaction.start_child, or start_span relying on the transaction being in the current scope. It is okay to start a transaction without a name and set it later. Sometimes the proper name is not known until after the transaction has started. We could fail the transaction if it has no name when calling the finish method. Instead, set a default name that will prompt users to give a name to their transactions. This is the same behavior as implemented in JS. Span.continue_from_headers, Span.continue_from_environ, Span.from_traceparent and the equivalent methods on Transaction always return a Transaction and take kwargs to set attributes on the new Transaction. Rename Span.new_span to Span.start_child (and Transaction.start_child)a, aligning with JS / tracing API spec. The old name is kept for backwards compatibility. Co-authored-by: Markus Unterwaditzer <[email protected]>
7c94965
to
9389f49
Compare
Removed some commentary, will move the discussion into new issues where appropriate. |
This aligns the tracing implementation with the current JS tracing implementation, up to a certain extent.
Hub.start_transaction or start_transaction are meant to be used when starting transactions, replacing most uses of Hub.start_span / start_span.
Spans are typically created from their parent transactions via transaction.start_child, or start_span relying on the transaction being in the current scope.
It is okay to start a transaction without a name and set it later.
Sometimes the proper name is not known until after the transaction has started.
We could fail the transaction if it has no name when calling the finish method. Instead, set a default name that will prompt users to give a name to their transactions. This is the same behavior as implemented in JS.