ファイルにログを記録して標準出力に出力するロガー構成


353

私はPythonのロギングモジュールを使用して、いくつかのデバッグ文字列をかなりうまく機能するファイルに記録しています。さらに、このモジュールを使用して、文字列をstdoutにも出力します。どうすればよいですか?文字列をファイルに記録するには、次のコードを使用します。

import logging
import logging.handlers
logger = logging.getLogger("")
logger.setLevel(logging.DEBUG)
handler = logging.handlers.RotatingFileHandler(
    LOGFILE, maxBytes=(1048576*5), backupCount=7
)
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)
logger.addHandler(handler)

そして、次のようなロガー関数を呼び出します

logger.debug("I am written to the file")

ここで助けてくれてありがとう!

回答:


451

ルートロガーへのハンドルを取得し、を追加するだけStreamHandlerです。StreamHandlerstderrへの書き込み。stderrよりもstdoutが本当に必要かどうかはわかりませんが、これは私がPythonロガーをセットアップするときに使用するものであり、私もを追加してFileHandlerいます。次に、すべてのログが両方の場所に送られます(これは、あなたが望むように聞こえることです)。

import logging
logging.getLogger().addHandler(logging.StreamHandler())

stdout代わりに出力したい場合はstderrStreamHandlerコンストラクタにそれを指定するだけです。

import sys
# ...
logging.getLogger().addHandler(logging.StreamHandler(sys.stdout))

にを追加Formatterして、すべてのログ行に共通のヘッダーを付けることもできます。

つまり:

import logging
logFormatter = logging.Formatter("%(asctime)s [%(threadName)-12.12s] [%(levelname)-5.5s]  %(message)s")
rootLogger = logging.getLogger()

fileHandler = logging.FileHandler("{0}/{1}.log".format(logPath, fileName))
fileHandler.setFormatter(logFormatter)
rootLogger.addHandler(fileHandler)

consoleHandler = logging.StreamHandler()
consoleHandler.setFormatter(logFormatter)
rootLogger.addHandler(consoleHandler)

次の形式で出力します。

2012-12-05 16:58:26,618 [MainThread  ] [INFO ]  my message

19
また、StreamHandlerwithを初期化するだけでもsys.stdout、stderrの代わりにログに記録されます。
Silas Ray

1
@ sr2222 logger.addHandler(sys.stdout)がNameErrorを返します:名前 'sys'が定義されていません
stdcerr

21
ええと...あなたがimport sys最初にする必要があります そして実際に、つまりハンドラを初期化consoleHandler = logging.StreamHandler(sys.stdout)
シラスレイ

15
私がすでに言ったように、それはあなたがそれをする方法ではないからです。sys.stdoutでHANDLERを作成し、ハンドラーをロガーに接続します。
Silas Ray

6
rootLogger.setLevel(logging.DEBUG)情報メッセージまたはデバッグメッセージを表示しようとしている場合は忘れないでください
storm_m2138

247

logging.basicConfig()handlersPython 3.3以降でキーワード引数を取ることができます。これにより、特に同じフォーマッターで複数のハンドラーを設定するときに、ロギングの設定が大幅に簡略化されます。

handlers–指定する場合、これは、ルートロガーに追加するために作成済みのハンドラーの反復可能でなければなりません。フォーマッタセットがまだないハンドラには、この関数で作成されたデフォルトのフォーマッタが割り当てられます。

したがって、次のような1回の呼び出しでセットアップ全体を実行できます。

import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
    handlers=[
        logging.FileHandler("debug.log"),
        logging.StreamHandler()
    ]
)

(またはimport sys+ StreamHandler(sys.stdout)元の質問の要件ごとに– StreamHandlerのデフォルトはstderrに書き込むことです。ログ形式をカスタマイズし、ファイル名/行、スレッド情報などを追加する場合は、LogRecord属性を確認してください。)

上記の設定は、スクリプトの冒頭付近で一度だけ行う必要があります。次のように、コードベースの他のすべての場所からのロギングを後で使用できます。

logging.info('Useful message')
logging.error('Something bad happened')
...

注:機能しない場合は、おそらく他の誰かがすでにロギングシステムを別の方法で初期化している可能性があります。コメントはlogging.root.handlers = []、への電話の前に行うことを提案していbasicConfig()ます。


