sentry_sdk.envelope
import io import json import mimetypes from sentry_sdk.session import Session from sentry_sdk.utils import json_dumps, capture_internal_exceptions from typing import TYPE_CHECKING if TYPE_CHECKING: from typing import Any from typing import Optional from typing import Union from typing import Dict from typing import List from typing import Iterator from sentry_sdk._types import Event, EventDataCategory def parse_json(data: "Union[bytes, str]") -> "Any": # on some python 3 versions this needs to be bytes if isinstance(data, bytes): data = data.decode("utf-8", "replace") return json.loads(data) class Envelope: """ Represents a Sentry Envelope. The calling code is responsible for adhering to the constraints documented in the Sentry docs: https://develop.sentry.dev/sdk/envelopes/#data-model. In particular, each envelope may have at most one Item with type "event" or "transaction" (but not both). """ def __init__( self, headers: "Optional[Dict[str, Any]]" = None, items: "Optional[List[Item]]" = None, ) -> None: if headers is not None: headers = dict(headers) self.headers = headers or {} if items is None: items = [] else: items = list(items) self.items = items @property def description(self) -> str: return "envelope with %s items (%s)" % ( len(self.items), ", ".join(x.data_category for x in self.items), ) def add_event( self, event: "Event", ) -> None: self.add_item(Item(payload=PayloadRef(json=event), type="event")) def add_transaction( self, transaction: "Event", ) -> None: self.add_item(Item(payload=PayloadRef(json=transaction), type="transaction")) def add_profile( self, profile: "Any", ) -> None: self.add_item(Item(payload=PayloadRef(json=profile), type="profile")) def add_profile_chunk( self, profile_chunk: "Any", ) -> None: self.add_item( Item( payload=PayloadRef(json=profile_chunk), type="profile_chunk", headers={"platform": profile_chunk.get("platform", "python")}, ) ) def add_checkin( self, checkin: "Any", ) -> None: self.add_item(Item(payload=PayloadRef(json=checkin), type="check_in")) def add_session( self, session: "Union[Session, Any]", ) -> None: if isinstance(session, Session): session = session.to_json() self.add_item(Item(payload=PayloadRef(json=session), type="session")) def add_sessions( self, sessions: "Any", ) -> None: self.add_item(Item(payload=PayloadRef(json=sessions), type="sessions")) def add_item( self, item: "Item", ) -> None: self.items.append(item) def get_event(self) -> "Optional[Event]": for items in self.items: event = items.get_event() if event is not None: return event return None def get_transaction_event(self) -> "Optional[Event]": for item in self.items: event = item.get_transaction_event() if event is not None: return event return None def __iter__(self) -> "Iterator[Item]": return iter(self.items) def serialize_into( self, f: "Any", ) -> None: f.write(json_dumps(self.headers)) f.write(b"\n") for item in self.items: item.serialize_into(f) def serialize(self) -> bytes: out = io.BytesIO() self.serialize_into(out) return out.getvalue() @classmethod def deserialize_from( cls, f: "Any", ) -> "Envelope": headers = parse_json(f.readline()) items = [] while 1: item = Item.deserialize_from(f) if item is None: break items.append(item) return cls(headers=headers, items=items) @classmethod def deserialize( cls, bytes: bytes, ) -> "Envelope": return cls.deserialize_from(io.BytesIO(bytes)) def __repr__(self) -> str: return "<Envelope headers=%r items=%r>" % (self.headers, self.items)