예제 #1
0
    def setup_once():
        # type: () -> None

        try:
            SanicIntegration.version = tuple(map(int, SANIC_VERSION.split(".")))
        except (TypeError, ValueError):
            raise DidNotEnable("Unparsable Sanic version: {}".format(SANIC_VERSION))

        if SanicIntegration.version < (0, 8):
            raise DidNotEnable("Sanic 0.8 or newer required.")

        if not HAS_REAL_CONTEXTVARS:
            # We better have contextvars or we're going to leak state between
            # requests.
            raise DidNotEnable(
                "The sanic integration for Sentry requires Python 3.7+ "
                " or the aiocontextvars package." + CONTEXTVARS_ERROR_MESSAGE
            )

        if SANIC_VERSION.startswith("0.8."):
            # Sanic 0.8 and older creates a logger named "root" and puts a
            # stringified version of every exception in there (without exc_info),
            # which our error deduplication can't detect.
            #
            # We explicitly check the version here because it is a very
            # invasive step to ignore this logger and not necessary in newer
            # versions at all.
            #
            # https://github.com/huge-success/sanic/issues/1332
            ignore_logger("root")

        if SanicIntegration.version < (21, 9):
            _setup_legacy_sanic()
            return

        _setup_sanic()
예제 #2
0
from sanic.response import stream
from sanic.response import text
from sanic.server import HttpProtocol

from ddtrace import config
from ddtrace.constants import ANALYTICS_SAMPLE_RATE_KEY
from ddtrace.constants import ERROR_MSG
from ddtrace.constants import ERROR_STACK
from ddtrace.constants import ERROR_TYPE
from ddtrace.propagation import http as http_propagation
from tests.utils import override_config
from tests.utils import override_http_config

# Helpers for handling response objects across sanic versions

sanic_version = tuple(map(int, sanic_version.split(".")))


def _response_status(response):
    return getattr(response, "status_code", getattr(response, "status", None))


async def _response_json(response):
    resp_json = response.json
    if callable(resp_json):
        resp_json = response.json()
    if asyncio.iscoroutine(resp_json):
        resp_json = await resp_json
    return resp_json

예제 #3
0
import sys

import random
import asyncio

import pytest

from sentry_sdk import capture_message, configure_scope
from sentry_sdk.integrations.sanic import SanicIntegration

from sanic import Sanic, request, response, __version__ as SANIC_VERSION_RAW
from sanic.response import HTTPResponse
from sanic.exceptions import abort

SANIC_VERSION = tuple(map(int, SANIC_VERSION_RAW.split(".")))


@pytest.fixture
def app():
    if SANIC_VERSION >= (20, 12):
        # Build (20.12.0) adds a feature where the instance is stored in an internal class
        # registry for later retrieval, and so add register=False to disable that
        app = Sanic(__name__, register=False)
    else:
        app = Sanic(__name__)

    @app.route("/message")
    def hi(request):
        capture_message("hi")
        return response.text("ok")
예제 #4
0
    def setup_once():
        # type: () -> None
        try:
            version = tuple(map(int, SANIC_VERSION.split(".")))
        except (TypeError, ValueError):
            raise DidNotEnable(
                "Unparsable Sanic version: {}".format(SANIC_VERSION))

        if version < (0, 8):
            raise DidNotEnable("Sanic 0.8 or newer required.")

        if not HAS_REAL_CONTEXTVARS:
            # We better have contextvars or we're going to leak state between
            # requests.
            raise DidNotEnable(
                "The sanic integration for Sentry requires Python 3.7+ "
                " or the aiocontextvars package." + CONTEXTVARS_ERROR_MESSAGE)

        if SANIC_VERSION.startswith("0.8."):
            # Sanic 0.8 and older creates a logger named "root" and puts a
            # stringified version of every exception in there (without exc_info),
            # which our error deduplication can't detect.
            #
            # We explicitly check the version here because it is a very
            # invasive step to ignore this logger and not necessary in newer
            # versions at all.
            #
            # https://github.com/huge-success/sanic/issues/1332
            ignore_logger("root")

        old_handle_request = Sanic.handle_request

        async def sentry_handle_request(self, request, *args, **kwargs):
            # type: (Any, Request, *Any, **Any) -> Any
            hub = Hub.current
            if hub.get_integration(SanicIntegration) is None:
                return old_handle_request(self, request, *args, **kwargs)

            weak_request = weakref.ref(request)

            with Hub(hub) as hub:
                with hub.configure_scope() as scope:
                    scope.clear_breadcrumbs()
                    scope.add_event_processor(
                        _make_request_processor(weak_request))

                response = old_handle_request(self, request, *args, **kwargs)
                if isawaitable(response):
                    response = await response

                return response

        Sanic.handle_request = sentry_handle_request

        old_router_get = Router.get

        def sentry_router_get(self, request):
            # type: (Any, Request) -> Any
            rv = old_router_get(self, request)
            hub = Hub.current
            if hub.get_integration(SanicIntegration) is not None:
                with capture_internal_exceptions():
                    with hub.configure_scope() as scope:
                        scope.transaction = rv[0].__name__
            return rv

        Router.get = sentry_router_get

        old_error_handler_lookup = ErrorHandler.lookup

        def sentry_error_handler_lookup(self, exception):
            # type: (Any, Exception) -> Optional[object]
            _capture_exception(exception)
            old_error_handler = old_error_handler_lookup(self, exception)

            if old_error_handler is None:
                return None

            if Hub.current.get_integration(SanicIntegration) is None:
                return old_error_handler

            async def sentry_wrapped_error_handler(request, exception):
                # type: (Request, Exception) -> Any
                try:
                    response = old_error_handler(request, exception)
                    if isawaitable(response):
                        response = await response
                    return response
                except Exception:
                    # Report errors that occur in Sanic error handler. These
                    # exceptions will not even show up in Sanic's
                    # `sanic.exceptions` logger.
                    exc_info = sys.exc_info()
                    _capture_exception(exc_info)
                    reraise(*exc_info)

            return sentry_wrapped_error_handler

        ErrorHandler.lookup = sentry_error_handler_lookup