Coverage for app/venv/lib/python3.14/site-packages/weblate/utils/errors.py: 53%
67 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 07:15 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-10-07 07:15 +0000
1# Copyright © Michal Čihař <michal@weblate.org>
2#
3# SPDX-License-Identifier: GPL-3.0-or-later
4from __future__ import annotations
6import logging
7import sys
8from json import JSONDecodeError
9from typing import TYPE_CHECKING, Literal
11import sentry_sdk
12from django.conf import settings
13from django.utils.translation import get_language
14from sentry_sdk.integrations.celery import CeleryIntegration
15from sentry_sdk.integrations.django import DjangoIntegration
16from sentry_sdk.integrations.logging import ignore_logger
17from sentry_sdk.integrations.redis import RedisIntegration
19import weblate.utils.version
21if TYPE_CHECKING: 21 ↛ 22line 21 didn't jump to line 22 because the condition on line 21 was never true
22 from weblate.auth.models import AuthenticatedHttpRequest
24ERROR_LOGGER = "weblate.errors"
26LOGGER = logging.getLogger(ERROR_LOGGER)
28try:
29 import rollbar
31 HAS_ROLLBAR = True
32except ImportError:
33 HAS_ROLLBAR = False
36def report_error(
37 cause: str,
38 *,
39 level: Literal[
40 "fatal", "critical", "error", "warning", "info", "debug"
41 ] = "warning",
42 skip_sentry: bool = False,
43 print_tb: bool = False,
44 extra_log: str | None = None,
45 project=None,
46 message: bool = False,
47) -> None:
48 """
49 Report errors.
51 This can be used for store exceptions in error reporting solutions as rollbar while
52 handling error gracefully and giving user cleaner message.
53 """
54 __traceback_hide__ = True # noqa: F841
55 if HAS_ROLLBAR and hasattr(settings, "ROLLBAR"): 55 ↛ 56line 55 didn't jump to line 56 because the condition on line 55 was never true
56 rollbar.report_exc_info(level=level)
58 if not skip_sentry and settings.SENTRY_DSN: 58 ↛ 59line 58 didn't jump to line 59 because the condition on line 58 was never true
59 sentry_sdk.set_tag("cause", cause)
60 if project is not None:
61 sentry_sdk.set_tag("project", project.slug)
62 sentry_sdk.set_tag("user.locale", get_language())
63 sentry_sdk.set_level(level)
64 if message:
65 sentry_sdk.capture_message(cause)
66 else:
67 sentry_sdk.capture_exception()
69 log = getattr(LOGGER, level)
71 error = sys.exc_info()[1]
73 # Include JSON document if available. It might be missing
74 # when the error is raised from requests.
75 if isinstance(error, JSONDecodeError) and not extra_log and hasattr(error, "doc"): 75 ↛ 76line 75 didn't jump to line 76 because the condition on line 75 was never true
76 extra_log = repr(error.doc)
78 if error: 78 ↛ 80line 78 didn't jump to line 80 because the condition on line 78 was always true
79 log("%s: %s: %s", cause, error.__class__.__name__, error)
80 if extra_log: 80 ↛ 81line 80 didn't jump to line 81 because the condition on line 80 was never true
81 if error:
82 log("%s: %s: %s", cause, error.__class__.__name__, extra_log)
83 else:
84 log("%s: %s", cause, extra_log)
85 if print_tb: 85 ↛ 87line 85 didn't jump to line 87 because the condition on line 85 was never true
86 # This is called from an exception handler
87 LOGGER.exception(cause) # noqa: LOG004
90def add_breadcrumb(category: str, message: str, level: str = "info", **data) -> None:
91 # Add breadcrumb only if settings are already loaded,
92 # we do not want to force loading settings early
93 if not settings.configured or not getattr(settings, "SENTRY_DSN", None): 93 ↛ 95line 93 didn't jump to line 95 because the condition on line 93 was always true
94 return
95 sentry_sdk.add_breadcrumb(
96 category=category, message=message, level=level, data=data
97 )
100def celery_base_data_hook(request: AuthenticatedHttpRequest, data) -> None:
101 data["framework"] = "celery"
104def init_sentry() -> None:
105 integrations = [
106 CeleryIntegration(monitor_beat_tasks=settings.SENTRY_MONITOR_BEAT_TASKS),
107 DjangoIntegration(),
108 RedisIntegration(),
109 ]
111 sentry_sdk.init(
112 dsn=settings.SENTRY_DSN,
113 integrations=integrations,
114 auto_enabling_integrations=False,
115 send_default_pii=settings.SENTRY_SEND_PII,
116 release=weblate.utils.version.GIT_REVISION or weblate.utils.version.TAG_NAME,
117 environment=settings.SENTRY_ENVIRONMENT,
118 traces_sample_rate=settings.SENTRY_TRACES_SAMPLE_RATE,
119 profiles_sample_rate=settings.SENTRY_PROFILES_SAMPLE_RATE,
120 in_app_include=[
121 "weblate",
122 "wlhosted",
123 "wllegal",
124 "weblate_fedora_messaging",
125 "weblate_language_data",
126 "translate",
127 "translation_finder",
128 ],
129 attach_stacktrace=True,
130 _experiments={"max_spans": 2000},
131 keep_alive=True,
132 **settings.SENTRY_EXTRA_ARGS,
133 )
134 # Ignore Weblate logging, those should trigger proper errors
135 ignore_logger("weblate")
136 ignore_logger("weblate.*")
139def init_rollbar() -> None:
140 rollbar.init(**settings.ROLLBAR)
141 rollbar.BASE_DATA_HOOK = celery_base_data_hook
142 LOGGER.info("configured Rollbar error collection")
145def init_error_collection(celery=False) -> None:
146 if settings.SENTRY_DSN: 146 ↛ 147line 146 didn't jump to line 147 because the condition on line 146 was never true
147 init_sentry()
149 if celery and HAS_ROLLBAR and hasattr(settings, "ROLLBAR"): 149 ↛ 150line 149 didn't jump to line 150 because the condition on line 149 was never true
150 init_rollbar()