расширение logging.Logger модуль в Python 3.5 - PullRequest
0 голосов
/ 17 мая 2018

Я пытался создать новый класс Logger, создав подкласс logging.Logger. Python версия 3.5

У меня есть несколько модулей в моем приложении, и я настраиваю ведение журнала только в главном модуле, где я устанавливаю класс регистратора, используя logging.setLoggerClass(...)

Однако, когда я получаю тот же экземпляр Logger из какого-то другого модуля, он все равно создает новый экземпляр класса Logger, а не тот экземпляр дочернего класса, который я определил.

Например, мой код:

# module 1
import logging
class MyLoggerClass(logging.getLoggerClass()):
     def __init__(name):
         super(MyLoggerClass, self).__init__(name)

     def new_logger_method(...):
         # some new functionality

if __name__ == "__main__":
    logging.setLoggerClass(MyLoggerClass)
    mylogger = logging.getLogger("mylogger")
    # configuration of mylogger instance

# module 2
import logging
applogger = logging.getLogger("mylogger")
print(type(applogger))
def some_function():
     applogger.debug("in module 2 some_function")

Когда этот код будет выполнен, я ожидаю, что applogger в модуле 2 будет иметь тип MyLoggerClass. Я намерен использовать new_logger_method для некоторых новых функций.

Однако, поскольку applogger оказывается типа logging.Logger, при запуске кода Logger не имеет атрибута с именем new_logger_method.

Кто-нибудь когда-нибудь сталкивался с этой проблемой?

Заранее спасибо за любую помощь! Pranav

1 Ответ

0 голосов
/ 18 мая 2018

Вместо попытки повлиять на глобальный logger путем изменения фабрики логгеров по умолчанию, если вы хотите, чтобы ваш модуль хорошо работал в любой среде, вы должны определить логгер только для вашего модуля (и его дочерних элементов) и использовать его как Основной регистратор для всего остального более глубоко в структуре вашего модуля. Проблема в том, что вы явно хотите использовать класс logging.Logger, отличный от класса по умолчанию / определенного глобально, а модуль logging не предоставляет простого способа переключения фабрики на основе контекста, поэтому вам придется это делать сами.

Есть много способов сделать это, но мое личное предпочтение - быть максимально явным и определять свой собственный logger модуль, который вы будете затем импортировать в другие модули в вашем пакете всякий раз, когда вам нужно получить собственный регистратор , В вашем случае вы можете создать logger.py в корне вашего пакета и сделать что-то вроде:

import logging

class CustomLogger(logging.Logger):

    def __init__(self, name):
        super(CustomLogger, self).__init__(name)

    def new_logger_method(self, caller=None):
        self.info("new_logger_method() called from: {}.".format(caller))

def getLogger(name=None, custom_logger=True):
    if not custom_logger:
        return logging.getLogger(name)
    logging_class = logging.getLoggerClass()  # store the current logger factory for later
    logging._acquireLock()  # use the global logging lock for thread safety
    try:
        logging.setLoggerClass(CustomLogger)  # temporarily change the logger factory
        logger = logging.getLogger(name)
        logging.setLoggerClass(logging_class)  # be nice, revert the logger factory change
        return logger
    finally:
        logging._releaseLock()

Не стесняйтесь включать в него другую пользовательскую логику инициализации журнала, если вы того пожелаете. Затем из других ваших модулей (и подпакетов) вы можете импортировать этот регистратор и использовать его getLogger() для получения локального настраиваемого регистратора. Например, все, что вам нужно в module1.py, это:

from . import logger  # or `from package import logger` for external/non-relative use

log = logger.getLogger(__name__)  # obtain a main logger for this module

def test():  # lets define a function we can later call for testing
    log.new_logger_method("Module 1")

Это охватывает внутреннее использование - пока вы будете придерживаться этого шаблона во всех ваших модулях / подмодулях, у вас будет доступ к вашему настраиваемому регистратору.

Когда речь идет о внешнем использовании, вы можете написать простой тест, чтобы показать вам, что ваш собственный регистратор создается и , что он не мешает остальной системе ведения журналов, поэтому ваш пакет / модуль может быть объявлен добропорядочным гражданином . При условии, что ваш module1.py находится в пакете с именем package и вы хотите протестировать его в целом извне:

import logging  # NOTE: we're importing the global, standard `logging` module
import package.module1

logging.basicConfig()  # initialize the most rudimentary root logger
root_logger = logging.getLogger()  # obtain the root logger
root_logger.setLevel(logging.DEBUG)  # set root log level to DEBUG

# lets see the difference in Logger types:
print(root_logger.__class__)  # <class 'logging.RootLogger'>
print(package.module1.log.__class__)  # <class 'package.logger.CustomLogger'>

# you can also obtain the logger by name to make sure it's in the hierarchy
# NOTE: we'll be getting it from the standard logging module so outsiders need
#       not to know that we manage our logging internally
print(logging.getLogger("package.module1").__class__) # <class 'package.logger.CustomLogger'>

# and we can test that it indeed has the custom method:
logging.getLogger("package.module1").new_logger_method("root!")
# INFO:package.module1:new_logger_method() called from: root!.
package.module1.test()  # lets call the test method within the module
# INFO:package.module1:new_logger_method() called from: Module 1.

# however, this will not affect anything outside of your package/module, e.g.:
test_logger = logging.getLogger("test_logger")
print(test_logger.__class__)  # <class 'logging.Logger'>
test_logger.info("I am a test logger!")
# INFO:test_logger:I am a test logger!
test_logger.new_logger_method("root - test")
# AttributeError: 'Logger' object has no attribute 'new_logger_method'
...