分析自定义记录器 class 的性能,用于读取 Word 文档的应用程序

Profling performance of custom logger class, for an application reading Word document

我有一个应用程序,它使用 OpenXmlPowerTools 从 .docx 文件中读取注释和段落。它是一个控制台应用程序,它会在 运行 时创建一个 debug.log 文件。

实现了一个记录器 class,它将消息保存到所有构建的文本文件中,并将这些消息打印到调试构建的控制台。以下代码是此记录器的一部分 class:

public static class Logger
{
    public enum LogLevel
    {
        ERROR, WARNING, DEBUG
    }

    public static void Log(string message, LogLevel level, bool newline)
    {
        try
        {
            // the very next line was a hotspot, as shown in the profiler
            using (StreamWriter sw = File.AppendText(path))
            {
                // write the messages to this file
            }
        }
        catch (Exception ex)
        {
            // handle it
            // I know it is bad practice to catch System.Exception, I need to fix this.
        }
     }
}

在代码中,这个函数经常这样调用:

private void doSomething(string someParameter)
{
    Logger.Log("The parameter is: " + someParameter, Logger.LogLevel.DEBUG, true);
}

我已经分析了它的性能,对于一个有几十条评论的相当大的 word 文档,它需要 1 分 40 秒才能完成。没有记录,只用了几秒钟。经过一些调查,似乎 File.AppendText 在 .NET 中非常慢。

作为替代方案,我尝试使用缓冲区:

using (StreamWriter sw = new StreamWriter(path, false, Encoding.UTF8, 65536)
{
    // write the messages to the file
}

与我读过的一篇推荐这种方法的文章中的信息相反,性能似乎变差了(耗时超过 2 分钟)。为什么是这样?我怎样才能提高它的性能?

您的登录代码有误。 15 年前,Entprise Library 就是这样开始的,但结果并不好。 使用日志记录框架并完成它。

现在谈谈你的实际问题。您为每个日志调用打开和关闭文件,这非常慢并且会导致大量开销。保持 Log 文件和 StreamWriter 打开并使用锁来确保您不会同时向日志文件写入数据。 接下来您需要处理生命周期问题,因为如果 FileStream 首先完成,您的 StreamWriter 将无法将挂起的数据刷新到磁盘并且您丢失了最后的日志消息(很可能是重要的崩溃异常消息)。

要在每个日志上解决刷新问题,调用 StreamWriter(慢)或创建一个包装器 class,它派生自 CriticalFinalizerObject 并保持您的 FileStream 打开并在 FileStream 实例上调用 GC.SuppressFinalize 以防止在应用程序关闭期间提前完成。

这是您在创建自己的日志记录库时最常遇到的陷阱。

一个小型记录器例如这里: https://github.com/Alois-xx/WMIWatcher/blob/master/FileLogger.cs