logging 設定ファイル

ここまでログ出力の設定を直接コードに記述したサンプルを見てきましたが、実運用上はログの設定は設定ファイルを使用することも多いです。このページではログの設定ファイルについて解説します。

ini形式による設定

ログの設定使用できる設定ファイルにはyaml形式(というか辞書形式)とini形式があります。まずはini形式について説明します。

基本的な設定

設定ファイルには、フォーマッタ、ハンドラ、ロガーの設定を記述しますが、考え方としてはコードに直接記述した時と同様、ロガーに対し、複数のハンドラーを設定し、それぞれのハンドラーには1つのフォーマッタを設定するだけです。まずはrootロガーを設定するサンプルを見てみましょう。

;logconf.ini


[loggers]
keys=root

[handlers]
keys=h1

[formatters]
keys=fmt1

; 以下、フォーマッター、ハンドラー、ロガーの個別設定

; フォーマッター
[formatter_fmt1]
format=%(asctime)s %(name)s %(levelname)s %(message)s
class=logging.Formatter

; ハンドラー
[handler_h1]
class=StreamHandler
level=DEBUG
formatter=fmt1
args=(sys.stdout,)

;ロガー
[logger_root]
level=NOTSET
handlers=h1

使用する側のPythonファイルは以下のようになります。

import logging.config

logging.config.fileConfig('logconf.ini')
logger = logging.getLogger(__name__)
logger.error('エラーが発生しました')

h1というハンドラーをrootロガーに設定しています。また、fmt1というフォーマッターをh1に設定しています。なお、上のサンプルはあくまでも説明用なので、実運用では働きがわかるような名前を設定したほうが良いでしょう。iniファイルのセクションは以下から構成されます。

セクション名 内容
loggers 使用するロガーの名前をカンマ区切りで列挙
handlers 使用するハンドラーの名前をカンマ区切りで列挙
formatters 使用するフォーマッターの名前をカンマ区切りで列挙
logger_ロガー名 ロガーの個別設定。rootロガー以外はqualnameにてロガー名を設定。
handler_ハンドラー名 ハンドラーの個別設定。
classで使用するハンドラーのクラスを指定。
argsでハンドラー生成時の引数を指定。
formatter_フォーマッター名 フォーマッターの個別設定

複数のロガーを設定

では、もうひとつサンプルです。今度は複数のロガーを設定して、標準出力とファイルの両方にログを出力してみます。対象とするロガーの名前はsampleとしましょう。

;logconf.ini


[loggers]
keys=root, sample

[handlers]
keys=h1, h2

[formatters]
keys=fmt1, fmt2

; 以下、フォーマッター、ハンドラー、ロガーの個別設定

; フォーマッター
[formatter_fmt1]
format=%(asctime)s %(name)s %(levelname)s %(message)s [fmt1]
class=logging.Formatter

[formatter_fmt2]
format=%(asctime)s %(name)s %(levelname)s %(message)s [fmt2]
class=logging.Formatter


; ハンドラー
[handler_h1]
; 標準出力
class=StreamHandler
level=DEBUG
formatter=fmt1
args=(sys.stdout,)

[handler_h2]
; ファイル出力
class=FileHandler
level=DEBUG
formatter=fmt2
args=('sample.log',)

;ロガー
[logger_root]
level=NOTSET
handlers=h1

[logger_sample]
level=NOTSET
handlers=h2
qualname=sample

import logging.config
                                                                                                          
logging.config.fileConfig('logconf.ini')

logger = logging.getLogger('sample')
logger.error('エラーが発生しました')

上のコードを実行してみると、sampleロガーにはrootロガーで設定したログが標準出力に、sampleロガーで設定したログがファイルに、と2種類のログが出力されることが確認できます。

YAML形式による設定

前述の通りYAMLでも設定の記述が可能です。最低限設定する必要がある項目は以下のとおりとなります。ini形式との違いは、フォーマッター、ハンドラー、ロガーを別途列挙する必要が無いこと、rootロガーの設定が必須であることが挙げられます。

項目 説明
version スキーマのバージョンを表す整数値に設定されます。現在有効な値は1だけです。
formatters フォーマッターの設定を記述します。
handlers ハンドラーの設定を記述します。
loggers ロガーの設定を記述します。
root rootロガーの設定を記述します。

YAML形式で使用するにはYAMLパーサが必要となります。以下でインストールしましょう。

pip install PyYAML

ではサンプルです。

# logconf.yml
version: 1

formatters:
  fmt1:
    format: '%(asctime)s %(name)s %(levelname)s %(message)s [fmt1]'
    
handlers:
  h1:
    class: logging.StreamHandler    
    level: DEBUG
    formatter: fmt1 
    stream: ext://sys.stdout
  h2: 
    class: logging.FileHandler
    level: DEBUG
    formatter: fmt1
    filename: sample.log

loggers:     
  sample:
    handlers: [h2]
    level: DEBUG
    qualname: console
    propagate: no

root:
  level: DEBUG
  handlers: [h1]

使用する側のPythonファイルは以下のようになります。

import yaml
from logging import config,getLogger
config.dictConfig(yaml.load(open("logconf.yml", encoding='UTF-8').read()))
logger = getLogger(__name__)
logger.error("エラーが発生しました")

yaml.loadでyaml形式を辞書型に変換して、config.dictConfigに読み込ませます。上のコードでは、h1というハンドラーをrootロガーに、h2というハンドラーをsampleという名前のロガーに設定しています。また、fmt1というフォーマッターをh1、h2に設定しています。なお、上のサンプルはあくまでも説明用なので、実運用では働きがわかるような名前を設定したほうが良いでしょう。なお、ロガーに複数のハンドラーを指定したい場合は、以下のようにカンマ区切りで列挙します。

handlers: [h1, h2]

JSON形式による設定

dictConfigで指定しているのは辞書型なので、実はYAML形式以外であっても辞書型であれば何でも使用可能です。あまり設定ファイルでJSONは使用しないかもしれませんが、参考として上のコードをJSONに書きなおしたものも掲載します。

{
  "version": 1,
  "formatters": {
    "fmt1": {
      "format": "%(asctime)s %(name)s %(levelname)s %(message)s [fmt1]"
    }   
  },  
  "handlers": {
    "h1": {
      "class": "logging.StreamHandler",
      "level": "DEBUG",
      "formatter": "fmt1",
      "stream": "ext://sys.stdout"
    },
    "h2": {
      "class": "logging.StreamHandler",
      "level": "DEBUG",
      "formatter": "fmt1",
      "stream": "ext://sys.stdout"
    }
  },  
  "loggers": {
    "console": {
      "handlers": [
        "h2"
      ],  
      "level": "DEBUG",
      "qualname": "sample",
      "propagate": "no"
    }   
  },  
  "root": {
    "level": "DEBUG",
    "handlers": [
      "h1"
    ]   
  }
}
from logging import config,getLogger
import json

config.dictConfig(json.loads(open("logconf.json", encoding='UTF-8').read()))
logger = getLogger(__name__)
logger.error("エラーが発生しました")