Documentation

TICKscript 语法

概念

章节 IntroductionGetting Started 介绍了 节点管道 的关键概念。节点代表过程调用单元,可以以批量或逐点流的方式接收数据,然后改变这些数据、存储这些数据,或根据这些数据的变化触发其他活动,例如警报。管道只是逻辑上组织的节点链。

在Kapacitor中,TICKscript用于直接定义任务以及定义模板任务,这些模板任务可以被重用以生成新的任务。

TICKscript语法受到了许多不同语言的启发。其中最具影响力的是Go。例如,这可以在变量声明习惯、字符串模板、duration等类型、在lambda表达式中使用的函数中看到,它的影响也体现在文档的其他地方。

语法子空间

在使用TICKscript时,会遇到一些语法子空间,这些子空间对于某些用户来说造成了困惑。最主要的是TICKscript文件的TICKscript语法。这主要由变量声明和在管道中连接在一起的节点组成。在创建时,query节点需要一个表示InfluxQL语句的字符串。因此,InfluxQL表示可能使用的第一个语法子空间。其他节点和方法使用Lambda表达式,这代表了将要遇到的第二个语法子空间。这些空间之间的语法,例如在访问变量、标签和字段值时,可能会有所不同,这有时会成为困惑的来源。

总结一下,在TICKscript中需要注意的两个语法子空间是:

有向无环图 (DAGs)

在《入门指南》中提到,管道是一个有向无环图(DAG)。 (欲了解更多信息,请参见 WolframWikipedia)。它包含有限数量的节点(即顶点)和边。每条边都是从一个节点指向另一个节点。没有任何边路径可以返回到路径中的早期节点,这将导致循环或回路。TICKscript路径(即管道和链)通常以数据源定义节点开始,并与数据集定义节点连接,然后将其结果传递给数据处理和处理节点。

TICKscript 语法

TICKscript对大小写敏感并使用Unicode。 TICKscript解析器从上到下、从左到右扫描TICKscript代码,实例化变量和节点,然后将它们链接到管道中。当加载TICKscript时,解析器检查对节点调用的链接方法是否有效。如果遇到无效的链接方法,解析器将抛出一条错误消息:“在上没有方法或属性”。

代码表示

源文件应使用 UTF-8 编码。脚本分为 声明表达式。声明会创建一个变量,并发生在一行上。表达式可以跨越多行,并导致整个管道的创建,一个管道 或一个管道 分支

空白 用于声明中将变量名与运算符和字面值分开。它也用于表达式中创建缩进,这表示方法调用的层次结构。这也有助于使脚本更易读。否则,空白会被忽略。

注释可以通过在文本前使用一对正斜杠“//”来创建在单一行上。注释正斜杠可以在空白字符之前,并且不需要是新行的第一个字符。

关键词

关键词是具有特殊含义的符号,因此不能用作函数或变量的标识符。TICKscript 是简洁的,仅包含一小组关键词。

表 1 – 关键字

单词用法
TRUE字面布尔值“true”。
FALSE字面布尔值“false”。
AND标准布尔联合运算符。
标准布尔析取运算符。
lambda:后面的内容将被解释为一个lambda表达式的标志。
var开始变量声明。
dbrp开始一个数据库声明

由于在TICKscript中可用的原生节点类型集合有限,每种节点类型,如 batchstream,都可以被视为关键。节点类型及其分类在下面的节点类型的分类章节中有详细讨论。

运算符

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
逻辑或status > warn OR delta > sigma

标准操作符在TICKscript和Lambda表达式中使用。

表 3 – 链接运算符

操作符用法示例
|声明一个链式方法调用,该调用创建一个新节点的实例并将其链接到上面的节点。stream
   |from()
.声明一个属性方法调用,设置或更改其所属节点的内部属性。from()
   .database(mydb)
@声明一个用户定义的函数 (UDF) 调用。实际上是一个链式方法,将一个新的 UDF 节点添加到管道中。from()
...
@MyFunc()

链式操作符在表达式中用于定义管道或管道段。

变量和字面量

TICKscript中的变量对于存储和重用值以及提供一个友好的助记符以快速理解变量所代表的内容是非常有用的。它们通常与字面值的赋值一起声明。在一个意图被用作template task的TICKscript中,它们也可以仅仅用一个类型标识符来声明。

