🚨

Pythonのloggingを使ってエラーログをテキストファイルに出力してみた。

4 min read 8

最初に

普段は print() でデバッグを済ましており、それだけだと対応仕切れない場面が出てきたのでloggingの使い方を学ぼうと思う。例:Pythonスクリプトをmacで実行出来る形式にした際にコンソールに出力されなくなり、必要になった。

ログレベル

Pythonのロギングには5つのレベルがある。デフォルトは値は warning になっており、 critical, error, warning までが出力される。

info, debug はコンソールに出力されない

import logging

logging.critical('critical')
logging.error('error')
logging.warning('warning')
logging.info('info')
logging.debug('debug')

#実行結果
CRITICAL:root:critical
ERROR:root:error
WARNING:root:warning

info, debug も出力したい場合は basicConfig でログレベルを下に変更する。

import logging

# ログレベルを DEBUG に変更
logging.basicConfig(level=logging.DEBUG)

logging.critical('critical')
logging.error('error')
logging.warning('warning')
logging.info('info')
logging.debug('debug')

#実行結果
CRITICAL:root:critical
ERROR:root:error
WARNING:root:warning
INFO:root:info
DEBUG:root:debug

ログのフォーマットを変更する。

個人的にはログが出てこればそれで十分なのだが一応見ていこうと思う。

import logging

# ログレベルを DEBUG に変更
logging.basicConfig(level=logging.DEBUG)

# 従来の出力
logging.info('error{}'.format('outputting error'))
logging.info('warning %s %s' % ('was', 'outputted'))
# logging のみの書き方
logging.info('info %s %s', 'test', 'test')

まぁテンプレートリテラルみたいなの使えますよってだけだった。

ログをファイルに出力

これを今回行いたかった。コンソールに出力されないのでログをファイルに書き出す。

import logging

# ログレベルを DEBUG に変更
logging.basicConfig(filename='logfile/logger.log', level=logging.DEBUG)

filenameにパスを指定してあげれば生成されるみたいだ。フォルダは生成されないのであらかじめ作って置かないとパスエラーになる。

ロガーの設定

import logging

# ロギングの基本設定(infoレベルを指定)
logging.basicConfig(level=logging.INFO)
logging.info('info')

# 現在のロギングの情報を取得(引数はファイル名)
logger = logging.getLogger(__name__)
# ロギングの設定を DEBUG に変更
logger.setLevel(logging.DEBUG)
logger.debug('debug')

#実行結果
INFO:root:info
DEBUG:__main__:debug

__name__ を引数にする事で現在実行中のPythonファイル名を取得している。それはわかるんだけどいまいち上記のコードではただ debug という文字列が出力されるだけでデバッグの意味をなしていない気がする。 logging.info('文字列') , logging.debug('文字列') とする事でそのレベルでログの出力が許可されていたら文字列を出力するくらいの機能にしかなってない。

どんなエラーなのかまでは分からない。そもそもエラー出なくても出力されているから上記だけでは意味ないよな。

ロギング ハンドラとは

ハンドラ:特定の処理に対して、処理を行うプログラムを指す。

例をあげるとtry except構文もエラーの発生に対して処理を実行するからハンドラーに該当する。

main処理に記述するbasiConfigのfilenameにログのパスを記述すればログが書き込まれる事については上記に記してある。

main処理以外のログ情報をファイルに出力する為には、ハンドラを使う必要がある。

logging.FileHandler('logfile/logger.log') を使用する事でコンソールではなくファイルにログを残せる。

上記で先に紹介した logging.basicConfig(filename='logfile/logger.log', level=logging.DEBUG) との違いは上記はデフォルトのロギング設定を変更するものであり、 FileHandler はまず、 logging.getLogger(__name__) でloggerと呼ばれる物を生成してそれの設定を変更してるイメージなのかもしれない。一段階壁を置くみたいな感じどんな意味があるかよく分からない。別の設定でロガーを生成したり汎用性が高くなるのかもしれない。

import logging

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

##ハンドラ取得
get_handler = logging.FileHandler('logfile/logger.log')
logger.addHandler(get_handler)

def do_something():
    logger.info('from logger_lesson2')
    logger.info('from logger_lesson2_debug')

これをデバックしたいスクリプトに書いてmain.py 等 のメイン処理にimportして実行する。ことで個別にデバックレベル等設定出来る。

上記の情報を元にオリジナルのロガー関数を作成してみた。

import logging

