質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

1回答

14344閲覧

Pythonでmultiprocessing使用時にloggingの出力がダブる

miyahan

総合スコア3095

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2016/09/14 07:23

編集2016/09/14 07:42

起きている問題

Python 3.5 で multiprocessing を使い並列処理を行うスクリプトを書いています。処理自体はうまくいっているのですが、logging でログを記録した際、同じメッセージが複数回出力されてしまいます。

ダブって出力されてしまうメカニズム・回避方法を教えてください。

該当のソースコード

python

1import multiprocessing 2import logging 3import time 4import os 5 6class Hoge: 7 def __init__(self): 8 self.logger = logging.getLogger('HogeClass') 9 self.logger.addHandler(logging.StreamHandler()) 10 self.logger.setLevel(logging.DEBUG) 11 12 def child(self, num): 13 self.logger.info('I am {}, pid {}'.format(num, os.getpid())) 14 time.sleep(1) 15 16 17if __name__ == '__main__': 18 def mp_hoge(num): 19 h = Hoge() 20 h.child(num) 21 22 multiprocessing.Pool(2).map(mp_hoge, range(10))

実行時ログ

sh

1$ ./mp_test.py 2I am 0, pid 13604 3I am 2, pid 13605 4I am 3, pid 13605 5I am 3, pid 13605 6I am 1, pid 13604 7I am 1, pid 13604 8I am 4, pid 13605 9I am 6, pid 13604 10I am 6, pid 13604 11I am 6, pid 13604 12I am 4, pid 13605 13I am 4, pid 13605 14I am 7, pid 13604 15I am 7, pid 13604 16I am 7, pid 13604 17I am 7, pid 13604 18I am 5, pid 13605 19I am 5, pid 13605 20I am 5, pid 13605 21I am 5, pid 13605 22I am 8, pid 13605 23I am 8, pid 13605 24I am 8, pid 13605 25I am 8, pid 13605 26I am 8, pid 13605 27I am 9, pid 13605 28I am 9, pid 13605 29I am 9, pid 13605 30I am 9, pid 13605 31I am 9, pid 13605 32I am 9, pid 13605

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

マルチプロセス環境で複数のプロセスから単一ファイル(標準エラー出力等も単一ファイルの一種です)へのログ記録自体がサポートされていません。

Logging クックブック — Python 3.5.2 ドキュメント#複数のプロセスからの単一ファイルへのログ記録

ログ記録はスレッドセーフであり、単一プロセスの複数のスレッドからの単一ファイルへのログ記録はサポート されています が、 複数プロセス からの単一ファイルへのログ記録はサポート されません

つまり、そもそもがサポートされない方法を使っていると言うことです。上記クックブックに複数プロセスからも単一のファイルに書き込む方法が記載されていますので、ご参考ください。


【蛇足】

では、なんでそうなるのか…ですが、ソースコードレベルまで確認して訳ではありませんので、以下はただの推測です。たぶん、間違っているような気がしますので、詳しい人はツッコミをお願いします。

multiprocessingは(UNIX/Linuxの場合)通常forkで子プロセスを作ります。logginはgetLogger()で同じインスタンスを返すために、親側に大本が既にあると考えられます。通常、fork内のオブジェクト(メモリ上のデータ)は親からのコピー(実際はコピーに見せかけですが)ですので、親と子のそれぞれは独立します。しかし、loggingは同じインスタンスを返すために、共有メモリ(みんなが一緒に見ているところ)に、全ての共通として存在する必要があると考えられます。つまり、forkした子供達にも同じloggingが存在し、getLogger()は同じ物を返しているのではないかと考えられます。

さて、getLogger()でインスタンス取得して出力するとき、何が起こるのでしょうか?そのロガーインスタンスはその子プロセスの標準エラー出力と結びつけられると考えられます。ですが、子プロセス間でインスタンスは共通です。でも、それぞれのプロセスで出力先はそれぞれ設定されています。

つまり、ログをとりあえず入れておくバッファは、共有メモリ上の同じインスタンスなので、子プロレス間で共通になっていると思われます。それをいざ出力するとなったとき、それぞれの子プロセスで標準エラー出力に結びつけられているため、その数だけ標準エラー出力に出力されるのでは無いかと考えられます。子プロセスが終了すれば、結びつけは終わりますので、今動いているプロセス数だけが出力され、数も一定にはなりません。

こうして考えるとなんとかうまく説明できそうですが、4と6の挟んであるところは何?と言われると…うーん、ちょっとわからないです。

投稿2016/09/16 10:33

raccy

総合スコア21735

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

miyahan

2016/09/16 11:23

回答ありがとうございます。ちゃんと公式リファレンスを読むべきでした…。そこに次のような記述がありました。 > 同じ name で getLogger() を複数回呼び出すと、常に同じロガー・オブジェクトへの参照が返されます。 つまり本事象は、新しくロガーのインスタンスを生成してそこにハンドラを1つセットしていたつもりが、実は1つのロガーにハンドラをいくつもセットしていたため、同じログが複数回出力されていたようです。 そこで、ロガーオブジェクトをクラス変数に格納し、Hogeクラスの各インスタンスで使いまわすことにより問題は解決しました。 また指摘いただいた「複数のプロセスからの単一ファイルへのログ記録」の問題については、別途クックブックを参考に改良したいと思います。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問