簡體   English   中英

如何在不傳遞記錄器變量的情況下使用其他記錄器? (Python日志記錄)

[英]How to use different loggers without passing logger variable? (Python logging)

我有多個使用相同utils.py包的模塊。 如何使utils.py的記錄器utils.py而不必從調用者(即ClassA或ClassB)傳遞logger變量?

非常簡單的示例代碼如下。 實際上,我在utils.py有很多函數和類,這就是為什么我不想將logger變量傳遞給utils.py

~/test-two-loggers$ tree .

├── main.py
├── configs.py
├── ClassA.py
├── ClassB.py
└── utils.py

0 directories, 5 files

main.py

import ClassA
import ClassB

ClassA.func()
ClassB.func()

ClassA.py

import utils
import configs
import logging

def func():
    logger = logging.getLogger("classA")
    logger.info("in ClassA")
    utils.common_func(logger)  # I want to change this line!!!!

ClassB.py

import utils
import configs
import logging

def func():
    logger = logging.getLogger("classB")
    logger.info("in ClassB")
    utils.common_func(logger)  # I want to change this line!!!!

utils.py

def common_func(logger):  # I want to change this line!!!!
    # do a lot of things ClassA and ClassB both need to do
    logger.info("in utils - step one finished")
    # do a lot of things ClassA and ClassB both need to do
    logger.info("in utils - step two finished")
    # do a lot of things ClassA and ClassB both need to do
    logger.info("in utils - step three finished")

configs.py

import logging.config

logging_config = {
        "version": 1, 
        "formatters": {
            "formatter_a": {
                "format": u"[A][%(levelname)s] %(module)s.%(lineno)d: %(message)s"
            },
            "formatter_b": {
                "format": u"[B][%(levelname)s] %(module)s.%(lineno)d: %(message)s"
            },
        },
        "handlers": {
            "console_a": {
                "class": "logging.StreamHandler",
                "level": "DEBUG",
                "formatter": "formatter_a",
                "stream": "ext://sys.stdout"
            },
            "console_b": {
                "class": "logging.StreamHandler",
                "level": "DEBUG",
                "formatter": "formatter_b",
                "stream": "ext://sys.stdout"
            },
        },
        "loggers": {
            "classA": {
                "level": "DEBUG",
                "handlers": ["console_a"],
                "propagate": "no"
            },
            "classB": {
                "level": "DEBUG",
                "handlers": ["console_b"],
                "propagate": "no"
            },
        },
}

logging.config.dictConfig(logging_config)

結果我想要:

~/test-two-loggers$ python main.py 
[A][INFO] ClassA.7: in ClassA
[A][INFO] utils.3: in utils - step one finished
[A][INFO] utils.5: in utils - step two finished
[A][INFO] utils.7: in utils - step three finished
[B][INFO] ClassB.7: in ClassB
[B][INFO] utils.3: in utils - step one finished
[B][INFO] utils.5: in utils - step two finished
[B][INFO] utils.7: in utils - step three finished

但是我想要除此以外的其他解決方案。 我不想將logger變量傳遞給utils

看起來您正在尋找諸如隱式參數之類的東西。

這是Python所沒有的( 顯式優於隱式 )。

但是,像往常一樣,有一種或多或少的優雅方式可以模仿它:

class LoggerWrapper:
    def __init__(self, logger_name):
        self.logger = logging.getLogger(logger_name)

    def common_func(self):
        pass # do stuff here

logger = LoggerWrapper('classA')
logger.common_func()

如果您需要在util.common_func內使用記錄器,則無需傳遞它。 您可以只使用logging.getLoggerutils.common_func內檢索記錄器。 只要傳遞相同的字符串(例如classA ), logging.getLogger將返回相同的日志記錄對象。

logger1 = logging.getLogger('classA')
def func():
   logger2 = logging.getLogger('classA')
   print logger1 is logger2 #True

文檔中

請注意,永遠不會直接實例化Logger,而是始終通過模塊級函數logging.getLogger(name)實例化。 對具有相同名稱的getLogger()的多次調用將始終返回對同一Logger對象的引用。

假設您不對應用程序進行線程化,最簡單的解決方案是僅對模塊屬性進行monkeypatch:

util.py

import logging

logger = logging.getLogger(__name__)


def do_something():
    logger.info('Doing something')

whatever.py

import logging
from contextlib import contextmanager


@contextmanager
def patch_logger(module, logger):
    '''
    Patch the given ``module``'s logger with
    the provided ``logger``. Return the module's
    original logger when we're done.
    '''

    prev_logger = module.logger
    try:
        module.logger = logger
        yield
    finally:
        module.logger = prev_logger


class ClassA:
    def __init__(self):
        self.logger = logging.getLogger('A')

    def func(self):
        self.logger.info('Starting A')
        with patch_logger(util, self.logger):
            util.do_something()


class ClassB:
    def __init__(self):
        self.logger = logging.getLogger('B')

    def func(self):
        self.logger.info('Starting B')
        with patch_logger(util, self.logger):
            util.do_something()

有充分的理由不贊成猴子打補丁。

您可以更改格式器,並使用extra關鍵字參數將額外的字典參數傳遞給日志消息。 這樣,您就可以傳遞想要假裝從中調用記錄器的任何“模塊”。

因此,請從以下位置更改格式器:

"[A][%(levelname)s] %(module)s.%(lineno)d: %(message)s"

至:

"[A][%(levelname)s] %(mymodule)s.%(lineno)d: %(message)s"

並將您的函數稱為:

logger.info("in utils", extra={'mymodule':'somemodule'})

如果要使用從中調用的真實模塊,請將'somemodule'更改為__name__

我也可能只是覆蓋module的值(因此您必須更改格式化程序),但是logging不允許這樣做,因此似乎您必須更改格式化程序。


編輯:

只是為了使其更加清晰,您應該將ClassA.py func()更改為:

def func():
    logger = logging.getLogger("classA")
    logger.info("in ClassA", extra={'mymodule':__name__)
    logger.info("in utils", extra={'mymodule':'utils')
    utils.common_func() #call the function without passing the logger

並且在您的logging_config字典中,您應該將logging_config['formatters']['formatter_a']['format']的字符串module更改為mymodule

對ClassB也應如此。 很顯然,您應該刪除使用loggercommon_func行。


進一步參考:

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM