这是使用装饰器的绝佳案例。
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'
以下是您问题的答案:
- 真的有必要创建这么多记录器吗?
使用装饰器,您只需创建一个记录器。所以不,每个功能一个记录器就足够了。由于您的记录器采用这种格式“{func-name}_id_logger”,这意味着每个不同的函数都必须有一个唯一的记录器。
- 记录器能否处理每个函数的异常?
是的,记录器将捕获任何属于 Exception 子类的异常。尽管无论如何都会捕获您的异常,但您仍然应该尝试在函数中捕获 + 处理异常。
- 打开的文件是否会在函数完成后或捕获异常时保持打开状态?
不,会适当关闭。