デバッグ情報を含むPythonエラーをログに記録するにはどうすればよいですか?


468

私はPythonの例外メッセージをログファイルに出力していますlogging.error

import logging
try:
    1/0
except ZeroDivisionError as e:
    logging.error(e)  # ERROR:root:division by zero

例外文字列だけではなく、例外とそれを生成したコードに関する詳細な情報を出力することはできますか?行番号やスタックトレースなどがいいでしょう。

回答:


733

logger.exception エラーメッセージとともにスタックトレースを出力します。

例えば:

import logging
try:
    1/0
except ZeroDivisionError as e:
    logging.exception("message")

出力:

ERROR:root:message
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: integer division or modulo by zero

@Paulo Checkは、「Python 3ではlogging.exceptionexceptパーツのすぐ内側でメソッドを呼び出す必要があることに注意してください。このメソッドを任意の場所で呼び出すと、奇妙な例外が発生する可能性があります。ドキュメントに関する警告が表示されます。」


131
exceptionこの方法は、単純に呼び出しますerror(message, exc_info=1)exc_info例外コンテキストからロギングメソッドのいずれかに渡すとすぐに、トレースバックが取得されます。
Helmut Grohne 2013年

16
また、すべてのコードをtry / exceptでラップする必要がないように設定することもできますsys.excepthookここを参照)。
2015

23
あなたexcept Exception:はまったく使用eしていないので、単に書くことができます;)
マルコフェラーリ

21
e対話的にコードをデバッグしようとする場合は、検査することをお勧めします。:)これは私がいつもそれを含める理由です。
ヴィッキー・レイドラー2016

4
私が間違っている場合は修正してください。この場合、例外の実際の処理はないためraiseexceptスコープの最後に追加することは理にかなっています。それ以外の場合は、すべてが問題なく実行されます。
Dror、

184

約いいところlogging.exceptionというSiggyFの答えは:あなたは、任意のメッセージを渡すことができるということです、そしてロギングがまだすべての例外の詳細との完全なトレースバックが表示されます表示されません。

import logging
try:
    1/0
except ZeroDivisionError:
    logging.exception("Deliberate divide by zero traceback")

エラーをsys.stderrに出力するだけのデフォルト(最近のバージョン)のロギング動作では、次のようになります。

>>> import logging
>>> try:
...     1/0
... except ZeroDivisionError:
...     logging.exception("Deliberate divide by zero traceback")
... 
ERROR:root:Deliberate divide by zero traceback
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: integer division or modulo by zero

メッセージを提供せずに例外をログに記録できますか?
Stevoisiak

@StevenVascellaro- ''メッセージを入力したくない場合は渡すことをお勧めします...ただし、関数は少なくとも1つの引数なしでは呼び出すことができないため、何かを指定する必要があります。
ArtOfWarfare

147

exc_infoエラーレベルを選択できるようにするために、オプションを使用する方が良い場合があります(を使用する場合はexception、常にそのerrorレベルになります)。

try:
    # do something here
except Exception as e:
    logging.critical(e, exc_info=True)  # log exception info at CRITICAL log level

@CivFan:私は実際には他の編集や投稿のイントロを見ていませんでした。そのイントロもサードパーティのエディターによって追加されました。削除されたコメントの中に意図されていたものはどこにもありませんが、編集を取り消してコメントを削除することもできます。ここでの投票が編集されたバージョン以外のものであるのは長すぎます。
Martijn Pieters

logging.fatal、ロギングライブラリ内の方法は?しか見えないcritical
Ian

1
@Ianそれは別名だcriticalと同じように、warnにありますwarning
0xc0de

35

引用

アプリケーションが他の方法でログを記録している場合(loggingモジュールを使用しない場合)はどうなりますか?

ここで、tracebackここで使用できます。

import traceback

def log_traceback(ex, ex_traceback=None):
    if ex_traceback is None:
        ex_traceback = ex.__traceback__
    tb_lines = [ line.rstrip('\n') for line in
                 traceback.format_exception(ex.__class__, ex, ex_traceback)]
    exception_logger.log(tb_lines)
  • Python 2で使用します。

    try:
        # your function call is here
    except Exception as ex:
        _, _, ex_traceback = sys.exc_info()
        log_traceback(ex, ex_traceback)
  • Python 3で使用します。

    try:
        x = get_number()
    except Exception as ex:
        log_traceback(ex)

「_、_、ex_traceback = sys.exc_info()」を関数log_tracebackの外側に配置して、それを引数として渡したのはなぜですか?関数内で直接使用しないのはなぜですか?
バジルムーサ2017

@BasilMusa、ので、Pythonの3との互換性に、要するに、あなたの質問に答えるex_tracebackからである ex.__traceback__Pythonの3の下で、しかしex_tracebackからであるsys.exc_info()Pythonの2の下で
zangw

12

プレーンログを使用する場合、すべてのログレコードがこのルールに対応している必要がありますone record = one line。このルールに従ってgrep、ログファイルを処理するために他のツールを使用できます。

ただし、トレースバック情報は複数行です。だから私の答えは、このスレッドで上記のzangwによって提案されたソリューションの拡張バージョンです。問題は、トレースバック行が\n内部にある可能性があることです。そのため、この行末を取り除くために追加の作業を行う必要があります。

import logging


logger = logging.getLogger('your_logger_here')

