새 형식 문자열로 변수 데이터 로깅


85

파이썬 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))

이 경우 메시지는 로거 레벨에 관계없이 항상 형식화되기 때문입니다.


1
당신은 이것을 할 수 있습니다 : log.debug("format this message%d" % 1)
ronak

1
Formatter'{'를 스타일로 사용 하도록 구성해야합니다
mata

2
@ronak 조언을 해주셔서 감사합니다. 이유는 "ps"섹션을 참조하십시오. BTW log.debug ( "format this message % d", 1)-정상적으로 작동합니다.
MajesticRa

@mata 어떻게 구성합니까? 이를 수행하는 직접적인 문서가 있습니까?
MajesticRa

@mata 나는 그것을 찾았습니다. "정답입니다. 다시 한 번 감사합니다.
MajesticRa

답변:


38

편집 :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 메시지가 기록되지 않으면 포맷이 전혀 수행되지 않습니다.


4
Python 3.6부터 다음과 같이 f- 문자열을 사용할 수 있습니다.num = 2; name = 'placeholders'; log.debug(f'Message with {num} {name}')
Jacktose

11
@ P1h3r1e3d13 답변의 로깅 코드와 달리 f ''-문자열은 즉시 형식 지정을 수행합니다.
jfs

1
권리. 로그 메서드를 호출하기 전에 정규 문자열을 형식화하고 반환하기 때문에 여기에서 작동합니다. 그것은 누군가와 관련이있을 수도 있고 아닐 수도 있으므로 옵션으로 언급 할 가치가 있다고 생각합니다.
Jacktose

3
@Jacktose 저는 사용자가 f- 문자열을 사용하여 기록해서는 안된다고 생각합니다. 이는 로그 집계 서비스 (예 : 센트리)를 무력화시킵니다. stdlib 로깅이 문자열 템플릿을 연기하는 데에는 좋은 이유가 있습니다.

31

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

5
Python 3에 대한 귀하의 진술이 잘못되었습니다. 스타일 매개 변수는 개별 로그 메시지가 아닌 포맷터 형식 문자열에만 적용됩니다. 링크 한 페이지에 "이전 버전과의 호환성을 유지하면서 변경할 수 없습니다"라고 명시 적으로 표시됩니다.
mhsmith

1
정직하게 해주셔서 감사합니다. 첫 번째 부분은 지금은 덜 유용하지만 Formatter지금은 옳습니다 (제 생각에). StyleAdapter 스틸 작품
펠리페

@falstro-지적 해 주셔서 감사합니다. 이제 업데이트 된 버전이 작동합니다. BraceString은 문자열 하위 클래스 이므로 자신을 반환하는 것이 안전합니다__str__
Felipe

1
style = "{", +1
Tom S.

24

이것은 로깅이 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} substitution", type="brace")

중괄호 대체에 사용되는 핵심 단어를 포함하는 경우이 구현에 문제가 있다는 지적이의 가치 level, msg, args, exc_info, extra또는 stack_info. 의 log메서드에서 사용하는 인수 이름 입니다 Logger. 이러한 이름 중 하나가 필요한 경우 이러한 이름 process을 제외하도록 수정 하거나 호출 log_kwargs에서 제거 _log하십시오. 추가로,이 구현은 Logger (예 :)를 의미하는 맞춤법이 틀린 키워드를 조용히 무시합니다 ectra.


4
이 방법은 python doc, docs.python.org/3/howto/…에서
eshizhan

23

더 쉬운 해결책은 우수한 모듈 을 사용하는 것입니다.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

이것은 멋져 보이지만 몇 초가 아닌 밀리 초를 갖는 방법이 있습니까?
제프

@Jeff, logbook을 사용하면 사용자 지정 문자열 형식으로 사용자 지정 처리기를 정의하고 사용할 수 있습니다.
Thomas Orozco 2013 년

5
@Jeff 몇 년 후-기본 시간 정밀도는 밀리 초입니다.
Jan Vlcinsky

12

다른 답변에서 언급했듯이 Python 3.2에 도입 된 중괄호 스타일 형식 은 실제 로그 메시지가 아닌 형식 문자열에만 사용됩니다.

실제 로그 메시지에서 중괄호 형식의 형식을 사용하려면 로거 코드를 약간 원숭이 패치 할 수 있습니다.

다음은 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
    its 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>

메모:

  • 일반 로깅 방법 (바로 교체와 완벽하게 호환 logging.getLoggerget_logger)
  • get_logger함수에 의해 생성 된 특정 로거에만 영향을 미칩니다 (타사 패키지를 손상시키지 않음).
  • 일반 logging.getLogger()호출 에서 로거에 다시 액세스하는 경우 새 스타일 형식이 계속 적용됩니다.
  • kwargs로는 (차종이 불가능와 충돌에 내장에서 지원되지 않습니다 exc_info, stack_info, stacklevelextra)를.
  • 성능 적중은 최소화되어야합니다 (각 로그 메시지에 대해 단일 함수 포인터를 다시 작성).
  • 메시지 형식화는 출력 될 때까지 지연됩니다 (또는 로그 메시지가 필터링 된 경우에는 아예 아님).
  • Args는 logging.LogRecord평소와 같이 개체 에 저장됩니다 (사용자 지정 로그 처리기를 사용하는 경우에 유용함).
  • logging모듈 소스 코드를 살펴보면 Python 2.6 str.format이 도입 되었을 때 다시 작동해야하는 것처럼 보입니다 (하지만 3.5 이상에서만 테스트했습니다).

2
디버거 메시지가 인쇄되는 경우에만 디버그 문자열을 계산해야한다고 생각하는 유일한 대답입니다. 감사!
Fafaman

2

logging.setLogRecordFactoryPython 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)

작동하지만 문제는 %레코드 팩토리가 로깅 모듈에 대해 전역이므로 형식을 사용하는 타사 모듈을 손상 시키는 것입니다.
jtaylor

1

다음 과 같이 문제를 처리하는 ColorFormatter 라는 사용자 지정 Formatter를 만들었습니다 .

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…

이것은 다양한 라이브러리와의 호환성을 유지합니다. 단점은 잠재적으로 문자열 형식을 두 번 시도하기 때문에 성능이 떨어질 수 있다는 것입니다.


0

작동하는 정말 간단한 것이 있습니다.

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))

그때:

mydebuglog("hello {} {val}", "Python", val="World")

0

, 포장 'pR0Ps 유사 솔루션 getMessageLogRecord포장하여 makeRecord(대신 handle의 경우에 자신의 대답) Logger그 새로운 포맷 지원해야한다 :

def getLogger(name):
    log = logging.getLogger(name)
    def Logger_makeRecordWrapper(name, level, fn, lno, msg, args, exc_info, func=None, extra=None, sinfo=None):
        self = log
        record = logging.Logger.makeRecord(self, name, level, fn, lno, msg, args, exc_info, func, sinfo)
        def LogRecord_getMessageNewStyleFormatting():
            self = record
            msg = str(self.msg)
            if self.args:
                msg = msg.format(*self.args)
            return msg
        record.getMessage = LogRecord_getMessageNewStyleFormatting
        return record
    log.makeRecord = Logger_makeRecordWrapper
    return log

나는 이것을 Python 3.5.3으로 테스트했습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.