Ведение журнала на уровне функций - PullRequest
0 голосов
/ 27 апреля 2018

Рассмотрим случай, когда модуль python содержит несколько функций. Каждая функция занимает id.

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

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

Предположим, что идентификаторы всегда уникальны и передаются каждой функции. Например, если 1 передается f1, 1 нельзя запросить снова с f1. То же самое с другими функциями.

Я хочу регистрировать данные для каждой функции, а не для модуля. Так что каждая функция входит в уникальный файл, такой как имя_функции / id.txt

Таким образом, после выполнения функции нет необходимости открывать имя_функции / id.txt для регистрации по функциям, поскольку следующий запрос будет содержать другой идентификатор. Поэтому обработчики файлов для этого файла должны быть закрыты после выполнения функции

Каким образом ведение журналов для каждого модуля может быть реализовано в 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 голосов
/ 27 апреля 2018

Это отличный случай для использования декораторов.

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'

Вот ответы на ваши вопросы:

  1. действительно ли необходимо создавать так много регистраторов?

Используя декораторы, вы создаете только один регистратор. Так что нет, одного регистратора достаточно для каждой функции. Поскольку ваши регистраторы имеют этот формат "{func-name} _id_logger", это означает, что для каждой отдельной функции должен быть уникальный регистратор.

  1. Сможет ли регистратор обрабатывать исключения для каждой функции?

Да, регистратор будет перехватывать любые исключения, которые являются подклассом исключений. Несмотря на то, что ваше исключение будет перехвачено независимо, вы все равно должны попытаться поймать + обработать исключение внутри функции.

  1. Будет ли открытый файл оставаться открытым после выполнения функции или при обнаружении какого-либо исключения?

Нет, он будет закрыт соответствующим образом.

0 голосов
/ 27 апреля 2018

Вам не нужно настраивать регистраторы для каждого случая отдельно. Вы должны настроить их один раз, чтобы у вас было два регистратора, каждый из которых выводил свой файл. Затем используйте два разных регистратора в двух функциях.

Например, вы можете настроить регистраторы таким образом *:

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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...