首页 > 解决方案 > Python:登录到通用文件

问题描述

我有一个带有各种嵌套模块的 python 项目。我想要一个setup_logger函数,在每个文件的开头调用它来设置记录器

from _base import setup_logger
logger = setup_logger()

并在主调用脚本的文件名之后自动生成日志文件的名称。例如,我希望能够在终端中执行脚本myscript.py,这应该记录到名为myscript_<proc_id>.log的文件中。在该日志文件中应该由将在执行期间调用的所有模块写入。也就是说,我想要的输出是单个日志文件(以主调用者命名,例如myscript_45469.logmyscript.py)和日志字符串如下:

2021-05-15 21:06:15: myscript[45469]    INFO : A log message from myscript.py
2021-05-15 21:06:16: submodule_1[45469] INFO : A log message from submodule_1.py
2021-05-15 21:06:17: submodule_2[45469] INFO : A log message from submodule_2.py

在我的终端中,这正是我得到的。但是,输出仍然写入单独的文件,每个文件中只有相应模块的消息:

$ ls
myscript_45469.log  submodule_1_45469.log  submodule_2_45469.log
$ cat submodule_1_45469.log
2021-05-15 21:06:16: submodule_1[45469] INFO : A log message from submodule_1.py

到目前为止,我的setup_logger函数如下所示:

# _base.py
from pathlib import Path
import logging
import os

def setup_logger(name=None, level="INFO", logfile=True):
    frame = inspect.stack()[1]
    caller_filename = frame[0].f_code.co_filename
    caller_filename = Path(caller_filename).stem

    if not name:
        name = caller_filename

    logger = logging.getLogger(name)
    logger.setLevel(os.getenv('LOGLEVEL', 'INFO').upper())

    formatter = logging.Formatter('%(asctime)s: %(name)s[%(process)d] %(levelname)-8s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S')

    if logfile:
        logfile = f'{name}_{os.getpid()}.log'
        fh = logging.FileHandler(logfile)
        fh.setFormatter(formatter)
        fh.setLevel(os.getenv('LOGLEVEL', 'INFO').upper())
        logger.addHandler(fh)

    ch = logging.StreamHandler()
    ch.setFormatter(formatter)
    logger.addHandler(ch)

    return logger

然后在每个(子)模块的开头调用该函数:

# submodule_1 or submodule_2, etc
from _base import *
logger = setup_logger()

logger.info("A log message from submodule_1")

如何在不对记录器设置中的文件名进行硬编码的情况下实现我想要的目标?我已经在 stackoverflow 和许多日志记录教程上浏览了相当多的帖子,但到目前为止我找不到解决方案。提前感谢您的任何意见!

标签: pythonlogging

解决方案


当你处理的时候logging,你应该依赖层次结构

假设您的项目的树结构设置如下:

project/
├─ myscript.py
├─ package/
│  ├─ submodule.py

在您的子模块中,您需要获取模块名称记录器;在这种情况下是package.submodule

# submodule.py
import logging

logger = logging.getLogger(__name__)  # package.submodule

之后,前往myscript,您需要按照您希望的工作方式设置记录器。

# myscript.py
import logging

logger = logging.getLogger("package")  # intercept all "package" loggers
logger.setLevel("<your_level>")

formatter = logging.Formatter("<your_format>")

sh = logging.StreamHandler()
fh = logging.FileHandler("package.log")

for handler in (sh, fh):
    handler.setFormatter(formatter)
    logger.addHandler(handler)

像这样设置您的环境,执行中引用的所有记录器都会像您希望的那样运行,因此使用通用格式和通用日志级别记录到控制台和(相同)日志文件。

如果您的子模块处于模块级别(因此没有package),您应该getLogger()- 它会选择根记录器。无论哪种情况,它都会按预期工作。


推荐阅读