【Python】multiprocessing.Pool でログを記録するコード例(プロセスごとに分ける)

Python

Python の multiprocessing.Pool でロギングするコード例を書きました。

プロセスごとにログファイルを分けて記録する Python コード例です。

multiprocessing.Pool で並列処理しているときに、logging でログを記録する方法です。

環境は Windows 10 Pro (64 bit) で、Python 3.8.6 (64 bit) を使用しました。

説明

おやプロセスのロガーとかくプロセスのロガーに、それぞれ独立した FileHandler を取り付けます。

これで、プロセスごとにログファイルを分けて記録することができました。

【Python】logging でログを記録する方法のドキュメントの場所(コメント付き)

メインモジュールをロギングする

メインモジュールを multiprocessing.Pool で並列実行するときのコード例です。

コード例

メインモジュールのコードです。

app_main.py

import logging, pathlib
import multiprocessing
logger = logging.getLogger(__name__) # NOTSET (0)
# 親プロセスの名前付きロガー または 各子プロセスの名前付きロガー

def main():
    """メイン関数です。"""
    root = logging.getLogger() # 親プロセスのルートロガー
    root.setLevel(logging.INFO) # WARNING (30) ⇒ INFO (20)

    sh = logging.StreamHandler() # NOTSET (0)
    sh.setFormatter(logging.Formatter( # %()-17s: 17 文字分のスペースで左寄せ。
        '%(processName)-17s %(name)-11s %(levelname)-8s '
        '%(module)s.py %(funcName)s(): %(message)s'))
    root.addHandler(sh)

    log_dir = pathlib.Path(r'F:\apps\log')
    log_txt = log_dir.joinpath(f'mp-fh-{__name__}.txt')
    fh = logging.FileHandler(log_txt, encoding='utf-8', mode='a') # NOTSET (0)
    fh.setFormatter(logging.Formatter(
        '%(processName)-17s %(name)-11s %(levelname)-8s '
        '%(module)s.py %(funcName)s(): %(message)s'))
    root.addHandler(fh)

    root.info('start')
    root.info(f'root.level: {logging.getLevelName(root.level)} ({root.level})')
    root.info(f'sh.level: {logging.getLevelName(sh.level)} ({sh.level})')
    root.info(f'fh.level: {logging.getLevelName(fh.level)} ({fh.level})')
    logger.debug('デバッグログ') # 今は出ません root: INFO (20)
    logger.info('インフォログ')
    logger.warning('ウォーニングログ')
    logger.error('エラーログ')
    logger.critical('クリティカルログ')

    logger.info('pool.map() を実行します。')
    max_processes = 2
    logger.info(f'プロセス数 max_processes: {max_processes}')
    logger.info('各子プロセスのログは個別にファイルに記録中...')
    args_list = [
        [100, 'あいうえお'],
        [200, 'かきくけこ'],
        [300, 'さしすせそ'],
        [400, 'たちつてと'],
        [500, 'なにぬねの'],
    ]
    with multiprocessing.Pool(
            processes=max_processes,
            initializer=jisaku_initializer, initargs=(log_dir,),
        ) as pool: # type(pool): <class 'multiprocessing.pool.Pool'>
        datas = pool.map(jisaku_func_wrapper, args_list)

    logger.info('pool.map() の結果を表示します。')
    for data in datas:
        logger.info(data)
    root.info('end')
    return

def jisaku_initializer(log_dir):
    """各子プロセスで最初に 1 回だけ実行される関数です。"""
    root = logging.getLogger() # 各子プロセスのルートロガー
    root.setLevel(logging.DEBUG) # WARNING (30) ⇒ DEBUG (10)
    cp = multiprocessing.current_process()
    # type(cp): <class 'multiprocessing.context.SpawnProcess'>
    log_txt = log_dir.joinpath(f'mp-fh-{__name__}-{cp.name}.txt')
    fh = logging.FileHandler(log_txt, encoding='utf-8', mode='a') # NOTSET (0)
    fh.setFormatter(logging.Formatter(
        '%(processName)-14s %(name)-11s %(levelname)-8s '
        '%(module)s.py %(funcName)s(): %(message)s'))
    root.addHandler(fh)
    root.info('(ファイルハンドラ追加完了)')
    root.info(f'root.level: {logging.getLevelName(root.level)} ({root.level})')
    root.info(f'logger.level: {logging.getLevelName(logger.level)} ({logger.level})')
    root.info(f'fh.level: {logging.getLevelName(fh.level)} ({fh.level})')
    return

