Question

Do you know how we can get errors from enterprise library 5 logging block?

As I know, logging's philosophy is to be non disrupting, so if there is an error in configuration for example, we will not get the error thrown.

But in our application, logging is so critical, that we need to have application out of order, than running without logs.

So we need to configure it so, it will throw an error if there is any.

I've found something about events here: Enterprise Library Logging Application Block options

So we should be able to listen to this event, and throw our own error at least, but I was not able to get the reference to the logging instrumentation, because, I cannot see the GetInstrumentationEventProvider() method in following code.

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

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

Thank you for your help.

Was it helpful?

Solution

I think that the LoggingInstrumentationProvider definition changed between version 4 and 5 to remove the events so the posted approach won't work for Enterprise Library 5.

There are a few ways to change the behavior so that Enterprise Library will not swallow exceptions:

  • Modify Enterprise Library source code to implement the behavior you want
  • Duplicate the LogWriterImpl/LogSource code and inject into the container (this approach can be seen in Enterprise Library Logging Extensions – Part 1
  • Replace the current LoggingInstrumentationProvider implementation with one that throws

I'm going to demonstrate the last approach (not because it's the best but because it's the most interesting). So for this approach to work we will need to create a custom LoggingInstrumentationProvider a custom TraceListener that throws and configure the Logging Application Block errors special source to use this new TraceListener.

Basically, the custom TraceListener will be used with the error special source and invoked when an error occurs trying to log to the standard TraceListeners (or some other error but that is the typical error scenario). The custom TraceListener will throw which will cause the LoggingInstrumentationProvider.FireFailureLoggingErrorEvent method to be invoked which will throw causing the exception to bubble up. (Yes, it's a long way to go!)

First create a custom 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);
    }
}

The only functional change from the Enterprise Library version is the addition of throw exception in the FireFailureLoggingErrorEvent method.

Next we need to create a TraceListener to throw the exception:

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

Next we need to configure Enterprise Library at startup to use our new LoggingInstrumentationProvider (I'm accessing the underlying UnityContainer directly):

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

Then we can configure the errors special source to log our failure and then throw the exception:

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

Now when we run our test code we will get an exception (assuming that the Database Trace Listener in the config fails):

LogWriter logWriter = EnterpriseLibraryContainer.Current.GetInstance<LogWriter>();

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

Now, that's a long way to go to get exceptions throwing. It might be better to bite the bullet and modify the Enterprise Library source code directly by adding a throw statement.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top