Pythonのログ出力に色を付けるにはどうすればよいですか?


352

しばらく前に、おそらくすべてのメッセージが標準化されているために、おそらくログシステムが原因で、カラー出力のMonoアプリケーションを見ました。

現在、Pythonにはloggingモジュールがあり、出力をカスタマイズするための多くのオプションを指定できます。したがって、Pythonでも同様のことが可能になると想像していますが、これをどこで行うのかわかりません。

Python loggingモジュールをカラーで出力する方法はありますか?

たとえば、赤でエラーを表示したり、青や黄色でデバッグメッセージを表示したりします。

もちろん、これにはおそらく互換性のある端末が必要です(ほとんどの最新の端末はそうです)。しかしlogging、色がサポートされていない場合は、元の出力にフォールバックできます。

Loggingモジュールで色付きの出力を取得する方法はありますか?


1
LinuxとWindowsの両方のマルチプラットフォームソリューションが必要であることを指定する必要があります。
ソリン

1
Eclipse / PyDevを使用している場合に関連:Eclipse コンソールでログを
色分けする


5
また、すべてのオペレーティングシステムとPythonバージョン(2.7および3. *)をサポートするために私が書いたchromalogを試すこともできます
ereOn

1
ログファイルに実際にANSIコードをダンプするソリューションはお勧めできません。6か月後に何かをgreppingしているときに、正規表現パターンでANSI文字を許可することを忘れると、それらはあなたを見つけ出します。ログが書き込まれるときではなく、ログを表示するときに色を追加するいくつかの解決策があります...
ジョナサンハートレー

回答:


192

カラーエスケープについてはすでに知っていました。しばらく前にbashプロンプトで使用しました。とにかくありがとう。
私が欲しかったのは、それをロギングモジュールと統合することでした。
これが私が最終的に何をするかです:

BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)

#The background is set with 40 plus the number of the color, and the foreground with 30

#These are the sequences need to get colored ouput
RESET_SEQ = "\033[0m"
COLOR_SEQ = "\033[1;%dm"
BOLD_SEQ = "\033[1m"

def formatter_message(message, use_color = True):
    if use_color:
        message = message.replace("$RESET", RESET_SEQ).replace("$BOLD", BOLD_SEQ)
    else:
        message = message.replace("$RESET", "").replace("$BOLD", "")
    return message

COLORS = {
    'WARNING': YELLOW,
    'INFO': WHITE,
    'DEBUG': BLUE,
    'CRITICAL': YELLOW,
    'ERROR': RED
}

class ColoredFormatter(logging.Formatter):
    def __init__(self, msg, use_color = True):
        logging.Formatter.__init__(self, msg)
        self.use_color = use_color

    def format(self, record):
        levelname = record.levelname
        if self.use_color and levelname in COLORS:
            levelname_color = COLOR_SEQ % (30 + COLORS[levelname]) + levelname + RESET_SEQ
            record.levelname = levelname_color
        return logging.Formatter.format(self, record)

それを使用するには、独自のロガーを作成します。

# Custom logger class with multiple destinations
class ColoredLogger(logging.Logger):
    FORMAT = "[$BOLD%(name)-20s$RESET][%(levelname)-18s]  %(message)s ($BOLD%(filename)s$RESET:%(lineno)d)"
    COLOR_FORMAT = formatter_message(FORMAT, True)
    def __init__(self, name):
        logging.Logger.__init__(self, name, logging.DEBUG)                

        color_formatter = ColoredFormatter(self.COLOR_FORMAT)

        console = logging.StreamHandler()
        console.setFormatter(color_formatter)

        self.addHandler(console)
        return


logging.setLoggerClass(ColoredLogger)

他の誰かがそれを必要とする場合に備えて。

複数のロガーまたはハンドラーを使用している場合は注意してください。ColoredFormatter他のハンドラーに渡されるか、他のロガーに伝達されるレコードオブジェクトを変更しています。ファイルロガーなどを設定している場合は、ログファイルに色を付けたくないでしょう。これを回避するには、フォーマットされた文字列を返す前に、levelname属性を操作する前にrecordwithのコピーを作成するcopy.copy()か、レベル名を以前の値にリセットするのがおそらく最善です(コメントのMichaelへのクレジット)。


YELLOW、WHITE、BLUEなどはどこで定義されていますか?
スワループCH

1
@Swaroop -あなたがGoogleで検索し、読み取り、またはここに見つけることができるものですANSIエスケープコード、:en.wikipedia.org/wiki/ANSI_escape_code、あるいはpueblo.sourceforge.net/doc/manual/ansi_color_codes.html
ブライアン・Mは、ハント

53
これだけのためにロガーサブクラスを作成する必要があるとは思いません。特別なものを作成し、Formatterその使用を指定する限り、あなたの答えは問題ありませんStreamHandler。しかし、ロガーのサブクラスは必要ありません。実際、ロガークラスを使用すると、作成されたすべてのロガーにハンドラーが追加されますが、これは通常は必要ありません。
Vinay Sajip、2009


6
片側の注意事項ColoredFormatter。それはレコードオブジェクトを変更し、それはさらに他のハンドラーに渡されるか、他のロガーに伝播されます。ファイルロガーなどを設定している場合は、ログファイルに色を付けたくないでしょう。これを回避するには、レベル名属性を操作する前にrecordwithのコピーを作成するcopy.copy()か、フォーマットされた文字列を返す前にレベル名を以前の値にリセットするのがおそらく最善です。
マイケル、

149

数年前、私は自分用のカラーストリームハンドラーを作成しました。それから私はこのページに出くわし、人々がコピー/貼り付けしているコードスニペットのコレクションを見つけました:-(。私のストリームハンドラは現在UNIX(Linux、Mac OS X)でのみ機能しますが、PyPI(およびGitHub)で利用できるという利点があります)そして、それは非常に簡単に使用できます。また、Vim構文モードもあります:-)。将来的には、Windowsで動作するように拡張する予定です。

パッケージをインストールするには:

$ pip install coloredlogs

機能することを確認するには:

$ coloredlogs --demo

独自のコードを開始するには:

$ python
> import coloredlogs, logging
> coloredlogs.install()
> logging.info("It works!")
2014-07-30 21:21:26 peter-macbook root[7471] INFO It works!

上記の例に示されているデフォルトのログ形式には、日付、時刻、ホスト名、ロガーの名前、PID、ログレベル、およびログメッセージが含まれています。これは実際には次のようになります。

色付きログ出力のスクリーンショット

注:MinTTYでGit Bashを使用する場合

WindowsのGit Bashには、文書化されたいくつかの癖があります: WinptyとGit Bash

ANSIエスケープコードとncursesスタイルの文字書き換えとア​​ニメーションの場合、コマンドの前にを付ける必要がありますwinpty

$ winpty coloredlogs --demo
$ winpty python your_colored_logs_script.py

2
面白いことに、このスレッドに「pypi.python.org/pypi/coloredlogs/0.4.7」へのリンクを追加するだけでした
Iosu S. 2014

1
何らかの理由でAttributeError: 'module' object has no attribute 'install'、を使用しているときに取得し続けcoloredlogs.install()ます。最新版で確認できますか?
con-f-use

11
これは美しく見えます。残念ながら、それは多くのものを壊します。特に、logging.basicConfigの呼び出しは無効になります。これにより、たとえばカスタムフォーマッタを使用できなくなります。
クレメント

