Search code examples
c#loggingevent-logserilogevent-viewer

Dynamically set EventID when logging to Event Viewer using Serilog


Currently, I have set up my logger to log to the event viewer like so:

 Log.Logger = new LoggerConfiguration()
               .MinimumLevel.Information()
               .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
               .Enrich.FromLogContext()
               .WriteTo.EventLog("MySource", "EventViewerArea")
               .CreateLogger();

When I log I use the following command:

_logger.LogWarning(logText);

It looks as though I can pass in the EventId to the LogWarning method, so I wanted to set it when running the LogWarning/LogInformation methods:

_logger.LogWarning(9876, logText);

But this does not override the Event ID in the entry in the Event Viewer. Any ideas on how I can dynamically set this EventId when logging? I need to have this dynamic and not set to one value when instantiating the logger.

Thanks in advance


Solution

  • I was able to solve this with the following steps:

    Add the following when instantiating the logger:

    .WriteTo.EventLog("SourceInEventViewer", 
        "AreaInEventViewer", 
        formatProvider: new EventLogFormatProvider(),
        eventIdProvider: new EventIdProvider(),
        manageEventSource: true,
        restrictedToMinimumLevel: LogEventLevel.Information)
    

    Add the following class to the project

    using Newtonsoft.Json.Linq;
    using Serilog.Events;
    using Serilog.Sinks.EventLog;
    using System;
    using System.Linq;
    
    namespace IndependentFile.Extensions
    {
        public class LoggerSetupExtensions
        {
            public class EventLogFormatProvider : IFormatProvider, ICustomFormatter
            {
                public object GetFormat(Type formatType)
                {
                    return formatType == typeof(ICustomFormatter) ? this : null;
                }
    
                public string Format(string format, object arg, IFormatProvider formatProvider)
                {
                    return arg.ToString();
                }
            }       
    
            public class EventIdProvider : IEventIdProvider
            {
                public ushort ComputeEventId(LogEvent logEvent)
                {
                    var eventTypeProp = logEvent.Properties.FirstOrDefault(prop => prop.Key == "EventId");
    
                    if (eventTypeProp.Value == null)
                    {
                        return (ushort)LogValuesEnum.Unknown;
                    }
                    try
                    {
                        var val = eventTypeProp.Value;
                        string eventType = eventTypeProp.Value.ToString();
    
                        //this is not the right way to parse the logEventPropertyValue
                        var parseEventType = JObject.Parse(eventType);
    
                        var eventIdInt = parseEventType["Id"].ToString();
    
                        if (eventType == null) return (int)LogValuesEnum.Unknown;
    
                        var tryParseEventId = Enum.TryParse<LogValuesEnum>(eventIdInt, ignoreCase: true, out var res);
                        if (tryParseEventId)
                        {
                            return (ushort)res;
                        }
    
                        return (ushort)LogValuesEnum.Unknown;
                    }
                    catch(Exception exc)
                    {
                        return (ushort)LogValuesEnum.Unknown;
                    }
    
                }
            }
        }
    }
    

    Now you can use the log and pass your event id to that:

    _logger.LogInformation(LogValuesEnum.MyEnumVal, "My log message");