记录功能级别

时间:2018-04-26 23:56:35

标签: python python-3.x logging

考虑一个python模块包含多个函数的情况。每个函数都需要id

def f1(id):
    log into file f1/{id}.txt

def f2(id):
    log into file f2/{id}.txt

假设传递给每个函数的id始终是唯一的。就像1传递给f1一样,1再次请求f1。与其他功能相同。

我希望每个功能的记录而不是模块。这样每个函数都会记录到像function_name / id.txt

这样的唯一文件中

因此在执行该函数后,无需打开function_name / id.txt进行日志记录,因为下一个请求将包含不同的id。因此,在执行函数后应该关闭该文件的文件处理程序

如何在python中实现每个模块的日志记录,以便每个模块都能正确捕获所有异常?

我正在尝试这种方法:

   def setup_logger( name, log_file, level=logging.DEBUG ):
        handler = logging.FileHandler(log_file)
        handler.setFormatter(logging.Formatter('[%(asctime)s][%(levelname)s]%(message)s'))
        logger = logging.getLogger(name)
        logger.setLevel(level)
        logger.addHandler(handler)
        return logger

   def f1(id):
        logger = setup_logger('f1_id_logger', f'f1/{id}.txt', level=logging.DEBUG)

    def f2(id):
        logger = setup_logger('f2_id_logger', f'f2/{id}.txt', level=logging.DEBUG)

但我担心的是:

  • 是否真的有必要创建这么多记录器?
  • 记录器是否能够处理每个功能的异常?
  • 在功能完成或捕获到异常后,文件打开是否仍然打开?

2 个答案:

答案 0 :(得分:2)

这是使用装饰器的一个很好的例子。

import logging
from os import mkdir
from os.path import exists
from sys import exc_info # for retrieving the exception
from traceback import format_exception # for formatting the exception

def id_logger_setup(level=logging.DEBUG):

    def setup_logger(func):
        if not exists(func.__name__): # makes the directory if it doesn't exist
            mkdir(func.__name__)
        logger = logging.getLogger("{}_id_logger".format(func.__name__))
        logger.setLevel(level)

        def _setup_logger(id, *args, **kwargs):
            handler = logging.FileHandler("{}/{}.txt".format(func.__name__, id)) # a unique handler for each id
            handler.setFormatter(logging.Formatter("[%(asctime)s][%(levelname)s]%(message)s"))
            logger.addHandler(handler)
            try:
                rtn = func(id, logger=logger, *args, **kwargs)
            except Exception: # if the function breaks, catch the exception and log it
                logger.critical("".join(format_exception(*exc_info())))
                rtn = None
            finally:
                logger.removeHandler(handler) # remove ties between the logger and the soon-to-be-closed handler
                handler.close() # closes the file handler
                return rtn

        return _setup_logger
    return setup_logger

@id_logger_setup(level=logging.DEBUG) # set the level
def f1(id, *, logger):
    logger.debug("In f1 with id {}".format(id))

@id_logger_setup(level=logging.DEBUG)
def f2(id, *, logger):
    logger.debug("In f2 with id {}".format(id))

@id_logger_setup(level=logging.DEBUG)
def f3(id, *, logger):
    logger.debug("In f3 with id {}".format(id))
    logger.debug("Something's going wrong soon...")
    int('opps') # raises an error


f1(1234)
f2(5678)
f1(4321)
f2(8765)
f3(345774)

从代码示例中,您可以获得以下内容:

f1 -
   |
   1234.txt
   4321.txt
f2 -
   |
   5678.txt
   8765.txt
f3 -
   |
   345774.txt

前四个txt文件中的位置如下所示:

[2018-04-26 18:49:29,209][DEBUG]In f1 with id 1234

,在f3 / 345774.txt中,您得到:

[2018-04-26 18:49:29,213][DEBUG]In f3 with id 345774
[2018-04-26 18:49:29,213][DEBUG]Something's going wrong soon...
[2018-04-26 18:49:29,216][CRITICAL]Traceback (most recent call last):
  File "/path.py", line 20, in _setup_logger
    rtn = func(id, logger=logger, *args, **kwargs)
  File "/path.py", line 43, in f3
    int('opps')
ValueError: invalid literal for int() with base 10: 'opps'

以下是您的问题的答案:

  1. 是否真的有必要创建这么多记录器?
  2. 使用装饰器,您只需创建一个记录器。所以不,一个记录器足以满足每个功能。由于您的记录器的格式为“{func-name} _id_logger”,这意味着每个不同的函数都必须有唯一的记录器。

    1. 记录器是否能够处理每个功能的异常?
    2. 是的,记录器将捕获作为Exception子类的任何异常。虽然您无论如何都会被捕获,但您仍应尝试捕获+处理函数中的异常。

      1. 在功能完成或捕获到异常后,文件打开是否仍然打开?
      2. 不,它将被适当关闭。

答案 1 :(得分:0)

您不必分别为每个案例设置记录器。您应该将它们设置一次,以便您有两个记录器,每个输出到另一个文件。然后在两个函数中使用两个不同的记录器。

例如,您可以通过这种方式配置记录器*:

import logging.config

logging.config.dictConfig({
    'version': 1,
    'formatters': {
        'simple_formatter': {
            'format': '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
        }
    },
    'handlers': {
        'first_handler': {
            'class' : 'logging.FileHandler',
            'formatter': 'simple_formatter',
            'filename': 'C:\\Temp\\log1.txt'
        },
        'second_handler': {
            'class' : 'logging.FileHandler',
            'formatter': 'simple_formatter',
            'filename': 'C:\\Temp\\log2.txt'
        }
    },
    'loggers': {
        'first_logger': {
            'handlers': ['first_handler']
        },
        'second_logger': {
            'handlers': ['second_handler']
        }
    }
})

然后,只需使用您需要的一个或另一个记录器:

def f1():
    logger = logging.getLogger('first_logger')
    logger.warning('Hello from f1')

def f2():
    logger = logging.getLogger('second_logger')
    logger.warning('Hello from f2')

*配置记录器的方法有多种,有关其他选项,请参阅https://docs.python.org/3.6/library/logging.config.html