こんにちは、はけです。

普段はWEBアプリのシステムエンジニアをしています。

  • Pythonでログを出力したいけどどうやってコーディングすればいいかわからない!
  • ログの出力ってどうすれば簡単に実装できるの?

Pythonを使用していれば必ずどこかでぶつかる疑問だと思います。

わたしもPython初心者の頃は、最初に誰かが作ってくれたログ出力クラスを
あまり深く考えずに使うだけで「Python標準のログ出力の基礎」をあまり知りませんでした。

ですが新規のシステムを立ち上げないといけないとなると、ログ出力のような共通部分も自分で考えて実装しなければならず、、、
あれこれ調べて苦労しながらログ出力クラスを作った経験があります。

本記事では、Pythonのログ出力方法を、基本から、設定ファイルでログ出力設定をするなどの応用まで紹介していきます。

わかりやすくまとめていますのでみなさんも実際にコードを実行しながら試してみてください!

Pythonログ出力の基本を解説!

ではさっそくPythonのログ出力について解説します。

実行環境

  • Windows10
  • Python3.7.6
  • PyYAML5.3.1

基本的なPythonログ出力を試してみる!

まずは基本的なPythonログ出力を行います。

次のコードになります。

# loggingライブラリをインストールする。
import logging

# ログ出力インスタンスを作成する。引数にログ出力名称を設定する。
logger = logging.getLogger('Logging')

# ログを標準出力する。
sh = logging.StreamHandler()
logger.addHandler(sh)

# log関数でログ出力処理。デフォルトはログレベルinfoは出力しない。
logger.info('info')
logger.warning('warning')
logger.error('error')

まずざっくり用語を解説しておくと、次のようになります。

  • ロガー(logger)=ログを出力するやつ
  • ハンドラー(handler)=ログの出力方法(標準出力/ファイル/メールなど)を決めるやつ

それぞれのコードは次のようなことをしています。

  • 「logging.getLogger」でロガーを取得します。
  • 引数(‘Logging’)でロガーの名前を設定しています。ここで名前を設定することで後で名前を指定してロガーを取得することができます。
  • 「logging.StreamHandler()」このStreamHandlerが標準出力を行うクラスです。このクラスを使用する=標準出力にログを出力することになります。

これらのコードを実際に動かしてみてください。標準出力にログが出力できます。

warning
error

このように基本的なログ出力は簡単にできてしまいます。

ログをファイルに出力してみる!

標準出力できるようになると、やっぱりファイルに出力したくなると思います。

ファイルの出力方法は次の部分を変えるだけです。

# loggingライブラリをインストールする。
import logging

# ログ出力インスタンスを作成する。引数にログ出力名称を設定する。
logger = logging.getLogger('Logging')

# ログを標準出力する。
sh = logging.StreamHandler()
logger.addHandler(sh)

# ログをファイル出力する。★
# (エンコードを指定しなければ、環境依存のエンコード指定となる)
fl = logging.FileHandler('test.log', encoding='utf-8')
logger.addHandler(fl)

# log関数でログ出力処理。デフォルトはログレベルinfoは出力しない。
logger.info('info')
logger.warning('日本語') # ★
logger.warning('warning')
logger.error('error')

★マークの部分が変わった部分です。

これを動かしてみると、実行ファイルと同じ場所にログファイルができていると思います。

日本語
warning
error

このようにハンドラーを変えるだけでログの出力方法を自在に変えることができます

ファイル出力の際は、サンプルのようにencodingを指定するようにしてください。指定しないと日本語のログ出力をした際に文字化けしてしまうことがあります。

特に指定しないと環境のデフォルト設定になります。(WindowsならSJIS)

システムによってencodingの指定は変わるので必ず指定するようにしましょう。

ログのフォーマットを設定してみる!

次にログのフォーマットを指定していきましょう!

特に必要なのは日付とユーザやプロセスIDだと思います。

サンプルコードでは日付/プロセスID/関数名/ログメッセージを出すようにしています。

# loggingライブラリをインストールする。
import logging

# ログ出力インスタンスを作成する。引数にログ出力名称を設定する。
logger = logging.getLogger('Logging')

