読者です 読者をやめる 読者になる 読者になる

DjangoでDEBUG=Falseの際にSQLのログを出力する

Djangoでクエリのログを出力したい場合、ロギングの設定でdjango.db.backendsのロガーを設定すればできます。
ただし、これはsettings.DEBUG=Trueのときにしか出力されないようになっています。
これはドキュメントにも書かれています。
ロギング — Django 1.4 documentation
https://docs.djangoproject.com/en/1.6/topics/logging/
DEBUG=Falseの際でもSQLのログを記録したい場合があり、方法を調べてました。
試したのは、Python 2.7, Django 1.6.6

注意

Djangoデバッグ時にしかクエリログを出力しないようにしているのは、パフォーマンスに問題がある等の理由で意図的なものです。
この記事の方法でログを出力すると、パフォーマンスに問題が出る可能性があることに注意してください。

内部の動き

内部の仕組みとしては、django/db/__init__.pyのBaseDatabaseWrapperクラス、cursorメソッドでCursorクラスをデバッグ用と通常用を切り替えています。

    def cursor(self):
        """
        Creates a cursor, opening a connection if necessary.
        """
        self.validate_thread_sharing()
        if (self.use_debug_cursor or
            (self.use_debug_cursor is None and settings.DEBUG)):
            cursor = self.make_debug_cursor(self._cursor())
        else:
            cursor = util.CursorWrapper(self._cursor(), self)
        return cursor

self.use_debug_cursorの値がTrueであればデバッグ用のCursorクラスが使われるようです。

use_debug_cursorの値を変更するミドルウェアを作成する

connectionオブジェクトのuse_debug_cursorをTrueに設定するミドルウェアを作成します。

myproject/middleware.py
# coding: utf-8
class UseDebugCursorMiddleware(object):
    def process_request(self, request):
        # SQLのログを出力するようにデバッグ用のCursorクラスを使用
        from django.db import connection
        connection.use_debug_cursor = True

settings.pyでミドルウェアを使用する

作成したミドルウェアは、DB接続の前に実行されていないとダメなので、MIDDLEWARE_CLASSESの先頭に設定します。

myproject/settings.py

LOGGINGの設定は通常のクエリログを出力するためのものと同じです。

# 中略

DEBUG = False

# 中略

MIDDLEWARE_CLASSES = (
    'myproject.middleware.UseDebugCursorMiddleware',  # 一番最初にミドルウェアを適用します
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
)

# 中略

LOGGING = {
    'version': 1,
    'handlers': {
        'logfile': {
            'class': 'logging.handlers.RotatingFileHandler',
            'filename': os.path.join(BASE_DIR, 'query.log'),
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['logfile'],
            'level': 'DEBUG',
        }
    },
}

これでDEBUG=Falseの場合でも、SQLのログをファイルに出力することができました。