Closed
Description
https://docs.python.org/3/library/logging.html#logrecord-objects gives the following pattern for adding contextual information to LogRecord objects which can then be used in log formats:
old_factory = logging.getLogRecordFactory()
def record_factory(*args, **kwargs):
record = old_factory(*args, **kwargs)
record.custom_attribute = 0xdecafbad
return record
logging.setLogRecordFactory(record_factory)
However, if you use this pattern and run mypy, you'll get an error like error: "LogRecord" has no attribute "custom_attribute"
. You can get around this error by using setattr(record, "custom_attribute", 0xdecafbad)
instead of record.custom_attribute = 0xdecafbad
.
Should the stub for the LogRecord
class define a __setattr__
method because this sort of usage of assigning to arbitrary attributes is expected for the LogRecord
class? Would that have undesirable side effects?
Here is a complete example:
import logging
custom_contextual_data = "XYZ"
def main() -> None:
logging.basicConfig(
format="[%(asctime)s] [%(custom_contextual_data)s] [%(name)s] %(levelname)s: %(message)s", level=logging.DEBUG
)
old_factory = logging.getLogRecordFactory()
def record_factory(*args: object, **kwargs: object) -> logging.LogRecord:
global custom_contextual_data
record = old_factory(*args, **kwargs)
# mypy: error: "LogRecord" has no attribute "custom_contextual_data"
record.custom_contextual_data = custom_contextual_data
# mypy: no error
# setattr(record, "custom_contextual_data", custom_contextual_data)
return record
logging.setLogRecordFactory(record_factory)
logger = logging.getLogger(__name__)
logger.info("Hello XYZ world!")
global custom_contextual_data
custom_contextual_data = "ABC"
logger.info("Hello ABC world!")
if __name__ == "__main__":
main()
mypy.ini:
[mypy]
files = mypy_log_record_repro/
strict_equality = True
disallow_untyped_defs = True
disallow_untyped_calls = True
disallow_untyped_decorators = True
no_implicit_optional = True
strict_optional = True
warn_unused_ignores = True
warn_redundant_casts = True
warn_no_return = True
warn_return_any = True
warn_unreachable = True
Metadata
Metadata
Assignees
Labels
No labels