变量

变量使用关键字 var 在声明的开始处声明。 变量是不可变的,不能在脚本后面重新赋值,不过它们可以在其他声明中使用并可以传递给方法。 变量还被用于模板任务中,作为占位符,当模板用于创建新任务时填充。

有关模板任务的详细介绍,请参阅指南模板任务。如果一个TICKscript被证明有用,可能希望将其作为模板任务重新使用,以便快速创建其他类似任务。因此,建议尽可能使用变量。

命名变量

变量标识符必须以标准 ASCII 字母开头,后面可以跟任意数量的字母、数字和下划线。可以使用大写和小写字母。在用于直接定义任务的 TICKscript 中,变量所持有的类型取决于分配给它的字面值。在为任务模板编写的 TICKscript 中,也可以使用关键字设置变量将会持有的类型。在用于直接定义任务的 TICKscript 中,使用类型标识符会导致编译时错误 invalid TICKscript: missing value for var "".

示例 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 识别五种类型标识符。这些标识符可以直接用于用于模板任务的 TICKscripts 中。否则,字面量的类型将根据其声明进行解释。

表 4 – 类型标识符

标识符用法
string在模板任务中,将变量声明为类型 string
duration在模板任务中,声明一个类型为 duration 的变量。
int在模板任务中,将变量声明为类型 int64
float在模板任务中,将变量声明为类型 float64
lambda在模板任务中,将变量声明为Lambda表达式类型。
布尔值

布尔值是使用布尔关键字生成的: TRUEFALSE。请注意,这些关键字使用全大写字母。当使用小写字符时,解析器将抛出错误,例如 Truetrue

示例 3 – 布尔字面量

var true_bool = TRUE
...
   |flatten()
       .on('host','port')
       .dropOriginalFieldName(FALSE)
...

在上述例子3中,第一行展示了使用布尔字面量的简单赋值。第二个例子展示了在方法调用中使用布尔字面量 FALSE

数值类型

任何仅包含数字的文字标记,并且可选地带有小数,将导致生成一个数值类型的实例。TICKscript 理解两种基于 Go 的数值类型:int64float64。任何包含小数点的数值标记将导致创建一个 float64 值。任何以没有小数点结尾的数值标记将导致创建一个 int64 值。如果一个整数前面带有零字符 0,则它被解释为八进制。

示例 4 - 数值文字

var my_int = 6
var my_float = 2.71828
var my_octal = 0400
...

在上面的示例4中 my_int 的类型是 int64my_float 的类型是 float64,而 my_octal 的类型是 int64 八进制。

持续时间文字

持续时间文字定义了一段时间。它们的语法遵循InfluxQL中存在的相同语法。持续时间文字由两个部分组成:一个整数和一个时间单位。它本质上是一个以一个或一对保留字符结束的整数,这些字符代表一个时间单位。

下表展示了用于声明持续时间类型的时间单位。

表 5 – 持续时间文字单位

单位含义
u或µ微秒(1百万分之一秒)
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 中,三个值被添加到两个字符串模板中。在对设置器 id() 的调用中,标签 "host" 的值被添加到字符串的开头。对设置器 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 节点被用作管道的根节点,节点 fromevalwindowmean 按顺序链接在一起。在第二部分,管道通过赋值给变量 alert 被扩展,从而可以将第二个 eval 节点应用于数据。

处理标签、字段和变量

在任何脚本中,仅仅声明变量是不够的。它们所持有的值也必须被访问。在TICKscript中,还必须处理从InfluxDB数据系列中提取的标签和字段所持有的值。这在目前为止展示的示例中最为明显。此外,由lambda表达式生成的值可以作为新字段添加到数据集中,然后作为这些表达式的命名结果进行访问。以下部分将探讨如何处理不仅是变量,还有可以从数据中提取的标签和字段值,以及命名结果。

访问值

访问数据标签和字段,使用字符串字面量和访问TICKscript变量各涉及不同的语法。此外,还可以访问与某些节点一起使用的lambda表达式的结果。

  • 变量 – 要访问 TICKscript 变量,只需使用其标识符。

示例 14 – 变量访问

var db = 'website'
...
var data = stream
 |from()
     .database(db)
