日志全都在,我却不知道该问什么
目标
直接向 Pod 里的真正的 Loki 发送 LogQL,通过流选择器和行过滤器取出想要的行,并能读懂返回的 JSON 的结构。
为什么重要
向 Loki 提问分为两层。首先由选择器决定读取哪些流的数据块,然后由行过滤器把读来的行逐条比对。不了解这个顺序,就永远解释不清“为什么这个查询快、那个查询慢”。正文没有索引,所以行过滤器会把选择器选出的内容全部读一遍——因此事故调查中最先做的事,就是缩小选择器和时间区间。limit 和 direction 也会改变事故调查的结果。不知道方向就加 limit,看到的将是最后一个错误,而不是最重要的第一个错误。
步骤
- 在
/root/lk-logql中启动 Loki,把date +%s的值以一行写入/root/lk-logql/anchor.txt,然后用python3 /opt/lab/d5/gen.py logql "$(cat anchor.txt)"放入数据。接着数一数 Loki 里现在有多少个流,以streams=<정수>(占位符为整数)一行写入/root/lk-logql/01-boot.txt。 - 求出从基准时刻往前一小时内,
app为web且level为error的行有多少行。把所用的查询写入/root/lk-logql/02-selector.logql,把行数以lines=<정수>(占位符为整数)一行写入/root/lk-logql/02-selector.txt。 - 求出在同一个一小时内,三个服务(
web、api、worker)全部中,正文含有timeout的行有多少行。查询写入/root/lk-logql/03-line.logql,答案以lines=<정수>(占位符为整数)写入/root/lk-logql/03-line.txt。 - 数出三个服务全部中,正文含有
refused且不含rpc的行。查询写入/root/lk-logql/04-chain.logql,答案以lines=<정수>(占位符为整数)写入/root/lk-logql/04-chain.txt。 - 用一个正则表达式行过滤器,数出三个服务全部中,正文含有三位数字 500 或 503 的行。查询写入
/root/lk-logql/05-regex.logql,答案以lines=<정수>(占位符为整数)写入/root/lk-logql/05-regex.txt。查询里必须包含正则表达式行过滤器运算符。 - 对
{app="web",level="error"}在同一个一小时区间里用limit=5查询两次。一次是direction=forward,一次是direction=backward。把结果写成三行存入/root/lk-logql/06-order.txt——forward_first=<나노초 타임스탬프>、backward_first=<나노초 타임스탬프>、total=<구간 전체 줄 수>(占位符依次为纳秒时间戳、纳秒时间戳、区间内的总行数)。 - 随便发一次日志查询,查看原始 JSON,并在
/root/lk-logql/07-shape.txt中写三行。result_type=后面写data.resultType的值,ts_unit=后面写values的第一项是什么时间单位(ns、us、ms、s之一),entry_len=后面以整数写values的一个元素是几项的数组。 - 只选出
web和api两个服务中,正文含有timeout或refused的行,求出其中最早一行的纳秒时间戳和总行数。查询写入/root/lk-logql/08-triage.logql,答案以lines=<정수>和first_ts=<나노초>(占位符依次为整数、纳秒)两行写入/root/lk-logql/08-triage.txt。查询里必须包含正则表达式行过滤器,并且不能包含 worker。
参考
- 工作目录是
/root/lk-logql。Loki 不会在 Pod 启动时自动开启——要在第 1 步里亲手启动。 - 数据生成器是
/opt/lab/d5/gen.py。用基准时刻参数运行logql数据。评分器不读它,所以不需要修改其内容。 - 查询也可以用
logcli发送:LOKI_ADDR=http://localhost:3100 logcli query --limit=5 '{app="web"}'。不过本实验的答案要按绝对区间来测量,所以直接调用query_rangeAPI 更准确。 - 常见错误:用
since=1h来测量。时间一流逝,答案就变了,重新评分时会失败。要用anchor.txt里的基准时刻来给出start和end。 - 常见错误:用双引号包住行过滤器的字符串。在 LogQL 里,反引号更安全——反斜杠不会被当作转义。
- LogQL 概述 · 日志查询 · HTTP API · 标签
启动 Loki,放入一小时而不是一整天的数据
在 /root/lk-logql 中启动 Loki,把 date +%s 的值以一行写入 /root/lk-logql/anchor.txt,然后用 python3 /opt/lab/d5/gen.py logql "$(cat anchor.txt)" 放入数据。接着数一数 Loki 里现在有多少个流,以 streams=<정수>(占位符为整数)一行写入 /root/lk-logql/01-boot.txt。
配置复制 /opt/lab/loki/loki.yaml 来用。/ready 返回 ready 要等 20 秒左右,所以不要用固定的 sleep,要用检查条件的循环。流的数量由 /opt/lab/loki/streams.sh 来数——流就是不同标签组合的个数。
只用选择器确定要看的地方
求出从基准时刻往前一小时内,app 为 web 且 level 为 error 的行有多少行。把所用的查询写入 /root/lk-logql/02-selector.logql,把行数以 lines=<정수>(占位符为整数)一行写入 /root/lk-logql/02-selector.txt。
流选择器是花括号里的标签匹配器。用逗号连接条件,只会选出同时满足两个条件的流。区间要用纳秒给出 start 和 end——把 anchor.txt 的值乘以 1000000000 就是纳秒。
用行过滤器搜索正文
求出在同一个一小时内,三个服务(web、api、worker)全部中,正文含有 timeout 的行有多少行。查询写入 /root/lk-logql/03-line.logql,答案以 lines=<정수>(占位符为整数)写入 /root/lk-logql/03-line.txt。
行过滤器放在选择器后面。按原样查找字符串的运算符,与用正则表达式查找的运算符是不同的。正文没有索引,所以行过滤器要把选择器选出的流的行全部读一遍并比对。
把过滤器串起来筛选
数出三个服务全部中,正文含有 refused 且不含 rpc 的行。查询写入 /root/lk-logql/04-chain.logql,答案以 lines=<정수>(占位符为整数)写入 /root/lk-logql/04-chain.txt。
行过滤器可以串联多个,从左到右依次应用。表示“不包含”的运算符是单独存在的。不要试图把两个条件合并成一个正则表达式——否定用串联的方式更容易读。
正则表达式行过滤器
用一个正则表达式行过滤器,数出三个服务全部中,正文含有三位数字 500 或 503 的行。查询写入 /root/lk-logql/05-regex.logql,答案以 lines=<정수>(占位符为整数)写入 /root/lk-logql/05-regex.txt。查询里必须包含正则表达式行过滤器运算符。
正则表达式行过滤器采用 RE2 语法。“一个字符可以是多个值中的某一个”,用方括号来写。把两个 |= 串联,就成了“两者都含有的行”,答案就会不同。
limit 与 direction——哪些会被截掉
对 {app="web",level="error"} 在同一个一小时区间里用 limit=5 查询两次。一次是 direction=forward,一次是 direction=backward。把结果写成三行存入 /root/lk-logql/06-order.txt——forward_first=<나노초 타임스탬프>、backward_first=<나노초 타임스탬프>、total=<구간 전체 줄 수>(占位符依次为纳秒时间戳、纳秒时间戳、区间内的总行数)。
limit 不是“从前面开始的几行”,而是“按排序方向计的几行”。改变方向,同一个 limit 会返回完全相反的行。在事故调查中不知道这一点,就看不到最重要的第一个错误,只能看到最后一个错误。总行数要把 limit 给得宽裕一些再数。
读懂返回的 JSON
随便发一次日志查询,查看原始 JSON,并在 /root/lk-logql/07-shape.txt 中写三行。result_type= 后面写 data.resultType 的值,ts_unit= 后面写 values 的第一项是什么时间单位(ns、us、ms、s 之一),entry_len= 后面以整数写 values 的一个元素是几项的数组。
用 curl ... | jq . 原样查看。日志查询和指标查询的 resultType 不同。数一数时间戳的位数就能知道单位——epoch 秒是十位数。了解这个结构,才能写自动化脚本。
应用——用一个查询缩小事故区间
只选出 web 和 api 两个服务中,正文含有 timeout 或 refused 的行,求出其中最早一行的纳秒时间戳和总行数。查询写入 /root/lk-logql/08-triage.logql,答案以 lines=<정수> 和 first_ts=<나노초>(占位符依次为整数、纳秒)两行写入 /root/lk-logql/08-triage.txt。查询里必须包含正则表达式行过滤器,并且不能包含 worker。
在选择器里同时使用只选这两个服务的匹配器,和查找二者之一的正则表达式。要找最早的行,可以把排序方向设为向前,或者对收到的结果自己排序。这就是事故调查的第一个动作——缩小范围,把起始时刻钉死。