web-dev-qa-db-ja.com

スレッドセーフロギングクラスの実装

次は、かなり簡単なスレッドセーフロギングクラスを実装する正しい方法でしょうか?

TextWriterを明示的に閉じることはありませんが、それは問題ですか?

最初にTextWriter.Synchronizedメソッドを使用したとき、静的コンストラクターで初期化し、読み取り専用にしたまで動作しなかったようです:

public static class Logger
{
    static readonly TextWriter tw; 

    static Logger()
    {
        tw = TextWriter.Synchronized(File.AppendText(SPath() + "\\Log.txt")); 
    }

    public static string SPath()
    {
        return ConfigManager.GetAppSetting("logPath"); 
    }

    public static void Write(string logMessage)
    {
        try
        {
            Log(logMessage, tw);
        }
        catch (IOException e)
        {
            tw.Close();
        }
    }

    public static void Log(string logMessage, TextWriter w)
    {
        w.WriteLine("{0} {1}", DateTime.Now.ToLongTimeString(),
            DateTime.Now.ToLongDateString());
        w.WriteLine("  :");
        w.WriteLine("  :{0}", logMessage);
        w.WriteLine("-------------------------------");

        // Update the underlying file.
        w.Flush();
    }
}
50
Sean Thoman

ここでは他の回答とはまったく異なるアプローチを取り、より優れたスレッド対応コードの書き方を実際に学びたいと想定し、私たちからのサードパーティの提案を探していないと仮定します1。)

他の人が言ったように、スレッドセーフTextWriterを作成しています。つまり、WriteLineの呼び出しはスレッドセーフです。つまり、WriteLineの呼び出しが次のように実行されることを意味しません。アトミック操作。つまり、4つのWriteLine呼び出しが順番に発生するという保証はありません。スレッドセーフTextWriterを持っているかもしれませんが、スレッドセーフLogger.Logメソッドを持っていません;)なぜですか?これらの4つの呼び出しのどの時点でも、別のスレッドがLogを呼び出すことを決定する可能性があるためです。これは、WriteLine呼び出しが同期していないことを意味します。これを修正する方法は、次のようなlockステートメントを使用することです。

private static readonly object _syncObject = new object();

public static void Log(string logMessage, TextWriter w)    {
   // only one thread can own this lock, so other threads
   // entering this method will wait here until lock is
   // available.
   lock(_syncObject) {
      w.WriteLine("{0} {1}", DateTime.Now.ToLongTimeString(),
          DateTime.Now.ToLongDateString());
      w.WriteLine("  :");
      w.WriteLine("  :{0}", logMessage);
      w.WriteLine("-------------------------------");
      // Update the underlying file.
      w.Flush();
   }
}

これで、スレッドセーフTextWriterとスレッドセーフLoggerができました。

理にかなっていますか?

92
x0n

TextWriter.Synchronized を呼び出すと、TextWriterの単一インスタンスが保護されますが、書き込みが同期されないため、1つの "Log"呼び出しがファイル内にまとめられます。

複数のスレッドからWrite(または内部Logインスタンスを使用してTextWriter)を呼び出すと、個々のWriteLine呼び出しが織り込まれ、日付と時刻が作成される場合があります使用できないスタンプ。

私は、このためにすでに存在するサードパーティのログソリューションを個人的に使用します。それがオプションではない場合、フレームワークのTextWriter.Synchronizedラッパー。

5
Reed Copsey

このクラス(.NET 2.0の一部)を調べる必要があります。独自のロガーを「作成」する必要はありません。テキストファイル、イベントビューなどにログを記録できます。

http://msdn.Microsoft.com/en-us/library/system.diagnostics.tracesource.aspx

「Log」メソッドは次のようになります(「traceSource」と呼ばれる内部メンバー変数があると仮定します)。

    public void Log(TraceEventType eventType, string message)
    {
        this.traceSource.TraceEvent(eventType, 0, message);
        this.traceSource.Flush();
    }

これをサポートするのは、TraceSourceに名前を付け、いくつかの構成設定を持つ構成セクションです。ロガーでTraceSourceを構築する場合、設定で指定されたトレースソースの1つでインスタンス化することを想定しています。

<system.diagnostics>
<sources>
  <source name="Sample" switchValue="Information,ActivityTracing">
    <listeners>
      <add name="file"
         initializeData="C:\temp\Sample-trace.log"
         traceOutputOptions="DateTime"
         type="System.Diagnostics.TextWriterTraceListener, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
    </listeners>
  </source>
</sources>

また、ロガーを静的にしないでください。代わりに、依存性注入/ IOCにはEnterprise Library 5.0 Unityを使用してください。

お役に立てれば!

2
Glenn Ferrie

今日、ロギングの問題について議論しているときに、誰かがこの投稿を指摘してくれました。ここにはすでにかなり良い答えがありますが、完全にLoggerの方法でまったく同じことを行うThreadsafeクラスの単純なバージョンを示すために、答えを追加しています。
ここで注目すべき主な点は、TextWriter.Synchronizedは、適切なlock内にファイルを書き込むため、スレッドセーフのために必要です。

注:これは、x0nの回答のコメントセクションで既に説明されています。

public static class Logger
{
    static readonly object _locker = new object();

    public static void Log(string logMessage)
    {
        try
        {
            var logFilePath = Path.Combine(@"C:\YourLogDirectoryHere", "Log.txt");
            //Use this for daily log files : "Log" + DateTime.Now.ToString("yyyy-MM-dd") + ".txt";
            WriteToLog(logMessage, logFilePath);
        }
        catch (Exception e)
        {
            //log log-exception somewhere else if required!
        }
    }

    static void WriteToLog(string logMessage, string logFilePath)
    {
        lock (_locker)
        {
            File.AppendAllText(logFilePath,
                    string.Format("Logged on: {1} at: {2}{0}Message: {3}{0}--------------------{0}", 
                    Environment.NewLine, DateTime.Now.ToLongDateString(),
                    DateTime.Now.ToLongTimeString(), logMessage));
        }
    }
}

何かを記録するには、単に

Logger.Log("Some important event has occurred!");

そして、このようなログエントリを作成します

ログオン:2015年10月7日:02:11:23
メッセージ:いくつかの重要なイベントが発生しました!
--------------------

1
Arghya C

コードをインスツルメントする簡単な方法を探している場合、この機能は.NET内にすでに存在します。

http://msdn.Microsoft.com/en-us/library/system.diagnostics.trace.aspx

さらに、サードパーティのツールを使用すると、ログの堅牢なソリューションが得られます。例には、 log4netnLog 、および Enterprise Library が含まれます。

私はこれで車輪を再発明しないことを本当にお勧めします:)

0
Dave R.