在python 3.5中扩展logging.Logger模块

时间:2018-05-17 18:40:46

标签: python python-3.x logging python-3.5

我一直在尝试通过继承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")

执行此代码时,我希望模块2中的applogger属于MyLoggerClass类型。我打算使用new_logger_method来获得一些新功能。

但是,由于applogger的格式为logging.Logger,因此当代码运行时,它会抛出Logger没有名为new_logger_method的属性。

有没有人遇到过这个问题?

提前感谢您的帮助! Pranav

1 个答案:

答案 0 :(得分:1)

通过更改默认记录器工厂而不是尝试影响全局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'