分类 Codelab

贡献者:cphoenix@

此 Codelab 介绍了分类实用程序:

  • 用途。
  • 运行方式,包括命令行选项。
  • 如何添加和测试配置规则以检测 Fuchsia 快照中的问题。

如需查看本文档所提及的源文件和示例,请访问以下网址:

什么是分类?

通过分类,您可以扫描 Fuchsia 快照(snapshot.zip 文件)以检查预定义条件。

分类系统使您可以轻松配置新条件,从而提高分类功能的实用性。

前提条件

在开始学习此 Codelab 之前,请确保您已完成以下操作:

  • 开始使用
  • 使用 fx build 创建了一个 Fuchsia build。

从命令行运行分类

  • 如需运行分类,请执行以下操作:
fx triage

此命令会使用 fx snapshot 命令下载新的 snapshot.zip 文件。此命令会运行位于源代码树中的默认规则:

  • //src/diagnostics/config/triage/*.triage

如需分析特定 snapshot.zip 文件,请使用 --data

  • 您最多可以指定一个 --data 参数。
  • --data 的参数也可以是包含解压缩快照的目录的路径。
  • 如果您在未指定 --data 选项的情况下运行 fx triage,它会运行新的 fx snapshot 并分析其文件。
fx triage --data my/foo/snapshot.zip

如需使用特定配置文件或特定目录中的所有 *.triage 文件,请使用 --config

  • 您可以使用多个 --config 参数。
  • 如果使用了 --config 参数,则系统不会自动加载默认规则。
fx triage --config my/directory --config my/file.triage

添加分类规则

此 Codelab 的其余部分介绍了如何在 Triage 中配置新行为。

概览

分类功能通过以下步骤将大量诊断数据压缩为实用信息:

  1. 使用配置文件 select 部分中的 selector 字符串从 inspect.json 文件中选择值。
  2. 执行计算和比较以生成新值,如配置文件的 eval 部分指定。
  3. 根据配置文件 act 部分中的条目采取措施。
    1. 如果错误条件(布尔表达式)为 true,则发出警告。
    2. 向用户显示一个值。
  4. 支持通过 test 部分中的条目对操作和计算进行单元测试。

查找 Codelab 的示例文件

进入源代码树中的 examples/diagnostics/triage 目录。以下命令旨在从该目录运行:

fx triage --config . --data snapshot

在包含未经修改的 Codelab 文件的示例目录中运行此命令,会输出一行预先提供的操作:

Warning: 'always_triggered' in 'rules' detected 'Triage is running': 'always_true' was true

检查.json

此 Codelab 包含一个 inspect.json 文件,其中包含检查数据,以确保运动以可预测的方式运行。此文件位于 snapshot/inspect.json 下的示例目录中。

rules.triage

分类程序会使用从一个或多个 .triage 文件加载的配置。 此 Codelab 使用位于示例目录中的 rules.triage 文件。

.triage 文件使用 JSON5,后者比 JSON 更易于写入和读取:

  • 在最后一个列表项后添加英文逗号是一种不错的样式。
  • 大多数键(包括所有有效的分类名称)不需要用引号括起来。
  • /* 可以使用多行 */ 和 // 单行注释。

为 Inspect 值添加选择器

示例目录中的 inspect.json 文件表明系统存在一些问题。您将配置分类系统以检测这些问题。

此步骤会将分类配置为从 inspect.json 文件中的数据中提取值。

rules.triage 文件包含一个名为 select 的键值对部分。每个键(名称)都可以在其他配置条目的正文中使用。每个值都是一个选择器字符串。实际上,select 部分(以及如下所述的 eval 部分)中的每个条目都定义了一个变量。

选择器字符串是以英文冒号分隔的字符串,告知可以在检查数据中的哪个位置查找所需的值。

select: {
    disk_total: "INSPECT:bootstrap/fshost:root/data_stats/stats:total_bytes",
    // "data_stat" is an intentional typo to fix later in the codelab.
    disk_used: "INSPECT:bootstrap/fshost:root/data_stat/stats:used_bytes",
}

由组件发布的检查数据以节点树的形式进行组织,其值(属性)位于左边。inspect.json 文件是这些树的数组,其中每个树都有一个用于标识源组件的名称。

INSPECT: 和第二个冒号之间的选择器字符串部分应与 inspect.json 文件中的其中一个名字字符串匹配。

第二个和第三个冒号之间的部分是一系列以 / 分隔的节点名称。

最后一个冒号后面的部分是属性名称。

上述选择器字符串表示了一个组件,该组件的 moniker 与字符串 bootstrap/fshost 匹配,且其检查树包含路径 root/data_stat/stats。并且还指明了该组件的检查树的 root 节点的 stats 子节点中的 used_bytes 属性。

将上述选择器放入 rules.triage 文件的“选择”部分。

生成选择器

手动输入选择器很容易出错,因此可以使用 fx triage --select 来输出有效的选择器。

