🏋️‍♀️

aws lambda(python)に最適なlogging classの作成

2023/09/17に公開

概要・背景

aws lambda(python)で関数をいくつか作成しています。
毎度、関数の作成時にloggingの処理を入れるのが面倒でclass化したい思いが膨らんできました。
でもaws lambdaはcloud watch logsへのログ出力があるためfile出力はできません。
webでの参考になるサイトなどから学習し個人的にいい感じの範囲で共通化&class化しました。

作成したクラス

Logging_Class.py
import logging

#コメントアウトのフォーマットは全項目出力版フォーマット
#LOG_FORMAT = '%(asctime)s - %(created)f - %(filename)s - %(funcName)s - %(levelname)s - %(levelno)s - %(lineno)d - %(message)s - %(module)s - %(msecs)d - %(name)s - %(pathname)s - %(process)d - %(processName)s - %(relativeCreated)d - %(thread)d - %(threadName)s'

#チェックした結果使えるフォーマット
LOG_FORMAT = '[%(levelname)s] %(asctime)s %(name)s %(message)s'

class Logger:
    def __init__(self,name,lvl):
        """コンストラクター
        Args:
            name (str): logging名
            lvl (str): 出力レベル
        """
        self.logger = logging.getLogger(name)
        self.logger.setLevel(lvl)
        self.defaultlevel = lvl
        self.logger.propagate = False
        if not self.logger.hasHandlers():
            sh = logging.StreamHandler()
            handler_format = logging.Formatter(LOG_FORMAT)
            sh.setFormatter(handler_format)
            self.logger.addHandler(sh)

    #infoメッセージ
    def info(self,message):
        self.logger.info(message)

    #exceptionメッセージ
    def exception(self,message):
        self.logger.exception(message)

    #warningメッセージ
    def warning(self,message):
        self.logger.warning(message)

    #debugメッセージ 
    def debug(self,message):
        self.logger.debug(message)

    #criticalメッセージ
    def critical(self,message):
        self.logger.critical(message)

このクラスを呼び出すlambdaをテストで作成

lambda_function.py
import LoggingClass as lc
import common_function as cm

lg = lc.Logger(__name__,'DEBUG')

def lambda_handler(event, context):
    
    lg.info('infoテストです')
    lg.debug('debugテストです')
    fnc()
    cm.fnc_a()
    
def fnc():
    lg.info('fnc呼び出し')
    

別ファイルの関数を呼び出した時の出力項目を確認するためにcommon_function.pyを作成。

common_function.py

import LoggingClass as lc
    
mylg = lc.Logger(__name__,'DEBUG')
def fnc_a():
    
    mylg.warning('fnc_aの呼び出し(warning)')
   

出力結果フォーマット

[INFO] 2023-09-17 12:43:22,126 lambda_function  infoテストです
[DEBUG] 2023-09-17 12:43:22,126 lambda_function  debugテストです
[INFO] 2023-09-17 12:43:22,127 lambda_function  fnc呼び出し
[WARNING] 2023-09-17 12:43:22,127 common_function  fnc_aの呼び出し(warning)

こだわりポイント

loggingで準備されているフォーマットはいくつもあるがlambdaで使える項目は限定的である。
ですので、出力項目を使える項目だけにし、フォーマットを作成しました。

テストのために使用した全項目出力版もコメントアウトしてあるのでご自身で内容を確認するときなどにお使いください。

Format 内容 テスト結果(sample値) 所感
%(asctime)s ログの出力時刻 2023-09-17 11:23:07,161 普通にlambdaで出力できたので使用できる値と判定
%(created)f ログ出力時刻(time.time()によって返される形式) 1694915993.637037 UNIX時間?普通には使えない。
%(filename)s pathname のファイル名部分。 LoggingClass.py 別ファイルの関数を呼び出しても部品化してしまったことで使えない値。
%(funcName)s ロギングの呼び出しを含む関数の名前。 info lambdaだとlog levelが出力されてしまい意味がない。
%(levelname)s メッセージのための文字のロギングレベル (‘DEBUG’, ‘INFO’, ‘WARNING’, ‘ERROR’, ‘CRITICAL’)。 INFO ロギングレベルはこれが一番使える。
%(levelno)s メッセージのための数値のロギングレベル (DEBUG, INFO, WARNING, ERROR, CRITICAL)。 20 上の項目のラベルが使用できるので不要。
%(lineno)d ロギングの呼び出しが発せられたソース行番号 (利用できる場合のみ) 24 class化してしまったことで共通classの業弁号が出力されてしまう。使えない。出てほしい!
%(message)s msg % args として求められた、ログメッセージ。 message内容 これはメインとなるメッセージなので必須です。
%(module)s モジュール (filename の名前部分)。 LoggingClass 共通class化してしまったことで使えない項目。
%(msecs)d LogRecord が生成された時刻のミリ秒部分 637 これだけでは何も使えないのでは?
%(name)s ロギングに使われたロガーの名前。 lambda_function getLogger呼び出し時に__name__を設定すれば有効と判断
%(pathname)s ロギングの呼び出しが発せられたファイルの完全なパス名 (利用できる場合のみ) /var/task/LoggingClass.py ロガー名を__name__にすれば不要。
%(process)d プロセス ID (利用可能な場合のみ)。 1 プロセス管理する場合は必要だが、通常処理では不要
%(processName)s プロセス名 (利用可能な場合のみ)。 MainProcess 上記理由より同様。
%(thread)d スレッド ID (利用可能な場合のみ)。 140220845348288 マルチスレッド処理を使う場合は必要かも
%(threadName)s スレッド名 (利用可能な場合のみ)。 MainThread マルチスレッド処理を使う場合は必要かも

参考url

https://blog.hiros-dot.net/?p=10328
https://resanaplaza.com/2021/10/23/【コピペok】pythonのログ出力クラスを作ってみました-by/

Discussion