查看源代码 教程

可视化消息序列图

使用 ET 最简单的方法是将其用作显示消息序列图的图形工具。为此,您需要首先启动一个 Viewer(默认情况下会启动一个 Collector)。

      {ok, ViewerPid} = et_viewer:start([{title,"Coffee Order"}]),
      CollectorPid = et_viewer:get_collector_pid(ViewerPid).

然后,您可以使用函数 et_collector:report_event/6 将事件发送到 Collector,如下所示:

      et_collector:report_event(CollectorPid,85,from,to,message,extra_stuff).

Viewer 将自动从 Collector 拉取事件,并在屏幕上显示它们。

数字(在此示例中为 85)是一个从 1 到 100 的整数,用于指定消息的“详细级别”。数字越大,消息越重要。这提供了一种粗略的优先级过滤形式。

fromtomessage 参数的含义与它们的字面意思相同。fromtoViewer 中被可视化为“生命线”,消息从一个传递到另一个。如果 fromto 的值相同,则它会作为“操作”显示在生命线旁边。extra_stuff 值只是您附加的数据,当有人在 Viewer 窗口中实际单击操作或消息时,将显示这些数据。

模块 et/examples/et_display_demo.erl 说明了如何使用它


-module(et_display_demo).

-export([test/0]).

test() ->
    {ok, Viewer} = et_viewer:start([{title,"Coffee Order"}, {max_actors,10}]),
    Drink = {drink,iced_chai_latte},
    Size = {size,grande},
    Milk = {milk,whole},
    Flavor = {flavor,vanilla},
    C = et_viewer:get_collector_pid(Viewer),
    et_collector:report_event(C,99,customer,barrista1,place_order,[Drink,Size,Milk,Flavor]),
    et_collector:report_event(C,80,barrista1,register,enter_order,[Drink,Size,Flavor]),
    et_collector:report_event(C,80,register,barrista1,give_total,"$5"),
    et_collector:report_event(C,80,barrista1,barrista1,get_cup,[Drink,Size]),
    et_collector:report_event(C,80,barrista1,barrista2,give_cup,[]),
    et_collector:report_event(C,90,barrista1,customer,request_money,"$5"),
    et_collector:report_event(C,90,customer,barrista1,pay_money,"$5"),
    et_collector:report_event(C,80,barrista2,barrista2,get_chai_mix,[]),
    et_collector:report_event(C,80,barrista2,barrista2,add_flavor,[Flavor]),
    et_collector:report_event(C,80,barrista2,barrista2,add_milk,[Milk]),
    et_collector:report_event(C,80,barrista2,barrista2,add_ice,[]),
    et_collector:report_event(C,80,barrista2,barrista2,swirl,[]),
    et_collector:report_event(C,80,barrista2,customer,give_tasty_beverage,[Drink,Size]),
    ok.

当您在上面的示例中运行 et_display_demo:test(). 函数时,Viewer 窗口将如下所示:

Screenshot of the Viewer window

四个模块

事件跟踪器框架由四个模块组成:

  • et
  • et_collector
  • et_viewer
  • et_selector

此外,您可能还需要熟悉 dbg 模块,以及可能的 seq_trace 模块。

事件跟踪器接口

et 模块与其他模块不同。它包含一个名为 et:trace_me/5 的函数。该函数本身没有任何实际作用。它的唯一目的是成为一个易于跟踪的函数。对它的调用可能如下所示:

      et:trace_me(85,from,to,message,extra_stuff).

et:trace_me/5 的参数与上一章中的 et_collector:report_event/6 的参数相同。两者之间最大的区别在于这两个函数的语义。第二个函数实际上向 Collector 报告一个 Event,而第一个函数什么也不做,它只是返回原子 hopefully_traced。为了使 et:trace_me/5 的参数出现在 Collector 中,必须激活该函数的跟踪,并且必须将 Collector 注册为 Raw Trace DataTracer

Erlang 跟踪是一堆令人头疼的事情,它涉及对巧妙端口、跟踪返回格式和专门的跟踪 MatchSpecs(它们实际上是它们自己特殊的地狱)的相当复杂的知识。跟踪机制确实非常强大,但可能很难掌握。

幸运的是,有一种简化的方法可以开始跟踪 et:trace_me/5 函数调用。其思想是,您应该在代码中战略性地使用对 et:trace_me/5 的调用,其中您的程序中有可用的有趣信息。然后,您只需启用全局跟踪来启动 Collector

      et_viewer:start([{trace_global, true}, {trace_pattern, {et,max}}]).

这将启动一个 Collector、一个 Viewer,并开始跟踪 et:trace_me/5 函数调用。Raw Trace DataCollector 收集,其视图由 Viewer 显示在屏幕上。您可以通过实现自己的 Filter 函数并在 Viewer 中注册它们来定义您自己的数据“视图”。

收集器和查看器

