-
Notifications
You must be signed in to change notification settings - Fork 629
feat(attachments): Add basic support for attachments #856
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
Changes from all commits
920cc7e
5a04fd3
5535514
6260639
60fae9f
1c8db36
9168367
ca428bf
d38d64f
0d88816
4e61316
bbcc0fa
7d809ba
2c14b77
01152f3
a9e35f5
d49a99b
7e747aa
3749713
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| import os | ||
| import mimetypes | ||
|
|
||
| from sentry_sdk._types import MYPY | ||
| from sentry_sdk.envelope import Item, PayloadRef | ||
|
|
||
| if MYPY: | ||
| from typing import Optional, Union, Callable | ||
|
|
||
|
|
||
| class Attachment(object): | ||
| def __init__( | ||
| self, | ||
| bytes=None, # type: Union[None, bytes, Callable[[], bytes]] | ||
| filename=None, # type: Optional[str] | ||
| path=None, # type: Optional[str] | ||
| content_type=None, # type: Optional[str] | ||
| add_to_transactions=False, # type: bool | ||
| ): | ||
| # type: (...) -> None | ||
| if bytes is None and path is None: | ||
| raise TypeError("path or raw bytes required for attachment") | ||
| if filename is None and path is not None: | ||
| filename = os.path.basename(path) | ||
| if filename is None: | ||
| raise TypeError("filename is required for attachment") | ||
| if content_type is None: | ||
| content_type = mimetypes.guess_type(filename)[0] | ||
| self.bytes = bytes | ||
| self.filename = filename | ||
| self.path = path | ||
| self.content_type = content_type | ||
| self.add_to_transactions = add_to_transactions | ||
|
|
||
| def to_envelope_item(self): | ||
| # type: () -> Item | ||
| """Returns an envelope item for this attachment.""" | ||
| payload = None # type: Union[None, PayloadRef, bytes] | ||
| if self.bytes is not None: | ||
| if callable(self.bytes): | ||
| payload = self.bytes() | ||
| else: | ||
| payload = self.bytes | ||
| else: | ||
| payload = PayloadRef(path=self.path) | ||
| return Item( | ||
| payload=payload, | ||
| type="attachment", | ||
| content_type=self.content_type, | ||
| filename=self.filename, | ||
| ) | ||
|
|
||
| def __repr__(self): | ||
| # type: () -> str | ||
| return "<Attachment %r>" % (self.filename,) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,7 +23,7 @@ | |
| from sentry_sdk.integrations import setup_integrations | ||
| from sentry_sdk.utils import ContextVar | ||
| from sentry_sdk.sessions import SessionFlusher | ||
| from sentry_sdk.envelope import Envelope, Item, PayloadRef | ||
| from sentry_sdk.envelope import Envelope | ||
|
|
||
| from sentry_sdk._types import MYPY | ||
|
|
||
|
|
@@ -146,16 +146,14 @@ def dsn(self): | |
| def _prepare_event( | ||
| self, | ||
| event, # type: Event | ||
| hint, # type: Optional[Hint] | ||
| hint, # type: Hint | ||
| scope, # type: Optional[Scope] | ||
| ): | ||
| # type: (...) -> Optional[Event] | ||
|
|
||
| if event.get("timestamp") is None: | ||
| event["timestamp"] = datetime.utcnow() | ||
|
|
||
| hint = dict(hint or ()) # type: Hint | ||
|
|
||
| if scope is not None: | ||
| event_ = scope.apply_to_event(event, hint) | ||
| if event_ is None: | ||
|
|
@@ -322,10 +320,13 @@ def capture_event( | |
| if hint is None: | ||
| hint = {} | ||
| event_id = event.get("event_id") | ||
| hint = dict(hint or ()) # type: Hint | ||
|
|
||
| if event_id is None: | ||
| event["event_id"] = event_id = uuid.uuid4().hex | ||
| if not self._should_capture(event, hint, scope): | ||
| return None | ||
|
|
||
| event_opt = self._prepare_event(event, hint, scope) | ||
| if event_opt is None: | ||
| return None | ||
|
|
@@ -336,19 +337,27 @@ def capture_event( | |
| if session: | ||
| self._update_session_from_event(session, event) | ||
|
|
||
| if event_opt.get("type") == "transaction": | ||
| # Transactions should go to the /envelope/ endpoint. | ||
| self.transport.capture_envelope( | ||
| Envelope( | ||
| headers={ | ||
| "event_id": event_opt["event_id"], | ||
| "sent_at": format_timestamp(datetime.utcnow()), | ||
| }, | ||
| items=[ | ||
| Item(payload=PayloadRef(json=event_opt), type="transaction"), | ||
| ], | ||
| ) | ||
| attachments = hint.get("attachments") | ||
| is_transaction = event_opt.get("type") == "transaction" | ||
|
|
||
| if is_transaction or attachments: | ||
| # Transactions or events with attachments should go to the | ||
|
mitsuhiko marked this conversation as resolved.
|
||
| # /envelope/ endpoint. | ||
| envelope = Envelope( | ||
| headers={ | ||
| "event_id": event_opt["event_id"], | ||
| "sent_at": format_timestamp(datetime.utcnow()), | ||
| } | ||
| ) | ||
|
|
||
| if is_transaction: | ||
| envelope.add_transaction(event_opt) | ||
| else: | ||
| envelope.add_event(event_opt) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @rhcarvalho I'm starting to think this entire forcing events and transactions to go different paths on the envelope to be wrong-ish. We now need to check if it's a transaction or not to call a different method on the envelope but in both cases they do the same internally. The only difference is that |
||
|
|
||
| for attachment in attachments or (): | ||
|
mitsuhiko marked this conversation as resolved.
|
||
| envelope.add_item(attachment.to_envelope_item()) | ||
| self.transport.capture_envelope(envelope) | ||
|
rhcarvalho marked this conversation as resolved.
|
||
| else: | ||
| # All other events go to the /store/ endpoint. | ||
| self.transport.capture_event(event_opt) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.