5
level = logging.INFOまたは必要なレベルも設定することを忘れないでください
Andy Matteson

5
の定義FileHandlerlogging.FileHandler(filename, mode='a', encoding=None, delay=False)。つまり、同じフォルダにログインするだけの場合は、だけを使用できますFileHandler("mylog.log")。ログを毎回上書きする場合は、2番目の引数として「w」を設定します。
user136036

7
私はこれを試しましたが、コンソールが出力を出していますが、出力ファイルは空です。
Ramesh-X

4
@ Ramesh-X、これも私を狂わせた。ちょうどんlogging.root.handlers = []を呼び出す前にbasicConfig、機能を見てみましょう-それは迷惑なんです。
ihadanny

70

引数なしでStreamHandlerを追加すると、stdoutではなくstderrに移動します。他のプロセスがstdoutダンプに依存している場合(つまり、NRPEプラグインを作成する場合)、stdoutを明示的に指定してください。予期しない問題が発生する可能性があります。

次に、想定された値と質問のLOGFILEを再利用する簡単な例を示します。

import logging
from logging.handlers import RotatingFileHandler
from logging import handlers
import sys

log = logging.getLogger('')
log.setLevel(logging.DEBUG)
format = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")

ch = logging.StreamHandler(sys.stdout)
ch.setFormatter(format)
log.addHandler(ch)

fh = handlers.RotatingFileHandler(LOGFILE, maxBytes=(1048576*5), backupCount=7)
fh.setFormatter(format)
log.addHandler(fh)

私はこれを試しています。
Ajay Kumar

19

他のハンドラーをセットアップしたり、メッセージをログに記録したりする前に、引数としてを使用して実行basicConfigするかstream=sys.stdoutStreamHandlerメッセージをstdoutにプッシュするを手動でルートロガー(または、必要な他のロガー)に追加します。


5

Waterboyのコードを複数のPythonパッケージで何度も使用した後、ついにそれを小さなスタンドアロンPythonパッケージにキャストしました。

https://github.com/acschaefer/duallog

コードは十分に文書化されており、使いやすいです。.pyファイルをダウンロードしてプロジェクトに含めるか、からパッケージ全体をインストールしますpip install duallog


何らかの理由でコンソールにログが記録されない、またはファイルが空にならない(空)
JackTheKnife

5

ログインstdoutし、rotating fileさまざまなレベルやフォーマットに:

import logging
import logging.handlers
import sys

if __name__ == "__main__":

    # Change root logger level from WARNING (default) to NOTSET in order for all messages to be delegated.
    logging.getLogger().setLevel(logging.NOTSET)

    # Add stdout handler, with level INFO
    console = logging.StreamHandler(sys.stdout)
    console.setLevel(logging.INFO)
    formater = logging.Formatter('%(name)-13s: %(levelname)-8s %(message)s')
    console.setFormatter(formater)
    logging.getLogger().addHandler(console)

    # Add file rotating handler, with level DEBUG
    rotatingHandler = logging.handlers.RotatingFileHandler(filename='rotating.log', maxBytes=1000, backupCount=5)
    rotatingHandler.setLevel(logging.DEBUG)
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    rotatingHandler.setFormatter(formatter)
    logging.getLogger().addHandler(rotatingHandler)

    log = logging.getLogger("app." + __name__)

    log.debug('Debug message, should only appear in the file.')
    log.info('Info message, should appear in file and stdout.')
    log.warning('Warning message, should appear in file and stdout.')
    log.error('Error message, should appear in file and stdout.')

2

これは、Waterboyの回答とその他のさまざまなソースに基づいた、完全に適切にラップされたソリューションです。コンソールとログファイルの両方へのロギングをサポートし、さまざまなログレベル設定を可能にし、色分けされた出力を提供し、簡単に構成できます(Gistとしても利用可能):

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

# -------------------------------------------------------------------------------
#                                                                               -
#  Python dual-logging setup (console and log file),                            -
#  supporting different log levels and colorized output                         -
#                                                                               -
#  Created by Fonic <https://github.com/fonic>                                  -
#  Date: 04/05/20                                                               -
#                                                                               -
#  Based on:                                                                    -
#  https://stackoverflow.com/a/13733863/1976617                                 -
#  https://uran198.github.io/en/python/2016/07/12/colorful-python-logging.html  -
#  https://en.wikipedia.org/wiki/ANSI_escape_code#Colors                        -
#                                                                               -
# -------------------------------------------------------------------------------

