使用 Serilog 登录到事件查看器时动态设置 EventID

Dynamically set EventID when logging to Event Viewer using Serilog

目前,我已经将我的记录器设置为像这样记录到事件查看器:

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

当我登录时,我使用以下命令:

_logger.LogWarning(logText);

看起来好像我可以将 EventId 传递给 LogWarning 方法,所以我想在 运行 LogWarning/LogInformation 方法时设置它:

_logger.LogWarning(9876, logText);

但这不会覆盖事件查看器条目中的事件 ID。关于如何在记录时动态设置此 EventId 的任何想法?在实例化记录器时,我需要有这个动态而不是设置为一个值。

提前致谢

如果您查看 event log sink 的源代码,您会发现它使用 EventIdHashProvider 通过散列事件消息来生成唯一 ID。

您可以做的是在配置接收器时提供 IEventIdProvider 接口的实现,如下所示(这是 eventIdProvider: new CustomEventIdProvider() 添加的):

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

.LogWarnning() 来自 Microsoft.Extensions.Logging。它实际上接受 EventId strcut,它具有来自 int.
的隐式转换 但是正如您所看到的,这个 id 从未在接收器中使用过。 EventId 用于轻松跟踪事件的身份,但我不确定是否有任何接收器正在重复使用此 ID ... EventLog 接收器没有。

我能够通过以下步骤解决此问题:

在实例化记录器时添加以下内容:

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

将以下class添加到项目

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

            }
        }
    }
}

现在您可以使用日志并将您的事件 ID 传递给它:

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