Использование журнала Python в нескольких модулях
У меня есть небольшой проект Python, который имеет следующую структуру -
Project
-- pkg01
-- test01.py
-- pkg02
-- test02.py
-- logging.conf
Я планирую использовать модуль регистрации по умолчанию для печати сообщений на стандартный вывод и файл журнала. Чтобы использовать модуль регистрации, требуется некоторая инициализация -
import logging.config
logging.config.fileConfig('logging.conf')
logger = logging.getLogger('pyApp')
logger.info('testing')
В настоящее время я выполняю эту инициализацию в каждом модуле, прежде чем начать регистрировать сообщения. Можно ли выполнить эту инициализацию только один раз в одном месте, чтобы одни и те же настройки использовались повторно при ведении журнала по всему проекту?
Ответы
Ответ 1
Наилучшая практика заключается в том, чтобы в каждом модуле иметь такой журнал:
import logging
logger = logging.getLogger(__name__)
рядом с верхней частью модуля, а затем в другом коде в модуле, например,
logger.debug('My message with %s', 'variable data')
Если вам нужно разделить активность журналирования внутри модуля, используйте, например,
loggerA = logging.getLogger(__name__ + '.A')
loggerB = logging.getLogger(__name__ + '.B')
и войдите в loggerA
и loggerB
, если это необходимо.
В вашей основной программе или программах выполните, например:
def main():
"your program code"
if __name__ == '__main__':
import logging.config
logging.config.fileConfig('/path/to/logging.conf')
main()
или
def main():
import logging.config
logging.config.fileConfig('/path/to/logging.conf')
# your program code
if __name__ == '__main__':
main()
Смотрите здесь для ведения журнала из нескольких модулей и здесь для регистрации конфигурации для кода, который будет использоваться как библиотечный модуль другим кодом.
Обновление: При вызове fileConfig()
вы можете указать disable_existing_loggers=False
, если используете Python 2.6 или новее (см. docs для получения дополнительной информации). Значение по умолчанию True
для обратной совместимости, что приводит к отключению всех существующих регистраторов с помощью fileConfig()
, если они или их предок явно не указаны в конфигурации. При значении, установленном на False
, существующие регистраторы остаются в силе. Если вы используете Python 2.7/Python 3.2 или новее, вы можете рассмотреть API dictConfig()
, который лучше, чем fileConfig()
, поскольку он дает больше контроля над конфигурацией.
Ответ 2
Фактически каждый регистратор является дочерним элементом родительского регистратора пакетов (т.е. package.subpackage.module
наследует конфигурацию от package.subpackage)
, поэтому все, что вам нужно сделать, это просто настроить корневой регистратор. Этого можно достичь с помощью logging.config.fileConfig
(ваша собственная конфигурация для регистраторов) или logging.basicConfig
(устанавливает root logger). Регистрация в вашем модуле ввода (__main__.py
или все, что вы хотите запустить, например main_script.py
. __init__.py
работает также)
используя basicConfig:
# package/__main__.py
import logging
import sys
logging.basicConfig(stream=sys.stdout, level=logging.INFO)
с помощью fileConfig:
# package/__main__.py
import logging
import logging.config
logging.config.fileConfig('logging.conf')
а затем создайте каждый регистратор, используя:
# package/submodule.py
# or
# package/subpackage/submodule.py
import logging
log = logging.getLogger(__name__)
log.info("Hello logging!")
Для получения дополнительной информации см. Расширенное руководство по протоколированию.
Ответ 3
Я всегда делаю это, как показано ниже.
Используйте один файл Python для настройки моего журнала как одноэлементного шаблона с именем ' log_conf.py
'
#-*-coding:utf-8-*-
import logging.config
def singleton(cls):
instances = {}
def get_instance():
if cls not in instances:
instances[cls] = cls()
return instances[cls]
return get_instance()
@singleton
class Logger():
def __init__(self):
logging.config.fileConfig('logging.conf')
self.logr = logging.getLogger('root')
В другом модуле просто импортируйте конфиг.
from log_conf import Logger
Logger.logr.info("Hello World")
Это единый шаблон для регистрации, просто и эффективно.
Ответ 4
Некоторые из этих ответов предполагают, что в верхней части модуля вы делаете
import logging
logger = logging.getLogger(__name__)
Насколько я понимаю, это считается очень плохой практикой. Причина в том, что файл конфигурации отключит все существующие регистраторы по умолчанию. Например
#my_module
import logging
logger = logging.getLogger(__name__)
def foo():
logger.info('Hi, foo')
class Bar(object):
def bar(self):
logger.info('Hi, bar')
И в вашем основном модуле:
#main
import logging
# load my module - this now configures the logger
import my_module
# This will now disable the logger in my module by default, [see the docs][1]
logging.config.fileConfig('logging.ini')
my_module.foo()
bar = my_module.Bar()
bar.bar()
Теперь журнал, указанный в logging.ini, будет пустым, так как существующий регистратор был отключен вызовом fileconfig.
Хотя это, безусловно, можно обойти (disable_existing_Loggers = False), реально многие клиенты вашей библиотеки не будут знать об этом и не будут получать ваши журналы. Сделайте это проще для ваших клиентов, всегда вызывая logging.getLogger локально. Наконечник шляпы: я узнал об этом поведении от Веб-сайта Виктора Линя.
Поэтому хорошей практикой является всегда вызывать logging.getLogger локально. Например
#my_module
import logging
logger = logging.getLogger(__name__)
def foo():
logging.getLogger(__name__).info('Hi, foo')
class Bar(object):
def bar(self):
logging.getLogger(__name__).info('Hi, bar')
Кроме того, если вы используете fileconfig в своем основном файле, установите disable_existing_loggers = False, на тот случай, если ваши разработчики библиотеки используют экземпляры логгера уровня модуля.
Ответ 5
Бросив в другое решение.
В моем модуле init.py у меня есть что-то вроде:
# mymodule/__init__.py
import logging
def get_module_logger(mod_name):
logger = logging.getLogger(mod_name)
handler = logging.StreamHandler()
formatter = logging.Formatter(
'%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
return logger
Затем в каждом модуле мне нужен регистратор, я делаю:
# mymodule/foo.py
from [modname] import get_module_logger
logger = get_module_logger(__name__)
Когда пропущены журналы, вы можете различать их источник по модулю, из которого они пришли.
Ответ 6
@Решение Yarkee показалось лучше. Я хотел бы добавить somemore к нему -
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances.keys():
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
class LoggerManager(object):
__metaclass__ = Singleton
_loggers = {}
def __init__(self, *args, **kwargs):
pass
@staticmethod
def getLogger(name=None):
if not name:
logging.basicConfig()
return logging.getLogger()
elif name not in LoggerManager._loggers.keys():
logging.basicConfig()
LoggerManager._loggers[name] = logging.getLogger(str(name))
return LoggerManager._loggers[name]
log=LoggerManager().getLogger("Hello")
log.setLevel(level=logging.DEBUG)
Итак, LoggerManager может быть подключаемым ко всему приложению.
Надеюсь, это имеет смысл и ценность.
Ответ 7
Вы также можете придумать что-то вроде этого!
def get_logger(name=None):
default = "__app__"
formatter = logging.Formatter('%(levelname)s: %(asctime)s %(funcName)s(%(lineno)d) -- %(message)s',
datefmt='%Y-%m-%d %H:%M:%S')
log_map = {"__app__": "app.log", "__basic_log__": "file1.log", "__advance_log__": "file2.log"}
if name:
logger = logging.getLogger(name)
else:
logger = logging.getLogger(default)
fh = logging.FileHandler(log_map[name])
fh.setFormatter(formatter)
logger.addHandler(fh)
logger.setLevel(logging.DEBUG)
return logger
Теперь вы можете использовать несколько логгеров в одном модуле и во всем проекте, если вышеуказанное определено в отдельном модуле и импортировано в другие модули, требуется ведение журнала.
a=get_logger("__app___")
b=get_logger("__basic_log__")
a.info("Starting logging!")
b.debug("Debug Mode")
Ответ 8
Есть несколько ответов. я получил похожее, но другое решение, которое имеет смысл для меня, может быть, оно будет иметь смысл и для вас. Моя главная цель состояла в том, чтобы иметь возможность передавать журналы обработчикам по их уровню (журналы уровня отладки на консоль, предупреждения и выше в файлы):
from flask import Flask
import logging
from logging.handlers import RotatingFileHandler
app = Flask(__name__)
# make default logger output everything to the console
logging.basicConfig(level=logging.DEBUG)
rotating_file_handler = RotatingFileHandler(filename="logs.log")
rotating_file_handler.setLevel(logging.INFO)
app.logger.addHandler(rotating_file_handler)
создал хороший утилитный файл с именем logger.py:
import logging
def get_logger(name):
return logging.getLogger("flask.app." + name)
the flask.app - это жестко закодированное значение в колбе. регистратор приложений всегда запускается с flask.app в качестве имени модуля.
Теперь в каждом модуле я могу использовать его в следующем режиме:
from logger import get_logger
logger = get_logger(__name__)
logger.info("new log")
Это создаст новый журнал для "app.flask.MODULE_NAME" с минимальными усилиями.
Ответ 9
Лучше всего было бы создать модуль отдельно, у которого есть только один метод, задача которого состоит в том, чтобы предоставить обработчик логгера вызывающему методу. Сохраните этот файл как m_logger.py
import logger, logging
def getlogger():
# logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
# create console handler and set level to debug
#ch = logging.StreamHandler()
ch = logging.FileHandler(r'log.txt')
ch.setLevel(logging.DEBUG)
# create formatter
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
# add formatter to ch
ch.setFormatter(formatter)
# add ch to logger
logger.addHandler(ch)
return logger
Теперь вызывайте метод getlogger() всякий раз, когда необходим обработчик логгера.
from m_logger import getlogger
logger = getlogger()
logger.info('My mssg')
Ответ 10
Впервые в Python, поэтому я не знаю, целесообразно ли это, но он отлично работает, если не переписывать шаблон.
Ваш проект должен иметь init.py, чтобы его можно было загрузить как модуль
# Put this in your module __init__.py
import logging.config
import sys
# I used this dictionary test, you would put:
# logging.config.fileConfig('logging.conf')
# The "" entry in loggers is the root logger, tutorials always
# use "root" but I can't get that to work
logging.config.dictConfig({
"version": 1,
"formatters": {
"default": {
"format": "%(asctime)s %(levelname)s %(name)s %(message)s"
},
},
"handlers": {
"console": {
"level": 'DEBUG',
"class": "logging.StreamHandler",
"stream": "ext://sys.stdout"
}
},
"loggers": {
"": {
"level": "DEBUG",
"handlers": ["console"]
}
}
})
def logger():
# Get the name from the caller of this function
return logging.getLogger(sys._getframe(1).f_globals['__name__'])
sys._getframe(1)
предложение исходит от здесь
Затем использовать ваш регистратор в любом другом файле:
from [your module name here] import logger
logger().debug("FOOOOOOOOO!!!")
Предостережения:
- Вы должны запускать свои файлы как модули, иначе
import [your module]
не будет работать:
python -m [your module name].[your filename without .py]
- Имя регистратора для точки входа вашей программы будет
__main__
, но любое решение, использующее __name__
, будет иметь эту проблему.