指标查询
指标查询通过对日志查询结果应用函数来扩展日志查询。这一强大功能能够从日志中创建指标。
指标查询可用于计算错误消息的速率,或查找在过去 3 小时内日志数量最多的前 N 个日志源。
结合解析器,指标查询还可以用于从日志行中的样本值计算指标,例如延迟或请求大小。所有标签,包括提取的标签,都可用于聚合和生成新序列。
范围向量聚合
LogQL 借鉴了 Prometheus 的范围向量概念。在 Grafana Loki 中,选定的样本范围是选定的日志或标签值的范围。
聚合应用于一段时间间隔。Loki 定义的时间间隔与 Prometheus 的语法相同。
Loki 支持两种类型的范围向量聚合:日志范围聚合和 unwrapped 范围聚合。
日志范围聚合
日志范围聚合是后跟时间间隔的查询。函数应用于在该时间间隔内聚合查询结果。时间间隔可以放在日志流选择器之后或日志 Pipeline 的末尾。
函数
rate(log-range)
: 计算每秒条目数count_over_time(log-range)
: 计算给定范围内的每个日志流的条目数。bytes_rate(log-range)
: 计算每个流每秒的字节数。bytes_over_time(log-range)
: 计算每个日志流在给定范围内的字节量。absent_over_time(log-range)
: 如果传递给它的范围向量有任何元素,则返回一个空向量;如果传递给它的范围向量没有元素,则返回一个值为 1 的单元素向量。(absent_over_time
在特定时间段内不存在任何时序和日志流的标签组合时非常有用,可用于告警。)
示例
计算 MySQL 作业在过去五分钟内的所有日志行数。
count_over_time({job="mysql"}[5m])
此聚合包括过滤器和解析器。它返回 MySQL 作业在过去一分钟内每台主机的非超时错误每秒速率,并且仅包含持续时间超过十秒的错误。
sum by (host) (rate({job="mysql"} |= "error" != "timeout" | json | duration > 10s [1m]))
偏移修饰符
偏移修饰符允许改变查询中单个范围向量的时间偏移。
例如,以下表达式计算 MySQL 作业在过去十分钟到五分钟内的所有日志,而不是过去五分钟内的日志。请注意,offset
修饰符总是需要紧跟在范围向量选择器之后。
count_over_time({job="mysql"}[5m] offset 5m) // GOOD
count_over_time({job="mysql"}[5m]) offset 5m // INVALID
Unwrapped 范围聚合
Unwrapped 范围使用提取的标签作为样本值,而不是日志行。然而,要选择将在聚合中使用的标签,日志查询必须以 unwrap 表达式结尾,并且可以选择使用标签过滤表达式来丢弃错误。
unwrap 表达式记为 | unwrap label_identifier
,其中 label_identifier
是用于提取样本值的标签名。
由于标签值是字符串,默认会尝试将其转换为浮点数(64 位),转换失败时会将 __error__
标签添加到样本中。可选地,标签标识符可以使用转换函数 | unwrap <function>(label_identifier)
包裹,该函数将尝试从特定格式转换标签值。
我们目前支持的函数有
duration_seconds(label_identifier)
(或其简写duration
)将标签值从go duration 格式(例如5m
,24s30ms
)转换为秒。bytes(label_identifier)
将应用字节单位(例如5 MiB
,3k
,1G
)将标签值转换为原始字节。
支持对 unwrapped 范围进行操作的函数有
rate(unwrapped-range)
: 计算指定时间间隔内所有值的总和的每秒速率。rate_counter(unwrapped-range)
: 计算指定时间间隔内值的每秒速率,并将它们视为“计数器指标”sum_over_time(unwrapped-range)
: 指定时间间隔内所有值的总和。avg_over_time(unwrapped-range)
: 指定时间间隔内所有点的平均值。max_over_time(unwrapped-range)
: 指定时间间隔内所有点的最大值。min_over_time(unwrapped-range)
: 指定时间间隔内所有点的最小值first_over_time(unwrapped-range)
: 指定时间间隔内所有点的第一个值last_over_time(unwrapped-range)
: 指定时间间隔内所有点的最后一个值stdvar_over_time(unwrapped-range)
: 指定时间间隔内值的总体标准方差。stddev_over_time(unwrapped-range)
: 指定时间间隔内值的总体标准差。quantile_over_time(scalar,unwrapped-range)
: 指定时间间隔内值的 φ 分位数 (0 ≤ φ ≤ 1)。absent_over_time(unwrapped-range)
: 如果传递给它的范围向量有任何元素,则返回一个空向量;如果传递给它的范围向量没有元素,则返回一个值为 1 的单元素向量。(absent_over_time
在特定时间段内不存在任何时序和日志流的标签组合时非常有用,可用于告警。)
除 sum_over_time
、absent_over_time
、rate
和 rate_counter
外,unwrapped 范围聚合支持分组。
<aggr-op>([parameter,] <unwrapped-range>) [without|by (<label list>)]
通过包含 without
或 by
子句,可用于按不同的标签维度进行聚合。
without
从结果向量中移除列出的标签,同时保留所有其他标签。by
则相反,它会丢弃未在 by
子句中列出的标签,即使这些标签值在向量的所有元素之间是相同的。
有关使用 unwrap 表达式的查询示例,请参阅Unwrap 示例。
内置聚合运算符
与 PromQL 类似,LogQL 支持一部分内置聚合运算符,可用于聚合单个向量的元素,从而产生一个元素较少但带有聚合值的新向量。
sum
: 计算标签总和avg
: 计算标签平均值min
: 选择标签最小值max
: 选择标签最大值stddev
: 计算标签总体标准差stdvar
: 计算标签总体标准方差count
: 计算向量中的元素数量topk
: 根据样本值选择最大的 k 个元素bottomk
: 根据样本值选择最小的 k 个元素sort
: 返回按样本值升序排序的向量元素。sort_desc
: 与 sort 相同,但按降序排序。
聚合运算符可以通过包含 without
或 by
子句来对所有标签值或一组不同的标签值进行聚合。
<aggr-op>([parameter,] <vector expression>) [without|by (<label list>)]
使用 topk
和 bottomk
时需要 parameter
。topk
和 bottomk
与其他聚合器不同之处在于,结果向量中返回的是输入样本的一个子集,包括原始标签。
by
和 without
仅用于对输入向量进行分组。without
子句从结果向量中移除列出的标签,同时保留所有其他标签。by
子句则相反,它会丢弃未在子句中列出的标签,即使这些标签值在向量的所有元素之间是相同的。
有关使用向量聚合表达式的查询示例,请参阅向量聚合示例。
函数
LogQL 支持一组内置函数。
vector(s scalar)
: 将标量 s 作为不带标签的向量返回。其行为与 Prometheus 的vector()
函数完全相同。vector
主要用于返回一个在其他情况下不会返回任何值的序列值;在使用 LogQL 定义告警时这很有用。
示例
计算 traefik 命名空间在过去五分钟内的所有日志行数。
sum(count_over_time({namespace="traefik"}[5m])) # will return nothing or vector(0) # will return 0
概率聚合
LogQL 的 approx_topk
函数提供了 topk
的概率近似值。它是 topk
的直接替代品,非常适用于 topk
查询超时或达到最大序列限制的情况。这种情况通常发生在用于查找最常见值而进行排序的值列表非常大时。在需要更快、近似而非更慢、更精确的答案的情况下,approx_topk
也非常有用。
函数形式为
approx_topk(k, <vector expression>)
approx_topk
仅支持即时查询。也不支持分组,应由内部的 sum by
或 sum without
处理,即使这可能与 topk by
的行为不完全相同。
在底层,approx_topk
使用分片实现。利用count-min sketch 算法和堆来近似每个分片的计数。近似的准确性取决于堆的大小,堆的大小由 Loki 的 max_count_min_sketch_heap_size
参数定义。随着 k 接近堆的大小(默认大小为 10,000),准确性会降低。
表达式 approx_topk(k,inner)
变为
topk(
k,
eval_cms(
__count_min_sketch__(inner, shard=1) ++ __count_min_sketch__(inner, shard=2)...
)
)
为每个分片计算 __count_min_sketch__
并在前端合并。然后 eval_cms
遍历标签列表并确定每个标签的计数。最后 topk
选择顶部项。