背景
外部APIを他チームに公開する必要があり(しょうきb)、pythonのlogging機能をAIに相談しながら実装してみた
ディレクトリ構造
my_library/
init.py(パッケージ認識させるためだけの空ファイル)
main.py
math_operations.py
app.py
学んだポイント
- ライブラリではloggerを使用することで、rootのロガーを汚さないようにする
⇒ 可読性: ログメッセージから、どのモジュールで問題が発生したかを簡単に特定可能 - NullHandler の使用: 必ず NullHandler を追加して、デフォルトのログ設定がない場合にエラーが発生しないようにする
⇒ ライブラリのコードを変更せずに、ログ設定を変更可能 - アプリケーション側の設定: ログ出力の設定はアプリケーション側に委ね、ライブラリ側では行わない
⇒ 柔軟性: アプリケーション開発者は、自分の要件に合わせてログ出力を自由に設定可能 - ハンドラによってログレベルを変更可能。コンソール出力のハンドラにはエラーレベルだけ出すなど
- ルートロガーはすべてのロガーの親となるので、ライブラリのロガーはルートロガーへの設定が自動的に反映される
- アプリ側でlogger = logging.getLogger(name)して、ハンドラなど設定した場合は、アプリ側のロガーと、ライブラリーのロガーは異なる階層に位置するため、設定が引き継がれない
- パッケージ内のロガーは親子関係となるので設定が引き継がれる
実コード
# app.py
import logging
import my_library.main # ライブラリをインポート
# ロガーの設定
logger = logging.getLogger('') # ルートロガーを取得
logger.setLevel(logging.DEBUG) # ログレベルを設定
# ハンドラーの作成(コンソールに出力)
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG) #ハンドラのログレベルの設定
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
# ファイルハンドラを作成し、ログをファイルに出力する
file_handler = logging.FileHandler("my_app.log")
file_handler.setLevel(logging.DEBUG) #ハンドラのログレベルの設定
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
logger = logging.getLogger(__name__)
# 計算の実行
logger.info("Starting calculation")
result1 = my_library.main.calculate("add", 5, 3)
print(f"Result of addition: {result1}")
result2 = my_library.main.calculate("subtract", 10, 4)
print(f"Result of subtraction: {result2}")
result3 = my_library.main.calculate("multiply", 2, 6) #無効なoperation
print(f"Result of multiply: {result3}")
# my_library/main.py
import logging
from . import math_operations # 相対インポート
logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())
# 外部関数として公開
def calculate(operation, x, y):
"""指定された演算を実行する"""
logger.debug(f"Performing operation: {operation}")
if operation == "add":
result = math_operations.add(x, y)
elif operation == "subtract":
result = math_operations.subtract(x, y)
else:
logger.error(f"Invalid operation: {operation}")
return None
logger.info(f"Calculation completed. Result: {result}")
return result
# my_library/math_operations.py
import logging
logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())
def add(x, y):
"""2つの数を足し合わせる"""
logger.debug(f"Adding {x} and {y}")
result = x + y
logger.info(f"Result of {x} + {y} is {result}")
return result
def subtract(x, y):
"""2つの数を引き算する"""
logger.debug(f"Subtracting {y} from {x}")
result = x - y
logger.info(f"Result of {x} - {y} is {result}")
return result