SLS告警重磅更新——让你的告警通知一目了然

前言

SLS告警最近升级了通知的模板语法,在保持对原有模板语法兼容的情况下,增加了动态渲染的功能,不仅支持条件语句、循环语句等控制流,还内置了50+函数,可以非常方便地对数据进行各种处理,从而可以非常灵活方便地对通知内容进行定制,让通知内容呈现的效果更加丰富、内容一目了然。


旧模板语法回顾

在配置告警通知的时候,我们需要配置通知的内容模板,例如下面的一个钉钉通知模板:

- 所属项目: ${project}
- 告警名称: ${alert_name}
- 首次触发: ${fire_time}
- 告警时间: ${alert_time}
- 告警状态: ${status}
- 告警严重度: ${severity}
- 告警标签: ${labels}
- 告警标注: ${annotations}
- 触发日志: ${fire_results}

假如告警内容为:

{
 "project": "test-project",
 "alert_id": "nginx-error",
 "alert_name": "Nginx访问错误",
 "fire_time": 1632802350,
 "alert_time": 1632802351,
 "resolve_time": 0,
 "status": "firing",
 "severity": 10,
 "labels": {
    "service": "nginx",
    "region": "cn-hangzhou"
 },
 "annotations": {
    "title": "Nginx访问错误",
    "desc": "Nginx最近1分钟内有96条错误日志"
 },
  "fire_results": [{
   "msg": "ERROR: Internal Server Error"
  }, {
   "msg": "ERROR: uri /foo/bar not exist"
  }]
}

则渲染后的通知内容为:

- 所属项目: test-project
- 告警名称: Nginx访问错误
- 首次触发: 2021-09-28 12:12:30
- 告警时间: 2021-09-28 12:12:31
- 告警状态: 触发
- 告警严重度: 严重
- 告警标签: {"service": "nginx", "region": "cn-hangzhou"}
- 告警标注: {"title": "Nginx访问错误", "desc": "Nginx最近1分钟内有96条错误日志"}
- 触发日志: [{"msg": "ERROR: Internal Server Error"}, {"msg": "ERROR: uri /foo/bar not exist"}]

这里模板的渲染逻辑比较简单,可以简单理解为近似于变量直接替换。通过上面的例子我们可以发现旧的模板语法存在的几个问题:

  1. 逻辑处理能力缺失,无法做到动态渲染
    • 例如希望根据告警状态来进行动态渲染:告警触发是展示触发日志,告警恢复时无需展示触发日志
    • 例如希望遍历 fire_results,将错误日志以列表形式展示
  1. 数据处理能力弱
    • 例如希望将触发日志里的 ERROR 前缀去掉
    • 例如希望告警触发时间展示为其它格式
  1. 内容与样式耦合
    • 告警的时间、状态、严重度等属性,都是根据当前的语言环境自动进行了格式化(例如时间由时间戳转成了“2021-09-28 12:12:30”;告警状态由“firing” 转成了“触发”;严重度由数字10自动转成了“严重”)
    • 如果希望根据原始属性值,自定义展示形式,则无法做到

这些能力的缺失,一方面会导致最终的通知展示不够友好,另一方面,也会导致某些场景无法支持。例如Webhook通知渠道的模板配置为JSON格式:

{
  "msg": "${fire_results[0].msg}"
}

假如实际告警的触发日志中,包含了换行符或者双引号,则上面的模板渲染后就不是一个合法的JSON。例如原始 msg 内容为:

Uncaught Error: unexpected error
    at <anonymous>:1:7

则渲染后的内容为:

{
  "msg": "Uncaught Error: unexpected error
    at <anonymous>:1:7"
}

由于渲染后的结果不是合法JSON,因此就会导致告警通知发送失败。


新模板语法

为了解决上述痛点,我们重新设计了告警通知的内容模板语法,不仅可以解决上述问题,而且内置了非常多的数据处理函数,可以按需来对原始的告警内容进行处理,从而达到自己想要的通知效果。新旧版本的告警通知模板语法比较如下:


新版通知模板语法

旧版通知模板语法

变量引用