fx triage --data snapshot --select total_bytes
INSPECT:bootstrap/fshost:root/data_stats/stats:total_bytes

可以使用多个 --select 参数。该程序将为快照的诊断数据生成所有可能的选择器,然后通过所有 --select 参数进行过滤 (grep)。

添加计算

inspect.json 文件中选择值后,您需要执行一些逻辑(可能还需要进行一些算术),以查看这些值是否表示值得标记的条件。

复制以下行并将其添加到 rules.triage 文件的 eval 部分,以计算磁盘已满的容量:

eval: {
    ....
    disk_percentage: "disk_used / disk_total",
}

eval 条目使用普通中后缀数学表达式。如需了解详情,请参阅详细信息部分。

添加操作

在配置文件的“act”部分中,添加一个操作,用于在磁盘已满 98% 时输出警告。使用以下几行代码:

act: {
    ...
    disk_full: {
        type: "Warning",
        trigger: "disk_percentage > 0.98",
        print: "Disk reached 98% full",
    },
}

请注意以下几点:

  • “trigger”是一个计算结果为布尔值的表达式。这可以是布尔类型选择器或计算的名称,或者任何合适的数学表达式。
  • 如需详细了解比较,请参阅详细信息部分。
  • 如需了解其他受支持的操作,请参阅配置参考

添加刻度盘图

act: {
    ...
    disk_display: {
        type: "Gauge",
        value: "disk_percentage",
        format: "percentage",
    }
}

量表始终显示提供的值。

format 字段为可选字段。“percentage”值会告知输出格式化程序预期会获得介于 0 到 1 之间的值,并将其显示为百分比。

试试看

以下命令将针对本地配置文件运行分类。

fx triage --config . --data snapshot

您会收到一条类似如下所示的错误:

[ERROR] In config 'rules': No value found matching selector
bootstrap/fshost:root/data_stat/stats:used_bytes

选择器规则中存在拼写错误。分类无法找到评估规则所需的值。实际上,正确的选择器是“data_stats”,而不是“data_stat”。 请在选择器规则中修正此问题,然后重试。

fx triage --config . --data snapshot

现在发生了什么情况?什么都没有,对吧?那么,如何判断 inspect.json 文件中是不存在问题,还是规则存在 bug?

测试规则

您可以(并且应该!)为自己的操作添加测试。对于每个测试,请指定值,以及这些值是否应触发您的规则。

如需测试您添加的规则,请将以下代码添加到 rules.triage 文件的 test 部分:

test: {
    ....
    is_full: {
        yes: ["disk_full"],
        values: {
            disk_used: 98,
            disk_total: 100,
        }
    }
}

values 部分中的键应为 evalselect 条目的名称。提供的值将覆盖该条目本应选择或计算的值。

您还可以测试不应触发操作的条件:

test: {
    ....
    not_full: {
        no: ["disk_full"],
        values: {
            disk_used: 97,
            disk_total: 100,
        }
    }
}

只需运行 Triage 即可运行测试。每次运行时,它都会自动自测。

fx triage --config . --data snapshot

哎呀!这应表示发生错误:

Test is_full failed: trigger 'disk_percentage > 0.98' of action disk_full returned Bool(false), expected true

修正规则

您希望在磁盘可用空间达到 98% 或更多时触发,但这并不在您写入的内容中,并且您的测试发现了问题。将您操作中的 > 修改为 >=

        trigger: "disk_percentage >= 0.98",

再次运行 Triage。错误应该会消失,并替换为一条警告,提示您 inspect.json 文件实际上表示磁盘已满。

Warning: 'disk_full' in 'rules' detected 'Disk is 98% full': 'disk98' was true

日志文件扫描

您可以编写表达式来测试日志文件的某一行(syslog.txt、klog.txt、bootlog.txt)是否与正则表达式匹配。如下所示:

    eval: {
        syslog_has_not_found: "SyslogHas('ERROR.*not found')",
        ...
    }
    act: {
        something_not_found: {
            trigger: "SyslogHas('ERROR.*not found')",
            ...
        }
    }

函数包括 SyslogHas()、KlogHas()、BootlogHas()。如果缺少某个日志文件(例如,某些快照不包含 bootlog.txt),系统会将其视为空文件。

如需对此进行测试,您可以在测试中添加如下条目:

test: {
    test_error: {
        yes: [error_scan],
        syslog: "ERROR: file Foo not found\nSecond line OK",
    }
}

批注.json

快照包含一个文件 annotations.json,其中包含有关 build、开发板、正常运行时间等的信息。

您可以通过将 Annotation() 函数与单个字符串参数(该文件中 JSON 对象的键)结合使用,从此文件中提取值。例如:

eval: {
    using_chromebook: "Annotation('build.board') == 'chromebook-x64'",
}

使用多个配置文件

