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')
유사한 로그 레코드를 생성합니다.
파이썬3
Python 3.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
공장 호출 가능한 상태로 유지하면 업데이트될 수 있습니다.
어댑터/필터를 사용하는 것보다 더 나은 이유는 무엇입니까?
- 응용프로그램에서 로거를 전달할 필요가 없습니다.
- 실제로는 자체 로거를 사용하는 타사 라이브러리와 함께 작동합니다(통화만 하면).
logger = logging.getLogger(..)
이제 동일한 로그 형식이 됩니다.(같은 로거 개체를 사용해야 하는 필터/어댑터의 경우는 해당되지 않습니다.) - 여러 공장을 스택/체인할 수 있습니다.
그런 식으로 하려면 딕트를 매개 변수로 전달해야 합니다.
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
또한 딕트를 전달하지 않고 메시지를 기록하려고 하면 실패합니다.
>>> 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.
저는 이 SO 질문을 직접 구현한 후에 발견했습니다.누군가에게 도움이 되길 바랍니다.아래 코드에서 추가 키를 유도하고 있습니다.claim_id
로거 형식으로.다음이 있을 때마다 claim_id를 기록합니다.claim_id
환경에 존재하는 키.제 사용 사례에서는 AWS Lambda 함수에 대해 이 정보를 기록해야 했습니다.
import logging
import os
LOG_FORMAT = '%(asctime)s %(name)s %(levelname)s %(funcName)s %(lineno)s ClaimID: %(claim_id)s: %(message)s'
class AppLogger(logging.Logger):
# Override all levels similarly - only info overriden here
def info(self, msg, *args, **kwargs):
return super(AppLogger, self).info(msg, extra={"claim_id": os.getenv("claim_id", "")})
def get_logger(name):
""" This function sets log level and log format and then returns the instance of logger"""
logging.setLoggerClass(AppLogger)
logging.basicConfig(level=logging.INFO, format=LOG_FORMAT)
logger = logging.getLogger(name)
logger.setLevel(logging.INFO)
return logger
LOGGER = get_logger(__name__)
LOGGER.info("Hey")
os.environ["claim_id"] = "12334"
LOGGER.info("Hey")
요지: https://gist.github.com/ygivenx/306f2e4e1506f302504fb67abef50652
승인된 응답은 로그 파일에 형식을 기록하지 않았지만 형식은 sys 출력에 반영되었습니다.또는 더 간단한 접근 방식을 사용하여 다음과 같이 작업했습니다.
logging.basicConfig(filename="mylogfile.test",
filemode="w+",
format='%(asctime)s: ' +app_name+': %(message)s ',
level=logging.DEBUG)
이 extra
매핑, 그리고 당신이 애드혹 로그 메시지를 위해 그것을 사용자 정의하기를 원한다면, 이것은 Python 2.7+에서 작동합니다.LoggerAdapter
인 기값병니를 합니다.extra
의 의의가 사전extra
특정 메시지에서.
import logging
import os
import sys
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s %(levelname)-8s Py%(python)-4s pid:%(pid)-5s %(message)s',
)
_logger = logging.getLogger("my-logger")
_logger.setLevel(logging.DEBUG)
class DefaultExtrasAdapter(logging.LoggerAdapter):
def __init__(self, logger, extra):
super(DefaultExtrasAdapter, self).__init__(logger, extra)
def process(self, msg, kwargs):
# Speed gain if no extras are present
if "extra" in kwargs:
copy = dict(self.extra).copy()
copy.update(kwargs["extra"])
kwargs["extra"] = copy
else:
kwargs["extra"] = self.extra
return msg, kwargs
LOG = DefaultExtrasAdapter(_logger, {"python": sys.version_info[0], "pid": os.getpid()})
if __name__ == "__main__":
LOG.info("<-- With defaults")
LOG.info("<-- With my version", extra={"python": 3.10})
LOG.info("<-- With my pid", extra={"pid": 0})
LOG.info("<-- With both", extra={"python": 2.7, "pid": -1})
결과:
2021-08-05 18:58:27,308 INFO Py2 pid:8435 <-- With defaults
2021-08-05 18:58:27,309 INFO Py3.1 pid:8435 <-- With my version
2021-08-05 18:58:27,309 INFO Py2 pid:0 <-- With my pid
2021-08-05 18:58:27,309 INFO Py2.7 pid:-1 <-- With both
을 사용하여, 는 이 해결책을 . ( - 하고 mr2ert를 합니다.extra
메소드 내 사전:
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')
출력:
2019-03-02 20:06:51,998 [bar] test
다음은 참조를 위한 내장 기능입니다.
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)
가져오기 로깅;
클래스 LogFilter(로깅)입니다.필터):
def __init__(self, code):
self.code = code
def filter(self, record):
record.app_code = self.code
return True
logging.basicConfig(format='[%(asctime)s:%(levelname)s]::[%(모듈)s -> %(이름)s] - APP_CODE:%(app_code)s - MSG:%(메시지)s';
클래스 로거:
def __init__(self, className):
self.logger = logging.getLogger(className)
self.logger.setLevel(logging.ERROR)
@staticmethod
def getLogger(className):
return Logger(className)
def logMessage(self, level, code, msg):
self.logger.addFilter(LogFilter(code))
if level == 'WARN':
self.logger.warning(msg)
elif level == 'ERROR':
self.logger.error(msg)
else:
self.logger.info(msg)
클래스 테스트: logger = Logger.getLogger('Test')
if __name__=='__main__':
logger.logMessage('ERROR','123','This is an error')
언급URL : https://stackoverflow.com/questions/17558552/how-do-i-add-custom-field-to-python-log-format-string
'programing' 카테고리의 다른 글
WebDev 활용 방법x64에서 WebServer.exe(VS 웹 서버)? (0) | 2023.07.20 |
---|---|
SYS_GUID()를 어떻게 varchar로 변환합니까? (0) | 2023.07.20 |
SQL에서 NULL이 NULL과 일치하지 않는 이유는 무엇입니까? (0) | 2023.07.20 |
virtualenvwrapper.sh 은 pip 설치 후 어디에 있습니까? (0) | 2023.07.20 |
glib를 위한 "헬로 월드"를 구축할 수 없는 이유는 무엇입니까? (0) | 2023.07.20 |