通过 {{ alert.xxx }} 来引用告警的属性,例如 {{ alert.project }}(其中大括号的空格不是必须,这里只是为了格式好看)

通过 ${xxx} 来引用告警的属性,例如 ${project}

操作符

支持常用的算数操作和逻辑操作,包括:

  • 加减乘除四则运算
  • and,or,not 逻辑运算
  • 大小比较运算
  • 取模计算

不支持

条件判断

支持

不支持

循环语句

支持

不支持

数据处理函数

内置了50+函数用于数据的处理和格式化,包括:

  • 数学计算函数
  • 字符串处理函数
  • 日期函数
  • 编解码函数
  • 等等

不支持


针对上面的例子,我们使用新版本的模板语法,可以配置通知模板如下:

- 所属项目: {{ alert.project }}
- 告警名称: {{ alert.alert_name }}
- 首次触发: {{ alert.fire_time | format_date }}
- 告警时间: {{ alert.alert_time | format_date }}
- 告警状态: {{ alert.status | format_status }}
- 告警严重度: {{ alert.severity | format_severity }}
- 告警标签:
{%- for key, value in alert.labels.items() %}
 - {{ key }}: {{ value }}
{%- endfor %}
- 告警标注:
{%- for key, value in alert.annotations.items() %}
 - {{ key }}: {{ value }}
{%- endfor %}
- 触发日志:
{% for result in alert.fire_results %}
 - {{ result.msg }}
{% endfor %}

则最终渲染后的内容为:

- 所属项目: test-project
- 告警名称: Nginx访问错误
- 首次触发: 2021-09-28 12:12:30
- 告警时间: 2021-09-28 12:12:31
- 告警状态: 触发
- 告警严重度: 严重
- 告警标签:
 - service: nginx
  - region: cn-hangzhou
- 告警标注:
  - title: Nginx访问错误
  - desc: Nginx最近1分钟内有96条错误日志
- 触发日志:
 - ERROR: Internal Server Error
  - ERROR: uri /foo/bar not exist


案例

案例一:格式化钉钉通知内容

旧版模板

新版模板

模板配置

- 所在项目: ${project}
- 告警名称: ${alert_name}
- 触发时间: ${fire_time}
- 告警状态: ${status}
- 严重度: ${severity}
- 标签: ${labels}
- 标注: ${annotations}

[[查看详情](${query_url})]
[[告警设置](${alert_url})]
- 所在项目: {{ alert.project }}
- 告警名称: {{ alert.alert_name }}
- 触发时间: {{ alert.fire_time | format_date }}
- 告警状态: {{ alert.status | format_status }}
- 严重度: {{ alert.severity | format_severity }}
- 标签:
{{ alert.labels | to_list | blockquote }}
- 标注:
{{ alert.annotations | annotations_to_list | blockquote }}

[[查看详情]({{ alert.query_url }})]
[[告警设置]({{ alert.alert_url }})]

展示效果

SLS告警重磅更新——让你的告警通知一目了然

SLS告警重磅更新——让你的告警通知一目了然

这里我们使用了如下内置函数:

函数处理

作用

{{ alert.fire_time | format_date }}

format_date 用来对时间进行格式化,默认格式化微 yyyy-MM-dd HH:mm:ss 格式

{{ alert.status | format_status }}

format_status 用来对告警状态进行格式化:

  • 根据当前语言环境展示不同的语言内容,例如 "firing" 会展示为 "触发" 或 "Firing"
  • 根据触发或者恢复状态,展示不同颜色的字体(针对钉钉、企业微信、邮件等富文本渠道有效)

{{ alert.severity | format_severity }}

format_severity 用来对告警严重度进行格式化,类似于 format_status:

  • 根据当前语言环境展示不同的语言内容,例如严重度为 10 会展示位 "严重" 或 "Critical"
  • 根据告警严重度,展示不同颜色的字体(针对钉钉、企业微信、邮件等富文本渠道有效)

