我是否需要在 python 项目中跨模块共享记录器实例?
Do I need to share logger instance across modules in python project?
我有一个特定于我的项目的通用记录器实例。它会自动创建并附加 2 个处理程序(StreamHandler
和 TimedRotatingFileHandler
),并预先配置了不同的格式等。
logging_formatters = {
'fmt': "%(asctime)s [%(levelname)8s:%(process)05d] [%(module)10s:%(lineno)03d] (%(name)s) %(message)s",
'datefmt': "%Y-%m-%d %H:%M:%S"
}
def get_logger(
application_name=None,
filename=None,
*args,
**kwargs
):
if not isinstance(application_name, str):
raise ValueError("Logger class expects a string type application name")
if filename is None:
filename = application_name
if not filename.endswith(".log"):
filename = filename.split('.')[0] + ".log"
log_path = kwargs.get('log_path')
service_name = kwargs.get('service_name', '')
console_level = kwargs.get('console_level', logging.INFO)
file_level = kwargs.get('file_level', logging.DEBUG)
logger = logging.getLogger(application_name)
if len(logger.handlers) > 0:
return logger
# Create 2 handlers, and add them to the logger created
# ...
# ...
# ...
现在,假设我的 Flask 项目结构类似于:
/
- app.py
- settings.py
- dir1/
- __init__.py
- mod1.py
- dir2/
- __init__.py
- mod2.py
我使用 python app.py
启动服务器。 app.py
本身导入了 dir1.mod1
和 dir2.mod2
模块。这些模块中的每一个都创建自己的记录器实例,如下所示:
logger = log_package.get_logger(
application_name='{}.{}'.format(settings.APPLICATION_NAME, __name__),
filename=settings.LOG_FILE_NAME,
service_name=settings.SERVICE_NAME,
)
在 app.py
的情况下是:
logger = log_package.get_logger(
application_name='{}.{}'.format(settings.APPLICATION_NAME, 'run'),
filename=settings.LOG_FILE_NAME,
service_name=settings.SERVICE_NAME,
)
现在,我面临的问题是; TimedRotatingFileHandler
对所有子模块(即 dir1.mod1
、dir2.mod2
等)工作正常,但是,来自 app.py
的日志没有被滚动到新文件中。该特定实例将日志写入与服务启动时相同的文件。例如。如果我在 2017-07-11
上启动服务,那么 app.py
将继续将日志写入 LOG_FILE_NAME.log.2017-07-11
而其他模块每天都在滚动 (when=midnight
) 并且正在写入新日志至 LOG_FILE_NAME.log
.
TimedRotatingFileHandler
对特定文件不起作用背后的问题可能是什么?我对目录中的所有文件 运行 lsof
命令,这是输出:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
python 23795 ubuntu 4w REG 202,1 2680401 150244 /path/to/LOG_FILE_NAME.2017-07-12
python 23795 ubuntu 33w REG 202,1 397074 150256 /path/to/LOG_FILE_NAME.log
我是否需要在 python 项目中跨模块共享记录器实例?我认为这不应该是必需的,因为日志记录模块本身是线程安全的。
TimedRotatingHandler
实例不应写入 LOG_FILE_NAME.log
以外的文件 - 其他文件(例如 LOG_FILE_NAME.2017-07-12
将在滚动时创建,不应写入打开。
您应该确保您的进程中只有一个 TimedRotatingFileHandler
实例用于给定的文件名 - 如果您有两个不同的处理程序实例都引用相同的文件名,您可能会出现意外行为(如果一个实例滚动结束时,它更改了它正在使用的文件,但另一个实例仍将引用旧文件,并继续写入该文件。
您应该只将处理程序附加到根记录器,而不是单独的模块记录器,其他记录器会在默认条件下选择这些处理程序(记录器的默认 propagate
设置未更改)。
更新: %(name)s
总是给出用于记录调用的记录器的名称,即使处理程序附加到祖先记录器也是如此。如果记录器的 propagate
设置为 False
,则不会使用祖先记录器中的处理程序 - 因此您应该将 propagate
保留为其默认值 True
。
我有一个特定于我的项目的通用记录器实例。它会自动创建并附加 2 个处理程序(StreamHandler
和 TimedRotatingFileHandler
),并预先配置了不同的格式等。
logging_formatters = {
'fmt': "%(asctime)s [%(levelname)8s:%(process)05d] [%(module)10s:%(lineno)03d] (%(name)s) %(message)s",
'datefmt': "%Y-%m-%d %H:%M:%S"
}
def get_logger(
application_name=None,
filename=None,
*args,
**kwargs
):
if not isinstance(application_name, str):
raise ValueError("Logger class expects a string type application name")
if filename is None:
filename = application_name
if not filename.endswith(".log"):
filename = filename.split('.')[0] + ".log"
log_path = kwargs.get('log_path')
service_name = kwargs.get('service_name', '')
console_level = kwargs.get('console_level', logging.INFO)
file_level = kwargs.get('file_level', logging.DEBUG)
logger = logging.getLogger(application_name)
if len(logger.handlers) > 0:
return logger
# Create 2 handlers, and add them to the logger created
# ...
# ...
# ...
现在,假设我的 Flask 项目结构类似于:
/
- app.py
- settings.py
- dir1/
- __init__.py
- mod1.py
- dir2/
- __init__.py
- mod2.py
我使用 python app.py
启动服务器。 app.py
本身导入了 dir1.mod1
和 dir2.mod2
模块。这些模块中的每一个都创建自己的记录器实例,如下所示:
logger = log_package.get_logger(
application_name='{}.{}'.format(settings.APPLICATION_NAME, __name__),
filename=settings.LOG_FILE_NAME,
service_name=settings.SERVICE_NAME,
)
在 app.py
的情况下是:
logger = log_package.get_logger(
application_name='{}.{}'.format(settings.APPLICATION_NAME, 'run'),
filename=settings.LOG_FILE_NAME,
service_name=settings.SERVICE_NAME,
)
现在,我面临的问题是; TimedRotatingFileHandler
对所有子模块(即 dir1.mod1
、dir2.mod2
等)工作正常,但是,来自 app.py
的日志没有被滚动到新文件中。该特定实例将日志写入与服务启动时相同的文件。例如。如果我在 2017-07-11
上启动服务,那么 app.py
将继续将日志写入 LOG_FILE_NAME.log.2017-07-11
而其他模块每天都在滚动 (when=midnight
) 并且正在写入新日志至 LOG_FILE_NAME.log
.
TimedRotatingFileHandler
对特定文件不起作用背后的问题可能是什么?我对目录中的所有文件 运行 lsof
命令,这是输出:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
python 23795 ubuntu 4w REG 202,1 2680401 150244 /path/to/LOG_FILE_NAME.2017-07-12
python 23795 ubuntu 33w REG 202,1 397074 150256 /path/to/LOG_FILE_NAME.log
我是否需要在 python 项目中跨模块共享记录器实例?我认为这不应该是必需的,因为日志记录模块本身是线程安全的。
TimedRotatingHandler
实例不应写入 LOG_FILE_NAME.log
以外的文件 - 其他文件(例如 LOG_FILE_NAME.2017-07-12
将在滚动时创建,不应写入打开。
您应该确保您的进程中只有一个 TimedRotatingFileHandler
实例用于给定的文件名 - 如果您有两个不同的处理程序实例都引用相同的文件名,您可能会出现意外行为(如果一个实例滚动结束时,它更改了它正在使用的文件,但另一个实例仍将引用旧文件,并继续写入该文件。
您应该只将处理程序附加到根记录器,而不是单独的模块记录器,其他记录器会在默认条件下选择这些处理程序(记录器的默认 propagate
设置未更改)。
更新: %(name)s
总是给出用于记录调用的记录器的名称,即使处理程序附加到祖先记录器也是如此。如果记录器的 propagate
设置为 False
,则不会使用祖先记录器中的处理程序 - 因此您应该将 propagate
保留为其默认值 True
。