我报告说没有错误,那一小时其实有三十条
目标
亲手重现解析器取出的名称与流标签冲突而被挤掉的现象,并用 label_format、line_format、keep、drop 把结果整理成想要的样子。
为什么重要
在 Loki 里,标签来自两个地方——采集时附加并被索引的流标签,以及查询时解析器从正文里取出的标签。名称重复时,后取出的那个会被加上 _extracted 挤掉,既没有警告也没有错误。所以 level="error" 返回 0 条,人就会把它读成“没有错误”。查询时刻对名称的整理只作用于结果,不触及索引,所以尽管用,成本也不会增加——这就是“把担心基数的值留在正文里、查询时取出来用”这种设计能够成立的原因。
步骤
- 在
/root/lk-format中启动 Loki,把date +%s写入/root/lk-format/anchor.txt,然后用python3 /opt/lab/d5/gen.py format "$(cat anchor.txt)"放入数据。接着把{app="payment"}一小时的行数,以及附在该流上的流标签名称,写成两行存入/root/lk-format/01-boot.txt——lines=<정수>和labels=<이름들을 쉼표로, 사전순>(占位符依次为整数、按字典序用逗号连接的名称)。 - 用一小时区间发出
{app="payment"} | logfmt | level="error",看结果有几行,再用被挤掉名称的标签重新查询,数出有几行。答案写成三行存入/root/lk-format/02-collision.txt——naive=<정수>、extracted_name=<밀려난 라벨 이름>、correct=<정수>(占位符依次为整数、被挤掉的标签名称、整数)。 - 用
label_format让正文里的级别值以level这个名称出现,然后用level="error"过滤。查询写入/root/lk-format/03-relabel.logql,答案以lines=<정수>(占位符为整数)写入/root/lk-format/03-relabel.txt。结果行数必须与第 2 步的correct相同。 - 把
{app="payment"}的所有行重写成<svc>/<본문의 수준>/<dur_ms>(占位符依次为 svc、正文中的级别、dur_ms)形态的查询,写入/root/lk-format/04-line.logql。只用两个斜杠分隔,不放其他字符。然后把结果中最早一行的正文,以一行写入/root/lk-format/04-line.txt。 - 把按正文中的级别统计行数的指标查询写入
/root/lk-format/05-bylevel.logql。结果必须恰好是两条时间序列,每条序列的标签只有lvl一个。值以info=<정수>和error=<정수>(占位符为整数)两行写入/root/lk-format/05-bylevel.txt。 - 在第 5 步的查询里,用
drop代替keep,写出能得到同样结果的查询,保存到/root/lk-format/06-drop.logql。再在/root/lk-format/06-drop.txt中写两行——series=<계열 수>和dropped=<버린 라벨 이름들을 쉼표로, 사전순>(占位符依次为时间序列数、按字典序用逗号连接的被丢弃标签名称)。 - 第 3 步用
label_format做了标签,第 5 步用新名称合并了时间序列。确认之后索引里的流数量有没有变化,写成两行存入/root/lk-format/07-index.txt——streams=<정수>和changed=<yes|no>(占位符为整数、yes 或 no)。流数量用 series API 来数。 - 在
/root/lk-format/08-panel.logql中写一个查询。条件有三个——(1)只返回正文级别为error的行,(2)返回的正文是<dur_ms>ms <svc> <region>(占位符依次为 dur_ms、svc、region)形态(只用两个空格分隔,没有其他字符),(3)行过滤器要放在重写正文之前。然后把结果中最早的一行,以一行写入/root/lk-format/08-panel.txt。
参考
- 工作目录是
/root/lk-format。Loki 要在第 1 步里亲手启动。 - 数据生成器是
/opt/lab/d5/gen.py,使用format数据。评分器不读这个文件。 - 模板是 Go 的 text/template。在字段前加点,再用两层花括号包起来。查询语句用反引号包起来更安全。
- 结果的标签列表,要养成用
jq '.data.result[0].stream'展开看一遍的习惯。名称被挤掉,只有这样才看得出来。 - 常见错误:把
line_format放在行过滤器前面。它后面的过滤器看到的是新的正文。 - 常见错误:用
since=1h来测量。要用anchor.txt的基准时刻给出start和end。 - 日志查询与格式化 · 指标查询 · 标签 · HTTP API
放入标签和正文不一致的数据
在 /root/lk-format 中启动 Loki,把 date +%s 写入 /root/lk-format/anchor.txt,然后用 python3 /opt/lab/d5/gen.py format "$(cat anchor.txt)" 放入数据。接着把 {app="payment"} 一小时的行数,以及附在该流上的流标签名称,写成两行存入 /root/lk-format/01-boot.txt——lines=<정수> 和 labels=<이름들을 쉼표로, 사전순>(占位符依次为整数、按字典序用逗号连接的名称)。
流标签就是结果的 stream 对象的键。Loki 3.x 会自动加上 service_name,所以它也在列表里。按字典序排序后用逗号连接(不带空格)。
level="error" 返回了 0 条
用一小时区间发出 {app="payment"} | logfmt | level="error",看结果有几行,再用被挤掉名称的标签重新查询,数出有几行。答案写成三行存入 /root/lk-format/02-collision.txt——naive=<정수>、extracted_name=<밀려난 라벨 이름>、correct=<정수>(占位符依次为整数、被挤掉的标签名称、整数)。
用 jq '.data.result[0].stream' 展开一条结果,看键列表。解析器取出的名称如果与已有的流标签冲突,Loki 会加上后缀把它挤掉。用那个名称重新查询,就会出现数字。
用 label_format 把名称改回来
用 label_format 让正文里的级别值以 level 这个名称出现,然后用 level="error" 过滤。查询写入 /root/lk-format/03-relabel.logql,答案以 lines=<정수>(占位符为整数)写入 /root/lk-format/03-relabel.txt。结果行数必须与第 2 步的 correct 相同。
label_format 可以写成 새이름=기존라벨(占位符依次为新名称、已有标签)的形态,也可以给出模板。覆盖已有的名称也可以。顺序很重要——要在改名之后再过滤。
用 line_format 重写行
把 {app="payment"} 的所有行重写成 <svc>/<본문의 수준>/<dur_ms>(占位符依次为 svc、正文中的级别、dur_ms)形态的查询,写入 /root/lk-format/04-line.logql。只用两个斜杠分隔,不放其他字符。然后把结果中最早一行的正文,以一行写入 /root/lk-format/04-line.txt。
line_format 接受 Go 模板。在字段名前加点。级别要用第 2 步找到的被挤掉的名称。用 direction=forward 接收,最早的行会排在最前面。
用 keep 合并时间序列
把按正文中的级别统计行数的指标查询写入 /root/lk-format/05-bylevel.logql。结果必须恰好是两条时间序列,每条序列的标签只有 lvl 一个。值以 info=<정수> 和 error=<정수>(占位符为整数)两行写入 /root/lk-format/05-bylevel.txt。
用 label_format 做出要用的名称,用 keep 只留下那个名称,再在外面套上 sum by (lvl) (count_over_time(... [1h]))。也看一看去掉 keep 会怎么样——时间序列数马上就会暴露出来。
drop 丢弃什么,保留什么
在第 5 步的查询里,用 drop 代替 keep,写出能得到同样结果的查询,保存到 /root/lk-format/06-drop.logql。再在 /root/lk-format/06-drop.txt 中写两行——series=<계열 수> 和 dropped=<버린 라벨 이름들을 쉼표로, 사전순>(占位符依次为时间序列数、按字典序用逗号连接的被丢弃标签名称)。
drop 要把名称逐个写出来丢弃。把第 1 步记下的流标签列表,与第 2 步看到的解析器标签列表合在一起,就知道该丢弃什么。要保留的少时用 keep,要丢弃的少时用 drop,写起来更短。
应用 ① ——改了标签,索引也不变
第 3 步用 label_format 做了标签,第 5 步用新名称合并了时间序列。确认之后索引里的流数量有没有变化,写成两行存入 /root/lk-format/07-index.txt——streams=<정수> 和 changed=<yes|no>(占位符为整数、yes 或 no)。流数量用 series API 来数。
给 /loki/api/v1/series 传入 match[]={app=~".+"},就会得到现在索引里的流列表。看看查询时刻做出的标签在不在里面。如果不在,那正是“查询时刻的标签不会被存储”的证据。
应用 ② ——人能读懂的错误面板查询
在 /root/lk-format/08-panel.logql 中写一个查询。条件有三个——(1)只返回正文级别为 error 的行,(2)返回的正文是 <dur_ms>ms <svc> <region>(占位符依次为 dur_ms、svc、region)形态(只用两个空格分隔,没有其他字符),(3)行过滤器要放在重写正文之前。然后把结果中最早的一行,以一行写入 /root/lk-format/08-panel.txt。
顺序就是含义——line_format 之后的过滤器看到的是新的正文。所以要先过滤,后重写。按级别过滤时,用第 2 步找到的名称或第 3 步做出的名称都可以。