def jisaku_func_wrapper(args):
    return jisaku_func(*args)

def jisaku_func(number, text):
    """自作関数です。"""
    logger.debug(f'デバッグログ {number} {text}')
    logger.info(f'インフォログ {number} {text}')
    logger.warning(f'ウォーニングログ {number} {text}')
    logger.error(f'エラーログ {number} {text}')
    logger.critical(f'クリティカルログ {number} {text}')
    return [number, text, 'OK']

if __name__ == '__main__':
    main()

実行結果 (processes=2)

メインモジュールprocesses=2 で実行しました。

実行結果の画面表示です。

MainProcess       root        INFO     app_main.py main(): start
MainProcess       root        INFO     app_main.py main(): root.level: INFO (20)
MainProcess       root        INFO     app_main.py main(): sh.level: NOTSET (0)
MainProcess       root        INFO     app_main.py main(): fh.level: NOTSET (0)
MainProcess       __main__    INFO     app_main.py main(): インフォログ
MainProcess       __main__    WARNING  app_main.py main(): ウォーニングログ
MainProcess       __main__    ERROR    app_main.py main(): エラーログ
MainProcess       __main__    CRITICAL app_main.py main(): クリティカルログ
MainProcess       __main__    INFO     app_main.py main(): pool.map() を実行します。
MainProcess       __main__    INFO     app_main.py main(): プロセス数 max_processes: 2
MainProcess       __main__    INFO     app_main.py main(): 各子プロセスのログは個別にファイルに記録中...
MainProcess       __main__    INFO     app_main.py main(): pool.map() の結果を表示します。
MainProcess       __main__    INFO     app_main.py main(): [100, 'あいうえお', 'OK']
MainProcess       __main__    INFO     app_main.py main(): [200, 'かきくけこ', 'OK']
MainProcess       __main__    INFO     app_main.py main(): [300, 'さしすせそ', 'OK']
MainProcess       __main__    INFO     app_main.py main(): [400, 'たちつてと', 'OK']
MainProcess       __main__    INFO     app_main.py main(): [500, 'なにぬねの', 'OK']
MainProcess       root        INFO     app_main.py main(): end

ログファイルの内容です。

mp-fh-__main__.txt

MainProcess       root        INFO     app_main.py main(): start
MainProcess       root        INFO     app_main.py main(): root.level: INFO (20)
MainProcess       root        INFO     app_main.py main(): sh.level: NOTSET (0)
MainProcess       root        INFO     app_main.py main(): fh.level: NOTSET (0)
MainProcess       __main__    INFO     app_main.py main(): インフォログ
MainProcess       __main__    WARNING  app_main.py main(): ウォーニングログ
MainProcess       __main__    ERROR    app_main.py main(): エラーログ
MainProcess       __main__    CRITICAL app_main.py main(): クリティカルログ
MainProcess       __main__    INFO     app_main.py main(): pool.map() を実行します。
MainProcess       __main__    INFO     app_main.py main(): プロセス数 max_processes: 2
MainProcess       __main__    INFO     app_main.py main(): 各子プロセスのログは個別にファイルに記録中...
MainProcess       __main__    INFO     app_main.py main(): pool.map() の結果を表示します。
MainProcess       __main__    INFO     app_main.py main(): [100, 'あいうえお', 'OK']
MainProcess       __main__    INFO     app_main.py main(): [200, 'かきくけこ', 'OK']
MainProcess       __main__    INFO     app_main.py main(): [300, 'さしすせそ', 'OK']
MainProcess       __main__    INFO     app_main.py main(): [400, 'たちつてと', 'OK']
MainProcess       __main__    INFO     app_main.py main(): [500, 'なにぬねの', 'OK']
MainProcess       root        INFO     app_main.py main(): end

mp-fh-__mp_main__-SpawnPoolWorker-1.txt

