Это отличный случай для использования декораторов.
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
Где в первых четырех текстовых файлах вы получите что-то вроде этого:
[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", это означает, что для каждой отдельной функции должен быть уникальный регистратор.
- Сможет ли регистратор обрабатывать исключения для каждой функции?
Да, регистратор будет перехватывать любые исключения, которые являются подклассом исключений. Несмотря на то, что ваше исключение будет перехвачено независимо, вы все равно должны попытаться поймать + обработать исключение внутри функции.
- Будет ли открытый файл оставаться открытым после выполнения функции или при обнаружении какого-либо исключения?
Нет, он будет закрыт соответствующим образом.