NetCore Tutorials - Adding EventViewer Logging To GlobalException Handlers

This is how to incorporate the two most common global exception handlers and utilize EventViewer.

See the prevoius guide that demonstrates the logic behind global exception handler wiring.

  1. Create a Program.cs that will handle the two main global exceptions that can occur.

Getting Started

NuGets

The main package we need here is Microsoft.Extensions.PlatformAbstractions. Its the NetCore interface to utilize EventViewer on Windows systems.

EventHandlers wired up to Events

using System;
using System.Diagnostics;
using System.Threading.Tasks;

namespace NetCore.GlobalExceptionHandler
{
    public static class Program
    {
        private static string DefaultErrorMessage { get; set; } = "Unhandled Error Occurred. No details are known.";

        public static void Main(string[] args)
        {
            // Exception Handling Wiring
            AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler;
            TaskScheduler.UnobservedTaskException += UnobservedTaskExceptionHandler;

            LogMessageToEventViewer("Application has started."); // new

            Console.ReadLine();
        }

        #region Helpers

        private static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs e)
        {

        }

        private static void UnobservedTaskExceptionHandler(object sender, UnobservedTaskExceptionEventArgs e)
        {

        }

        #endregion
    }
}

We modified the previous example to use this yet unwritten LogMessageToEventViewer() function.

UnhandledExceptionHandler

private static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs e)
{
    try
    {
        var message = DefaultErrorMessage;

        if (e.ExceptionObject != null && e.ExceptionObject is Exception uex)
        {
            message = string.Format("{0} Exception: {1}", DefaultErrorMessage, uex.Message);
            LogExceptionToEventViewer(uex); // new
        }
        else if (sender is Exception ex)
        {
            message = string.Format("{0} Exception: {1}", DefaultErrorMessage, ex.Message);
            LogExceptionToEventViewer(ex); // new
        }

        Trace.Write(message);
    }
    catch { } // Swallow exception
}

We added a log exception function so that we can write to EventViewer fairly cleanly.

UnobservedTaskExceptionHandler

private static void UnobservedTaskExceptionHandler(object sender, UnobservedTaskExceptionEventArgs e)
{
    try
    {
        var message = DefaultErrorMessage;
        e?.SetObserved(); // Prevents the Program from terminating.

        if (e.Exception != null && e.Exception is Exception tuex)
        {
            message = string.Format("{0} Exception: {1}", DefaultErrorMessage, tuex.Message);
            LogExceptionToEventViewer(tuex); // new
        }
        else if (sender is Exception ex)
        {
            message = string.Format("{0} Exception: {1}", DefaultErrorMessage, ex.Message);
            LogExceptionToEventViewer(ex); // new
        }

        Trace.Write(message);
    }
    catch { } // Swallow exception
}

Same modification from Unahere.

EventViewer Helper Methods

private const string SourceName = "ApplicationName";
private const string LogName = "Application";

public static void LogExceptionToEventViewer(Exception ex)
{
    if (!EventLog.SourceExists(SourceName))
    { EventLog.CreateEventSource(SourceName, LogName); }

    var eventLogger = new EventLog(LogName)
    {
        Source = SourceName
    };

    if (ex is AggregateException)
    { (ex as AggregateException)?.Flatten(); }

    eventLogger?.WriteEntry($"Message: {ex.Message}\n\rStackTrace: {ex.StackTrace}", EventLogEntryType.Error);
}

public static void LogMessageToEventViewer(string message)
{
    if (!EventLog.SourceExists(SourceName))
    { EventLog.CreateEventSource(SourceName, LogName); }

    var eventLogger = new EventLog(LogName)
    {
        Source = SourceName
    };

    eventLogger?.WriteEntry(message, EventLogEntryType.Information);
}

We always do a quick check to ensure we have en entry in the ApplicationLog before writing to it.

Everything Together In Program.cs

using System;
using System.Diagnostics;
using System.Threading.Tasks;

namespace NetCore.GlobalExceptionHandler
{
    public static class Program
    {
        private static string DefaultErrorMessage { get; set; } = "Unhandled Error Occurred. No details are known.";
        private const string SourceName = "ApplicationName";
        private const string LogName = "Application";

        public static void Main(string[] args)
        {
            // Exception Handling Wiring
            AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionHandler;
            TaskScheduler.UnobservedTaskException += UnobservedTaskExceptionHandler;

            LogMessageToEventViewer("Application has started.");

            Console.ReadLine();
        }

        #region Helpers

        // Cleanup
        private static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs e)
        {
            try
            {
                if (e.ExceptionObject != null && e.ExceptionObject is Exception uex)
                {
                    Trace.WriteLine(string.Format("{0} Exception: {1}", DefaultErrorMessage, uex.Message));
                    LogExceptionToEventViewer(uex);
                }
                else if (sender is Exception ex)
                {
                    Trace.WriteLine(string.Format("{0} Exception: {1}", DefaultErrorMessage, ex.Message));
                    LogExceptionToEventViewer(ex);
                }
                else { Trace.WriteLine(DefaultErrorMessage); }
            }
            catch { } // Swallow exception
        }

        // Cleanup
        private static void UnobservedTaskExceptionHandler(object sender, UnobservedTaskExceptionEventArgs e)
        {
            try
            {
                e?.SetObserved(); // Prevents the Program from terminating.

                if (e.Exception != null && e.Exception is Exception tuex)
                {
                    Trace.WriteLine(string.Format("{0} Exception: {1}", DefaultErrorMessage, tuex.Message));
                    LogExceptionToEventViewer(tuex);
                }
                else if (sender is Exception ex)
                {
                    Trace.WriteLine(string.Format("{0} Exception: {1}", DefaultErrorMessage, ex.Message));
                    LogExceptionToEventViewer(ex);
                }
                else { Trace.WriteLine(DefaultErrorMessage); }
            }
            catch { } // Swallow exception
        }

        // Cleanup
        public static void LogExceptionToEventViewer(Exception ex)
        {
            if (!EventLog.SourceExists(SourceName))
            { EventLog.CreateEventSource(SourceName, LogName); }

            var eventLogger = new EventLog(LogName)
            {
                Source = SourceName
            };

            if (ex is AggregateException)
            { (ex as AggregateException)?.Flatten(); }

            eventLogger?.WriteEntry($"Message: {ex.Message}\n\rStackTrace: {ex.StackTrace}", EventLogEntryType.Error);
        }

        // Cleanup
        public static void LogMessageToEventViewer(string message)
        {
            if (!EventLog.SourceExists(SourceName))
            { EventLog.CreateEventSource(SourceName, LogName); }

            var eventLogger = new EventLog(LogName)
            {
                Source = SourceName
            };

            eventLogger?.WriteEntry(message, EventLogEntryType.Information);
        }

        #endregion
    }
}

That is pretty much all there is to it.