SpawnPoolWorker-1 root        INFO     app_main.py jisaku_initializer(): (ファイルハンドラ追加完了)
SpawnPoolWorker-1 root        INFO     app_main.py jisaku_initializer(): root.level: DEBUG (10)
SpawnPoolWorker-1 root        INFO     app_main.py jisaku_initializer(): logger.level: NOTSET (0)
SpawnPoolWorker-1 root        INFO     app_main.py jisaku_initializer(): fh.level: NOTSET (0)
SpawnPoolWorker-1 __mp_main__ DEBUG    app_main.py jisaku_func(): デバッグログ 100 あいうえお
SpawnPoolWorker-1 __mp_main__ INFO     app_main.py jisaku_func(): インフォログ 100 あいうえお
SpawnPoolWorker-1 __mp_main__ WARNING  app_main.py jisaku_func(): ウォーニングログ 100 あいうえお
SpawnPoolWorker-1 __mp_main__ ERROR    app_main.py jisaku_func(): エラーログ 100 あいうえお
SpawnPoolWorker-1 __mp_main__ CRITICAL app_main.py jisaku_func(): クリティカルログ 100 あいうえお
SpawnPoolWorker-1 __mp_main__ DEBUG    app_main.py jisaku_func(): デバッグログ 400 たちつてと
SpawnPoolWorker-1 __mp_main__ INFO     app_main.py jisaku_func(): インフォログ 400 たちつてと
SpawnPoolWorker-1 __mp_main__ WARNING  app_main.py jisaku_func(): ウォーニングログ 400 たちつてと
SpawnPoolWorker-1 __mp_main__ ERROR    app_main.py jisaku_func(): エラーログ 400 たちつてと
SpawnPoolWorker-1 __mp_main__ CRITICAL app_main.py jisaku_func(): クリティカルログ 400 たちつてと

mp-fh-__mp_main__-SpawnPoolWorker-2.txt

SpawnPoolWorker-2 root        INFO     app_main.py jisaku_initializer(): (ファイルハンドラ追加完了)
SpawnPoolWorker-2 root        INFO     app_main.py jisaku_initializer(): root.level: DEBUG (10)
SpawnPoolWorker-2 root        INFO     app_main.py jisaku_initializer(): logger.level: NOTSET (0)
SpawnPoolWorker-2 root        INFO     app_main.py jisaku_initializer(): fh.level: NOTSET (0)
SpawnPoolWorker-2 __mp_main__ DEBUG    app_main.py jisaku_func(): デバッグログ 200 かきくけこ
SpawnPoolWorker-2 __mp_main__ INFO     app_main.py jisaku_func(): インフォログ 200 かきくけこ
SpawnPoolWorker-2 __mp_main__ WARNING  app_main.py jisaku_func(): ウォーニングログ 200 かきくけこ
SpawnPoolWorker-2 __mp_main__ ERROR    app_main.py jisaku_func(): エラーログ 200 かきくけこ
SpawnPoolWorker-2 __mp_main__ CRITICAL app_main.py jisaku_func(): クリティカルログ 200 かきくけこ
SpawnPoolWorker-2 __mp_main__ DEBUG    app_main.py jisaku_func(): デバッグログ 300 さしすせそ
SpawnPoolWorker-2 __mp_main__ INFO     app_main.py jisaku_func(): インフォログ 300 さしすせそ
SpawnPoolWorker-2 __mp_main__ WARNING  app_main.py jisaku_func(): ウォーニングログ 300 さしすせそ
SpawnPoolWorker-2 __mp_main__ ERROR    app_main.py jisaku_func(): エラーログ 300 さしすせそ
SpawnPoolWorker-2 __mp_main__ CRITICAL app_main.py jisaku_func(): クリティカルログ 300 さしすせそ
SpawnPoolWorker-2 __mp_main__ DEBUG    app_main.py jisaku_func(): デバッグログ 500 なにぬねの
SpawnPoolWorker-2 __mp_main__ INFO     app_main.py jisaku_func(): インフォログ 500 なにぬねの
SpawnPoolWorker-2 __mp_main__ WARNING  app_main.py jisaku_func(): ウォーニングログ 500 なにぬねの
SpawnPoolWorker-2 __mp_main__ ERROR    app_main.py jisaku_func(): エラーログ 500 なにぬねの
SpawnPoolWorker-2 __mp_main__ CRITICAL app_main.py jisaku_func(): クリティカルログ 500 なにぬねの

