web-dev-qa-db-ja.com

Djangoフォーマッタにカスタム属性を記録する

Djangoロギングを使用して、フォーマッタのカスタム属性を使用してログを記録するにはどうすればよいですか?たとえば、ログインしたユーザー名のロギングを考えています。

の中に settings.pyスクリプトでは、LOGGING変数が定義されています。

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'filters': {
        'require_debug_false': {
            '()': 'Django.utils.log.RequireDebugFalse'
        },
    },
    'formatters' : {
        'info_format' : {
            'format' : '%(asctime)s %(levelname)s - %(message)s',
        },
    }
}

次のような形式を使用したいと思います。

'format' : '%(asctime).19s %(levelname)s - %(username)s: %(message)s'

Usernameは現在ログインしているユーザーです。たぶん他の種類のセッションの変数がここに追加されるかもしれません。

ここでの回避策は、ロガーメソッドでextraパラメーターを使用することです。これは、フォーマット文字列で使用する文字列としてキーを含む辞書を受け取ります。

logger.info(message, extra={'username' : request.user.username})

別の(醜い)回避策は、ロギングフォーマッタが持つデフォルトの属性の一部ではないものを含めるようにメッセージ属性を構築することです。

message = request.user.username + " - " + message
logger.info(message)

しかし、特定の属性を使用してフォーマット文字列を設定し、Djangoを自動的にロギングAPIに提供する方法はありますか?%(username)sの場合、例えばrequest.user.username 、おそらく他の...

17
Javier Novoa C.

フィルターを使用して、カスタム属性を追加できます。例えば ​​:

def add_my_custom_attribute(record):
    record.myAttribute = 'myValue'
    record.username = record.request.user.username 
    return True

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'filters': {
        ...
        'add_my_custom_attribute': {
            '()': 'Django.utils.log.CallbackFilter',
            'callback': add_my_custom_attribute,
        }
    },
    'handlers': {
        ...
        'Django.server': {
            'level': 'INFO',
            'class': 'logging.StreamHandler',
            'filters': ['add_my_custom_attribute'],
            'formatter': 'Django.server',
        },            
    },
    ...
}

フィルターをインストールすることにより、各ログレコードを処理し、ロガーからハンドラーに渡すかどうかを決定できます。

フィルターは、ログのすべての詳細(つまり、時間、重大度、要求、ステータスコード)を含むログレコードを取得します。

レコードの属性は、フォーマッタが文字列メッセージにフォーマットするために使用されます。そのレコードにカスタム属性を追加すると、フォーマッターでも使用できるようになります。

24
napuzba

extraキーワードは回避策ではありません。 custom logging を完全に記述しない限り、これがカスタマイズされたフォーマッタを記述する最も説得力のある方法です。

format: '%(asctime).19s %(levelname)s - %(username)s: %(message)s'
logging.basicConfig(format=format)
logger.info(message, extra={'username' : request.user.username})

ドキュメントからの注意( **​​ kwars for Django logger ):

Extraで渡されたディクショナリのキーは、ロギングシステムで使用されるキーと衝突しないようにする必要があります。

Formatterが予期するストリングが欠落している場合、メッセージはログに記録されません。

この機能は特別な状況での使用を意図しており、常にではありません。

3
Bishwas Mishra

私はこの質問に対する多くの可能な完全な答えの1つを提供します:

Djangoロギングを使用して、フォーマッタのカスタム属性を使用してログを記録するにはどうすればよいですか?たとえば、ログインしたユーザー名のロギングを考えています。

他の回答は、pythonのロギングユーティリティを使用してコンテキスト情報を追加する方法に触れました。フィルターを使用してログレコードに追加情報を添付する方法は理想的であり、ドキュメントで最もよく説明されています。

https://docs.python.org/3/howto/logging-cookbook.html#using-filters-to-impart-contextual-information

これはまだ普遍的な方法でリクエストからユーザーを取得する方法を教えてくれません。次のライブラリがこれを行います:

https://github.com/ninemoreminutes/Django-crum

したがって、2つを組み合わせると、質問された質問に対する完全な回答が得られます。

import logging
from crum import get_current_user

class ContextFilter(logging.Filter):
    """
    This is a filter injects the Django user
    """

    def filter(self, record):

        record.user = get_current_user()
        return True

if __name__ == '__main__':
    logging.basicConfig(level=logging.DEBUG,
                        format='User: %(user)-8s %(message)s')
    a1 = logging.getLogger('a.b.c')

    f = ContextFilter()
    a1.addFilter(f)
    a1.debug('A debug message')

これは、CRUMライブラリが適切にインストールされているDjango要求/応答サイクル内で発生する必要があります。

1
AlanSE