Python: модуль регистрации - глобально
Привет, мне было интересно, как реализовать глобальный регистратор, который мог бы использоваться везде с вашими настройками:
я имею
class customLogger(logging.Logger):
...
в файле с его форматерами и прочим. Регистратор работает отлично самостоятельно.
Я импортирую этот модуль в свой файл main.py и создаю такой объект:
self.log = log.customLogger(arguments)
Но, очевидно, я не могу получить доступ к этому объекту из других частей моего кода. Я использую неправильный подход? Есть лучший способ сделать это?
5 ответов
Использование 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
Поскольку я не нашел удовлетворительного ответа, я хотел бы более подробно остановиться на ответе на вопрос, чтобы дать некоторое представление о работе и намерениях logging
библиотека, которая поставляется со стандартной библиотекой Python.
В отличие от подхода OP (оригинальный постер) библиотека четко разделяет интерфейс к регистратору и конфигурацию самого регистратора.
Конфигурация обработчиков является прерогативой разработчика приложения, который использует вашу библиотеку.
Это означает, что вы не должны создавать собственный класс регистратора и настраивать регистратор внутри этого класса, добавляя любую конфигурацию или что-либо еще.
logging
Библиотека представляет четыре компонента: регистраторы, обработчики, фильтры и форматеры.
- Регистраторы предоставляют интерфейс, который непосредственно использует код приложения.
- Обработчики отправляют записи журнала (созданные регистраторами) в соответствующий пункт назначения.
- Фильтры предоставляют более точную возможность определить, какие записи журнала выводить.
- Форматировщики определяют формат записей журнала в конечном выводе.
Общая структура проекта выглядит следующим образом:
Project/
|-- .../
| |-- ...
|
|-- project/
| |-- package/
| | |-- __init__.py
| | |-- module.py
| |
| |-- __init__.py
| |-- project.py
|
|-- ...
|-- ...
Внутри вашего кода (как в module.py) вы обращаетесь к экземпляру logger вашего модуля для регистрации событий на их определенных уровнях.
Хорошим соглашением для использования при именовании регистраторов является использование регистратора уровня модуля, в каждом модуле, который использует ведение журнала, который называется следующим образом:
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__
который в данном примере равен "проекту" и, следовательно, является родительским регистратором регистраторов всех подмодулей. Необходимо только добавить соответствующий обработчик и форматер к этому логгеру.Настройте регистратор с обработчиком и форматером в исполняемом скрипте (например, main.py) с именем самого верхнего пакета.
При разработке библиотеки, в которой используется ведение журналов, вы должны позаботиться о том, чтобы документировать, как библиотека использует ведение журналов, например, имена используемых регистраторов.
Выполнение сценария, например, 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
библиотека предлагает структурированный и модульный подход, который просто необходимо использовать в соответствии со своими потребностями.
Модуль ведения журнала python уже достаточно хорош в качестве глобального регистратора, вы можете просто искать это:
main.py
import logging
logging.basicConfig(level = logging.DEBUG,format = '[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s')
Поместите приведенные выше коды в исполняемый скрипт, затем вы можете использовать этот регистратор с теми же конфигурациями в любом месте ваших проектов:
module.py
import logging
logger = logging.getLogger(__name__)
logger.info('hello world!')
Для более сложных конфигураций вы можете использовать файл конфигурации logging.conf с логированием.
logging.config.fileConfig("logging.conf")
Создать экземпляр customLogger
в вашем модуле журнала и используйте его в качестве одиночного - просто используйте импортированный экземпляр, а не класс.
Вы можете просто передать ему строку с общей подстрокой перед первым периодом. Части строки, разделенные точкой ("."), Могут быть использованы для разных классов / модулей / файлов / и т. Д. 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)