自作ライブラリをロギングする

自作ライブラリを multiprocessing.Pool で並列実行するときのコード例です。

コード例

メインモジュールのコードです。

app_main.py

import logging, pathlib
import multiprocessing
import jisaku
logger = logging.getLogger(__name__) # NOTSET (0)
# 親プロセスの名前付きロガー

def main():
    """メイン関数です。"""
    root = logging.getLogger() # 親プロセスのルートロガー
    root.setLevel(logging.INFO) # WARNING (30) ⇒ INFO (20)

    sh = logging.StreamHandler() # NOTSET (0)
    sh.setFormatter(logging.Formatter( # %()-17s: 17 文字分のスペースで左寄せ。
        '%(processName)-17s %(name)-11s %(levelname)-8s '
        '%(module)s.py %(funcName)s(): %(message)s'))
    root.addHandler(sh)

    log_dir = pathlib.Path(r'F:\apps\log')
    log_txt = log_dir.joinpath(f'mp-fh-lib-{__name__}.txt')
    fh = logging.FileHandler(log_txt, encoding='utf-8', mode='a') # NOTSET (0)
    fh.setFormatter(logging.Formatter(
        '%(processName)-17s %(name)-11s %(levelname)-8s '
        '%(module)s.py %(funcName)s(): %(message)s'))
    root.addHandler(fh)

    root.info('start')
    root.info(f'root.level: {logging.getLevelName(root.level)} ({root.level})')
    root.info(f'sh.level: {logging.getLevelName(sh.level)} ({sh.level})')
    root.info(f'fh.level: {logging.getLevelName(fh.level)} ({fh.level})')
    logger.debug('デバッグログ') # 今は出ません root: INFO (20)
    logger.info('インフォログ')
    logger.warning('ウォーニングログ')
    logger.error('エラーログ')
    logger.critical('クリティカルログ')

    logger.info('pool.map() を実行します。')
    max_processes = 2
    logger.info(f'プロセス数 max_processes: {max_processes}')
    logger.info('各子プロセスのログは個別にファイルに記録中...')
    args_list = [
        [100, 'あいうえお'],
        [200, 'かきくけこ'],
        [300, 'さしすせそ'],
        [400, 'たちつてと'],
        [500, 'なにぬねの'],
    ]
    with multiprocessing.Pool(
            processes=max_processes,
            initializer=jisaku.jisaku_initializer, initargs=(log_dir,),
        ) as pool: # type(pool): <class 'multiprocessing.pool.Pool'>
        datas = pool.map(jisaku.jisaku_func_wrapper, args_list)

    logger.info('pool.map() の結果を表示します。')
    for data in datas:
        logger.info(data)
    root.info('end')
    return

if __name__ == '__main__':
    main()

自作ライブラリのコードです。

jisaku.py

import logging
import multiprocessing
logger = logging.getLogger(__name__) # NOTSET (0)
# 親プロセスの名前付きロガー または 各子プロセスの名前付きロガー

def jisaku_initializer(log_dir):
    """各子プロセスで最初に 1 回だけ実行される関数です。"""
    root = logging.getLogger() # 各子プロセスのルートロガー
    root.setLevel(logging.DEBUG) # WARNING (30) ⇒ DEBUG (10)
    cp = multiprocessing.current_process()
    # type(cp): <class 'multiprocessing.context.SpawnProcess'>
    log_txt = log_dir.joinpath(f'mp-fh-lib-{__name__}-{cp.name}.txt')
    fh = logging.FileHandler(log_txt, encoding='utf-8', mode='a') # NOTSET (0)
    fh.setFormatter(logging.Formatter(
        '%(processName)-17s %(name)-11s %(levelname)-8s '
        '%(module)s.py %(funcName)s(): %(message)s'))
    root.addHandler(fh)
    root.info('(ファイルハンドラ追加完了)')
    root.info(f'root.level: {logging.getLevelName(root.level)} ({root.level})')
    root.info(f'logger.level: {logging.getLevelName(logger.level)} ({logger.level})')
    root.info(f'fh.level: {logging.getLevelName(fh.level)} ({fh.level})')
    return

def jisaku_func_wrapper(args):
    return jisaku_func(*args)

