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 时,解析器会检查在节点上调用的链接方法是否有效。如果遇到无效的链接方法,解析器将抛出错误,并显示消息“节点类型
代码表示
源文件应使用 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 处理能力“已用”。请注意,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 函数和 10 的幂来舍入到精度较低的值。请注意,在字符串模板中使用 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
值向下舍入到千分之一百分比,然后在警报节点的阈值方法中用于比较。然后将其写入警报消息。
时间精度
由于 Kapacitor 和 TICKscript 可用于将值写入 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”作为度量 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 示例,显示了编写表达式的推荐风格。此示例包含三个表达式语句。第一个以数据帧的批处理节点声明开始。这被分配给变量 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 “@”字符添加到管道中。
管道中的每个节点都有可以使用句点“.”分隔的属性方法设置的内部属性。这些方法在节点处理数据之前被调用。
管道中的每个节点都可以更改传递给后续节点的数据:过滤数据、重组数据、将其缩减为新的度量等等。在某些节点中,设置属性可能会显着改变下游兄弟节点接收的数据。例如,对于 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
- 链接方法接受字符串。它缓存其接收的每个组的最新数据,使其可以通过 Kapicator http 服务器使用字符串参数作为最终定位器上下文来访问。httpPost
- 链接方法接受字符串数组。它也可以为空。它将数据发布到字符串数组中指定的 HTTP 端点。influxDBOut
- 空的链接方法 – 通过属性设置器配置。它在接收数据时将其写入 InfluxDB。k8sAutoscale
- 空的链接方法。它依赖于许多属性进行配置。它在 Kubernetes™ 资源上触发自动缩放。kapacitorLoopback
- 空的链接方法 – 通过属性设置器配置。它将数据写回 Kapacitor 流。log
- 空的链接方法。它依赖于level
和prefix
属性进行配置。它记录通过它的所有数据。
用户定义的函数 (UDF)
用户定义的函数是实现由用户程序或脚本定义的功能的节点,这些程序或脚本作为单独的进程运行,并通过套接字或标准系统数据流与 Kapacitor 通信。
UDF
- 签名、属性和功能由用户定义。要了解有关编写用户定义的函数的信息,请参阅 用户定义的函数网络研讨会,网址为 Influx 在线大学。
内部使用的节点 - 请勿使用
noOp
- 一个不执行任何操作的辅助节点。请勿使用它!
TICKscript 中的 InfluxQL
InfluxQL 主要在 query
节点中出现在 TICKscript 中,其链接方法接受 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
子句进行别名。星号*
通配符也可以用于从度量中检索所有标签和字段。- 使用
AS
子句时,别名标识符稍后可以在 TICKscript 中通过使用双引号作为命名结果来访问。
- 使用
FROM
子句需要从中选择值的数据库、保留策略和度量名称。每个标记都用点分隔。数据库和保留策略的值需要使用双引号设置。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 度量中获取 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 节点时,请注意不要创建到从中读取数据的同一数据库和同一度量的循环重写。
示例 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 代码库中的 示例。另请参阅 指南部分中的详细用例解决方案。
此页是否对您有帮助?
感谢您的反馈!