线程安全日志记录类实现
本文关键字:实现 记录 日志 安全 线程 | 更新日期: 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。
我真的建议不要在这个:)上重新发明轮子