以下是实现相当直接的线程安全日志记录类的正确方法吗?
我知道我从未明确关闭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();
}
}
答案 0 :(得分:90)
我将采取与其他答案完全不同的方法并假设您实际上想要学习如何编写更好的线程感知代码,并且不寻找我们的第三方建议(即使您实际上可能最终使用一个。)
正如其他人所说,你正在创建一个线程安全的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
。
有意义吗?
答案 1 :(得分:5)
虽然调用TextWriter.Synchronized将保护TextWriter
的单个实例,但它不会同步您的写入,以便一个" Log"呼叫在文件内保持在一起。
如果您从多个主题调用Write
(或Log
使用内部TextWriter
实例),则可以交织各个WriteLine
个来电,使您的日期和时间标记无法使用。
我个人会使用已存在的第三方日志记录解决方案。如果这不是一个选项,那么自己同步(即使使用简单的锁定)可能比使用框架的TextWriter.Synchronized
包装器更有用。
答案 2 :(得分:1)
你应该研究这个类(.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时,您使用配置中指定的跟踪源之一来实例化它。
<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>
另外,不要让你的记录器静止。相反,使用Enterprise Library 5.0 Unity进行依赖注入/ IOC。
希望这有帮助!
答案 3 :(得分:1)
有人在今天讨论一些日志记录问题时指出了这篇文章。我们在这里已经有了很好的答案,但是我添加我的答案只是为了展示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
消息:发生了一些重要事件!
--------------------
答案 4 :(得分:0)
如果您正在寻找一种简单的代码检测方法,那么该功能已经存在于.NET中:
http://msdn.microsoft.com/en-us/library/system.diagnostics.trace.aspx
此外,第三方工具将为您提供强大的日志记录解决方案;示例包括log4net,nLog和Enterprise Library。
我真的建议不要重新发明轮子:)