@Clément:2つの(重複する)質問:(1)「logging.basicConfigへの呼び出しを無効にする」とはどういう意味ですか?(2)代替案は何ですか?どちらlogging.basicConfig()coloredlogs.install()その「ボイド」なしでコンソールにログインすると、あなたが重複したメッセージを取得することをストリームハンドラをインストール...
xolox

(1)の魔法、または(より合理的に)パッケージのcoloredlogs.installように、使用する形式を指示する方法のいずれかを期待していましたcolorlog
クレメント

74

これは、あらゆるプラットフォームで機能するソリューションです。教えてくれない場合は更新します。

仕組み:ANSIエスケープをサポートするプラットフォームではエスケープを使用し(Windows以外)、WindowsではAPI呼び出しを使用してコンソールの色を変更します。

スクリプトは、ラッパーを追加する標準ライブラリのlogging.StreamHandler.emitメソッドをハックします。

TestColorer.py

# Usage: add Colorer.py near you script and import it.
import logging
import Colorer

logging.warn("a warning")
logging.error("some error")
logging.info("some info")

Colorer.py

#!/usr/bin/env python
# encoding: utf-8
import logging
# now we patch Python code to add color support to logging.StreamHandler
def add_coloring_to_emit_windows(fn):
        # add methods we need to the class
    def _out_handle(self):
        import ctypes
        return ctypes.windll.kernel32.GetStdHandle(self.STD_OUTPUT_HANDLE)
    out_handle = property(_out_handle)

    def _set_color(self, code):
        import ctypes
        # Constants from the Windows API
        self.STD_OUTPUT_HANDLE = -11
        hdl = ctypes.windll.kernel32.GetStdHandle(self.STD_OUTPUT_HANDLE)
        ctypes.windll.kernel32.SetConsoleTextAttribute(hdl, code)

    setattr(logging.StreamHandler, '_set_color', _set_color)

    def new(*args):
        FOREGROUND_BLUE      = 0x0001 # text color contains blue.
        FOREGROUND_GREEN     = 0x0002 # text color contains green.
        FOREGROUND_RED       = 0x0004 # text color contains red.
        FOREGROUND_INTENSITY = 0x0008 # text color is intensified.
        FOREGROUND_WHITE     = FOREGROUND_BLUE|FOREGROUND_GREEN |FOREGROUND_RED
       # winbase.h
        STD_INPUT_HANDLE = -10
        STD_OUTPUT_HANDLE = -11
        STD_ERROR_HANDLE = -12

        # wincon.h
        FOREGROUND_BLACK     = 0x0000
        FOREGROUND_BLUE      = 0x0001
        FOREGROUND_GREEN     = 0x0002
        FOREGROUND_CYAN      = 0x0003
        FOREGROUND_RED       = 0x0004
        FOREGROUND_MAGENTA   = 0x0005
        FOREGROUND_YELLOW    = 0x0006
        FOREGROUND_GREY      = 0x0007
        FOREGROUND_INTENSITY = 0x0008 # foreground color is intensified.

        BACKGROUND_BLACK     = 0x0000
        BACKGROUND_BLUE      = 0x0010
        BACKGROUND_GREEN     = 0x0020
        BACKGROUND_CYAN      = 0x0030
        BACKGROUND_RED       = 0x0040
        BACKGROUND_MAGENTA   = 0x0050
        BACKGROUND_YELLOW    = 0x0060
        BACKGROUND_GREY      = 0x0070
        BACKGROUND_INTENSITY = 0x0080 # background color is intensified.     

        levelno = args[1].levelno
        if(levelno>=50):
            color = BACKGROUND_YELLOW | FOREGROUND_RED | FOREGROUND_INTENSITY | BACKGROUND_INTENSITY 
        elif(levelno>=40):
            color = FOREGROUND_RED | FOREGROUND_INTENSITY
        elif(levelno>=30):
            color = FOREGROUND_YELLOW | FOREGROUND_INTENSITY
        elif(levelno>=20):
            color = FOREGROUND_GREEN
        elif(levelno>=10):
            color = FOREGROUND_MAGENTA
        else:
            color =  FOREGROUND_WHITE
        args[0]._set_color(color)

        ret = fn(*args)
        args[0]._set_color( FOREGROUND_WHITE )
        #print "after"
        return ret
    return new

def add_coloring_to_emit_ansi(fn):
    # add methods we need to the class
    def new(*args):
        levelno = args[1].levelno
        if(levelno>=50):
            color = '\x1b[31m' # red
        elif(levelno>=40):
            color = '\x1b[31m' # red
        elif(levelno>=30):
            color = '\x1b[33m' # yellow
        elif(levelno>=20):
            color = '\x1b[32m' # green 
        elif(levelno>=10):
            color = '\x1b[35m' # pink
        else:
            color = '\x1b[0m' # normal
        args[1].msg = color + args[1].msg +  '\x1b[0m'  # normal
        #print "after"
        return fn(*args)
    return new

import platform
if platform.system()=='Windows':
    # Windows does not support ANSI escapes and we are using API calls to set the console color
    logging.StreamHandler.emit = add_coloring_to_emit_windows(logging.StreamHandler.emit)
else:
    # all non-Windows platforms are supporting ANSI escapes so we use them
    logging.StreamHandler.emit = add_coloring_to_emit_ansi(logging.StreamHandler.emit)
    #log = logging.getLogger()
    #log.addFilter(log_filter())
    #//hdlr = logging.StreamHandler()
    #//hdlr.setFormatter(formatter())

3
私はこれに基づいてStreamHandlerのクラスを書いて、参照gist.github.com/mooware/a1ed40987b6cc9ab9c65を
ムーウェア2014年

2
これは私のために働いた!90行目:である必要がありますargs[1].msg = color + str(args[1].msg) + '\x1b[0m' # normal
Rasika Perera、2015

私はこのソリューションが好きです。現在使用しています。属性_set_colorがあるようですが、特定のログメッセージに対してこれを行う方法はありますか?編集、ああ、それはWindowsマシン用のパッチです。さまざまなユースケースにカスタムを追加すると便利です。
brizz

ANSIカラーの場合は+1。xtermでは、一度に256色を取得することもでき、パレットを動的に定義できます!ただし、関数定義の外部でログを記録するときの潜在的なインポートロックの問題を回避するためにロギング関数の呼び出しはすべて関数定義内で行う必要があります。あなたのコードは大体良く見えます。ほんの少しだけが気になりますTestColorer.py
personal_cloud

これにより、実際のログファイルのログメッセージの最初と最後にカラーコードが表示されます。
MehmedB

74

更新:これは私が長い間スクラッチすることを意味していたかゆみなので、私は先に進んで、物事を行う簡単な方法をただ望んでいる私のような怠惰な人々のためにライブラリを書きました:zenlog

Colorlogはこれに最適です。それはだは、PyPI上で利用可能(と通じので、インストール可能pip install colorlog)とされて積極的に維持します

以下は、ロギングを設定し、適切に見えるログメッセージを出力するための簡単なコピーアンドペースト可能なスニペットです。

import logging
LOG_LEVEL = logging.DEBUG
LOGFORMAT = "  %(log_color)s%(levelname)-8s%(reset)s | %(log_color)s%(message)s%(reset)s"
from colorlog import ColoredFormatter
logging.root.setLevel(LOG_LEVEL)
formatter = ColoredFormatter(LOGFORMAT)
stream = logging.StreamHandler()
stream.setLevel(LOG_LEVEL)
stream.setFormatter(formatter)
log = logging.getLogger('pythonConfig')
log.setLevel(LOG_LEVEL)
log.addHandler(stream)

