ASP.NETでNlogを使って、ログを出力していました。
userIdを見て動的に出力ファイルを変更していたのですが、マルチスレッドの影響で出力先が変わることがあったので、原因と対策を記します。
1.staticなLoggerで動的に出力先を変更する際の注意
私の実装はスレッド間で共通のstaticなloggerを作成していました。
LogManager.cs
publicstaticclassLogManager(){// スレッド間で共通のLoggerを定義publicstaticLogFactoryfactory=newLogFactory();publicstaticLoggerLogger=factory.GetLogger("API");publicstaticvoidWriteLog(longuserId,stringmessage){// Logの出力先を設定logger.Factory.Configuration.Variables["userId"]=userId.ToString();logger.Factory.Configuration.Variables["outputDateTime"]=DateTime.Now.ToString(); // ☆ // Logの出力logger.Info(message);}}
問題は☆の位置で別スレッドからlogger.Factory.Configuration.Variablesの値が変更されると出力先も変わってしまいます。
原因はloggerがstaticであり、スレッド間で共通のオブジェクトだからです。
こちらの対策ですが、ロックすることで同期をとるのもいいと思いますが、私はスレッドごとにloggerのインスタンスを作ることにしました。
publicstaticclassLogManager(){//外からLoggerを渡すようにする publicstaticvoidWriteLog(Loggerlogger,longuserId,stringmessage){logger.Factory.Configuration.Variables["userId"]=userId.ToString();logger.Factory.Configuration.Variables["outputDateTime"]=DateTime.Now.ToString();logger.Info(message);}}publicclassBaseApiController(){// スレッドが立ちあがる際にインスタンス生成publicLoggerapiLogger=NLog.LogManager.GetLogger(LogConstants.TargetAPI);publicvoidWriteApiLog(stringmessage){LogManager.WriteLog(apiLogger,messsage);}}
スレッドが立ちあがる際にインスタンス生成されるように記述しておくことでスレッドセーフなLoggerが出来上がります。
私はControllerのBaseに置きました。
2.ReconfigExistingLoggersは別スレッドまで上書きしてしまう
私の勝手な認識でlogger.Factory.Configurationを更新したら、logger.Factory.ReconfigExistingLoggersで反映するものだと思っていたのですが、これをやってしまうと、別スレッドのLoggerまで書き換わってしまいます。
publicstaticclassLogManager(){publicstaticvoidWriteLog(Loggerlogger,longuserId,stringmessage){logger.Factory.Configuration.Variables["userId"]=userId.ToString();logger.Factory.Configuration.Variables["outputDateTime"]=DateTime.Now.ToString();// 今生きているすべてのスレッドのloggerにuserIdとoutputDateTimeを反映してしまうlogger.Factory.ReconfigExistingLoggers();// 結果、スレッド間のタイミングでここに来た際に書き変わっていることがある。logger.Info(message);}}
Variablesの値は書き換えた時点でloggerのインスタンスに反映されています。
ReconfigExistingLoggersの利用は注意が必要でした。
自分はこの2か所ではまったので、もし解決の糸口になれば幸いです。