Djangoアプリを開発しています。エラー/トレースロギングにPythonのロギングモジュールを使用しようとしています。サイトのさまざまな領域に異なるロガーを構成したいのですが。これまでのところ、これですべての作業が完了しましたが、1つは頭を悩ませることがあります。
ルートロガーをsys.stderrに移動し、ファイルに書き込むように別のロガーを構成しました。これは私のsettings.pyファイルにあります:
sviewlog = logging.getLogger('MyApp.views.scans')
view_log_handler = logging.FileHandler('C:\\MyApp\\logs\\scan_log.log')
view_log_handler.setLevel(logging.INFO)
view_log_handler.setFormatter(logging.Formatter('%(asctime)s %(name)-12s %(levelname)-8s %(message)s'))
sviewlog.addHandler(view_log_handler)
かなりシンプルに思えます。ただし、ここに問題があります。sviewlogに書き込んだものはすべて、ログファイルに2回書き込まれます。ルートロガーはそれを一度だけ印刷します。 addHandler()が2回呼び出されているようです。そして、コードをデバッガーに通すと、これがまさに私が目にするものです。 settings.pyのコードは2回実行されるため、2つのFileHandlerが作成され、同じロガーインスタンスに追加されます。しかし、なぜ?そして、どうすればこれを回避できますか?
誰がここで何が起こっているのか教えてもらえますか? sviewlogロガー/ハンドラーのインスタンス化コードを、それが使用されているファイルに移動しようとしました(実際には適切な場所のように思われるため)が、同じ問題があります。オンラインで見たほとんどの例ではルートロガーのみを使用しており、複数のロガーを使用することをお勧めします。
自分の質問に答えさせてください。ここでの根本的な問題は、settings.pyが2回またはおそらくそれ以上インポートされることです( ここ を参照)。 (私はまだこれがなぜなのか理解していません。たぶんいくつかのDjango専門家が私にそれを説明することができます。)これは他のいくつかのモジュールにも当てはまるようです。現時点では私はしませんsettings.pyが何回インポートされるかについて想定するのが賢明だと思います。そのため、そのような想定は一般的に安全ではありません。このコードをsettings.py以外の場所に置いたところ、結果は同じです。
これの周りにコーディングする必要があります。つまり、ロガーにハンドラーを追加する前に、既存のハンドラーがないかロガーを確認する必要があります。 1つのロガーに複数のハンドラー(同じタイプであっても)を接続することは完全に合理的であるため、これは少し醜いです。これに対処するにはいくつかの解決策があります。 1つは、ロガーオブジェクトのhandlersプロパティを確認することです。ハンドラが1つだけ必要で長さが0より大きい場合は、追加しないでください。個人的には、このソリューションは好きではありません。ハンドラーが増えると、面倒になるからです。
私はこのようなものが好きです(Thomas Guettlerのおかげです):
# file logconfig.py
if not hasattr(logging, "set_up_done"):
logging.set_up_done=False
def set_up(myhome):
if logging.set_up_done:
return
# set up your logging here
# ...
logging.set_up_done=True
Django imports settings.pyが複数回ドキュメント化されていたほうがいいと思います。また、私の構成が何らかの形でこの複数のインポートを引き起こしていると思いますが、見つけるのに問題があります。問題の原因と理由を調べます。おそらくドキュメントでそれが見つからなかったのかもしれませんが、それはユーザーに警告する必要がある種類のことだと思います。
特定のケースについてコメントするのは難しい。 settings.pyが2回実行される場合、送信されたログごとに2行を取得するのが通常です。
同じ問題が発生したため、ロギング専用のモジュールを1つ持つようにプロジェクトで設定しました。そのモジュールには「モジュールシングルトン」パターンがあるため、興味深いコードを1回だけ実行します。
次のようになります。
def init_logging():
stdoutHandler = logging.StreamHandler( sys.stdout )
stdoutHandler.setLevel( DEBUG )
stdoutHandler.setFormatter( logging.Formatter( LOG_FORMAT_WITH_TIME ) )
logging.getLogger( LOG_AREA1 ).addHandler( stdoutHandler )
logInitDone=False #global variable controlling the singleton.
if not logInitDone:
logInitDone = True
init_logging()
初めてlog.pyをインポートすると、ロギングが正しく設定されます。
古いスレッドを復活させましたが、Django 1.3 Python Logging with dictConfig format )を使用しているときに重複メッセージが発生しました。
disable_existing_loggers
は、複数のsettings.pyのロードによる重複したハンドラー/ロギングの問題を解消しますが、特定のpropagate
にlogger
ブール値を適切に指定しない場合でも、重複したログメッセージを取得できます。 。つまり、propagate=False
子ロガー用。例えば。、
'loggers': {
'Django': {
'handlers':['null'],
'propagate': True,
'level':'INFO',
},
'Django.request': {
'handlers': ['console'],
'level': 'ERROR',
'propagate': False,
},
'project': {
'handlers': ['console', 'project-log-file'],
'level': 'DEBUG',
'propagate': True,
},
'project.customapp': {
'handlers': ['console', 'customapp-log-file'],
'level': 'DEBUG',
'propagate': False,
},
}
ここに、 project.customapp
セットpropagate=False
project
ロガーにも捕捉されないようにします。 Django logging docs はいつものように優れています。
「Djangoがsettings.pyを複数回インポートする」理由に関する質問に答えるには、そうしません。
実際には、2回インポートされます(最初のコードチャンクをスキップしてそこに進みますが、時間がある場合はよく読んでください)。
http://blog.dscpl.com.au/2010/03/improved-wsgi-script-for-use-with.html
PS –古いスレッドを復活させて申し訳ありません。
Initを実行するときにハンドラーの数を確認することで問題を回避できます。
def init_logging():
stdoutHandler = logging.StreamHandler( sys.stdout )
stdoutHandler.setLevel( DEBUG )
stdoutHandler.setFormatter( logging.Formatter( LOG_FORMAT_WITH_TIME ) )
logger = logging.getLogger( LOG_AREA1 )
if len(logger.handlers) < 1:
logger.addHandler( stdoutHandler )
これはそれを処理するための素晴らしい方法だとは思いません。個人的には、Djangoでpythonロギングモジュールを使用してログインするために、関心のある各アプリケーションのviews.pyにロガーを作成し、次に各ビュー機能のロガー。
from Django.http import HttpResponse
from magic import makeLogger
from magic import getLogger
makeLogger('myLogName', '/path/to/myLogName.log')
def testLogger(request):
logger = getLogger('myLogName')
logger.debug('this worked')
return HttpResponse('TEXT, HTML or WHATEVER')
これはデバッグに関するかなり良い記事ですDjangoおよびいくつかのロギングをカバーします: http://simonwillison.net/2008/May/22/debugging/
「Djangoがsettings.pyを複数回インポートする」理由に関する質問に答えるには、そうしません。
おそらく、複数のpythonサブインタープリターを作成するマルチプロセス/マルチスレッドWebサーバーを実行しています。これらのサブインタープリターはそれぞれ、Djangoアプリからコードをインポートします。
Djangoテストサーバーでテストします。設定が何度もインポートされていないことがわかります。
しばらく前に、最初のDjango/Apacheアプリでニースシングルトン(より正確にはpython borgイディオムバージョン)を設計しましたが、そうだとすぐに気付きました。シングルトンの複数のインスタンスが作成されていた...
一度限りのミドルウェアを使用して、プライベート変数なしで同様の効果を得ることができます。これはWebリクエストのロギングのみを構成することに注意してください。シェルにログインしたり、コマンドを実行したりするには、別の解決策を見つける必要があります。
from Django.conf import settings
from Django.core.exceptions import MiddlewareNotUsed
import logging
import logging.handlers
import logging.config
__all__ = ('LoggingConfigMiddleware',)
class LoggingConfigMiddleware:
def __init__(self):
'''Initialise the logging setup from settings, called on first request.'''
if hasattr(settings, 'LOGGING'):
logging.config.dictConfig(settings.LOGGING)
Elif getattr(settings, 'DEBUG', False):
print 'No logging configured.'
raise MiddlewareNotUsed('Logging setup only.')
Django-loggingの代わりにpython loggerを使用するのはなぜですか?試してみてください。問題が解決する可能性があります。
http://code.google.com/p/Django-logging/wiki/Overview
現時点ではルートロガーの表示のみが許可されていますが、複数のロガーに確実に書き込むことができます。
A Lee投稿に追加するには、pythonロギングのドキュメントに、伝播について次のように記載されています。
Logger.propagate
これがfalseと評価された場合、ロギングメッセージはこのロガーまたはその子ロガーによって上位レベル(祖先)ロガーのハンドラーに渡されません。コンストラクターはこの属性を1に設定します。
つまり、propagate == False
then child loggerはロギングメッセージをそのparent loggerに渡しません
ハッカな方法ですが、ロギングコードをadmin.py内に配置することもできます。一度だけインポートすることになっています。
あるいは;最初にMyApp.views.scans
ログは存在しますか?存在する場合(エラーが発生する可能性があります)、作成をスキップすることができます(したがって、ハンドラーを再度追加しないでください)。よりクリーンな方法ですが、私はこれを試していません。
また、このコードを配置するためのより適切な場所がなければなりません(__init__.py
?)。 settings.py
は設定用です。