Esempio n. 1
0
    def to_json(self, client):
        # type: (Optional[sentry_sdk.Client]) -> Dict[str, Any]
        rv = {
            "trace_id": self.trace_id,
            "span_id": self.span_id,
            "parent_span_id": self.parent_span_id,
            "same_process_as_parent": self.same_process_as_parent,
            "op": self.op,
            "description": self.description,
            "start_timestamp": partial_serialize(
                client,
                self.start_timestamp,
                is_databag=False,
                should_repr_strings=False,
            ),
            "timestamp": partial_serialize(
                client, self.timestamp, is_databag=False, should_repr_strings=False
            ),
        }  # type: Dict[str, Any]

        transaction = self.transaction
        if transaction:
            rv["transaction"] = transaction

        tags = self._tags
        if tags:
            rv["tags"] = tags

        data = self._data
        if data:
            rv["data"] = data

        return rv
Esempio n. 2
0
    def finish(self, hub=None):
        # type: (Optional[sentry_sdk.Hub]) -> Optional[str]
        hub = hub or self.hub or sentry_sdk.Hub.current

        if self.timestamp is not None:
            # This transaction is already finished, so we should not flush it again.
            return None

        self.timestamp = datetime.now()

        _maybe_create_breadcrumbs_from_span(hub, self)

        if self._span_recorder is None:
            return None

        self._span_recorder.finish_span(self)

        if self.transaction is None:
            # If this has no transaction set we assume there's a parent
            # transaction for this span that would be flushed out eventually.
            return None

        client = hub.client

        if client is None:
            # We have no client and therefore nowhere to send this transaction
            # event.
            return None

        if not self.sampled:
            # At this point a `sampled = None` should have already been
            # resolved to a concrete decision. If `sampled` is `None`, it's
            # likely that somebody used `with sentry_sdk.Hub.start_span(..)` on a
            # non-transaction span and later decided to make it a transaction.
            if self.sampled is None:
                logger.warning("Discarding transaction Span without sampling decision")

            return None

        return hub.capture_event(
            {
                "type": "transaction",
                "transaction": self.transaction,
                "contexts": {"trace": self.get_trace_context()},
                "timestamp": partial_serialize(
                    client, self.timestamp, is_databag=False, should_repr_strings=False
                ),
                "start_timestamp": partial_serialize(
                    client,
                    self.start_timestamp,
                    is_databag=False,
                    should_repr_strings=False,
                ),
                "spans": [
                    s.to_json(client)
                    for s in self._span_recorder.finished_spans
                    if s is not self
                ],
            }
        )
Esempio n. 3
0
    def event_processor(event, hint):
        # type: (Event, Hint) -> Optional[Event]
        client = Hub.current.client

        with capture_internal_exceptions():
            extra = event.setdefault("extra", {})
            extra["celery-job"] = partial_serialize(
                client,
                {
                    "task_name": task.name,
                    "args": args,
                    "kwargs": kwargs
                },
                should_repr_strings=False,
            )

        if "exc_info" in hint:
            with capture_internal_exceptions():
                if issubclass(hint["exc_info"][0], SoftTimeLimitExceeded):
                    event["fingerprint"] = [
                        "celery",
                        "SoftTimeLimitExceeded",
                        partial_serialize(
                            client,
                            getattr(task, "name", task),
                            should_repr_strings=False,
                        ),
                    ]

        return event
Esempio n. 4
0
    def _emit(self, record):
        # type: (LogRecord) -> None
        if not _can_record(record):
            return

        hub = Hub.current
        if hub.client is None:
            return

        client_options = hub.client.options

        # exc_info might be None or (None, None, None)
        if record.exc_info is not None and record.exc_info[0] is not None:
            event, hint = event_from_exception(
                record.exc_info,
                client_options=client_options,
                mechanism={
                    "type": "logging",
                    "handled": True
                },
            )
        elif record.exc_info and record.exc_info[0] is None:
            event = {}
            hint = {}
            with capture_internal_exceptions():
                event["threads"] = {
                    "values": [{
                        "stacktrace":
                        current_stacktrace(client_options["with_locals"]),
                        "crashed":
                        False,
                        "current":
                        True,
                    }]
                }
        else:
            event = {}
            hint = {}

        hint["log_record"] = record

        client = Hub.current.client

        event["level"] = _logging_to_event_level(record.levelname)
        event["logger"] = record.name
        event["logentry"] = {
            "message":
            to_string(record.msg),
            "params":
            partial_serialize(client, record.args, should_repr_strings=False),
        }
        event["extra"] = partial_serialize(client,
                                           _extra_from_record(record),
                                           should_repr_strings=False)

        hub.capture_event(event, hint=hint)