# ログを標準出力する。
sh = logging.StreamHandler()
logger.addHandler(sh)

# ログをファイル出力する。(エンコードを指定しなければ、環境依存のエンコード指定となる)
fl = logging.FileHandler('test.log', encoding='utf-8')
logger.addHandler(fl)

# フォーマットを行う。★
debugformatter = logging.Formatter("[%(asctime)s] [%(filename)s] [%(funcName)s] [%(process)d] [%(name)s] %(message)s")
sh.setFormatter(debugformatter)
normalformatter = logging.Formatter("[%(asctime)s] [%(process)d] [%(name)s] %(message)s")
fl.setFormatter(normalformatter)

# log関数でログ出力処理。デフォルトはログレベルinfoでdebugログは出力しない。
logger.info('info')
logger.warning('warning')
logger.error('error')

ログのフォーマットが変更できます。

[2021-01-27 08:36:21,460] [test.py] [<module>] [14424] [Logging] warning
[2021-01-27 08:36:21,461] [test.py] [<module>] [14424] [Logging] error

他にも次のようなフォーマットが指定できるので参考にしてください。

フォーマット役割
%(asctime)s実行時刻
%(filename)sファイル名
%(funcName)sログ呼び出しを
含む関数名
%(levelname)sログレベル名
%(lineno)d行番号
%(message)sログメッセージ
%(module)sモジュール名
%(name)sロガー名
%(process)dプロセスID
%(thread)dスレッドID

ログレベルを設定してみる!

ログ出力基礎の最後としてログレベルの設定をしましょう。

本番環境ではたいていINFO以上のログ出力、開発環境ではDEBUG以上のログ出力を行います。

# loggingライブラリをインストールする。
import logging

# ログ出力インスタンスを作成する。引数にログ出力名称を設定する。
logger = logging.getLogger('Logging')

# ログレベルの設定を行う。★
logger.setLevel(logging.DEBUG)

# ログを標準出力する。
sh = logging.StreamHandler()
logger.addHandler(sh)

# ログをファイル出力する。(エンコードを指定しなければ、環境依存のエンコード指定となる)
fl = logging.FileHandler('test.log', encoding='utf-8')
logger.addHandler(fl)

ff = logging.Handler
# フォーマットを行う。
debugformatter = logging.Formatter("[%(asctime)s] [%(filename)s] [%(funcName)s] [%(process)d] [%(name)s] %(message)s")
sh.setFormatter(debugformatter)
normalformatter = logging.Formatter("[%(asctime)s] [%(process)d] [%(name)s] %(message)s")
fl.setFormatter(normalformatter)

# log関数でログ出力処理。デフォルトはログレベルinfoは出力しない。
logger.debug('debug') # ★
logger.info('info')
logger.warning('warning')
logger.error('error')
logger.fatal('fatal')

注意点としては指定したログレベルが含まれることです。

DEBUGを指定した場合、DEBUGログも出力されます。

[2021-01-27 08:37:55,953] [test.py] [<module>] [15664] [Logging] debug
[2021-01-27 08:37:55,954] [test.py] [<module>] [15664] [Logging] info
[2021-01-27 08:37:55,954] [test.py] [<module>] [15664] [Logging] warning
[2021-01-27 08:37:55,954] [test.py] [<module>] [15664] [Logging] error
[2021-01-27 08:37:55,954] [test.py] [<module>] [15664] [Logging] fatal

Pythonログ出力を設定ファイルで行う!

ではここから応用で少し難易度が上がります。

もっと簡単にログの出力方法を知りたいという方だけ次に進んでください。

すでに何度かワードが出てきているので気づいている方も多いと思いますが、これまでPythonコードで実装してきたログ出力を設定ファイルでできてしまいます。

最初からそれを教えてくれよ!と思ったかたすみません。

ただ、これまでやってきた内容が理解できていないとわかりづらい内容となります。

設定ファイルでやる方法はyaml、config、jsonなどいくつかありますが今回はyamlでの方法を紹介します。

configの場合ハンドラーの設定が少ししづらいので、yamlかjsonを使用するのをおすすめします。

手順は次の3STEPになります。

  • PyYAMLをインストールする。
  • yamlファイルを作成する。
  • pythonファイルを作成する。

