diff --git a/CHANGELOG.md b/CHANGELOG.md index 0154f92..308764e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ CHANGELOG](http://keepachangelog.com/) for how to update this file. This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +- Feat: honeybadger.event() for sending events to Honeybadger Insights ## [0.19.1] - 2024-04-07 diff --git a/honeybadger/connection.py b/honeybadger/connection.py index f197863..4251765 100644 --- a/honeybadger/connection.py +++ b/honeybadger/connection.py @@ -9,16 +9,14 @@ logger = logging.getLogger(__name__) - -def send_notice(config, payload): - notice_id = payload.get("error", {}).get("token", None) - request_object = request.Request(url="{}/v1/notices/".format(config.endpoint), - data=b(json.dumps(payload, cls=StringReprJSONEncoder))) +def _make_http_request(path, config, payload): if not config.api_key: logger.error("Honeybadger API key missing from configuration: cannot report errors.") return - + + request_object = request.Request(url=config.endpoint + path, + data=b(json.dumps(payload, cls=StringReprJSONEncoder))) request_object.add_header('X-Api-Key', config.api_key) request_object.add_header('Content-Type', 'application/json') request_object.add_header('Accept', 'application/json') @@ -32,9 +30,18 @@ def send_request(): if config.force_sync: send_request() - else: t = threading.Thread(target=send_request) t.start() + +def send_notice(config, payload): + notice_id = payload.get("error", {}).get("token", None) + path = '/v1/notices/' + _make_http_request(path, config, payload) return notice_id + + +def send_event(config, payload): + path = '/v1/events/' + return _make_http_request(path, config, payload) diff --git a/honeybadger/core.py b/honeybadger/core.py index 71f8aea..8936a18 100644 --- a/honeybadger/core.py +++ b/honeybadger/core.py @@ -3,6 +3,7 @@ import sys import logging import copy +import time from honeybadger.plugins import default_plugin_manager import honeybadger.connection as connection @@ -25,6 +26,12 @@ def _send_notice(self, exception, exc_traceback=None, context=None, fingerprint= return fake_connection.send_notice(self.config, payload) else: return connection.send_notice(self.config, payload) + + def _send_event(self, payload): + if self.config.is_dev() and not self.config.force_report_data: + return fake_connection.send_event(self.config, payload) + else: + return connection.send_event(self.config, payload) def _get_context(self): return getattr(self.thread_local, 'context', {}) @@ -55,6 +62,35 @@ def notify(self, exception=None, error_class=None, error_message=None, context={ merged_context.update(context) return self._send_notice(exception, context=merged_context, fingerprint=fingerprint) + + + def event(self, event_type=None, data=None, **kwargs): + """ + Send an event to Honeybadger + Events logged with this method will appear in Honeybadger Insights. + """ + # If the first argument is a string, treat it as event_type + if isinstance(event_type, str): + payload = data.copy() if data else {} + payload['event_type'] = event_type + # If the first argument is a dictionary, merge it with kwargs + elif isinstance(event_type, dict): + payload = event_type.copy() + payload.update(kwargs) + # Raise an error if event_type is not provided correctly + else: + raise ValueError("The first argument must be either a string or a dictionary") + + # Ensure 'event_type' is in payload + if 'event_type' not in payload: + raise ValueError("An event_type must be provided") + + # Add a timestamp to the payload if not provided + if 'ts' not in payload: + payload['ts'] = time.time() + + return self._send_event(payload) + def configure(self, **kwargs): self.config.set_config_from_dict(kwargs) diff --git a/honeybadger/fake_connection.py b/honeybadger/fake_connection.py index 87717b5..5467672 100644 --- a/honeybadger/fake_connection.py +++ b/honeybadger/fake_connection.py @@ -8,3 +8,8 @@ def send_notice(config, payload): logger.info('Development mode is enabled; this error will be reported if it occurs after you deploy your app.') logger.debug('The config used is {} with payload {}'.format(config, payload)) return notice_id + +def send_event(config, payload): + logger.info('Development mode is enabled; this event will be reported if it occurs after you deploy your app.') + logger.debug('The config used is {} with payload {}'.format(config, payload)) + return True