这两个组件协同工作。基本上,Collector 接收 Raw Trace Data,并将其处理成 et 特定格式(在 et/include/et.hrl 中定义)的 EventsViewer 查询 Collector 并显示数据的交互式表示。

您可能想知道为什么它们不只是一个模块。Collector 是一个通用的、功能完善的框架,它允许进程“订阅”其收集的 Events。一个 Collector 可以为多个 Viewers 提供服务。典型的情况是,您有一个 Viewer 以一种形式可视化 Events,而另一个 Viewer 以另一种形式可视化它们。例如,如果您正在跟踪基于文本的协议(如 HTML(或 Megaco/H.248)),则能够以纯文本以及消息的内部表示形式显示 Events 会很有用。只要符合 Collector/Viewer 协议之间的协议,该架构还允许您实现自己的 Viewer 程序。目前存在两种类型的 Viewers。即旧的基于 GSViewer 和新的基于 wxWidgetsViewer。但是如果您有兴趣,您可以实现自己的 Viewer,例如,它可以将 Events 显示为 ASCII 艺术或任何您觉得有用的东西。

Viewer 默认情况下会为您创建一个 Collector。通过一些选项和配置设置,您可以开始收集 Events

Collector API 还允许您将收集的 Events 保存到文件,并在以后的会话中加载它们。

选择器

这可能是整个 et 套件中最核心的模块。Collector 需要“过滤器”将 Raw Trace Data 转换为它可以显示的“事件”。et_selector 模块提供默认的 Filter 和一些用于管理 Trace Pattern 的 API 调用。Selector 提供了实现以下功能的各种函数:

  • Raw Trace Data 转换为适当的 Event
  • 神奇地注意到 et:trace_me/5 函数的跟踪,并创建适当的 Events
  • 小心地防止两次转换 Raw Trace Data
  • 管理 Trace Pattern

Trace Pattern 基本上是 moduledetail level (整数或原子 max 表示完整详细信息)的元组。在大多数情况下,Trace Pattern {et,max} 就足够了。但是,如果您不希望 et 有任何运行时依赖项,您可以在某些模块中实现自己的 trace_me/5 函数,并在 Trace Pattern 中引用该模块。

您对 Viewer 的实例化指定的模块会传递到它自动创建的 Collector,作为 Trace Pattern 存储,并最终进入 Selector 的内部。

您指定的模块(最终)会传递到 Selector 的默认 Filter 中。et:trace_me/5 函数调用的格式在该 Filter 中是硬编码的。

如何将它们组合在一起

Collector 会自动注册以侦听跟踪 Events,因此您所要做的就是启用它们。

对于那些想要进行通用跟踪的人,请查阅 dbg 模块,了解如何跟踪您感兴趣的任何内容,并让它发挥其魔力。如果您只想让 et:trace_me/5 工作,请执行以下操作:

  1. 创建一个 Collector
  2. 创建一个 Viewer(它可以为您完成步骤 1)
  3. 启用并精简调试

模块 et/examples/et_trace_demo.erl 实现了这一点。


-module(et_trace_demo).

-export([test/0]).

test() ->
    et_viewer:start([
        {title,"Coffee Order"},
        {trace_global,true},
        {trace_pattern,{et,max}},
        {max_actors,10}
      ]),
      %% dbg:p(all,call),
      %% dbg:tpl(et, trace_me, 5, []),
      Drink = {drink,iced_chai_latte},
      Size = {size,grande},
      Milk = {milk,whole},
      Flavor = {flavor,vanilla},
      et:trace_me(99,customer,barrista1,place_order,[Drink,Size,Milk,Flavor]),
      et:trace_me(80,barrista1,register,enter_order,[Drink,Size,Flavor]),
      et:trace_me(80,register,barrista1,give_total,"$5"),
      et:trace_me(80,barrista1,barrista1,get_cup,[Drink,Size]),
      et:trace_me(80,barrista1,barrista2,give_cup,[]),
      et:trace_me(90,barrista1,customer,request_money,"$5"),
      et:trace_me(90,customer,barrista1,pay_money,"$5"),
      et:trace_me(80,barrista2,barrista2,get_chai_mix,[]),
      et:trace_me(80,barrista2,barrista2,add_flavor,[Flavor]),
      et:trace_me(80,barrista2,barrista2,add_milk,[Milk]),
      et:trace_me(80,barrista2,barrista2,add_ice,[]),
      et:trace_me(80,barrista2,barrista2,swirl,[]),
      et:trace_me(80,barrista2,customer,give_tasty_beverage,[Drink,Size]),
      ok.

回顾上述内容,最重要的几点是:

  • 启用全局跟踪
  • 设置 Trace Pattern
  • 告诉 dbg 跟踪函数调用
  • 明确告诉它跟踪 et:trace_me/5 函数

当您运行上面的 et_trace_demo:test() 函数时,Viewer 窗口将如下面这个屏幕截图所示:

Screenshot of the Viewer window