def get_logger(logger_name, log_file, f_fmt='%(message)s'):

	"""ロガーを取得"""
	# ロガー作成
	logger = logging.getLogger(logger_name)
	logger.setLevel(logging.DEBUG)

	# ファイルハンドラ作成
	file_handler = logging.FileHandler(log_file, mode='a', encoding='utf-8')
	file_handler.setLevel(logging.DEBUG)
	file_handler.setFormatter(logging.Formatter(f_fmt))

	# ロガーに追加
	logger.addHandler(file_handler)

	return logger

この関数を使用したいスクリプトで import して

import sys
# 同じ階層のmodulesフォルダにcreatelog.pyで保存している。
from modules import createlog

lg = createlog.get_logger(__name__, 'log.txt')
lg.debug('ロギング 開始')

try:
	#デバックしたい処理
except:
	lg.exception(sys.exc_info())#エラーをlog.txtに書き込む

実行するとスクリプトを実行した階層にlog.txtが生成される。

終わりに

結局、実行形式にしたアプリに導入したが、ファイルを生成する事が出来ない(バグか仕様?)ので機能しなかった。普通のPythonスクリプトではもちろん使えるので今後、使用する機会があれば積極的に使っていきたい。
一応Githubにもコードを残しておいた。
Pylogger

記事に関するコメント等は

🕊:Twitter
📺:Youtube
📸:Instagram
👨🏻‍💻:Github
😥:Stackoverflow

でも受け付けています。どこかにはいます。

参照

Pythonでロギングを学ぼう - Qiita

Pythonでログを出力するコード例【logging】

Discussion

実行形式にしたアプリに導入したが、ファイルを生成する事が出来ない(バグか仕様?)ので機能しなかった。

僕も以前似たような状態になったことがあるのでお役に立てるかはわかりませんがコメントします。

もしPyInstallerを利用して実行形式にしたのであれば以下の記事が参考になるかもしれません!

https://stackoverflow.com/questions/43784468/pyinstaller-exe-file-doesnt-create-log-file

おそらく今回の問題はログ・ファイルが生成されていないことではなく、生成されているが投稿主さんの意図したパスに生成できていないことだと思います。

試しに、絶対パスでログ・ファイル名を指定してみてはいかがでしょうか?

コメントと参考記事までありがとうございます。

使用しているのはcx_Freezeと呼ばれるライブラリを使用してビルドしました。
Pyinstallerよりもビルドしたファイルの起動が早いと聞いたのでこちらを使用しています。

パスの件について
絶対パスで指定したのですが、変わらないので問題はパスではないと考えています。
ためしに open() でダミー のファイルを生成するコードを処理の一番はじめに記述したらアプリが起動しなくなったのでおそらくファイル生成でエラーが起きていると思います。

転職活動用・自分が欲しかった用でポートフォリオで作成したのですが、肝心なビルドが上手く行かず2週間ほどこの問題で躓いていますw

ためしに open() でダミー のファイルを生成するコードを処理の一番はじめに記述したらアプリが起動しなくなったのでおそらくファイル生成でエラーが起きていると思います。

なるほど、、
その時のエラーなどは吐き出されていないのでしょうか?差し支えなければコードを見ても良いですか??もしかしたら問題の原因を特定できるかもしれません!

現状の情報だけでは僕では何が原因かわからないです、、

ビルドしたアプリだとコンソールが立ち上がらないのでエラーも表示されず、ただアプリが起動しないといった状況です。
コードは下記のGitHub にあげてあります。setup.pyでそのままビルドも可能です。
よろしくお願いします。

https://github.com/wimpykid719/YT-Download

open() でダミー

これに関してはmodeが指定されておらず、デフォルトの'r'が適用されているからですね。

mode='w'とかで指定すればエラーにならないはずです。

返信ありがとうございます。
書き換えて下記のようにしたのですが、結果は変わらなかったです。
すいません。

path = 'test.txt'
f = open(path, mode='w')
f.write('test')
f.close()

なるほど、、

一旦話を区切って、こちらのスクラップでやりとりしませんか?
現状の話題はこの記事の本題とはずれていて、かつ長引く可能性があるのでスクラップに移動できればと思います。

それと、どう上手くいかないかをできるだけ正確に把握するために以下をスクラップに書き込んでもらいたいです(最初からこうすべきでした)。

  • 実行環境のOS
  • Python version
  • ビルド手順(実行形式化手順)
  • 失敗したときのアプリケーション実行手順
ログインするとコメントできます