diff --git a/CHANGELOG.md b/CHANGELOG.md index de416556..3495a5ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,10 @@ Changelog ### Enhancements -* Remove depricated `datetime.utcnow()` method call from utils class +* Remove deprecated `datetime.utcnow()` method call from utils class [#394](https://github.com/bugsnag/bugsnag-python/pull/394). +* Set default endpoints based on API key + [#399](https://github.com/bugsnag/bugsnag-php/pull/399) ## v4.7.1 (2024-05-22) diff --git a/bugsnag/client.py b/bugsnag/client.py index 558b074a..9717ce9a 100644 --- a/bugsnag/client.py +++ b/bugsnag/client.py @@ -32,10 +32,11 @@ class Client: """ def __init__(self, configuration: Optional[Configuration] = None, - install_sys_hook=True, **kwargs): + install_sys_hook=True, configure=True, **kwargs): self.configuration = configuration or Configuration() # type: Configuration # noqa: E501 self.session_tracker = SessionTracker(self.configuration) - self.configuration.configure(**kwargs) + if configure: + self.configuration.configure(**kwargs) self._context = ContextLocalState(self) self._request_tracker = RequestTracker() diff --git a/bugsnag/configuration.py b/bugsnag/configuration.py index 114de35b..20125937 100644 --- a/bugsnag/configuration.py +++ b/bugsnag/configuration.py @@ -30,8 +30,11 @@ validate_int_setter, validate_path_setter ) -from bugsnag.delivery import (create_default_delivery, DEFAULT_ENDPOINT, - DEFAULT_SESSIONS_ENDPOINT) +from bugsnag.delivery import (create_default_delivery, + DEFAULT_ENDPOINT, + DEFAULT_SESSIONS_ENDPOINT, + HUB_ENDPOINT, + HUB_SESSIONS_ENDPOINT) from bugsnag.uwsgi import warn_if_running_uwsgi_without_threads from bugsnag.error import Error @@ -55,6 +58,11 @@ _sentinel = object() +def _is_hub_api_key(api_key: str) -> bool: + hub_prefix = "00000" + return api_key is not None and api_key.startswith(hub_prefix) + + class Configuration: """ Global app-level Bugsnag configuration settings. @@ -83,8 +91,8 @@ def __init__(self, logger=_sentinel): "django.http.Http404", "django.http.response.Http404", ] - self.endpoint = DEFAULT_ENDPOINT - self.session_endpoint = DEFAULT_SESSIONS_ENDPOINT + self.endpoint = None + self.session_endpoint = None self.auto_capture_sessions = True self.traceback_exclude_modules = [] @@ -126,8 +134,6 @@ def configure(self, api_key=None, app_type=None, app_version=None, Validate and set configuration options. Will warn if an option is of an incorrect type. """ - if api_key is not None: - self.api_key = api_key if app_type is not None: self.app_type = app_type if app_version is not None: @@ -140,8 +146,6 @@ def configure(self, api_key=None, app_type=None, app_version=None, self.auto_capture_sessions = auto_capture_sessions if delivery is not None: self.delivery = delivery - if endpoint is not None: - self.endpoint = endpoint if hostname is not None: self.hostname = hostname if ignore_classes is not None: @@ -162,8 +166,6 @@ def configure(self, api_key=None, app_type=None, app_version=None, self.send_code = send_code if send_environment is not None: self.send_environment = send_environment - if session_endpoint is not None: - self.session_endpoint = session_endpoint if traceback_exclude_modules is not None: self.traceback_exclude_modules = traceback_exclude_modules if logger is not _sentinel: @@ -175,6 +177,11 @@ def configure(self, api_key=None, app_type=None, app_version=None, if max_breadcrumbs is not None: self.max_breadcrumbs = max_breadcrumbs + # Default endpoints depend on the API key + if api_key is not None: + self.api_key = api_key + self._initialize_endpoints(endpoint, session_endpoint, self.api_key) + return self def get(self, name): @@ -584,6 +591,26 @@ def _create_null_logger(self) -> logging.Logger: return logger + def _initialize_endpoints(self, endpoint, session_endpoint, api_key): + # Default endpoints depending on the API key, if not already set + if ( + endpoint is None and + session_endpoint is None and + self.endpoint is None and + self.session_endpoint is None + ): + if _is_hub_api_key(api_key): + self.endpoint = HUB_ENDPOINT + self.session_endpoint = HUB_SESSIONS_ENDPOINT + else: + self.endpoint = DEFAULT_ENDPOINT + self.session_endpoint = DEFAULT_SESSIONS_ENDPOINT + # Do set endpoints if explicitly provided + if endpoint is not None: + self.endpoint = endpoint + if session_endpoint is not None: + self.session_endpoint = session_endpoint + class RequestConfiguration: """ diff --git a/bugsnag/delivery.py b/bugsnag/delivery.py index 6dc8cd05..1a8f03cb 100644 --- a/bugsnag/delivery.py +++ b/bugsnag/delivery.py @@ -28,6 +28,8 @@ DEFAULT_ENDPOINT = 'https://notify.bugsnag.com' DEFAULT_SESSIONS_ENDPOINT = 'https://sessions.bugsnag.com' +HUB_ENDPOINT = 'https://notify.insighthub.smartbear.com' +HUB_SESSIONS_ENDPOINT = 'https://sessions.insighthub.smartbear.com' __all__ = ('default_headers', 'Delivery') @@ -82,8 +84,10 @@ def deliver_sessions(self, config, payload: Any, options=None): """ Sends sessions to Bugsnag """ - if (config.endpoint != DEFAULT_ENDPOINT and config.session_endpoint == - DEFAULT_SESSIONS_ENDPOINT): + if ((config.endpoint != DEFAULT_ENDPOINT and + config.session_endpoint == DEFAULT_SESSIONS_ENDPOINT) or + (config.endpoint != HUB_ENDPOINT and + config.session_endpoint == HUB_SESSIONS_ENDPOINT)): if not self.sent_session_warning: warnings.warn('The session endpoint has not been configured. ' 'No sessions will be sent to Bugsnag.') diff --git a/bugsnag/legacy.py b/bugsnag/legacy.py index 018e6735..32848b52 100644 --- a/bugsnag/legacy.py +++ b/bugsnag/legacy.py @@ -7,7 +7,7 @@ from bugsnag.configuration import RequestConfiguration from bugsnag.client import Client -default_client = Client() +default_client = Client(configure=False) configuration = default_client.configuration logger = configuration.logger ExcInfoType = Tuple[Type, Exception, types.TracebackType] diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 03266940..57dfe03e 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -83,7 +83,7 @@ def test_hostname(self): def test_session_tracking_defaults(self): c = Configuration() self.assertTrue(c.auto_capture_sessions) - self.assertEqual(c.session_endpoint, "https://sessions.bugsnag.com") + self.assertEqual(c.session_endpoint, None) def test_default_middleware_location(self): c = Configuration() @@ -114,6 +114,16 @@ def test_validate_endpoint(self): c.configure(endpoint='https://notify.example.com') assert c.endpoint == 'https://notify.example.com' + def test_validate_endpoint_bugsnag_api_key(self): + c = Configuration() + c.configure(api_key='12312312312312312312312312321312') + assert c.endpoint == 'https://notify.bugsnag.com' + + def test_validate_endpoint_hub_api_key(self): + c = Configuration() + c.configure(api_key='00000312312312312312312312321312') + assert c.endpoint == 'https://notify.insighthub.smartbear.com' + def test_validate_app_type(self): c = Configuration() assert c.app_type is None @@ -410,6 +420,17 @@ def test_validate_session_endpoint(self): c.configure(session_endpoint='https://sessions.example.com') assert c.session_endpoint == 'https://sessions.example.com' + def test_validate_session_endpoint_bugsnag_api_key(self): + c = Configuration() + c.configure(api_key='12312312312312312312312312321312') + assert c.session_endpoint == 'https://sessions.bugsnag.com' + + def test_validate_session_endpoint_hub_api_key(self): + c = Configuration() + c.configure(api_key='00000312312312312312312312321312') + assert (c.session_endpoint == + 'https://sessions.insighthub.smartbear.com') + def test_validate_traceback_exclude_modules(self): c = Configuration() with pytest.warns(RuntimeWarning) as record: