线程安全日志类实现

发布于 2024-11-11 06:09:50 字数 1043 浏览 4 评论 0原文

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

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

Would the following be the correct way to implement a fairly straightforward thread-safe logging class?

I know that I never explicitly close the TextWriter, would that be a problem?

When I initially used the TextWriter.Synchronized method, it did not seem to work until I initialized it in a static constructor and made it readonly like so:

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();
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

完美的未来在梦里 2024-11-18 06:09:50

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

正如其他人所说,您正在创建一个线程安全的 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 >。

有道理吗?

I'm going to take a completely different approach here than the other answers and assume you actually want to learn how to write better thread-aware code, and are not looking for 3rd party suggestions from us (even though you may actually end up using one.)

As others have said, you are creating a thread safe TextWriter which means calls to WriteLine are thread-safe, that doesn't mean that a bunch of calls to WriteLine are going to be performed as an atomic operation. By that I mean there is no guarantee that the four WriteLine calls are going to happen in sequence. You may have a thread-safe TextWriter, but you don't have a thread-safe Logger.Log method ;) Why? Because at any point during those four calls, another thread might decide to call Log also. This means your WriteLine calls will be out of sync. The way to fix this is by using a lock statement like so:

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();
   }
}

So, now you have a thread-safe TextWriter AND a thread-safe Logger.

Make sense?

纵山崖 2024-11-18 06:09:50

今天有人在讨论一些日志记录问题时向我指出了这篇文章。我们在这里已经有了很好的答案,但我添加我的答案只是为了展示 Logger 类的更简单版本,它以完全线程安全的方式执行完全相同的操作.
这里需要注意的一件主要事情是,线程安全不需要 TextWriter.Synchronized,因为我们是在适当的 内写入文件。

注意:这已经在 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!");

它就会生成如下所示的日志条目

登录时间:2015 年 10 月 7 日,时间:02:11:23
消息:发生了一些重要事件!
--------------------

Someone pointed me to this post while discussing some logging issues today. We already have pretty good answers here, but I'm adding my answer just to show a simpler version of the Logger class which does the exact same thing, in completely Threadsafe way.
One main thing to notice here is, no TextWriter.Synchronized is required for thread safety, as we are writing the file within a proper lock.

Note: This has already been discussed in the comments section of x0n's answer.

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));
        }
    }
}

To log something, simply call as

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

And it will make a log entry like this

Logged on: 07 October 2015 at: 02:11:23
Message: Some important event has occurred!
--------------------

浊酒尽余欢 2024-11-18 06:09:50

调用 TextWriter.Synchronized 将保护 的单个实例TextWriter,它不会同步您的写入,因此一个“Log”调用会保留在文件内。

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

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

While calling TextWriter.Synchronized will protect that single instance of TextWriter, it will not synchronize your writes so that one "Log" call stays together inside of the file.

If you call Write (or Log using the internal TextWriter instance) from multiple threads, the individual WriteLine calls may be interwoven, making your date and time stamps unusable.

I would personally use a third party logging solution that already exists for this. If that is not an option, synchronizing this yourself (even with a simple lock) will likely be more useful than using the framework's TextWriter.Synchronized wrapper.

岁月蹉跎了容颜 2024-11-18 06:09:50

您应该研究这个类(.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。

希望这有帮助!

You should look into this class (part of .NET 2.0), no need to "create" your own logger. enables you to log to a text file, event view, etc.

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

Your "Log" method can look something like this (assuming there is an intermal member variable called 'traceSource'):

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

Supporting this is a config section that names the TraceSource and has some Config settings. It is assumed that when you construct a TraceSource in your logger you are instantiating it with one of the trace sources named in the config.

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

Also, don't make your logger static. Instead, use Enterprise Library 5.0 Unity for Dependency Injection / IOC.

Hope this helps!

世界等同你 2024-11-18 06:09:50

如果您正在寻找一种简单的方法来检测代码,.NET 中已经存在该工具:

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

此外,第三方工具将为您提供强大的日志记录解决方案;示例包括 log4netnLog,以及企业库

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

If you're looking for a simple method of instrumenting your code, the facility already exists within .NET:

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

Additionally, third party tools will give you robust solutions for logging; examples include log4net, nLog, and the Enterprise Library.

I really recommend not reinventing the wheel on this :)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文