log.debug("A quirky message only developers care about")
log.info("Curious users might want to know this")
log.warn("Something is wrong and any user should be informed")
log.error("Serious stuff, this is red for a reason")
log.critical("OH NO everything is on fire")

出力:

Colorlog出力


4
すばらしい答えです。+1。ただし、コード例はトリミングできます(3つの呼び出しがsetLevel本当に必要ですか?)
ClémentDec

1
私が十分に長い間答えをくぐり抜けた場合、私はこのような答えが見つかることを望んでいました。@ @airmindがこれを受け入れられた回答にすることを検討してほしいので、将来の仕事賢い人々は、最適な怠惰で最高のライブラリであると思われるものを見つけることができます。😉
マイケルScheper

出力のメッセージ例にこれを賛成しました^^
Agustin Barrachina

69

事前定義されたログレベルのための、新しいクラスを定義することのない迅速でダーティなソリューション

logging.addLevelName( logging.WARNING, "\033[1;31m%s\033[1;0m" % logging.getLevelName(logging.WARNING))
logging.addLevelName( logging.ERROR, "\033[1;41m%s\033[1;0m" % logging.getLevelName(logging.ERROR))

@ spiderplant0インポートログ。#@ABCのコードを貼り付けます。logging.warning( 'this is a test')で試してください。「警告:これはテストです」の大文字の部分が色付きで表示されます。Linuxでのみ動作します
Riccardo Galli

3
ログレベル名のみが色分けされているため、ログレベル名がコンソールに出力されることを確認する必要があります。これはそのままでは起こりません。これらの線に沿った何かが役立ちます:logging.basicConfig(format='%(asctime)s [%(name)s] [%(levelname)s] %(message)s')もちろん、どこ%(levelnames)sが重要ですか。
セバスチャン

4
適用して理解する最もシンプルでクリーンなソリューション。
F.サンティアゴ

1
Linuxコンソールで試してください。 echo -e "Normal texst \033[1;31mred bold text\033[0m normal text again"。エコー-eオプションは、「\ 033」を8進形式のエスケープASCII記号として解釈します。この特別なシンボルにより、互換性のある一部の端末は、後続の文字(charをm含む)を特別なコマンドとして解釈します。 en.wikipedia.org/wiki/ANSI_escape_code
eugene-bright

1
マイナーな改善:このコードをに入れますif sys.sdterr.isatty():。この場合、出力をファイルにリダイレクトすると、ファイルにはこれらのエスケープ文字は含まれません。
lesnik

35

2020コード、追加パッケージは不要、Python 3

クラスを定義する

import logging

class CustomFormatter(logging.Formatter):
    """Logging Formatter to add colors and count warning / errors"""

    grey = "\x1b[38;21m"
    yellow = "\x1b[33;21m"
    red = "\x1b[31;21m"
    bold_red = "\x1b[31;1m"
    reset = "\x1b[0m"
    format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s (%(filename)s:%(lineno)d)"

    FORMATS = {
        logging.DEBUG: grey + format + reset,
        logging.INFO: grey + format + reset,
        logging.WARNING: yellow + format + reset,
        logging.ERROR: red + format + reset,
        logging.CRITICAL: bold_red + format + reset
    }

    def format(self, record):
        log_fmt = self.FORMATS.get(record.levelno)
        formatter = logging.Formatter(log_fmt)
        return formatter.format(record)

ロガーをインスタンス化する

# create logger with 'spam_application'
logger = logging.getLogger("My_app")
logger.setLevel(logging.DEBUG)

# create console handler with a higher log level
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)

ch.setFormatter(CustomFormatter())

logger.addHandler(ch)

そして使う!

logger.debug("debug message")
logger.info("info message")
logger.warning("warning message")
logger.error("error message")
logger.critical("critical message")

結果 ここに画像の説明を入力してください

フルカラースキーム ここに画像の説明を入力してください

窓用

このソリューションは、Mac OS、IDE端末で機能します。ウィンドウのコマンドプロンプトには、デフォルトでは色がないように見えます。これらを有効にする方法は次のとおりですが、試していないhttps://www.howtogeek.com/322432/how-to-customize-your-command-prompts-color-scheme-with-microsofts-colortool/


