# Portions of this file are derived from getsentry/sentry-javascript by Software, Inc. dba Sentry
# Licensed under the MIT License
# 💖open source (under MIT License)
import logging
import sys
import threading
from enum import Enum
from typing import TYPE_CHECKING, List, Optional
if TYPE_CHECKING:
from posthog.client import Client
class Integrations(str, Enum):
Django = "django"
class ExceptionCapture:
# TODO: Add client side rate limiting to prevent spamming the server with exceptions
log = logging.getLogger("posthog")
def __init__(self, client: "Client", integrations: Optional[List[Integrations]] = None):
self.client = client
self.original_excepthook = sys.excepthook
sys.excepthook = self.exception_handler
threading.excepthook = self.thread_exception_handler
self.enabled_integrations = []
for integration in integrations or []:
# TODO: Maybe find a better way of enabling integrations
# This is very annoying currently if we had to add any configuration per integration
if integration == Integrations.Django:
try:
from posthog.exception_integrations.django import DjangoIntegration
enabled_integration = DjangoIntegration(self.exception_receiver)
self.enabled_integrations.append(enabled_integration)
except Exception as e:
self.log.exception(f"Failed to enable Django integration: {e}")
def close(self):
sys.excepthook = self.original_excepthook
for integration in self.enabled_integrations:
integration.uninstall()
def exception_handler(self, exc_type, exc_value, exc_traceback):
# don't affect default behaviour.
self.capture_exception((exc_type, exc_value, exc_traceback))
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))
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)
def capture_exception(self, exception, metadata=None):
try:
distinct_id = metadata.get("distinct_id") if metadata else None
self.client.capture_exception(exception, distinct_id)
except Exception as e:
self.log.exception(f"Failed to capture exception: {e}")