/ Windows / 113浏览

如何通过 ETW Provider 来记录应用日志

背景

最近在做一些性能优化的时候,发现很多应用经常会出现文件 I/O 爆高,但又没做啥事情。结果分析下来发现,都是在写日志。

于是就有了这么一篇,怎么利用 Windows 自己提供的 ETW 来记录应用日志。

通过 ETW 记录日志有什么好处

ETW(Event trace for Windows) 是 Windows 提供的一种事件跟踪机制,可以用来记录应用程序的运行时事件。

ETW Provider 提供的日志相比文件日志,主要有以下优点:

  • 方便扩展:日志采用结构化数据,方便第三方解析
  • 高性能、低开销:采用内核缓冲区,批量写入磁盘
  • 实时记录:支持实时事件流
  • 隐私性:采用二进制日志 + 资源 dll/exe 的方式,在不具备资源 dll 的前提下,无法解析

怎么做

作为普通应用程序,没法直接使用微软的标准库来做,这里会相对麻烦一点。但通过微软提供的一些工具,可以稍微提升一定的效率。

我们采取基于清单的方式来创建 provider。

  1. 创建清单文件
  2. 打开 ecmangen.exe

我们可以通过 SDK 中自带的图形化工具(ecmangen.exe)来生成清单文件。

如果你安装了 8.1 的 SDK,你可以在 C:\Program Files (x86)\Windows Kits\8.1\bin\x64\ 找到它。

如果你没安装,可以看下面这个:

打开它,会得到一个这样的应用:

然后就可以开始创建 provider 了。

  1. 创建 Provider

选择【Events Section】部分,右击,选择【New】-【Provider】

根据提示,填写好 NameSymbolGUID(可以自动生成一个)、Decoding file locations(这个可以后面再进行修改的)

如下图所示:

下一步,我们就要开始创建我们的 event 了。开始 Event 之前,我们需要先创建一些关键属性处理,以方便 Event 创建的时候进行选择。

  1. 创建基础属性

关于基础属性,详细介绍可以看:ETW:Windows 事件追踪

这里,我们需要创建或修改的有:

  • Channels:主要用来区分不同权限的事件。比如 Admin 为管理员需要关注的事件;而 Debug 则是开发人员需要关注的事件
  • Tasks:主要用来对事件进行分类的。Tasks 主要针对不同的任务或者场景。这个需要结合业务来拆分
  • Opcodes:也是用来对事件进行分类的。Opcodes 主要针对的具体的行为。比如创建进程等
  • Templates:用于辅助事件显示结构化信息的关键属性。给与字段的解析和定义。比如,在场景激活这个事件中,我有这么一个 template:

还有额外的一些属性可以按需使用。

  1. 创建 Event

关键就是创建 Event 了,这是对日志的直接表达。

需要定义 Symbol、EventId、Event Version(主要用于版本管理)和一些基础属性,这个需要根据业务定义来。

如下是场景激活事件的事件详情,供参考。

然后按需完成事件的增加。满足需求后,通过【File】-【Save/Save As】将 man 文件保存即可。

于是我们就得到了我们的 xxx.man 清单文件。

  1. 创建日志宏

创建完清单文件,接下来就可以通过清单文件生成宏信息,在代码中调用更方便。

这里需要用到 mc.exe,可以通过 man 文件来生成 .h.rc.bin文件。

mc.exe 是跟随 win10 的 SDK 带下来的。

frend in ~\Desktop\testt λ mc -um .\MyProvider.man
frend in ~\Desktop\testt λ ls

    Directory: C:\Users\frend\Desktop\testt

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-----           1/23/2025  4:28 PM            144 MSG00001.bin
-----           1/23/2025  4:28 PM          35741 MyProvider.h
-a---           12/5/2024 11:52 AM           4912 MyProvider.man
-----           1/23/2025  4:28 PM             77 MyProvider.rc
-----           1/23/2025  4:28 PM            970 MyProviderTEMP.BIN

于是,我们就可以创建 VS 项目,然后引入 .h 和 .rc 进行使用了。

  1. 创建项目

这里需要把 .h 和 .rc 和 bin 文件加到项目中。

然后,就可以开始写代码了。.h 中有了较多的宏,所以写起来也非常的方便。

直接放代码:

#include <iostream>
#include <windows.h>
#include <evntprov.h>

#include "MyProvider.h"

REGHANDLE g_ProviderHandle = NULL;

int main()
{
    ULONG status = EventRegisterProviderDemo();
    if (status != ERROR_SUCCESS) {
        std::cout << "Failed to register ETW provider. Error: " << status << std::endl;
        return status;
    }

    // 写入事件
    while (true) {
        if (EventEnabledScene_Active()) {
            // 准备参数
            const wchar_t* sceneName = L"MainScene";
            ULONG processId = GetCurrentProcessId();

            EventWriteScene_Active(sceneName, processId);
        }

        std::cout << "Event written successfully!\n";
        Sleep(1000);
    }

    // 程序结束前注销 provider
    EventUnregisterProviderDemo();

    return 0;
}

编译生成,即可将日志写到 ETW 中去了。

但这个时候,是没法看到这个 provider 和对应的 event 的。

这里建议将日志部分封装成一个 dll,将 bin 文件链接到 dll 中,用于解码 message

  1. 安装清单文件

由于截至到目前为止,我们还没有安装清单文件,所以系统中是没法查到这个 provider 的,也就没法获取日志了。

安装清单文件可以用 wevtutil来安装。

wevtutil im .\MyProvider.man

安装完成后,就可以看到了。可以通过 logman来获取:

frend in ~\Desktop\testt λ logman query providers "providerDemo"

Provider                                 GUID
-------------------------------------------------------------------------------
ProviderDemo                             {16676E5F-DFC8-4A42-9F1E-14CF280F7AFD}

Value               Keyword              Description
-------------------------------------------------------------------------------
0x8000000000000000  Debug
0x4000000000000000  Admin

Value               Level                Description
-------------------------------------------------------------------------------
0x04                win:Informational    Information


The command completed successfully.

怎么查看日志

记录了日志,接下来就可以看看怎么获取日志了。

  1. 实时观察

观察的话,可以通过 Microsoft Message Analyzer 实时观察。

如果在创建 provider 的时候,正确填写了 Decoding file locations,那应该直接在 Microsoft Message Analyzer 就可以看到了。

如果 dll/exe 存放的位置跟预定的 Decoding file locations 不一致,可以通过注册表来进行修改。

注册表地址:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\WINEVT\Publishers\<Provider GUID>,例如:

  1. 通过 C++ 获取

还可以通过接口来获取日志,这样方便编写日志工具来获取、分析日志。

具体怎么做可以看:如何通过 C++ 实时监听 ETW 事件

Windows是如何区分互联网下载文件和本地文件的
Windows是如何区分互联网下载文件和本地文件的
如何低成本的获取到应用卡顿情况
如何低成本的获取到应用卡顿情况
【译】ETW 堆跟踪 – 每个分配都被记录
【译】ETW 堆跟踪 – 每个分配都被记录
【译】Wait Analysis – 寻找空闲时间
【译】Wait Analysis – 寻找空闲时间
如何通过 C++ 实时监听 ETW 事件
如何通过 C++ 实时监听 ETW 事件
【译】调查并确定 Windows 运行速度变慢问题
【译】调查并确定 Windows 运行速度变慢问题

0

  1. This post has no comment yet

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注