81

logging出力を画面に出力するためにPython のメカニズムを使用しています。print ステートメントでこれを行うこともできますが、ユーザーが特定の種類の出力を無効にできるように、より細かく調整できるようにしたいと考えています。エラーに対して出力される形式が気に入っていますが、出力レベルが「情報」の場合は、より単純な形式を好むでしょう。

例えば:

  logger.error("Running cmd failed")
  logger.info("Running cmd passed")

この例では、エラーの形式を別の方法で出力したいと考えています。

# error
Aug 27, 2009 - ERROR: Running cmd failed
# info
Running cmd passed

複数のログ オブジェクトを使用せずに、ログ レベルごとに異なる形式を使用することは可能ですか? 出力をログに記録する方法を決定する if/else ステートメントが多数あるため、ロガーが作成されたら変更せずにこれを行うことをお勧めします。

4

8 に答える 8

85

この問題に遭遇したばかりで、上記の例に残された「穴」を埋めるのに苦労しました。これは、私が使用したより完全で機能するバージョンです。うまくいけば、これは誰かを助ける:

# Custom formatter
class MyFormatter(logging.Formatter):

    err_fmt  = "ERROR: %(msg)s"
    dbg_fmt  = "DBG: %(module)s: %(lineno)d: %(msg)s"
    info_fmt = "%(msg)s"


    def __init__(self, fmt="%(levelno)s: %(msg)s"):
        logging.Formatter.__init__(self, fmt)


    def format(self, record):

        # Save the original format configured by the user
        # when the logger formatter was instantiated
        format_orig = self._fmt

        # Replace the original format with one customized by logging level
        if record.levelno == logging.DEBUG:
            self._fmt = MyFormatter.dbg_fmt

        elif record.levelno == logging.INFO:
            self._fmt = MyFormatter.info_fmt

        elif record.levelno == logging.ERROR:
            self._fmt = MyFormatter.err_fmt

        # Call the original formatter class to do the grunt work
        result = logging.Formatter.format(self, record)

        # Restore the original format configured by the user
        self._fmt = format_orig

        return result

編集:

Halloleo への賛辞。スクリプトで上記を使用する方法の例を次に示します。

fmt = MyFormatter()
hdlr = logging.StreamHandler(sys.stdout)

hdlr.setFormatter(fmt)
logging.root.addHandler(hdlr)
logging.root.setLevel(DEBUG)

編集2:

Python3 のロギングが少し変更されました。Python3 のアプローチについては、こちらを参照してください。

于 2011-12-01T22:15:30.687 に答える
32

はい、カスタムFormatterクラスを持つことでこれを行うことができます:

class MyFormatter(logging.Formatter):
    def format(self, record):
        #compute s according to record.levelno
        #for example, by setting self._fmt
        #according to the levelno, then calling
        #the superclass to do the actual formatting
        return s

MyFormatter次に、インスタンスをハンドラーにアタッチします。

于 2009-08-27T19:20:42.063 に答える
16

また、JSの回答と同様ですが、よりコンパクトです。

class SpecialFormatter(logging.Formatter):
    FORMATS = {logging.DEBUG :"DBG: %(module)s: %(lineno)d: %(message)s",
               logging.ERROR : "ERROR: %(message)s",
               logging.INFO : "%(message)s",
               'DEFAULT' : "%(levelname)s: %(message)s"}

    def format(self, record):
        self._fmt = self.FORMATS.get(record.levelno, self.FORMATS['DEFAULT'])
        return logging.Formatter.format(self, record)

hdlr = logging.StreamHandler(sys.stderr)
hdlr.setFormatter(SpecialFormatter())
logging.root.addHandler(hdlr)
logging.root.setLevel(logging.INFO)
于 2013-01-25T10:44:12.990 に答える
10

これは、書式設定スタイルに依存する新しい実装に対する estani の回答の適応です。私はスタイル形式にlogging.Formatter依存していますが、適応させることができます。'{'より一般的になるように洗練され、書式設定スタイルとカスタム メッセージを へ__init__の引数として選択できるようにすることもできます。

class SpecialFormatter(logging.Formatter):
    FORMATS = {logging.DEBUG : logging._STYLES['{']("{module} DEBUG: {lineno}: {message}"),
           logging.ERROR : logging._STYLES['{']("{module} ERROR: {message}"),
           logging.INFO : logging._STYLES['{']("{module}: {message}"),
           'DEFAULT' : logging._STYLES['{']("{module}: {message}")}

    def format(self, record):
        # Ugly. Should be better
        self._style = self.FORMATS.get(record.levelno, self.FORMATS['DEFAULT'])
        return logging.Formatter.format(self, record)

hdlr = logging.StreamHandler(sys.stderr)
hdlr.setFormatter(SpecialFormatter())
logging.root.addHandler(hdlr)
logging.root.setLevel(logging.INFO)
于 2013-05-21T00:37:39.377 に答える
10

スタイルや内部フィールドに依存する代わりに、record.levelno (またはその他の基準) に応じて他のフォーマッターに委任するフォーマッターを作成することもできます。私の謙虚な意見では、これは少しクリーンなソリューションです。以下のコードは、2.7 以上のすべての Python バージョンで動作するはずです。

簡単な方法は次のようになります。

class MyFormatter(logging.Formatter):

    default_fmt = logging.Formatter('%(levelname)s in %(name)s: %(message)s')
    info_fmt = logging.Formatter('%(message)s')

    def format(self, record):
        if record.levelno == logging.INFO:
            return self.info_fmt.format(record)
        else:
            return self.default_fmt.format(record)

ただし、より一般的なものにすることもできます。

class VarFormatter(logging.Formatter):

    default_formatter = logging.Formatter('%(levelname)s in %(name)s: %(message)s')

    def __init__(self, formats):
        """ formats is a dict { loglevel : logformat } """
        self.formatters = {}
        for loglevel in formats:
            self.formatters[loglevel] = logging.Formatter(formats[loglevel])

    def format(self, record):
        formatter = self.formatters.get(record.levelno, self.default_formatter)
        return formatter.format(record)

ここでは入力として dict を使用しましたが、明らかにタプル、**kwargs など、ボートに浮かぶものは何でも使用できます。これは次のように使用されます。

formatter = VarFormatter({logging.INFO: '[%(message)s]', 
                          logging.WARNING: 'warning: %(message)s'})
<... attach formatter to logger ...>
于 2015-01-08T07:51:54.323 に答える
3

特定のレベルの書式設定をスキップするだけの場合は、次のような他の回答よりも簡単なことを行うことができます。

class FormatterNotFormattingInfo(logging.Formatter):
    def __init__(self, fmt = '%(levelname)s:%(message)s'):
        logging.Formatter.__init__(self, fmt)

    def format(self, record):
        if record.levelno == logging.INFO:
            return record.getMessage()
        return logging.Formatter.format(self, record)

これには、self._fmt や self._style などの内部変数を使用しないことで、3.2 リリースの前後で機能するという利点もあります。

于 2014-08-03T04:46:26.813 に答える