{{ alert.labels | to_list | blockquote }}

  • to_list 将告警标签转化为列表形式(在钉钉渠道下是 Markdown 形式列表,在邮件渠道下是 HTML <ul> 标签)
  • blockquote 将内容转化为引用格式(在钉钉下是 > xxx 语法,在邮件渠道下是 <blockquote>xxxx</blockquote> 形式)

{{ alert.annotations | annotations_to_list | blockquote }}

annotations_to_list 功能与 to_list 非常类似,只不过会额外将一些字段进行翻译,例如 title 会根据语言环境展示为 "标题" 或 "Title"


使用内置的格式化函数有如下优点:

  • 开箱即用:默认对告警状态、严重度等属性,以及列表、引用等常见格式做了包装。
  • 屏蔽了不同渠道的差异:不同的通知渠道对格式的支持是有区别的,例如钉钉支持彩色字体,但是有些渠道可能就不执行。另外同样都是列表展示,在钉钉下是 Markdown 格式,在邮件下是 HTML 格式。内置的函数对此进行了封装,屏蔽了不同渠道的差异,对外提供出来的是一套统一的语法,从而方便使用。


案例二:Webhook JSON 内容配置

在使用 Webhook 通知渠道的时候,通常我们都会使用 JSON 格式的内容。

旧版模板

新版模板

模板配置

{
  "project": "${project}",
  "alert_name": "${alert_name}",
  "fire_time": ${fire_time},
  "status": "${status}",
  "severity": "${severity}",
  "labels": ${labels},
  "annotations": ${annotations}
}
{
  "project": "{{ alert.project }}",
  "alert_name": "{{ alert.alert_name }}",
  "fire_time": {{ alert.fire_time }},
  "status": {{ alert.status | format_status | quote }},
  "severity": {{ alert.severity | format_severity | quote }},
  "labels": {{ alert.labels | to_json }},
  "annotations": {{ alert.annotations | to_json }}
}

这里新版模板使用了如下函数:

函数处理

作用

{{ alert.status | format_status | quote }}

{{ alert.status | format_status | quote }}"{{ alert.status | format_status }}" 作用相同,但是使用 quote 的好处是,如果需要引用的内容里本身包含了双引号或者换行等字符,那么就会导致字符串不合法,最终导致 JSON 格式错误,而使用 quote 函数处理就可以避免该问题。

{{ alert.labels | to_json }}

to_json 用于将数据转为 JSON 格式,这里我们可以非常明显地看到告警标签是以 JSON 格式来进行展示的。

这里我们可以看到新版模板语法的一个显著特点,即内容与样式分离,例如通过 quote 可以显式的看出渲染后是一个字符串,通过 to_json 可以显式的看到是一个 JSON 格式。通过内容与样式分离,可以通过内置的函数来自定义需要的展示样式,从而更加灵活。

案例三:动态渲染

例如原始数据如下:

SLS告警重磅更新——让你的告警通知一目了然

希望将错误的 host 和 remote_uri 遍历展示出来,可以配置内容模板如下:

- 所在项目: {{ alert.project }}
- 告警名称: {{ alert.alert_name }}
- 触发时间: {{ alert.fire_time | format_date }}
- 告警状态: {{ alert.status | format_status }}
- 严重度: {{ alert.severity | format_severity }}
- 标签:
{{ alert.labels | to_list | blockquote }}
- 触发详情:
{%- if alert.status == 'firing' %}
{%- for result in alert.fire_results %}
    - {{ result.host }}{{ result.request_uri }}
{%- endfor %}
{%- endif %}
[[查看详情]({{ alert.query_url }})]
[[告警设置]({{ alert.alert_url }})]

通知效果如下:

SLS告警重磅更新——让你的告警通知一目了然

这里我们通过 if 条件判断和 for 循环结合,从而可以在告警是触发状态的时候,遍历渲染出所有的触发数据。


进一步参考

更多相关信息,可以参考:

对我们工作感兴趣的,可以通过如下方式了解更多,谢谢关注!

SLS告警重磅更新——让你的告警通知一目了然

上一篇:SLS告警最佳实践——Webhook通知最佳实践


下一篇:[Qt] Qt中使用自定义字体文件 .ttf