def log_app_error(e: BaseException, level=logging.ERROR) -> None:
    e_traceback = traceback.format_exception(e.__class__, e, e.__traceback__)
    traceback_lines = []
    for line in [line.rstrip('\n') for line in e_traceback]:
        traceback_lines.extend(line.splitlines())
    logger.log(level, traceback_lines.__str__())

その後(ログを分析するとき)、必要なトレースバック行をログファイルからコピーして貼り付け、これを実行できます。

ex_traceback = ['line 1', 'line 2', ...]
for line in ex_traceback:
    print(line)

利益!


9

この答えは、上記の優れたものから構築されます。

ほとんどのアプリケーションでは、logging.exception(e)を直接呼び出すことはありません。ほとんどの場合、次のようにアプリケーションまたはモジュールに固有のカスタムロガーを定義しています。

# Set the name of the app or module
my_logger = logging.getLogger('NEM Sequencer')
# Set the log level
my_logger.setLevel(logging.INFO)

# Let's say we want to be fancy and log to a graylog2 log server
graylog_handler = graypy.GELFHandler('some_server_ip', 12201)
graylog_handler.setLevel(logging.INFO)
my_logger.addHandler(graylog_handler)

この場合、ロガーを使用して、次のように例外(e)を呼び出します。

try:
    1/0
except ZeroDivisionError, e:
    my_logger.exception(e)

例外のためだけに専用のロガーが必要な場合、これは実際に便利な仕上げです。
logicOnAbstractions

7

例外なくスタックトレースを記録できます。

https://docs.python.org/3/library/logging.html#logging.Logger.debug

2番目のオプションのキーワード引数はstack_infoで、デフォルトはFalseです。trueの場合、実際のロギング呼び出しを含むスタック情報がロギングメッセージに追加されます。これは、exc_infoを指定して表示されるものと同じスタック情報ではないことに注意してください。前者はスタックの一番下から現在のスレッドのロギング呼び出しまでのスタックフレームですが、後者は巻き戻されたスタックフレームに関する情報です。例外ハンドラーを検索しながら、例外を追跡します。

例:

>>> import logging
>>> logging.basicConfig(level=logging.DEBUG)
>>> logging.getLogger().info('This prints the stack', stack_info=True)
INFO:root:This prints the stack
Stack (most recent call last):
  File "<stdin>", line 1, in <module>
>>>

5

少しのデコレータ処理(Maybeモナドとリフティングに非常に大まかに影響)。安全にPython 3.6タイプの注釈を削除し、古いメッセージフォーマットスタイルを使用できます。

fallible.py

from functools import wraps
from typing import Callable, TypeVar, Optional
import logging


A = TypeVar('A')


def fallible(*exceptions, logger=None) \
        -> Callable[[Callable[..., A]], Callable[..., Optional[A]]]:
    """
    :param exceptions: a list of exceptions to catch
    :param logger: pass a custom logger; None means the default logger, 
                   False disables logging altogether.
    """
    def fwrap(f: Callable[..., A]) -> Callable[..., Optional[A]]:

        @wraps(f)
        def wrapped(*args, **kwargs):
            try:
                return f(*args, **kwargs)
            except exceptions:
                message = f'called {f} with *args={args} and **kwargs={kwargs}'
                if logger:
                    logger.exception(message)
                if logger is None:
                    logging.exception(message)
                return None

        return wrapped

    return fwrap

デモ:

In [1] from fallible import fallible

In [2]: @fallible(ArithmeticError)
    ...: def div(a, b):
    ...:     return a / b
    ...: 
    ...: 

In [3]: div(1, 2)
Out[3]: 0.5

In [4]: res = div(1, 0)
ERROR:root:called <function div at 0x10d3c6ae8> with *args=(1, 0) and **kwargs={}
Traceback (most recent call last):
  File "/Users/user/fallible.py", line 17, in wrapped
    return f(*args, **kwargs)
  File "<ipython-input-17-e056bd886b5c>", line 3, in div
    return a / b

In [5]: repr(res)
'None'

また、このソリューションを変更Noneして、exceptパーツからよりも意味のある何かを返すようにすることもできます(または、この戻り値をfallibleの引数で指定することにより、ソリューションを汎用的にすることもできます)。


0

ロギングモジュール(カスタムモジュールの場合)で、stack_infoを有効にします。

api_logger.exceptionLog("*Input your Custom error message*",stack_info=True)

-1

追加の依存関係に対処できる場合は、twisted.logを使用します。エラーを明示的にログに記録する必要はありません。また、トレースバック全体と時間をファイルまたはストリームに返します。


8
おそらくtwisted良い提案ですが、この回答はあまり貢献しません。使用方法、標準ライブラリのモジュールよりtwisted.logも優れている点、「エラーを明示的にログに記録する必要がない」loggingの意味については説明されていません
Mark Amery

-8

それを行うためのクリーンな方法はformat_exc()、関連する部分を取得するために出力を使用して解析することです:

from traceback import format_exc

try:
    1/0
except Exception:
    print 'the relevant part is: '+format_exc().split('\n')[-2]

よろしく


4
えっ?なぜ「関連部分」なのですか?すべての.split('\n')[-2]行いで捨てるの結果から、行番号とトレースバックをformat_exc()-あなたは通常欲しい有用な情報!しかも、それはさえの良い仕事しませんことを。例外メッセージに改行が含まれている場合、このアプローチでは例外メッセージの最終行のみが出力されます。つまり、例外クラスとほとんどの例外メッセージが失われ、さらにトレースバックが失われます。-1。
Mark Amery
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.