/ Windows / 20浏览

如何通过 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 事件

【译】ETW 堆跟踪 – 每个分配都被记录
【译】ETW 堆跟踪 – 每个分配都被记录
【译】Wait Analysis – 寻找空闲时间
【译】Wait Analysis – 寻找空闲时间
如何通过 C++ 实时监听 ETW 事件
如何通过 C++ 实时监听 ETW 事件
【译】调查并确定 Windows 运行速度变慢问题
【译】调查并确定 Windows 运行速度变慢问题
【译】丢失的 WPA 文档 —— 磁盘使用
【译】丢失的 WPA 文档 —— 磁盘使用
【译】丢失的 WPA 文档 —— CPU 调度
【译】丢失的 WPA 文档 —— CPU 调度

0

  1. This post has no comment yet

发表回复

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