Skip to content
Merged
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 7.0.1 - 2025-11-15

Try to use repr() when formatting code variables

# 7.0.0 - 2025-11-11

NB Python 3.9 is no longer supported
Expand Down
33 changes: 22 additions & 11 deletions posthog/exception_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -969,19 +969,30 @@ def _serialize_variable_value(value, limiter, max_length=1024):
return result
except Exception:
try:
fallback = f"<{type(value).__name__}>"
fallback_size = len(fallback)
if not limiter.can_add(fallback_size):
result = repr(value)
if len(result) > max_length:
result = result[: max_length - 3] + "..."

result_size = len(result)
if not limiter.can_add(result_size):
return None
limiter.add(fallback_size)
return fallback
limiter.add(result_size)
return result
except Exception:
fallback = "<unserializable object>"
fallback_size = len(fallback)
if not limiter.can_add(fallback_size):
return None
limiter.add(fallback_size)
return fallback
try:
fallback = f"<{type(value).__name__}>"
fallback_size = len(fallback)
if not limiter.can_add(fallback_size):
return None
limiter.add(fallback_size)
return fallback
except Exception:
fallback = "<unserializable object>"
fallback_size = len(fallback)
if not limiter.can_add(fallback_size):
return None
limiter.add(fallback_size)
return fallback


def _is_simple_type(value):
Expand Down
76 changes: 75 additions & 1 deletion posthog/test/test_exception_capture.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def process_data():
assert b"'my_number': 42" in output
assert b"'my_bool': 'True'" in output
assert b'"my_dict": "{\\"name\\": \\"test\\", \\"value\\": 123}"' in output
assert b'"my_obj": "<UnserializableObject>"' in output
assert b"<__main__.UnserializableObject object at" in output
assert b"'my_password': '$$_posthog_redacted_based_on_masking_rules_$$'" in output
assert b"'__should_be_ignored':" not in output

Expand Down Expand Up @@ -332,3 +332,77 @@ def process_data():
assert '"code_variables":' not in output
assert "'my_var'" not in output
assert "'important_value'" not in output


def test_code_variables_repr_fallback(tmpdir):
app = tmpdir.join("app.py")
app.write(
dedent(
"""
import os
import re
from datetime import datetime, timedelta
from decimal import Decimal
from fractions import Fraction
from posthog import Posthog

class CustomReprClass:
def __repr__(self):
return '<CustomReprClass: custom representation>'

posthog = Posthog(
'phc_x',
host='https://eu.i.posthog.com',
debug=True,
enable_exception_autocapture=True,
capture_exception_code_variables=True,
project_root=os.path.dirname(os.path.abspath(__file__))
)

def trigger_error():
my_regex = re.compile(r'\\d+')
my_datetime = datetime(2024, 1, 15, 10, 30, 45)
my_timedelta = timedelta(days=5, hours=3)
my_decimal = Decimal('123.456')
my_fraction = Fraction(3, 4)
my_set = {1, 2, 3}
my_frozenset = frozenset([4, 5, 6])
my_bytes = b'hello bytes'
my_bytearray = bytearray(b'mutable bytes')
my_memoryview = memoryview(b'memory view')
my_complex = complex(3, 4)
my_range = range(10)
my_custom = CustomReprClass()
my_lambda = lambda x: x * 2
my_function = trigger_error

1/0

trigger_error()
"""
)
)

with pytest.raises(subprocess.CalledProcessError) as excinfo:
subprocess.check_output([sys.executable, str(app)], stderr=subprocess.STDOUT)

output = excinfo.value.output.decode("utf-8")

assert "ZeroDivisionError" in output
assert "code_variables" in output

assert "re.compile(" in output and "\\\\d+" in output
assert "datetime.datetime(2024, 1, 15, 10, 30, 45)" in output
assert "datetime.timedelta(days=5, seconds=10800)" in output
assert "Decimal('123.456')" in output
assert "Fraction(3, 4)" in output
assert "{1, 2, 3}" in output
assert "frozenset({4, 5, 6})" in output
assert "b'hello bytes'" in output
assert "bytearray(b'mutable bytes')" in output
assert "<memory at" in output
assert "(3+4j)" in output
assert "range(0, 10)" in output
assert "<CustomReprClass: custom representation>" in output
assert "<lambda>" in output
assert "<function trigger_error at" in output
2 changes: 1 addition & 1 deletion posthog/version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
VERSION = "7.0.0"
VERSION = "7.0.1"

if __name__ == "__main__":
print(VERSION, end="") # noqa: T201
Loading