通过服务器端表达式,可以用数学和其他操作来处理查询返回的数据。注意,表达式将创建新的数据,而不是操作数据源返回的数据。
服务器端表达式允许你用数学和其他操作来处理查询返回的数据。表达式会创建新数据,而不会操作数据源返回的数据,只会进行一些小的数据重组,使数据成为表达式可接受的输入。
表达式最常用于 Grafana 报警。处理是在服务器端完成的,因此表达式可以在没有浏览器会话的情况下运行。不过,表达式也可用于后端数据源和可视化。
注意:
表达式不适用于传统的仪表盘报警。表达式用于增强数据源,可将不同数据源的查询组合在一起,或提供数据源中不可用的操作。
注意:
如有可能,应在数据源内部进行数据处理。将数据从存储复制到 Grafana 服务器进行处理的效率很低,因此表达式针对的是轻量级数据处理。
表达式适用于返回时间序列或数字数据的数据源查询。表达式还可对多维数据进行操作。例如,返回多个序列的查询,其中每个序列都由标签或标记标识。
单个表达式将一个或多个查询或其他表达式作为输入,并将数据添加到结果中。每个单独表达式或查询都由一个变量表示,该变量是一个命名标识符,称为 RefID(例如,默认字母 A 或 B)。
要在另一个表达式中引用单个表达式或数据源查询的输出,可将该标识符用作变量。
表达式可处理两种类型的数据。
时间序列集合。
数字集合,其中每个数字都是一个项目。
每个集合从单个数据源查询或表达式返回,并由 Ref ID 表示。每个集合都是一个集合,其中集合中的每个项都通过其维度进行唯一标识,这些维度存储为标签或键值对。
服务器端表达式只支持后端数据源的数据源查询。数据通常被假定为带标签的时间序列数据。今后,我们打算添加查询返回类型(数字或时间序列)数据的断言,以便表达式能更好地处理错误。
数据源查询与表达式一起使用时,由表达式引擎执行。执行时,它会将数据重组为每个数据帧一个时间序列或一个数字。因此,举例来说,如果在表格视图中使用的数据源在一个数据帧中返回多个序列,那么在使用表达式执行时,你可能会注意到它看起来有所不同。
目前,唯一支持的非时间序列格式(数字)是在使用数据帧时,表格响应返回的数据帧没有时间、字符串列和一个数字列:
Loc | Host | Avg_CPU |
MIA | A | 1 |
NYC | B | 2 |
上面的示例将生成一个可与表达式配合使用的数字。字符串列成为标签,数字列则是相应的值。例如 { "Loc":"MIA", "Host":"A" } 的值为 1。
您可以在表达式中使用以下操作:math、还原(reduce)和重新采样(resample)。
Math 用于对时间序列或数字数据进行自由形式的数学公式运算。数学运算将数字和时间序列作为输入,并将其转换为不同的数字和时间序列。
引用其他查询或表达式中的数据时,RefID 会以美元符号作为前缀,例如 $A。如果变量名中有空格,则可以使用 ${my variable} 这样的括号语法。
数字常量可以是十进制(2.24)、八进制(以 0 为前导,如 072)或十六进制(以 0x 为前导,如 0x2A),也支持指数和符号(如 -0.8e-2)。
支持算术运算符(+、二元和一元 -、*、/、%、指数 **)、关系运算符(<、>、==、!=、>=、<=)和逻辑运算符(&&、|| 和一元 !)。
操作符对数据的影响取决于数据是数字还是时间序列数据。
对于二进制操作符,如 $A + $B 或 $A || $B,操作符的应用方式取决于数据类型:
如果 $A 和 $B 都是数字,则在两个数字之间进行运算。
如果一个变量是数字,另一个变量是时间序列,则在时间序列中每个点的值与数字之间进行运算。
如果 $A 和 $B 都是时间序列数据,则对 $A 和 $B 中存在的每个时间戳执行两个序列中每个值之间的运算。重采样操作可用于将时间戳排成一行 (注:今后,我们计划为数学运算添加选项,以实现不同的行为)。
总结:
数字 OP 数字 = 数字
数字 OP 系列 = 系列
系列 OP 系列 = 系列
由于表达式处理的是由单个变量表示的多个系列或数字,因此二进制运算也在两个变量之间执行联合(连接)。这是根据与每个序列或数字相关的标识标签进行的。
因此,如果 $A 中的数字带有 {host=web01} 这样的标签,而 $B 中的另一个数字也带有相同的标签,那么操作将在每个变量中的这两个项目之间进行,结果将共享相同的标签。这种联合的行为规则如下:
没有标签的项目将连接到任何项目。
如果 $A 和 $B 都只包含一个项目(一个系列或一个数字),它们将合并。
如果标签是精确数学,它们将连接。
如果标签是另一个标签的子集,例如,$A 中的项目标签为 {host=A,dc=MIA} ,而 $B 中的项目标签为 {host=A} ,则它们将连接。
目前,如果在 $A 这样的变量中,每个条目都有不同的标签键,则连接行为是未定义的。
关系运算符和逻辑运算符的返回值为 0 表示 false,1 表示 true。
虽然大多数函数都存在于自身的表达式运算中,但数学运算中确实存在一些类似于数学运算符或符号的函数。当函数可以接受数字或系列时,将返回与参数相同的类型。如果是系列,则对数列中每个点的值进行运算。
abs 返回参数的绝对值,参数可以是数字或系列。例如 abs(-1) 或 abs($A)。
is_inf 接收一个数字或系列,对于 Inf 值(负值或正值)返回 1,对于其他值返回 0。例如 is_inf($A)。
注意:如果需要特别检查负无穷大,可以进行比较,例如 $A == infn()。
is_nan 接收一个数字或系列,对于 NaN 值返回 1,对于其他值返回 0。例如 is_nan($A)。该函数的存在是因为 NaN 不等于 NaN。
is_null 接收一个数字或系列,空值返回 1,其他值返回 0。例如 is_null($A)。
is_number 接收一个数字或系列,所有实数值返回 1,其他数值(空、Inf+、Inf- 和 NaN)返回 0。例如 is_number($A)。
log 返回参数的自然对数,参数可以是数字或系列。如果数值小于 0,则返回 NaN。例如 log(-1) 或 log($A)。
inf、infn、nan 和 null 函数都返回名称的单个值。它们主要用于测试。例如:null()。
round 返回一个四舍五入的整数值。例如:round(3.123) 或 round($A),该函数可能需要一个参数,以便为舍入值增加精度。
ceil 将数字向上舍入为最接近的整数值。例如,ceil(3.123) 返回 4。
floor 将数字向下舍入到最接近的整数值。例如,floor(3.123) 返回 3。
Reduce 从查询或表达式中获取一个或多个时间序列,并将每个序列转换成一个数字。时间序列的标签将作为标签保留在每个输出的还原数中。
字段:
Function - 要使用的还原函数
Input - 要重新采样的变量(refID,如 A)
Mode - 当数列包含非数值(null、NaN、+-Inf)时,允许控制还原函数的行为。
Count 返回每个序列中的点数。
Mean 返回每个系列中所有值的总和除以该系列中的点数。在严格模式下,如果序列中的任何值为 null 或 nan,或者序列为空,则返回 NaN。
Min 和 Max 分别返回序列中的最小值或最大值。在严格模式下,如果序列中的任何值为 null 或 nan,或者序列为空,则返回 NaN。
Sum 返回系列中所有值的总和。在严格模式下,如果序列中有任何 NaN 或 Null 值,将返回 NaN。
Last 返回数列中的最后一个数字。如果序列中没有数值,则返回 NaN。
在 Strict 模式下,输入序列按原样处理。如果序列中的任何值是非数值(空、NaN 或 +-Inf),则返回 NaN。
在此模式下,输入序列中的所有非数值(空、NaN 或 +-Inf)都会在执行还原函数前被过滤掉。
在此模式下,所有非数值都会被一个预定义值替换。
重新采样会更改每个时间序列中的时间戳,使其具有一致的时间间隔。主要用途是对不共享相同时间戳的时间序列进行重采样,以便在它们之间进行数学运算。这可以通过对两个序列分别重新采样,然后在数学运算中引用重新采样的变量来实现。
字段:
Input - 要重新采样的时间序列数据变量(refID,如 A
Resample to - 重新采样到的时间长度,例如 10s。单位可以是 s 秒、m 分钟、h 小时、d 天、w 周、y 年。
Downsample - 当每个窗口采样有一个以上数据点时使用的缩减功能。有关操作详情,请参阅还原操作。
Upsample - 用于填充无数据点的窗口样本的方法。
pad 填充最后一个已知值
backfill 回填下一个已知值
fillna 用 NaNs 填充空样本窗口
如果您的数据源支持表达式,Grafana 会显示表达式按钮,并在查询编辑器列表中显示任何现有表达式。
(1)打开面板。
(2)在查询下方单击“Expression”。
(3)在操作字段中,选择要编写的表达式类型。
(4)写入表达式。
(5)单击“Apply”。
当查询的数据源没有返回序列或数字时,表达式引擎会返回 NoData。例如,如果一个请求包含两个通过表达式合并的数据源查询,如果其中至少一个数据源查询返回 NoData,那么整个查询返回的结果就是 NoData。
在多个查询中使用表达式时,表达式引擎要求所有查询都返回相同的时间戳。例如,如果使用数学方法合并多个 SQL 查询的结果(每个查询都使用 SELECT NOW() AS "time"),那么只有当所有查询都将 NOW() 评估为相同的时间戳时,表达式才会起作用;但这种情况并不总是发生。要解决这个问题,可以用任意时间替换 NOW(),如 SELECT 1 AS "time",或任何其他有效的 UNIX 时间戳。