从Enterprise Library Logging 3.1升级到6.0 Writer.Dispose()不再适用于关闭平面文件日志

时间:2017-05-18 21:00:58

标签: c# logging enterprise-library

我们的应用程序使用Enterprise Library日志版本3.1来写入平面文件日志。在每次执行Logger.Write之后我们调用了Logger.Writer.Dispose(),这允许我们在应用程序仍在运行时删除或访问平面文件。虽然这不建议用于性能并且不是线程安全的,但我们需要能够从多个线程写入文件。我们不想花太多时间设置msmq并编写自定义日志记录,因此dispose方法效果最好。

升级到6.0版后,我们再也无法处理Logger.Writer而无法再次创建它。

我们需要做些什么来确定Logger.Writer是否已经处置并重新创建它?

这就是我创建Logger.Writer的方式:`

        IConfigurationSource configurationSource = ConfigurationSourceFactory.Create();
        LogWriterFactory logWriterFactory = new LogWriterFactory(configurationSource);
        Logger.SetLogWriter(logWriterFactory.Create(), false);`

有没有办法查看LogWriter是否已设置/创建或检查它是否已被处置?

1 个答案:

答案 0 :(得分:0)

我可以想出4种方法来做你想做的事情:

  1. InvalidOperationException实例为空时,抓住Logger.Writer引发的LogWriter
  2. 创建一个自定义NullLogWriter类,将该值设置为LogWriter并检查该类型以确定LogWriter是否为空。
  3. 复制EntLib Logger外观代码并添加所需的确切功能。
  4. 使用反射来访问私有writer成员变量
  5. 这些方法试图解决的主要障碍是,如果值为null,Logger facade不会授予对LogWriter实例的访问权限。相反,它会抛出InvalidOperationException

    选项1 - Catch InvalidOperationException

    选项1的缺点是,捕获每个日志记录调用的异常都是性能损失(尽管由于您的现有设计已经受到重创)并且被认为是代码味道。

    代码可能如下所示:

    Log("Test 1", "General");            
    Log("Test 2", "General");            
    
    static void Log(string message, string category)
    {
        bool isLogWriterDisposed = false;
        try
        {
            // If Writer is null InvalidOperation will be thrown
            var logWriter = Logger.Writer;
        }
        catch (InvalidOperationException e)
        {
            isLogWriterDisposed = true;
        }
    
        if (isLogWriterDisposed)
        {
            InitializeLogger();
        }
    
        // Write message
        Logger.Write(message, category);
    
        // Dispose the existing LogWriter and set it to null
        Logger.Reset();
    }
    

    选项2 - 自定义NullLogWriter

    选项2可能如下所示:

    public class NullLogWriter : LogWriter
    {
        /// <inheritdoc />
        public NullLogWriter(LoggingConfiguration config) : base(config)
        {
        }
    
        /// <inheritdoc />
        public NullLogWriter(IEnumerable<ILogFilter> filters, IDictionary<string, LogSource> traceSources, LogSource errorsTraceSource, string defaultCategory) : base(filters, traceSources, errorsTraceSource, defaultCategory)
        {
        }
    
        /// <inheritdoc />
        public NullLogWriter(IEnumerable<ILogFilter> filters, IDictionary<string, LogSource> traceSources, LogSource allEventsTraceSource, LogSource notProcessedTraceSource, LogSource errorsTraceSource, string defaultCategory, bool tracingEnabled, bool logWarningsWhenNoCategoriesMatch) : base(filters, traceSources, allEventsTraceSource, notProcessedTraceSource, errorsTraceSource, defaultCategory, tracingEnabled, logWarningsWhenNoCategoriesMatch)
        {
        }
    
        /// <inheritdoc />
        public NullLogWriter(IEnumerable<ILogFilter> filters, IDictionary<string, LogSource> traceSources, LogSource allEventsTraceSource, LogSource notProcessedTraceSource, LogSource errorsTraceSource, string defaultCategory, bool tracingEnabled, bool logWarningsWhenNoCategoriesMatch, bool revertImpersonation) : base(filters, traceSources, allEventsTraceSource, notProcessedTraceSource, errorsTraceSource, defaultCategory, tracingEnabled, logWarningsWhenNoCategoriesMatch, revertImpersonation)
        {
        }
    
        /// <inheritdoc />
        public NullLogWriter(IEnumerable<ILogFilter> filters, IEnumerable<LogSource> traceSources, LogSource errorsTraceSource, string defaultCategory) : base(filters, traceSources, errorsTraceSource, defaultCategory)
        {
        }
    
        /// <inheritdoc />
        public NullLogWriter(IEnumerable<ILogFilter> filters, IEnumerable<LogSource> traceSources, LogSource allEventsTraceSource, LogSource notProcessedTraceSource, LogSource errorsTraceSource, string defaultCategory, bool tracingEnabled, bool logWarningsWhenNoCategoriesMatch) : base(filters, traceSources, allEventsTraceSource, notProcessedTraceSource, errorsTraceSource, defaultCategory, tracingEnabled, logWarningsWhenNoCategoriesMatch)
        {
        }
    
        /// <inheritdoc />
        public NullLogWriter(LogWriterStructureHolder structureHolder) : base(structureHolder)
        {
        }
    }
    
    class Program
    {
        private static readonly LogWriter NullLogWriter = new NullLogWriter(new LoggingConfiguration());
    
        static void Main(string[] args)
        {
            Log("Test 1", "General");            
            Log("Test 2", "General");            
        }
    
        static void Log(string message, string category)
        {
            if (HasLogWriterBeenDisposed())
            {
                InitializeLogger();
            }
    
            // Write message
            Logger.Write(message, category);
    
            // Set the logger to the null logger this disposes the current LogWriter
            // Note that this call is not thread safe so would need to be synchronized in multi-threaded environment
            Logger.SetLogWriter(NullLogWriter, false);
        }
    
        static bool HasLogWriterBeenDisposed()
        {
            try
            {
                // If logger is the NullLogWriter then it needs to be set
                return Logger.Writer is NullLogWriter;
            }
            catch (InvalidOperationException e)
            {
                // If InvalidOperationException is thrown then logger is not set -- consider this to be disposed
                return true;
            }
        }
    
        static void InitializeLogger()
        {
            IConfigurationSource configurationSource = ConfigurationSourceFactory.Create();
            LogWriterFactory logWriterFactory = new LogWriterFactory(configurationSource);
            Logger.SetLogWriter(logWriterFactory.Create(), false);            
        }
    }
    

    它有一个额外的类,但不会在每次记录调用时抛出异常。

    选项3 - 重复记录器

    选项3可以直接访问私有writer成员变量,因此可以检查它是否为null并返回true / false。

    选项4 - 反思

    这种方法可能看起来与选项2类似,但是使用反射检查而不是对NullLogWriter进行检查。请注意,如果在中等信任下运行,则反射调用将失败。

    Log("Test 1", "General");
    Log("Test 2", "General");
    
    static bool HasLogWriterBeenDisposed()
    {
        var fieldInfo = typeof(Logger).GetField("writer", BindingFlags.NonPublic | BindingFlags.Static);       
        return fieldInfo?.GetValue(null) == null;
    }
    
    static void Log(string message, string category)
    {
        if (HasLogWriterBeenDisposed())
        {
            InitializeLogger();
        }
    
        // Write message
        Logger.Write(message, category);
    
        // Dispose and set the logger to the null
        Logger.Reset();
    }
    
    static void InitializeLogger()
    {
        IConfigurationSource configurationSource = ConfigurationSourceFactory.Create();
        LogWriterFactory logWriterFactory = new LogWriterFactory(configurationSource);
        Logger.SetLogWriter(logWriterFactory.Create(), false);
    }
    

    结论

    IMO,这些方法都不理想。真正的问题是原始(非推荐)设计迫使我们走这条道路之一。这些方法也不是线程安全的。如果我必须选择一种方法,我可能会使用选项2(NullLogWriter),因为它不依赖于代码重复,在每次调用时捕获异常,或者使用反射来访问私有成员变量,所有这些都是代码味道在某种程度上。

    附录

    如果您想要的是一个不保持文件打开的线程安全实现,那么您可以坚持使用您正在进行的open / dispose方法(尽管效率低下),避免使用Enterprise Library Logger static外观,而是使用您自己的静态类/单例,它可以完全按照您的需要处理逻辑。这样的事情应该有效:

    public static class CustomLogger
    {
        private static readonly LogWriterFactory LogWriterFactory = new LogWriterFactory(ConfigurationSourceFactory.Create());
        private static readonly object Locker = new object();
    
        public static void Write(string message, string category)
        {
            lock (Locker)
            {
                using (var logWriter = LogWriterFactory.Create())
                {
                    logWriter.Write(message, category);
                }
            }
        }
    }
    
    
    CustomLogger.Write("Test 1", "General");
    CustomLogger.Write("Test 2", "General");