Esempio n. 5
0
    def aiohttp_processor(
            event,  # type: Dict[str, Any]
            hint,  # type: Dict[str, Tuple[type, BaseException, Any]]
    ):
        # type: (...) -> Dict[str, Any]
        request = weak_request()
        if request is None:
            return event

        with capture_internal_exceptions():
            request_info = event.setdefault("request", {})

            request_info["url"] = "%s://%s%s" % (
                request.scheme,
                request.host,
                request.path,
            )

            request_info["query_string"] = request.query_string
            request_info["method"] = request.method
            request_info["env"] = {"REMOTE_ADDR": request.remote}

            hub = Hub.current
            request_info["headers"] = partial_serialize(
                hub.client,
                _filter_headers(dict(request.headers)),
                should_repr_strings=False,
            )

            # Just attach raw data here if it is within bounds, if available.
            # Unfortunately there's no way to get structured data from aiohttp
            # without awaiting on some coroutine.
            request_info["data"] = get_aiohttp_request_data(hub, request)

        return event
Esempio n. 6
0
    def tornado_processor(event, hint):
        # type: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any]
        handler = weak_handler()
        if handler is None:
            return event

        request = handler.request

        with capture_internal_exceptions():
            method = getattr(handler, handler.request.method.lower())
            event["transaction"] = transaction_from_function(method)

        with capture_internal_exceptions():
            extractor = TornadoRequestExtractor(request)
            extractor.extract_into_event(event)

            request_info = event["request"]

            request_info["url"] = "%s://%s%s" % (
                request.protocol,
                request.host,
                request.path,
            )

            request_info["query_string"] = partial_serialize(
                Hub.current.client, request.query, should_repr_strings=False)
            request_info["method"] = request.method
            request_info["env"] = {"REMOTE_ADDR": request.remote_ip}
            request_info["headers"] = _filter_headers(dict(request.headers))

        with capture_internal_exceptions():
            if handler.current_user and _should_send_default_pii():
                event.setdefault("user", {})["is_authenticated"] = True

        return event
Esempio n. 7
0
    def event_processor(self, event, hint, asgi_scope):
        # type: (Event, Hint, Any) -> Optional[Event]
        request_info = event.get("request", {})

        if asgi_scope["type"] in ("http", "websocket"):
            request_info["url"] = self.get_url(asgi_scope)
            request_info["method"] = asgi_scope["method"]
            request_info["headers"] = _filter_headers(
                self.get_headers(asgi_scope))
            request_info["query_string"] = self.get_query(asgi_scope)

        if asgi_scope.get("client") and _should_send_default_pii():
            request_info["env"] = {"REMOTE_ADDR": asgi_scope["client"][0]}

        if asgi_scope.get("endpoint"):
            # Webframeworks like Starlette mutate the ASGI env once routing is
            # done, which is sometime after the request has started. If we have
            # an endpoint, overwrite our path-based transaction name.
            event["transaction"] = self.get_transaction(asgi_scope)

        event["request"] = partial_serialize(Hub.current.client,
                                             request_info,
                                             should_repr_strings=False)

        return event
Esempio n. 8
0
    def aiohttp_processor(
        event,  # type: Dict[str, Any]
        hint,  # type: Dict[str, Tuple[type, BaseException, Any]]
    ):
        # type: (...) -> Dict[str, Any]
        request = weak_request()
        if request is None:
            return event

        with capture_internal_exceptions():
            # TODO: Figure out what to do with request body. Methods on request
            # are async, but event processors are not.

            request_info = event.setdefault("request", {})

            request_info["url"] = "%s://%s%s" % (
                request.scheme,
                request.host,
                request.path,
            )

            request_info["query_string"] = request.query_string
            request_info["method"] = request.method
            request_info["env"] = {"REMOTE_ADDR": request.remote}
            request_info["headers"] = partial_serialize(
                Hub.current.client,
                _filter_headers(dict(request.headers)),
                should_repr_strings=False,
            )

        return event
