
Grafana Loki 查询指南从 LogQL 流选择器、日志管道到指标聚合的完整实战【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/lokiGrafana Loki 的日志查询能力统一建立在 LogQLLoki Query Language之上本文以官方查询文档为核心系统讲解 Loki 的数据查询模型、LogQL 的流选择器与日志管道语法、日志查询与指标查询两大类型并结合仓库源码与配套文档给出可直接上手的配置与示例。读完本文你将掌握用 LogQL 从海量日志中筛选、解析、格式化日志行以及将日志转化为指标进行聚合分析的完整方法。查询 Loki 的整体流程流、块与索引当你想要在 Loki 中查找特定日志时需要指定一组标签来标识它们。Loki 在接收日志条目时会将其分组为日志流log stream存储时日志流会被压缩并写入块chunk随后 Loki 为这些块建立索引相当于一本目录table of contents。当你执行一条查询时Loki 会先在索引中检索确定需要从存储中取回哪些块用于展示。因此Loki 的查询本质上是先按标签找流、再对流内日志做处理的两段式过程标签决定检索范围影响性能管道决定处理逻辑影响结果。这也解释了为什么把标签设计得足够精确、把行过滤尽量前置是优化查询性能的关键。查询 Loki 的几种方式Grafana、Logs Drilldown 与 LogCLILoki 本身没有内置用户界面所有查询方式在底层都使用 LogQLGrafana Explore最常见的交互式查询入口支持即席ad-hoc探索日志、构建并打磨 LogQL 查询然后将查询嵌入 Dashboard。Grafana Logs Drilldown使用默认查询自动生成一组初始可视化帮助你无需手写查询即可快速浏览日志。LogCLILoki 的命令行客户端适合脚本化、批量下载日志、做分析型运维任务如统计日志流数量以评估标签基数。注意 logcli 是纯查询工具不能用于写入日志。LogCLI 的安装与使用详见 LogCLI 入门 与 LogCLI 教程其入口实现在仓库 cmd/logcli/main.go客户端封装在 pkg/logcli/client。基础用法示例# 连接本地 Loki export LOKI_ADDRhttp://localhost:3100 logcli query {service_namewebsite} # 连接需要认证的实例 export LOKI_ADDRhttps://logs-us-west1.grafana.net export LOKI_USERNAMEusername export LOKI_PASSWORDpassword logcli query {service_namewebsite}常用参数包括--addr/LOKI_ADDR、--username/--password、--org-id/LOKI_ORG_ID指定租户、-o输出模式default|raw|jsonl、-q静默输出查询元数据、--stats显示查询统计等所有参数均可通过同名环境变量覆盖且环境变量优先于命令行参数。LogQL查询时动态定义schemaLogQL 是 Grafana Loki 的查询语言。由于 Loki 在采集日志时不强制要求预定义 schemaLogQL 实现了查询时 schemaschema at query——日志行的结构是在你书写查询的那一刻被推断出来的而不是在日志摄入ingest时。LogQL 借鉴了 PromQL 的设计但你不必先掌握 PromQL 也能写出 LogQL。一条 Loki 日志由三部分组成时间戳timestamp标签/选择器labels/selectors日志行内容contentLoki 只对时间戳和标签建立索引日志行的其余内容不做索引。LogQL 查询的基本格式为{ log stream selector } | log pipeline其中日志流选择器是必填的日志管道是可选的。该语法对应的 AST 节点可以在仓库 pkg/logql/syntax/ast.go 中看到LogSelectorExpr第 69 行起定义了选择器接口MatchersExpr第 328 行起承载标签匹配表达式PipelineExpr第 371 行起承载管道阶段序列。日志流选择器Log stream selector日志流选择器也称标签选择器是包含键值对的字符串例如{service_namenginx, status500}所有键值对的唯一组合被称为一个流stream。选择器的目标是通过使用预定义在 Loki 配置中或自动检测的标签缩小日志管道需要处理的数据集范围。因此传给选择器的标签会直接影响查询执行的相对性能。提示service_name是 Loki 创建的默认标签之一它会尝试从日志行中填充像服务名一样的内容常被 Logs Drilldown 用于发现和探索日志该默认行为可以在 Loki 配置中修改。流选择器支持的运算符运算符含义标签与选择器完全相等!标签与选择器不相等~标签与正则表达式匹配!~标签与正则表达式不匹配其中~表示使用正则表达式。示例{name ~ mysql.}{name !~ mysql.}{name !~ mysql-\d}注意与行过滤正则不同~和!~是**完全锚定fully anchored**的正则必须匹配整个字符串含换行。正则中的.默认不匹配换行如需匹配换行可用单行标志如(?s)search_term.或用[\S\s]组合匹配任意字符含换行{name ~ .*mysql.*}不匹配含换行的标签值{name ~ (?s).*mysql.*}匹配含换行的标签值{name ~ [\S\s]*mysql[\S\s]*}匹配含换行的标签值选择器语义与 Prometheus 标签选择器一致查询会包含所有同时满足app值为mysql且name值为mysql-backup的流流中即使还有其它标签对也不影响入选判定。日志管道Log pipeline日志管道可以附加在流选择器之后对选中的日志流做进一步处理和过滤。它由一组阶段表达式组成从左到右对每一行日志依次执行一旦某个表达式过滤掉了某行管道便停止处理该行并转向下一行。某些表达式如| line_format {{.status_code}}会改写日志内容与对应标签改写结果可供后续阶段继续过滤或处理。管道表达式分为四类过滤表达式行过滤表达式line filter与标签过滤表达式label filter解析表达式parser格式化表达式行格式化line format与标签格式化label format标签表达式丢弃标签drop labels与保留标签keep labels行过滤表达式Line filter行过滤表达式在匹配到的日志流聚合结果上执行一次分布式grep按区分大小写的表达式丢弃不匹配的行。每个行过滤表达式由过滤运算符加文本或正则组成运算符含义\|日志行包含该字符串!日志行不包含该字符串\|~日志行匹配该正则表达式!~日志行不匹配该正则表达式与选择器正则不同|~和!~不是完全锚定的.可以匹配包括换行在内的所有字符。示例{jobmysql} | error # 保留包含 error 的行 {instance~kafka-[23],namekafka} ! kafka.server:typeReplicaManager {namekafka} |~ tsdb-ops.*io:2003 # 正则包含匹配 {namecassandra} |~ error\w # 反引号避免转义 {jobmysql} | error ! timeout # 过滤器可链式串联使用|~和!~时可用 Go 的 RE2 语法默认区分大小写可用前缀(?i)切换为不区分大小写。性能要点行过滤表达式可以放在管道任意位置但几乎总是应该放在最前面——放在开头意味着只有匹配的行才进入后续处理。例如下面两条查询结果相同但前者总是更快{jobmysql} | error | json | line_format {{.err}} # 更快 {jobmysql} | json | line_format {{.message}} | error流选择器应用之后行过滤表达式是过滤日志最快的方式。此外行过滤表达式还支持去除 ANSI 颜色码{jobexample} | decolorize标签过滤表达式Label filter标签过滤表达式基于原始标签或解析出的标签过滤日志行可包含多个谓词。每个谓词由标签标识符恒在运算符左侧、运算符和值组成例如clusternamespace。值类型会根据查询输入自动推断字符串String双引号或反引号包裹如200或us-central1。其行为与流选择器中的标签匹配完全相同支持、!、~、!~。时长Duration形如300ms、1.5h、2h45m查询字面量接受ns、us或µs、ms、s、m、h、d、w、y而标签值比较时只接受ns~h因此若标签值是d/w/y单位可先用label_format转换。数字Number64 位浮点数如250、89.923。字节Bytes如42MB、1.5KiB、20B合法单位有B、kB、MB、GB、TB、PB、KB、KiB、MiB、GiB、TiB、PiB。Duration、Number、Bytes 类型在比较前会转换标签值支持/、!、/、/| logfmt | duration 1m and bytes_consumed 20MB如果标签值转换失败该行不会被过滤掉而是被打上__error__标签处理这类错误见管道错误一节。多个谓词可以用and、or链接and也可用逗号或空格表达Loki 先计算and再计算or可用括号强制分组| duration 20ms or size 20KB and method!~2.. | duration 20ms or size 20KB , method!~2.. | duration 20ms or size 20KB method!~2.. | duration 20ms or (size 20KB and method!~2..) | (duration 20ms or size 20KB) and method!~2..注意|会开启新的管道阶段而不是新的谓词因此下面两条等价| duration 20ms or size 20KB | method!~2.. | (duration 20ms or size 20KB) and method!~2..标签过滤表达式是unwrap 表达式之后唯一允许出现的表达式主要用于过滤指标提取过程中产生的错误。解析表达式Parser解析表达式可以从日志内容中提取标签提取出的标签可用于标签过滤或指标聚合。所有解析器都会自动清洗提取出的标签键以符合 Prometheus 指标命名规范仅含 ASCII 字母、数字、下划线和冒号且不能以数字开头。例如| json会把{ a.b: {c: d}, e: f }解析为{a_b_cd, ef}。解析出错时日志行不会被过滤而是附加__error__标签若提取的标签键与原始流标签重名会加_extracted后缀以区分可用标签格式化表达式强制覆盖同键重复提取时只保留第一个值。Loki 支持五种解析器JSON、logfmt、pattern、regexp、unpack。能用预定义的json、logfmt就优先使用结构特殊的日志用pattern比regexp更易写且更快或regexp。一个管道可以组合多个解析器解析复杂日志示例见多解析器示例。JSON 解析器有两种模式不带参数| json提取所有 JSON 属性为标签嵌套属性用_连接打平数组会被跳过。例如对下面的文档{ protocol: HTTP/2.0, servers: [129.0.1.1,10.2.1.3], request: {time: 6.032, method: GET, host: foo.grafana.net, size: 55, headers: {Accept: */*, User-Agent: curl/7.68.0}}, response: {status: 401, size: 228, latency_seconds: 6.031} }会提取出protocol、request_time、request_method、request_host、request_size、request_headers_Accept、request_headers_User_Agent、response_status、response_size、response_latency_seconds等标签。带参数| json labelexpression, anotherexpression只提取指定字段支持字段访问my.field、my[field]与数组访问list[0]及其任意嵌套组合。例如| json first_serverservers[0], uarequest.headers[\User-Agent\]如果标签名与 JSON 字段同名可直接写| json servers等价于serversservers若表达式返回数组或对象则会以 JSON 格式赋给标签。logfmt 解析器同样有两种模式| logfmt提取所有键值对例如把atinfo methodGET path/ hostgrafana.net fwd124.133.124.161 service8ms status200提取为at、method、path、host、fwd、service、status等标签带参数形式| logfmt host, fwd_ipfwd可只提取指定字段并重命名。它还支持两个标志--strict启用严格解析遇到格式不佳的键值对立即停止并返回错误不加该标志则跳过无效对继续解析尽力而为。--keep-empty保留无值独立键值为空字符串为标签。| logfmt --strict | logfmt --strict host, fwd_ipfwd | logfmt --keep-empty --strict host标志必须紧跟在logfmt之后、标签提取参数之前。pattern 解析器通过模式表达式| pattern pattern-expression显式提取字段模式由捕获captures与字面量literals组成。捕获是与包裹的字段名如example匿名捕获_用于跳过内容字面量可以是任意 UTF-8 字符序列含空白。捕获从行首或上一组字面量匹配到行尾或下一组字面量未匹配则解析停止。模式默认锚定行首不想锚定就在表达式开头用_。例如对 NGINX 日志0.191.12.2 - - [10/Jun/2021:09:14:29 0000] GET /api/plugins/versioncheck HTTP/1.1 200 2 - Go-http-client/2.0 13.76.247.102, 34.120.177.193 TLSv1.2 US 可用ip - - _ method uri _ status size _ agent _提取ip、method、uri、status、size、agent字段。模式不含任何命名捕获或包含两个未被空白分隔的连续捕获时属于无效表达式。regexp 解析器接收单个参数| regexp reGo RE2 语法正则必须至少包含一个命名子匹配如(?Pnamere)每个子匹配提取一个标签。例如| regexp (?Pmethod\\w) (?Ppath[\\w|/]) \\((?Pstatus\\d?)\\) (?Pduration.*)可把POST /api/prom/api/v1/query_range (200) 1.5s提取为methodPOST、path/api/prom/api/v1/query_range、status200、duration1.5s。unpack 解析器解析 JSON 日志行解包所有嵌入标签对应采集端pack阶段打包的数据并用特殊属性_entry替换原始日志行。例如| unpack会把{container: myapp, pod: pod-3223f, _entry: original log message}提取出container、pod标签并把original log message设为新的日志行。若嵌入的日志行是特定格式还可与json等其它解析器组合使用。行格式化表达式Line format| line_format {{.label_name}}使用 Go text/template 格式改写日志行内容不修改底层源数据只影响查询返回结果。所有标签都被注入模板变量可用{{.label_name}}引用{containerfrontend} | logfmt | line_format {{.query}} {{.duration}}模板可用双引号或反引号{{.label_name}}避免转义。line_format还支持math函数例如把毫秒duration除以 1000 转成秒{containerfrontend} | logfmt | line_format {{.ip}} {{.status}} {{div .duration 1000}}此外可通过__line__与__timestamp__函数访问原始日志行与时间戳全部可用模板函数见模板函数文档。标签格式化表达式Labels format| label_format可重命名、修改或新增标签参数为逗号分隔的等式列表两侧均为标签标识符如dstsrc时把src重命名为dst若dst不存在则新建重命名后src会被丢弃。右侧为模板字符串如dst{{.status}} {{.query}}时用 text/template 求值结果替换dst此时会保留被引用的标签dst{{.src}}会让dst与src同值并存。单个标签名在每个表达式中只能出现一次例如| label_format foobar,foonew不合法需拆成两次| label_format foobar | label_format foonew。丢弃标签与保留标签表达式Drop Labels语法|drop name, other_name, some_namesome_value丢弃指定标签也支持正则如app~some-api.*还可用于丢弃__error__标签。例如{jobvarlogs}|json|drop level, methodGET {jobvarlogs}|json|drop __error__ {jobvarlogs}|json|drop level, path, app~some-api.*Keep Labels语法|keep name, other_name, some_namesome_value只保留指定标签并丢弃其余标签。注意 keep 阶段不会丢弃 Loki 在查询时添加的__error__或__error_details__标签如需丢弃请用|drop。两种查询类型Log queries 与 Metric queriesLogQL 查询分为两类日志查询Log queries返回日志行的内容结构化或非结构化使用流选择器与日志管道且可以链式拼接形成更长的查询。详细语法见日志查询。指标查询Metric queries基于日志查询结果计算数值把日志变成指标。日志查询的完整示例{containerquery-frontend,namespaceloki-dev} | metrics.go | logfmt | duration 10s and throughput_mb 500它由两部分构成流选择器{containerquery-frontend,namespaceloki-dev}锁定loki-dev命名空间下的query-frontend容器管道| metrics.go | logfmt | duration 10s and throughput_mb 500先过滤出包含metrics.go的行再用 logfmt 解析出更多标签最后按duration与throughput_mb做标签过滤。查询的组成结构可参考查询构成示意图。技巧为避免转义特殊字符可用反引号代替双引号例如\w等价于\\w在写含多个反斜杠的正则时特别有用。指标查询Range Vector 聚合指标查询扩展了日志查询——对日志查询结果应用函数即可从日志中制造指标例如计算错误消息的速率或统计最近 3 小时产日志最多的 Top N 日志源配合解析器还可以从日志行中的采样值如延迟、请求大小计算指标。所有标签含提取标签都可用于聚合和生成新序列。LogQL 与 Prometheus 共享 range vector 概念在 Loki 中所选样本范围是选中的日志或标签值范围。Loki 支持两类 range vector 聚合日志范围聚合Log range aggregations查询后跟时长函数在时长内聚合。时长可放在流选择器之后或管道末尾。支持的函数rate(log-range)每秒条目数count_over_time(log-range)给定范围内各日志流的条目数bytes_rate(log-range)各流每秒字节数bytes_over_time(log-range)给定范围内各流消耗的字节量absent_over_time(log-range)范围向量有元素时返回空向量无元素时返回值为 1 的单元素向量适合对某段时间内不存在某标签组合的日志流告警示例count_over_time({jobmysql}[5m]) sum by (host) (rate({jobmysql} | error ! timeout | json | duration 10s [1m]))Offset 修饰符可改变单个 range vector 的时间偏移且必须紧跟 range vector 之后count_over_time({jobmysql}[5m] offset 5m) // 正确统计 10 分钟前到 5 分钟前的日志 count_over_time({jobmysql}[5m]) offset 5m // 非法解包范围聚合Unwrapped range aggregations用提取标签作为样本值而非日志行。日志查询须以 unwrap 表达式结尾可选标签过滤丢弃错误aggr-op([parameter,] unwrapped-range) [without|by (label list)]| unwrap label_identifier默认把字符串标签值转换为 64 位浮点数转换失败会打__error__标签也可用转换函数| unwrap function(label_identifier)duration_seconds(label)短写duration把 Go duration 格式如5m、24s30ms转为秒bytes(label)按字节单位如5 MiB、3k、1G转为原始字节数解包范围支持的聚合函数rate每秒所有值之和、rate_counter按计数器语义计算每秒速率、sum_over_time、avg_over_time、max_over_time、min_over_time、first_over_time、last_over_time、stdvar_over_time、stddev_over_time、quantile_over_time(scalar, unwrapped-range)φ 分位数0≤φ≤1、absent_over_time。除sum_over_time、absent_over_time、rate、rate_counter外均支持by/without分组without从结果向量中移除列出的标签并保留其余by反之丢弃未列出的标签。更多示例见 unwrap 示例。内置聚合操作符与 PromQL 类似LogQL 支持对单个向量的元素做聚合生成元素更少但带聚合值的新向量sum、avg、min、max、stddev、stdvar、count、topk、bottomk、sort按样本值升序、sort_desc降序。aggr-op([parameter,] vector expression) [without|by (label list)]其中topk/bottomk必须提供参数且与其它聚合器不同它们返回的是包含原始标签的输入样本子集by/without只用于对输入向量分组。函数与概率聚合vector(s scalar)把标量s作为无标签向量返回与 Prometheusvector()行为一致主要用于让原本无结果的查询返回一个值便于告警sum(count_over_time({namespacetraefik}[5m])) # 无结果 or vector(0) # 返回 0approx_count_distinct近似统计某个标签或提取字段的去重数量而不会为每个去重值生成一条序列适合count by会撑爆基数series cardinality的场景。使用前提在 Loki 配置的limits_config.shard_aggregations中加入approx_count_distinct并要求frontend.encoding: protobuf。语法approx_count_distinct( counted field, log expression [duration] ) [by (grouping fields)]支持即时与范围查询范围时长必填分组可选省略by保留剩余流标签by ()得到单条无标签序列不要按被统计字段分组。底层为每个输出组构建精度 14 的 HyperLogLog 与 pkg/logql/count_min_sketch.go。approx_topk实验特性无 SLAtopk的概率近似替代适合topk超时或触及最大序列数限制、以及更快的近似答案优于更慢的精确答案的场景。仅支持即时查询不支持分组应由内层sum by/sum without处理。底层基于分片 count-min sketch 堆实现精度取决于max_count_min_sketch_heap_size默认堆大小 10000k越接近堆大小精度越低。结果排序指标查询结果不保证任何顺序除非查询使用sort或sort_desc仅影响即时查询结果范围查询即使用了sort/sort_desc顺序也不保证。二进制操作符速览在 LogQL 指标查询中还可使用二进制操作符详见 LogQL 参考算术操作符、-、*、/、%、^可作用于标量/标量、向量/标量、向量/向量。示例1 1、sum(rate({appfoo}[1m])) * 2、sum(rate({appfoo, levelwarn}[1m])) / sum(rate({appfoo, levelerror}[1m]))。逻辑/集合操作符仅向量间and交集、or并集、unless补集。比较操作符、!、、、、默认起过滤作用可在其后加bool改为返回 0/1 而非过滤例如count_over_time({foobar}[1m]) 10与... bool 10。从源码看 LogQL 的实现骨架LogQL 的语法解析与执行在仓库 pkg/logql 中语法树节点定义于 pkg/logql/syntax/ast.goLogSelectorExpr、MatchersExpr、PipelineExpr、VectorAggregationExpr等词法/语法解析与查询优化位于同目录下的 pkg/logql/syntax执行引擎与评估器见 pkg/logql/engine.go、pkg/logql/evaluator.go向量与范围向量实现见 pkg/logql/vector 与 pkg/logql/range_vector.go。理解这些结构有助于排查查询问题或阅读配套的查询故障排查、查询示例与查询加速文档。小结Loki 查询体系的核心是一以贯之的 LogQL先用流选择器按标签收敛数据范围再用管道逐阶段过滤、解析、格式化日志最后根据需求选择日志查询取回日志行或指标查询聚合出指标。把行过滤前置、精心设计标签、合理使用解析器与unwrap是让 Loki 查询既快又准的三大关键实践。【免费下载链接】lokiLike Prometheus, but for logs.项目地址: https://gitcode.com/GitHub_Trending/lok/loki创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考