5

独自のフォーマッターを持つ複数のハンドラーを持つ単一のロガーがあります。次に、実行時にインデントレベルを制御して、インデント機能を追加します。すべてのハンドラーからのメッセージでこのインデントを取得したいと思います。フィルタとして作成しようとしましたが、メッセージの内容を変更できないようです。それから私はそれをフォーマッターとして試しましたが、ハンドラーごとに1つしか持てません。すべてのハンドラーのフォーマッターを明示的に変更せずに、このようなインデントを追加するにはどうすればよいですか?
私が持っているフォーマッターの1つは、出力に色を追加するクラスです。単純なフォーマット文字列ではありません。


また、設定ファイルを使用しています。理想的には、これをほとんどそこから運転できるようにしたいと思います。ただし、インデントフォーマッターの状態を変更する必要があります(たとえば、インデントレベルを設定する)が、メソッドがないため、その特定のフォーマッターに到達する方法がわかりませんlogger.getFormatter("by_name")
明確にするために、基本的にその場でフォーマットを調整するために、特定のフォーマッターインスタンスにアクセスする必要があります。インスタンスは、ファイルからlogging.configによって作成されています。名前を指定して特定のフォーマッターを取得できるアクセサーメソッドが見つかりません。

4

3 に答える 3

5
#!/usr/bin/env python

import logging
from random import randint

log = logging.getLogger("mylog")
log.setLevel(logging.DEBUG)

class MyFormatter(logging.Formatter):
    def __init__(self, fmt):
        logging.Formatter.__init__(self, fmt)

    def format(self, record):
        indent = " " * randint(0, 10) # To show that it works
        msg = logging.Formatter.format(self, record)
        return "\n".join([indent + x for x in msg.split("\n")])

# Log to file
filehandler = logging.FileHandler("indent.txt", "w")
filehandler.setLevel(logging.DEBUG)
filehandler.setFormatter(MyFormatter("%(levelname)-10s %(message)s"))
log.addHandler(filehandler)

# Log to stdout too
streamhandler = logging.StreamHandler()
streamhandler.setLevel(logging.INFO)
streamhandler.setFormatter(MyFormatter("%(message)s"))
log.addHandler(streamhandler)

# Test it
log.debug("Can you show me the dog-kennels, please")
log.info("They could grip it by the husk")
log.warning("That's no ordinary rabbit!")
log.error("Nobody expects the spanish inquisition")
try:
    crunchy_frog()
except:
    log.exception("It's a real frog")

結果:

    彼らは殻でそれをつかむことができました
    それは普通のウサギではありません!
          誰もスペイン異端審問を期待していません
         それは本物のカエルです
         トレースバック(最後の最後の呼び出し):
           ファイル"./logtest2.py"、36行目
             crunchy_frog()
         NameError:名前'crunchy_frog'が定義されていません

私はあなたの2番目の質問を理解するかどうかわかりません。

于 2011-01-20T09:58:51.880 に答える
1

これはもう1つ、ハッキーですが単純なものです。すべてのハンドラーのメッセージは、常にメッセージレベルの文字列で始まります。インデントを変更するたびに、これらのダーン文字列を変更するだけです。

# (make a LEVELS dict out of all the logging levels first)    
def indent(self, step = 1):
        "Change the current indent level by the step (use negative to decrease)"
        self._indent_level += step
        if self._indent_level < 0:
            self._indent_level = 0
        self._indent_str = self._indent_str_base * self._indent_level
        for lvl in LEVELS:
            level_name = self._indent_str + LEVELS[lvl]
            logging.addLevelName(lvl, level_name)

(インデント関数を取り巻くものについては、他の回答を参照してください)
これで、インデンターは、ロギングプロセスの詳細に煩わされることなく、独立したクラスになります。メッセージにレベル文字列が含まれている限り、その前に何かが入っていても、インデントはそこにあります。一般的には理想的ではありませんが、私にとってはうまくいくかもしれません。
誰かがどんなメッセージ形式でも機能するアイデアをもっと持っていますか?

于 2011-01-20T22:53:06.420 に答える
0

わかりました。これが、必要なものをほぼすべて取得する1つの方法です。LogRecordをサブクラス化して、インデントを挿入するgetMessageを上書きし、ロガーをサブクラス化してmakeRecordを作成します。

import logging
import logging.config  

################################################################################
class IndentingLogger(logging.Logger):
    """A Logger subclass to add indent on top of any logger output
    """
    ############################################################################
    def __init__(self, name = 'root', logging_level = logging.NOTSET):
        "Constructor to keep indent persistent"
        logging.Logger.__init__(self, name, logging_level)
        self.indenter = IndentedRecord("", logging.NOTSET, "", 0, None, None, None, None, None)

    ############################################################################
    def makeRecord(self, name, level, fn, lno, msg, args, exc_info, func=None, extra=None):
        return self.indenter.set_record(name, level, fn, lno, msg, args, exc_info, func, extra)


################################################################################
class IndentedRecord(logging.LogRecord):
    """A LogRecord subclass to add indent on top of any logger output
    """
    ######## Class data #########
    DEFAULT_INDENT_STR = '    '

    ############################################################################
    def __init__(self, name, level, fn, lno, msg, args, exc_info, func=None, extra=None):
        "Constructor"
        logging.LogRecord.__init__(self, name, level, fn, lno, msg, args, exc_info, func)
        self._indent_level = 0
        self._indent_str_base = IndentedRecord.DEFAULT_INDENT_STR
        self._indent_str = ""    # cache it

    ############################################################################
    def set_record(self, name, level, fn, lno, msg, args, exc_info, func=None, extra=None):
        "Constructs the base record"
        logging.LogRecord.__init__(self, name, level, fn, lno, msg, args, exc_info, func)
        return self

    ################################################################################
    def getMessage(self):
        "Adds indent on top of the normal getMessage result"

        # Call base class to get the formatted message
        message = logging.LogRecord.getMessage(self)

       # Now insert the indent
        return self._indent_str + message

    ################################################################################
    def indent(self, step = 1):
        "Change the current indent level by the step (use negative to decrease)"
        self._indent_level += step
        if self._indent_level < 0:
            self._indent_level = 0
        self._indent_str = self._indent_str_base * self._indent_level

    ################################################################################
    def set_indent_str(self, chars):
        "Change the current indent string"
        if not isinstance(chars, str):
            raise ValueError("Argument must be a string. Got %s" % chars)
        self._indent_str_base = chars

logging.config.fileConfig("reporter.conf")
logging.setLoggerClass(IndentingLogger)
logger = logging.getLogger('root') # will be wrong logger, if without argument

logger.debug("debug message")
logger.info("info message")
logger.indenter.indent(+1)
logger.warn("Indented? warn message")
logger.indenter.set_indent_str("***")
logger.error("Indented? error message: %s", "Oops, I did it again!")
logger.indenter.indent(+1)
logger.error("Indented? error message: %s", "Oops, I did it again!")
logger.indenter.indent(-1)
logger.critical("No indent; critical message")

結果は(実際には色付き)です:

Debug: debug message
Info: info message
Warning:     Indented? warn message
Error:     Indented? error message: Oops, I did it again!
Error: ******Indented? error message: Oops, I did it again!
Internal Error: ***No indent; critical message

どういうわけか、ログレベルの文字列はまだ前面に潜んでいるので、私が望んでいるものではありません。その上、これは厄介です-そのような単純な機能には多すぎます:(
より良いアイデア?

于 2011-01-20T22:37:05.550 に答える