Skip to content

Commit 14baf67

Browse files
committed
feat: attach to all frames
1 parent ba38f6f commit 14baf67

File tree

3 files changed

+81
-42
lines changed

3 files changed

+81
-42
lines changed

posthog/client.py

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
handle_in_app,
2020
exception_is_already_captured,
2121
mark_exception_as_captured,
22-
extract_code_variables_from_exception,
22+
try_attach_code_variables_to_frames,
2323
DEFAULT_CODE_VARIABLES_MASK_PATTERNS,
2424
DEFAULT_CODE_VARIABLES_IGNORE_PATTERNS,
2525
)
@@ -993,15 +993,6 @@ def capture_exception(
993993
)
994994
all_exceptions_with_trace_and_in_app = event["exception"]["values"]
995995

996-
properties = {
997-
"$exception_type": all_exceptions_with_trace_and_in_app[0].get("type"),
998-
"$exception_message": all_exceptions_with_trace_and_in_app[0].get(
999-
"value"
1000-
),
1001-
"$exception_list": all_exceptions_with_trace_and_in_app,
1002-
**properties,
1003-
}
1004-
1005996
context_enabled = get_code_variables_enabled_context()
1006997
context_mask = get_code_variables_mask_patterns_context()
1007998
context_ignore = get_code_variables_ignore_patterns_context()
@@ -1023,13 +1014,21 @@ def capture_exception(
10231014
)
10241015

