Подтвердить что ты не робот

Python: модуль протоколирования - глобально

Эй, мне было интересно, как реализовать глобальный регистратор, который можно использовать везде с вашими собственными настройками:

У меня

class customLogger(logging.Logger):
   ...

в файле с его форматировщиками и другими материалами. Регистратор отлично работает сам по себе.

Я импортирую этот модуль в свой файл main.py и создаю такой объект:

self.log = log.customLogger(arguments)

Но, очевидно, я не могу получить доступ к этому объекту из других частей моего кода. Я использую неправильный подход? Есть ли лучший способ сделать это?

4b9b3361

Ответ 1

Используйте logging.getLogger(name), чтобы создать именованный глобальный регистратор.

main.py

import log
logger = log.setup_custom_logger('root')
logger.debug('main message')

import submodule

log.py

import logging

def setup_custom_logger(name):
    formatter = logging.Formatter(fmt='%(asctime)s - %(levelname)s - %(module)s - %(message)s')

    handler = logging.StreamHandler()
    handler.setFormatter(formatter)

    logger = logging.getLogger(name)
    logger.setLevel(logging.DEBUG)
    logger.addHandler(handler)
    return logger

submodule.py

import logging

logger = logging.getLogger('root')
logger.debug('submodule message')

Выход

2011-10-01 20:08:40,049 - DEBUG - main - main message
2011-10-01 20:08:40,050 - DEBUG - submodule - submodule message

Ответ 2

Поскольку я не нашел удовлетворительного ответа, я хотел бы немного рассказать об ответе на вопрос, чтобы дать представление о работе и намерениях библиотеки logging, которая поставляется с стандартом Python библиотека.

В отличие от подхода OP (оригинальный плакат) библиотека четко разделяет интерфейс на регистратор и конфигурацию самого регистратора.

Конфигурация обработчиков является прерогативой разработчика приложения, который использует вашу библиотеку.

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

В библиотеке logging представлены четыре компонента: регистраторы, обработчики, фильтры и форматиры.

  • Регистраторы отображают интерфейс, который использует код приложения.
  • Обработчики отправляют записи журнала (созданные регистраторами) в соответствующий пункт назначения.
  • Фильтры предоставляют более мелкие возможности для определения того, какие записи журнала выводятся.
  • Форматировщики определяют расположение записей журнала в конечном выпуске.

Общая структура проекта выглядит следующим образом:

Project/
|-- .../
|   |-- ...
|
|-- project/
|   |-- package/
|   |   |-- __init__.py
|   |   |-- module.py
|   |   
|   |-- __init__.py
|   |-- project.py
|
|-- ...
|-- ...

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

Хорошим соглашением для использования, когда именованием регистраторов является использование регистратора уровня модуля, в каждом модуле, который использует ведение журнала, называется следующим образом:

logger = logging.getLogger(__name__)

Специальная переменная __name__ относится к имени вашего модуля и выглядит примерно как project.package.module в зависимости от структуры кода приложения.

module.py (и любой другой класс) может выглядеть примерно так:

import logging
...
log = logging.getLogger(__name__)

class ModuleClass:
    def do_something(self):
        log.debug('do_something() has been called!')

Регистратор в каждом модуле будет распространять любое событие на родительский регистратор, который в свою очередь передает информацию своему прикрепленному обработчику! Аналогично структуре пакета/модуля python родительский логгер определяется пространством имен, используя "имена точечных модулей". Поэтому имеет смысл инициализировать регистратор специальной переменной __name__ (в приведенном выше примере имя соответствует строке "project.package.module" ).

Существует два варианта настройки журнала в глобальном масштабе:

  • Создайте учетную запись в project.py с именем __package__, которое в этом примере равно "проекту" и, следовательно, является родительским регистратором регистраторов всех подмодулей. Необходимо только добавить к этому регистратору соответствующий обработчик и форматтер.

  • Настройте регистратор с обработчиком и форматированием в исполняемом script (например, main.py) с именем самого верхнего пакета.

При разработке библиотеки, использующей ведение журнала, вы должны позаботиться о том, как библиотека использует ведение журнала - например, имена используемых журналов.

Выполнение script, например main.py, может выглядеть примерно так:

import logging
from project import App

def setup_logger():
    # create logger
    logger = logging.getLogger('project')
    logger.setLevel(logging.DEBUG)

    # create console handler and set level to debug
    ch = logging.StreamHandler()
    ch.setLevel(level)

    # create formatter
    formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(name)s: %(message)s')

    # add formatter to ch
    ch.setFormatter(formatter)

    # add ch to logger
    logger.addHandler(ch)

if __name__ == '__main__' and __package__ is None:
     setup_logger()
     app = App()
     app.do_some_funny_stuff()

