Files
zulip/zproject/sentry.py
Alex Vandiver 30968e8b5a sentry: Increase shutdown_timeout from 2s to 10s.
This increases the possible maximum wait time to send exceptions
during shutdown.  The larger value makes it possible to send larger
exceptions, and weather larger network hiccups, during shutdown.  In
instances where a service is crash-looping, it is already not serving
requests reliably, and better ensuring those exceptions are captured
is of significant value.
2021-11-08 18:11:47 -08:00

98 lines
4.2 KiB
Python

from typing import TYPE_CHECKING, Optional
import sentry_sdk
from django.utils.translation import override as override_language
from sentry_sdk.integrations.django import DjangoIntegration
from sentry_sdk.integrations.logging import ignore_logger
from sentry_sdk.integrations.redis import RedisIntegration
from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration
from sentry_sdk.utils import capture_internal_exceptions
from version import ZULIP_VERSION
if TYPE_CHECKING:
from sentry_sdk._types import Event, Hint
def add_context(event: "Event", hint: "Hint") -> Optional["Event"]:
if "exc_info" in hint:
_, exc_value, _ = hint["exc_info"]
# Ignore GeneratorExit, KeyboardInterrupt, and SystemExit exceptions
if not isinstance(exc_value, Exception):
return None
from django.conf import settings
from zerver.lib.request import RequestNotes, get_current_request
from zerver.models import get_user_profile_by_id
with capture_internal_exceptions():
# event.user is the user context, from Sentry, which is
# pre-populated with some keys via its Django integration:
# https://docs.sentry.io/platforms/python/guides/django/enriching-error-data/additional-data/identify-user/
event.setdefault("tags", {})
user_info = event.get("user", {})
if user_info.get("id"):
user_profile = get_user_profile_by_id(user_info["id"])
event["tags"]["realm"] = user_info["realm"] = user_profile.realm.string_id or "root"
with override_language(settings.LANGUAGE_CODE):
# str() to force the lazy-translation to apply now,
# since it won't serialize into json for Sentry otherwise
user_info["role"] = str(user_profile.get_role_name())
# These are PII, and should be scrubbed
if "username" in user_info:
del user_info["username"]
if "email" in user_info:
del user_info["email"]
request = get_current_request()
if request:
request_notes = RequestNotes.get_notes(request)
if request_notes.client is not None:
event["tags"]["client"] = request_notes.client.name
if request_notes.realm is not None:
event["tags"].setdefault("realm", request_notes.realm.string_id)
return event
def setup_sentry(dsn: Optional[str], environment: str) -> None:
if not dsn:
return
sentry_sdk.init(
dsn=dsn,
environment=environment,
release=ZULIP_VERSION,
integrations=[
DjangoIntegration(),
RedisIntegration(),
SqlalchemyIntegration(),
],
before_send=add_context,
# Increase possible max wait to send exceptions during
# shutdown, from 2 to 10; potentially-large exceptions are of
# value to catch during shutdown.
shutdown_timeout=10,
# Because we strip the email/username from the Sentry data
# above, the effect of this flag is that the requests/users
# involved in exceptions will be identified in Sentry only by
# their IP address, user ID, realm, and role. We consider
# this an appropriate balance between avoiding Sentry getting
# PII while having the identifiers needed to determine that an
# exception only affects a small subset of users or realms.
send_default_pii=True,
)
# Ignore all of the loggers from django.security that are for user
# errors; see https://docs.djangoproject.com/en/3.2/ref/exceptions/#suspiciousoperation
ignore_logger("django.security.SuspiciousOperation")
ignore_logger("django.security.DisallowedHost")
ignore_logger("django.security.DisallowedModelAdminLookup")
ignore_logger("django.security.DisallowedModelAdminToField")
ignore_logger("django.security.DisallowedRedirect")
ignore_logger("django.security.InvalidSessionKey")
ignore_logger("django.security.RequestDataTooBig")
ignore_logger("django.security.SuspiciousFileOperation")
ignore_logger("django.security.SuspiciousMultipartForm")
ignore_logger("django.security.SuspiciousSession")
ignore_logger("django.security.TooManyFieldsSent")