Django 1.5.1:
DEBUG = False
LOGGING = {
'version': 1,
'disable_existing_loggers': True,
'formatters': {
'verbose': {
'format': '%(levelname)s %(asctime)s %(module)s %(message)s'
},
},
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'verbose',
},
},
'loggers': {
# root logger
'': {
'handlers': ['console'],
},
#'Django.request': {
# 'handlers': ['console'],
# 'level': 'DEBUG',
# 'propagate': False,
#},
}
}
コメント付きの行のコメントを外して、1/0
、トレースバックがコンソールに出力されます:
ERROR 2013-11-29 13:33:23,102 base Internal Server Error: /comment/*******/
Traceback (most recent call last):
...
File "*****/comments/views.py", line 10, in post
1/0
ZeroDivisionError: integer division or modulo by zero
WARNING 2013-11-29 13:33:23,103 csrf Forbidden (CSRF cookie not set.): /comment/******/
[29/Nov/2013 13:33:23] "POST /comment/******/ HTTP/1.0" 500 27
ただし、行がコメントのままの場合、トレースバックはコンソールに出力されません。
[29/Nov/2013 13:33:23] "POST /comment/******/ HTTP/1.0" 500 27
Django.request
ロガーは設定されていません。すべてをコンソールに出力するルートロガーに伝播します。
Django.request
は特別です。
なぜ動作しないのですか?
ここ 私は読んだ:
Django 1.5より前は、LOGGING設定は常にデフォルトのDjangoロギング構成を上書きしました。Django 1.5以降、プロジェクトのロギング構成をDjangoのデフォルトとマージして取得できるため、既存の構成に追加するか、既存の構成を置き換えるかを決定できます。
LOGGING dictConfigのdisable_existing_loggersキーがTrue(デフォルト)に設定されている場合、デフォルト設定は完全に上書きされます。または、disable_existing_loggersをFalseに設定して、ロガーの一部またはすべてを再定義できます。
Django/utils/log.py
:
# Default logging for Django. This sends an email to the site admins on every
# HTTP 500 error. Depending on DEBUG, all other log records are either sent to
# the console (DEBUG=True) or discarded by mean of the NullHandler (DEBUG=False).
DEFAULT_LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'require_debug_false': {
'()': 'Django.utils.log.RequireDebugFalse',
},
'require_debug_true': {
'()': 'Django.utils.log.RequireDebugTrue',
},
},
'handlers': {
'console':{
'level': 'INFO',
'filters': ['require_debug_true'],
'class': 'logging.StreamHandler',
},
'null': {
'class': 'Django.utils.log.NullHandler',
},
'mail_admins': {
'level': 'ERROR',
'filters': ['require_debug_false'],
'class': 'Django.utils.log.AdminEmailHandler'
}
},
'loggers': {
'Django': {
'handlers': ['console'],
},
'Django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': False,
},
'py.warnings': {
'handlers': ['console'],
},
}
}
したがって、デフォルトではDjango.request
にはpropagate = False
。しかし、私の場合、私は'disable_existing_loggers': True
。
解決策は、Djangoがログを構成して自分で処理しないようにすることです。幸い、これは簡単です。settings.py
で:
LOGGING_CONFIG = None
LOGGING = {...} # whatever you want, as you already have
import logging.config
logging.config.dictConfig(LOGGING)
UPDATE〜2015年3月:Django has clarified their documentation =:
LOGGING dictConfigのdisable_existing_loggersキーがTrueに設定されている場合、デフォルト構成のすべてのロガーが無効になります。無効化されたロガーは削除されたものと同じではありません。ロガーは引き続き存在しますが、ログに記録されたものは何も表示せずに破棄し、エントリを親ロガーに伝達することもしません。したがって、 'disable_existing_loggers'を使用する場合は十分に注意する必要があります。それはおそらくあなたが望むものではありません。代わりに、disable_existing_loggersをFalseに設定して、デフォルトロガーの一部またはすべてを再定義できます。または、LOGGING_CONFIGをNoneに設定して、ロギング構成を自分で処理することもできます。
後世と詳細について:説明?混乱のほとんどは、Djangoの貧弱な explanation of disable_existing_loggers
に起因すると考えられます。これは、Trueの場合、「デフォルトの構成は完全に上書きされる」と述べています。あなた自身の答えで、あなたはそれが正しくないことを発見しました。 Djangoがすでに構成されている)既存のロガーがdisabled置き換えられないことが起こっています。
Python logging documentation はそれをよりよく説明しています(強調を追加):
disable_existing_loggers – Falseと指定した場合、この呼び出しが行われたときに存在するロガーはそのまま残されます。これは下位互換性のある方法で古い動作を可能にするため、デフォルトはTrueです。この動作は、既存のロガーまたはその祖先がロギング構成で明示的に指定されていない限り、既存のロガーをdisableすることです。
Django docsに基づいて、「自分のLOGGING構成でデフォルトを上書きすると、指定していないものはbubble up」になります。 'この期待も上回っています。期待される動作は、replace_existing_loggers(これは現実のものではありません)の行に沿ったものです。代わりに= Djangoロガーはシャットダウンされませんバブリングされます。
最初にこれらのDjangoロガーのセットアップを防止する必要があります。ここではDjango docs がより役立ちます:
ロギングをまったく構成しない場合(または独自の方法でロギングを手動で構成する場合)、LOGGING_CONFIGをNoneに設定できます。これにより、構成プロセスが無効になります。
注:LOGGING_CONFIGをNoneに設定すると、設定プロセスが無効になり、それ自体はログに記録されません。設定プロセスを無効にした場合、Djangoは引き続きログを呼び出し、定義されているデフォルトのログ動作にフォールバックします。
Djangoは引き続きロガーを使用しますが、ロガーは構成によって処理されないため(無効にされるため)、これらのロガーは期待どおりにバブルアップします。上記の設定を使用した簡単なテスト:
manage.py Shell
>>> import logging
>>> logging.warning('root logger')
WARNING 2014-03-11 13:35:08,832 root root logger
>>> l = logging.getLogger('Django.request')
>>> l.warning('request logger')
WARNING 2014-03-11 13:38:22,000 Django.request request logger
>>> l.propagate, l.disabled
(1, 0)
わかりましたので、動作は「正しい」ですが、予期されていません。 Django/conf/__init__.py:65
:
def _configure_logging(self):
...
if self.LOGGING_CONFIG:
from Django.utils.log import DEFAULT_LOGGING
# First find the logging configuration function ...
logging_config_path, logging_config_func_name = self.LOGGING_CONFIG.rsplit('.', 1)
logging_config_module = importlib.import_module(logging_config_path)
logging_config_func = getattr(logging_config_module, logging_config_func_name)
logging_config_func(DEFAULT_LOGGING)
if self.LOGGING:
# Backwards-compatibility shim for #16288 fix
compat_patch_logging_config(self.LOGGING)
# ... then invoke it with the logging settings
logging_config_func(self.LOGGING)
何が起こっているかというと、デフォルトのロギング設定が適用され、Django.request
ロガーが作成されます。次に、私のカスタムLOGGING
構成がdisable_existing_loggers = True
、ただしPythonは既存のロガーを削除しませんDjango.request
、しかしそれを無効にするだけです。
だから私は手動で再構成する必要がありますDjango.request
ロガーを構成します。 :(
Django-2.1の場合、ロギング構成がより簡潔であることがわかりました。
$ ./manage.py Shell
>>> import logging
>>> # Grub all Django loggers
>>> loggers = [
name for name in logging.root.manager.loggerDict
if 'Django' in name
]
>>> for each in loggers:
logger = logging.getLogger(each)
print(
'Logger Name: {0}\nLogger Handlers: {1}\n'
'Logger Propagates: {2}\n\n'.format(
each,
logger.handlers,
logger.propagate
)
)
Logger Name: Django.db
Logger Handlers: []
Logger Propagates: True
Logger Name: Django.request
Logger Handlers: []
Logger Propagates: True
Logger Name: Django.template
Logger Handlers: []
Logger Propagates: True
Logger Name: Django.db.backends
Logger Handlers: []
Logger Propagates: True
Logger Name: Django.db.backends.schema
Logger Handlers: []
Logger Propagates: True
Logger Name: Django.security.csrf
Logger Handlers: []
Logger Propagates: True
Logger Name: Django
Logger Handlers: [<logging.StreamHandler object at 0x7f706d5dd780>, <Django.utils.log.AdminEmailHandler object at 0x7f706d740cf8>]
Logger Propagates: True
Logger Name: Django.contrib.gis
Logger Handlers: []
Logger Propagates: True
Logger Name: Django.contrib
Logger Handlers: []
Logger Propagates: True
Logger Name: Django.security
Logger Handlers: []
Logger Propagates: True
Logger Name: Django.server
Logger Handlers: [<logging.StreamHandler object at 0x7f706d59eba8>]
Logger Propagates: False
引用通り ドキュメント内 :
Django.serverを除くすべてのロガーは、ロギングを親に伝達し、ルートまで記録しますDjangoロガー。コンソールとmail_adminsハンドラーは、上記の動作を提供するためにルートロガーに接続されます。
これは propagate
のドキュメントに従い、次のように述べています:
注意
ハンドラーをロガーとその祖先の1つ以上にアタッチすると、同じレコードが複数回出力される場合があります。一般に、ハンドラーを複数のロガーにアタッチする必要はありません。ロガー階層の最上位にある適切なロガーにハンドラーをアタッチするだけであれば、すべての子孫ロガーによってログに記録されたすべてのイベントが表示されます。設定はTrueのままにしておきます。一般的なシナリオは、ルートロガーにのみハンドラーを接続し、残りの部分は伝播に任せることです。
したがって、私はDjangoがログを設定しないようにしません。- sentry を使用しているため、管理者への電子メールの送信を停止したいと思い、ルートロガーを使用するように設定しましたDjango docs examples によると、console
およびfile
ハンドラ
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
'style': '{',
},
'simple': {
'format': '{levelname} {message}',
'style': '{',
},
},
'filters': {
'require_debug_false': {
'()': 'Django.utils.log.RequireDebugFalse',
},
'require_debug_true': {
'()': 'Django.utils.log.RequireDebugTrue',
},
},
'handlers': {
'console': {
'level': 'INFO',
'filters': ['require_debug_true'],
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'file': {
'level': 'INFO',
'filters': ['require_debug_false'],
'class': 'logging.FileHandler',
'filename': os.path.join(LOGGING_DIR, 'Django.log'),
'formatter': 'verbose'
},
},
'loggers': {
'Django': {
'handlers': ['file', 'console'],
'level': 'INFO',
'propagate': True,
},
}
}
その結果:
Logger Name: Django
Logger Handlers: [<logging.FileHandler object at 0x7f5aa0fd1cc0>, <logging.StreamHandler object at 0x7f5aa0fd1ef0>]
Logger Propagates: True
まだ本番環境ではテストされていませんが、期待どおりに動作するようです。