Skip to content
36 changes: 34 additions & 2 deletions sentry_sdk/integrations/dedupe.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,35 @@ def __init__(self):
# type: () -> None
self._last_seen = ContextVar("last-seen")

@staticmethod
def _get_exception_hash(exc):
# type: (Exception) -> int
"""
Create a memory-efficient hash for an exception.

Instead of storing the entire exception object, we store just enough
information to identify it uniquely. This avoids keeping the traceback
and local variables in memory.
"""
# Get the exception type name and message
exc_type = type(exc).__name__
exc_message = str(exc)

# Get the full stacktrace
stacktrace = []
if hasattr(exc, "__traceback__") and exc.__traceback__:
tb = exc.__traceback__
while tb:
frame = tb.tb_frame
filename = frame.f_code.co_filename
lineno = tb.tb_lineno
func_name = frame.f_code.co_name
stacktrace.append((filename, lineno, func_name))
tb = tb.tb_next

# Create a tuple of the essential information and hash it
return hash((exc_type, exc_message, tuple(stacktrace)))

@staticmethod
def setup_once():
# type: () -> None
Expand All @@ -36,9 +65,12 @@ def processor(event, hint):
return event

exc = exc_info[1]
if integration._last_seen.get(None) is exc:
exc_hash = DedupeIntegration._get_exception_hash(exc)

if integration._last_seen.get(None) == exc_hash:
return None
integration._last_seen.set(exc)

integration._last_seen.set(exc_hash)
return event

@staticmethod
Expand Down
Loading