TT Lab
开始
学习 学习路径 课程

Loki — 不索引日志的日志库

一个错误都没有,5xx 的数量却只有真实值的三分之一

在 TT Lab 中继续学习

目标

在 Pod 里的真正的 Loki 上亲手接上四种 LogQL 解析器,分别数出解析出错的行和没有报错却悄悄没能取出值的行,求出真正的 5xx 数量。

为什么重要

Loki 不为正文建立索引。所以要用正文里的值来过滤,就必须在每次查询时用解析器取出来,解析器选错了,数字就会悄悄减少。json 会用 __error__ 标签报告失败,但 logfmt 不会——遇到格式不同的行,它不生成任何标签就过去了,没有标签的话,后面的标签过滤器会悄悄丢掉那一行。仪表板上既没有错误也没有警告。所以查询新的流之前,总要先测量两个数字——解析失败的行数,以及没有失败却没取出值的行数。

步骤

  1. 在 /root/lk-parsers 中启动 Loki,把 date +%s 写入 /root/lk-parsers/anchor.txt,然后用 python3 /opt/lab/d5/gen.py parsers "$(cat anchor.txt)" 放入数据。接着数出从基准时刻往前一小时内,{app="mixed"} 和 {app="orders"} 各有多少行,以 mixed=<정수> 和 orders=<정수>(占位符为整数)两行写入 /root/lk-parsers/01-boot.txt。
  2. 用 logfmt 解析器数出 orders 流中 status 为 500 的行有多少行。查询写入 /root/lk-parsers/02-logfmt.logql,答案以 lines=<정수>(占位符为整数)写入 /root/lk-parsers/02-logfmt.txt。
  3. 给 mixed 流接上 json 解析器,分别数出解析成功的行和带有 __error__ 标签的行。答案以 ok=<정수> 和 err=<정수>(占位符为整数)两行写入 /root/lk-parsers/03-json.txt,并把 __error__ 的一个实际值以一行写入 /root/lk-parsers/03-json-name.txt。
  4. 给 mixed 流接上 logfmt 解析器时,数出没有报错、status 标签却没有生成的行有多少行。查询写入 /root/lk-parsers/04-silent.logql,答案以 lines=<정수>(占位符为整数)写入 /root/lk-parsers/04-silent.txt。
  5. 只选出正文含有 legacy 的行,用 pattern 解析器取出状态码,数出该值为 500 的行。查询写入 /root/lk-parsers/05-pattern.logql,答案以 lines=<정수>(占位符为整数)写入 /root/lk-parsers/05-pattern.txt。查询里必须包含 pattern 解析器。
  6. 在同样的遗留行中,用 regexp 解析器取出所花的时间(毫秒),数出大于 1000 的行。查询写入 /root/lk-parsers/06-regexp.logql,答案以 lines=<정수>(占位符为整数)写入 /root/lk-parsers/06-regexp.txt。查询里必须包含 regexp 解析器。
  7. 把 mixed 流的行分成三种格式来数,做出 /root/lk-parsers/tally.tsv。不要表头,共三行,每行是用制表符分隔的两列 <형식><탭><줄수>(占位符依次为格式、制表符、行数)。格式名称依次为 json、legacy、logfmt,三行之和必须等于第 1 步数出的 mixed 的总行数。
  8. 求出 mixed 流中状态码为 500 的行的真正总数,以 total=<정수>(占位符为整数)写入 /root/lk-parsers/08-total.txt,在下一行写一个以 reason= 开头的句子(去掉空格后至少 40 个字)。用自己的话写出为什么只用一个解析器无法数出这个数字。

参考

拿到一个格式混杂的流

在 /root/lk-parsers 中启动 Loki,把 date +%s 写入 /root/lk-parsers/anchor.txt,然后用 python3 /opt/lab/d5/gen.py parsers "$(cat anchor.txt)" 放入数据。接着数出从基准时刻往前一小时内,{app="mixed"} 和 {app="orders"} 各有多少行,以 mixed=<정수> 和 orders=<정수>(占位符为整数)两行写入 /root/lk-parsers/01-boot.txt。