您可以添加任意数量的 Triage 配置文件,甚至可以使用在一个文件内的另一个文件中定义的变量。它有很多用途:

  • 一个文件用于与磁盘相关的变量和操作,另一个用于与网络相关的变量和操作。
  • 用于定义产品特定编号的文件。
  • 为特定工程师或团队创建单独的文件。

添加一个包含以下内容的文件“product.triage”:

{
    eval: {
        max_components: "4",
    },
}

请注意以下几点:

  • .triage 文件中可以省略空白部分。此文件不包含任何 selectacttest 条目。
  • 虽然 JSON 中的数值未加引号,但 4 是一个数学表达式字符串,因此需要加上英文引号。

将以下条目添加到 rules.triage 文件中:

select: {
    ...
    actual_components: "INSPECT:bootstrap/archivist:root/event_stats:components_started",
}

这将提取出设备中的活动组件数量。

eval: {
    ...
    too_many_components: "actual_components > product::max_components",

这会将实际分量与产品理论最大值进行比较。

最后,添加操作:

act: {
    ...
    component_overflow: {
        type: "Warning",
        trigger: "too_many_components",
        print: "Too many components!",
    },
}

遗憾的是,此设备尝试使用的组件过多,因此在运行“fx 分类”时应触发此警告。

在生产环境中,可以在不同的目录中维护多个“product.triage”文件,并且可以指示 Triage 通过“--config”命令行参数使用其中的任何文件。

测试和命名空间

测试仅使用发生测试的文件中的指标,以及测试提供的值。使用命名空间型值(如“a::b”)的表达式(评估或测试触发器)必须具有由测试值中的“a::b”条目提供的这些值。

test: {
    component_max_ok: {
        no: [
            "component_overflow",
        ],
        values: {
            actual_components: 17,
            "product::max_components": 17,
        },
    },
},

详细信息

姓名

名称(选择器、表达式、操作、测试以及配置文件的基名)可以是任何字母或下划线,后跟任意数量的字母、数字或下划线。

在分类结构的未来版本中,以下划线开头的名称可能具有特殊含义。这些行为并未被禁止,但最好还是要避免。

每个 .triage 文件的名称都会确定其命名空间。不允许从不同目录加载两个同名的 .triage 文件。

数学表达式

  • 变量可以是 64 位浮点数、有符号的 64 位整数或布尔值。
  • 算术表达式使用 + - * / // 运算符,具有普通的运算顺序和优先级。
  • 除法运算符 / 会生成一个浮点值。
  • 除法运算符 // 会生成一个 int 值,将结果截断至 0,即使使用浮点参数也是如此。(请注意,这与 Python 3 不同, Python 3 // 会向下截断。)
  • + - * 会保留其操作数的类型(混合提升为浮点数)。
  • 比较运算符为 > >= < <= == !=
  • 比较运算具有布尔值结果类型,可用于触发操作。
  • 您可以在一条 eval 规则中结合使用计算和比较运算。
  • 您可以使用括号。
  • 您可以将 evalselect 条目的键名用作变量。
  • 空格在任何位置都是可选的,在 filename::variable 命名空间型变量内以外的任何位置都允许使用。

预定义函数

分类提供可在 eval 表达式中使用的预定义函数:

  • Max(value1, value2, value3...) 返回最大值,类型提升为浮点数。
  • Min(value1, value2, value3...) 返回最小值,类型提升为浮点数。
  • And(value1, value2, value3...) 接受布尔值参数并返回值的逻辑与 (AND)。
  • Or(value1, value2, value3...) 接受布尔值参数并返回值的逻辑 OR。
  • Not(value) 接受一个布尔值参数并返回其逻辑 NOT。
  • 如果相应的日志文件具有行匹配匹配器(即包含正则表达式的字符串),则 SyslogHas(matcher)KlogHas(matcher)BootlogHas(matcher) 会返回 true。
  • Annotation(key) 从 annotation.json 文件中返回相应的值。
  • Option(value1, value2, value3...) 会返回第一个有用值以支持选择器迁移和默认值:第一个非空列表、非 Missing 值(如果有)或空列表(如果已提供);或者缺失。
  • 如果值是错误指示,Missing(value) 会返回 true。
  • Days()Hours()Minutes()Seconds()Millis()Micros()Nanos() 会计算与单调时间戳进行比较的值。
  • Now() 会返回创建诊断数据的大致时间戳。
  • StringMatches(value, regex) 会将指定的正则表达式应用于指定值,如果存在匹配项,则返回 true。Rust 正则表达式 crate 支持正则表达式语法。

函数编程

分类可以将函数应用于值的矢量。矢量的格式为 "[expr, expr, expr...]"。某些选择器会返回多元素向量。

分类提供函数 Map()Fold()Filter()Count() 来处理矢量,Fn() 定义要应用的映射、折叠和过滤器的函数或 lambda,并提供 Apply() 来将 Fn() 应用到参数。

如需了解详情,请参阅配置 fx 分类

延伸阅读

如需了解最新功能和选项,请参阅 fx triage - 分类将不断改进!