PyYAMLをインストールする

yamlファイルを読み込むために外部ライブラリのPyYAMLをインストールする必要があるのでインストールしておきます。

コマンドラインなどで実行し、PyYAMLをインストールしてください。

pip install PyYAML

yamlファイルを作成する

次のファイルを作成します。内容はこれまでのログ出力の設定と同じになります。

設定ファイルに書いている内容はここまで見た方は想像がつくと思いますので割愛します。

version: 1
formatters:
    default:
        format : '[%(asctime)s] [%(filename)s] [%(funcName)s] [%(process)d] [%(name)s] %(message)s'
handlers:
    file:
        class : logging.FileHandler
        level : DEBUG
        formatter : default
        filename : 'test.log'
        encoding : 'utf-8'
root:
    level: DEBUG
    handlers: [ file ]

pythonファイルを作成する

次に、yamlファイルを読み込み、ログ出力設定を適用するpythonファイルを作成します。

import logging.config
import yaml # ★

# ログ設定ファイルからログ設定を読み込む。★
logging.config.dictConfig(yaml.load(open('logging.yaml').read(), Loader=yaml.SafeLoader))
# logging.config.fileConfig('logging.conf')

# ログ設定ファイルからログ設定を読み込み
logger = logging.getLogger()

# log関数でログ出力処理。デフォルトはログレベルinfoは出力しない。
logger.debug('debug')
logger.info('info')
logger.warning('warning')
logger.error('error')
logger.fatal('fatal')

これを実行すると、ログ出力が行えます。

[2021-01-27 08:54:07,425] [outlog_5.py] [<module>] [9328] [root] debug
[2021-01-27 08:54:07,426] [outlog_5.py] [<module>] [9328] [root] info
[2021-01-27 08:54:07,426] [outlog_5.py] [<module>] [9328] [root] warning
[2021-01-27 08:54:07,426] [outlog_5.py] [<module>] [9328] [root] error
[2021-01-27 08:54:07,426] [outlog_5.py] [<module>] [9328] [root] fatal

Pythonログ出力をログローテートしてみる!

最後にログローテートをしてくれる便利なハンドラを紹介します。

ログローテートは、ログファイルが肥大化してしまうのを防ぐ方法で1日や1週間毎にファイル名を変更し、一定期間過ぎたログファイルは自動で削除してくれるものです。

これも実際のシステムを作る際には必ず必要になってくるものです。

そんな便利な機能をしてくれるハンドラが「TimedRotatingFileHandler」です。

このハンドラを使うだけで簡単にログローテートを実施してくれます。

次のようにyamlファイルを少し書き換えます。

version: 1
formatters:
    default:
        format : '[%(asctime)s] [%(filename)s] [%(funcName)s] [%(process)d] [%(name)s] %(message)s'
handlers:
    file:
        class : logging.handlers.TimedRotatingFileHandler
        level : DEBUG
        formatter : default
        filename : 'test.log'
        backupCount : 3
        when : 'M'
        encoding : 'utf-8'
root:
    level: DEBUG
    handlers: [ file ]

変更したのは次になります。

  • handlers:file:class:logging.handlers.TimedRotatingFileHandler、使用するハンドラを変更します。
  • handlers:file:backupCount:3、保持するファイル数を指定します。3ファイル分の過去ファイルを保持します。
  • handlers:file:when:M、どのタイミングでファイル名を切り替えるかを指定します。Mは分なので1分毎。S(秒)、H(時間)、D(日)なども指定できます。

これを実行するとわかりますが、3分ごとに新しいファイルが作られます。

このハンドラの他にもたくさん便利なハンドラが用意されているので、興味のある方は公式サイトを見てみてください。

まとめ

最後まで読んでいただきありがとうございます。
Pythonのログ出力について少しでも理解が深まったでしょうか。

本記事では次のことを紹介しました。

  • Pythonの基礎的なログ出力
    • ログの標準出力
    • ログのファイル出力
    • ログフォーマットの設定
    • ログレベルの設定
  • 設定ファイル(yaml)でログ出力を行う方法
  • ログローテートを行う方法

以上、これからもWEB開発でつまずいたことや役に立つ情報を配信していきます!