fix: prevent mutation of original dict when record.msg is a dict - #66
fix: prevent mutation of original dict when record.msg is a dict#66gaoflow wants to merge 2 commits into
Conversation
When record.msg is used as a dict log message, the format() method assigned it by reference to message_dict. Subsequent mutations to message_dict (adding exc_info, stack_info) would leak into the caller's original dict. Fix by copying the dict immediately so the original is never modified.
|
Hi @gaoflow, thanks for putting this together. Overall it makes sense what it's trying to achieve. I would however like to take some time to think about if this is a change I want to make. In particular, if my understanding of Assuming I decide to go ahead with this change, it would also need a unit test to ensure there are no regressions (the sample provided in the description could form the basis of it). We'd also need consider what documentation / doc-strings need to be updated to make this API contract clear. Finally, it looks like the failing CI is unrelated to your change. |
Covers both directions of the copy behaviour: - test_log_dict_not_modified pins that exc_info/stack_info are not added to the caller's dict. - test_log_dict_shallow_copied pins that nested values stay shared, so swapping in a deep copy is a deliberate change rather than a silent one. Documents the contract in the format() docstring and the quickstart, and adds the changelog entry.
|
The shallow copy is sufficient here, though not for a comforting reason: the only writes into What does remain is that nested values are shared, so the output is a live view of the caller's nested data at serialization time. A deep copy wouldn't close that either — with a buffering handler like So I documented the contract rather than deepening the copy. Both tests were mutation-checked: reverting |
Summary
When
record.msgis a dict (used as a structured log message), theformat()method assigns it tomessage_dictby reference. Subsequent mutations tomessage_dict-- such as injectingexc_infoorstack_info-- leak into the caller's original dict, causing unexpected side effects.Reproduction
Fix
Use
record.msg.copy()instead of a direct reference, so the original dict remains untouched.Tests
All 135 existing tests pass.
(This contribution was made under my direction and I take full responsibility for its correctness.)