Example #1
0
def _bulk_snuba_query(snuba_param_list, headers):
    with sentry_sdk.start_span(
            op="start_snuba_query",
            description=f"running {len(snuba_param_list)} snuba queries",
    ) as span:
        span.set_tag("referrer", headers.get("referer", "<unknown>"))
        if len(snuba_param_list) > 1:
            query_results = list(
                _query_thread_pool.map(
                    _snuba_query,
                    [
                        params + (Hub(Hub.current), headers)
                        for params in snuba_param_list
                    ],
                ))
        else:
            # No need to submit to the thread pool if we're just performing a
            # single query
            query_results = [
                _snuba_query(snuba_param_list[0] + (Hub(Hub.current), headers))
            ]

    results = []
    for response, _, reverse in query_results:
        try:
            body = json.loads(response.data)
            if SNUBA_INFO:
                if "sql" in body:
                    logger.info("{}.sql: {}".format(
                        headers.get("referer", "<unknown>"), body["sql"]))
                if "error" in body:
                    logger.info("{}.err: {}".format(
                        headers.get("referer", "<unknown>"), body["error"]))
        except ValueError:
            if response.status != 200:
                logger.error("snuba.query.invalid-json")
                raise SnubaError("Failed to parse snuba error response")
            raise UnexpectedResponseError(
                f"Could not decode JSON response: {response.data}")

        if response.status != 200:
            if body.get("error"):
                error = body["error"]
                if response.status == 429:
                    raise RateLimitExceeded(error["message"])
                elif error["type"] == "schema":
                    raise SchemaValidationError(error["message"])
                elif error["type"] == "clickhouse":
                    raise clickhouse_error_codes_map.get(
                        error["code"], QueryExecutionError)(error["message"])
                else:
                    raise SnubaError(error["message"])
            else:
                raise SnubaError(f"HTTP {response.status}")

        # Forward and reverse translation maps from model ids to snuba keys, per column
        body["data"] = [reverse(d) for d in body["data"]]
        results.append(body)

    return results
def _resolve_hub_marker_value(marker_value):
    if marker_value is None:
        marker_value = DEFAULT_HUB
    else:
        marker_value = marker_value.args[0]

    if callable(marker_value):
        marker_value = marker_value()

    if marker_value is None:
        # user explicitly disabled reporting
        return Hub()

    if isinstance(marker_value, str):
        return Hub(Client(marker_value))

    if isinstance(marker_value, dict):
        return Hub(Client(**marker_value))

    if isinstance(marker_value, Client):
        return Hub(marker_value)

    if isinstance(marker_value, Hub):
        return marker_value

    raise RuntimeError(
        "The `sentry_client` value must be a client, hub or string, not {}".
        format(repr(type(marker_value))))
Example #3
0
    def test_basic(self, request):
        requests = []

        def queue_event(method, url, body, headers):
            requests.append((method, url, body, headers))

        request.side_effect = queue_event

        hub = Hub(Client(
            'http://%s:%s@localhost:8000/%s' %
            (self.pk.public_key, self.pk.secret_key, self.pk.project_id),
            default_integrations=False
        ))

        hub.capture_message('foo')
        hub.client.close()

        for _request in requests:
            self.send_event(*_request)

        assert request.call_count is 1
        assert Group.objects.count() == 1
        group = Group.objects.get()
        assert group.event_set.count() == 1
        instance = group.event_set.get()
        assert instance.data['logentry']['formatted'] == 'foo'
Example #4
0
def test_attach_stacktrace_disabled():
    events = []
    hub = Hub(Client(attach_stacktrace=False, transport=events.append))
    hub.capture_message("HI")

    event, = events
    assert "threads" not in event
Example #5
0
    def test_basic(self, request):
        requests = []

        def queue_event(method, url, body, headers):
            requests.append((method, url, body, headers))

        request.side_effect = queue_event

        hub = Hub(Client(
            'http://%s:%s@localhost:8000/%s' %
            (self.pk.public_key, self.pk.secret_key, self.pk.project_id),
            default_integrations=False
        ))

        hub.capture_message('foo')
        hub.client.close()

        for _request in requests:
            self.send_event(*_request)

        assert request.call_count is 1
        assert Group.objects.count() == 1
        group = Group.objects.get()
        assert group.event_set.count() == 1
        instance = group.event_set.get()
        assert instance.data['logentry']['formatted'] == 'foo'
