少し前に、おそらくすべてのメッセージが標準化されていたために、おそらくログシステムのために、出力が色付きのMonoアプリケーションを見ました。
現在、Pythonにはlogging
モジュールがあり、出力をカスタマイズするための多くのオプションを指定できます。したがって、Pythonで同様のことが可能になると想像していますが、どこでこれを行うかはわかりません。
Python logging
モジュールをカラーで出力する方法はありますか?
(たとえば)赤のエラー、青または黄色のデバッグメッセージなど。
もちろん、これにはおそらく互換性のある端末が必要になります(最新の端末のほとんどがそうです)。色がサポートされていない場合は、元のlogging
出力にフォールバックできます。
ロギングモジュールで色付きの出力を取得する方法はありますか?
カラーエスケープについては既に知っていたので、しばらく前にbashプロンプトで使用しました。とにかくありがとう。
私が望んでいたのは、ロギングモジュールと統合することでした。最終的には、2、3回試行錯誤を繰り返しました。
これが私が最終的に得たものです。
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
は、レコードオブジェクトを変更します。レコードオブジェクトは、他のハンドラーにさらに渡されるか、他のロガーに伝達されます。ファイルロガーなどを設定している場合は、おそらくログファイルに色を付けたくないでしょう。それを避けるには、おそらく、_name_name属性を操作する前にcopy.copy()
でrecord
のコピーを作成するか、フォーマットされた文字列を返す前にlevelnameを以前の値にリセットするのが最善です(クレジットは Michael コメント内)。
数年前、私は自分用にカラーストリームハンドラを作成しました。次に、このページに出会い、人々がコピー/貼り付けしているコードスニペットのコレクションを見つけました: 。私のストリームハンドラは現在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、ログレベル、およびログメッセージが含まれています。これは実際には次のようになります。
これは、どのプラットフォームでも機能するソリューションです。それだけで教えてくれない場合は更新します。
動作方法: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())
新しいクラスを定義せずに、事前に定義されたログレベル用の迅速でダーティなソリューション。
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))
Update:これは私が長い間ひっかき続けることを意味していたかゆみなので、私は先に進み、私のような怠け者のためのライブラリを書きました物事を行う簡単な方法が必要な場合: 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 モジュールをインポートし、その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')
出力:
前景と背景のタグをサポートする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
さて、色付きロガーのバリエーションを追加することも考えられます。
これは空想ではありませんが、使用するのが非常に簡単で、レコードオブジェクトを変更しないため、ファイルハンドラーが使用されている場合に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
次のソリューションをご覧ください。ストリームハンドラーが色付けを行うものでなければなりません。その場合、行全体(Formatterを使用)ではなく単語を色付けするオプションがあります。
http://plumberjack.blogspot.com/2010/12/colorizing-logging-output-in-terminals.html
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)
現在、カスタマイズ可能な色付きログ出力用のリリースされたPyPiモジュールがあります。
https://pypi.python.org/pypi/Rainbow_logging_handler/
そして
https://github.com/laysakura/Rainbow_logging_handler
Windowsをサポート
Djangoをサポート
カスタマイズ可能な色
これはPython Eggとして配布されるため、どのPythonアプリケーションにも簡単にインストールできます。
たくさんの回答があります。しかし、デコレータについて話しているものはありません。これが私のものです。
はるかに単純だからです。
何もインポートする必要も、サブクラスを作成する必要もありません。
#!/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)
すべてを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)
任意の端末テキストを着色するためのシンプルだが非常に柔軟なツールは、「 colout 」です。
pip install colout
myprocess | colout REGEX_WITH_GROUPS color1,color2...
正規表現のグループ1に一致する「myprocess」の出力のテキストは、color1で色付けされ、グループ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」とは異なります-出力から何もフィルタリングされません。
これは明らかに、ログファイルの末尾だけでなく、あらゆるプロセスで使用できるほど十分に柔軟です。私は通常、何かを色付けしたいときはいつでもその場で新しい正規表現を作成します。このため、カスタムログファイルの色付けツールよりもcoloutの方が好きです。なぜなら、色付けするものに関係なく、1つのツールを学習するだけでよいからです。
また、ログファイル自体にANSIコードを実際にダンプすることも避けます。これは、grep正規表現のANSIコードと常に一致することを忘れない限り、ログファイル内のパターンのgrepのようなものを壊すためです。
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)))`
私のソリューションは次のとおりです。
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])
クラスを定義する
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")
私が問題を抱えていたのは、フォーマッタを適切にセットアップすることでした。
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
他のソリューションは問題ないようですが、いくつかの問題があります。時には望まれない行全体を色付けするものもあれば、すべてを一緒に持つ構成を省略するものもあります。以下の解決策は、メッセージ自体以外には影響しません。
コード
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
に色コードを渡すだけです。
これは色コードを含む列挙です:
class TerminalColour:
"""
Terminal colour formatting codes
"""
# https://stackoverflow.com/questions/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'
これは、各ログレベルのnamesに適用できます。 これは巨大なハックであることに注意してください。
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'
},
追加するサブミッションは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
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")
ZetaSyanthisの色を使用した別のソリューション:
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__
関数から1回呼び出します。私はそこにこのようなものがあります:
options, arguments = p.parse_args()
log_level = logging.DEBUG if options.verbose else logging.WARNING
config_log(log_level)
また、出力がコンソールであることを確認します。そうでない場合、色は使用されません。