...

在示例14中,变量 db 被赋值为字面量 'website'。然后它在链式方法 from() 下的设置器 .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 中,名为 'used' 的 lambda 表达式在 eval() 方法下的结果,然后在示例 16 中作为方法 'mean()' 的参数被访问,随后将其结果命名为 as 'stat'。然后开始一个新的语句。这包含对方法 'eval()' 的新调用,该方法有一个 lambda 表达式,访问 "stat" 并将其结果 as 'sigma'。命名结果 "stat" 也在 message() 方法及 alert() 链式方法下的阈值方法(info()warn()crit())中被访问。命名结果 "sigma" 也在这些方法的 lambda 表达式中被使用。

注意 - InfluxQL 节点和标签或字段访问 - InfluxQL 节点,例如 mean() 在示例 16 中,是包装 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 node

类型转换

在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 值被向下舍入到千分之一,并用于在警报节点的阈值方法中的比较。然后它被写入警报消息中。

时间精度

由于Kapacitor和TICKscripts可以用于将值写入InfluxDB数据库,在某些情况下,可能希望指定要使用的时间精度。当使用计算得出的平均值来缩小数据时,就会出现一个示例。要写入的精度可以设置为比默认值更粗的值,甚至超过桶大小,即通过调用像window.every()这样的函数所设置的值。不建议使用大于桶大小的精度。指定时间精度可以带来存储和性能的改善。最常见的示例发生在使用InfluxDBOut节点时,其精度属性可以被设置。请注意,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 有两种类型的声明:数据库声明和变量声明。

A 数据库声明 以关键字 dbrp 开始,后面跟着两个用句点分隔的字符串。第一个字符串声明默认数据库,脚本将使用该数据库。第二个字符串声明其保留策略。请注意,数据库和保留策略也可以使用标志 -dbrp 在命令行中定义任务时通过命令 kapacitor define 声明,因此此语句是可选的。使用时,数据库声明语句应为 TICKscript 的第一个声明。

示例 22 – 数据库声明

dbrp "telegraf"."autogen"
...

示例 22 声明 TICKscript 将用于数据库 telegraf,其保留策略为 autogen

A 变量声明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 显示了六个声明语句。其中五个创建了 holding strings 的变量,一个创建了 lambda 表达式。

声明还可以用于将一个表达式赋值给一个变量。

示例 24 – 将表达式声明为变量

var data = stream
    |from()
        .database(db)
        .retentionPolicy(rp)

在示例24中,data 变量持有在以节点 stream 开头的表达式中声明的流管道。

表达式

一个表达式以节点标识符或者持有另一个表达式的变量标识符开始。然后它将额外的节点创建方法(链式方法)、属性设置器(属性方法)或用户定义函数(UDF)链接在一起。管道操作符“|”表示链式方法调用的开始,返回一个新的节点到链中。点操作符“.”添加一个属性设置器。at操作符“@”引入一个用户定义函数。

表达式可以全部写在单行上,但这可能会导致可读性问题。命令 kapacitor show 将显示 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 属性。

节点创建

有两个例外(streambatch),节点总是出现在管道表达式(链)中,它们通过链式方法创建。链式方法通常通过节点类型名称来识别。一个显著的例外是 InfluxQL 节点,它使用别名。请参见下面的 节点类型分类 部分。

对于每种节点类型,创建该类型实例的方法使用相同的签名。因此,如果一个 query 节点创建一个 eval 节点并将其添加到链中,并且如果一个 from 节点也可以创建一个 eval 节点并将其添加到链中,则创建新的 eval 节点的链式方法将接受相同的参数(例如一个或多个lambda表达式),而不管是哪个节点创建的。

示例 27 – 在流中实例化评估节点

...
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创建了三个节点: streamfromeval

示例 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,queryeval

示例27和28都创建了一个 eval 节点。尽管在示例27中 eval 节点链在 from 节点下,而在示例28中链在 query 节点下,但链式方法的签名保持不变。

下面的部分展示了节点的简短分类法 Taxonomy of node types 。节点类型的目录在主题 TICKscript nodes 下可用。

管道