Example #6
0
    def test_basic(self, request):
        requests = []

        def queue_event(method, url, body, headers):
            requests.append((method, url, body, headers))

        request.side_effect = queue_event

        hub = Hub(
            Client(
                "http://%s:%s@localhost:8000/%s" %
                (self.pk.public_key, self.pk.secret_key, self.pk.project_id),
                default_integrations=False,
            ))

        hub.capture_message("foo")
        hub.client.close()

        for _request in requests:
            self.send_event(*_request)

        assert request.call_count == 1
        assert Group.objects.count() == 1
        group = Group.objects.get()
        assert group.data["title"] == "foo"
Example #7
0
def test_with_locals_enabled():
    events = []
    hub = Hub(Client(with_locals=True, transport=events.append))
    try:
        1 / 0
    except Exception:
        hub.capture_exception()

    (event, ) = events

    assert all(
        frame["vars"]
        for frame in event["exception"]["values"][0]["stacktrace"]["frames"])
Example #8
0
def test_with_locals_disabled():
    events = []
    hub = Hub(Client(with_locals=False, transport=events.append))
    try:
        1 / 0
    except Exception:
        hub.capture_exception()

    event, = events

    assert all(
        "vars" not in frame
        for frame in event["exception"]["values"][0]["stacktrace"]["frames"])
Example #9
0
    def test_traceparent_header_wsgi(self):
        # Assert that posting something to store will not create another
        # (transaction) event under any circumstances.
        #
        # We use Werkzeug's test client because Django's test client bypasses a
        # lot of request handling code that we want to test implicitly (such as
        # all our WSGI middlewares and the entire Django instrumentation by
        # sentry-sdk).
        #
        # XXX(markus): Ideally methods such as `_postWithHeader` would always
        # call the WSGI application => swap out Django's test client with e.g.
        # Werkzeug's.
        client = WerkzeugClient(application)

        calls = []

        def new_disable_transaction_events():
            with configure_scope() as scope:
                assert scope.span.sampled
                assert scope.span.transaction
                disable_transaction_events()
                assert not scope.span.sampled

            calls.append(1)

        events = []

        auth = get_auth_header("_postWithWerkzeug/0.0.0",
                               self.projectkey.public_key,
                               self.projectkey.secret_key, "7")

        with mock.patch("sentry.web.api.disable_transaction_events",
                        new_disable_transaction_events):
            with self.tasks():
                with Hub(
                        Client(
                            transport=events.append,
                            integrations=[
                                CeleryIntegration(),
                                DjangoIntegration()
                            ],
                        )):
                    app_iter, status, headers = client.post(
                        reverse("sentry-api-store"),
                        data=b'{"message": "hello"}',
                        headers={
                            "x-sentry-auth": auth,
                            "sentry-trace": "1",
                            "content-type": "application/octet-stream",
                        },
                        environ_base={"REMOTE_ADDR": "127.0.0.1"},
                    )

                    body = "".join(app_iter)

        assert status == "200 OK", body
        assert set(
            (e.get("type"), e.get("transaction"))
            for e in events) == {("transaction", "rule_processor_apply")}
        assert calls == [1]
 def inner(*args, **kwargs):
     with Hub(Hub.current):
         try:
             return f(*args, **kwargs)
         except Exception:
             _capture_and_reraise()
         finally:
             if flush:
                 _flush_client()
Example #11
0
    def test_exception_captured_by_sentry(self):
        events = []
        with Hub(Client(transport=events.append)):
            # This endpoint should return 500 as it internally raises an exception
            response = self.app.get('/tests/error')

            assert response.status_code == 500
            assert len(events) == 1
            assert events[0]['exception']['values'][0]['type'] == 'ZeroDivisionError'
Example #12
0
def test_ignore_errors():
    class MyDivisionError(ZeroDivisionError):
        pass

    def raise_it(exc_info):
        reraise(*exc_info)

    hub = Hub(Client(ignore_errors=[ZeroDivisionError], transport=_TestTransport()))
    hub._capture_internal_exception = raise_it

    def e(exc):
        try:
            raise exc
        except Exception:
            hub.capture_exception()

    e(ZeroDivisionError())
    e(MyDivisionError())
    pytest.raises(EventCaptured, lambda: e(ValueError()))
