如何获取打印到控制台和最少行文件的记录器

时间:2016-09-16 18:09:11

标签: python logging

这是我获取打印到控制台和文件的记录器的方法:

import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
handler.setFormatter(formatter)
handler2 = logging.FileHandler(os.path.join(os.path.split(os.path.abspath(__file__))[0], 'my.log'), mode='w')
handler2.setFormatter(formatter)
logger.addHandler(handler)
logger.addHandler(handler2)

有没有办法在更少的操作中完成类似的操作?我并不特别关心文件模式或文件名。

编辑:这个用例就是我正在为一个新脚本进行原型设计,并且不希望花时间编写配置文件直到以后。

2 个答案:

答案 0 :(得分:1)

我的观点:当你进行原型设计时,你需要一个每个模块的日志文件。

上下文处理程序

为此,您可以按照以下步骤进行处理:

  • 创建LogNameFileHandler:这是logging.StreamHandler的子类,其行为类似logging.FileHandler,但会为每个记录器创建一个日志文件(基于其名称)。
  • 创建一个单例LOGGER,它使用此处理程序和经典控制台记录器(按照您的建议方式)设置(根)记录器。
  • 以最简单的方式在不同模块中使用记录器:logger = LOGGER.getChild(__name__)

详情实施

log_handler.py

import io
import logging
import os


class LogNameFileHandler(logging.StreamHandler):
    def __init__(self, root_dir, mode='a', encoding=None):
        super(LogNameFileHandler, self).__init__(stream=None)
        self.root_dir = os.path.abspath(root_dir)
        self.mode = mode
        self.encoding = encoding
        # Set stream to None, because StreamHandler set it to sys.stderr
        self.stream = None
        #: :type log_path: str
        self.log_path = None

    def _open(self):
        log_dir = os.path.dirname(self.log_path)
        if not os.path.isdir(log_dir):
            os.makedirs(log_dir)
        return io.open(self.log_path, mode=self.mode, encoding=self.encoding)

    def close(self):
        self.acquire()
        try:
            try:
                if self.stream:
                    try:
                        self.flush()
                    finally:
                        stream = self.stream
                        self.stream = None
                        if hasattr(stream, "close"):
                            stream.close()
            finally:
                super(LogNameFileHandler, self).close()
        finally:
            self.release()

    def emit(self, record):
        name = record.name.replace(".", os.sep)
        log_name = name + ".log"
        self.log_path = os.path.join(self.root_dir, log_name)
        self.stream = self._open()
        super(LogNameFileHandler, self).emit(record)

此处理程序为每个emit打开一个新文件,当然,由您来处理缓存...对于原型设计,它应该没问题。您可以使用映射record.name => stream

log_setup.py

import logging
import os

from log_handler import LogNameFileHandler


def _init_logger(log_dir, name=None):
    logger = logging.getLogger(name)
    logger.setLevel(logging.DEBUG)
    formatter = logging.Formatter(u'%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
    handler1 = logging.StreamHandler()
    handler1.setFormatter(formatter)
    handler2 = LogNameFileHandler(log_dir, mode="a+", encoding="utf8")
    handler2.setFormatter(formatter)
    logger.addHandler(handler1)
    logger.addHandler(handler2)
    return logger


LOGGER = _init_logger(os.path.dirname(__file__))

这个模块定义了LOGGER单例,您可以在所有Python模块中使用它...

用法

package/module.py

from log_setup import LOGGER

logger = LOGGER.getChild(__name__)
logger.info("hello solution3")

这就是全部(所有灰尘都在地毯下)。

如果你设置了root记录器,你可以写:

import logging

logger = logging.getLogger(__name__)
...

答案 1 :(得分:0)

我很好奇:你为什么要这样?

您可以在INI文件中使用外部配置,并使用logging.config.fileConfig加载它。

或者

创建自己的句柄,组合文件和控制台处理程序。

或者

创建一个类文件对象,将其写入文件并在控制台中。然后将此文件与logging.basicConfig一起使用。