【问题标题】:Do I need to share logger instance across modules in python project?我需要在 python 项目中的模块之间共享记录器实例吗?
【发布时间】:2017-12-18 01:40:34
【问题描述】:

我有一个特定于我的项目的通用记录器实例。它会自动创建并附加 2 个处理程序(StreamHandlerTimedRotatingFileHandler),它们具有预先配置的不同格式等。

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
    # ...
    # ...
    # ...

现在,假设我的烧瓶项目结构类似于:

/
- app.py
- settings.py
- dir1/
    - __init__.py
    - mod1.py
- dir2/
    - __init__.py
    - mod2.py

我使用python app.py 启动服务器。 app.py 本身导入 dir1.mod1dir2.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.mod1dir2.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 项目中的模块之间共享记录器实例吗?我认为这不应该是必需的,因为日志记录模块本身就是线程安全的。

【问题讨论】:

  • 我更新了答案。

标签: python logging flask logrotate


【解决方案1】:

TimedRotatingHandler 实例不应写入 LOG_FILE_NAME.log 以外的文件 - 其他文件(例如 LOG_FILE_NAME.2017-07-12)会在滚动时创建,并且不应保持打开状态。

您应该确保您的进程中只有一个给定文件名的 TimedRotatingFileHandler 实例 - 如果您有两个不同的处理程序实例都引用相同的文件名,您可能会出现意外行为(如果一个实例翻转,它会更改它正在使用的文件,但另一个实例仍将引用旧文件,并继续写入)。

您可能应该只将您的处理程序附加到根记录器,而不是单个模块记录器,并且其他记录器会在默认条件下选择这些处理程序(记录器的默认 propagate 设置未更改)。

更新:%(name)s 总是给出用于记录调用的记录器的名称,即使处理程序附加到祖先记录器也是如此。如果 propagate 设置为 False 用于记录器,则不使用祖先记录器中的处理程序 - 因此您应该将 propagate 保留为默认值 True

【讨论】:

  • 但我希望%(name)s 代表记录器的名称。如果有人(我与几个同事共享log_package)正在创建一个项目A,他们会将项目中的所有记录器表示为A.appA.dir1.mod1,...并且日志文件将始终为@ 987654336@。在我的函数定义结束时,我已经覆盖了传播值:logger.propagate = False。我应该将logger.propagate 重置为True 吗?
猜你喜欢
  • 1970-01-01
  • 2019-09-19
  • 1970-01-01
  • 2010-12-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-01
相关资源
最近更新 更多