Skip to content

Commit

Permalink
Merge PR #2888 into 14.0
Browse files Browse the repository at this point in the history
Signed-off-by moylop260
  • Loading branch information
OCA-git-bot committed Sep 13, 2024
2 parents 989863f + f3dd4d9 commit 2f52078
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 21 deletions.
6 changes: 5 additions & 1 deletion sentry/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,11 @@ def get_sentry_logging(level=DEFAULT_LOG_LEVEL):
if level not in LOG_LEVEL_MAP:
level = DEFAULT_LOG_LEVEL

return LoggingIntegration(level=LOG_LEVEL_MAP[level], event_level=logging.WARNING)
return LoggingIntegration(
# Gather warnings into breadcrumbs regardless of actual logging level
level=logging.WARNING,
event_level=LOG_LEVEL_MAP[level],
)


def get_sentry_options():
Expand Down
151 changes: 131 additions & 20 deletions sentry/tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from odoo.tests import TransactionCase
from odoo.tools import config

from ..const import to_int_if_defined
from ..hooks import initialize_sentry

GIT_SHA = "d670460b4b4aece5915caf5c68d12f560a9fe3e4"
Expand Down Expand Up @@ -56,22 +57,49 @@ def kill(self, *args, **kwargs):
pass


class TestClientSetup(TransactionCase):
class NoopHandler(logging.Handler):
"""
A Handler subclass that does nothing with any given log record.
Sentry's log patching works by having the integration process things after
the normal log handlers are run, so we use this handler to do nothing and
move to Sentry logic ASAP.
"""

def emit(self, record):
pass


class TestSentryCommon(TransactionCase):
def setUp(self):
super(TestClientSetup, self).setUp()
super().setUp()
self.dsn = "http://public:[email protected]/1"

def assertEventCaptured(self, client, event_level, event_msg):
self.assertTrue(
client.transport.has_event(event_level, event_msg),
msg='Event: "%s" was not captured' % event_msg,
)

def assertEventNotCaptured(self, client, event_level, event_msg):
self.assertFalse(
client.transport.has_event(event_level, event_msg),
msg='Event: "%s" was captured' % event_msg,
)


class TestClientSetupStartup(TestSentryCommon):
def setUp(self):
super().setUp()
config.options["sentry_enabled"] = True
config.options["sentry_dsn"] = self.dsn
config.options["sentry_logging_level"] = "info"
with patch(
"odoo.addons.sentry.const.select_transport", return_value=InMemoryTransport
):
self.client = initialize_sentry(config)._client
self.handler = self.client.integrations["logging"]._handler

def log(self, level, msg, exc_info=None):
record = logging.LogRecord(__name__, level, __file__, 42, msg, (), exc_info)
self.handler.emit(record)

