Вместо попытки повлиять на глобальный 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'