Python 로그 형식 문자열에 사용자 정의 필드를 어떻게 추가합니까?
내 현재 형식 문자열은 다음과 같습니다.
formatter = logging.Formatter('%(asctime)s : %(message)s')
이 포맷터를 포함하는 각 스크립트에서 다른 값을 갖는 app_name이라는 새 필드를 추가하고 싶습니다.
import logging
formatter = logging.Formatter('%(asctime)s %(app_name)s : %(message)s')
syslog.setFormatter(formatter)
logger.addHandler(syslog)
그러나 해당 app_name
값을 로거에 전달 하여 형식 문자열로 보간 하는 방법을 모르겠습니다 . 분명히 로그 메시지에 나타날 수 있지만 매번 전달하지만 이것은 지저분합니다.
난 노력 했어:
logging.info('Log message', app_name='myapp')
logging.info('Log message', {'app_name', 'myapp'})
logging.info('Log message', 'myapp')
하지만 작동하지 않습니다.
LoggerAdapter를 사용하면 모든 로깅 호출에 추가 정보를 전달할 필요가 없습니다.
import logging
extra = {'app_name':'Super App'}
logger = logging.getLogger(__name__)
syslog = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s %(app_name)s : %(message)s')
syslog.setFormatter(formatter)
logger.setLevel(logging.INFO)
logger.addHandler(syslog)
logger = logging.LoggerAdapter(logger, extra)
logger.info('The sky is so blue')
로그 (같은 것)
2013-07-09 17:39:33,596 Super App : The sky is so blue
필터 를 사용하여 컨텍스트 정보를 추가 할 수도 있습니다.
import logging
class AppFilter(logging.Filter):
def filter(self, record):
record.app_name = 'Super App'
return True
logger = logging.getLogger(__name__)
logger.addFilter(AppFilter())
syslog = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s %(app_name)s : %(message)s')
syslog.setFormatter(formatter)
logger.setLevel(logging.INFO)
logger.addHandler(syslog)
logger.info('The sky is so blue')
유사한 로그 레코드를 생성합니다.
그렇게하려면 extra에 매개 변수로 dict를 전달해야합니다.
logging.info('Log message', extra={'app_name': 'myapp'})
증명:
>>> import logging
>>> logging.basicConfig(format="%(foo)s - %(message)s")
>>> logging.warning('test', extra={'foo': 'bar'})
bar - test
또한 참고로 dict를 전달하지 않고 메시지를 기록하려고하면 실패합니다.
>>> logging.warning('test')
Traceback (most recent call last):
File "/usr/lib/python2.7/logging/__init__.py", line 846, in emit
msg = self.format(record)
File "/usr/lib/python2.7/logging/__init__.py", line 723, in format
return fmt.format(record)
File "/usr/lib/python2.7/logging/__init__.py", line 467, in format
s = self._fmt % record.__dict__
KeyError: 'foo'
Logged from file <stdin>, line 1
또 다른 방법은 사용자 지정 LoggerAdapter를 만드는 것입니다. 이는 형식을 변경할 수 없거나 형식이 고유 키를 전송하지 않는 코드 (예 : app_name ) 와 공유되는 경우 특히 유용합니다 .
class LoggerAdapter(logging.LoggerAdapter):
def __init__(self, logger, prefix):
super(LoggerAdapter, self).__init__(logger, {})
self.prefix = prefix
def process(self, msg, kwargs):
return '[%s] %s' % (self.prefix, msg), kwargs
그리고 코드에서 평소와 같이 로거를 만들고 초기화합니다.
logger = logging.getLogger(__name__)
# Add any custom handlers, formatters for this logger
myHandler = logging.StreamHandler()
myFormatter = logging.Formatter('%(asctime)s %(message)s')
myHandler.setFormatter(myFormatter)
logger.addHandler(myHandler)
logger.setLevel(logging.INFO)
마지막으로 래퍼 어댑터를 만들어 필요에 따라 접두사를 추가합니다.
logger = LoggerAdapter(logger, 'myapp')
logger.info('The world bores you when you are cool.')
출력은 다음과 같습니다.
2013-07-09 17:39:33,596 [myapp] The world bores you when you are cool.
Python3
Python3.2부터는 이제 LogRecordFactory를 사용할 수 있습니다.
>>> import logging
>>> logging.basicConfig(format="%(custom_attribute)s - %(message)s")
>>> old_factory = logging.getLogRecordFactory()
>>> def record_factory(*args, **kwargs):
record = old_factory(*args, **kwargs)
record.custom_attribute = "my-attr"
return record
>>> logging.setLogRecordFactory(record_factory)
>>> logging.info("hello")
my-attr - hello
물론 record_factory
모든 호출 가능으로 사용자 정의 할 custom_attribute
수 있으며 팩토리 호출 가능에 대한 참조를 유지하면 의 값을 업데이트 할 수 있습니다.
어댑터 / 필터를 사용하는 것보다 더 나은 이유는 무엇입니까?
- You don't need to pass your logger around the application
- It actually works with 3rd party libraries that uses their own logger (by just calling
logger = logging.getLogger(..)
) would now have the same log format. (this is not the case with Filters / Adapters where you need to be using the same logger object) - You can stack/chain multiple factories
Using mr2ert's answer, I came up with this comfortable solution (Though I guess it's not recommended) - Override the built-in logging methods to accept the custom argument and create the extra
dictionary inside the methods:
import logging
class CustomLogger(logging.Logger):
def debug(self, msg, foo, *args, **kwargs):
extra = {'foo': foo}
if self.isEnabledFor(logging.DEBUG):
self._log(logging.DEBUG, msg, args, extra=extra, **kwargs)
*repeat for info, warning, etc*
logger = CustomLogger('CustomLogger', logging.DEBUG)
formatter = logging.Formatter('%(asctime)s [%(foo)s] %(message)s')
handler = logging.StreamHandler()
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.debug('test', 'bar')
Output:
2019-03-02 20:06:51,998 [bar] test
This is the built in function for reference:
def debug(self, msg, *args, **kwargs):
"""
Log 'msg % args' with severity 'DEBUG'.
To pass exception information, use the keyword argument exc_info with
a true value, e.g.
logger.debug("Houston, we have a %s", "thorny problem", exc_info=1)
"""
if self.isEnabledFor(DEBUG):
self._log(DEBUG, msg, args, **kwargs)
참고URL : https://stackoverflow.com/questions/17558552/how-do-i-add-custom-field-to-python-log-format-string
'Programing' 카테고리의 다른 글
git이 "~"로 끝나는 모든 파일을 추적하지 못하도록하는 방법은 무엇입니까? (0) | 2020.11.17 |
---|---|
특정 클래스가있는 요소를 어떻게 선택합니까? (0) | 2020.11.17 |
여러 작업이있는 Ansible 핸들러를 작성하려면 어떻게해야합니까? (0) | 2020.11.17 |
net :: ERR_INSECURE_RESPONSE (Chrome) (0) | 2020.11.17 |
Windows 설치자를위한 무료 소프트웨어 : NSIS 대 WiX? (0) | 2020.11.17 |