7839

雑草魂エンジニアブログ

【Python/Django】logging を設定して、リモート syslog サービスにログを出力する(システム監視)

今回、Djangoのアプリケーション運用監視において、Zabbix を用いることになり、監視サーバーにアプリケーション上のエラーログなどを出力する必要があった。Python の標準モジュールである logging を用いて簡単に設定できたので紹介する。

動作環境

logging に関して

docs.python.org

ログを取ること(出力すること)を、ロギング(logging)と言う。Pythonでは、 print() でログを出力することもできるが、logging モジュールでロギングをすることで以下のような利点がある。

  1. ログの種類を区別できる(Error / Debug / Info 等)
  2. フォーマットを指定すれば、簡単に統一されたログ出力ができる
  3. ログ出力先を設定できる(コンソール、テキスト等)

(1)ログの種別を区別することで、ただのDEBUG用のログ出力なのか、ユーザ(プログラム実行者)に伝えたい情報としての出力なのかを切り分けることができ、有用である。

Django の設定・使い方

docs.djangoproject.com

Django の logging では、独自のモジュールではなく、Pythonの標準モジュール logging を用いている。

Python の logging は、以下の4つで構成されている。

名前 役割(概要)
Logger ロギングシステムのエントリーポイント。すなわち、出力したいログの受け入れ口であり、ログ出力のトリガーとなる部分。
Handler どのようなログをどのように出力するか、設定する。
Filter ログレベルとは別に、ログデータにフィルタを設定する。
Formatter ログに出力する文字列の形式を設定する。

Djangoでは、 settings.pyLOGGING に設定を行う。

Django のデフォルトのロギング設定 は、 django/utils/log.py に定義されている。

カスタムする場合は、DEFAULTをコピーして貼り付けて、編集するのが一番楽かつ間違いなく設定できるかもしれない。以下はデフォルトの設定である。

DEBUG = True

ALLOWED_HOSTS = ["your_prod_host"]

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'filters': {
        'require_debug_false': {
            '()': 'django.utils.log.RequireDebugFalse',
        },
        'require_debug_true': {
            '()': 'django.utils.log.RequireDebugTrue',
        },
    },
    'formatters': {
        'django.server': {
            '()': 'django.utils.log.ServerFormatter',
            'format': '[{server_time}] {message}',
            'style': '{',
        }
    },
    'handlers': {
        'console': {
            'level': 'INFO',
            'filters': ['require_debug_true'],
            'class': 'logging.StreamHandler',
        },
        'django.server': {
            'level': 'INFO',
            'class': 'logging.StreamHandler',
            'formatter': 'django.server',
        },
        'mail_admins': {
            'level': 'ERROR',
            'filters': ['require_debug_false'],
            'class': 'django.utils.log.AdminEmailHandler'
        }
    },
    'loggers': {
        'django': {
            'handlers': ['console', 'mail_admins'],
            'level': 'INFO',
        },
        'django.server': {
            'handlers': ['django.server'],
            'level': 'INFO',
            'propagate': False,
        },
    }
}

ログを出力したい場合は、以下のように呼び出して、適切なログを選択して出力する。

import logging

logger = logging.getLogger("django")

logger.info("info-message")
logger.error("error-message")
logger.warning("warning-message")
logger.critical("critical-message")

開発時には、SQLをみたい場合が多いと思うので、以下を追加することでconsoleで確認することができる。(django.db.backends

'loggers': {
    'django.db.backends': {
          'handlers': ['console'],
          'level': 'DEBUG',
    },
},

本番時に、runserver では運用しないと思うので、以下も追加しておくといいと思う。(django.request )handlersに関しては、statusのエラーを検知して、ファイルに書き込むや通知するなどの設定が適宜必要かと思われる。

'loggers': {
    'django.request': {
          'handlers': ['console'],
          'level': 'ERROR',
    },
},

リモート syslog サービスにログを出力する

本題のリモート syslog サービスへのログ出力では、Python の標準モジュールに含まれている SysLogHandler を用いる。

SysLogHandler(address=('localhost', SYSLOG_UDP_PORT), facility=LOG_USER, socktype=socket.SOCK_DGRAM)

address の部分に、hostとportを指定することでリモートの syslog に出力することができる。

LOGGING = {
    <-- 省略 -->
    "formatters": {
        "simple": {
            "format": "{asctime} {message}",
            "style": "{",
        },
    },
    "handlers": {
        <-- 省略 -->
        "syslog": {
            "level": "INFO",
            "class": "logging.handlers.SysLogHandler",
            "formatter": "simple",
            "facility": "local1",
            "address": ("<-- IP address -->", 514),
        },
    },
    "loggers": {
        <-- 省略 -->
        "syslog": {
            "handlers": ["syslog"],
            "level": "INFO", # 
            "propagate": False, # 上位のロガーに伝達しない
        },
    },
}

使う場合は、以下のようにする。

import logging

syslog_logger = logging.getLogger("syslog")

syslog_logger.info("info-message")

まとめ

簡単にログ出力が実装できて、とても便利であった。実際には、本番環境では、DEBUGをFALSEにする必要があったりするため、settingsのファイルを分割するなどした方が運用としてはいいのではないかと思っている。

それでは、ステキな開発ライフを。