Django Logging, The Right Way
良いログはデバッグや問題のトラブルシューティングに欠かせません。 ローカルな開発で役に立つだけでなく、実稼働環境では不可欠です。 問題のログをレビューするとき、誰かが「私たちのアプリケーションではロギングが多すぎる」と言うのはまれですが、逆のことを聞くのはよくあることです。
A Crash Course in Python Loggers
すべてのファイルの先頭には、次のようなものがあるはずです:
import logginglogger = logging.getLogger(__name__)
__name__
はモジュールのドット付き Python パスに評価されます、たとえば、myproject/myapp/views.py
は myproject.myapp.views
を使用することになります。
# A simple string logged at the "warning" levellogger.warning("Your log message is here")# A string with a variable at the "info" levellogger.info("The value of var is %s", var)# Logging the traceback for a caught exceptiontry: function_that_might_raise_index_error()except IndexError: # equivalent to logger.error(msg, exc_info=True) logger.exception("Something bad happened")
Note: Python のロガーは、ロギング関数の引数として変数を渡せば、ログメッセージへの変数の挿入を処理します。 ログが出力される必要がない場合、変数の置換は決して起こらないので、決して使用されないログのための小さなパフォーマンスのヒットを回避するのに役立ちます。
-
debug
: 通常の操作には必要ないが、開発時には有用な情報。 -
info
: 通常の操作時に有用な情報。 -
error
: 重要で迅速な対応が必要な情報。 -
critical
: 実際には使用しませんが、error
より高い値が必要な場合は、これを使用します
Where to Log
アプリではログがどこに行くかは気にしない方がよいでしょう。 代わりに、コンソール (stdout
/stderr
) にすべてを記録し、そこから何をするかはサーバーに決定させる必要があります。 通常、これは専用の(そしてログローテートされた)ファイルに置かれ、Systemd ジャーナルや Docker によってキャプチャされ、ElasticSearch などの別のサービスに送られ、あるいはそれらのいくつかの組み合わせで行われます。 ログ ストレージは、デプロイメントに関するものであり、アプリケーションに関するものではありません。 通常、これは関連するデータを含む単なる文字列ですが、サーバーがすでにログにタイムスタンプを追加している場合、おそらく独自の formatter
からそれを除外したいことでしょう。 同様に、ログアグリゲータが JSON を受け入れることができる場合、 python-json-logger
のようなフォーマッタがより適切かもしれません。
Configuring the Logger
Python でロガーに書き込むことは簡単です。 正しい場所に行くように設定することは、予想以上に困難です。 前の投稿で説明したように、Django のデフォルトのロギングをバイパスすることから始めましょう。
Setting up Sentry
Error reporting services は、特別なサイトでない限り、重要です。 デフォルトでは、捕捉されない例外をキャッチし、問題を通知し (インシデントごとに 1 回のみ)、例外が発生したときのアプリケーションの状態を確認するための素晴らしいインターフェイスを提供します。 このための私のお気に入りのサービスは Sentry.
Sentry をさらに一歩進めて、warning
以上のログ メッセージをすべてこのサービスに送信することができます。 そうしないと、ログファイルの海の中に紛れ込んでしまい、実際にはほとんど見直されないからです。 これを行うには、任意の Python モジュールから送信されるすべてのログのためのキャッチオールとして機能する “root” ロガーを追加します。 Django の設定では、以下のようになります。
import logging.configLOGGING_CONFIG = Nonelogging.config.dictConfig({ 'version': 1, 'disable_existing_loggers': False, 'formatters': { 'console': { # exact format is not important, this is the minimum information 'format': '%(asctime)s %(name)-12s %(levelname)-8s %(message)s', }, }, 'handlers': { 'console': { 'class': 'logging.StreamHandler', 'formatter': 'console', }, # Add Handler for Sentry for `warning` and above 'sentry': { 'level': 'WARNING', 'class': 'raven.contrib.django.raven_compat.handlers.SentryHandler', }, }, 'loggers': { # root logger '': { 'level': 'WARNING', 'handlers': , }, },})
Logging From Your Application
サードパーティの依存関係からの警告やエラーについてだけ知りたいかもしれませんが、一般的にはアプリケーションコードについてもっと深い洞察を得たいものでしょう。 理想的には、コードはすべて単一のネームスペースの下に存在し、loggers
に 1 回追加するだけでキャプチャできます。 あなたのプロジェクトが名前空間 myproject
を使用していると仮定すると、上記のコードを基に次のように追加します。 新しいコードをコミットしてそれをデプロイしなければならないのは、過剰な作業のように感じられます。 これは環境変数の素晴らしい使用例です。 前のスタンザを次のように修正できます:
import osLOGLEVEL = os.environ.get('LOGLEVEL', 'info').upper()logging.config.dictConfig({ # ... 'loggers': { '': { 'level': 'WARNING', 'handlers': , }, 'myproject': { 'level': LOGLEVEL, 'handlers': , # required to avoid double logging with root logger 'propagate': False, }, },})
これで、アプリケーションのロギングはデフォルトで info
になりますが、環境変数 LOGLEVEL=debug
を設定することで簡単に一時的に増加させることができます。 また、ログの保存が問題でなければ、常に debug
レベルでログを記録することを検討してください。 これらのログは、単純な grep
や Kibana などのログ視覚化ツールで簡単にフィルターできます。
ノイズを取り除く
一度、ログを設定して実行すると、いくつかのモジュールは、本当に気にしない情報をログし、余計なノイズを生成するのに役立つことがあります (私は newrelic
に注目しています)。 このようなモジュールには、別のロガーを追加して、それらを調整することができます。
logging.config.dictConfig({ # ... 'loggers': { '': { 'level': 'WARNING', 'handlers': , }, 'myproject': { 'level': LOGLEVEL, 'handlers': , # required to avoid double logging with root logger 'propagate': False, }, # Don't send this module's logs to Sentry 'noisy_module': { 'level':'ERROR', 'handlers': , 'propagate': False, }, },})
もし、コンソールにさえもうるさいと感じたら、それらを完全に削除することができます。 Django の設定を上書きすることで、これを失いますが、元に戻すのは簡単です:
from django.utils.log import DEFAULT_LOGGINGlogging.config.dictConfig({ # ... 'formatters': { # ... 'django.server': DEFAULT_LOGGING, }, 'handlers': { # ... 'django.server': DEFAULT_LOGGING, }, 'loggers': { # ... 'django.server': DEFAULT_LOGGING, },})
。
Leave a Reply