Вызов метода log.setLevel(...) указывает сообщение журнала с наименьшей степенью серьезности, которое будет обрабатывать журнал, но не обязательно выводить! Это просто означает, что сообщение передается обработчику, пока уровень серьезности сообщения выше (или равен) установленному. Но обработчик отвечает за обработку сообщения журнала (например, путем печати или сохранения его).

Следовательно, библиотека logging предлагает структурированный и модульный подход, который просто необходимо использовать в соответствии с потребностями.

Документация по протоколированию

Ответ 3

Создайте экземпляр customLogger в вашем модуле журнала и используйте его как одноэлементный - просто используйте импортированный экземпляр, а не класс.

Ответ 4

Вы можете просто передать ему строку с общей подстрокой до первого периода. Части строки, разделенные периодом ( "." ), Могут использоваться для разных классов/модулей/файлов/и т.д. Так же (в частности, часть logger = logging.getLogger(loggerName)):

def getLogger(name, logdir=LOGDIR_DEFAULT, level=logging.DEBUG, logformat=FORMAT):
    base = os.path.basename(__file__)
    loggerName = "%s.%s" % (base, name)
    logFileName = os.path.join(logdir, "%s.log" % loggerName)
    logger = logging.getLogger(loggerName)
    logger.setLevel(level)
    i = 0
    while os.path.exists(logFileName) and not os.access(logFileName, os.R_OK | os.W_OK):
        i += 1
        logFileName = "%s.%s.log" % (logFileName.replace(".log", ""), str(i).zfill((len(str(i)) + 1)))
    try:
        #fh = logging.FileHandler(logFileName)
        fh = RotatingFileHandler(filename=logFileName, mode="a", maxBytes=1310720, backupCount=50)
    except IOError, exc:
        errOut = "Unable to create/open log file \"%s\"." % logFileName
        if exc.errno is 13: # Permission denied exception
            errOut = "ERROR ** Permission Denied ** - %s" % errOut
        elif exc.errno is 2: # No such directory
            errOut = "ERROR ** No such directory \"%s\"** - %s" % (os.path.split(logFileName)[0], errOut)
        elif exc.errno is 24: # Too many open files
            errOut = "ERROR ** Too many open files ** - Check open file descriptors in /proc/<PID>/fd/ (PID: %s)" % os.getpid()
        else:
            errOut = "Unhandled Exception ** %s ** - %s" % (str(exc), errOut)
        raise LogException(errOut)
    else:
        formatter = logging.Formatter(logformat)
        fh.setLevel(level)
        fh.setFormatter(formatter)
        logger.addHandler(fh)
    return logger

class MainThread:
    def __init__(self, cfgdefaults, configdir, pidfile, logdir, test=False):
        self.logdir = logdir
        logLevel = logging.DEBUG
        logPrefix = "MainThread_TEST" if self.test else "MainThread"
        try:
            self.logger = getLogger(logPrefix, self.logdir, logLevel, FORMAT)
        except LogException, exc:
            sys.stderr.write("%s\n" % exc)
            sys.stderr.flush()
            os._exit(0)
        else:
            self.logger.debug("-------------------- MainThread created.  Starting __init__() --------------------")

    def run(self):
        self.logger.debug("Initializing ReportThreads..")
        for (group, cfg) in self.config.items():
            self.logger.debug(" ------------------------------ GROUP '%s' CONFIG ------------------------------     " % group)
            for k2, v2 in cfg.items():
                self.logger.debug("%s <==> %s: %s" % (group, k2, v2))
            try:
                rt = ReportThread(self, group, cfg, self.logdir, self.test)
            except LogException, exc:
                sys.stderr.write("%s\n" % exc)
                sys.stderr.flush()
                self.logger.exception("Exception when creating ReportThread (%s)" % group)
                logging.shutdown()
                os._exit(1)
            else:
                self.threads.append(rt)
        self.logger.debug("Threads initialized.. \"%s\"" % ", ".join([t.name for t in self.threads]))
        for t in self.threads:
            t.Start()
        if not self.test:
            self.loop()


class ReportThread:
    def __init__(self, mainThread, name, config, logdir, test):
        self.mainThread = mainThread
        self.name = name
        logLevel = logging.DEBUG
        self.logger = getLogger("MainThread%s.ReportThread_%s" % ("_TEST" if self.test else "", self.name), logdir, logLevel, FORMAT)
        self.logger.info("init database...")
        self.initDB()
        # etc....

if __name__ == "__main__":
    # .....
    MainThread(cfgdefaults=options.cfgdefaults, configdir=options.configdir, pidfile=options.pidfile, logdir=options.logdir, test=options.test)