Использование журнала 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!!!")

Предостережения:

  1. Вы должны запускать свои файлы как модули, иначе import [your module] не будет работать:
    • python -m [your module name].[your filename without .py]
  2. Имя регистратора для точки входа вашей программы будет __main__, но любое решение, использующее __name__, будет иметь эту проблему.