为什么 level="error" 查出来是零条
一句话总结
解析器取出的名称如果与已有的流标签冲突,Loki 会悄悄加上 _extracted 把它挤掉。label_format、line_format、keep、drop 是收拾这之后的局面的工具,四个都只改变响应,不触及索引。
为什么需要它
采集器给每个 Pod 都贴了 level 标签。值全是 info——在采集器看来,那个文件是应用的标准输出,所以当成了 info。可是应用在正文里也打印了自己的级别 level=error。
调查的人发出了 {app="payment"} | logfmt | level="error",结果是 0 条。他报告说“一个错误都没有”,实际上那段时间有 30 条。因为解析器取出的 level 与流标签 level 冲突,名称被改成了 level_extracted。他说没有错误的依据,其实是“名称冲突了”。
工作原理
解析器生成标签时,如果已经有同名的标签,Loki 会给后取出的那个加上 _extracted 后缀。原来的标签原样保留。既没有警告,也没有错误。所以第一次查询新的流时,要养成展开一条结果、用眼睛看 metric(或 stream)的键列表的习惯。
整理的工具有四个。
| 语法 | 作用 |
|---|---|
label_format 새이름=기존라벨(占位符依次为新名称、已有标签)或 label_format 이름=\{{.字段}}``(占位符依次为名称、字段) |
给标签重新命名,或用模板生成标签 |
line_format \{{.a}} {{.b}}`` |
重写行的正文本身 |
keep a, b |
只保留所列的标签 |
drop a, b |
丢弃所列的标签 |
模板是 Go 的 text/template。用 {{.필드}}(占位符为字段名)放入值,也可以像 {{.a | trim}} 这样用管道。line_format 会把正文整个换掉,所以它后面的行过滤器看到的是新的正文——顺序就是含义。
这四种语法都只作用于查询结果。索引中存储的流一个字也不会变。所以即使用 label_format 增加标签,流的数量也不会增加,成本也不会增加。对担心基数的值,不要作为标签建立索引,而是留在正文里,查询时像这样取出来用——这就是“不建立索引”这一设计真正占便宜的地方。
在指标查询里,这些工具更重要。想在 sum by (...) 里用的名称如果只在正文里有,就要先用 label_format 做出来;如果还留着取值五花八门的标签,时间序列就会被拆开,所以要用 keep 或 drop 整理。
在现场相遇的样子
名称冲突最常出在 level 上。因为采集器、运行时、应用各自都想说“级别”。如果在团队规约里定下“采集器贴的标签要加前缀”(例如 k8s_),这个事故本身就会消失。
第二是仪表板上难以阅读的日志面板。一行是 200 个字符的 JSON,人是没法扫读的。用 line_format 只留下需要的四五项,同样的面板马上就有用了。不过如果有人要在这个面板里搜索正文,最好加个注释,说明 line_format 必须放在行过滤器之后,这样更体贴。
第三是“加了标签,为什么成本不变”这个问题。这是正常的。查询时刻的标签不会被存储。
下一项实验要做什么
把流标签 level 与正文里的 level= 故意错开的数据放进 Pod 里的 Loki,亲眼看到 level="error" 返回 0 条。找出 _extracted 名称并数一数,用 label_format 覆盖后按想要的名称过滤,再用 line_format 重写行,用 keep 合并时间序列,得出各级别的条数。最后确认,即使改了标签,索引里的流数量也不变。