mirror of
https://github.com/odoo/runbot.git
synced 2025-03-15 23:45:44 +07:00

Breakages: - the entire http.py API was updated requiring fixing the uses of `request.jsonrequest` and the patches to `WebRequest` to hook in sentry - `fontawesome` was moved - `*[@groups]` are now completely removed from the view if not matching, so any field inside of them which needs to be used outside (e.g. attrs) has to be added as invisible outside the element - discuss removed the mail tracking value helpers from RPC in odoo/odoo#88547, so reimplement locally (and better)
118 lines
4.3 KiB
Python
118 lines
4.3 KiB
Python
import logging
|
|
from os import environ
|
|
|
|
import sentry_sdk
|
|
from sentry_sdk.integrations.logging import LoggingIntegration
|
|
from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware
|
|
|
|
from odoo import http
|
|
from odoo.addons.base.models.ir_cron import ir_cron
|
|
from odoo.http import HttpDispatcher, JsonRPCDispatcher
|
|
|
|
from .exceptions import FastForwardError, MergeError, Unmergeable
|
|
|
|
|
|
def delegate(self, attr):
|
|
return getattr(self.app, attr)
|
|
SentryWsgiMiddleware.__getattr__ = delegate
|
|
|
|
def enable_sentry():
|
|
logger = logging.getLogger('runbot_merge')
|
|
|
|
dsn = environ.get('SENTRY_DSN')
|
|
if not dsn:
|
|
logger.info("No DSN found, skipping sentry...")
|
|
return
|
|
|
|
try:
|
|
setup_sentry(dsn)
|
|
except Exception:
|
|
logger.exception("DSN found, failed to enable sentry...")
|
|
else:
|
|
logger.info("DSN found, sentry enabled...")
|
|
|
|
|
|
def setup_sentry(dsn):
|
|
sentry_sdk.init(
|
|
dsn,
|
|
auto_session_tracking=False,
|
|
# traces_sample_rate=1.0,
|
|
integrations=[
|
|
# note: if the colorformatter is enabled, sentry gets lost
|
|
# and classifies everything as errors because it fails to
|
|
# properly classify levels as the colorformatter injects
|
|
# the ANSI color codes right into LogRecord.levelname
|
|
LoggingIntegration(level=logging.INFO, event_level=logging.WARNING),
|
|
],
|
|
before_send=event_filter,
|
|
# apparently not in my version of the sdk
|
|
# functions_to_trace = []
|
|
)
|
|
http.root = SentryWsgiMiddleware(http.root)
|
|
instrument_odoo()
|
|
|
|
def instrument_odoo():
|
|
"""Monkeypatches odoo core to copy odoo metadata into sentry for more
|
|
informative events
|
|
"""
|
|
# add user to wsgi request context
|
|
for d in [HttpDispatcher, JsonRPCDispatcher]:
|
|
def dispatch(self, endpoint, args, old_dispatch=d.dispatch):
|
|
if self.request.uid:
|
|
sentry_sdk.set_user({
|
|
'id': self.request.uid,
|
|
'email': self.request.env.user.email,
|
|
'username': self.request.env.user.login,
|
|
})
|
|
else:
|
|
sentry_sdk.set_user({'username': '<public>'})
|
|
return old_dispatch(self, endpoint, args)
|
|
d.dispatch = dispatch
|
|
|
|
# create transaction for tracking crons, add user to that
|
|
old_callback = ir_cron._callback
|
|
def _callback(self, cron_name, server_action_id, job_id):
|
|
sentry_sdk.start_transaction(name=f"cron {cron_name}")
|
|
sentry_sdk.set_user({
|
|
'id': self.env.user.id,
|
|
'email': self.env.user.email,
|
|
'username': self.env.user.login,
|
|
})
|
|
return old_callback(self, cron_name, server_action_id, job_id)
|
|
ir_cron._callback = _callback
|
|
|
|
dummy_record = logging.LogRecord(name="", level=logging.NOTSET, pathname='', lineno=0, msg='', args=(), exc_info=None)
|
|
# mapping of exception types to predicates, if the predicate returns `True` the
|
|
# exception event should be suppressed
|
|
SUPPRESS_EXCEPTION = {
|
|
# Someone else deciding to push directly to the branch (which is generally
|
|
# what leads to this error) is not really actionable.
|
|
#
|
|
# Other possibilities are more structural and thus we probably want to know:
|
|
# - other 422 Unprocessable github errors (likely config issues):
|
|
# - reference does not exist
|
|
# - object does not exist
|
|
# - object is not a commit
|
|
# - branch protection issue
|
|
# - timeout on ref update (github probably dying)
|
|
# - other HTTP error (also github probably dying)
|
|
#
|
|
# might be worth using richer exceptions to make this clearer, and easier to classify
|
|
FastForwardError: lambda e: 'not a fast forward' in str(e.__cause__),
|
|
# Git conflict when merging (or non-json response which is weird),
|
|
# notified on PR
|
|
MergeError: lambda _: True,
|
|
# Failed preconditions on merging, notified on PR
|
|
Unmergeable: lambda _: True,
|
|
}
|
|
def event_filter(event, hint):
|
|
# event['level'], event['logger'], event['logentry'], event['exception']
|
|
# known hints: log_record: LogRecord, exc_info: (type, BaseExeption, Traceback) | None
|
|
exc_info = hint.get('exc_info') or hint.get('log_record', dummy_record).exc_info
|
|
if exc_info:
|
|
etype, exc, _ = exc_info
|
|
if SUPPRESS_EXCEPTION.get(etype, lambda _: False)(exc):
|
|
return None
|
|
|
|
|