def assertEventCaptured(self, client, event_level, event_msg):
self.assertTrue(
client.transport.has_event(event_level, event_msg),
Expand All @@ -97,53 +125,132 @@ def test_startup_event_disabled(self):
self.client = initialize_sentry(config)._client
self.assertEventNotCaptured(self.client, "info", "Starting Odoo Server")

def test_startup_event_disabled_if_warning_level(self):
config.options["sentry_enabled"] = True
config.options["sentry_dsn"] = self.dsn
config.options["sentry_ignore_startup_event"] = True
config.options["sentry_logging_level"] = "warning"
with patch(
"odoo.addons.sentry.const.select_transport", return_value=InMemoryTransport
):
self.client = initialize_sentry(config)._client
self.assertEventNotCaptured(self.client, "info", "Starting Odoo Server")


class TestClientSetup(TestSentryCommon):
def setUp(self):
super().setUp()
self.patch_config(
{
"sentry_enabled": True,
"sentry_dsn": self.dsn,
"sentry_logging_level": "error",
}
)
self.client = initialize_sentry(config)._client
self.client.transport = InMemoryTransport({"dsn": self.dsn})

# Setup our own logger so we don't flood stderr with error logs
self.logger = logging.getLogger("odoo.sentry.test.logger")
# Do not mutate list while iterating it
handlers = [handler for handler in self.logger.handlers]
for handler in handlers:
self.logger.removeHandler(handler)
self.logger.addHandler(NoopHandler())
self.logger.propagate = False

def patch_config(self, options: dict):
"""
Patch Odoo's config with the given `options`, ensuring that the patch
is undone when the test completes.
"""
_config_patcher = patch.dict(
in_dict=config.options,
values=options,
)
_config_patcher.start()
self.addCleanup(_config_patcher.stop)

def log(self, level, msg, exc_info=None):
self.logger.log(level, msg, exc_info=exc_info)

def test_initialize_raven_sets_dsn(self):
self.assertEqual(self.client.dsn, self.dsn)

def test_capture_event(self):
def test_ignore_low_level_event(self):
level, msg = logging.WARNING, "Test event, can be ignored"
self.log(level, msg)
level = "warning"
self.assertEventNotCaptured(self.client, level, msg)

def test_capture_event(self):
level, msg = logging.ERROR, "Test event, should be captured"
self.log(level, msg)
level = "error"
self.assertEventCaptured(self.client, level, msg)

def test_capture_event_exc(self):
level, msg = logging.WARNING, "Test event, can be ignored exception"
level, msg = logging.ERROR, "Test event, can be ignored exception"
try:
raise TestException(msg)
except TestException:
exc_info = sys.exc_info()
self.log(level, msg, exc_info)
level = "warning"
level = "error"
self.assertEventCaptured(self.client, level, msg)

def test_ignore_exceptions(self):
config.options["sentry_ignore_exceptions"] = "odoo.exceptions.UserError"
self.patch_config(
{
"sentry_ignore_exceptions": "odoo.exceptions.UserError",
}
)
client = initialize_sentry(config)._client
client.transport = InMemoryTransport({"dsn": self.dsn})
level, msg = logging.WARNING, "Test exception"
level, msg = logging.ERROR, "Test exception"
try:
raise exceptions.UserError(msg)
except exceptions.UserError:
exc_info = sys.exc_info()
self.log(level, msg, exc_info)
level = "warning"
level = "error"
self.assertEventNotCaptured(client, level, msg)

def test_exclude_logger(self):
config.options["sentry_enabled"] = True
config.options["sentry_exclude_loggers"] = __name__
self.patch_config(
{
"sentry_enabled": True,
"sentry_exclude_loggers": self.logger.name,
}
)
client = initialize_sentry(config)._client
client.transport = InMemoryTransport({"dsn": self.dsn})
level, msg = logging.WARNING, "Test exclude logger %s" % __name__
level, msg = logging.ERROR, "Test exclude logger %s" % __name__
self.log(level, msg)
level = "warning"
level = "error"
# Revert ignored logger so it doesn't affect other tests
remove_handler_ignore(__name__)
remove_handler_ignore(self.logger.name)
self.assertEventNotCaptured(client, level, msg)

def test_invalid_logging_level(self):
self.patch_config(
{
"sentry_logging_level": "foo_bar",
}
)
client = initialize_sentry(config)._client
client.transport = InMemoryTransport({"dsn": self.dsn})
level, msg = logging.WARNING, "Test we use the default"
self.log(level, msg)
level = "warning"
self.assertEventCaptured(client, level, msg)

def test_undefined_to_int(self):
self.assertIsNone(to_int_if_defined(""))

@patch("odoo.addons.sentry.hooks.get_odoo_commit", return_value=GIT_SHA)
def test_config_odoo_dir(self, get_odoo_commit):
config.options["sentry_odoo_dir"] = "/opt/odoo/core"
self.patch_config({"sentry_odoo_dir": "/opt/odoo/core"})
client = initialize_sentry(config)._client

self.assertEqual(
Expand All @@ -154,8 +261,12 @@ def test_config_odoo_dir(self, get_odoo_commit):

@patch("odoo.addons.sentry.hooks.get_odoo_commit", return_value=GIT_SHA)
def test_config_release(self, get_odoo_commit):
config.options["sentry_odoo_dir"] = "/opt/odoo/core"
config.options["sentry_release"] = RELEASE
self.patch_config(
{
"sentry_odoo_dir": "/opt/odoo/core",
"sentry_release": RELEASE,
}
)
client = initialize_sentry(config)._client

self.assertEqual(
Expand Down

0 comments on commit 2f52078

Please sign in to comment.