Programing

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

lottogame 2020. 10. 22. 07:36
반응형

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


파이썬 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 when str.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

반응형