问题描述
如何将上下文中的日志记录重定向到文件。如果在范围内调用,还会重定向其他模块(第 3 方,例如请求、numpy 等)中的日志记录。
用例是我们想要集成另一个团队的算法。 我们需要将算法的日志输出到附加文件中,以便我们可以将其提供给他们用于调试目的。
例如:
import some_func_3rd_party
some_func_3rd_party() # logs will only be written to predefined handlers
logger.info("write only to predefined handlers")
with log2file("somefile.txt"):
logger.info("write to file and predefined handlers")
some_func_3rd_party() # logs will be written to predefined handlers and to file
logger.info("write only to predefined handlers")
解决方法
目前,我看不到一种方法可以在不访问和修改根记录器的情况下实现您想要的。
如果您想要更有针对性的方法,您需要知道第 3 方库的记录器是如何配置的。
请查看“Log all requests from the python-requests module”的答案以更好地理解我的意思。
以下是一种可能适用于您的特定情况的方法:
import contextlib
import logging
import requests
import sys
@contextlib.contextmanager
def special_logger(app_logger,log_file,log_level=logging.DEBUG):
# Get all handlers added to the app_logger.
handlers = app_logger.handlers
# Add handlers specific to this context.
handlers.append(logging.FileHandler(filename=log_file))
handlers.append(logging.StreamHandler(stream=sys.stderr))
# Get the root logger,set the logging level,# and add all the handlers above.
root_logger = logging.getLogger()
root_logger_level = root_logger.level
root_logger.setLevel(log_level)
for handler in handlers:
root_logger.addHandler(handler)
# Yield the modified root logger.
yield root_logger
# Clean up handlers.
for handler in handlers:
root_logger.removeHandler(handler)
# Reset log level to what it was.
root_logger.setLevel(root_logger_level)
# Get logger for this module.
app_logger = logging.getLogger('my_app')
# Add a handler logging to stdout.
sh = logging.StreamHandler(stream=sys.stdout)
app_logger.addHandler(sh)
app_logger.setLevel(logging.DEBUG)
app_logger.info("Logs go only to stdout.")
# 'requests' logs go to stdout only but won't be emitted in this case
# because root logger level is not set to DEBUG.
requests.get("http://www.google.com")
# Use the new context with modified root logger.
with special_logger(app_logger,'my_app.log') as spec_logger:
# The output will appear twice in the console because there is
# one handler logging to stdout,and one to stderr.
# This is for demonstration purposes only.
spec_logger.info("Logs go to stdout,stderr,and file.")
# 'requests' logs go to stdout,and file.
requests.get("http://www.google.com")
app_logger.info("Logs go only to stdout.")
# 'requests' logs go to stdout only but won't be emitted in this case
# because root logger level is not set to DEBUG.
requests.get("http://www.google.com")