我认为 LoggingInstrumentationProvider 定义在版本 4 和 5 之间更改以删除事件,因此发布的方法不适用于 Enterprise Library 5。
有几种方法可以更改行为,以便企业库不会吞下异常:
我将演示最后一种方法(不是因为它是最好的,而是因为它是最有趣的)。因此,要使这种方法起作用,我们将需要创建一个自定义LoggingInstrumentationProvider
的自定义TraceListener
,它会抛出并配置 Logging Application Block 错误特殊源以使用这个新的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);
}
}
与 Enterprise Library 版本相比,唯一的功能变化是添加throw exception
了FireFailureLoggingErrorEvent
方法。
接下来我们需要创建一个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();
}
}
接下来我们需要在启动时配置 Enterprise Library 以使用我们新的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 & 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 语句。