/ready 返回 ready 要等 20 秒左右。区间用 anchor.txt 的值以纳秒给出 start 和 end。不用解析器,只用选择器来数就行。

格式整洁时,一行 logfmt 就够了

用 logfmt 解析器数出 orders 流中 status 为 500 的行有多少行。查询写入 /root/lk-parsers/02-logfmt.logql,答案以 lines=<정수>(占位符为整数)写入 /root/lk-parsers/02-logfmt.txt。

解析器用管道接在选择器后面。解析器生成的标签,可以在其后用作标签过滤器。这与用行过滤器 |= "status=500" 这样查找字符串是不同的——这次要用解析器取出的值。

json 解析器会报告失败

给 mixed 流接上 json 解析器,分别数出解析成功的行和带有 __error__ 标签的行。答案以 ok=<정수> 和 err=<정수>(占位符为整数)两行写入 /root/lk-parsers/03-json.txt,并把 __error__ 的一个实际值以一行写入 /root/lk-parsers/03-json-name.txt。

解析器失败时会加上 __error__ 标签。分别选出该标签为空的行和不为空的行即可。标签的值是什么,直接看结果里的 stream 就能看到——__error_details__ 也会一起带上。

数出没有报错、却什么也没取出来的行

给 mixed 流接上 logfmt 解析器时,数出没有报错、status 标签却没有生成的行有多少行。查询写入 /root/lk-parsers/04-silent.logql,答案以 lines=<정수>(占位符为整数)写入 /root/lk-parsers/04-silent.txt。

在 LogQL 里,“不存在的标签”与空字符串等同比较。利用这个性质,就能选出“解析器没能生成值的行”。这个数字是本实验里最可怕的数字——仪表板上哪里都不会出现,却在悄悄削减合计。

位置固定的行适合用 pattern

只选出正文含有 legacy 的行,用 pattern 解析器取出状态码,数出该值为 500 的行。查询写入 /root/lk-parsers/05-pattern.logql,答案以 lines=<정수>(占位符为整数)写入 /root/lk-parsers/05-pattern.txt。查询里必须包含 pattern 解析器。

遗留行的形态是 legacy handler finished code 500 in 123ms。pattern 在要提取的位置用尖括号写上名称,其余照原样写成文字。在解析器前面放一个行过滤器,只把遗留行传下去,这一点很重要——其他格式的行里没有这种形态。

用正则表达式解析器取出数字并比较

在同样的遗留行中,用 regexp 解析器取出所花的时间(毫秒),数出大于 1000 的行。查询写入 /root/lk-parsers/06-regexp.logql,答案以 lines=<정수>(占位符为整数)写入 /root/lk-parsers/06-regexp.txt。查询里必须包含 regexp 解析器。

regexp 只会把命名捕获组变成标签——没有名字的括号会被丢弃。取出的值是字符串,但用不等号比较时,LogQL 会把它转成数字。同样的答案也可以用 pattern 得出——比一比哪一种更好读。

应用 ① ——按格式做一张行数表

把 mixed 流的行分成三种格式来数,做出 /root/lk-parsers/tally.tsv。不要表头,共三行,每行是用制表符分隔的两列 <형식><탭><줄수>(占位符依次为格式、制表符、行数)。格式名称依次为 json、legacy、logfmt,三行之和必须等于第 1 步数出的 mixed 的总行数。

JSON 行就是 json 解析器不报错的行。遗留行的正文里有 legacy。剩下的是 logfmt 行,这些行用 logfmt 能生成 status。一定要确认三个数字之和与总数相符——不符的话,说明某处数了两遍或漏掉了。

应用 ② ——真正的 5xx 数量,以及把这个事实留下来

求出 mixed 流中状态码为 500 的行的真正总数,以 total=<정수>(占位符为整数)写入 /root/lk-parsers/08-total.txt,在下一行写一个以 reason= 开头的句子(去掉空格后至少 40 个字)。用自己的话写出为什么只用一个解析器无法数出这个数字。

在一个查询里接两个解析器,也不能同时读取两种格式。要按格式分别数,再相加。想一想第 4 步数出的“悄悄漏掉的行”去了哪里,就能看出要分成几路。写答案时,也要确认三路的数字各是多少。