TICKscript 语法
概念
在 简介 和 入门 章节中介绍了 节点 和 管道 的关键概念。节点代表进程调用单元,它们可以批量或逐点地获取数据,然后更改数据、存储数据,或根据数据变化触发其他活动,例如警报。管道只是逻辑上组织的节点链。
在 Kapacitor 中,TICKscript 用于直接定义任务和定义模板任务,模板任务充当可以重用于生成新任务的模板。
Go
TICKscript 语法受到许多不同语言的启发。其中最具影响力的是 Go。例如,变量声明的惯用语法、字符串模板、诸如 duration
之类的类型、lambda 表达式中使用的函数以及文档中其他地方都显而易见地体现了这一点。
语法子空间
在使用 TICKscript 时,会遇到几个语法子空间,这些子空间给一些用户造成了困惑。最重要的是 TICKscript 文件的 TICKscript 语法。它主要由变量声明和管道中链接在一起的节点组成。在创建时,query
节点需要一个表示 InfluxQL 语句的字符串。因此,InfluxQL 代表可能使用的第一个语法子空间。其他节点和方法使用 Lambda 表达式,这代表将遇到的第二个语法子空间。这些空间之间的语法(例如,访问变量、标签和字段值时)可能有所不同,这有时可能是造成困惑的根源。
总而言之,在 TICKscript 中需要注意的两个语法子空间是
有向无环图 (DAG)
如入门中所述,管道是有向无环图 (DAG)。(有关更多信息,请参阅 Wolfram 或 Wikipedia)。它包含有限数量的节点(也称为顶点)和边。每条边都从一个节点指向另一个节点。没有边路径可以返回到路径中较早的节点,否则会导致循环。TICKscript 路径(也称为管道和链)通常以数据源定义节点开始,该节点具有到数据集定义节点的边,然后将其结果传递到数据操作和处理节点。
TICKscript 语法
TICKscript 区分大小写并使用 Unicode。TICKscript 解析器从上到下、从左到右扫描 TICKscript 代码,实例化变量和节点,然后将它们链接或连接到管道中。加载 TICKscript 时,解析器会检查在节点上调用的链接方法是否有效。如果遇到无效的链接方法,解析器将抛出一个错误,消息为 “no method or property <identifier> on <node type>”。
代码表示
源文件应使用 UTF-8 编码。脚本分为 声明 和 表达式。声明导致变量的创建,并且在一行中发生。表达式可以跨越多行,并导致创建整个管道、管道 链 或管道 分支。
空格 用于声明中,将变量名与运算符和字面量值分隔开。它也用于表达式中,创建缩进,以指示方法调用的层次结构。这也有助于提高脚本的可读性。否则,空格将被忽略。
可以通过在文本前使用一对正斜杠 “//” 在单行上创建 注释。注释正斜杠前面可以有空格,并且不必是换行符的第一个字符。
关键字
关键字是在语言中具有特殊含义的标记,因此不能用作函数或变量的标识符。TICKscript 很简洁,只包含一小部分关键字。
表 1 – 关键字
词 | 用法 |
---|---|
TRUE | 字面量布尔值 “true”。 |
FALSE | 字面量布尔值 “false”。 |
AND | 标准布尔与运算符。 |
OR | 标准布尔或运算符。 |
lambda | 标记后面要解释为 lambda 表达式的内容。 |
var | 启动变量声明。 |
dbrp | 启动数据库声明 |
由于 TICKscript 中可用的本机节点类型集有限,因此每个节点类型(例如 batch
或 stream
)都可以被认为是关键字。节点类型及其分类在下面的 节点类型分类 部分中详细讨论。
运算符
TICKscript 支持传统的数学运算符以及一些在其数据处理领域中有意义的运算符。
表 2 – 标准运算符
运算符 | 用法 | 示例 |
---|---|---|
+ | 加法和字符串连接 | 3 + 6 、total + count 和 'foo' + 'bar' |
- | 减法 | 10 - 1 、total - errs |
* | 乘法 | 3 * 6 、ratio * 100.0 |
/ | 除法 | 36 / 4 、errs / total |
== | 相等比较 | 1 == 1 、date == today |
!= | 不等比较 | result != 0 、id != "testbed" |
< | 小于比较 | 4 < 5 、timestamp < today |
<= | 小于或等于比较 | 3 <= 6 、flow <= mean |
> | 大于比较 | 6 > 3.0 、delta > sigma |
>= | 大于或等于比较 | 9.0 >= 8.1 、quantity >= threshold |
=~ | 正则表达式匹配。右值必须是正则表达式 或持有此类表达式的变量。 | tag =~ /^cz\d+/ |
!~ | 正则表达式不匹配。右值必须是正则表达式 或持有此类表达式的变量。 | tag !~ /^sn\d+/ |
! | 逻辑非 | !TRUE 、!(cpu_idle > 70) |
AND | 逻辑与 | rate < 20.0 AND rate >= 10 |
OR | 逻辑或 | status > warn OR delta > sigma |
标准运算符用于 TICKscript 和 Lambda 表达式中。
表 3 – 链接运算符
运算符 | 用法 | 示例 |
---|---|---|
| | 声明链接方法调用,该调用创建新节点的实例并将其链接到上面的节点。 | stream | from() |
. | 声明属性方法调用,设置或更改其所属节点中的内部属性。 | from() .database(mydb) |
@ | 声明用户定义的函数 (UDF) 调用。本质上是一种链接方法,它向管道添加一个新的 UDF 节点。 | from() ... @MyFunc() |
链接运算符在表达式中用于定义管道或管道段。
变量和字面量
TICKscript 中的变量对于存储和重用值以及为快速理解变量代表什么提供友好的助记符非常有用。它们通常与字面量值的赋值一起声明。在旨在用作 模板任务 的 TICKscript 中,它们也可以仅使用类型标识符进行声明。
变量
变量使用关键字 var
在声明的开头声明。变量是不可变的,并且不能在脚本的后续部分重新分配新值,但它们可以在其他声明中使用,并且可以传递到方法中。变量也用作模板任务中的占位符,以便在模板用于创建新任务时填充。
有关使用 模板任务 的详细介绍,请参阅指南 模板任务。如果 TICKscript 证明有用,则可能需要将其重用为模板任务,以便快速创建其他类似的任务。因此,建议尽可能多地使用变量。
命名变量
变量标识符必须以标准 ASCII 字母开头,并且可以后跟任意数量的字母、数字和下划线。可以使用大写和小写字母。在用于直接定义任务的 TICKscript 中,变量持有的类型取决于分配给它的字面量值。在为任务模板编写的 TICKscript 中,也可以使用变量将持有的类型的关键字来设置类型。在用于直接定义任务的 TICKscript 中,使用类型标识符将导致编译时错误 invalid TICKscript: missing value for var "<VARNAME>".
。
示例 1 – 任务的变量声明
var my_var = 'foo'
var MY_VAR = 'BAR'
var my_float = 2.71
var my_int = 1
var my_node = stream
模板中的变量声明不需要字面量赋值,如下面的示例 2 所示。
示例 2 – 任务模板中的变量声明
var measurement string
var frame duration
var warn = float
var period = 12h
var critical = 3.0
字面量值
字面量值被解析为 TICKscript 中可用类型的实例。它们可以直接在方法参数中声明,也可以分配给变量。解析器根据上下文解释类型,并创建以下原语的实例:布尔值、字符串、浮点数、整数。正则表达式、列表、lambda 表达式、持续时间结构和节点也被识别。以下类型部分讨论了解析器用于识别类型的规则。
类型
TICKscript 识别五个类型标识符。这些标识符可以直接在用于模板任务的 TICKscript 中使用。否则,字面量的类型将从其声明中解释。
表 4 – 类型标识符
标识符 | 用法 |
---|---|
string | 在模板任务中,将变量声明为 string 类型。 |
duration | 在模板任务中,将变量声明为 duration 类型。 |
int | 在模板任务中,将变量声明为 int64 类型。 |
float | 在模板任务中,将变量声明为 float64 类型。 |
lambda | 在模板任务中,将变量声明为 Lambda 表达式类型。 |
布尔值
布尔值使用布尔关键字生成:TRUE
和 FALSE
。请注意,这些关键字使用所有大写字母。当使用小写字符(例如 True
或 true
)时,解析器将抛出错误。
示例 3 – 布尔字面量
var true_bool = TRUE
...
|flatten()
.on('host','port')
.dropOriginalFieldName(FALSE)
...
在上面的示例 3 中,第一行显示了使用布尔字面量的简单赋值。第二个示例显示了在方法调用中使用布尔字面量 FALSE
。
数值类型
任何仅包含数字且可选地包含小数点的字面量标记都将导致生成数值类型的实例。TICKscript 理解基于 Go 的两种数值类型:int64
和 float64
。任何包含小数点的数值标记都将导致创建 float64
值。任何不包含小数点结尾的数值标记都将导致创建 int64
值。如果整数以零字符 0
开头,则将其解释为八进制。
示例 4 – 数值字面量
var my_int = 6
var my_float = 2.71828
var my_octal = 0400
...
在上面的示例 4 中,my_int
的类型为 int64
,my_float
的类型为 float64
,my_octal
的类型为 int64
八进制。
持续时间字面量
持续时间字面量定义时间跨度。它们的语法遵循 InfluxQL 中存在的相同语法。持续时间字面量由两部分组成:整数和持续时间单位。它本质上是一个以一个或一对保留字符结尾的整数,这些字符表示时间单位。
下表列出了用于声明持续时间类型的时间单位。
表 5 – 持续时间字面量单位
单位 | 含义 |
---|---|
u 或 µ | 微秒(百万分之一秒) |
ms | 毫秒(千分之一秒) |
s | 秒 |
m | 分钟 |
h | 小时 |
d | 天 |
w | 周 |
示例 5 – 持续时间表达式
var span = 10s
var frequency = 10s
...
var views = batch
|query('SELECT sum(value) FROM "pages"."default".views')
.period(1h)
.every(1h)
.groupBy(time(1m), *)
.fill(0)
在上面的示例 5 中,前两行显示了持续时间类型的声明。第一个表示 10 秒的时间跨度,第二个表示 10 秒的时间范围。最后一个示例显示了直接在方法调用中声明持续时间字面量。
字符串
字符串以一个或三个单引号开头:'
或 '''
。字符串可以使用加法 +
运算符连接。要在单引号分隔的字符串中转义引号,请使用反斜杠字符。如果要预计在字符串内部会遇到许多单引号,请改用三重单引号分隔它。由三重引号分隔的字符串不需要转义序列。在这两种字符串划界情况下,用于访问字段和标签值的双引号都可以使用,而无需转义。
示例 6 – 基本字符串
var region1 = 'EMEA'
var old_standby = 'foo' + 'bar'
var query1 = 'SELECT 100 - mean(usage_idle) AS stat FROM "telegraf"."autogen"."cpu" WHERE cpu = \'cpu-total\' '
var query2 = '''SELECT 100 - mean(usage_idle) AS stat FROM "telegraf"."autogen"."cpu" WHERE cpu = 'cpu-total' '''
...
batch
|query('''SELECT 100 - mean(usage_idle) AS stat FROM "telegraf"."autogen"."cpu" WHERE cpu = 'cpu-total' ''')
...
在上面的示例 6 中,第一行显示了使用字符串字面量的简单字符串赋值。第二行使用连接运算符。第三行和第四行显示了声明复杂字符串字面量的两种不同方法,分别带有和不带有内部转义的单引号。最后一个示例显示了直接在方法调用中使用字符串字面量。
为了使长而复杂的字符串更具可读性,允许在字符串内换行。
示例 7 – 多行字符串
batch
|query('SELECT 100 - mean(usage_idle)
AS stat
FROM "telegraf"."autogen"."cpu"
WHERE cpu = \'cpu-total\'
')
在上面的示例 7 中,字符串被分解,以使查询更容易理解。
字符串模板
字符串模板允许将节点属性、标签和字段添加到字符串中。格式遵循 Go text.template 包提供的相同格式。这在编写警报消息时很有用。要将属性、标签或字段值添加到字符串模板,需要将其包装在双花括号内:“{{}}”。
示例 8 – 字符串模板内的变量
|alert()
.id('{{ index .Tags "host"}}/mem_used')
.message('{{ .ID }}:{{ index .Fields "stat" }}')
在示例 8 中,将三个值添加到两个字符串模板。在调用 setter id()
时,标签 "host"
的值将添加到字符串的开头。然后,调用 setter message()
会添加 id
,然后添加字段 "stat"
的值。
字符串模板当前适用于 Alert 节点,并在下面的 在字符串模板中访问值 部分中进一步讨论。
字符串模板还可以包括流语句(例如 if...else
)以及对内部格式化方法的调用。
.message('{{ .ID }} is {{ if eq .Level "OK" }}alive{{ else }}dead{{ end }}: {{ index .Fields "emitted" | printf "%0.3f" }} points/10s.')
字符串列表
字符串列表是在两个方括号之间声明的字符串集合。它们可以使用字面量、其他变量的标识符或星号通配符 “*” 进行声明。它们可以传递到采用多个字符串参数的方法中。它们在模板任务中尤其有用。请注意,在函数调用中使用时,列表内容会被展开,并且元素用作函数的所有参数。当给出列表时,理解为列表包含函数的所有参数。
示例 9 – 标准任务中的字符串列表
var foo = 'foo'
var bar = 'bar'
var foobar_list = [foo, bar]
var cpu_groups = [ 'host', 'cpu' ]
...
stream
|from()
.measurement('cpu')
.groupBy(cpu_groups)
...
示例 9 声明了两个字符串列表。第一个包含其他变量的标识符。第二个包含字符串字面量。列表 cpu_groups
在方法 from.groupBy()
中使用。
示例 10 – 模板任务中的字符串列表
dbrp "telegaf"."not_autogen"
var measurement string
var where_filter = lambda: TRUE
var groups = [*]
var field string
var warn lambda
var crit lambda
var window = 5m
var slack_channel = '#alerts'
stream
|from()
.measurement(measurement)
.where(where_filter)
.groupBy(groups)
|window()
.period(window)
.every(window)
|mean(field)
|alert()
.warn(warn)
.crit(crit)
.slack()
.channel(slack_channel)
示例 10 取自 代码存储库 中的示例,定义了 implicit_template.tick
。它使用 groups
列表来保存要传递给 from.groupBy()
方法的变量参数。groups
列表的内容将在模板用于创建新任务时确定。
正则表达式
正则表达式以正斜杠 /
开头和结尾。正则表达式语法与 Perl、Python 和其他语言相同。有关语法的详细信息,请参阅 Go 正则表达式库。
示例 11 – 正则表达式
var cz_turbines = /^cz\d+/
var adr_senegal = /\.sn$/
var local_ips = /^192\.168\..*/
...
var locals = stream
|from()
.measurement('responses')
.where(lambda: "node" =~ local_ips )
var south_afr = stream
|from()
.measurement('responses')
.where(lambda: "dns_node" =~ /\.za$/ )
在示例 11 中,前三行显示了将正则表达式赋值给变量。locals
流使用分配给变量 local_ips
的正则表达式。south_afr
流使用正则表达式比较,其中正则表达式作为 lambda 表达式的一部分以字面量形式声明。
Lambda 表达式作为字面量
Lambda 表达式是表示简短易懂的参数,该参数要传递到方法调用中或保存在变量中。它可以包装布尔表达式、数学表达式、对内部函数的调用或这三者的组合。Lambda 表达式始终对点数据进行操作。它们通常很简洁,因此用作字面量,最终传递到节点方法中。可以在 Lambda 表达式中使用的内部函数在下面的 类型转换 和 Lambda 表达式 部分中讨论。Lambda 表达式在主题 Lambda 表达式 中详细介绍。
Lambda 表达式以标记 lambda
开头,后跟冒号 ‘:’ – lambda:
。
示例 12 – Lambda 表达式
var my_lambda = lambda: 1 > 0
var lazy_lambda = lambda: "usage_idle" < 95
...
var data = stream
|from()
...
var alert = data
|eval(lambda: sigma("stat"))
.as('sigma')
.keep()
|alert()
.id('{{ index .Tags "host"}}/cpu_used')
.message('{{ .ID }}:{{ index .Fields "stat" }}')
.info(lambda: "stat" > 70 OR "sigma" > 2.5)
.warn(lambda: "stat" > 80 OR "sigma" > 3.0)
.crit(lambda: "stat" > 90 OR "sigma" > 3.5)
上面的示例 12 显示了 lambda 表达式可以直接赋值给变量。在 eval 节点中,使用了一个 lambda 语句,该语句调用 sigma 函数。alert 节点使用 lambda 表达式来定义给定事件的日志级别。
节点
与更简单的类型一样,节点类型被声明并且可以分配给变量。
示例 13 – 节点表达式
var data = stream
|from()
.database('telegraf')
.retentionPolicy('autogen')
.measurement('cpu')
.groupBy('host')
.where(lambda: "cpu" == 'cpu-total')
|eval(lambda: 100.0 - "usage_idle")
.as('used')
|window()
.period(span)
.every(frequency)
|mean('used')
.as('stat')
...
var alert = data
|eval(lambda: sigma("stat"))
.as('sigma')
.keep()
|alert()
.id('{{ index .Tags "host"}}/cpu_used')
...
在上面的示例 13 中,在第一部分中,创建了五个节点。顶级节点 stream
被分配给变量 data
。然后,stream
节点用作管道的根节点,节点 from
、eval
、window
和 mean
按顺序链接到该管道。在第二部分中,然后使用对变量 alert
的赋值来扩展管道,以便可以将第二个 eval
节点应用于数据。
使用标签、字段和变量
在任何脚本中,仅仅声明变量是不够的。还必须访问它们持有的值。在 TICKscript 中,还需要使用从 InfluxDB 数据系列中提取的标签和字段中保存的值。这在到目前为止呈现的示例中最明显。此外,lambda 表达式生成的值可以作为新字段添加到管道中的数据集,然后作为这些表达式的命名结果访问。以下部分探讨的不仅是变量的使用,还包括可以从数据中提取的标签和字段值以及命名结果。
访问值
访问数据标签和字段、使用字符串字面量和访问 TICKscript 变量各自涉及不同的语法。此外,还可以访问与某些节点一起使用的 lambda 表达式的结果。
- 变量 – 要访问 TICKscript 变量,只需使用其标识符。
示例 14 – 变量访问
var db = 'website'
...
var data = stream
|from()
.database(db)
...
在示例 14 中,变量 db
被分配字面量值 'website'
。然后在链接方法 from()
下的 setter .database()
中使用它。
- 字符串字面量 – 要声明 字符串字面量,请使用单引号,如上面的 字符串 部分所述。
- 标签和字段值 – 要在 Lambda 表达式中访问 标签值 或 字段值,请使用双引号。要在方法调用中引用它们,请使用单引号。在方法调用中,这些本质上是要由节点在数据系列中匹配标签或字段值时使用的字符串字面量。
示例 15 – 字段访问
// Data frame
var data = stream
|from()
.database('telegraf')
.retentionPolicy('autogen')
.measurement('cpu')
.groupBy('host')
.where(lambda: "cpu" == 'cpu-total')
|eval(lambda: 100.0 - "usage_idle")
.as('used')
...
在示例 15 中,访问了数据帧中的两个值。在 where()
方法调用中,lambda 表达式使用标签 "cpu"
将数据帧过滤为仅包含 “cpu” 标签等于字面量值 'cpu-total'
的数据点。链接方法 eval()
也采用 lambda 表达式,该表达式访问字段 "usage-idle"
以计算 cpu 处理能力 “used”。请注意,groupBy()
方法使用字符串字面量 'host'
与数据系列中的标签名称匹配。然后,它将按此标签对数据进行分组。
- 命名 lambda 表达式结果 – Lambda 表达式结果被命名并使用
as()
方法作为字段添加到数据集。将as()
方法视为与 InfluxQL 中的 ‘AS’ 关键字的功能相同。请参阅上面示例 15 中的eval()
方法。lambda 表达式的结果可以使用双引号在其他 Lambda 表达式中访问,也可以像数据标签和字段一样,使用单引号在方法调用中访问。
示例 16 – 命名 lambda 表达式访问
...
|window()
.period(period)
.every(every)
|mean('used')
.as('stat')
// Thresholds
var alert = data
|eval(lambda: sigma("stat"))
.as('sigma')
.keep()
|alert()
.id('{{ index .Tags "host"}}/cpu_used')
.message('{{ .ID }}:{{ index .Fields "stat" }}')
.info(lambda: "stat" > info OR "sigma" > infoSig)
.warn(lambda: "stat" > warn OR "sigma" > warnSig)
.crit(lambda: "stat" > crit OR "sigma" > critSig)
上面的示例 16 继续了示例 15 中的管道。在示例 15 中,在 eval()
方法下命名为 'used'
的 lambda 表达式的结果然后在示例 16 中作为方法 'mean()'
的参数访问,该方法然后将其结果 命名为 'stat'
。然后开始一个新的语句。这包含对方法 'eval()'
的新调用,该方法具有访问 "stat"
并将其结果 设置为 'sigma'
的 lambda 表达式。命名结果 "stat"
也在 message()
方法和 alert()
链接方法下的阈值方法(info()
、warn()
、crit()
)中使用。命名结果 "sigma"
也在这些方法的 lambda 表达式中使用。
注意 – InfluxQL 节点和标签或字段访问 – InfluxQL 节点,例如示例 16 中的 mean()
,是包装 InfluxQL 函数的特殊节点。请参阅下面的 节点类型分类 部分。当使用此节点类型访问字段值、标签值或命名结果时,使用单引号。
示例 17 – 使用 InfluxQL 节点的字段访问
// Dataframe
var data = stream
|from()
.database('telegraf')
.retentionPolicy('autogen')
.measurement('cpu')
.groupBy('host')
.where(lambda: "cpu" == 'cpu-total')
|eval(lambda: 100.0 - "usage_idle")
.as('used')
|window()
.period(period)
.every(every)
|mean('used')
.as('stat')
在上面的示例 17 中,eval
结果被命名为 used
。链接方法 mean
是节点类型 InfluxQL 的别名。它包装了 InfluxQL mean
函数。在调用 mean 时,仅使用单引号访问命名结果 'used'
。
在字符串模板中访问值
正如字符串模板章节中提到的,可以从节点特定的属性以及标签和字段中添加值到输出字符串中。这可以在示例 16 中的 alert
节点下看到。访问器表达式被包裹在两个花括号中。要访问属性,在标识符前使用句点 .
。要从标签或字段访问值,请使用令牌 ‘index’,后跟一个空格和一个句点,然后是要访问的数据序列部分(例如 .Tags
或 .Fields
);实际名称随后在双引号中指定。
示例 18 – 在字符串模板中访问值
|alert()
.id('{{ index .Tags "host"}}/mem_used')
.message('{{ .ID }}:{{ index .Fields "stat" }}')
在上面的示例 18 中,属性方法 .id()
使用数据流中键为 "host"
的标签值来设置 id 值的一部分。然后,此值在属性方法 message()
中用作 .ID
。此属性方法还访问来自名为 "stat"
的结果的值。
有关更多具体信息,请参阅 Alert 节点。
类型转换
在 lambda 表达式中,可以使用无状态转换函数在类型之间转换值。
bool()
- 将字符串、int64 或 float64 转换为布尔值。int()
- 将字符串、float64、布尔值或持续时间类型转换为 int64。float()
- 将字符串、int64 或布尔值转换为 float64。string()
- 将 int64、float64、布尔值或持续时间值转换为字符串。duration()
- 将 int64、float64 或字符串转换为持续时间类型。
示例 19 – 类型转换
|eval(lambda: float("total_error_responses")/float("total_responses") * 100.0)
在上面的示例 19 中,使用了 float
转换函数来确保当数据系列中的字段值可能存储为整数时,计算出的百分比使用浮点精度。
数值精度
在消息中或写入 InfluxDB 时写入浮点值时,指定十进制精度可能有助于使值更具可读性或更易于比较。例如,在 alert
节点的 message()
方法中,可以将值“管道”传输到 printf
语句。
|alert()
.id('{{ index .Tags "host"}}/mem_used')
.message('{{ .ID }}:{{ index .Fields "stat" | printf "%0.2f" }}')
在 lambda 表达式中使用浮点值时,也可以使用 floor 函数和十的幂来舍入到精度较低的值。请注意,在字符串模板中使用 printf
要快得多。另请注意,由于值以 64 位写入,因此这对存储没有影响。如果将其与 InfluxDBOut
节点一起使用,例如在缩小数据时,可能会导致不必要的信息丢失。
示例 20 – 降低浮点数的精度
stream
// Select just the cpu measurement from our example database.
|from()
.measurement('cpu')
|eval(lambda: floor("usage_idle" * 1000.0)/1000.0)
.as('thousandths')
.keep('usage_user','usage_idle','thousandths')
|alert()
.crit(lambda: "thousandths" < 95.000)
.message('{{ index .Fields "thousandths" }}')
// Whenever we get an alert write it to a file.
.log('/tmp/alerts.log')
示例 20 完成了类似于使用 printf
的操作。 usage_idle
值向下舍入到千分之一百分比,然后用于 alert 节点的阈值方法中的比较。然后将其写入警报消息。
时间精度
由于 Kapacitor 和 TICKscripts 可用于将值写入 InfluxDB 数据库,因此在某些情况下,可能需要指定要使用的时间精度。一个例子是在使用计算平均值缩小数据时发生。要写入的精度可以设置为比默认值更粗糙的值,甚至超过存储桶大小,即通过调用诸如 window.every()
之类的方法设置的值。不建议使用大于存储桶大小的精度。指定时间精度可以带来存储和性能的改进。最常见的例子是在使用 InfluxDBOut
节点时,可以设置其 precision 属性。请注意,InfluxDBOut
节点默认为最精确的精度,即纳秒。重要的是不要混淆数学精度(最常用于字段值)和时间精度(为时间戳指定)。
示例 21 – 使用 InfluxDBOut 设置时间精度
stream
|from()
.database('telegraf')
.measurement('cpu')
.groupBy(*)
|window()
.period(5m)
.every(5m)
.align()
|mean('usage_idle')
.as('usage_idle')
|influxDBOut()
.database('telegraf')
.retentionPolicy('autogen')
.measurement('mean_cpu_idle')
.precision('s')
...
在示例 21 中,取自指南主题 连续查询,要写入数据库 “telegraf” 作为 measurement mean_cpu_idle
的系列的时间精度设置为单位秒。
精度的有效值与 InfluxDB 中使用的值相同。
表 6 – 精度单位
字符串 | 单位 |
---|---|
“ns” | 纳秒 |
“ms” | 毫秒 |
“s” | 秒 |
“m” | 分钟 |
“h” | 小时 |
语句
TICKscript 中有两种类型的语句:声明和表达式。声明可以声明变量或数据库,TICKscript 将使用它们。表达式表达方法调用的管道(也称为链),这些调用创建处理节点并设置其属性。
声明
TICKscript 使用两种类型的声明:数据库声明和变量声明。
数据库声明以关键字 dbrp
开头,后跟两个用句点分隔的字符串。第一个字符串声明脚本将使用的默认数据库。第二个字符串声明其保留策略。请注意,数据库和保留策略也可以在使用命令 kapacitor define
在命令行上定义任务时使用标志 -dbrp
声明,因此此语句是可选的。当使用时,数据库声明语句应是 TICKscript 的第一个声明。
示例 22 – 数据库声明
dbrp "telegraf"."autogen"
...
示例 22 声明 TICKscript 将针对数据库 telegraf
及其保留策略 autogen
使用。
变量声明以 var
关键字开头,后跟要声明的变量的标识符。后跟赋值运算符和文字右侧值,这将设置新变量的类型和值。
示例 23 – 典型声明
...
var db = 'website'
var rp = 'autogen'
var measurement = 'responses'
var whereFilter = lambda: ("lb" == '17.99.99.71')
var name = 'test rule'
var idVar = name + ':{{.Group}}'
...
示例 23 显示了六个声明语句。其中五个创建了包含字符串的变量,一个创建了 lambda 表达式。
声明也可以用于将表达式分配给变量。
示例 24 – 将表达式声明给变量
var data = stream
|from()
.database(db)
.retentionPolicy(rp)
在示例 24 中,data
变量保存了以节点 stream
开头的表达式中声明的流管道。
表达式
表达式以节点标识符或保存另一个表达式的变量标识符开头。然后,它将其他节点创建方法(链接方法)、属性设置器(属性方法)或用户定义函数 (UDF) 链接在一起。管道运算符 “|” 表示链接方法调用的开始,将新节点返回到链中。点运算符 “.” 添加属性设置器。at 运算符 “@” 引入用户定义函数。
表达式可以全部写在单行上,但这可能会导致可读性问题。命令 kapacitor show <taskname>
将在控制台输出中显示 TICKscript。此命令以美观的方式打印或使用换行符和缩进,而与定义 TICKscript 的编写方式无关。添加新行并缩进新的方法调用是编写 TICKscript 表达式的推荐做法。通常,当在表达式中引入新的链接方法时,会创建一个新行,并且链中的新链接会缩进三个或更多空格。同样,当调用新的属性设置器时,它会在新行上设置并额外缩进一定数量的空格。为了提高可读性,用户定义的函数应与链接方法缩进相同。
表达式以管道中最后一个节点的最后一个设置器结束。
示例 25 – 单行表达式
...
// Dataframe
var data = batch|query('''SELECT mean(used_percent) AS stat FROM "telegraf"."autogen"."mem" ''').period(period).every(every).groupBy('host')
// Thresholds
var alert = data|eval(lambda: sigma("stat")).as('sigma').keep()|alert().id('{{ index .Tags "host"}}/mem_used').message('{{ .ID }}:{{ index .Fields "stat" }}')
.info(lambda: "stat" > info OR "sigma" > infoSig).warn(lambda: "stat" > warn OR "sigma" > warnSig).crit(lambda: "stat" > crit OR "sigma" > critSig)
...
示例 25 显示了一个表达式,其中包含多个节点和设置器,所有节点和设置器都在同一行中声明。虽然这是可能的,但这不是推荐的风格。另请注意,Kapacitor 发行版附带的命令行实用程序 tickfmt
可用于重新格式化 TICKscript 以遵循推荐的风格。
示例 26 – 推荐的表达式语法
...
// Dataframe
var data = batch
|query('''SELECT mean(used_percent) AS stat FROM "telegraf"."autogen"."mem" ''')
.period(period)
.every(every)
.groupBy('host')
// Thresholds
var alert = data
|eval(lambda: sigma("stat"))
.as('sigma')
.keep()
|alert()
.id('{{ index .Tags "host"}}/mem_used')
.message('{{ .ID }}:{{ index .Fields "stat" }}')
.info(lambda: "stat" > info OR "sigma" > infoSig)
.warn(lambda: "stat" > warn OR "sigma" > warnSig)
.crit(lambda: "stat" > crit OR "sigma" > critSig)
// Alert
alert
.log('/tmp/mem_alert_log.txt')
...
示例 26 取自代码库中 mem_alert_batch.tick 示例,显示了编写表达式的推荐风格。此示例包含三个表达式语句。第一个以声明数据帧的 batch 节点开始。这被分配给变量 data
。第二个表达式采用 data
变量并定义警告消息的阈值。这被分配给 alert
变量。第三个表达式设置 alert
节点的 log
属性。
节点创建
除了两个例外(stream
和 batch
)之外,节点始终出现在管道表达式(链)中,在管道表达式中,它们通过链接方法创建。链接方法通常使用节点类型名称来标识。一个值得注意的例外是 InfluxQL 节点,它使用别名。请参阅下面的 节点类型分类 章节。
对于每种节点类型,创建该类型实例的方法使用相同的签名。因此,如果 query
节点创建一个 eval
节点并将其添加到链中,并且如果 from
节点也可以创建一个 eval
节点并将其添加到链中,则创建新 eval
节点的链接方法将接受相同的参数(例如,一个或多个 lambda 表达式),而与哪个节点创建它无关。
示例 27 – 在流中实例化 eval 节点
...
var data = stream
|from()
.database('telegraf')
.retentionPolicy('autogen')
.measurement('cpu')
.groupBy('host')
.where(lambda: "cpu" == 'cpu-total')
|eval(lambda: 100.0 - "usage_idle")
.as('used')
.keep()
...
示例 27 创建了三个节点:stream
、from
和 eval
。
示例 28 – 在批处理中实例化 eval 节点
...
var data = batch
|query('''SELECT 100 - mean(usage_idle) AS stat FROM "telegraf"."autogen"."cpu" WHERE cpu = 'cpu-total' ''')
.period(period)
.every(every)
.groupBy('host')
|eval(lambda: sigma("stat"))
.as('sigma')
.keep()
...
示例 28 也创建了三个节点:batch
、query
和 eval
。
示例 27 和 28 都创建了一个 eval
节点。尽管 eval
在示例 27 中链接在 from
节点之后,在示例 28 中链接在 query
节点之后,但链接方法的签名保持不变。
下面的 节点类型分类 章节中提供了一个简短的节点分类。节点类型目录可在主题 TICKscript 节点 下找到。
管道
重申一下,管道是由一个或多个表达式定义的逻辑排序的节点链。“逻辑排序” 意味着节点不能以任何随机顺序链接,而是根据它们在处理数据中的作用出现在管道中。管道可以从两个模式定义节点之一开始:batch
或 stream
。batch
管道的数据帧在 query
定义节点中定义。stream
管道的数据流在 from
定义节点中定义。在定义节点之后,可以跟随任何其他类型的节点。
标准节点类型使用管道 “|” 字符指示的链接方法添加到管道中。用户定义函数可以使用 at “@” 字符添加到管道中。
管道中的每个节点都具有可以使用句点 “.” 分隔的属性方法设置的内部属性。这些方法在节点处理数据之前被调用。
管道中的每个节点都可以更改传递给后续节点的数据:过滤数据、重组数据、将其简化为新的 measurement 等。在某些节点中,设置属性可以显着改变下游兄弟节点接收的数据。例如,对于 eval
节点,使用 as
属性设置 lambda 函数的名称会有效地阻止字段和标签名称向下游传递。因此,如果稍后的节点需要它们,则设置 keep
属性以将其保留在管道中可能很重要。
在使用 TICKscript 中的每种节点类型之前,熟悉 参考文档 很重要。
示例 29 – 典型的管道
// Dataframe
var data = batch
|query('''SELECT 100 - mean(usage_idle) AS stat FROM "telegraf"."autogen"."cpu" WHERE cpu = 'cpu-total' ''')
.period(period)
.every(every)
.groupBy('host')
// Thresholds
var alert = data
|eval(lambda: sigma("stat"))
.as('sigma')
.keep()
|alert()
.id('{{ index .Tags "host"}}/cpu_used')
.message('{{ .ID }}:{{ index .Fields "stat" }}')
.info(lambda: "stat" > info OR "sigma" > infoSig)
.warn(lambda: "stat" > warn OR "sigma" > warnSig)
.crit(lambda: "stat" > crit OR "sigma" > critSig)
// Alert
alert
.log('/tmp/cpu_alert_log.txt')
示例 29 显示了一个 batch
→query
管道,它使用两个变量分解为三个表达式。第一个表达式声明数据帧,第二个表达式声明警报阈值,最后一个表达式设置 alert
节点的 log
属性。整个管道以 batch
节点的声明开始,以调用属性方法 log()
结束。
节点类型分类
为了帮助理解不同节点在管道中扮演的角色,定义了一个简短的分类。有关每种节点类型的完整文档,请参阅主题 TICKscript 节点。
特殊节点
这些节点很特殊,因为可以使用类型名称以外的标识符创建和返回它们。可以使用表示其功能方面的别名。这可能适用于所有实例,例如 InfluxQL 节点,或者仅适用于一个实例,例如 Alert 节点。
alert
- 可以作为deadman
开关返回influxQL
- 直接调用 InfluxQL 中的函数,因此当调用使用 InfluxQL 方法名称的 TICKScript 链接方法时,可以返回它。- 示例 1:
from()|mean()
- 对 from 节点中定义的数据流调用 mean 函数,并返回 InfluxQL 节点。 - 示例 2:
query()|mode()
- 对 Query 节点中定义的数据帧调用 mode 函数,并返回 InfluxQL 节点。
- 示例 1:
数据源定义节点
TICKscript 管道中的第一个节点是 batch
或 stream
。它们定义了用于处理数据的数据源。
数据定义节点
模式定义节点通常后跟一些节点,这些节点的目的是定义要由其他节点处理的帧或流数据。
数据操作节点
可以使用操作节点更改或生成数据集中的值。
default
- 具有空的链接方法。其field
和tag
属性可用于设置数据系列中字段和标签的默认值。sample
- 链接方法采用 int64 或持续时间字符串。它根据计数或时间段提取数据样本。shift
- 链接方法采用持续时间字符串。它移动数据点时间戳。持续时间字符串前面可以加一个减号,以使时间戳向后移动。where
- 链接方法采用 lambda 节点。它与stream
管道一起工作,类似于 InfluxQL 中的WHERE
语句。window
- 具有空的链接方法。它使用属性方法配置。它在stream
管道中工作,通常在from
节点之后,以在移动时间范围内缓存数据。
处理节点
一旦定义了数据集,就可以将其传递给其他节点,这些节点将处理它,转换它或基于其中的更改触发其他进程。
用于更改数据结构或将管道混合在一起的节点
combine
- 链接方法采用一个或多个 lambda 表达式的列表。它可以将来自单个节点的数据与其自身组合。eval
- 链接方法采用一个或多个 lambda 表达式的列表。它评估接收到的每个数据点的表达式,并使用其as
属性,使结果可用于管道中后续的节点。请注意,当使用多个 lambda 表达式时,as
方法可以包含字符串列表,以命名每个 lambda 的结果。groupBy
- 链接方法采用一个或多个字符串的列表,这些字符串表示系列的标签。它按标签对传入数据进行分组。join
- 链接方法采用一个或多个变量的列表,这些变量引用管道表达式。它基于匹配的时间戳连接来自任意数量管道的数据。union
- 链接方法采用一个或多个变量的列表,这些变量引用管道表达式。它创建任意数量管道的联合。
用于转换或处理数据集内数据点的节点
delete
- 空的链接方法。它依赖于属性 (field
,tag
) 从数据点中删除字段和标签。derivative
- 链接方法采用一个字符串,表示将为其计算导数的字段。flatten
- 空的链接方法。它依赖于属性在特定维度上展平一组点。influxQL
- 特殊节点(见上文)。它提供对 InfluxQL 函数的访问。它不能直接创建。stateCount
- 链接方法采用 lambda 表达式。它计算处于给定状态的连续点的数量。stateDuration
- 链接方法采用 lambda 表达式。它计算给定状态持续的时间。stats
- 链接方法采用持续时间表达式。它在给定的间隔发出关于另一个节点的内部统计信息。
用于触发事件、进程的节点
alert
- 空的链接方法。它依赖于许多属性来配置警报的发出。deadman
- 实际上是一个辅助函数,它是alert
的别名,当数据流降至指定阈值以下时触发。httpOut
- 链接方法采用字符串。它缓存接收到的每个组的最新数据,使其可通过 Kapacitor http 服务器使用字符串参数作为最终定位器上下文。httpPost
- 链接方法采用字符串数组。它也可以为空。它将数据发布到字符串数组中指定的 HTTP 端点。influxDBOut
- 空的链接方法 – 通过属性设置器配置。它在接收到数据时将其写入 InfluxDB。k8sAutoscale
- 空的链接方法。它依赖于许多属性进行配置。它在 Kubernetes™ 资源上触发自动缩放。kapacitorLoopback
- 空的链接方法 – 通过属性设置器配置。它将数据写回 Kapacitor 流。log
- 空的链接方法。它依赖于level
和prefix
属性进行配置。它记录所有通过它的数据。
用户定义函数 (UDF)
用户定义函数是节点,这些节点实现由用户程序或脚本定义的功能,这些程序或脚本作为单独的进程运行,并通过套接字或标准系统数据流与 Kapacitor 通信。
UDF
- 签名、属性和功能由用户定义。要了解有关编写用户定义函数的信息,请参阅 用户定义函数网络研讨会,可在 Influx 在线大学 中找到。
内部使用的节点 - 请勿使用
noOp
- 一个执行任何操作的辅助节点。请勿使用它!
TICKscript 中的 InfluxQL
InfluxQL 主要在 TICKscript 的 query
节点中出现,其链接方法采用 InfluxQL 查询字符串。这几乎总是一个 SELECT
语句。
InfluxQL 在语法上与 SQL 非常相似。在为 TICKscript query
节点编写查询字符串时,通常只需要三个子句:SELECT
、FROM
和 WHERE
。一般模式如下
SELECT {<FIELD_KEY> | <TAG_KEY> | <FUNCTION>([<FIELD_KEY>|<TAG_KEY])} FROM <DATABASE>.<RETENTION_POLICY>.<MEASUREMENT> WHERE {<CONDITIONAL_EXPRESSION>}
- 基本
SELECT
子句可以采用一个或多个字段或标签键或函数。这些可以与数学运算和文字值组合。它们的值或结果将添加到数据帧,并且可以使用AS
子句进行别名。星号*
通配符也可以用于从 measurement 中检索所有标签和字段。- 当使用
AS
子句时,别名标识符稍后可以在 TICKscript 中通过使用双引号作为命名结果来访问。
- 当使用
FROM
子句需要从中选择值的数据库、保留策略和 measurement 名称。这些令牌中的每一个都用句点分隔。数据库和保留策略的值需要使用双引号设置。WHERE
子句需要条件表达式。这可能包括AND
和OR
布尔运算符以及数学运算。
示例 30 – 简单的 InfluxQL 查询语句
batch
|query('SELECT cpu, usage_idle FROM "telegraf"."autogen".cpu WHERE time > now() - 10s')
.period(10s)
.every(10s)
|httpOut('dump')
示例 30 显示了一个简单的 SELECT
语句,该语句从 cpu measurement 中获取 cpu
标签和 usage_idle
字段,记录在过去十秒内。
示例 31 – 带有变量的简单 InfluxQL 查询语句
var my_field = 'usage_idle'
var my_tag = 'cpu'
batch
|query('SELECT ' + my_tag + ', ' + my_field + ' FROM "telegraf"."autogen".cpu WHERE time > now() - 10s')
.period(10s)
.every(10s)
|httpOut('dump')
示例 31 重复了示例 30 中的相同查询,但展示了如何将变量添加到查询字符串中。
示例 32 – 带有函数调用的 InfluxQL 查询语句
...
var data = batch
|query('''SELECT 100 - mean(usage_idle) AS stat FROM "telegraf"."autogen"."cpu" WHERE cpu = 'cpu-total' ''')
.period(period)
.every(every)
.groupBy('host')
...
示例 32 显示了一个 SELECT
语句,该语句在 SELECT
子句中包含函数和数学运算,以及 AS
别名子句。
请注意,select 语句直接传递到 InfluxDB API。在 InfluxQL 查询字符串中,字段和标签名称不需要像 TICKscript 中的其他地方那样使用双引号访问。但是,数据库名称和保留策略确实用双引号包裹。字符串文字,例如 'cpu-total'
在查询字符串内用单引号表示。
有关使用查询语言的完整介绍,请参阅 InfluxQL 文档。
Lambda 表达式
Lambda 表达式出现在许多链接和属性方法中。最常见的两种用法是在创建 eval
节点和在 alert
节点上定义阈值属性。它们使用关键字 “lambda” 后跟冒号声明:lambda:
。它们可以包含数学和布尔运算以及对大型内部函数库的调用。对于许多节点,可以通过在节点上设置 as
属性来捕获它们的结果。
内部函数可以是无状态的,例如常见的数学和字符串操作函数,也可以是有状态的,每次新调用都会更新内部值。截至 1.3 版本,提供了三个有状态函数。
sigma
- 计算给定值与运行平均值的标准偏差数。count
- 计算处理的值的数量。spread
- 计算所有值的运行范围。
lambda 表达式及其用法的完整范围在主题 Lambda 表达式 中介绍。
在 lambda 表达式中,可以使用其纯标识符访问 TICKscript 变量。可以访问数据系列的标签和字段值,方法是用双引号将它们括起来。文字也可以直接使用。
示例 33 – Lambda 表达式
...
// Parameters
var info = 70
var warn = 85
var crit = 92
var infoSig = 2.5
var warnSig = 3
var critSig = 3.5
var period = 10s
var every = 10s
// Dataframe
var data = batch
|query('''SELECT mean(used_percent) AS stat FROM "telegraf"."autogen"."mem" ''')
.period(period)
.every(every)
.groupBy('host')
// Thresholds
var alert = data
|eval(lambda: sigma("stat"))
.as('sigma')
.keep()
|alert()
.id('{{ index .Tags "host"}}/mem_used')
.message('{{ .ID }}:{{ index .Fields "stat" }}')
.info(lambda: "stat" > info OR "sigma" > infoSig)
.warn(lambda: "stat" > warn OR "sigma" > warnSig)
.crit(lambda: "stat" > crit OR "sigma" > critSig)
// Alert
alert
.log('/tmp/mem_alert_log.txt')
示例 33 包含四个 lambda 表达式。第一个表达式传递给 eval
节点。它调用内部有状态函数 sigma
,它将命名结果 stat
传递到其中,该命名结果使用 query
节点的查询字符串中的 AS
子句设置。通过 eval
节点的 .as()
设置器,其结果被命名为 sigma
。其他三个 lambda 表达式出现在 alert
节点的阈值确定属性方法内部。这些 lambda 表达式还访问命名结果 stat
和 sigma
以及在脚本开头声明的变量。它们各自定义了一系列布尔运算,这些运算设置了警报消息的级别。
语法子空间之间变量用法的总结
以下章节总结了如何在 TICKscript 和不同的语法子空间中访问变量和数据系列标签和字段。
TICKscript 变量
声明示例
var my_var = 'foo'
var my_field = `usage_idle`
var my_num = 2.71
访问方式…
- 在 TICKscript 中,只需使用标识符。
var my_other_num = my_num + 3.14
...
|default()
.tag('bar', my_var)
...
- 在查询字符串中,只需使用带有字符串连接的标识符。
...
|query('SELECT ' + my_field + ' FROM "telegraf"."autogen".cpu WHERE host = \'' + my_var + '\'' )
...
- 在 lambda 表达式中,只需使用标识符。
...
.info(lambda: "stat" > my_num )
...
- 在 InfluxQL 节点中,使用标识符。请注意,在大多数情况下,字符串将用作字段或标签名称。
...
|mean(my_var)
...
标签、字段或命名结果
示例
...
|query('SELECT mean(usage_idle) AS mean ...')
...
|eval(lambda: sigma("stat"))
.as('sigma')
...
访问方式…
- 在 TICKscript 方法调用中,使用单引号。
...
|derivative('mean')
...
- 在查询字符串中,直接在字符串中使用标识符。
...
|query('SELECT cpu, usage_idle FROM "telegraf"."autogen".cpu')
...
- 在 lambda 表达式中,使用双引号。
...
|eval(lambda: 100.0 - "usage_idle")
...
|alert
.info(lambda: "sigma" > 2 )
...
- 在 InfluxQL 节点中,使用单引号。
...
|mean('used')
...
注意事项
文字与字段值
请记住,文字字符串值使用单引号声明。双引号仅在 lambda 表达式中用于访问标签和字段的值。在大多数情况下,将双引号代替单引号使用将被捕获为错误:unsupported literal type
。另一方面,当打算使用双引号时使用单引号,即访问字段值,将不会被捕获,并且如果这种情况发生在 lambda 表达式中,则可以使用文字值代替所需的标签或字段值。
从 Kapacitor 1.3 开始,可以使用双引号声明变量,这是无效的,并且解析器不会将其标记为错误。例如,var my_var = "foo"
将会通过,只要它不被使用。但是,当此变量在 Lambda 表达式或其他方法调用中使用时,它将触发编译错误:unsupported literal type *ast.ReferenceNode
。
循环重写
当使用 InfluxDBOut 节点时,请注意不要创建到从中读取数据的同一数据库和同一 measurement 的循环重写。
示例 34 – 循环重写
stream
|from()
.measurement('system')
|eval(lambda: "n_cpus" + 1)
.as('n_cpus')
|influxDBOut()
.database('telegraf')
.measurement('system')
注意:示例 34 说明了如何创建无限循环。请不要使用它!
示例 34 中的脚本可用于在数据库 telegraf
上定义任务,并使用保留策略 autogen
。例如
kapacitor define circular_task -type stream -tick circular_rewrite.tick -dbrp telegraf.autogen
在这种情况下,上面的脚本将无限循环,添加一个新数据点,其中包含字段 n_cpus
的新值,直到任务停止。
警报和 ID
当将 deadman
方法与一个或多个 alert
节点一起使用时,或者当在管道中使用多个 alert
节点时,请务必使用属性方法 id()
设置 ID 属性。ID 的值在每个节点上必须是唯一的。否则,Kapacitor 会认为它们都是同一组警报,因此某些警报可能不会按预期显示。
下一步去哪里?
请参阅 Github 代码库中的 示例。另请参阅 指南 章节中的详细用例解决方案。
此页是否对您有帮助?
感谢您的反馈!
支持和反馈
感谢您成为我们社区的一份子!我们欢迎并鼓励您提供关于 Kapacitor 和本文档的反馈和错误报告。要查找支持,请使用以下资源