【问题标题】:We need enterprise library logging to throw errors我们需要企业库日志来抛出错误
【发布时间】:2013-09-04 06:34:46
【问题描述】:

您知道我们如何从企业库 5 日志记录块中获取错误吗?

据我所知,日志记录的理念是不中断,所以如果配置中出现错误,我们不会抛出错误。

但是在我们的应用程序中,日志记录是如此重要,以至于我们需要让应用程序出现故障,而不是在没有日志的情况下运行。

所以我们需要这样配置,有的话会报错。

我在这里找到了一些关于事件的信息:Enterprise Library Logging Application Block options

所以我们应该能够监听这个事件,并至少抛出我们自己的错误,但是我无法获得对日志记录工具的引用,因为在下面的代码中我看不到 GetInstrumentationEventProvider() 方法。

LoggingInstrumentationProvider instrumentation = Logger.Writer.GetInstrumentationEventProvider() as LoggingInstrumentationProvider;

instrumentation.failureLoggingError += (s, z) => { throw z.Exception; };

感谢您的帮助。

【问题讨论】:

    标签: c# logging error-handling enterprise-library


    【解决方案1】:

    我认为 LoggingInstrumentationProvider 定义在版本 4 和 5 之间发生了更改以删除事件,因此发布的方法不适用于 Enterprise Library 5。

    有几种方法可以改变行为,以便企业库不会吞下异常:

    • 修改企业库源代码实现你想要的行为
    • 复制LogWriterImpl/LogSource代码并注入到容器中(这种方式可以看Enterprise Library Logging Extensions – Part 1
    • 将当前的 LoggingInstrumentationProvider 实现替换为 throws 的实现

    我将演示最后一种方法(不是因为它是最好的,而是因为它是最有趣的)。因此,要使这种方法起作用,我们需要创建一个自定义LoggingInstrumentationProvider 一个自定义TraceListener,它会抛出并配置记录应用程序块错误的特殊源以使用这个新的TraceListener

    基本上,自定义TraceListener 将与错误特殊源一起使用,并在尝试登录标准TraceListeners(或其他一些错误,但这是典型的错误场景)发生错误时调用。自定义TraceListener 将抛出,这将导致调用 LoggingInstrumentationProvider.FireFailureLoggingErrorEvent 方法,该方法将抛出导致异常冒泡。 (是的,还有很长的路要走!)

    首先创建一个自定义LoggingInstrumentationProvider

    [HasInstallableResourcesAttribute]
    [PerformanceCountersDefinition(counterCategoryName, "LoggingCountersHelpResource")]
    [EventLogDefinition("Application", "Enterprise Library Logging")]
    public class MyLoggingInstrumentationProvider : InstrumentationListener, ILoggingInstrumentationProvider
    {
        static EnterpriseLibraryPerformanceCounterFactory factory = new EnterpriseLibraryPerformanceCounterFactory();
        private const string TotalLoggingEventsRaised = "Total Logging Events Raised";
        private const string TotalTraceListenerEntriesWritten = "Total Trace Listener Entries Written";
    
        [PerformanceCounter("Logging Events Raised/sec", "LoggingEventRaisedHelpResource", PerformanceCounterType.RateOfCountsPerSecond32)]
        private EnterpriseLibraryPerformanceCounter logEventRaised;
    
        [PerformanceCounter(TotalLoggingEventsRaised, "TotalLoggingEventsRaisedHelpResource", PerformanceCounterType.NumberOfItems32)]
        private EnterpriseLibraryPerformanceCounter totalLoggingEventsRaised;
    
        [PerformanceCounter("Trace Listener Entries Written/sec", "TraceListenerEntryWrittenHelpResource", PerformanceCounterType.RateOfCountsPerSecond32)]
        private EnterpriseLibraryPerformanceCounter traceListenerEntryWritten;
    
        [PerformanceCounter(TotalTraceListenerEntriesWritten, "TotalTraceListenerEntriesWrittenHelpResource", PerformanceCounterType.NumberOfItems32)]
        private EnterpriseLibraryPerformanceCounter totalTraceListenerEntriesWritten;
    
        private const string counterCategoryName = "Enterprise Library Logging Counters";
        private IEventLogEntryFormatter eventLogEntryFormatter;
    
        /// <summary>
        /// Initializes a new instance of the <see cref="LoggingInstrumentationProvider"/> class.
        /// </summary>
        /// <param name="performanceCountersEnabled"><code>true</code> if performance counters should be updated.</param>
        /// <param name="eventLoggingEnabled"><code>true</code> if event log entries should be written.</param>
        /// <param name="applicationInstanceName">The application instance name.</param>
        public MyLoggingInstrumentationProvider(bool performanceCountersEnabled,
                                              bool eventLoggingEnabled,
                                              string applicationInstanceName)
            : base(performanceCountersEnabled, eventLoggingEnabled, new AppDomainNameFormatter(applicationInstanceName))
        {
            this.eventLogEntryFormatter = new EventLogEntryFormatter("Enterprise Library Logging Application Block");//Resources.BlockName);
        }
    
        /// <summary>
        /// Initializes a new instance of the <see cref="LoggingInstrumentationProvider"/> class.
        /// </summary>
        /// <param name="instanceName">The instance name.</param>
        /// <param name="performanceCountersEnabled"><code>true</code> if performance counters should be updated.</param>
        /// <param name="eventLoggingEnabled"><code>true</code> if event log entries should be written.</param>
        /// <param name="applicationInstanceName">The application instance name.</param>
        public MyLoggingInstrumentationProvider(string instanceName,
                                              bool performanceCountersEnabled,
                                              bool eventLoggingEnabled,
                                              string applicationInstanceName)
            : base(instanceName, performanceCountersEnabled, eventLoggingEnabled, new AppDomainNameFormatter(applicationInstanceName))
        {
            this.eventLogEntryFormatter = new EventLogEntryFormatter("Enterprise Library Logging Application Block");//Resources.BlockName);
        }
    
        /// <summary>
        /// Fires the <see cref="LoggingInstrumentationProvider.traceListenerEntryWritten"/> event.
        /// </summary>
        public void FireTraceListenerEntryWrittenEvent()
        {
            if (PerformanceCountersEnabled)
            {
                traceListenerEntryWritten.Increment();
                totalTraceListenerEntriesWritten.Increment();
            }
        }
    
        ///<summary>
        ///
        ///</summary>
        /// <param name="exception">The exception that describes the reconfiguration error.</param>
        public void FireReconfigurationErrorEvent(Exception exception)
        {
            if (exception == null) throw new ArgumentNullException("exception");
            if (EventLoggingEnabled)
            {
                string entryText = eventLogEntryFormatter.GetEntryText("An unknown error occurred reconfiguring the Logging Application Block. Reconfiguration will not take place."
                //Resources.ReconfigurationFailure
                , exception);
                EventLog.WriteEntry(GetEventSourceName(), entryText, EventLogEntryType.Error);
            }
        }
    
        /// <summary>
        /// </summary>
        /// <param name="message">A message describing the failure.</param>
        /// <param name="exception">The exception that caused the failure..</param>
        public void FireFailureLoggingErrorEvent(string message, Exception exception)
        {
            if (exception == null) throw new ArgumentNullException("exception");
            if (EventLoggingEnabled)
            {
                string entryText = eventLogEntryFormatter.GetEntryText(message, exception);
    
                EventLog.WriteEntry(GetEventSourceName(), entryText, EventLogEntryType.Error);
            }
    
            // New Code to throw an exception
            throw exception;
        }
    
        /// <summary>
        /// </summary>
        /// <param name="configurationException">The exception that describes the configuration error.</param>
        public void FireConfigurationFailureEvent(Exception configurationException)
        {
            if (configurationException == null) throw new ArgumentNullException("configurationException");
            if (EventLoggingEnabled)
            {
                string entryText = eventLogEntryFormatter.GetEntryText("The error occurred while refreshing the logging configuration. The configuration will not be updated."
                //Resources.ConfigurationFailureUpdating
                , configurationException);
                EventLog.WriteEntry(GetEventSourceName(), entryText, EventLogEntryType.Error);
            }
        }
    
        /// <summary>
        /// Fires the <see cref="LoggingInstrumentationProvider.logEventRaised"/> event.
        /// </summary>
        public void FireLogEventRaised()
        {
            if (PerformanceCountersEnabled)
            {
                logEventRaised.Increment();
                totalLoggingEventsRaised.Increment();
            }
        }
    
        /// <summary/>
        /// <param name="message"></param>
        public void FireLockAcquisitionError(string message)
        {
            if (EventLoggingEnabled)
            {
                string entryText = eventLogEntryFormatter.GetEntryText(message);
                EventLog.WriteEntry(GetEventSourceName(), entryText, EventLogEntryType.Error);
            }
        }
    
        /// <summary>
        /// Creates the performance counters to instrument the logging events to the instance names.
        /// </summary>
        /// <param name="instanceNames">The instance names for the performance counters.</param>
        protected override void CreatePerformanceCounters(string[] instanceNames)
        {
            logEventRaised = factory.CreateCounter(counterCategoryName, "Logging Events Raised/sec", instanceNames);
            traceListenerEntryWritten = factory.CreateCounter(counterCategoryName, "Trace Listener Entries Written/sec", instanceNames);
            totalLoggingEventsRaised = factory.CreateCounter(counterCategoryName, TotalLoggingEventsRaised, instanceNames);
            totalTraceListenerEntriesWritten = factory.CreateCounter(counterCategoryName, TotalTraceListenerEntriesWritten, instanceNames);
        }
    }
    

    企业库版本的唯一功能变化是在FireFailureLoggingErrorEvent 方法中添加了throw exception

    接下来我们需要创建一个TraceListener来抛出异常:

    [ConfigurationElementType(typeof(CustomTraceListenerData))]
    public class ExceptionThrowingTraceListener : CustomTraceListener
    {
        private void Throw()
        {
            throw new Exception("An Error occurred logging.  Check error logs.");
        }
    
        public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, object data)
        {
            Throw();
        }
    
        public override void Write(string message)
        {
            Throw();
        }
    
        public override void WriteLine(string message)
        {
            Throw();
        }
    }
    

    接下来我们需要在启动时配置企业库以使用我们新的LoggingInstrumentationProvider(我直接访问底层的UnityContainer):

    IUnityContainer container = new UnityContainer();
    
    container.AddNewExtension<EnterpriseLibraryCoreExtension>();
    
    var instrumentationSection =
        ConfigurationManager.GetSection(InstrumentationConfigurationSection.SectionName)
        as InstrumentationConfigurationSection;
    
    ILoggingInstrumentationProvider provider = null;
    
    if (instrumentationSection != null)
    {
        provider = new MyLoggingInstrumentationProvider(
            instrumentationSection.PerformanceCountersEnabled,
            instrumentationSection.EventLoggingEnabled,
            instrumentationSection.ApplicationInstanceName);
    }
    else
    {
        provider = new MyLoggingInstrumentationProvider(false, false, "DefaultApplication");
    }
    
    container.RegisterInstance<ILoggingInstrumentationProvider>(provider);
    
    EnterpriseLibraryContainer.Current = new UnityServiceLocator(container);
    

    然后我们可以配置错误特殊源来记录我们的失败然后抛出异常:

    <?xml version="1.0"?>
    <configuration>
      <configSections>
        <section name="instrumentationConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Common.Instrumentation.Configuration.InstrumentationConfigurationSection, Microsoft.Practices.EnterpriseLibrary.Common, Version=5.0.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" />
        <section name="loggingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.LoggingSettings, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" />
      </configSections>
      <instrumentationConfiguration performanceCountersEnabled="false"
          eventLoggingEnabled="false" applicationInstanceName="TestApp" />
      <loggingConfiguration name="" tracingEnabled="true" defaultCategory="General">
        <listeners>
          <!-- Using DB Trace Listener because it's easy to make it fail -->
          <add name="Database Trace Listener" type="Microsoft.Practices.EnterpriseLibrary.Logging.Database.FormattedDatabaseTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging.Database, Version=5.0.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
              listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Database.Configuration.FormattedDatabaseTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging.Database, Version=5.0.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
              databaseInstanceName="Database Connection String" writeLogStoredProcName="WriteLog"
              addCategoryStoredProcName="AddCategory" />
          <add name="Flat File Trace Listener" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.FlatFileTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
              listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.FlatFileTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
              fileName="error.log" />
          <add listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.CustomTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
              type="ConsoleApplication28.ExceptionThrowingTraceListener, ConsoleApplication28, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
              name="ExceptionThrowingTraceListener" />
        </listeners>
        <formatters />
        <categorySources>
          <add switchValue="All" name="General">
            <listeners>
              <add name="Database Trace Listener" />
            </listeners>
          </add>
        </categorySources>
        <specialSources>
          <allEvents switchValue="All" name="All Events" />
          <notProcessed switchValue="All" name="Unprocessed Category" />
          <errors switchValue="All" name="Logging Errors &amp; Warnings">
            <listeners>
              <add name="Flat File Trace Listener" />
              <add name="ExceptionThrowingTraceListener" />
            </listeners>
          </errors>
        </specialSources>
      </loggingConfiguration>
      <connectionStrings>
        <add name="Database Connection String" connectionString="database=mydb"
            providerName="System.Data.SqlClient" />
      </connectionStrings>
      <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
      </startup>
    </configuration>
    

    现在,当我们运行我们的测试代码时,我们将得到一个异常(假设配置中的数据库跟踪侦听器失败):

    LogWriter logWriter = EnterpriseLibraryContainer.Current.GetInstance<LogWriter>();
    
    try
    {
        logWriter.Write("Test", "General");
    }
    catch (Exception e)
    {
        Console.WriteLine("LogWriter.Write() threw an exception: " + e);
    }
    

    现在,要抛出异常还有很长的路要走。还是硬着头皮直接修改企业库源代码,添加一个 throw 语句可能会更好。

    【讨论】:

    • 非常感谢。你知道,如果 entlib 6 处理得更好?
    • 不,EntLib 6 也吞下异常。实际上,这种方法不适用于 EntLib 6,因为它不支持检测。对于 EntLib6,我认为您需要修改要抛出的源。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-21
    • 2010-09-05
    • 2010-09-26
    • 2011-08-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多