def jisaku_func(number, text):
    """自作関数です。"""
    logger.debug(f'デバッグログ {number} {text}')
    logger.info(f'インフォログ {number} {text}')
    logger.warning(f'ウォーニングログ {number} {text}')
    logger.error(f'エラーログ {number} {text}')
    logger.critical(f'クリティカルログ {number} {text}')
    return [number, text, 'OK']

実行結果 (processes=2)

自作ライブラリprocesses=2 で実行しました。

実行結果の画面表示です。

MainProcess       root        INFO     app_main.py main(): start
MainProcess       root        INFO     app_main.py main(): root.level: INFO (20)
MainProcess       root        INFO     app_main.py main(): sh.level: NOTSET (0)
MainProcess       root        INFO     app_main.py main(): fh.level: NOTSET (0)
MainProcess       __main__    INFO     app_main.py main(): インフォログ
MainProcess       __main__    WARNING  app_main.py main(): ウォーニングログ
MainProcess       __main__    ERROR    app_main.py main(): エラーログ
MainProcess       __main__    CRITICAL app_main.py main(): クリティカルログ
MainProcess       __main__    INFO     app_main.py main(): pool.map() を実行します。
MainProcess       __main__    INFO     app_main.py main(): プロセス数 max_processes: 2
MainProcess       __main__    INFO     app_main.py main(): 各子プロセスのログは個別にファイルに記録中...
MainProcess       __main__    INFO     app_main.py main(): pool.map() の結果を表示します。
MainProcess       __main__    INFO     app_main.py main(): [100, 'あいうえお', 'OK']
MainProcess       __main__    INFO     app_main.py main(): [200, 'かきくけこ', 'OK']
MainProcess       __main__    INFO     app_main.py main(): [300, 'さしすせそ', 'OK']
MainProcess       __main__    INFO     app_main.py main(): [400, 'たちつてと', 'OK']
MainProcess       __main__    INFO     app_main.py main(): [500, 'なにぬねの', 'OK']
MainProcess       root        INFO     app_main.py main(): end

ログファイルの内容です。

mp-fh-lib-__main__.txt

MainProcess       root        INFO     app_main.py main(): start
MainProcess       root        INFO     app_main.py main(): root.level: INFO (20)
MainProcess       root        INFO     app_main.py main(): sh.level: NOTSET (0)
MainProcess       root        INFO     app_main.py main(): fh.level: NOTSET (0)
MainProcess       __main__    INFO     app_main.py main(): インフォログ
MainProcess       __main__    WARNING  app_main.py main(): ウォーニングログ
MainProcess       __main__    ERROR    app_main.py main(): エラーログ
MainProcess       __main__    CRITICAL app_main.py main(): クリティカルログ
MainProcess       __main__    INFO     app_main.py main(): pool.map() を実行します。
MainProcess       __main__    INFO     app_main.py main(): プロセス数 max_processes: 2
MainProcess       __main__    INFO     app_main.py main(): 各子プロセスのログは個別にファイルに記録中...
MainProcess       __main__    INFO     app_main.py main(): pool.map() の結果を表示します。
MainProcess       __main__    INFO     app_main.py main(): [100, 'あいうえお', 'OK']
MainProcess       __main__    INFO     app_main.py main(): [200, 'かきくけこ', 'OK']
MainProcess       __main__    INFO     app_main.py main(): [300, 'さしすせそ', 'OK']
MainProcess       __main__    INFO     app_main.py main(): [400, 'たちつてと', 'OK']
MainProcess       __main__    INFO     app_main.py main(): [500, 'なにぬねの', 'OK']
MainProcess       root        INFO     app_main.py main(): end

mp-fh-lib-jisaku-SpawnPoolWorker-1.txt