# Imports
import os
import sys
import logging

# Logging formatter supporting colored output
class LogFormatter(logging.Formatter):

    COLOR_CODES = {
        logging.CRITICAL: "\033[1;35m", # bright/bold magenta
        logging.ERROR:    "\033[1;31m", # bright/bold red
        logging.WARNING:  "\033[1;33m", # bright/bold yellow
        logging.INFO:     "\033[0;37m", # white / light gray
        logging.DEBUG:    "\033[1;30m"  # bright/bold black / dark gray
    }

    RESET_CODE = "\033[0m"

    def __init__(self, color, *args, **kwargs):
        super(LogFormatter, self).__init__(*args, **kwargs)
        self.color = color

    def format(self, record, *args, **kwargs):
        if (self.color == True and record.levelno in self.COLOR_CODES):
            record.color_on  = self.COLOR_CODES[record.levelno]
            record.color_off = self.RESET_CODE
        else:
            record.color_on  = ""
            record.color_off = ""
        return super(LogFormatter, self).format(record, *args, **kwargs)

# Setup logging
def setup_logging(console_log_output, console_log_level, console_log_color, logfile_file, logfile_log_level, logfile_log_color, log_line_template):

    # Create logger
    # For simplicity, we use the root logger, i.e. call 'logging.getLogger()'
    # without name argument. This way we can simply use module methods for
    # for logging throughout the script. An alternative would be exporting
    # the logger, i.e. 'global logger; logger = logging.getLogger("<name>")'
    logger = logging.getLogger()

    # Set global log level to 'debug' (required for handler levels to work)
    logger.setLevel(logging.DEBUG)

    # Create console handler
    console_log_output = console_log_output.lower()
    if (console_log_output == "stdout"):
        console_log_output = sys.stdout
    elif (console_log_output == "stderr"):
        console_log_output = sys.stderr
    else:
        print("Failed to set console output: invalid output: '%s'" % console_log_output)
        return False
    console_handler = logging.StreamHandler(console_log_output)

    # Set console log level
    try:
        console_handler.setLevel(console_log_level.upper()) # only accepts uppercase level names
    except:
        print("Failed to set console log level: invalid level: '%s'" % console_log_level)
        return False

    # Create and set formatter, add console handler to logger
    console_formatter = LogFormatter(fmt=log_line_template, color=console_log_color)
    console_handler.setFormatter(console_formatter)
    logger.addHandler(console_handler)

    # Create log file handler
    try:
        logfile_handler = logging.FileHandler(logfile_file)
    except Exception as exception:
        print("Failed to set up log file: %s" % str(exception))
        return False

    # Set log file log level
    try:
        logfile_handler.setLevel(logfile_log_level.upper()) # only accepts uppercase level names
    except:
        print("Failed to set log file log level: invalid level: '%s'" % logfile_log_level)
        return False

    # Create and set formatter, add log file handler to logger
    logfile_formatter = LogFormatter(fmt=log_line_template, color=logfile_log_color)
    logfile_handler.setFormatter(logfile_formatter)
    logger.addHandler(logfile_handler)

    # Success
    return True

# Main function
def main():

    # Setup logging
    script_name = os.path.splitext(os.path.basename(sys.argv[0]))[0]
    if (not setup_logging(console_log_output="stdout", console_log_level="warning", console_log_color=True,
                        logfile_file=script_name + ".log", logfile_log_level="debug", logfile_log_color=False,
                        log_line_template="%(color_on)s[%(created)d] [%(threadName)s] [%(levelname)-8s] %(message)s%(color_off)s")):
        print("Failed to setup logging, aborting.")
        return 1

    # Log some messages
    logging.debug("Debug message")
    logging.info("Info message")
    logging.warning("Warning message")
    logging.error("Error message")
    logging.critical("Critical message")

# Call main function
if (__name__ == "__main__"):
    sys.exit(main())

-4

2.7の場合、以下を試してください。

fh = logging.handlers.RotatingFileHandler(LOGFILE, maxBytes=(1048576*5), backupCount=7)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.