Esempio n. 9
0
    def event_processor(event, hint):
        # type: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any]
        job = weak_job()
        if job is not None:
            with capture_internal_exceptions():
                extra = event.setdefault("extra", {})
                extra["rq-job"] = partial_serialize(
                    Hub.current.client,
                    {
                        "job_id": job.id,
                        "func": job.func_name,
                        "args": job.args,
                        "kwargs": job.kwargs,
                        "description": job.description,
                    },
                    should_repr_strings=False,
                )

        if "exc_info" in hint:
            with capture_internal_exceptions():
                if issubclass(hint["exc_info"][0], JobTimeoutException):
                    event["fingerprint"] = [
                        "rq", "JobTimeoutException", job.func_name
                    ]

        return event
Esempio n. 10
0
 def set_extra(
     self,
     key,  # type: str
     value,  # type: Any
 ):
     # type: (...) -> None
     """Sets an extra key to a specific value."""
     self._extras[key] = partial_serialize(
         sentry_sdk.Hub.current.client, value, should_repr_strings=False
     )
Esempio n. 11
0
 def set_context(
     self,
     key,  # type: str
     value,  # type: Any
 ):
     # type: (...) -> None
     """Binds a context at a certain key to a specific value."""
     self._contexts[key] = partial_serialize(
         sentry_sdk.Hub.current.client, value, should_repr_strings=False
     )
Esempio n. 12
0
    def add_breadcrumb(
            self,
            crumb=None,  # type: Optional[Breadcrumb]
            hint=None,  # type: Optional[BreadcrumbHint]
            **kwargs  # type: Any
    ):
        # type: (...) -> None
        """
        Adds a breadcrumb.

        :param crumb: Dictionary with the data as the sentry v7/v8 protocol expects.

        :param hint: An optional value that can be used by `before_breadcrumb`
            to customize the breadcrumbs that are emitted.
        """
        client, scope = self._stack[-1]
        if client is None:
            logger.info("Dropped breadcrumb because no client bound")
            return

        crumb = dict(crumb or ())  # type: Breadcrumb
        crumb.update(kwargs)
        if not crumb:
            return

        hint = dict(hint or ())  # type: Hint

        if crumb.get("timestamp") is None:
            crumb["timestamp"] = datetime.utcnow()
        if crumb.get("type") is None:
            crumb["type"] = "default"

        crumb = partial_serialize(client, crumb, should_repr_strings=False)

        if client.options["before_breadcrumb"] is not None:
            new_crumb = client.options["before_breadcrumb"](crumb, hint)
        else:
            new_crumb = crumb

        if new_crumb is not None:
            scope._breadcrumbs.append(new_crumb)
        else:
            logger.info("before breadcrumb dropped breadcrumb (%s)", crumb)

        max_breadcrumbs = client.options["max_breadcrumbs"]  # type: int
        while len(scope._breadcrumbs) > max_breadcrumbs:
            scope._breadcrumbs.popleft()
Esempio n. 13
0
    def event_processor(event, hint):
        # type: (Event, Hint) -> Optional[Event]
        extra = event.setdefault("extra", {})
        extra["lambda"] = {
            "remaining_time_in_millis":
            aws_context.get_remaining_time_in_millis(),
            "function_name": aws_context.function_name,
            "function_version": aws_context.function_version,
            "invoked_function_arn": aws_context.invoked_function_arn,
            "aws_request_id": aws_context.aws_request_id,
        }

        request = event.get("request", {})

        if "httpMethod" in aws_event:
            request["method"] = aws_event["httpMethod"]

        request["url"] = _get_url(aws_event, aws_context)

        if "queryStringParameters" in aws_event:
            request["query_string"] = aws_event["queryStringParameters"]

        if "headers" in aws_event:
            request["headers"] = _filter_headers(aws_event["headers"])

        if aws_event.get("body", None):
            # Unfortunately couldn't find a way to get structured body from AWS
            # event. Meaning every body is unstructured to us.
            request["data"] = AnnotatedValue("",
                                             {"rem": [["!raw", "x", 0, 0]]})

        if _should_send_default_pii():
            user_info = event.setdefault("user", {})

            id = aws_event.get("identity", {}).get("userArn")
            if id is not None:
                user_info["id"] = id

            ip = aws_event.get("identity", {}).get("sourceIp")
            if ip is not None:
                user_info["ip_address"] = ip

        event["request"] = partial_serialize(Hub.current.client,
                                             request,
                                             should_repr_strings=False)

        return event
