线程安全日志记录类实现

本文关键字:实现 记录 日志 安全 线程 | 更新日期: 2023-09-27 17:58:02

以下是否是实现相当简单的线程安全日志记录类的正确方法?

我知道我从来没有明确关闭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();
    }
}

线程安全日志记录类实现

我将在这里采用与其他答案完全不同的方法,并假设您实际上想学习如何编写更好的线程感知代码,而不是从我们这里寻找第三方建议(即使您实际上最终可能会使用一个。

正如其他人所说,您正在创建一个线程安全TextWriter这意味着对 WriteLine 的调用是线程安全的,这并不意味着对 WriteLine 的大量调用将作为原子操作执行。我的意思是不能保证四个WriteLine调用将按顺序发生。您可能具有线程安全的TextWriter,但没有线程安全的Logger.Log方法;)为什么?因为在这四个调用期间的任何时候,另一个线程也可能决定调用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

有意义?

有人在讨论今天的一些日志记录问题时指出了这篇文章。我们在这里已经有很好的答案,但我添加我的答案只是为了展示 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));
        }
    }
}

要记录某些内容,只需调用 as

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

它将像这样创建一个日志条目

登录日期: 07 十月 2015 在: 02:11:23
消息:发生了一些重要事件!
--------------------

虽然调用TextWriter.Syncized将保护TextWriter的单个实例,但它不会同步您的写入,因此一个"Log"调用在文件中保持在一起。

如果从多个线程调用Write(或使用内部TextWriter实例Log(,则各个WriteLine调用可能会交织在一起,从而使日期和时间戳不可用。

我个人会为此使用已经存在的第三方日志记录解决方案。 如果这不是一个选项,自己同步它(即使使用简单的锁(可能比使用框架的TextWriter.Synchronized包装器更有用。

您应该研究此类(.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 时,您将使用配置中指定的跟踪源之一来实例化它。

<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>

另外,不要使记录器成为静态的。 请改用企业库 5.0 Unity 进行依赖关系注入/IOC。

希望这有帮助!

如果要查找检测代码的简单方法,则 .NET 中已存在该工具:

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

此外,第三方工具将为您提供强大的日志记录解决方案;示例包括log4net,nLog和Enterprise Library。

我真的建议不要在这个:)上重新发明轮子