Example #13
0
    def test_traceparent_header_wsgi(self):
        # Assert that posting something to store will not create another
        # (transaction) event under any circumstances.
        #
        # We use Werkzeug's test client because Django's test client bypasses a
        # lot of request handling code that we want to test implicitly (such as
        # all our WSGI middlewares and the entire Django instrumentation by
        # sentry-sdk).
        #
        # XXX(markus): Ideally methods such as `_postWithHeader` would always
        # call the WSGI application => swap out Django's test client with e.g.
        # Werkzeug's.
        client = WerkzeugClient(application)

        calls = []

        def new_disable_transaction_events():
            with configure_scope() as scope:
                assert scope.span.sampled
                assert scope.span.transaction
                disable_transaction_events()
                assert not scope.span.sampled

            calls.append(1)

        events = []

        auth = get_auth_header('_postWithWerkzeug/0.0.0',
                               self.projectkey.public_key,
                               self.projectkey.secret_key, '7')

        with mock.patch('sentry.web.api.disable_transaction_events',
                        new_disable_transaction_events):
            with self.tasks():
                with Hub(
                        Client(transport=events.append,
                               integrations=[
                                   CeleryIntegration(),
                                   DjangoIntegration()
                               ])):
                    app_iter, status, headers = client.post(
                        reverse('sentry-api-store'),
                        data=b'{"message": "hello"}',
                        headers={
                            'x-sentry-auth': auth,
                            'sentry-trace': '1',
                            'content-type': 'application/octet-stream',
                        },
                        environ_base={'REMOTE_ADDR': '127.0.0.1'})

                    body = ''.join(app_iter)

        assert status == '200 OK', body
        assert not events
        assert calls == [1]
Example #14
0
def test_transport_infinite_loop(capturing_server, request, make_client):
    client = make_client(
        debug=True,
        # Make sure we cannot create events from our own logging
        integrations=[LoggingIntegration(event_level=logging.DEBUG)],
    )

    with Hub(client):
        capture_message("hi")
        client.flush()

    assert len(capturing_server.captured) == 1
Example #15
0
        def sentry_start(self, *a, **kw):
            hub = Hub.current
            integration = hub.get_integration(ThreadingIntegration)
            if integration is not None:
                if not integration.propagate_hub:
                    hub_ = None
                else:
                    hub_ = Hub(hub)

                self.run = _wrap_run(hub_, self.run)

            return old_start(self, *a, **kw)  # type: ignore
Example #16
0
def test_transport_infinite_loop(httpserver, request):
    httpserver.serve_content("ok", 200)

    client = Client(
        "http://foobar@{}/123".format(httpserver.url[len("http://") :]),
        debug=True,
        # Make sure we cannot create events from our own logging
        integrations=[LoggingIntegration(event_level=logging.DEBUG)],
    )

    with Hub(client):
        capture_message("hi")
        client.flush()

    assert len(httpserver.requests) == 1
Example #17
0
def test_attach_stacktrace_enabled():
    events = []
    hub = Hub(Client(attach_stacktrace=True, transport=events.append))

    def foo():
        bar()

    def bar():
        hub.capture_message("HI")

    foo()

    event, = events
    thread, = event["threads"]["values"]
    functions = [x["function"] for x in thread["stacktrace"]["frames"]]
    assert functions[-2:] == ["foo", "bar"]
Example #18
0
        def sentry_start(self, *a, **kw):
            hub = Hub.current
            integration = hub.get_integration(ThreadingIntegration)
            if integration is not None:
                if not integration.propagate_hub:
                    hub_ = None
                else:
                    hub_ = Hub(hub)
                # Patching instance methods in `start()` creates a reference cycle if
                # done in a naive way. See
                # https://github.com/getsentry/sentry-python/pull/434
                #
                # In threading module, using current_thread API will access current thread instance
                # without holding it to avoid a reference cycle in an easier way.
                self.run = _wrap_run(hub_, self.run.__func__)

            return old_start(self, *a, **kw)  # type: ignore