10251016
if enabled:
1026-
code_variables = extract_code_variables_from_exception(
1017+
try_attach_code_variables_to_frames(
1018+
all_exceptions_with_trace_and_in_app,
10271019
exc_info,
10281020
mask_patterns=mask_patterns,
10291021
ignore_patterns=ignore_patterns,
10301022
)
1031-
if code_variables:
1032-
properties["$exception_code_variables"] = code_variables
1023+
1024+
properties = {
1025+
"$exception_type": all_exceptions_with_trace_and_in_app[0].get("type"),
1026+
"$exception_message": all_exceptions_with_trace_and_in_app[0].get(
1027+
"value"
1028+
),
1029+
"$exception_list": all_exceptions_with_trace_and_in_app,
1030+
**properties,
1031+
}
10331032

10341033
if self.log_captured_exceptions:
10351034
self.log.exception(exception, extra=kwargs)

posthog/exception_utils.py

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@
5858

5959
DEFAULT_CODE_VARIABLES_IGNORE_PATTERNS = [r"^__.*"]
6060

61+
CODE_VARIABLES_REDACTED_VALUE = "$$_posthog_redacted_based_on_masking_rules_$$"
62+
6163
LogLevelStr = Literal["fatal", "critical", "error", "warning", "info", "debug"]
6264

6365
Event = TypedDict(
@@ -733,6 +735,11 @@ def set_in_app_in_frames(frames, in_app_exclude, in_app_include, project_root=No
733735
frame["in_app"] = True
734736
continue
735737

738+
# Mark __main__ module as in_app by default (user's main script)
739+
if module == "__main__":
740+
frame["in_app"] = True
741+
continue
742+
736743
return frames
737744

738745

@@ -991,7 +998,7 @@ def serialize_code_variables(
991998
value = all_vars[name]
992999

9931000
if _pattern_matches(name, compiled_mask):
994-
result[name] = "***"
1001+
result[name] = CODE_VARIABLES_REDACTED_VALUE
9951002
else:
9961003
result[name] = _serialize_variable_value(value, max_length)
9971004

@@ -1000,28 +1007,37 @@ def serialize_code_variables(
10001007
return result
10011008

10021009

1003-
def extract_code_variables_from_exception(
1004-
exception_info, mask_patterns, ignore_patterns
1010+
def try_attach_code_variables_to_frames(
1011+
all_exceptions, exc_info, mask_patterns, ignore_patterns
10051012
):
1006-
exc_type, exc_value, tb = exception_info
1007-
1013+
exc_type, exc_value, tb = exc_info
1014+
10081015
if tb is None:
1009-
return {}
1010-
1011-
innermost_tb = None
1012-
for tb_item in iter_stacks(tb):
1013-
innermost_tb = tb_item
1014-
1015-
if innermost_tb is None:
1016-
return {}
1017-
1018-
# Extract variables from the kaboom frame only
1019-
variables = serialize_code_variables(
1020-
innermost_tb.tb_frame,
1021-
mask_patterns=mask_patterns,
1022-
ignore_patterns=ignore_patterns,
1023-
max_vars=20,
1024-
max_length=1024,
1025-
)
1026-
1027-
return variables
1016+
return
1017+
1018+
tb_frames = list(iter_stacks(tb))
1019+
1020+
if not tb_frames:
1021+
return
1022+
1023+
for exception in all_exceptions:
1024+
stacktrace = exception.get("stacktrace")
1025+
if not stacktrace or "frames" not in stacktrace:
1026+
continue
1027+
1028+
serialized_frames = stacktrace["frames"]
1029+
1030+
for serialized_frame, tb_item in zip(serialized_frames, tb_frames):
1031+
if not serialized_frame.get("in_app"):
1032+
continue
1033+
1034+
variables = serialize_code_variables(
1035+
tb_item.tb_frame,
1036+
mask_patterns=mask_patterns,
1037+
ignore_patterns=ignore_patterns,
1038+
max_vars=20,
1039+
max_length=1024,
1040+
)
1041+
1042+
if variables:
1043+
serialized_frame["code_variables"] = variables

posthog/test/test_exception_capture.py

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ class UnserializableObject:
5252
code_variables_enabled=True
5353
)
5454
55-
def process_data():
55+
def trigger_error():
5656
my_string = "hello world"
5757
my_number = 42
5858
my_bool = True
@@ -63,6 +63,19 @@ def process_data():
6363
6464
1/0 # Trigger exception
6565
66+
def intermediate_function():
67+
request_id = "abc-123"
68+
user_count = 100
69+
is_active = True
70+
71+
trigger_error()
72+
73+
def process_data():
74+
batch_size = 50
75+
retry_count = 3
76+
77+
intermediate_function()
78+
6679
process_data()
6780
"""
6881
)
@@ -74,14 +87,25 @@ def process_data():
7487
output = excinfo.value.output
7588

7689
assert b"ZeroDivisionError" in output
77-
assert b"$exception_code_variables" in output
90+
assert b"code_variables" in output
91+
92+
# Variables from trigger_error frame
7893
assert b"'my_string': \"'hello world'\"" in output
7994
assert b"'my_number': '42'" in output
8095
assert b"'my_bool': 'True'" in output
8196
assert b'"my_dict": "{\\"name\\": \\"test\\", \\"value\\": 123}"' in output
8297
assert b'"my_obj": "<UnserializableObject>"' in output
83-
assert b"'my_password': '***'" in output
98+
assert b"'my_password': '$$_posthog_redacted_based_on_masking_rules_$$'" in output
8499
assert b"'__should_be_ignored':" not in output
100+
101+
# Variables from intermediate_function frame
102+
assert b"'request_id': \"'abc-123'\"" in output
103+
assert b"'user_count': '100'" in output
104+
assert b"'is_active': 'True'" in output
105+
106+
# Variables from process_data frame
107+
assert b"'batch_size': '50'" in output
108+
assert b"'retry_count': '3'" in output
85109

86110

87111
def test_code_variables_context_override(tmpdir):
@@ -122,6 +146,6 @@ def process_data():
122146
output = excinfo.value.output
123147

124148
assert b"ZeroDivisionError" in output
125-
assert b"$exception_code_variables" in output
126-
assert b"'bank': '***'" in output
149+
assert b"code_variables" in output
150+
assert b"'bank': '$$_posthog_redacted_based_on_masking_rules_$$'" in output
127151
assert b"'__dunder_var': \"'should_be_visible'\"" in output

0 commit comments

Comments
 (0)