![](/img/trans.png)
[英]How to configure and use specific loggers in python based upon logger name
[英]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.getLogger
在utils.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也應如此。 很顯然,您應該刪除使用logger
的common_func
行。
進一步參考:
extra
kwarg相關的部分。)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.