Example #19
0
def test_attach_stacktrace_enabled_no_locals():
    events = []
    hub = Hub(
        Client(attach_stacktrace=True, with_locals=False, transport=events.append)
    )

    def foo():
        bar()

    def bar():
        hub.capture_message("HI")

    foo()

    (event,) = events
    (thread,) = event["threads"]["values"]
    local_vars = [x.get("vars") for x in thread["stacktrace"]["frames"]]
    assert local_vars[-2:] == [None, None]
Example #20
0
 async def lottery_status_cron_job(self):
     with Hub(Hub.current):
         # ensure that only one instance of job is running, other instances will be discarded
         if not self.lock.locked():
             await self.lock.acquire()
             try:
                 should_reload_options = False
                 async with in_transaction():
                     await self._handle_stopping_sales()
                     await self._handle_selecting_winning_tickets()
                     should_reload_options = await self._handle_payments_to_winners()
                 if should_reload_options:
                     await reload_options_hack(self.bot)
             except Exception as e:
                 logging.debug(f":::lottery_cron: {e}")
                 capture_exception(e)
             finally:
                 self.lock.release()
Example #21
0
    def capture_exception(self, exception=None):
        with configure_scope() as scope:
            scope.set_level('info')
            scope.set_user({'email': env.get_gitlab_user_email()})
            scope.set_tag('host_url', env.get_target_host_data().full_url)
            scope.set_tag('git_branch_in_ci', env.get_git_branch_name())
            # Replace '/' to '.' needs for compatibility with Prometheus format
            scope.set_tag('test', env.test_name_with_path())
            scope.set_tag('ci_job_id', env.get_ci_job_id())
            if self.__overload_job_url:
                scope.set_extra('overload_job_url', self.__overload_job_url)

        # Yes, we use concurrency issues
        # https://docs.sentry.io/platforms/python/troubleshooting/#addressing-concurrency-issues
        with Hub(Hub.current):
            self.init_client()
            event_id = sentry_sdk.capture_exception(exception, scope)
        event_sentry_url = f"https://your_sentry_instance_url/?query={event_id}"
        logging.warning(
            f"--- Sentry captured exception URL is: '{event_sentry_url}' ---")
Example #22
0
def test_envelope_types():
    """
    Tests for calling the right transport method (capture_event vs
    capture_envelope) from the SDK client for different data types.
    """

    envelopes = []
    events = []

    class CustomTransport(Transport):
        def capture_envelope(self, envelope):
            envelopes.append(envelope)

        def capture_event(self, event):
            events.append(event)

    with Hub(Client(traces_sample_rate=1.0, transport=CustomTransport())):
        event_id = capture_message("hello")

        # Assert error events get passed in via capture_event
        assert not envelopes
        event = events.pop()

        assert event["event_id"] == event_id
        assert "type" not in event

        with start_transaction(name="foo"):
            pass

        # Assert transactions get passed in via capture_envelope
        assert not events
        envelope = envelopes.pop()

        (item, ) = envelope.items
        assert item.data_category == "transaction"
        assert item.headers.get("type") == "transaction"

    assert not envelopes
    assert not events
Example #23
0
    async def middleware(self, handler: ASGIApp, request: Request,
                         receive: Receive, send: Send):  # type: ignore
        """Capture exceptions to Sentry."""
        hub = Hub(Hub.current)
        with hub.configure_scope() as scope:
            scope.clear_breadcrumbs()
            scope._name = "muffin"
            self.current_scope.set(scope)
            scope.add_event_processor(
                partial(self.processData, request=request))

            with hub.start_transaction(
                    Transaction.continue_from_headers(
                        request.headers, op=f"{request.scope['type']}.muffin"),
                    custom_sampling_context={'asgi_scope': scope}):
                try:
                    return await handler(request, receive, send)

                except Exception as exc:
                    if type(exc) not in self.cfg.ignore_errors:
                        hub.capture_exception(exc)
                    raise exc from None
Example #24
0
def bulk_raw_query(snuba_param_list, referrer=None):
    headers = {}
    if referrer:
        headers["referer"] = referrer

    query_param_list = map(_prepare_query_params, snuba_param_list)

    def snuba_query(params):
        query_params, forward, reverse, thread_hub = params
        try:
            with timer("snuba_query"):
                referrer = headers.get("referer", "<unknown>")
                if SNUBA_INFO:
                    logger.info("{}.body: {}".format(referrer, json.dumps(query_params)))
                    query_params["debug"] = True
                body = json.dumps(query_params)
                with thread_hub.start_span(
                    op="snuba", description=u"query {}".format(referrer)
                ) as span:
                    span.set_tag("referrer", referrer)
                    for param_key, param_data in six.iteritems(query_params):
                        span.set_data(param_key, param_data)
                    return (
                        _snuba_pool.urlopen("POST", "/query", body=body, headers=headers),
                        forward,
                        reverse,
                    )
        except urllib3.exceptions.HTTPError as err:
            raise SnubaError(err)

    with sentry_sdk.start_span(
        op="start_snuba_query",
        description=u"running {} snuba queries".format(len(snuba_param_list)),
    ) as span:
        span.set_tag("referrer", headers.get("referer", "<unknown>"))
        if len(snuba_param_list) > 1:
            query_results = list(
                _query_thread_pool.map(
                    snuba_query, [params + (Hub(Hub.current),) for params in query_param_list]
                )
            )
        else:
            # No need to submit to the thread pool if we're just performing a
            # single query
            query_results = [snuba_query(query_param_list[0] + (Hub(Hub.current),))]

    results = []
    for response, _, reverse in query_results:
        try:
            body = json.loads(response.data)
            if SNUBA_INFO:
                if "sql" in body:
                    logger.info(
                        "{}.sql: {}".format(headers.get("referer", "<unknown>"), body["sql"])
                    )
                if "error" in body:
                    logger.info(
                        "{}.err: {}".format(headers.get("referer", "<unknown>"), body["error"])
                    )
        except ValueError:
            if response.status != 200:
                logger.error("snuba.query.invalid-json")
                raise SnubaError("Failed to parse snuba error response")
            raise UnexpectedResponseError(
                u"Could not decode JSON response: {}".format(response.data)
            )

        if response.status != 200:
            if body.get("error"):
                error = body["error"]
                if response.status == 429:
                    raise RateLimitExceeded(error["message"])
                elif error["type"] == "schema":
                    raise SchemaValidationError(error["message"])
                elif error["type"] == "clickhouse":
                    raise clickhouse_error_codes_map.get(error["code"], QueryExecutionError)(
                        error["message"]
                    )
                else:
                    raise SnubaError(error["message"])
            else:
                raise SnubaError(u"HTTP {}".format(response.status))

        # Forward and reverse translation maps from model ids to snuba keys, per column
        body["data"] = [reverse(d) for d in body["data"]]
        results.append(body)

    return results
        cur_exc_chain = getattr(item, "pytest_sentry_exc_chain", [])

        if call.excinfo is not None:
            item.pytest_sentry_exc_chain = cur_exc_chain = cur_exc_chain + [
                call.excinfo
            ]

        integration = Hub.current.get_integration(PytestIntegration)

        if (cur_exc_chain
                and call.excinfo is None) or integration.always_report:
            for exc_info in cur_exc_chain:
                capture_exception((exc_info.type, exc_info.value, exc_info.tb))


DEFAULT_HUB = Hub(Client())


def _resolve_hub_marker_value(marker_value):
    if marker_value is None:
        marker_value = DEFAULT_HUB
    else:
        marker_value = marker_value.args[0]

    if callable(marker_value):
        marker_value = marker_value()

    if marker_value is None:
        # user explicitly disabled reporting
        return Hub()
Example #26
0
 def captureException(self, *args, **kwargs):
     """Capture exception."""
     with Hub(Hub.current, self.current_scope.get()) as hub:
         return hub.capture_exception(*args, **kwargs)
Example #27
0
def test_simple_transport():
    events = []
    with Hub(Client(transport=events.append)):
        capture_message("Hello World!")
    assert events[0]["message"] == "Hello World!"
Example #28
0
 def captureMessage(self, *args, **kwargs):
     """Capture message."""
     with Hub(Hub.current, self.current_scope.get()) as hub:
         return hub.capture_message(*args, **kwargs)