SpawnPoolWorker-1 root        INFO     jisaku.py jisaku_initializer(): (ファイルハンドラ追加完了)
SpawnPoolWorker-1 root        INFO     jisaku.py jisaku_initializer(): root.level: DEBUG (10)
SpawnPoolWorker-1 root        INFO     jisaku.py jisaku_initializer(): logger.level: NOTSET (0)
SpawnPoolWorker-1 root        INFO     jisaku.py jisaku_initializer(): fh.level: NOTSET (0)
SpawnPoolWorker-1 jisaku      DEBUG    jisaku.py jisaku_func(): デバッグログ 100 あいうえお
SpawnPoolWorker-1 jisaku      INFO     jisaku.py jisaku_func(): インフォログ 100 あいうえお
SpawnPoolWorker-1 jisaku      WARNING  jisaku.py jisaku_func(): ウォーニングログ 100 あいうえお
SpawnPoolWorker-1 jisaku      ERROR    jisaku.py jisaku_func(): エラーログ 100 あいうえお
SpawnPoolWorker-1 jisaku      CRITICAL jisaku.py jisaku_func(): クリティカルログ 100 あいうえお
SpawnPoolWorker-1 jisaku      DEBUG    jisaku.py jisaku_func(): デバッグログ 400 たちつてと
SpawnPoolWorker-1 jisaku      INFO     jisaku.py jisaku_func(): インフォログ 400 たちつてと
SpawnPoolWorker-1 jisaku      WARNING  jisaku.py jisaku_func(): ウォーニングログ 400 たちつてと
SpawnPoolWorker-1 jisaku      ERROR    jisaku.py jisaku_func(): エラーログ 400 たちつてと
SpawnPoolWorker-1 jisaku      CRITICAL jisaku.py jisaku_func(): クリティカルログ 400 たちつてと

mp-fh-lib-jisaku-SpawnPoolWorker-2.txt

SpawnPoolWorker-2 root        INFO     jisaku.py jisaku_initializer(): (ファイルハンドラ追加完了)
SpawnPoolWorker-2 root        INFO     jisaku.py jisaku_initializer(): root.level: DEBUG (10)
SpawnPoolWorker-2 root        INFO     jisaku.py jisaku_initializer(): logger.level: NOTSET (0)
SpawnPoolWorker-2 root        INFO     jisaku.py jisaku_initializer(): fh.level: NOTSET (0)
SpawnPoolWorker-2 jisaku      DEBUG    jisaku.py jisaku_func(): デバッグログ 200 かきくけこ
SpawnPoolWorker-2 jisaku      INFO     jisaku.py jisaku_func(): インフォログ 200 かきくけこ
SpawnPoolWorker-2 jisaku      WARNING  jisaku.py jisaku_func(): ウォーニングログ 200 かきくけこ
SpawnPoolWorker-2 jisaku      ERROR    jisaku.py jisaku_func(): エラーログ 200 かきくけこ
SpawnPoolWorker-2 jisaku      CRITICAL jisaku.py jisaku_func(): クリティカルログ 200 かきくけこ
SpawnPoolWorker-2 jisaku      DEBUG    jisaku.py jisaku_func(): デバッグログ 300 さしすせそ
SpawnPoolWorker-2 jisaku      INFO     jisaku.py jisaku_func(): インフォログ 300 さしすせそ
SpawnPoolWorker-2 jisaku      WARNING  jisaku.py jisaku_func(): ウォーニングログ 300 さしすせそ
SpawnPoolWorker-2 jisaku      ERROR    jisaku.py jisaku_func(): エラーログ 300 さしすせそ
SpawnPoolWorker-2 jisaku      CRITICAL jisaku.py jisaku_func(): クリティカルログ 300 さしすせそ
SpawnPoolWorker-2 jisaku      DEBUG    jisaku.py jisaku_func(): デバッグログ 500 なにぬねの
SpawnPoolWorker-2 jisaku      INFO     jisaku.py jisaku_func(): インフォログ 500 なにぬねの
SpawnPoolWorker-2 jisaku      WARNING  jisaku.py jisaku_func(): ウォーニングログ 500 なにぬねの
SpawnPoolWorker-2 jisaku      ERROR    jisaku.py jisaku_func(): エラーログ 500 なにぬねの
SpawnPoolWorker-2 jisaku      CRITICAL jisaku.py jisaku_func(): クリティカルログ 500 なにぬねの

関連記事

concurrent.futures でログを記録するコード例。

multiprocessing でログを記録するコード例。

普通のシングルプロセスでログを記録するコード例。

並列処理のコード例。

以上です。

スポンサーリンク
シェアする(押すとSNS投稿用の『編集ページ』に移動します)
フォローする(RSSフィードに移動します)
スポンサーリンク
シラベルノート
タイトルとURLをコピーしました