Grafana Loki 查询语言 LogQL 使用

网站建设3年前发布
74 0 0
20230306121138f751bce53e0bf7d3ec98734d2cebd2cfda0c66552,受 PromQL 的启发,Loki 也有自己的查询语言,称为 LogQL,它就像一个分布式的 grep,可以聚合查看日志。和 PromQL 一样,LogQL 也是使用标签和运算符进行过滤的,主要有两种类型的查询功能:,一个基本的日志查询由两部分组成。,202303061211399968d087858ef88d17d427fe4feef19d2597e1208,由于 Loki 的设计,所有 LogQL 查询必须包含一个日志流选择器。一个 Log Stream 代表了具有相同元数据(Label 集)的日志条目。,日志流选择器决定了有多少日志将被搜索到,一个更细粒度的日志流选择器将搜索到流的数量减少到一个可管理的数量,通过精细的匹配日志流,可以大幅减少查询期间带来资源消耗。,而日志流选择器后面的日志管道是可选的,用于进一步处理和过滤日志流信息,它由一组表达式组成,每个表达式都以从左到右的顺序为每个日志行执行相关过滤,每个表达式都可以过滤、解析和改变日志行内容以及各自的标签。,2023030612113966eb39e45c67478e3d612433832bc591f80748269,下面的例子显示了一个完整的日志查询的操作:,该查询语句由以下几个部分组成:,日志流选择器决定了哪些日志流应该被包含在你的查询结果中,选择器由一个或多个键值对组成,其中每个键是一个日志标签,每个值是该标签的值。,日志流选择器是通过将键值对包裹在一对大括号中编写的,比如:,上面这个示例表示,所有标签为 app 且其值为 mysql 和标签为 name 且其值为 mysql-backup 的日志流将被包括在查询结果中。,其中标签名后面的 = 运算符是一个标签匹配运算符,LogQL 中一共支持以下几种标签匹配运算符:,例如:,适用于 Prometheus 标签选择器的规则同样适用于 Loki 日志流选择器。,日志管道可以附加到日志流选择器上,以进一步处理和过滤日志流。它通常由一个或多个表达式组成,每个表达式针对每个日志行依次执行。如果一个表达式过滤掉了日志行,则管道将在此处停止并开始处理下一行。一些表达式可以改变日志内容和各自的标签,然后可用于进一步过滤和处理后续表达式或指标查询。,一个日志管道可以由以下部分组成。,其中 unwrap 表达式是一个特殊的表达式,只能在度量查询中使用。,日志行过滤表达式用于对匹配日志流中的聚合日志进行分布式 grep。,编写入日志流选择器后,可以使用一个搜索表达式进一步过滤得到的日志数据集,搜索表达式可以是文本或正则表达式,比如:,上面示例中的 |=、|~ 和 != 是过滤运算符,支持下面几种:,过滤运算符可以是链式的,并将按顺序过滤表达式,产生的日志行必须满足每个过滤器。当使用 |~和 !~ 时,可以使用 Golang 的 RE2 语法的正则表达式,默认情况下,匹配是区分大小写的,可以用 (?i) 作为正则表达式的前缀,切换为不区分大小写。,虽然日志行过滤表达式可以放在管道的任何地方,但最好把它们放在开头,这样可以提高查询的性能,当某一行匹配时才做进一步的后续处理。例如,虽然结果是一样的,但下面的查询 {job="mysql"} |= "error" |json | line_format "{{.err}}" 会比 {job="mysql"} | json | line_format "{{.message}}" |= "error" 更快,日志行过滤表达式是继日志流选择器之后过滤日志的最快方式。,解析器表达式可以解析和提取日志内容中的标签,这些提取的标签可以用于标签过滤表达式进行过滤,或者用于指标聚合。,提取的标签键将由解析器进行自动格式化,以遵循 Prometheus 指标名称的约定(它们只能包含 ASCII 字母和数字,以及下划线和冒号,不能以数字开头)。,例如下面的日志经过管道 | json 将产生以下 Map 数据:,->,在出现错误的情况下,例如,如果该行不是预期的格式,该日志行不会被过滤,而是会被添加一个新的 __error__ 标签。,需要注意的是如果一个提取的标签键名已经存在于原始日志流中,那么提取的标签键将以 _extracted 作为后缀,以区分两个标签,你可以使用一个标签格式化表达式来强行覆盖原始标签,但是如果一个提取的键出现了两次,那么只有最新的标签值会被保留。,目前支持 json、logfmt、pattern、regexp 和 unpack 这几种解析器。,我们应该尽可能使用 json 和 logfmt 等预定义的解析器,这会更加容易,而当日志行结构异常时,可以使用 regexp,可以在同一日志管道中使用多个解析器,这在你解析复杂日志时很有用。,json 解析器有两种模式运行。,注意:数组会被忽略。,可以得到如下所示的标签列表:,提取的标签列表为:,如果表达式返回一个数组或对象,它将以 json 格式分配给标签。例如,|json server_list="services", headers="request.headers 将提取到如下标签:,logfmt 解析器可以通过使用 | logfmt 来添加,它将从 logfmt 格式的日志行中提前所有的键和值。,例如,下面的日志行数据:,将提取得到如下所示的标签:,与 logfmt 和 json(它们隐式提取所有值且不需要参数)不同,regexp 解析器采用单个参数 | regexp "<re>" 的格式,其参数是使用 Golang RE2 语法的正则表达式。,正则表达式必须包含至少一个命名的子匹配(例如(?P<name>re)),每个子匹配项都会提取一个不同的标签。,例如,解析器 | regexp "(?P<method>\\w+) (?P<path>[\\w|/]+) \\((?P<status>\\d+?)\\) (?P<duration>.*)" 将从以下行中提取标签:,提取的标签为:,模式解析器允许通过定义模式表达式(| pattern "<pattern-expression>")从日志行中显式提取字段,该表达式与日志行的结构相匹配。,比如我们来考虑下面的 NGINX 日志行数据:,该日志行可以用下面的表达式来解析:,解析后可以提取出下面的这些属性:,模式表达式的捕获是由 < 和 > 字符分隔的字段名称,比如 <example> 定义了字段名称为 example,未命名的 capture 显示为 <_>,未命名的 capture 会跳过匹配的内容。默认情况下,模式表达式锚定在日志行的开头,可以在表达式的开头使用 <_> 将表达式锚定在开头。,比如我们查看下面的日志行数据:,我们如果只希望去匹配 msg=" 的内容,我们可以使用下面的表达式来进行匹配:,前面大部分日志数据我们不需要,只需要使用 <_> 进行占位即可,明显可以看出这种方式比正则表达式要简单得多。,unpack 解析器将解析 json 日志行,并通过打包阶段解开所有嵌入的标签,一个特殊的属性 _entry 也将被用来替换原来的日志行。,例如,使用 | unpack 解析器,可以得到如下所示的标签:,允许提取 container 和 pod 标签以及原始日志信息作为新的日志行。,标签过滤表达式允许使用其原始和提取的标签来过滤日志行,它可以包含多个谓词。,一个谓词包含一个标签标识符、操作符和用于比较标签的值。,例如 cluster="namespace" 其中的 cluster 是标签标识符,操作符是 =,值是"namespace"。,LogQL 支持从查询输入中自动推断出的多种值类型:,字符串类型的工作方式与 Prometheus 标签匹配器在日志流选择器中使用的方式完全一样,这意味着你可以使用同样的操作符(=、!=、=~、!~)。,使用 Duration、Number 和 Bytes 将在比较前转换标签值,并支持以下比较器。,例如 logfmt | duration > 1m and bytes_consumed > 20MB 过滤表达式。,如果标签值的转换失败,日志行就不会被过滤,而会添加一个 __error__ 标签。你可以使用 and和 or 来连接多个谓词,它们分别表示且和或的二进制操作,and 可以用逗号、空格或其他管道来表示,标签过滤器可以放在日志管道的任何地方。,以下所有的表达式都是等价的:,默认情况下,多个谓词的优先级是从右到左,你可以用圆括号包装谓词,强制使用从左到右的不同优先级。,例如,以下内容是等价的:,它将首先评估 duration>=20ms or method="GET",要首先评估 method="GET" and size<=20KB,请确保使用适当的括号,如下所示。,日志行格式化表达式可以通过使用 Golang 的 text/template 模板格式重写日志行的内容,它需要一个字符串参数 | line_format "{{.label_name}}" 作为模板格式,所有的标签都是注入模板的变量,可以用 {{.label_name}} 的符号来使用。,例如,下面的表达式:,将提取并重写日志行,只包含 query 和请求的 duration。你可以为模板使用双引号字符串或反引号 `{{.label_name}}` 来避免转义特殊字符。,此外 line_format 也支持数学函数,例如:,如果我们有以下标签 ip=1.1.1.1, status=200 和 duration=3000(ms), 我们可以用 duration 除以 1000 得到以秒为单位的值:,上面的查询将得到的日志行内容为1.1.1.1 200 3。,| label_format 表达式可以重命名、修改或添加标签,它以逗号分隔的操作列表作为参数,可以同时进行多个操作。,当两边都是标签标识符时,例如 dst=src,该操作将把 src 标签重命名为 dst。,左边也可以是一个模板字符串,例如 dst="{{.status}} {{.query}}",在这种情况下,dst 标签值会被 Golang 模板执行结果所取代,这与 | line_format 表达式是同一个模板引擎,这意味着标签可以作为变量使用,也可以使用同样的函数列表。,在上面两种情况下,如果目标标签不存在,那么就会创建一个新的标签。,重命名形式 dst=src 会在将 src 标签重新映射到 dst 标签后将其删除,然而,模板形式将保留引用的标签,例如 dst="{{.src}}" 的结果是 dst 和 src 都有相同的值。,LogQL 同样支持通过函数方式将日志流进行度量,通常我们可以用它来计算消息的错误率或者排序一段时间内的应用日志输出 Top N。,LogQL 同样也支持有限的区间向量度量语句,使用方式和 PromQL 类似,常用函数主要是如下 4 个:,比如计算 nginx 的 qps:,计算 kernel 过去 5 分钟发生 oom 的次数:,LogQL 也支持聚合运算,我们可用它来聚合单个向量内的元素,从而产生一个具有较少元素的新向量,当前支持的聚合函数如下:,聚合函数我们可以用如下表达式描述:,对于需要对标签进行分组时,我们可以用 without 或者 by 来区分。比如计算 nginx 的 qps,并按照 pod 来分组:,只有在使用 bottomk 和 topk 函数时,我们可以对函数输入相关的参数。比如计算 nginx 的 qps 最大的前 5 个,并按照 pod 来分组:,Loki 存的是日志,都是文本,怎么计算呢?显然 LogQL 中的数学运算是面向区间向量操作的,LogQL 中的支持的二进制运算符如下:,比如我们要找到某个业务日志里面的错误率,就可以按照如下方式计算:,集合运算仅在区间向量范围内有效,当前支持,比如:,LogQL 支持的比较运算符和 PromQL 一样,包括:,通常我们使用区间向量计算后会做一个阈值的比较,这对应告警是非常有用的,比如统计 5 分钟内 error 级别日志条目大于 10 的情况:,我们也可以通过布尔计算来表达,比如统计 5 分钟内 error 级别日志条目大于 10 为真,反正则为假:,LogQL 查询可以使用 # 字符进行注释,例如:,对于多行 LogQL 查询,可以使用 # 排除整个或部分行:,这里我们部署一个示例应用,该应用程序是一个伪造的记录器,它的日志具有 debug、info 和 warning 输出到 stdout。error 级别的日志将被写入 stderr,实际的日志消息以 JSON 格式生成,每 500 毫秒将创建一条新的日志消息。日志消息格式如下所示:,使用下面的命令来创建示例应用:,我们可以使用 {app="fake-logger"} 在 Grafana 中查询到该应用的日志流数据。,20230306121141c524d7c94b1e98171f75115d36d19196712872650,由于我们该示例应用的日志是 JSON 形式的,我们可以采用 JSON 解析器来解析日志,表达式为 {app="fake-logger"} | json,如下所示。,20230306121325d824c5e95334ddd24c63773b8e045e5a120c0843020230306121144a5c107388c512166dee926d3349f7d8e54bfe7946,使用 JSON 解析器解析日志后可以看到 Grafana 提供的面板会根据 level 的值使用不同的颜色进行区分,而且现在我们日志的属性也被添加到了 Log 的标签中去了。,20230306121144543b87d145eaf0421de2339e3e68cc8f808a8385920230306121326e515292168a2007c95631973c61d2bee52db06139,现在 JSON 中的数据变成了日志标签我们自然就可以使用这些标签来过滤日志数据了,比如我们要过滤 level=error 的日志,只使用表达式 {app="fake-logger"} | json | level="error" 即可实现。,20230306121326e856be568180fa08c091937811fce0986b5b64343,此外我们还可以根据我们的需求去格式化输出日志,使用 line_format 即可实现,比如我们这里使用查询语句 {app="fake-logger"} | json |is_even="true" | line_format "在 {{.time}} 于 {{.level}}@{{.pod}} Pod中产生了日志 {{.msg}}" 来格式化日志输出。,20230306121145e1047b583e1438b04de179a56af181f79afe54269,这里我们以监控 Kubernetes 的事件为例进行说明。首先需要安装 [kubernetes-event-exporter],地址  https://github.com/opsgenie/kubernetes-event-exporter/tree/master/deploy,kubernetes-event-exporter 日志会打印到 stdout,然后我们的 promtail 会将日志上传到 Loki。,20230306121145c3a39ca99b89eeaec81556626c810a38573c4c785,然后导入 https://grafana.com/grafana/dashboards/14003 这个 Dashboard 即可,不过需要注意修改每个图表中的过滤标签为 job="monitoring/event-exporter"。,20230306121326e7a00b1214e0b22df727914edaaca35a86b6ee298,修改后正常就可以在 Dashboard 中看到集群中的相关事件信息了,不过建议用记录规则去替换面板中的查询语句。,2023030612114687f5dec69ac37996725401de709eca0d346830721,20230306121328a9fcf70826256d6a1717808cf43c6dbd191804157
© 版权声明

相关文章