Yuta NakataのBlog

Python / AWS / ITについて役立つ情報を発信します

【Python】print()を卒業してloggerを始める

この記事の対象者

  • Pythonでlogを書き始めたい人
  • loggerについて学びたい人
  • print()をログ代わりに使っている人

なぜlogを書くのか?

理由は色々あると思いますが、個人的にprintではなくlogが必要な理由は下記があると考えます。

  • print()では、いつ起きたかがわからない
  • print()では、表示したい場合と表示したくない場合、全てのコメントをコメントアウトする必要がある
  • logを使えば、どこで・なんのエラーが起きたか把握することができる

loggerの始め方

はじめに、logレベルについて学びましょう。

logには5つの段階があります。

DEBUG 開発中に、原因を突き止めやすくするために、詳細な情報を出力したいときに利用
INFO 運用中に、イベントの発生情報 (または、想定通りの動作情報) を出すときに利用
WARNING 今は問題ではないけど、今後の運用で注意が必要な情報出力として利用
ERROR 機能実行でエラーが発生した際に利用
CRITICAL 大問題が発生し、アプリ動作がおかしいときに利用

開発中にはdebug、運用中であれば局面局面でのお知らせにInfo等がよく使われるかと思います。

続いて、loggerの設定の仕方について学びましょう。

以下のコードをコピペすれば設定は完了です。

from logging import getLogger, basicConfig, INFO


logger = getLogger(__name__)
basicConfig(
    filename='log.log',
    filemode='w',
    level=INFO,
    format='{asctime} {name}:{lineno} [{levelname}]: {message}',
    style='{'
)

各、設定について解説します。

from logging import getLogger, basicConfig, INFO


logger = getLogger(__name__)
basicConfig(
    filename='log.log', # log.logというファイル名で書き出します
    filemode='w', # defaultは、'a'です。'w'にすると、実行の度に新たにファイルが作成されます。デフォルトでは上書きになります。
    level=INFO, # どのレベルからlogに表示させるかを設定します。デフォルトでは、WARNINGです。
    format='{asctime} {name}:{lineno} [{levelname}]: {message}', # 書き出すformatの指定です。詳細は下記にまとめます。
    style='{' # フォーマットのstyleを指定します。デフォルトは、"%"です。
)

書き出すフォーマットは下記のような候補があります。

{asctime}  # 生成時間。YYYY-MM-DD HH:MM:SS,UUU 形式。datefmtでフォーマット変更可能
{created}       # 生成時間。time.time(}が返却する形式
{msecs}     # 生成時間のミリ秒部
{relativeCreated}   # logginモジュールが読み込まれてからの経過時間(ミリ秒}
{levelname}     # レベル名(DEBUG, INFO, WARNING, ERROR, CRITICAL}
{levelno}       # レベル番号。DEBUGは10, INFOは20など
{module}        # モジュール名
{pathname}      # パス名
{filename}      # ファイル名
{funcName}      # 関数名
{lineno}        # 行番号
{message}       # ログメッセージ
{name}      # ロガー名
{process}       # プロセスID
{processName}       # プロセス名
{thread}        # スレッドID
{threadName}        # スレッド名

サンプルコード

上記の設定を行うと、サンプルコードは以下のようになります。

from logging import getLogger, basicConfig, INFO


logger = getLogger(__name__)
basicConfig(
    filename='log.log',
    filemode='w',
    level=INFO,
    format='{asctime} {name}:{lineno} [{levelname}]: {message}',
    style='{'
)

logger.debug('debug log')
logger.info('info log')
logger.warning('warning log')
logger.error('error log')
logger.critical('critical log')

log.logは下記のものが書き出されます。

2024-09-20 22:25:08,256 __main__:14 [INFO]: info log
2024-09-20 22:25:08,256 __main__:15 [WARNING]: warning log
2024-09-20 22:25:08,256 __main__:16 [ERROR]: error log
2024-09-20 22:25:08,256 __main__:17 [CRITICAL]: critical log

ケーススタディ

実際にありそうなプログラムを作ってみます。

売上データを読んで、書き出すプログラム

を作ります。

ファイル構成は、

.
├── input.csv : 読み込み用CSV
├── log.log : ログファイル
├── main.py : 処理用プログラム
├── output.csv : 書き出し用CSV
└── utils.py : logging設定プログラム

とします。

各ファイルの中身は、

# input.csv
product,price,amount
apple,100,3
orange,80,5
banana,120,2
# utils.py
from logging import getLogger, basicConfig, INFO


logger = getLogger(__name__)

basicConfig(
    filename='log.log',
    filemode='w',
    level=INFO,
    format='{asctime} {name}:{lineno} [{levelname}]: {message}',
    style='{'
)
import pandas as pd

from utils import logger

INPUT_FILE = "input.csv"
OUTPUT_FILE = "output.csv"

def main():
    # input
    df = pd.read_csv(INPUT_FILE)
    logger.info('read csv : %s', INPUT_FILE)

    # process
    df['sales'] = df['price'] * df['amount']

    # output
    df.to_csv(OUTPUT_FILE, index=False)
    logger.info("output : %s", OUTPUT_FILE)


if __name__ == "__main__":
    main()

Githubにもサンプルコードを上げておきます!

https://github.com/A-nkt/tech-blog-snippets/tree/main/src/python/logging

これで、いつ・何行目で・どのようなLogが出ているか把握することができるようになりましたね!