Pythonのロギングモジュールを使用して、DEBUGまたはINFOレベルのメッセージと、より高いレベルのメッセージを別のストリームに送信する簡単な方法はありますか?
とにかくそれは良い考えですか?
必ずしも良い考えではありませんが(情報とデバッグメッセージが通常の出力と混ざっているのを見るのは混乱するかもしれません!)、複数のハンドラーオブジェクトとそれぞれにカスタム filter を設定できるため、実現可能です。各ハンドラーが処理するログレコードを選択するため。
import logging
import sys
class LessThanFilter(logging.Filter):
def __init__(self, exclusive_maximum, name=""):
super(LessThanFilter, self).__init__(name)
self.max_level = exclusive_maximum
def filter(self, record):
#non-zero return means we log this message
return 1 if record.levelno < self.max_level else 0
#Get the root logger
logger = logging.getLogger()
#Have to set the root logger level, it defaults to logging.WARNING
logger.setLevel(logging.NOTSET)
logging_handler_out = logging.StreamHandler(sys.stdout)
logging_handler_out.setLevel(logging.DEBUG)
logging_handler_out.addFilter(LessThanFilter(logging.WARNING))
logger.addHandler(logging_handler_out)
logging_handler_err = logging.StreamHandler(sys.stderr)
logging_handler_err.setLevel(logging.WARNING)
logger.addHandler(logging_handler_err)
#demonstrate the logging levels
logger.debug('DEBUG')
logger.info('INFO')
logger.warning('WARNING')
logger.error('ERROR')
logger.critical('CRITICAL')
実装はさておき、特に別のハンドラーを追加してファイルに追加でログを記録できるため、pythonのログ機能を使用してターミナルに出力することをお勧めします。 stdoutをDEBUGではなくINFOに設定すると、ユーザーが通常ログファイルに表示しない追加のDEBUG情報を含めることもできます。
同じ問題が発生し、SplitStreamHandlerというカスタムロギングハンドラーを作成しました。
import sys
import logging
class SplitStreamHandler(logging.Handler):
def __init__(self):
logging.Handler.__init__(self)
def emit(self, record):
# mostly copy-paste from logging.StreamHandler
try:
msg = self.format(record)
if record.levelno < logging.WARNING:
stream = sys.stdout
else:
stream = sys.stderr
fs = "%s\n"
try:
if (isinstance(msg, unicode) and
getattr(stream, 'encoding', None)):
ufs = fs.decode(stream.encoding)
try:
stream.write(ufs % msg)
except UnicodeEncodeError:
stream.write((ufs % msg).encode(stream.encoding))
else:
stream.write(fs % msg)
except UnicodeError:
stream.write(fs % msg.encode("UTF-8"))
stream.flush()
except (KeyboardInterrupt, SystemExit):
raise
except:
self.handleError(record)
更新されたドキュメントから、このケースをかなりうまくカバーしています。
http://docs.python.org/howto/logging.html#logging-advanced-tutorial
import sys # Add this.
import logging
# create logger
logger = logging.getLogger('simple_example')
logger.setLevel(logging.DEBUG)
# create console handler and set level to debug
ch = logging.StreamHandler( sys.__stdout__ ) # Add this
ch.setLevel(logging.DEBUG)
# create formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# add formatter to ch
ch.setFormatter(formatter)
# add ch to logger
logger.addHandler(ch)
# 'application' code
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')
コメントで、出力をstdoutにするために例から必要な2つの変更について説明しました。レベルに応じて、フィルターを使用してリダイレクトすることもできます。
変更を理解するための詳細については、 http://docs.python.org/library/logging.handlers.html#module-logging.handlers を参照してください。
便宜上、すべてをフォーマッタと一緒に1つのパッケージに追加します。
# shared formatter, but you can use separate ones:
FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(threadName)s - %(message)s'
formatter = logging.Formatter(FORMAT)
# single app logger:
log = logging.getLogger(__name__)
log.setLevel(logging.INFO)
# 2 handlers for the same logger:
h1 = logging.StreamHandler(sys.stdout)
h1.setLevel(logging.DEBUG)
# filter out everything that is above INFO level (WARN, ERROR, ...)
h1.addFilter(lambda record: record.levelno <= logging.INFO)
h1.setFormatter(formatter)
log.addHandler(h1)
h2 = logging.StreamHandler(sys.stderr)
# take only warnings and error logs
h2.setLevel(logging.WARNING)
h2.setFormatter(formatter)
log.addHandler(h2)
# profit:
log.info(...)
log.debug(...)
私のユースケースは、処理中に画面にエラーが表示されているときに、stdoutをデータファイルにリダイレクトすることでした。