LogQL 模板函数
Go 模板语言嵌入在 Loki 查询语言 LogQL 中。用于 | line_format
和 | label_format
的文本模板格式支持函数的使用。
注意
在下面的示例中,我们使用反引号 (` `) 来引用模板字符串。这是因为某些模板字符串包含双引号,使用反引号可以避免转义双引号。如果您使用不同的引用风格,可能需要对双引号进行转义。
更多信息请参考Go 模板文档。
此外,您还可以使用 __line__
函数访问日志行,并使用 __timestamp__
函数访问时间戳。
模板 Pipeline 语法
Pipeline 是一个可能链式连接的“命令”序列。命令可以是简单值(参数)、函数或方法调用(可能带多个参数)。Pipeline 可以通过管道符 ' | ' 将命令序列“链式连接”。在链式 Pipeline 中,每个命令的结果作为最后一个参数传递给下一个命令。Pipeline 中最后一个命令的输出即为 Pipeline 的值。
您可以利用Pipeline将多个函数连接起来。
示例
`{{ .path | replace " " "_" | trunc 5 | upper }}`
对于返回布尔值的函数,例如 contains
, eq
, hasPrefix
和 hasSuffix
,您可以应用 AND
/ OR
以及嵌套的 if
逻辑。
示例
`{{ if and (contains "he" "hello") (contains "llo" "hello") }} yes {{end}}`
`{{ if or (contains "he" "hello") (contains("llo" "hello") }} yes {{end}}`
`{{ if contains .err "ErrTimeout" }} timeout {{else if contains "he" "hello"}} yes {{else}} no {{end}}`
日志行属性的内置变量
这些变量提供了一种在编写模板表达式时引用日志行中某些内容的方式。
.label_name
日志行中的所有标签都作为变量添加到模板引擎中。它们可以使用标签名前缀 .
进行引用(例如,.label_name
)。例如,以下模板将输出 path
标签的值
`{{ .path }}`
line
__line__
函数返回未经任何修改的原始日志行。
签名: line() string
示例
`{{ __line__ | lower }}`
`{{ __line__ }}`
timestamp
__timestamp__
函数返回当前日志行的时间戳。
签名: timestamp() time.Time
`{{ __timestamp__ }}`
`{{ __timestamp__ | date "2006-01-02T15:04:05.00Z-07:00" }}`
`{{ __timestamp__ | unixEpoch }}`
更多信息请参考博客在 Go 中解析和格式化日期/时间。
日期和时间
在构建 LogQL 查询时,您可以使用以下函数操作日期和时间。
date
根据提供的golang 日期时间布局格式化时间值,并返回文本表示。
签名: date(fmt string, date interface{}) string
示例
`{{ date "2006-01-02" now }}`
duration
是 duration_seconds
的别名
示例
`{{ .foo | duration }}`
`{{ duration .foo }}`
duration_seconds
使用 time.ParseDuration 将人性化的时间 duration 转换为秒。
签名: duration_seconds(string) float64
示例
`{{ .foo | duration_seconds }}`
`{{ duration_seconds .foo }}`
now
返回 Loki 服务器本地时区的当前时间。
签名: Now() time.Time
示例
`{{ now }}`
toDate
解析格式化字符串,并使用运行 Loki 的服务器本地时区返回对应的时间值。
为了在 Loki 安装之间获得更好的一致性,建议使用 toDateInZone
格式字符串必须使用golang 日期时间布局中定义的精确日期。
签名: toDate(fmt, str string) time.Time
示例
`{{ toDate "2006-01-02" "2021-11-02" }}`
`{{ .foo | toDate "2006-01-02T15:04:05.999999999Z" }}`
toDateInZone
解析格式化字符串,并在提供的时区中返回对应的时间值。
格式字符串必须使用golang 日期时间布局中定义的精确日期。
时区值可以是 Local
, UTC
,或任何 IANA 时区数据库值
签名: toDateInZone(fmt, zone, str string) time.Time
示例
`{{ toDateInZone "2006-01-02" "UTC" "2021-11-02" }}`
`{{ .foo | toDateInZone "2006-01-02T15:04:05.999999999Z" "UTC" }}`
unixEpoch
返回自 1970 年 1 月 1 日 UTC 起经过的秒数。
签名: unixEpoch(date time.Time) string
示例
`{{ unixEpoch now }}`
`{{ .foo | toDateInZone "2006-01-02T15:04:05.999999999Z" "UTC" | unixEpoch }}`
过滤创建时间在 1 天前的 Loki 查询器作业的示例查询
{job="loki/querier"} | label_format nowEpoch=`{{(unixEpoch now)}}`,createDateEpoch=`{{unixEpoch (toDate "2006-01-02" .createDate)}}` | label_format dateTimeDiff=`{{sub .nowEpoch .createDateEpoch}}` | dateTimeDiff > 86400
unixEpochMillis
返回自 1970 年 1 月 1 日 UTC 起经过的毫秒数。
签名: unixEpochMillis(date time.Time) string
示例
`{{ unixEpochMillis now }}`
`{{ .foo | toDateInZone "2006-01-02T15:04:05.999999999Z" "UTC" | unixEpochMillis }}`
unixEpochNanos
返回自 1970 年 1 月 1 日 UTC 起经过的纳秒数。
签名: unixEpochNanos(date time.Time) string
示例
`{{ unixEpochNanos now }}`
`{{ .foo | toDateInZone "2006-01-02T15:04:05.999999999Z" "UTC" | unixEpochNanos }}`
unixToTime
将字符串 epoch 转换为对应的时间值。支持以天、秒、毫秒、微秒和纳秒为单位的 Epoch 时间。
签名: unixToTime(epoch string) time.Time
示例
考虑以下日志行 {"from": "1679577215","to":"1679587215","message":"some message"}
。要以人类可读的格式打印 from
字段,请在 LogQL 查询末尾添加以下内容
... | json | line_format `from="{{date "2006-01-02" (unixToTime .from)}}"`
字符串操作
在构建 LogQL 查询时,您可以使用以下模板操作字符串。
alignLeft
使用此函数将字符串格式化为固定宽度,内容左对齐。
签名: alignLeft(count int, src string) string
示例
`{{ alignLeft 5 "hello world"}}` // output: "hello"
`{{ alignLeft 5 "hi"}}` // output: "hi "
alignRight
使用此函数将字符串格式化为固定宽度,内容右对齐。
签名: alignRight(count int, src string) string
示例
`{{ alignRight 5 "hello world"}}` // output: "world"
`{{ alignRight 5 "hi"}}` // output: " hi"
b64enc
Base64 编码字符串。
签名: b64enc(string) string
示例
`{{ .foo | b64enc }}`
`{{ b64enc .foo }}`
b64dec
Base64 解码字符串。
签名: b64dec(string) string
示例
`{{ .foo | b64dec }}`
`{{ b64dec .foo }}`
bytes
使用 go-humanize 将人性化的字节字符串转换为字节。 Duration 可以转换为诸如“3 days ago”的字符串,表示大小的数字(如 82854982)可以转换为有用的字符串,如“83 MB”或“79 MiB”
签名: bytes(string) string
示例
`{{ .foo | bytes }}`
`{{ bytes .foo }}`
default
如果源字符串为空,则启用输出默认值。如果 'src' 参数不为空,此函数返回 'src' 的值。对于 JSON 中可能缺失的字段很有用,例如日志行中非必需的 HTTP 头,如下例所示
{job="access_log"} | json | line_format `{{.http_request_headers_x_forwarded_for | default "-"}}`
签名: default(d string, src string) string
示例
`{{ default "-" "" }}` // output: -
`{{ default "-" "foo" }}` // output: foo
打印 -
的示例查询,如果 http_request_headers_x_forwarded_for
标签为空
{job="access_log"} | json | line_format `{{.http_request_headers_x_forwarded_for | default "-"}}`
fromJson
将 JSON 文档解码为结构体。如果输入无法解码为 JSON,函数将返回空字符串。
签名: fromJson(v string) interface{}
示例
`{{fromJson "{\"foo\": 55}"}}`
示例查询,为日志行中存储为 JSON 数组的每个查询打印新行
{job="loki/querier"} |= "finish in prometheus" | logfmt | line_format `{{ range $q := fromJson .queries }} {{ $q.query }} {{ end }}`
lower
使用此函数转换为小写。
签名: lower(string) string
示例
`{{ .request_method | lower }}`
`{{ lower "HELLO"}}`
最后一个示例将返回 hello
。
indent
indent 函数将给定字符串中的每一行缩进指定的宽度。这在对齐多行字符串时很有用。
签名: indent(spaces int,src string) string
示例
`{{ indent 4 .query }}`
这将 .query
中的每一行缩进四个 (4) 空格。
nindent
nindent 函数与 indent 函数相同,但在字符串开头添加新行。
签名: nindent(spaces int,src string) string
示例
`{{ nindent 4 .query }}`
这将把文本的每一行缩进 4 个空格字符,并在开头添加一个新行。
repeat
使用此函数重复字符串多次。
签名: repeat(c int,value string) string
示例
`{{ repeat 3 "hello" }}` // output: hellohellohello
printf
使用此函数以自定义方式格式化字符串。有关语法更多信息,请参考Go 文档。
签名: printf(format string, a ...interface{})
示例
`{{ printf "The IP address was %s" .remote_addr }}` // output: The IP address was 129.168.1.1
`{{ printf "%-40.40s" .request_uri}} {{printf "%-5.5s" .request_method}}`
// output:
// /a/509965767/alternative-to-my-mtg.html GET
// /id/609259548/hpr.html GET
line_format "\"|\" {{printf \"%15.15s\" .ClientHost}} \"|\""
replace
此函数执行简单的字符串替换。
签名: replace(old string, new string, src string) string
它接受三个参数
- 要替换的旧字符串
- 替换成的新字符串
- 源字符串
示例
`{{ .cluster | replace "-cluster" "" }}`
`{{ replace "hello" "world" "hello world" }}`
最后一个示例将返回 world world
。
substr
从字符串获取子字符串。
签名: substr(start int,end int,value string) string
如果 start < 0,则调用 value[:end]。如果 start >= 0 且 end < 0 或 end 大于 s 的长度,则调用 value[start:]。否则,调用 value[start, end]。
示例
`{{ .path | substr 2 5 }}`
`{{ substr 0 5 "hello world"}}` // output: hello
`{{ substr 6 11 "hello world"}}` // output: world
title
转换为标题大小写。
签名: title(string) string
示例
`{{.request_method | title}}`
`{{ title "hello world"}}`
最后一个示例将返回 Hello World
。
trim
trim 函数移除字符串两侧的空格。
签名: trim(string) string
示例
`{{ .ip | trim }}`
`{{ trim " hello " }}` // output: hello
trimAll
使用此函数从字符串的开头或结尾移除给定字符。
签名: trimAll(chars string,src string) string
示例
`{{ .path | trimAll "/" }}`
`{{ trimAll "$" "$5.00" }}` // output: 5.00
trimPrefix
使用此函数仅移除字符串的前缀。
签名: trimPrefix(prefix string, src string) string
示例
`{{ .path | trimPrefix "/" }}`
`{{ trimPrefix "-" "-hello" }}` // output: hello
trimSuffix
使用此函数仅移除字符串的后缀。
签名: trimSuffix(suffix string, src string) string
示例
`{{ .path | trimSuffix "/" }}`
`{{ trimSuffix "-" "hello-" }}` // output: hello
trunc
截断字符串且不添加后缀。
签名: trunc(count int,value string) string
示例
`{{ .path | trunc 2 }}`
`{{ trunc 5 "hello world"}}` // output: hello
`{{ trunc -5 "hello world"}}` // output: world
upper
使用此函数转换为大写。
签名: upper(string) string
示例
`{ .request_method | upper }}`
`{{ upper "hello"}}`
结果为 HELLO
。
urlencode
使用此函数urlencode字符串。
签名: urlencode(string) string
示例
`{{ .request_url | urlencode }}`
`{{ urlencode .request_url}}`
urldecode
使用此函数urldecode字符串。
签名: urldecode(string) string
示例
`{{ .request_url | urldecode }}`
`{{ urldecode .request_url}}`
逻辑函数
在构建模板表达式时,您可以使用以下逻辑函数比较字符串。
contains
使用此函数测试一个字符串是否包含在另一个字符串中。
签名: contains(s string, src string,) bool
示例
`{{ if contains "ErrTimeout" .err }} timeout {{end}}`
`{{ if contains "he" "hello" }} yes {{end}}`
eq
使用此函数测试一个字符串是否与另一个字符串完全匹配。
签名: eq(s string, src string) bool
示例
`{{ if eq "ErrTimeout" .err }} timeout {{end}}`
`{{ if eq "hello" "hello" }} yes {{end}}`
hasPrefix 和 hasSuffix
hasPrefix
和 hasSuffix
函数测试字符串是否具有给定的前缀或后缀。
签名
hasPrefix(prefix string, src string) bool
hasSuffix(suffix string, src string) bool
示例
`{{ if hasSuffix .err "Timeout" }} timeout {{end}}`
`{{ if hasPrefix "he" "hello" }} yes {{end}}`
数学函数
在编写模板表达式时,您可以使用以下数学函数。
add
求和。支持多个数字
签名: func(i ...interface{}) int64
示例
`{{ add 3 2 5 }}` // output: 10
addf
求浮点数和。支持多个数字。
签名: func(i ...interface{}) float64
示例
`{{ addf 3.5 2 5 }}` // output: 10.5
ceil
返回大于或等于输入值的最大浮点数
签名: ceil(a interface{}) float64
示例
`{{ ceil 123.001 }}` //output 124.0
div
整型相除。
签名: func(a, b interface{}) int64
示例
`{{ div 10 2}}` // output: 5
divf
浮点数相除。支持多个数字。
签名: func(a interface{}, v ...interface{}) float64
示例
`{{ divf 10 2 4}}` // output: 1.25
float64
将字符串转换为 float64。
签名: toFloat64(v interface{}) float64
示例
`{{ "3.5" | float64 }}` //output 3.5
floor
返回小于或等于输入值的最大浮点数。
签名: floor(a interface{}) float64
示例
`{{ floor 123.9999 }}` //output 123.0
int
将值转换为整型。
签名: toInt(v interface{}) int
示例
`{{ "3" | int }}` //output 3
max
返回一系列整型中的最大值
签名: max(a interface{}, i ...interface{}) int64
示例
`{{ max 1 2 3 }}` //output 3
maxf
返回一系列浮点数中的最大值
签名: maxf(a interface{}, i ...interface{}) float64
示例
`{{ maxf 1 2.5 3 }}` //output 3
min
返回一系列整型中的最小值。
签名: min(a interface{}, i ...interface{}) int64
示例
`{{ min 1 2 3 }}`//output 1
minf
返回一系列浮点数中的最小值。
签名: minf(a interface{}, i ...interface{}) float64
示例
`{{ minf 1 2.5 3 }}` //output 1.5
mul
乘法运算。支持多个数字。
签名: mul(a interface{}, v ...interface{}) int64
示例
`{{ mul 5 2 3}}` // output: 30
mulf
浮点数乘法运算。支持多个数字
签名: mulf(a interface{}, v ...interface{}) float64
示例
`{{ mulf 5.5 2 2.5 }}` // output: 27.5
mod
返回数字 'a' 除以数字 'b' 的余数。
签名: mod(a, b interface{}) int64
示例
`{{ mod 10 3}}` // output: 1
round
返回一个浮点数,其余数四舍五入到小数点后给定位数。
签名: round(a interface{}, p int, rOpt ...float64) float64
示例
`{{ round 123.555555 3 }}` //output 123.556
我们也可以提供一个 roundOn
数字作为第三个参数
示例
`{{ round 123.88571428571 5 .2 }}` //output 123.88572
使用默认 roundOn
值为 .5
,以上值将是 123.88571
sub
一个数减去另一个数。
签名: func(a, b interface{}) int64
示例
`{{ sub 5 2 }}` // output: 3
subf
浮点数相减。支持多个数字。
签名: func(a interface{}, v ...interface{}) float64
示例
`{{ subf 5.5 2 1.5 }}` // output: 2
正则表达式函数
您可以在模板表达式中使用以下函数执行正则表达式操作。
count
计算 regex (regex
) 在 (src
) 的出现次数。
签名: count(regex string, src string) int
示例
`{{ count "a|b" "abab" }}` // output: 4
`{{ count "o" "foo" }}` // output: 2
打印 XYZ 在一行中出现多少次的示例查询
{job="xyzlog"} | line_format `{{ __line__ | count "XYZ"}}`
regexReplaceAll 和 regexReplaceAllLiteral
regexReplaceAll
返回输入字符串的副本,用替换字符串 replacement 替换 Regexp 的匹配项。在字符串替换中,$ 符号的解释方式与 Expand 中相同,例如 $1 代表第一个子匹配的文本。请参见 golangRegexp.replaceAll 文档获取更多示例。
签名: regexReplaceAll(regex string, src string, replacement string) (source)
示例
`{{ regexReplaceAll "(a*)bc" .some_label "${1}a" }}`
regexReplaceAllLiteral
函数返回输入字符串的副本,用替换字符串 replacement 替换 Regexp 的匹配项。替换字符串是直接替换的,不使用 Expand。
签名: regexReplaceAllLiteral(regex string, src string, replacement string)
示例
`{{ regexReplaceAllLiteral "(ts=)" .timestamp "timestamp=" }}`