重申一下,管道是由一个或多个表达式定义的逻辑顺序节点链。“逻辑顺序”意味着节点不能以任何随机顺序连接,而是根据它们在处理数据中的角色出现在管道中。管道可以从两种模式定义节点之一开始: batchstreambatch 管道的数据框架在 query 定义节点中定义。 stream 管道的数据流在 from 定义节点中定义。在定义节点之后可以跟随任何其他类型的节点。

标准节点类型通过管道中的链式方法添加,链式方法由管道符“|”表示。用户定义的函数可以使用“@”符号添加到管道中。

流水线中的每个节点都有内部属性,可以使用用句点“.”划分的属性方法进行设置。这些方法在节点处理数据之前被调用。

管道中的每个节点可以改变传递给后续节点的数据:过滤、重构、减少到新的测量等等。在某些节点中,设置一个属性可以显著改变下游兄弟节点接收到的数据。例如,在一个 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显示了一个 batchquery 管道,使用两个变量拆分为三个表达式。第一个表达式声明数据框,第二个表达式声明警报阈值,最后一个表达式设置 alert 节点的 log 属性。整个管道从声明 batch 节点开始,最终调用属性方法 log()

节点类型的分类

为了帮助理解不同节点在流程中的角色,定义了一个简短的分类法。有关每种节点类型的完整文档,请参阅主题 TICKscript Nodes

特殊节点

这些节点是特别的,因为它们可以使用其他标识符而不是它们的类型名称来创建和返回。可以使用代表其功能方面的别名。这可能适用于所有实例,例如 InfluxQL 节点,或者仅适用于一个实例,例如警报节点。

  • alert - 可以作为 deadman 开关返回
  • influxQL - 直接调用InfluxQL中的函数,因此可以在使用InfluxQL方法名称的TICKScript链式方法被调用时返回。
    • 示例 1: from()|mean() - 在from节点定义的数据流上调用平均值函数并返回一个InfluxQL节点。
    • 示例 2: query()|mode() - 在Query节点定义的数据框上调用众数函数并返回一个InfluxQL节点。

数据源定义节点

TICKscript管道中的第一个节点是batchstream。它们定义了在处理数据时使用的数据源

  • batch - 链式方法调用语法在声明中未使用。
  • stream - 链式方法调用语法在声明中未使用。

数据定义节点

模式定义节点通常后面跟着的节点,其目的是定义要由其他节点处理的数据

  • from - 具有空链方法。只能跟随一个 stream 节点。使用属性方法进行配置。
  • query - 链式方法接受一个查询字符串。只能跟随一个 batch 节点。

数据处理节点

数据集中的值可以通过操作节点进行修改或生成。

  • default - 具有一个空的链式方法。它的 fieldtag 属性可用于设置数据系列中字段和标签的默认值。
  • sample - 链式方法接受一个 int64 或持续时间字符串。它根据计数或时间段提取数据样本。
  • shift - 链式方法接受一个持续时间字符串。它将数据点的时间戳向前移动。持续时间字符串可以在前面加上负号,以将时间戳向后移动。
  • where - 链接方法接受一个 lambda 节点。它与 stream 流水线一起工作,类似于 InfluxQL 中的 WHERE 语句。
  • window - 具有一个空的链接方法。它使用属性方法进行配置。通常在from节点之后的stream管道中工作,用于在移动时间范围内缓存数据。

处理节点

