写在前面
在讨论如何写好监控器配置时的事件通知内容之前,需要明确这样一个逻辑:
当监控器检测规则生效后,它会针对系统业务数据进行一系列聚合数据处理并以事件的形式留存。这些事件记录可以理解为当前监控器检测对象发出的异常信号的承载者,本文即将讨论的事件标题与内容就是这些事件记录的一部分。如果利益相关方认为当前异常是紧急处理事项,并且需要预估风险、及时响应,就可以以告警的形式向外发送这些事件记录。
在这个传送过程中,告警配置中的不聚合、规则聚合和智能聚合这三种方式会对事件标题与内容进行对应的处理,最终成为利益相关方接收到的异常告警通知。(如下图所示)
再回到最初的问题,当我们借用监控器来对各类数据进行检测,十分期望在产生异常事件时,被通知对象能第一时间获取异常时刻下详细的上下文信息。这就要求创建者在配置监控器时,重视并了解如何定义、编辑需要通知的事件标题及内容。
模版变量
模版变量是编辑标题与内容的核心要素之一。以下是观测云支持的模板变量,帮助渲染动态文案。
模板变量 | 类型 | 说明 |
---|---|---|
时间: | ||
date、timestamp | Integer | 事件产生时间。单位为秒 |
df_date_range | Integer | 时间范围。单位为秒 |
df_check_range_start | Integer | 检测范围开始时间。Unix 时间戳,单位为秒 |
df_check_range_end | Integer | 检测范围结束时间。Unix 时间戳,单位为秒 |
事件: | ||
df_status | String(Enum) | 事件状态,可能的值为: - 紧急 critical - 重要 error - 警告 warning - 正常 ok - 无数据 nodata |
df_event_id | String | 事件唯一 ID |
df_event_url | String | 事件详情页 URL |
df_dimension_tags | String | 事件维度。用于标识检测对象。如:{"host":"web-001"} |
df_event | Dict | 完整事件数据 |
告警: | ||
df_monitor_id | String | 告警策略 ID 如果对检测有疑问,可以将此 ID 发送给我们 |
df_monitor_name | String | 告警策略名称 |
监控器: | ||
df_monitor_checker_id | String | 监控器 ID 如果对检测有疑问,可以将此 ID 发送给我们 |
df_monitor_checker_name | String | 监控器名称 |
df_monitor_checker_value | String | 检测值,即被监控器检测的值 ⚠️ 检测值会强制转换为 String 类型以保证兼容性 |
df_monitor_checker_event_ref | String | 监控器事件关联。根据监控器 ID、事件 df_dimension_tags 计算所得 |
df_label | List | 监控器标签列表 |
df_label[#] | String | 监控器标签 |
故障: | ||
df_fault_id | String | 本轮故障 ID,取值为首次故障事件的 df_event_id |
df_fault_status | String(Enum) | 本轮故障状态,为 df_status 的冗余字段,可能的值为: - 正常 ok - 故障 fault |
df_fault_start_time | Integer | 本轮故障发生时间。Unix 时间戳,单位为秒 |
df_fault_duration | Integer | 本轮故障持续时间。单位为秒 |
手工恢复: | ||
df_user_id | String | 手工恢复时,操作者用户 ID |
df_user_name | String | 手工恢复时,操作者用户名称 |
df_user_email | String | 手工恢复时,操作者用户邮箱 |
检测: | ||
df_check_condition | Dict | 满足的检测条件 |
df_check_condition.operator | String | 满足检测条件的操作符,如:>、>= 等 |
df_check_condition.operands | List | 满足检测条件的操作数列表。 一般只有 1 个操作数,但 between 等操作符具有 2 个操作数 |
df_check_condition.operands[#] | Integer, Float | 满足检测条件的操作数 |
Result | Integer, Float | 检测值,与 df_monitor_checker_value 一样为产生本事件时,检测到的值,但字段类型为检测时获得的原始类型,不会强制转换为 String |
其他: | ||
df_site_name | String | 当前观测云节点名 |
df_workspace_name | String | 所属工作空间名 |
df_workspace_uuid | String | 所属工作空间 ID 如果对检测有疑问,可以将此 ID 发送给观测云侧 |
{df_dimension_tags 中的各字段} | String | df_dimension_tags 中的各字段会被提取 |
df_event | Dict | 完整事件数据 |
事件标题
事件标题讲究不拖沓,即一句话就能阐明要点。这样当接收到事件通知时,在第一眼看到标题的情况下,就能大致了解事件内容。如:
- Consul 集群里成员的状态存在异常
除了上述这样纯文本的标题,我们还可以在标题内插入模版变量。如:
- 主机 {{ host }} 可用内存小于 10%
- {{ service }} 服务的链路 Trace 错误率过高,错误率为 {{ ReConsult | to_fixed(2) }}%
事件内容
在指定事件内容时,我们可以借助模板语法来实现。接下来,我们会以几种常见的场景,展示事件内容通知的实际编辑效果。
场景一:模版变量
假设监控器 by
配置了 region
和 host
,基于上表中的模板变量,我们可以编辑一份最基础的事件内容:
- 事件标题:
监控器 {{ df_monitor_checker_name }} 发现 {{ df_dimension_tags }} 存在故障
- 事件内容:
地区:{{ region }}
主机:{{ host }}
主机:{{ host }}
级别:{{ df_status }}
检测值:{{ Result }}
监控器:{{ df_monitor_checker_name }}(告警策略:{{ df_monitor_name }})
那么,产生 error
事件后,经过渲染的事件输出如下:
- 输出事件标题:
监控器 监控器001 发现 {"region":"hangzhou","host":"web-001"} 存在故障
- 输出事件内容:
地区:hangzhou
主机:web-001
主机:web-001
级别:error
检测值:90.12345
监控器:监控器001(告警策略:团队001)
场景二:模版变量+模版函数
除了如场景一这样直接展示事件中的字段值外,我们还可以使用模板函数对字段值进行进一步处理。采用模版函数能优化事件的通知内容输出,整合必要信息。
其组合用法形式为: {{ <模板变量> | <模板函数> }}
在观测云,我们可用的模板函数列表如下:
模板函数 | 参数 | 说明 |
---|---|---|
to_datetime | 时区 | 将时间戳转换为日期(默认时区为 Asia/Shanghai) 示例:{{ date | to_datetime }} 输出:2022-01-01 01:23:45 |
to_status_human | 将 df_status 转换为易读形式 示例:{{ df_status | to_status_human }} 输出:紧急 | |
to_fixed | 小数位数 | 将数字输出为固定小数位数(默认保留 0 位小数) 示例:{{ Result | to_fixed(3) }} 输出:1.230 |
to_round | 小数位数 | 将数字四舍五入(默认保留 0 位小数) 示例:{{ Result | to_round(2) }} 输出:1.24 |
to_percent | 小数位数 | 将小数输出为百分比(默认保留 0 位小数) 示例:{{ Result | to_percent(1) }} 输出:12.3% |
to_pretty_tags | 美化输出标签 示例:{{ df_dimension_tags | to_pretty_tags }} 输出:region:hanghzou, host:web-001 |
还是参考场景一中的示例,此时在使用模版函数的情况下(即 {{ df_dimension_tags | to_pretty_tags }}
),事件标题与内容的写法分别为:
- 事件标题:
监控器 {{ df_monitor_checker_name }} 发现 {{ df_dimension_tags | to_pretty_tags }} 存在故障
- 事件内容:
对象:{{ df_dimension_tags | to_pretty_tags }}
时间:{{ date | to_datetime }}
级别:{{ df_status | to_status_human }}
检测值:{{ (Result * 100) | to_round(2) }}
那么,产生 error 事件后,经过渲染的事件输出如下:
- 输出事件标题:
监控器 我的监控器 发现 region:hangzhou, host:web-001 存在故障
- 输出事件内容:
检测对象:region:hangzhou, host:web-001
检测时间:2022-01-01 01:23:45
故障级别:重要
检测值:9012.35
场景三:模板分支
基于观测云现有的配置页面,假设我们期望在同一个事件内容框中描述不同等级下产生的异常,我们可以采用模版分支语法(if else
)来实现。
语法大致写法如下:
{% if df_status == 'critical' %}
紧急问题,请立即处理!
{% elif df_status == 'error' %}
重要问题,请处理
{% elif df_status == 'warning' %}
可能有问题,有空处理
{% elif df_status == 'nodata' %}
数据中断,请立即处理!
{% else %}
没问题!
{% endif %}
示例效果如下:
{% if df_status != 'ok' %}
等级:{{ df_status }}
主机:{{ host }}
内容:Elasticsearch JVM 堆内存的使用量为 {{ Result }}%
建议:当前 JVM 垃圾的收集已经跟不上 JVM 垃圾的产生请及时查看业务情况
{% else %}
等级:{{df_status}}
主机:{{host}}
内容:Elasticsearch JVM 堆内存告警已恢复
{% endif %}
场景四:内嵌 DQL 查询函数
还有这样一种情况:即事件关注者除了希望看到监控器下产生的事件相关的内容,还想实现额外的数据查询,无论该类数据与当前监控器配置规则有关与否。此时,仅使用模板变量无法满足渲染需求。我们可以采用内嵌 DQL 查询函数来实现。
该函数支持在本工作空间下本次检测时间范围内(即 check_start_time
和 end_time
的范围)执行任意 DQL 语句,通常情况下,查询所得的第一条数据可在模板中作为模板变量使用,使用方式如下:
{% set dql_data = DQL("需要执行的 DQL 语句") %}
某字段:{{ dql_data.some_field }}
假设,我们需要查询 host
字段为 "my_server"
的数据,并将第一条数据赋值给 dql_data
变量:
编辑效果为:
{% set dql_data = DQL("O::HOST:(host, host_ip, os, datakit_ver) { host = 'my_server' }") %}
主机 OS:{{ dql_data.os }}
此后的模板中即可使用 {{ dql_data.os }}
输出查询结果中的具体字段。
有时,所需执行的 DQL 语句需要传递参数。
假设监控器 by
条件中配置了 region
和 host
,且事件内容的写法如下:
{% set dql_data = DQL("O::HOST:(host_ip, os) { region = ?, host = ? }", region, host) %}
主机信息:
IP:{{ dql_data.host_ip }}
OS: {{ dql_data.os }}
由于事件仅包含 region
和 host
模板变量用于标记不同的数据,不包含 IP 地址、操作系统等更多信息。那么,使用内嵌 DQL 可以通过 region
和 host
作为 DQL 查询参数获取对应数据,并使用 {{ dql_data.host_ip }}
等输出关联信息。
结论
无论处于上述哪种场景,我们的最终输出目标都是:利用观测云提供的模板变量,来自定义并精准捕获异常时刻下的上下文事件信息,以便相关关注者能够及时做出反应。