1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
|
import io
import os
import urllib.parse
import urllib.request
import urllib.error
import urllib3
from itertools import chain
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from typing import Any
from typing import Callable
from typing import Dict
from typing import Optional
from sentry_sdk.utils import logger, env_to_bool, capture_internal_exceptions
from sentry_sdk.envelope import Envelope
DEFAULT_SPOTLIGHT_URL = "http://localhost:8969/stream"
DJANGO_SPOTLIGHT_MIDDLEWARE_PATH = "sentry_sdk.spotlight.SpotlightMiddleware"
class SpotlightClient:
def __init__(self, url):
# type: (str) -> None
self.url = url
self.http = urllib3.PoolManager()
self.tries = 0
def capture_envelope(self, envelope):
# type: (Envelope) -> None
if self.tries > 3:
logger.warning(
"Too many errors sending to Spotlight, stop sending events there."
)
return
body = io.BytesIO()
envelope.serialize_into(body)
try:
req = self.http.request(
url=self.url,
body=body.getvalue(),
method="POST",
headers={
"Content-Type": "application/x-sentry-envelope",
},
)
req.close()
except Exception as e:
self.tries += 1
logger.warning(str(e))
try:
from django.http import HttpResponseServerError
from django.conf import settings
class SpotlightMiddleware:
def __init__(self, get_response):
# type: (Any, Callable[..., Any]) -> None
self.get_response = get_response
def __call__(self, request):
# type: (Any, Any) -> Any
return self.get_response(request)
def process_exception(self, _request, exception):
# type: (Any, Any, Exception) -> Optional[HttpResponseServerError]
if not settings.DEBUG:
return None
import sentry_sdk.api
spotlight_client = sentry_sdk.api.get_client().spotlight
if spotlight_client is None:
return None
# Spotlight URL has a trailing `/stream` part at the end so split it off
spotlight_url = spotlight_client.url.rsplit("/", 1)[0]
try:
spotlight = urllib.request.urlopen(spotlight_url).read().decode("utf-8")
except urllib.error.URLError:
return None
else:
event_id = sentry_sdk.api.capture_exception(exception)
return HttpResponseServerError(
spotlight.replace(
"<html>",
(
f'<html><base href="{spotlight_url}">'
'<script>window.__spotlight = {{ initOptions: {{ startFrom: "/errors/{event_id}" }}}};</script>'.format(
event_id=event_id
)
),
)
)
except ImportError:
settings = None
def setup_spotlight(options):
# type: (Dict[str, Any]) -> Optional[SpotlightClient]
url = options.get("spotlight")
if isinstance(url, str):
pass
elif url is True:
url = DEFAULT_SPOTLIGHT_URL
else:
return None
if (
settings is not None
and settings.DEBUG
and env_to_bool(os.environ.get("SENTRY_SPOTLIGHT_ON_ERROR", "1"))
):
with capture_internal_exceptions():
middleware = settings.MIDDLEWARE
if DJANGO_SPOTLIGHT_MIDDLEWARE_PATH not in middleware:
settings.MIDDLEWARE = type(middleware)(
chain(middleware, (DJANGO_SPOTLIGHT_MIDDLEWARE_PATH,))
)
return SpotlightClient(url)
|