Python: настраиваемая регистрация во всех модулях

задача

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

Я написал небольшой модуль 'custom_logger', который я планирую вызвать из основного приложения один раз, чтобы он возвращал логгер, который я затем продолжил бы использовать.

Подмодули, которые я бы импортировал в приложение, должны только (или, скорее, я бы их хотел)

  • следует только "импортировать ведение журнала как журнал" - так что ничего конкретного для моего сайта не требуется, чтобы заставить их просто работать, если кто-то сочтет их полезными.
  • следует просто регистрировать сообщения с log.info/error('message '), не добавляя к ним ничего специфичного для сайта
  • следует использовать уже настроенный "корневой" регистратор со всеми его форматированием и обработчиками и не влиять на конфигурацию корневого регистратора

* Custom_logger.py *

import logging
import logging.handlers
import os
import sys


def getLogger(name='root', loglevel='INFO'):
  logger = logging.getLogger(name)

  # if logger 'name' already exists, return it to avoid logging duplicate
  # messages by attaching multiple handlers of the same type
  if logger.handlers:
    return logger
  # if logger 'name' does not already exist, create it and attach handlers
  else:
    # set logLevel to loglevel or to INFO if requested level is incorrect
    loglevel = getattr(logging, loglevel.upper(), logging.INFO)
    logger.setLevel(loglevel)
    fmt = '%(asctime)s %(filename)-18s %(levelname)-8s: %(message)s'
    fmt_date = '%Y-%m-%dT%T%Z'
    formatter = logging.Formatter(fmt, fmt_date)
    handler = logging.StreamHandler()
    handler.setFormatter(formatter)
    logger.addHandler(handler)

    if logger.name == 'root':
      logger.warning('Running: %s %s',
                     os.path.basename(sys.argv[0]),
                     ' '.join(sys.argv[1:]))
    return logger

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

submodule.py

import sys
import custom_logger
import logging


class SubClass(object):

  def __init__(self):
    # NOK (no idea why since by default (no name parameter), it should return the root logger)
    #log = logging.getLogger()
    #log.info('message from SubClass / __init__')

    # OK (works as expected)
    #log = logging.getLogger('root')
    #log.info('message from SubClass / __init__')

    # OK (works as expected)
    log = custom_logger.getLogger('root')
    log.info('message from SubClass / __init__')


  def SomeMethod(self):
    # OK but I'd have to define `log` for every method, which is unacceptable
    # Please see question below all code snippets
    log = custom_logger.getLogger('root')
    log.info('message from SubClass / SomeMethod')

И главное приложение: app.py Ничего особенного здесь:

#!/usr/bin/python

import custom_logger
import submodule

log = custom_logger.getLogger('root', loglevel='DEBUG')

log.debug('debug message')
log.info('info message')
log.warning('warning message')
log.error('error message')

a = submodule.SubClass() # this should produce a log message
a.SomeMethod()           # so should this

Вывод, который я получаю и который я получаю, просто очень уродливо:

% ./app.py 
2013-04-08T03:07:46BST custom_logger.py   WARNING : Running: app.py 
2013-04-08T03:07:46BST app.py             DEBUG   : debug message
2013-04-08T03:07:46BST app.py             INFO    : info message
2013-04-08T03:07:46BST app.py             WARNING : warning message
2013-04-08T03:07:46BST app.py             ERROR   : error message
2013-04-08T03:07:46BST submodule.py       INFO    : message from SubClass / __init__
2013-04-08T03:07:46BST submodule.py       INFO    : message from SubClass / SomeMethod

Я хочу иметь возможность определить регистратор в app.py, а затем в подмодулях использовать только стандартную библиотеку журналов Python, чтобы использовать уже настроенный регистратор в app.py.

Кроме того, уродливый обходной путь: если я помещу приведенный ниже код после импорта в submodule.py:

log = custom_logger.getLogger('root')

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

Другой обходной путь, который я рассмотрел: в конструкторе класса SubClass я мог бы определить

self.log = custom_logger.getLogger('root')

а затем используйте self.log.error ("некоторая ошибка"). Должен быть более хороший способ - если вы можете предложить что-то полезное или указать, где я неправильно понял документацию, я был бы очень благодарен!

PS. Я потратил немало времени, читая Howto по регистрации в Python (базовый и расширенный) и кулинарную книгу, поэтому, если я пропустил что-то полезное, пожалуйста, укажите на это.

Спасибо!

1 ответ

Если вы хотите изменить root logger, вы можете просто использовать getLogger() везде без аргументов.

Что касается установки экземпляра только в главном модуле, вы можете создать экземпляр своего логгера, добавить свой собственный обработчик и использовать его во всех других подмодулях (как я делал ниже).

Я создал класс, который наследует StreamHandler в custom_logger.py:

class MyHandler(logging.StreamHandler):

    def __init__(self):
        logging.StreamHandler.__init__(self)
        fmt = '%(asctime)s %(filename)-18s %(levelname)-8s: %(message)s'
        fmt_date = '%Y-%m-%dT%T%Z'
        formatter = logging.Formatter(fmt, fmt_date)
        self.setFormatter(formatter)

Затем в submodule.pyЯ поставил getLogger после импорта и прокомментировал его в методах:

import sys
import logging

log = logging.getLogger('root')

class SubClass(object):

    def __init__(self):
         log.info('message from SubClass / __init__')

    def SomeMethod(self):
        log.info('message from SubClass / SomeMethod')

Затем в app.py я создал экземпляр Logger (который будет одинаковым во всех модулях) и добавил свой обработчик, который форматирует вывод:

#!/usr/bin/python

import logging
import custom_logger
import submodule

log = logging.getLogger('root')
log.setLevel('DEBUG')
log.addHandler(custom_logger.MyHandler())

log.debug('debug message') 
log.info('info message')
log.warning('warning message')
log.error('error message')

a = submodule.SubClass() # this should produce a log message
a.SomeMethod()           # so should this

Выход:

./app.py 
2013-04-08T15:20:05EEST app.py             DEBUG   : debug message
2013-04-08T15:20:05EEST app.py             INFO    : info message
2013-04-08T15:20:05EEST app.py             WARNING : warning message
2013-04-08T15:20:05EEST app.py             ERROR   : error message
2013-04-08T15:20:05EEST submodule.py       INFO    : message from SubClass / __init__
2013-04-08T15:20:05EEST submodule.py       INFO    : message from SubClass / SomeMethod
Другие вопросы по тегам