如何在C#中获取所有Windows事件日志(事件查看器日志)的列表及其层次结构和友好名称

8

我正在尝试从事件查看器中复制以下内容:

enter image description here

我有几个问题遇到了困难。

  1. Some of the names I get back are not the display names or friendly names. For example, for "Microsoft Office Alerts" I just get back "OAlerts". How can I get the full "Microsoft Office Alerts" from "OAlerts"?
  2. Figuring out the hierarchy. It seems all I can do is parse out the dashes and do some sort of a best guess. There doesn't seem to be an easy way in the API to figure it out. The GetLogNames just gives you a flat list of all the logs

    EventLogSession session = new EventLogSession();
    List<string> logNames = new List<string>(session.GetLogNames());
    foreach (string name in logNames)
    {
        //EventLogConfiguration config = new EventLogConfiguration(name); //looks useful but doesn't give me any of the info i'm looking for.
    
       Console.WriteLine(name);
    }  
    
1个回答

7
这篇博客:The EventSource NuGet package and support for the Windows Event Log (Channel Support) 中有一个链接指向一份罕见的EventSource用户指南,其中提到:

使用EventSourceAttribute的Name属性为你的事件源提供一个描述性的、限定名称,以表示你的ETW事件提供程序。默认情况下,它是你的事件源类型的短名称,这很容易导致冲突,因为ETW提供程序名称共享一个机器范围的命名空间。一个好的提供程序名称示例是“<公司名称>-<产品名称>-<组件名称>”。遵循这个三元素约定将确保事件查看器在逻辑文件夹层次结构中显示你的事件日志:“应用程序和服务日志/<公司名称>/<产品名称>/<组件名称>”。

这似乎表明破折号更多是一种惯例而不是严格的要求(所以我认为你可以自己解析它)。请注意,该博客仍然开放评论。
至于名称不匹配的情况,有一个未记录在案的函数可以获取事件查看器中显示的名称。以下是如何使用它与会话和日志名称:
    static void Main(string[] args)
    {
        var session = new EventLogSession();
        foreach (string name in session.GetLogNames())
        {
            Console.WriteLine(GetDisplayName(session, name));
        }
    }

以下是支持代码(由于未经记录,使用风险自负,而且它似乎主要对这个“OAlert”条目有用,因此我不确定它是否值得):

    public static string GetDisplayName(EventLogSession session, string logName)
    {
        var sb = new StringBuilder(512);
        int bufferUsed = 0;
        if (EvtIntGetClassicLogDisplayName(GetSessionHandle(session).DangerousGetHandle(), logName, 0, 0, sb.Capacity, sb, out bufferUsed))
            return sb.ToString();

        return logName;
    }

    private static SafeHandle GetSessionHandle(EventLogSession session)
    {
        return (SafeHandle)session.GetType().GetProperty("Handle", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(session);
    }

    [DllImport("wevtapi.dll", CharSet = CharSet.Unicode)]
    private static extern bool EvtIntGetClassicLogDisplayName(IntPtr session, [MarshalAs(UnmanagedType.LPWStr)] string logName, int locale, int flags, int bufferSize, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder displayName, out int bufferUsed);

谢谢Simon,你的回答太棒了。GetDisplayName很好用(虽然使用DangerousGetHandle令人担忧——我想知道这是否会通过代码审查:)。但是命名约定似乎无法正确实现。即使在我添加的上面的截图中,Microsoft-SQLServerDataTools也未被放置在Microsoft下面。此外,当我调用GetLogNames()时,我看到Microsoft-IIS-Configuration没有出现在上面的屏幕截图中的Microsoft下。我认为可能还有另一个像你发现的“隐藏”API。 - Mark
类似的例子可以是 Microsoft-Rdms-UI 日志。 - Mark
1
Microsoft-SQLServerDataTools 只有两个部分,而不是三个元素的规范(微软的某个家伙没有看文档...)。此外,如果您检查 EventViewer 菜单中的“View/Show Analytic and Debug Logs”,会出现许多东西。这些日志标记为“Diagnostic”或“Analytic”。至于 Microsoft-IIS-Configuration,则是与提供程序“Microsoft-Windows-IIS-Configuration”相关联的日志(您可以使用 new ProviderMetadata("Microsoft-Windows-IIS-Configuration", session, null).LogLinks 发现该链接)。日志和其提供程序之间的命名空间并不总是一致的... - Simon Mourier

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接