새 형식 문자열로 변수 데이터 로깅
파이썬 2.7.3에 로깅 기능을 사용합니다. 이 Python 버전에 대한 문서는 다음과 같습니다.
로깅 패키지는 str.format () 및 string.Template과 같은 최신 형식화 옵션보다 이전입니다. 이러한 최신 서식 옵션이 지원됩니다 ...
중괄호가있는 '새'형식을 좋아합니다. 그래서 나는 다음과 같은 것을 시도하고 있습니다.
log = logging.getLogger("some.logger")
log.debug("format this message {0}", 1)
그리고 오류가 발생합니다.
TypeError : 문자열 형식화 중에 일부 인수가 변환되지 않았습니다.
내가 여기서 무엇을 그리워?
PS 나는 사용하고 싶지 않다
log.debug("format this message {0}".format(1))
이 경우 메시지는 로거 레벨에 관계없이 항상 형식화되기 때문입니다.
편집 : 이 StyleAdapter
답변 과 달리 @Dunes의 답변에서 접근 방식을 살펴보십시오 . 로거의 메서드 (debug (), info (), error () 등)를 호출하는 동안 상용구없이 대체 형식화 스타일을 사용할 수 있습니다.
문서에서 — 대체 서식 스타일 사용 :
로깅 호출 (logger.debug (), logger.info () 등)은 실제 로깅 메시지 자체에 대한 위치 매개 변수 만 취하며, 키워드 매개 변수는 실제 로깅 호출을 처리하는 방법에 대한 옵션을 결정하는 데만 사용됩니다 (예 : exc_info 키워드 매개 변수 추적 정보를 기록해야 함을 나타내거나 로그에 추가 할 추가 컨텍스트 정보를 나타내는 추가 키워드 매개 변수). 따라서 내부적으로 로깅 패키지가 형식 문자열과 변수 인수를 병합하기 위해 %-포맷을 사용하기 때문에 str.format () 또는 string.Template 구문을 사용하여 로깅 호출을 직접 할 수 없습니다. 기존 코드에있는 모든 로깅 호출이 % 형식 문자열을 사용하기 때문에 이전 버전과의 호환성을 유지하면서이를 변경할 수 없습니다.
과:
그러나 {}-및 $-형식을 사용하여 개별 로그 메시지를 구성 할 수있는 방법이 있습니다. 메시지의 경우 임의의 개체를 메시지 형식 문자열로 사용할 수 있으며 로깅 패키지는 해당 개체에서 str ()을 호출하여 실제 형식 문자열을 가져올 수 있습니다.
이것을 wherever
모듈에 복사-붙여 넣기 :
class BraceMessage(object):
def __init__(self, fmt, *args, **kwargs):
self.fmt = fmt
self.args = args
self.kwargs = kwargs
def __str__(self):
return self.fmt.format(*self.args, **self.kwargs)
그때:
from wherever import BraceMessage as __
log.debug(__('Message with {0} {name}', 2, name='placeholders'))
참고 : 실제 포맷은 필요할 때까지 지연됩니다. 예를 들어 DEBUG 메시지가 기록되지 않으면 포맷이 전혀 수행되지 않습니다.
Dunes의 답변에 언급 된 키워드 문제가없는 또 다른 옵션이 있습니다. {0}
키워드 ( {foo}
) 인수가 아닌 위치 ( ) 인수 만 처리 할 수 있습니다 . 또한 형식을 지정하기 위해 두 번의 호출이 필요하지 않습니다 (밑줄 사용). 하위 분류의 ick-factor가 있습니다 str
.
class BraceString(str):
def __mod__(self, other):
return self.format(*other)
def __str__(self):
return self
class StyleAdapter(logging.LoggerAdapter):
def __init__(self, logger, extra=None):
super(StyleAdapter, self).__init__(logger, extra)
def process(self, msg, kwargs):
if kwargs.pop('style', "%") == "{": # optional
msg = BraceString(msg)
return msg, kwargs
다음과 같이 사용합니다.
logger = StyleAdapter(logging.getLogger(__name__))
logger.info("knights:{0}", "ni", style="{")
logger.info("knights:{}", "shrubbery", style="{")
물론 # optional
어댑터를 통해 모든 메시지가 새 스타일 형식을 사용하도록 강제하기 위해 표시된 확인을 제거 할 수 있습니다 .
몇 년 후이 답변을 읽는 모든 사람을위한 참고 사항 : Python 3.2 부터는 객체 와 함께 스타일 매개 변수 를 사용할 수 있습니다Formatter
.
로깅 (3.2부터)은 이러한 두 가지 추가 서식 스타일에 대한 향상된 지원을 제공합니다. Formatter 클래스는라는 추가 선택적 키워드 매개 변수를 사용하도록 향상되었습니다
style
. 기본값은'%'
이지만 다른 가능한 값은 다른 두 서식 스타일에 해당하는'{'
및'$'
입니다. 이전 버전과의 호환성은 기본적으로 유지되지만 (예상대로) 스타일 매개 변수를 명시 적으로 지정하면str.format()
또는에서 작동하는 형식 문자열을 지정할 수string.Template
있습니다.
문서는 예제를 제공합니다. logging.Formatter('{asctime} {name} {levelname:8s} {message}', style='{')
이 경우에도 logger
새 형식으로를 호출 할 수 없습니다 . 즉, 다음은 여전히 작동하지 않습니다.
logger.info("knights:{say}", say="ni") # Doesn't work!
logger.info("knights:{0}", "ni") # Doesn't work either
이것은 로깅이 printf 스타일 형식화 만 사용한다는 것을 알았을 때 문제에 대한 나의 해결책이었습니다. 로깅 호출을 동일하게 유지할 수 있습니다 log.info(__("val is {}", "x"))
. 코딩에 필요한 변경 사항은 로거를 StyleAdapter
.
from inspect import getargspec
class BraceMessage(object):
def __init__(self, fmt, args, kwargs):
self.fmt = fmt
self.args = args
self.kwargs = kwargs
def __str__(self):
return str(self.fmt).format(*self.args, **self.kwargs)
class StyleAdapter(logging.LoggerAdapter):
def __init__(self, logger):
self.logger = logger
def log(self, level, msg, *args, **kwargs):
if self.isEnabledFor(level):
msg, log_kwargs = self.process(msg, kwargs)
self.logger._log(level, BraceMessage(msg, args, kwargs), (),
**log_kwargs)
def process(self, msg, kwargs):
return msg, {key: kwargs[key]
for key in getargspec(self.logger._log).args[1:] if key in kwargs}
사용법은 다음과 같습니다.
log = StyleAdapter(logging.getLogger(__name__))
log.info("a log message using {type} substiution", type="brace")
중괄호 대체에 사용되는 핵심 단어를 포함하는 경우이 구현에 문제가 있다는 지적이의 가치 level
, msg
, args
, exc_info
, extra
또는 stack_info
. 의 log
메서드에서 사용하는 인수 이름 입니다 Logger
. 이러한 이름 중 하나가 필요한 경우 이러한 이름 process
을 제외하도록 수정 하거나 호출 log_kwargs
에서 제거 _log
하십시오. 추가 참고로,이 구현은 Logger (예 :)에 대한 맞춤법이 틀린 키워드를 자동으로 무시합니다 ectra
.
더 쉬운 해결책은 우수한 모듈 을 사용하는 것입니다.logbook
import logbook
import sys
logbook.StreamHandler(sys.stdout).push_application()
logbook.debug('Format this message {k}', k=1)
또는 더 완전한 :
>>> import logbook
>>> import sys
>>> logbook.StreamHandler(sys.stdout).push_application()
>>> log = logbook.Logger('MyLog')
>>> log.debug('Format this message {k}', k=1)
[2017-05-06 21:46:52.578329] DEBUG: MyLog: Format this message 1
다른 답변에서 언급했듯이 Python 3.2에 도입 된 중괄호 스타일 형식 은 실제 로그 메시지가 아닌 형식 문자열에만 사용됩니다.
Python 3.5부터는 중괄호 스타일 형식을 사용하여 메시지를 기록하는 좋은 방법이 없습니다.
그러나 Python의 대부분과 마찬가지로 좋지 않은 방법이 있습니다.
다음은 logging
모듈을 원숭이 패치하여 get_logger
처리하는 모든 로그 레코드에 대해 새로운 스타일 형식을 사용하는 로거를 반환 하는 함수 를 만듭니다 .
import functools
import logging
import types
def _get_message(record):
"""Replacement for logging.LogRecord.getMessage
that uses the new-style string formatting for
it's messages"""
msg = str(record.msg)
args = record.args
if args:
if not isinstance(args, tuple):
args = (args,)
msg = msg.format(*args)
return msg
def _handle_wrap(fcn):
"""Wrap the handle function to replace the passed in
record's getMessage function before calling handle"""
@functools.wraps(fcn)
def handle(record):
record.getMessage = types.MethodType(_get_message, record)
return fcn(record)
return handle
def get_logger(name=None):
"""Get a logger instance that uses new-style string formatting"""
log = logging.getLogger(name)
if not hasattr(log, "_newstyle"):
log.handle = _handle_wrap(log.handle)
log._newstyle = True
return log
용법:
>>> log = get_logger()
>>> log.warning("{!r}", log)
<logging.RootLogger object at 0x4985a4d3987b>
메모:
get_logger
함수에 의해 생성 된 특정 로거에만 영향을 미칩니다 .- 일반
logging.getLogger()
호출 에서 로거에 다시 액세스 하면 새 스타일 형식이 계속 적용됩니다. - kwargs are not supported
- Performance hit should be minimal (rewriting a single function pointer for each log message)
- The formatting of the message is delayed until it is output
- Doesn't stop the args from being stored on
logging.LogRecord
objects (useful in certain cases) - From looking at the
logging
module source code it seems like it should work all the way back to Python 2.6 whenstr.format
was introduced (but was only tested on Python 3.5).
Try logging.setLogRecordFactory
in Python 3.2+:
import collections
import logging
class _LogRecord(logging.LogRecord):
def getMessage(self):
msg = str(self.msg)
if self.args:
if isinstance(self.args, collections.Mapping):
msg = msg.format(**self.args)
else:
msg = msg.format(*self.args)
return msg
logging.setLogRecordFactory(_LogRecord)
Here's something real simple that works:
debug_logger: logging.Logger = logging.getLogger("app.debug")
def mydebuglog(msg: str, *args, **kwargs):
if debug_logger.isEnabledFor(logging.DEBUG):
debug_logger.debug(msg.format(*args, **kwargs))
Then:
mydebuglog("hello {} {val}", "Python", val="World")
I created a custom Formatter, called ColorFormatter that handles the problem like this:
class ColorFormatter(logging.Formatter):
def format(self, record):
# previous stuff, copy from logging.py…
try: # Allow {} style
message = record.getMessage() # printf
except TypeError:
message = record.msg.format(*record.args)
# later stuff…
This keeps it compatible with various libraries. The drawback is that it is probably not performant due to potentially attempting format of the string twice.
참고URL : https://stackoverflow.com/questions/13131400/logging-variable-data-with-new-format-string
'Programing' 카테고리의 다른 글
UUID를 숫자로 저장하는 방법은 무엇입니까? (0) | 2020.10.22 |
---|---|
grep에서 큰 따옴표 이스케이프 (0) | 2020.10.22 |
각 행에 대해 가장 큰 값의 열 이름을 반환합니다. (0) | 2020.10.22 |
Java 용 SASS 구현? (0) | 2020.10.22 |
PHP + PDO + MySQL : MySQL의 정수 및 숫자 열을 PHP에서 정수 및 숫자로 반환하려면 어떻게해야합니까? (0) | 2020.10.22 |