我正在尝试创建一个自定义 TimedRotatingFileHandler。对于应该位于单个日志文件中的记录器,我创建这样的自定义文件处理程序,以自动为日志文件创建目录(如果不存在):
class CustomTimedRotatingFileHandler(logging.handlers.TimedRotatingFileHandler):
def __init__(self, filename: str, *args, **kwargs):
log_path = os.path.dirname(filename) # filename passes full path, seperate path from filename
pathlib.Path(log_path).mkdir(parents=True, exist_ok=True) # Use the seperated path to create directories for logfile
logging.handlers.TimedRotatingFileHandler.__init__(self, log_name, *args, **kwargs)
我想要知道的是创建一个特殊的处理程序,它不会根据预定义的名称创建日志文件,而是根据我在使用logging.getLogger(name)创建记录器时传递的模块名称创建日志文件。使用此处理程序的记录器将使用自己的模块名称创建自己的日志文件:
class CustomTimedRotatingFileHandler(logging.handlers.TimedRotatingFileHandler):
def __init__(self, filename: str, *args, **kwargs):
log_path = os.path.dirname(filename) # filename passes full path, seperate path from filename
pathlib.Path(log_path).mkdir(parents=True, exist_ok=True) # Use the seperated path to create directories for logfile
logger_name = # Somehow get the logger name e.g module name from __name__
new_filename f"{log_path}/{logger_name}.log" # create new filename for the __init__
logging.handlers.TimedRotatingFileHandler.__init__(self, new_filename, *args, **kwargs)
目前,为了实现相同的功能,我必须为每个记录器创建单独的处理程序,以便将它们记录在单独的文件中。
所以问题是:如何获取 Handler 中的记录器名称?
您可以将所需的任何参数添加到自定义处理程序的
__init__
方法中,那么为什么不只添加一个 logger_name
参数,然后在调用 logger.addHandler(CustomTimedRotatingLogHandler(filename, __name__))
时设置该参数呢?
如果您仍然想尝试获取调用模块的
__name__
,您可以使用 inspect
模块获取它,如 [https://stackoverflow.com/a/7272464/17030540](详细信息请参见此处),但是这是一个脆弱的解决方案并且破坏了封装。一般来说,函数或方法不应该知道或关心调用者是谁。
作为额外的更改,我还更新了最后一行以调用
super().__init__()
,而不是直接引用父类。
自定义_logger.py
import inspect
import logging
import logging.handlers
import os
import pathlib
class CustomTimedRotatingFileHandler(logging.handlers.TimedRotatingFileHandler):
def __init__(self, filename: str, logger_name=None, *args, **kwargs):
if logger_name is None:
logger_name = inspect.currentframe().f_back.f_locals.get("__name__", "<unknown>")
log_path = os.path.dirname(filename) # filename passes full path, seperate path from filename
pathlib.Path(log_path).mkdir(parents=True, exist_ok=True) # Use the seperated path to create directories for logfile
new_filename = f"{log_path}/{logger_name}.log" # create new filename for the __init__
super().__init__(new_filename, *args, **kwargs)
my_module.py
import logging
from custom_logger import CustomTimedRotatingFileHandler
logger=logging.getLogger("")
# Use default logger_name in handler
logger.addHandler(custom.CustomTimedRotatingFileHandler("./logs/custom_log_from_module.log"))
logger.critical("Message from Module")
# Submit a logger_name to handler
second=logging.getLogger("SecondLogger")
second.addHandler(custom.CustomTimedRotatingFileHandler("./logs/custom_log_2_from_module.log", logger_name="Second_logger_Override"))
logger.debug("Message from Module")
main.py
import logging
import custom_logger
import my_module
logger = logging.getLogger()
def main():
# Send override name to handler
logger.addHandler(custom_logger.CustomTimedRotatingFileHandler("./logs/Log_From_Main.txt", logger_name="From_main"))
logger.warning("Logging a message from main")
# Have handler use default
second_main = logging.getLogger("2ndMain")
second_main.addHandler(custom_logger.CustomTimedRotatingFileHandler("./logs/FromMain2.log"))
second_main.warning("Second message from main")
if __name__=="__main__":
main()
您可以看到传入的
logger_name
值在可用时用作文件名。 my_module.py
中的默认情况使用了 "my_module"
,而我的 __main__
脚本中的默认情况无法从帧堆栈中获取名称,而是使用了 "_unknown_"
的后备。
在我看来,最好将
logger_name
设置为必需参数,而不是尝试从框架堆栈中提取信息。