Esempio n. 14
0
    def extract_into_event(self, event):
        # type: (Dict[str, Any]) -> None
        client = Hub.current.client
        if client is None:
            return

        data = None  # type: Optional[Union[AnnotatedValue, Dict[str, Any]]]

        content_length = self.content_length()
        request_info = event.get("request", {})

        if _should_send_default_pii():
            request_info["cookies"] = dict(self.cookies())

        bodies = client.options["request_bodies"]
        if (bodies == "never" or (bodies == "small" and content_length > 10**3)
                or (bodies == "medium" and content_length > 10**4)):
            data = AnnotatedValue(
                "",
                {
                    "rem": [["!config", "x", 0, content_length]],
                    "len": content_length
                },
            )
        else:
            parsed_body = self.parsed_body()
            if parsed_body is not None:
                data = parsed_body
            elif self.raw_data():
                data = AnnotatedValue(
                    "",
                    {
                        "rem": [["!raw", "x", 0, content_length]],
                        "len": content_length
                    },
                )
            else:
                data = None

        if data is not None:
            request_info["data"] = data

        event["request"] = partial_serialize(client,
                                             request_info,
                                             should_repr_strings=False)
Esempio n. 15
0
    def capture_message(
            self,
            message,  # type: str
            level=None,  # type: Optional[str]
    ):
        # type: (...) -> Optional[str]
        """Captures a message.  The message is just a string.  If no level
        is provided the default level is `info`.

        :returns: An `event_id` if the SDK decided to send the event (see :py:meth:`sentry_sdk.Client.capture_event`).
        """
        if self.client is None:
            return None
        if level is None:
            level = "info"
        return self.capture_event({
            "message":
            partial_serialize(self.client, message, should_repr_strings=False),
            "level":
            level,
        })
Esempio n. 16
0
    def _prepare_event(
            self,
            event,  # type: Event
            hint,  # type: Optional[Hint]
            scope,  # type: Optional[Scope]
    ):
        # type: (...) -> Optional[Event]

        client = self  # type: Client  # type: ignore

        if event.get("timestamp") is None:
            event["timestamp"] = partial_serialize(client,
                                                   datetime.utcnow(),
                                                   is_databag=False,
                                                   should_repr_strings=False)

        hint = dict(hint or ())  # type: Hint

        if scope is not None:
            event_ = scope.apply_to_event(event, hint)
            if event_ is None:
                return None
            event = event_

        if (self.options["attach_stacktrace"] and "exception" not in event
                and "stacktrace" not in event and "threads" not in event):
            with capture_internal_exceptions():
                event["threads"] = {
                    "values": [{
                        "stacktrace":
                        current_stacktrace(self.options["with_locals"]),
                        "crashed":
                        False,
                        "current":
                        True,
                    }]
                }

        for key in "release", "environment", "server_name", "dist":
            if event.get(key) is None and self.options[key] is not None:
                event[key] = text_type(self.options[key]).strip()
        if event.get("sdk") is None:
            sdk_info = dict(SDK_INFO)
            sdk_info["integrations"] = sorted(self.integrations.keys())
            event["sdk"] = sdk_info

        if event.get("platform") is None:
            event["platform"] = "python"

        event = handle_in_app(event, self.options["in_app_exclude"],
                              self.options["in_app_include"])

        # Postprocess the event here so that annotated types do
        # generally not surface in before_send
        if event is not None and not self.options["_experiments"].get(
                "fast_serialize", False):
            event = serialize(event)

        before_send = self.options["before_send"]
        if before_send is not None:
            new_event = None
            with capture_internal_exceptions():
                new_event = before_send(event, hint or {})
            if new_event is None:
                logger.info("before send dropped event (%s)", event)
            event = new_event  # type: ignore

        return event
Esempio n. 17
0
 def set_data(self, key, value):
     # type: (str, Any) -> None
     self._data[key] = partial_serialize(
         sentry_sdk.Hub.current.client, value, should_repr_strings=False
     )