Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions posthog/exception_capture.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from enum import Enum
from typing import TYPE_CHECKING, List, Optional

from posthog.synthetic_exception import SyntheticException

if TYPE_CHECKING:
from posthog.client import Client

Expand Down Expand Up @@ -50,18 +52,23 @@ def close(self):

def exception_handler(self, exc_type, exc_value, exc_traceback):
# don't affect default behaviour.
self.capture_exception((exc_type, exc_value, exc_traceback))
synthetic_exc = SyntheticException(exc_type, exc_value, exc_traceback)
self.capture_exception(synthetic_exc)
self.original_excepthook(exc_type, exc_value, exc_traceback)

def thread_exception_handler(self, args):
self.capture_exception((args.exc_type, args.exc_value, args.exc_traceback))
synthetic_exc = SyntheticException(
args.exc_type, args.exc_value, args.exc_traceback
)
self.capture_exception(synthetic_exc)

def exception_receiver(self, exc_info, extra_properties):
if "distinct_id" in extra_properties:
metadata = {"distinct_id": extra_properties["distinct_id"]}
else:
metadata = None
self.capture_exception((exc_info[0], exc_info[1], exc_info[2]), metadata)
synthetic_exc = SyntheticException(exc_info[0], exc_info[1], exc_info[2])
self.capture_exception(synthetic_exc, metadata)

def capture_exception(self, exception, metadata=None):
try:
Expand Down
6 changes: 6 additions & 0 deletions posthog/exception_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
# Python 3.10 and below
BaseExceptionGroup = None # type: ignore

from posthog.synthetic_exception import SyntheticException


DEFAULT_MAX_VALUE_LENGTH = 1024

Expand Down Expand Up @@ -797,6 +799,10 @@ def exc_info_from_error(error):
# type: (Union[BaseException, ExcInfo]) -> ExcInfo
if isinstance(error, tuple) and len(error) == 3:
exc_type, exc_value, tb = error
elif isinstance(error, SyntheticException):
exc_type = error.exc_type
exc_value = error.exc_value
tb = error.exc_traceback
elif isinstance(error, BaseException):
tb = getattr(error, "__traceback__", None)
if tb is not None:
Expand Down
14 changes: 14 additions & 0 deletions posthog/synthetic_exception.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class SyntheticException(BaseException):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider inheriting from Exception instead of BaseException. BaseException is typically reserved for system exits and keyboard interrupts.

Suggested change
class SyntheticException(BaseException):
class SyntheticException(Exception):

"""A synthetic exception that wraps exc_info tuples for proper exception handling."""

def __init__(self, exc_type, exc_value, exc_traceback):
self.exc_type = exc_type
self.exc_value = exc_value
self.exc_traceback = exc_traceback
self.__traceback__ = exc_traceback

# Set the exception message
if exc_value:
super().__init__(str(exc_value))
else:
super().__init__(f"{exc_type.__name__}")
Comment on lines +11 to +14
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: Consider using a more descriptive message format that includes both type and value when available: f"{exc_type.name}: {exc_value if exc_value else ''}"

Loading