线程安全日志类的实现
以下是实现一个相当简单的线程安全日志类的正确方法吗?
我知道我从来没有明确closuresTextWriter
,那会是一个问题吗?
当我最初使用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(); } }
我将采取一个完全不同的方法比其他答案,并假设你真的想学习如何编写更好的线程感知的代码,并不寻找我们的第三方的build议(即使你可能实际上最终使用一。)
正如其他人所说,你正在创build一个线程安全的TextWriter
,这意味着调用WriteLine是线程安全的,这并不意味着一堆调用WriteLine
将作为一个primefaces操作来执行。 我的意思是不能保证四个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
。
合理?
虽然调用TextWriter.Synchronized将保护TextWriter的单个实例,但它不会同步您的写入,因此一个“Log”调用在文件内部保持在一起。
如果从多个线程调用Write
(或使用内部TextWriter
实例的Log
),则可能会将各个WriteLine
调用交织在一起,从而使您的date和时间戳不可用。
我个人会使用已经存在的第三方日志logging解决scheme。 如果这不是一个选项,自己同步(即使是一个简单的locking)可能会比使用框架的TextWriter.Synchronized
包装更有用。
你应该看看这个类(.NET 2.0的一部分),不需要“创build”你自己的logging器。 使您能够login到文本文件,事件视图等
http://msdn.microsoft.com/en-us/library/system.diagnostics.tracesource.aspx
你的“日志”方法可以看起来像这样(假设有一个名为“traceSource”的内部成员variables):
public void Log(TraceEventType eventType, string message) { this.traceSource.TraceEvent(eventType, 0, message); this.traceSource.Flush(); }
支持这个是一个configuration部分,命名TraceSource并具有一些configuration设置。 假设当你在logging器中构造一个TraceSource的时候,你正在使用config中命名的一个trace源来实例化它。
<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>
另外,不要让你的logging器静态。 相反,使用Enterprise Library 5.0 Unitydependency injection/ IOC。
希望这可以帮助!
有人在今天讨论一些日志问题的时候指出了这个post。 我们已经有了相当不错的答案,但是我添加了我的答案,仅仅是为了展示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)); } } }
要logging一些东西,只需简单地称为
Logger.Log("Some important event has occurred!");
它会做这样的日志条目
login:2015年10月7日在:02:11:23
信息:发生了一些重要事件!
——————–
如果你正在寻找一个简单的方法来testing你的代码,那么这个工具就已经存在于.NET中了:
http://msdn.microsoft.com/en-us/library/system.diagnostics.trace.aspx
此外,第三方工具将为您提供强大的日志logging解决scheme。 示例包括log4net , nLog和企业库 。
我真的不build议重塑这个:)