1
テスト(python 3.7、windows)を実行しましたが、ログに色が表示されません:←[38;21m2019-11-12 19:29:50,994 - My_app - DEBUG - debug message (test_colored_log.py:43)←[0m ←[38;21m2019-11-12 19:29:50,994 - My_app - INFO - info message (test_colored_log.py:44)←[0m ←[33;21m2019-11-12 19:29:50,994 - My_app - WARNING - warning message (test_colored_log.py:45)←[0m ←[31;21m2019-11-12 19:29:50,994 - My_app - ERROR - error message (test_colored_log.py:46)←[0m ←[31;1m2019-11-12 19:29:50,994 - My_app - CRITICAL - critical message (test_colored_log.py:47)←[0m
コンストラクター

これは残念ながら機能しません。
ジョー

2
私はこの答えがとても好きだったので、数個のインクリメントとANSIカラーのチートシートを使用してレポを作成しました。
テオドロ

@constructorどこで実行しますか?IDEコンソール?Windowsターミナル?
セルゲイプレシャコフ

@ジョー正確に何が機能しないのですか?あなたの環境は何ですか?どんなエラーが出ますか?ソリューションを修正してプラットフォーム全体で機能するようにしたい
セルゲイプレシャコフ

17

ええと、色付きロガーのバリエーションを追加することもできます。

これは特別なことではありませんが、非常に簡単に使用でき、レコードオブジェクトを変更しないため、ファイルハンドラーが使用されている場合、ANSIエスケープシーケンスをログファイルに記録する必要がありません。ログメッセージのフォーマットには影響しません。

すでにロギングモジュールのFormatterを使用している場合、カラーレベル名を取得するには、カウンセラーハンドラーFormatterをColoredFormatterに置き換えるだけです。アプリ全体をログに記録する場合は、最上位のロガーに対してのみこれを行う必要があります。

colored_log.py

#!/usr/bin/env python

from copy import copy
from logging import Formatter

MAPPING = {
    'DEBUG'   : 37, # white
    'INFO'    : 36, # cyan
    'WARNING' : 33, # yellow
    'ERROR'   : 31, # red
    'CRITICAL': 41, # white on red bg
}

PREFIX = '\033['
SUFFIX = '\033[0m'

class ColoredFormatter(Formatter):

    def __init__(self, patern):
        Formatter.__init__(self, patern)

    def format(self, record):
        colored_record = copy(record)
        levelname = colored_record.levelname
        seq = MAPPING.get(levelname, 37) # default white
        colored_levelname = ('{0}{1}m{2}{3}') \
            .format(PREFIX, seq, levelname, SUFFIX)
        colored_record.levelname = colored_levelname
        return Formatter.format(self, colored_record)

使用例

app.py

#!/usr/bin/env python

import logging
from colored_log import ColoredFormatter

# Create top level logger
log = logging.getLogger("main")

# Add console handler using our custom ColoredFormatter
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
cf = ColoredFormatter("[%(name)s][%(levelname)s]  %(message)s (%(filename)s:%(lineno)d)")
ch.setFormatter(cf)
log.addHandler(ch)

# Add file handler
fh = logging.FileHandler('app.log')
fh.setLevel(logging.DEBUG)
ff = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(ff)
log.addHandler(fh)

# Set log level
log.setLevel(logging.DEBUG)

# Log some stuff
log.debug("app has started")
log.info("Logging to 'app.log' in the script dir")
log.warning("This is my last warning, take heed")
log.error("This is an error")
log.critical("He's dead, Jim")

# Import a sub-module 
import sub_module

sub_module.py

#!/usr/bin/env python

import logging
log = logging.getLogger('main.sub_module')

log.debug("Hello from the sub module")

結果

端子出力

端子出力

app.logの内容

2017-09-29 00:32:23,434 - main - DEBUG - app has started
2017-09-29 00:32:23,434 - main - INFO - Logging to 'app.log' in the script dir
2017-09-29 00:32:23,435 - main - WARNING - This is my last warning, take heed
2017-09-29 00:32:23,435 - main - ERROR - This is an error
2017-09-29 00:32:23,435 - main - CRITICAL - He's dead, Jim
2017-09-29 00:32:23,435 - main.sub_module - DEBUG - Hello from the sub module

もちろん、ターミナルとログファイルの出力をフォーマットすることで、好きなだけ手に入れることができます。ログレベルのみが色分けされます。

私は誰かがこれが便利であると感じて、それがあまりにも同じではないことを願っています。:)

Pythonのサンプルファイルは、次のGitHub Gistからダウンロードできます。https//gist.github.com/KurtJacobson/48e750701acec40c7161b5a2f79e6bfd


2
ところで自身が直前に次の行を追加し、メッセージに色を追加するにはreturncolored_record.msg = ('{0}{1}m{2}{3}').format(self.PREFIX, seq, colored_record.getMessage(), self.SUFFIX)
ゴッドファーザー

15

前景と背景のairmindサポートタグの例を更新しました。ログフォーマッタ文字列で色変数$ BLACK-$ WHITEを使用するだけです。背景を設定するには、$ BG-BLACK-$ BG-WHITEを使用します。

import logging

BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)

COLORS = {
    'WARNING'  : YELLOW,
    'INFO'     : WHITE,
    'DEBUG'    : BLUE,
    'CRITICAL' : YELLOW,
    'ERROR'    : RED,
    'RED'      : RED,
    'GREEN'    : GREEN,
    'YELLOW'   : YELLOW,
    'BLUE'     : BLUE,
    'MAGENTA'  : MAGENTA,
    'CYAN'     : CYAN,
    'WHITE'    : WHITE,
}

RESET_SEQ = "\033[0m"
COLOR_SEQ = "\033[1;%dm"
BOLD_SEQ  = "\033[1m"

class ColorFormatter(logging.Formatter):

    def __init__(self, *args, **kwargs):
        # can't do super(...) here because Formatter is an old school class
        logging.Formatter.__init__(self, *args, **kwargs)

    def format(self, record):
        levelname = record.levelname
        color     = COLOR_SEQ % (30 + COLORS[levelname])
        message   = logging.Formatter.format(self, record)
        message   = message.replace("$RESET", RESET_SEQ)\
                           .replace("$BOLD",  BOLD_SEQ)\
                           .replace("$COLOR", color)
        for k,v in COLORS.items():
            message = message.replace("$" + k,    COLOR_SEQ % (v+30))\
                             .replace("$BG" + k,  COLOR_SEQ % (v+40))\
                             .replace("$BG-" + k, COLOR_SEQ % (v+40))
        return message + RESET_SEQ

logging.ColorFormatter = ColorFormatter

だから今あなたはあなたの設定ファイルで以下を簡単に行うことができます:

[formatter_colorFormatter]
class=logging.ColorFormatter
format= $COLOR%(levelname)s $RESET %(asctime)s $BOLD$COLOR%(name)s$RESET %(message)s

大きな改善。しかし、についてのコメントはsuper私が推測するいくつかの古いPythonバージョンにのみ適用されますか?この回答は2010年からです。Python2.7でうまく動作しました
Joakim

14

colorlogモジュールをインポートして、ColoredFormatterログメッセージの色付けに使用できます。

メインモジュールのボイラープレート:

import logging
import os
import sys
try:
    import colorlog
except ImportError:
    pass

def setup_logging():
    root = logging.getLogger()
    root.setLevel(logging.DEBUG)
    format      = '%(asctime)s - %(levelname)-8s - %(message)s'
    date_format = '%Y-%m-%d %H:%M:%S'
    if 'colorlog' in sys.modules and os.isatty(2):
        cformat = '%(log_color)s' + format
        f = colorlog.ColoredFormatter(cformat, date_format,
              log_colors = { 'DEBUG'   : 'reset',       'INFO' : 'reset',
                             'WARNING' : 'bold_yellow', 'ERROR': 'bold_red',
                             'CRITICAL': 'bold_red' })
    else:
        f = logging.Formatter(format, date_format)
    ch = logging.StreamHandler()
    ch.setFormatter(f)
    root.addHandler(ch)

setup_logging()
log = logging.getLogger(__name__)

colorlogモジュールがインストールされていて、出力が実際に端末に送信される場合、コードはログメッセージの色のみを有効にします。これにより、ログ出力がリダイレクトされたときにエスケープシーケンスがファイルに書き込まれるのを回避できます。

また、背景が暗い端末に適したカスタム配色が設定されています。

ログ呼び出しの例:

log.debug   ('Hello Debug')
log.info    ('Hello Info')
log.warn    ('Hello Warn')
log.error   ('Hello Error')
log.critical('Hello Critical')

出力:

ここに画像の説明を入力してください


2
いくつかの良いデフォルトがあるcolorlog.basicConfig代わりに使用することもできますlogging.basicConfig
MarSoft

1
記録のために、colorlogは常にWindowsプラットフォームで直接動作するとは限りません(指定されたとおり、coloramaの依存関係が必要です)。それでも、Anaconda / Spyder envで機能させるのに苦労しました。あなたは(このスレッドに示すようにescape_code.pyに例えばcolorama.init(ストリップ= false)を指定する必要がありgithub.com/spyder-ide/spyder/issues/1917
マット・マックマフィン-

11

次の解決策を見てください。ストリームハンドラーは色付けを行うものである必要があります。その場合、(フォーマッターを使用して)行全体ではなく単語に色を付けることができます。

http://plumberjack.blogspot.com/2010/12/colorizing-logging-output-in-terminals.html


この要点で更新された実装を見つけることができます(ブログ作成者が保守)。私はそれを使用しており、問題なく動作します。共有いただきありがとうございます。
2012

11

Sorinによって提供された元の例とサブクラス化されたStreamHandlerをColorizedConsoleHandlerに変更しました。

彼らの解決策の欠点は、メッセージを変更することです。これにより、実際のログメッセージが変更されるため、他のハンドラーも変更されたメッセージを取得します。

複数のロガーを使用しているため、今回のケースでは、カラーコードがログファイルに含まれていました。

以下のクラスはansiをサポートするプラットフォームでのみ機能しますが、Windowsのカラーコードを追加するのは簡単です。

import copy
import logging


class ColoredConsoleHandler(logging.StreamHandler):
    def emit(self, record):
        # Need to make a actual copy of the record
        # to prevent altering the message for other loggers
        myrecord = copy.copy(record)
        levelno = myrecord.levelno
        if(levelno >= 50):  # CRITICAL / FATAL
            color = '\x1b[31m'  # red
        elif(levelno >= 40):  # ERROR
            color = '\x1b[31m'  # red
        elif(levelno >= 30):  # WARNING
            color = '\x1b[33m'  # yellow
        elif(levelno >= 20):  # INFO
            color = '\x1b[32m'  # green
        elif(levelno >= 10):  # DEBUG
            color = '\x1b[35m'  # pink
        else:  # NOTSET and anything else
            color = '\x1b[0m'  # normal
        myrecord.msg = color + str(myrecord.msg) + '\x1b[0m'  # normal
        logging.StreamHandler.emit(self, myrecord)


7

たくさんの反応があります。しかし、誰もデコレータについて話していません。これが私のものです。

ずっとシンプルだからです。

何もインポートしたり、サブクラスを作成したりする必要はありません。

#!/usr/bin/env python
# -*- coding: utf-8 -*-


import logging


NO_COLOR = "\33[m"
RED, GREEN, ORANGE, BLUE, PURPLE, LBLUE, GREY = \
    map("\33[%dm".__mod__, range(31, 38))

logging.basicConfig(format="%(message)s", level=logging.DEBUG)
logger = logging.getLogger(__name__)

# the decorator to apply on the logger methods info, warn, ...
def add_color(logger_method, color):
  def wrapper(message, *args, **kwargs):
    return logger_method(
      # the coloring is applied here.
      color+message+NO_COLOR,
      *args, **kwargs
    )
  return wrapper

for level, color in zip((
  "info", "warn", "error", "debug"), (
  GREEN, ORANGE, RED, BLUE
)):
  setattr(logger, level, add_color(getattr(logger, level), color))

# this is displayed in red.
logger.error("Launching %s." % __file__)

これにより、エラーが赤に、デバッグメッセージが青に設定されます。質問のように。

以下をcolor使用してメッセージの色を動的に設定する引数を取るようにラッパーを適応させることもできますlogger.debug("message", color=GREY)

編集:したがって、実行時に色を設定するために適応されたデコレータを次に示します。

def add_color(logger_method, _color):
  def wrapper(message, *args, **kwargs):
    color = kwargs.pop("color", _color)
    if isinstance(color, int):
      color = "\33[%dm" % color
    return logger_method(
      # the coloring is applied here.
      color+message+NO_COLOR,
      *args, **kwargs
    )
  return wrapper

# blah blah, apply the decorator...

# this is displayed in red.
logger.error("Launching %s." % __file__)
# this is displayed in blue
logger.error("Launching %s." % __file__, color=34)
# and this, in grey
logger.error("Launching %s." % __file__, color=GREY)

6

すべてを1つのクラスに保つairmindのアプローチの別のマイナーリミックス:

class ColorFormatter(logging.Formatter):
  FORMAT = ("[$BOLD%(name)-20s$RESET][%(levelname)-18s]  "
            "%(message)s "
            "($BOLD%(filename)s$RESET:%(lineno)d)")

  BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)

  RESET_SEQ = "\033[0m"
  COLOR_SEQ = "\033[1;%dm"
  BOLD_SEQ = "\033[1m"

  COLORS = {
    'WARNING': YELLOW,
    'INFO': WHITE,
    'DEBUG': BLUE,
    'CRITICAL': YELLOW,
    'ERROR': RED
  }

  def formatter_msg(self, msg, use_color = True):
    if use_color:
      msg = msg.replace("$RESET", self.RESET_SEQ).replace("$BOLD", self.BOLD_SEQ)
    else:
      msg = msg.replace("$RESET", "").replace("$BOLD", "")
    return msg

  def __init__(self, use_color=True):
    msg = self.formatter_msg(self.FORMAT, use_color)
    logging.Formatter.__init__(self, msg)
    self.use_color = use_color

  def format(self, record):
    levelname = record.levelname
    if self.use_color and levelname in self.COLORS:
      fore_color = 30 + self.COLORS[levelname]
      levelname_color = self.COLOR_SEQ % fore_color + levelname + self.RESET_SEQ
      record.levelname = levelname_color
    return logging.Formatter.format(self, record)

フォーマッターをハンドラーにアタッチするには、次のようにします。

handler.setFormatter(ColorFormatter())
logger.addHandler(handler)

5

すべての端末テキストを色付けするためのシンプルだが非常に柔軟なツールは、 ' colout 'です。

pip install colout
myprocess | colout REGEX_WITH_GROUPS color1,color2...

正規表現のグループ1に一致する「myprocess」の出力のテキストは、color1で色付けされ、group 2はcolor2などで色付けされます。

例えば:

tail -f /var/log/mylogfile | colout '^(\w+ \d+ [\d:]+)|(\w+\.py:\d+ .+\(\)): (.+)$' white,black,cyan bold,bold,normal

つまり、最初の正規表現グループ(括弧)はログファイルの最初の日付に一致し、2番目のグループはpythonファイル名、行番号、関数名に一致し、3番目のグループはその後に続くログメッセージに一致します。また、「太字/法線」の並列シーケンスと色のシーケンスも使用します。これは次のようになります。

色付きフォーマットのログファイル

正規表現のいずれにも一致しない行または行の一部は引き続きエコーされるため、これは「grep --color」とは異なります-出力から何も除外されません。

明らかに、これは十分に柔軟性があり、ログファイルのテーリングだけでなく、あらゆるプロセスで使用できます。私は通常、何かを色付けしたいときはいつでも、その場で新しい正規表現を作成します。このため、色付けする内容に関係なく、1つのツール(ログ、テスト出力、構文のターミナルでのコードのスニペットの強調表示など)を学習するだけでよいので、カスタムログファイルの色付けツールよりもcoloutを好みます。

また、ログファイル自体にANSIコードを実際にダンプすることも回避します。これは、IMHOが悪い考えです。これは、常にgrep正規表現のANSIコードと一致することを忘れない限り、ログファイルのパターンのgreppingのようなものを壊してしまうためです。


4
import logging
import sys

colors = {'pink': '\033[95m', 'blue': '\033[94m', 'green': '\033[92m', 'yellow': '\033[93m', 'red': '\033[91m',
      'ENDC': '\033[0m', 'bold': '\033[1m', 'underline': '\033[4m'}

logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)


def str_color(color, data):
    return colors[color] + str(data) + colors['ENDC']

params = {'param1': id1, 'param2': id2}

logging.info('\nParams:' + str_color("blue", str(params)))`

+1 [9*m「明るい」ANSIカラーのコードを使った素晴らしい例!PS 関数定義の外でのロギングがPythonで安全かどうかはまだわからないため、最後の行は少し心配です。
personal_cloud

2

これが私の解決策です:

class ColouredFormatter(logging.Formatter):
    RESET = '\x1B[0m'
    RED = '\x1B[31m'
    YELLOW = '\x1B[33m'
    BRGREEN = '\x1B[01;32m'  # grey in solarized for terminals

    def format(self, record, colour=False):
        message = super().format(record)

        if not colour:
            return message

        level_no = record.levelno
        if level_no >= logging.CRITICAL:
            colour = self.RED
        elif level_no >= logging.ERROR:
            colour = self.RED
        elif level_no >= logging.WARNING:
            colour = self.YELLOW
        elif level_no >= logging.INFO:
            colour = self.RESET
        elif level_no >= logging.DEBUG:
            colour = self.BRGREEN
        else:
            colour = self.RESET

        message = colour + message + self.RESET

        return message


class ColouredHandler(logging.StreamHandler):
    def __init__(self, stream=sys.stdout):
        super().__init__(stream)

    def format(self, record, colour=False):
        if not isinstance(self.formatter, ColouredFormatter):
            self.formatter = ColouredFormatter()

        return self.formatter.format(record, colour)

    def emit(self, record):
        stream = self.stream
        try:
            msg = self.format(record, stream.isatty())
            stream.write(msg)
            stream.write(self.terminator)
            self.flush()
        except Exception:
            self.handleError(record)


h = ColouredHandler()
h.formatter = ColouredFormatter('{asctime} {levelname:8} {message}', '%Y-%m-%d %H:%M:%S', '{')
logging.basicConfig(level=logging.DEBUG, handlers=[h])

1

私が問題を抱えていたのは、フォーマッタを適切に設定することでした。

class ColouredFormatter(logging.Formatter):    
    def __init__(self, msg):
        logging.Formatter.__init__(self, msg)
        self._init_colour = _get_colour()

    def close(self):
        # restore the colour information to what it was
        _set_colour(self._init_colour)

    def format(self, record):        
        # Add your own colourer based on the other examples
        _set_colour( LOG_LEVEL_COLOUR[record.levelno] )
        return logging.Formatter.format(self, record)         

def init():
    # Set up the formatter. Needs to be first thing done.
    rootLogger = logging.getLogger()
    hdlr = logging.StreamHandler()
    fmt = ColouredFormatter('%(message)s')
    hdlr.setFormatter(fmt)
    rootLogger.addHandler(hdlr)

そして、使用するには:

import coloured_log
import logging

coloured_log.init()
logging.info("info")    
logging.debug("debug")    

coloured_log.close()    # restore colours

(_set_colourも同様に)疑似コードであるはずですが、何かを追加しています。最も問題があったのは、フォーマッターを正しく取り付ける方法を知ることでした。
Nick

「配管工ジャッキ」ソリューションを参照してください。これは問題を解決するためのより良い方法だと思います(つまり、ハンドラーは色付けを行う必要があります)。stackoverflow.com/questions/384076/...
ニック・

1

他のソリューションは問題ないように見えますが、いくつかの問題があります。全体が色付けされていない場合もあれば、すべての設定を省略している場合もあります。以下の解決策は、メッセージ自体にのみ影響します。

コード

class ColoredFormatter(logging.Formatter):
    def format(self, record):
        if record.levelno == logging.WARNING:
            record.msg = '\033[93m%s\033[0m' % record.msg
        elif record.levelno == logging.ERROR:
            record.msg = '\033[91m%s\033[0m' % record.msg
        return logging.Formatter.format(self, record)

logger = logging.getLogger('mylogger')
handler = logging.StreamHandler()

log_format = '[%(asctime)s]:%(levelname)-7s:%(message)s'
time_format = '%H:%M:%S'
formatter = ColoredFormatter(log_format, datefmt=time_format)
handler.setFormatter(formatter)
logger.addHandler(handler)

logger.warn('this should be yellow')
logger.error('this should be red')

出力

[17:01:36]:WARNING:this should be yellow
[17:01:37]:ERROR  :this should be red

ご覧のように、他のすべてはまだ出力され、初期色のままです。メッセージ以外のものを変更したい場合log_formatは、例の色コードを渡すだけです。


使用すると、メッセージが2回出力されます。なぜなのかご存知ですか?
Validus Oculus

@詳しく説明してもらえますか?つまり、あなたは何かを意味するのか、[17:01:36]:WARNING:this should be yellowthis should be yellowまたはフルラインが2回印刷されるのですか?
ピティコス2016年

コメントを簡潔にして申し訳ありません。前者が発生しました:[17:01:36]:警告:これは黄色であるはずです\ nこれは黄色であるはずです。しかし、私はフォーマットされたものだけを表示したいのですが、そうでなければ冗長なログのためにゴミのように見えます。
Validus Oculus

@MuratKarakuş実装を完全に把握せずにこれが発生する理由がわかりません。カスタムロガーを使用している場合、ある時点で干渉している可能性がありますか?速い修正は削除することができ7s:%(message)sからlog_format
ピティコス2016年

1

追加する送信は2つあります。1つはメッセージのみをカラー化(ColoredFormatter)、もう1つは行全体をカラー化(ColorizingStreamHandler)し​​ます。これらには、以前のソリューションよりも多くのANSIカラーコードが含まれています。

いくつかのコンテンツは(変更を加えて)提供されています:上記の投稿、およびhttp://plumberjack.blogspot.com/2010/12/colorizing-logging-output-in-terminals.html

メッセージのみに色を付けます。

class ColoredFormatter(logging.Formatter):
    """Special custom formatter for colorizing log messages!"""

    BLACK = '\033[0;30m'
    RED = '\033[0;31m'
    GREEN = '\033[0;32m'
    BROWN = '\033[0;33m'
    BLUE = '\033[0;34m'
    PURPLE = '\033[0;35m'
    CYAN = '\033[0;36m'
    GREY = '\033[0;37m'

    DARK_GREY = '\033[1;30m'
    LIGHT_RED = '\033[1;31m'
    LIGHT_GREEN = '\033[1;32m'
    YELLOW = '\033[1;33m'
    LIGHT_BLUE = '\033[1;34m'
    LIGHT_PURPLE = '\033[1;35m'
    LIGHT_CYAN = '\033[1;36m'
    WHITE = '\033[1;37m'

    RESET = "\033[0m"

    def __init__(self, *args, **kwargs):
        self._colors = {logging.DEBUG: self.DARK_GREY,
                        logging.INFO: self.RESET,
                        logging.WARNING: self.BROWN,
                        logging.ERROR: self.RED,
                        logging.CRITICAL: self.LIGHT_RED}
        super(ColoredFormatter, self).__init__(*args, **kwargs)

    def format(self, record):
        """Applies the color formats"""
        record.msg = self._colors[record.levelno] + record.msg + self.RESET
        return logging.Formatter.format(self, record)

    def setLevelColor(self, logging_level, escaped_ansi_code):
        self._colors[logging_level] = escaped_ansi_code

行全体を色分けします。

class ColorizingStreamHandler(logging.StreamHandler):

    BLACK = '\033[0;30m'
    RED = '\033[0;31m'
    GREEN = '\033[0;32m'
    BROWN = '\033[0;33m'
    BLUE = '\033[0;34m'
    PURPLE = '\033[0;35m'
    CYAN = '\033[0;36m'
    GREY = '\033[0;37m'

    DARK_GREY = '\033[1;30m'
    LIGHT_RED = '\033[1;31m'
    LIGHT_GREEN = '\033[1;32m'
    YELLOW = '\033[1;33m'
    LIGHT_BLUE = '\033[1;34m'
    LIGHT_PURPLE = '\033[1;35m'
    LIGHT_CYAN = '\033[1;36m'
    WHITE = '\033[1;37m'

    RESET = "\033[0m"

    def __init__(self, *args, **kwargs):
        self._colors = {logging.DEBUG: self.DARK_GREY,
                        logging.INFO: self.RESET,
                        logging.WARNING: self.BROWN,
                        logging.ERROR: self.RED,
                        logging.CRITICAL: self.LIGHT_RED}
        super(ColorizingStreamHandler, self).__init__(*args, **kwargs)

    @property
    def is_tty(self):
        isatty = getattr(self.stream, 'isatty', None)
        return isatty and isatty()

    def emit(self, record):
        try:
            message = self.format(record)
            stream = self.stream
            if not self.is_tty:
                stream.write(message)
            else:
                message = self._colors[record.levelno] + message + self.RESET
                stream.write(message)
            stream.write(getattr(self, 'terminator', '\n'))
            self.flush()
        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            self.handleError(record)

    def setLevelColor(self, logging_level, escaped_ansi_code):
        self._colors[logging_level] = escaped_ansi_code


1

これは、カラーコードを含むEnumです。

class TerminalColour:
    """
    Terminal colour formatting codes
    """
    # /programming/287871/print-in-terminal-with-colors
    MAGENTA = '\033[95m'
    BLUE = '\033[94m'
    GREEN = '\033[92m'
    YELLOW = '\033[93m'
    RED = '\033[91m'
    GREY = '\033[0m'  # normal
    WHITE = '\033[1m'  # bright white
    UNDERLINE = '\033[4m'

これは、各ログレベルの名前に適用される場合があります。これは巨大なハックであることに注意してください。

logging.addLevelName(logging.INFO, "{}{}{}".format(TerminalColour.WHITE, logging.getLevelName(logging.INFO), TerminalColour.GREY))
logging.addLevelName(logging.WARNING, "{}{}{}".format(TerminalColour.YELLOW, logging.getLevelName(logging.WARNING), TerminalColour.GREY))
logging.addLevelName(logging.ERROR, "{}{}{}".format(TerminalColour.RED, logging.getLevelName(logging.ERROR), TerminalColour.GREY))
logging.addLevelName(logging.CRITICAL, "{}{}{}".format(TerminalColour.MAGENTA, logging.getLevelName(logging.CRITICAL), .GREY))

ログフォーマッターにはログレベルの名前を含める必要があることに注意してください

%(levelname)

例えば:

    LOGGING = {
...
        'verbose': {
            'format': '%(asctime)s %(levelname)s %(name)s:%(lineno)s %(module)s %(process)d %(thread)d %(message)s'
        },
        'simple': {
            'format': '[%(asctime)s] %(levelname)s %(name)s %(message)s'
        },

1

FriendlyLogは別の代替手段です。Linux、Windows、MacOSのPython 2&3で動作します。


モジュールパスの混乱を減らすための新しいPRを楽しみにしています
mbspark

1

レベルごとの色付けに加えて、ログメッセージ引数を交互の色で強調表示することについてはどうですか?最近、そのための簡単なコードを書きました。別の利点は、log呼び出しがPython 3のブレーススタイルのフォーマットで行われることです。("{}")。

ここで最新のコードと例を参照してください:https : //github.com/davidohana/colargulog

サンプルログコード:

root_logger = logging.getLogger()
console_handler = logging.StreamHandler(stream=sys.stdout)
console_format = "%(asctime)s - %(levelname)-8s - %(name)-25s - %(message)s"
colored_formatter = ColorizedArgsFormatter(console_format)
console_handler.setFormatter(colored_formatter)
root_logger.addHandler(console_handler)

logger = logging.getLogger(__name__)
logger.info("Hello World")
logger.info("Request from {} handled in {:.3f} ms", socket.gethostname(), 11)
logger.info("Request from {} handled in {:.3f} ms", "127.0.0.1", 33.1)
logger.info("My favorite drinks are {}, {}, {}, {}", "milk", "wine", "tea", "beer")
logger.debug("this is a {} message", logging.getLevelName(logging.DEBUG))
logger.info("this is a {} message", logging.getLevelName(logging.INFO))
logger.warning("this is a {} message", logging.getLevelName(logging.WARNING))
logger.error("this is a {} message", logging.getLevelName(logging.ERROR))
logger.critical("this is a {} message", logging.getLevelName(logging.CRITICAL))
logger.info("Does old-style formatting also work? %s it is, but no colors (yet)", True)

出力:

ここに画像の説明を入力してください

実装:

"""
colargulog - Python3 Logging with Colored Arguments and new string formatting style

Written by david.ohana@ibm.com
License: Apache-2.0
"""

import logging
import logging.handlers
import re


class ColorCodes:
    grey = "\x1b[38;21m"
    green = "\x1b[1;32m"
    yellow = "\x1b[33;21m"
    red = "\x1b[31;21m"
    bold_red = "\x1b[31;1m"
    blue = "\x1b[1;34m"
    light_blue = "\x1b[1;36m"
    purple = "\x1b[1;35m"
    reset = "\x1b[0m"


class ColorizedArgsFormatter(logging.Formatter):
    arg_colors = [ColorCodes.purple, ColorCodes.light_blue]
    level_fields = ["levelname", "levelno"]
    level_to_color = {
        logging.DEBUG: ColorCodes.grey,
        logging.INFO: ColorCodes.green,
        logging.WARNING: ColorCodes.yellow,
        logging.ERROR: ColorCodes.red,
        logging.CRITICAL: ColorCodes.bold_red,
    }

    def __init__(self, fmt: str):
        super().__init__()
        self.level_to_formatter = {}

        def add_color_format(level: int):
            color = ColorizedArgsFormatter.level_to_color[level]
            _format = fmt
            for fld in ColorizedArgsFormatter.level_fields:
                search = "(%\(" + fld + "\).*?s)"
                _format = re.sub(search, f"{color}\\1{ColorCodes.reset}", _format)
            formatter = logging.Formatter(_format)
            self.level_to_formatter[level] = formatter

        add_color_format(logging.DEBUG)
        add_color_format(logging.INFO)
        add_color_format(logging.WARNING)
        add_color_format(logging.ERROR)
        add_color_format(logging.CRITICAL)

    @staticmethod
    def rewrite_record(record: logging.LogRecord):
        if not BraceFormatStyleFormatter.is_brace_format_style(record):
            return

        msg = record.msg
        msg = msg.replace("{", "_{{")
        msg = msg.replace("}", "_}}")
        placeholder_count = 0
        # add ANSI escape code for next alternating color before each formatting parameter
        # and reset color after it.
        while True:
            if "_{{" not in msg:
                break
            color_index = placeholder_count % len(ColorizedArgsFormatter.arg_colors)
            color = ColorizedArgsFormatter.arg_colors[color_index]
            msg = msg.replace("_{{", color + "{", 1)
            msg = msg.replace("_}}", "}" + ColorCodes.reset, 1)
            placeholder_count += 1

        record.msg = msg.format(*record.args)
        record.args = []

    def format(self, record):
        orig_msg = record.msg
        orig_args = record.args
        formatter = self.level_to_formatter.get(record.levelno)
        self.rewrite_record(record)
        formatted = formatter.format(record)

        # restore log record to original state for other handlers
        record.msg = orig_msg
        record.args = orig_args
        return formatted


class BraceFormatStyleFormatter(logging.Formatter):
    def __init__(self, fmt: str):
        super().__init__()
        self.formatter = logging.Formatter(fmt)

    @staticmethod
    def is_brace_format_style(record: logging.LogRecord):
        if len(record.args) == 0:
            return False

        msg = record.msg
        if '%' in msg:
            return False

        count_of_start_param = msg.count("{")
        count_of_end_param = msg.count("}")

        if count_of_start_param != count_of_end_param:
            return False

        if count_of_start_param != len(record.args):
            return False

        return True

    @staticmethod
    def rewrite_record(record: logging.LogRecord):
        if not BraceFormatStyleFormatter.is_brace_format_style(record):
            return

        record.msg = record.msg.format(*record.args)
        record.args = []

    def format(self, record):
        orig_msg = record.msg
        orig_args = record.args
        self.rewrite_record(record)
        formatted = self.formatter.format(record)

        # restore log record to original state for other handlers
        record.msg = orig_msg
        record.args = orig_args
        return formatted


0

ZetaSyanthisの色を使用した、もう1つのソリューション:

def config_log(log_level):

    def set_color(level, code):
        level_fmt = "\033[1;" + str(code) + "m%s\033[1;0m" 
        logging.addLevelName( level, level_fmt % logging.getLevelName(level) )

    std_stream = sys.stdout
    isatty = getattr(std_stream, 'isatty', None)
    if isatty and isatty():
        levels = [logging.DEBUG, logging.CRITICAL, logging.WARNING, logging.ERROR]
        for idx, level in enumerate(levels):
            set_color(level, 30 + idx )
        set_color(logging.DEBUG, 0)
    logging.basicConfig(stream=std_stream, level=log_level)

__main__関数から一度呼び出します。私はそこにこのようなものがあります:

options, arguments = p.parse_args()
log_level = logging.DEBUG if options.verbose else logging.WARNING
config_log(log_level)

また、出力がコンソールであることを確認します。それ以外の場合、色は使用されません。


0
import logging

logging.basicConfig(filename="f.log" filemode='w', level=logging.INFO,
                    format = "%(logger_name)s %(color)s  %(message)s %(endColor)s")


class Logger(object):
    __GREEN = "\033[92m"
    __RED = '\033[91m'
    __ENDC = '\033[0m'

    def __init__(self, name):
        self.logger = logging.getLogger(name)
        self.extra={'logger_name': name, 'endColor': self.__ENDC, 'color': self.__GREEN}


    def info(self, msg):
        self.extra['color'] = self.__GREEN
        self.logger.info(msg, extra=self.extra)

    def error(self, msg):
        self.extra['color'] = self.__RED
        self.logger.error(msg, extra=self.extra)

使用法

Logger("File Name").info("This shows green text")


コンソールの場合は、ファイル名を省略するか、単にfilename = ''で機能します。...ファイル番号、モジュールなどの他のプロパティを含めることにbasicConfigを修正
estifanos gebrehiwot

0

次の解決策はpython 3でのみ機能しますが、私には最も明確に見えます。

ログレコードファクトリを使用して「色」属性をログレコードオブジェクトに追加し、これらの「色」属性をログ形式で使用するという考え方です。

import logging
logger = logging.getLogger(__name__)

def configure_logging(level):

    # add 'levelname_c' attribute to log resords
    orig_record_factory = logging.getLogRecordFactory()
    log_colors = {
        logging.DEBUG:     "\033[1;34m",  # blue
        logging.INFO:      "\033[1;32m",  # green
        logging.WARNING:   "\033[1;35m",  # magenta
        logging.ERROR:     "\033[1;31m",  # red
        logging.CRITICAL:  "\033[1;41m",  # red reverted
    }
    def record_factory(*args, **kwargs):
        record = orig_record_factory(*args, **kwargs)
        record.levelname_c = "{}{}{}".format(
            log_colors[record.levelno], record.levelname, "\033[0m")
        return record

    logging.setLogRecordFactory(record_factory)

    # now each log record object would contain 'levelname_c' attribute
    # and you can use this attribute when configuring logging using your favorite
    # method.
    # for demo purposes I configure stderr log right here

    formatter_c = logging.Formatter("[%(asctime)s] %(levelname_c)s:%(name)s:%(message)s")

    stderr_handler = logging.StreamHandler()
    stderr_handler.setLevel(level)
    stderr_handler.setFormatter(formatter_c)

    root_logger = logging.getLogger('')
    root_logger.setLevel(logging.DEBUG)
    root_logger.addHandler(stderr_handler)


def main():
    configure_logging(logging.DEBUG)

    logger.debug("debug message")
    logger.info("info message")
    logger.critical("something unusual happened")


if __name__ == '__main__':
    main()

この例を簡単に変更して、他のカラー属性(fe message_c)を作成し、これらの属性を使用して、必要な場所にカラーテキスト(のみ)を取得できます。

(最近発見した便利なトリック:色付きのデバッグログを含むファイルがあり、アプリケーションのログレベルを一時的に上げたいときはいつでもtail -f、ログファイルを別のターミナルで表示するだけで、構成を変更してアプリケーションを再起動せずに画面にデバッグログを表示できます)


0

これはairmindの例の別のPython3バリアントです。他の例にはない特定の機能が欲しかった

  • 端末には色を使用しますが、ファイルハンドラーに印刷できない文字を記述しません(このために2つのフォーマッターを定義しました)
  • 特定のログメッセージの色を上書きする機能
  • ファイルからロガーを設定します(この場合はyaml)

注:コロラマを使用しましたが、これを変更できるため、必要ありません。また、私のテストでは、Pythonファイルを実行しているだけなので、私のクラスはモジュール内にあります。モジュールは何にでも__main__変更(): __main__.ColoredFormatterする必要があります。

pip install colorama pyyaml

logging.yaml

---
version: 1
disable_existing_loggers: False
formatters:
  simple:
    format: "%(threadName)s - %(name)s - %(levelname)s - %(message)s"
  color:
    format: "%(threadName)s - %(name)s - %(levelname)s - %(message)s"
    (): __main__.ColoredFormatter
    use_color: true

handlers:
  console:
    class: logging.StreamHandler
    level: DEBUG
    formatter: color
    stream: ext://sys.stdout

  info_file_handler:
    class: logging.handlers.RotatingFileHandler
    level: INFO
    formatter: simple
    filename: app.log
    maxBytes: 20971520 
    backupCount: 20
    encoding: utf8

  error_file_handler:
    class: logging.handlers.RotatingFileHandler
    level: ERROR
    formatter: simple
    filename: errors.log
    maxBytes: 10485760 
    backupCount: 20
    encoding: utf8

root:
  level: DEBUG
  handlers: [console, info_file_handler, error_file_handler]

main.py

import logging
import logging.config
import os
from logging import Logger

import colorama
import yaml
from colorama import Back, Fore, Style

COLORS = {
    "WARNING": Fore.YELLOW,
    "INFO": Fore.CYAN,
    "DEBUG": Fore.BLUE,
    "CRITICAL": Fore.YELLOW,
    "ERROR": Fore.RED,
}


class ColoredFormatter(logging.Formatter):
    def __init__(self, *, format, use_color):
        logging.Formatter.__init__(self, fmt=format)
        self.use_color = use_color

    def format(self, record):
        msg = super().format(record)
        if self.use_color:
            levelname = record.levelname
            if hasattr(record, "color"):
                return f"{record.color}{msg}{Style.RESET_ALL}"
            if levelname in COLORS:
                return f"{COLORS[levelname]}{msg}{Style.RESET_ALL}"
        return msg


with open("logging.yaml", "rt") as f:
    config = yaml.safe_load(f.read())
    logging.config.dictConfig(config)

logger: Logger = logging.getLogger(__name__)
logger.info("Test INFO", extra={"color": Back.RED})
logger.info("Test INFO", extra={"color": f"{Style.BRIGHT}{Back.RED}"})
logger.info("Test INFO")
logger.debug("Test DEBUG")
logger.warning("Test WARN")

出力:

出力

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.