From c2e4803ee80339b823b8a40961007ab67e7f7383 Mon Sep 17 00:00:00 2001 From: ForroKulcs <65923725+ForroKulcs@users.noreply.github.com> Date: Mon, 7 Dec 2020 08:24:00 +0100 Subject: [PATCH 1/4] Support for level parameter in constructor --- bugsnag/handlers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bugsnag/handlers.py b/bugsnag/handlers.py index ded43a19..1e565926 100644 --- a/bugsnag/handlers.py +++ b/bugsnag/handlers.py @@ -9,11 +9,11 @@ class BugsnagHandler(logging.Handler, object): - def __init__(self, client=None, extra_fields=None): + def __init__(self, level=logging.NOTSET, client=None, extra_fields=None): """ Creates a new handler which sends records to Bugsnag """ - super(BugsnagHandler, self).__init__() + super(BugsnagHandler, self).__init__(level) self.client = client self.custom_metadata_fields = extra_fields self.callbacks = [self.extract_default_metadata, From e9872186b58a96cd37cd929ad6e53cfcbe862602 Mon Sep 17 00:00:00 2001 From: SB-jigneshR Date: Wed, 20 May 2026 18:35:39 +0530 Subject: [PATCH 2/4] PLAT-5615 level parameter fixes --- CHANGELOG.md | 9 +++++ bugsnag/client.py | 10 ++++-- bugsnag/handlers.py | 6 ++-- tests/test_handlers.py | 80 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 99 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fda530e..cec8c42e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,15 @@ Changelog ========= +## TBD (Unreleased) + +### Enhancements + +* Add support for `level` parameter in `BugsnagHandler` constructor and `Client.log_handler()` method. + Allows setting the minimum logging level during handler initialization. + Fully backward compatible - existing code continues to work unchanged. + [#249](https://github.com/bugsnag/bugsnag-python/pull/249) + ## v4.9.0 (2026-04-21) ### Enhancements diff --git a/bugsnag/client.py b/bugsnag/client.py index 9717ce9a..93049ea3 100644 --- a/bugsnag/client.py +++ b/bugsnag/client.py @@ -1,4 +1,5 @@ import builtins +import logging import sys import threading import warnings @@ -234,9 +235,12 @@ def should_deliver(self, event: Event) -> bool: def log_handler( self, - extra_fields: Optional[List[str]] = None + extra_fields: Optional[List[str]] = None, + level: int = logging.NOTSET ) -> BugsnagHandler: - return BugsnagHandler(client=self, extra_fields=extra_fields) + return BugsnagHandler( + client=self, extra_fields=extra_fields, level=level + ) @property def feature_flags(self) -> List[FeatureFlag]: @@ -535,4 +539,4 @@ def __exit__(self, *exc_info): class LambdaTimeoutApproaching(Exception): def __init__(self, remaining_ms: int, tb): super().__init__('Lambda will timeout in %dms' % remaining_ms) - self.__traceback__ = tb + self.__traceback__ = tb \ No newline at end of file diff --git a/bugsnag/handlers.py b/bugsnag/handlers.py index 1e565926..0be68635 100644 --- a/bugsnag/handlers.py +++ b/bugsnag/handlers.py @@ -9,11 +9,11 @@ class BugsnagHandler(logging.Handler, object): - def __init__(self, level=logging.NOTSET, client=None, extra_fields=None): + def __init__(self, client=None, extra_fields=None, level=logging.NOTSET): """ Creates a new handler which sends records to Bugsnag """ - super(BugsnagHandler, self).__init__(level) + super(BugsnagHandler, self).__init__(level=level) self.client = client self.custom_metadata_fields = extra_fields self.callbacks = [self.extract_default_metadata, @@ -184,4 +184,4 @@ def leave_breadcrumbs(self, record): BreadcrumbType.LOG ) - return True + return True \ No newline at end of file diff --git a/tests/test_handlers.py b/tests/test_handlers.py index ed385648..2da4f075 100644 --- a/tests/test_handlers.py +++ b/tests/test_handlers.py @@ -689,3 +689,83 @@ def test_log_filter_leaves_breadcrumbs_when_handler_has_no_level(self): assert breadcrumb.message == 'Everything is not fine' assert breadcrumb.type == BreadcrumbType.LOG assert breadcrumb.metadata == {'logLevel': 'ERROR'} + + def test_handler_level_via_constructor(self): + """Test setting level via constructor parameter""" + handler = BugsnagHandler(level=logging.ERROR) + logger = logging.getLogger(__name__) + logger.setLevel(logging.DEBUG) + logger.addHandler(handler) + + # Should NOT report (below handler level) + logger.warning('This is just a warning') + self.assertSentReportCount(0) + + # Should report (at handler level) + logger.error('This is an error') + logger.removeHandler(handler) + self.assertSentReportCount(1) + + json_body = self.server.events_received[0]['json_body'] + event = json_body['events'][0] + exception = event['exceptions'][0] + self.assertEqual('LogERROR', exception['errorClass']) + + def test_handler_level_default_notset(self): + """Test default level is NOTSET""" + handler = BugsnagHandler() + self.assertEqual(handler.level, logging.NOTSET) + + def test_handler_level_with_client_and_extra_fields(self): + """Test level parameter works with other parameters""" + client = Client( + api_key='abcdef', + endpoint=self.server.events_url, + session_endpoint=self.server.sessions_url, + asynchronous=False + ) + handler = BugsnagHandler( + client=client, + extra_fields={'fruit': ['grapes']}, + level=logging.WARNING + ) + self.assertEqual(handler.level, logging.WARNING) + self.assertEqual(handler.client, client) + self.assertEqual(handler.custom_metadata_fields, {'fruit': ['grapes']}) + + def test_client_log_handler_with_level(self): + """Test client.log_handler() with level parameter""" + client = Client( + api_key='abcdef', + endpoint=self.server.events_url, + session_endpoint=self.server.sessions_url, + asynchronous=False + ) + + with scoped_logger() as logger: + handler = client.log_handler(level=logging.ERROR) + logger.setLevel(logging.DEBUG) + logger.addHandler(handler) + + logger.warning('Warning message') + self.assertSentReportCount(0) + + logger.error('Error message') + self.assertSentReportCount(1) + + json_body = self.server.events_received[0]['json_body'] + event = json_body['events'][0] + exception = event['exceptions'][0] + self.assertEqual('LogERROR', exception['errorClass']) + + def test_backward_compatibility_no_level(self): + """Ensure backward compatibility when level not specified""" + # All these should work without breaking + h1 = BugsnagHandler() + h2 = BugsnagHandler(client=None) + h3 = BugsnagHandler(extra_fields={'test': ['field']}) + h4 = BugsnagHandler(client=None, extra_fields={'test': ['field']}) + + # All should have default NOTSET level + for handler in [h1, h2, h3, h4]: + self.assertEqual(handler.level, logging.NOTSET) \ No newline at end of file From 40ddd588532fd03387786ed1d71a6fb35859d67d Mon Sep 17 00:00:00 2001 From: SB-jigneshR Date: Thu, 21 May 2026 19:03:11 +0530 Subject: [PATCH 3/4] PLAT-5615 lint issue fixes --- bugsnag/client.py | 2 +- bugsnag/handlers.py | 2 +- tests/test_handlers.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bugsnag/client.py b/bugsnag/client.py index 93049ea3..258ae908 100644 --- a/bugsnag/client.py +++ b/bugsnag/client.py @@ -539,4 +539,4 @@ def __exit__(self, *exc_info): class LambdaTimeoutApproaching(Exception): def __init__(self, remaining_ms: int, tb): super().__init__('Lambda will timeout in %dms' % remaining_ms) - self.__traceback__ = tb \ No newline at end of file + self.__traceback__ = tb diff --git a/bugsnag/handlers.py b/bugsnag/handlers.py index 0be68635..f741dfab 100644 --- a/bugsnag/handlers.py +++ b/bugsnag/handlers.py @@ -184,4 +184,4 @@ def leave_breadcrumbs(self, record): BreadcrumbType.LOG ) - return True \ No newline at end of file + return True diff --git a/tests/test_handlers.py b/tests/test_handlers.py index 2da4f075..1460cd5a 100644 --- a/tests/test_handlers.py +++ b/tests/test_handlers.py @@ -768,4 +768,4 @@ def test_backward_compatibility_no_level(self): # All should have default NOTSET level for handler in [h1, h2, h3, h4]: - self.assertEqual(handler.level, logging.NOTSET) \ No newline at end of file + self.assertEqual(handler.level, logging.NOTSET) From b2d40f3751786b0de7bbbbeb7876f8f6f76dd138 Mon Sep 17 00:00:00 2001 From: SB-jigneshR Date: Fri, 22 May 2026 17:12:01 +0530 Subject: [PATCH 4/4] PLAT-5615 minor issue fixes --- bugsnag/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bugsnag/client.py b/bugsnag/client.py index 258ae908..00dba65d 100644 --- a/bugsnag/client.py +++ b/bugsnag/client.py @@ -235,7 +235,7 @@ def should_deliver(self, event: Event) -> bool: def log_handler( self, - extra_fields: Optional[List[str]] = None, + extra_fields: Optional[Dict[str, List[str]]] = None, level: int = logging.NOTSET ) -> BugsnagHandler: return BugsnagHandler(