一旦数据集被定义,它可以被传递到其他节点,这些节点将处理它、转换它或根据内部的变化触发其他流程。

  • 用于更改数据结构或混合管道的节点:

    • combine - chaining method takes a list of one or more lambda expression. It can combine the data from a single node with itself.
    • eval - chaining method takes a list of one or more lambda expressions. It evaluates expressions on each datapoint it receives and, using its as property, makes the results available to nodes that follow in the pipeline. Note that when multiple lambda expressions are used, the as method can contain a list of strings to name the results of each lambda.
    • groupBy - chaining method takes a list of one or more strings representing the tags of the series. It groups incoming data by tags.
    • join - chaining method takes a list of one or more variables referencing pipeline expressions. It joins data from any number of pipelines based on matching time stamps.
    • union - chaining method takes a list of one or more variables referencing pipeline expressions. It creates a union of any number of pipelines.
  • 用于转换或处理数据集中数据点的节点:

    • delete - empty chaining method. It relies on properties (field, tag) to delete fields and tags from datapoints.
    • derivative - chaining method takes a string representing a field for which a derivative will be calculated.
    • flatten - empty chaining method. It relies on properties to flatten a set of points on specific dimensions.
    • influxQL - special node (see above). It provides access to InfluxQL functions. It cannot be created directly.
    • stateCount - chaining method takes a lambda expression. It computes the number of consecutive points that are in a given state.
    • stateDuration - chaining method takes a lambda expression. It computes the duration of time that a given state lasts.
    • stats - chaining method takes a duration expression. It emits internal stats about another node at the given interval.
  • 用于触发事件、过程的节点:

    • alert - empty chaining method. It relies on a number of properties for configuring the emission of alerts.
    • deadman - actually a helper function, it is an alias for an alert that gets triggered when data flow falls below a specified threshold.
    • httpOut - chaining method takes a string. It caches the most recent data for each group it receives, making it available over the Kapicator http server using the string argument as the final locator context.
    • httpPost - chaining method takes an array of strings. It can also be empty. It posts data to HTTP endpoints specified in the string array.
    • influxDBOut - empty chaining method – configured through property setters. It writes data to InfluxDB as it is received.
    • k8sAutoscale - empty chaining method. It relies on a number of properties for configuration. It triggers autoscale on Kubernetes™ resources.
    • kapacitorLoopback - empty chaining method – configured through property setters. It writes data back into the Kapacitor stream.
    • log - empty chaining method. It relies on level and prefix properties for configuration. It logs all data that passes through it.

用户定义函数 (UDFs)

用户定义的函数是实现由用户程序或脚本定义的功能的节点,这些程序或脚本作为独立进程运行,并通过套接字或标准系统数据流与Kapacitor通信。

内部使用的节点 - 不要使用

  • noOp - 一个不执行任何操作的辅助节点。不要使用它!

TICKscript中的InfluxQL

InfluxQL 主要出现在 query 节点中,其链式方法接受一个 InfluxQL 查询字符串。这通常会是一个 SELECT 语句。

InfluxQL在语法上与SQL非常相似。编写TICKscript query 节点的查询字符串时,通常只需要三个子句: SELECTFROMWHERE。一般模式如下:

SELECT {<FIELD_KEY> | <TAG_KEY> | <FUNCTION>([<FIELD_KEY>|<TAG_KEY])} FROM <DATABASE>.<RETENTION_POLICY>.<MEASUREMENT> WHERE {<CONDITIONAL_EXPRESSION>}
  • 基本的 SELECT 子句可以包含一个或多个字段或标签键,或函数。这些可以与数学运算和字面值结合使用。它们的值或结果将被添加到数据框中,并可以通过 AS 子句来别名。星号 * 通配符也可以用来从测量中检索所有标签和字段。
    • 当使用 AS 子句时,别名标识符可以在 TICKscript 中通过使用双引号作为命名结果来访问。
  • FROM 子句需要指定数据库、保留策略以及要选择值的测量名称。这些标记之间用点分隔。数据库和保留政策的值需要使用双引号设置。
  • WHERE 子句需要一个条件表达式。这可能包括 ANDOR 布尔运算符以及数学运算。

示例 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 Expressions 中呈现。

在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 表达式还访问命名结果 statsigma 以及在脚本开始时声明的变量。它们各自定义了一系列布尔操作,用于设置警报消息的级别。

语法子空间之间变量使用总结

以下部分总结了如何在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上的 示例。另请参阅 指南 部分中的详细用例解决方案。



Flux的未来

Flux 正在进入维护模式。您可以像现在一样继续使用它,而无需对您的代码进行任何更改。

阅读更多

InfluxDB 3 开源版本现已公开Alpha测试

InfluxDB 3 Open Source is now available for alpha testing, licensed under MIT or Apache 2 licensing.

我们将发布两个产品作为测试版的一部分。

InfluxDB 3 核心,是我们新的开源产品。 它是一个用于时间序列和事件数据的实时数据引擎。 InfluxDB 3 企业版是建立在核心基础之上的商业版本,增加了历史查询能力、读取副本、高可用性、可扩展性和细粒度安全性。

有关如何开始的更多信息,请查看: