第七章:查询语言
深入学习 LogQL 日志查询语言,包括日志查询和指标查询。
最后更新: 2024-01-21
页面目录
LogQL 查询语言
LogQL 是 Loki 的日志查询语言,类似于 Prometheus 的 PromQL。
LogQL 概述
┌─────────────────────────────────────────────────────────────────┐
│ LogQL 查询类型 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ LogQL │
│ ├── Log Queries (日志查询) │
│ │ ├── Log stream selectors │
│ │ ├── Line filters │
│ │ ├── Label filters │
│ │ └── Line format │
│ │ │
│ └── Metric Queries (指标查询) │
│ ├── Range vector aggregation │
│ ├── Instant vector aggregation │
│ └── Operations │
│ │
└─────────────────────────────────────────────────────────────────┘
日志查询语法
基本结构
{job="nginx"} |= "error" | json | line_format "{{.status}} {{.message}}"
│
├── Stream selector (日志流选择器)
├── Line filter (行过滤器)
├── Parser (解析器)
└── Line format (格式化)
日志流选择器
基本语法
# 基本标签选择
{job="nginx"}
{job="nginx", namespace="production"}
{job=~"api-.*", env=~"prod|staging"}
# 组合条件
{job="nginx", instance=~"web-.*", env="production"}
标签匹配运算符
| 运算符 | 说明 | 示例 |
|---|---|---|
= |
精确匹配 | job="nginx" |
!= |
不等于 | job!="nginx" |
=~ |
正则匹配 | job=~"api-.*" |
!~ |
正则不匹配 | job!~".*test" |
行过滤器
过滤器语法
{job="nginx"} |= "error"
{job="nginx"} != "debug"
{job="nginx"} |~ "timeout.*connection"
{job="nginx"} |~! "info.*debug"
过滤器类型
| 过滤器 | 说明 |
|---|---|
| ` | =` |
!= |
不包含字符串 |
| ` | ~` |
| ` | ~!` |
组合过滤器
# 多个过滤器
{job="nginx"} |= "error" != "timeout" |~ "connection.*failed"
# 顺序很重要
{job="nginx"} |= "error" |= "database"
标签过滤器
JSON 字段过滤
{job="api"} | json | status_code >= 500
{job="api"} | json | level != "debug"
{job="api"} | json | user_id != ""
常用操作符
| 操作符 | 说明 |
|---|---|
== |
等于 |
!= |
不等于 |
> |
大于 |
>= |
大于等于 |
< |
小于 |
<= |
小于等于 |
=~ |
正则匹配 |
!~ |
正则不匹配 |
==~ |
大小写敏感正则 |
示例
# JSON 日志过滤
{job="api"} | json | status >= 500
{job="api"} | json | duration > 1000
{job="api"} | json | method == "POST"
{job="api"} | json | path =~ "/api/.*"
# 复杂条件
{job="api"} | json | status >= 500 and duration > 1000
{job="api"} | json | level in ("error", "fatal")
{job="api"} | json | user_id notin ("", "anonymous")
日志解析
JSON 解析
{job="api"} | json
{job="api"} | json | status_code
{job="api"} | json | level="error"
Logfmt 解析
{job="api"} | logfmt
{job="api"} | logfmt | level="error"
{job="api"} | logfmt | latency > 100
正则解析
{job="api"} | regex "(?P<method>\\w+) (?P<path>\\w+)"
{job="api"} | regex "(?P<ip>\\d+\\.\\d+\\.\\d+\\.\\d+)"
Unpack 解析
{job="api"} | unpack
{job="api"} | unpack | _pkg="github.com/app/api"
日志格式化
line_format
{job="api"} | json | line_format "{{.status}} {{.message}}"
{job="api"} | json | line_format "{{.timestamp}} [{{.level}}] {{.message}}"
{job="api"} | json | line_format "[{{.status}}] {{.ip}} - {{.user}}"
格式化函数
| 函数 | 说明 | 示例 |
|---|---|---|
{{.field}} |
字段替换 | {{.status}} |
"text" |
文本输出 | "Error:" |
\n |
换行 | \n |
{{.field | json}} |
JSON 编码 | {{.data | json}} |
示例
# 基本格式化
{job="api"} | json | line_format "[{{.status}}] {{.message}}"
# 复杂格式化
{job="api"} | json | line_format "{{.ts}} {{.level | upper}} {{.service}} {{.msg}}"
# 条件格式化
{job="api"} | json | line_format "{{if gt .status 500}}ERROR{{else}}OK{{end}} {{.msg}}"
标签操作
label_format
{job="api"} | label_format dst="{{.src}}"
{job="api"} | json | label_format service="{{.app}}-{{.env}}"
{job="api"} | label_format duration_ms="{{.duration}}ms"
指标查询
概述
┌─────────────────────────────────────────────────────────────────┐
│ 指标查询类型 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Metric Query │
│ ├── Range Vector │
│ │ └── rate, increase, count_over_time │
│ │ │
│ └── Instant Vector │
│ └── sum, avg, count, topk, bottomk │
│ │
└─────────────────────────────────────────────────────────────────┘
基本语法
# 速率计算
rate({job="nginx"}[5m])
# 计数
count_over_time({job="nginx"}[5m])
# 聚合
sum(rate({job="nginx"}[5m]))
向量聚合函数
Range Vector 函数
# 速率计算
rate({job="api"}[5m]) # 每秒平均速率
increase({job="api"}[5m]) # 总增量
irate({job="api"}[5m]) # 即时速率
# 计数
count_over_time({job="api"}[5m]) # 时间窗口内日志数
bytes_rate({job="api"}[5m]) # 字节速率
bytes_over_time({job="api"}[5m]) # 时间窗口内字节数
Instant Vector 函数
# 聚合
sum(rate({job="api"}[5m]))
avg(rate({job="api"}[5m]))
max(rate({job="api"}[5m]))
min(rate({job="api"}[5m]))
count(rate({job="api"}[5m]))
# 排序
topk(5, rate({job="api"}[5m]))
bottomk(5, rate({job="api"}[5m]))
二元运算
# 运算
sum(rate({job="api"}[5m])) / sum(rate({job="total"}[5m])) * 100
# 比较
sum(rate({job="api", status=~"5.."}[5m])) > 10
聚合操作
sum_by
# 按标签聚合
sum by (status) (rate({job="api"}[5m]))
sum by (namespace, pod) (rate({job="nginx"}[5m]))
# 不按标签聚合(全局)
sum without (pod) (rate({job="nginx"}[5m]))
聚合运算符
| 运算符 | 说明 |
|---|---|
sum |
求和 |
min |
最小值 |
max |
最大值 |
avg |
平均值 |
stdvar |
标准方差 |
stddev |
标准差 |
count |
计数 |
bottomk |
最小 k 个 |
topk |
最大 k 个 |
实用查询示例
错误率
# 错误日志速率
sum(rate({job="api"} | json | status >= 500 [5m]))
请求延迟
# P99 延迟
quantile_over_time(0.99, {job="api"} | json | latency [5m])
流量统计
# 每秒请求数
sum(rate({job="api"}[1m]))
# 按服务分组
sum by (service) (rate({job="api"}[1m]))
日志分布
# 按状态码分布
sum by (status) (rate({job="api"}[5m]))
# 按 IP 分布
topk(10, sum by (client_ip) (rate({job="api"} | json [5m])))
告警规则查询
# 错误率告警
sum(rate({job="api"} | json | status >= 500 [5m])) > 0.01
# 错误数量告警
sum(increase({job="api"} | json | status >= 500 [5m])) > 100
# 无日志告警(服务离线)
sum(rate({job="api"}[5m])) == 0
常用查询模板
1. 错误日志查询
{job="myapp"} |= "error" | json | line_format "{{.timestamp}} [{{.level}}] {{.message}}"
2. 性能监控
# 请求延迟 P99
quantile_over_time(0.99, {job="api"} | json | unwrap latency [5m]) by (service)
# 吞吐量
sum by (service) (rate({job="api"}[1m]))
3. 安全审计
# 失败登录
{job="auth"} |= "login failed" | json | line_format "{{.user}} from {{.ip}}"
# 可疑 IP
topk(10, sum by (ip) (rate({job="auth"} |= "failed" [5m])))
4. 应用调试
# 特定请求追踪
{job="api"} |= "trace_id=abc123" | json
# 异常堆栈
{job="api"} |= "panic" | json | line_format "{{.stack}}"
Grafana 中的使用
Explore 面板
# 在 Grafana Explore 中使用
{job="nginx"} |= "error" | json
仪表板变量
# 变量查询
label_values({job="nginx"}, namespace)
# 查询
{job="nginx", namespace="$namespace"}
性能优化
1. 使用具体的选择器
# ✅ 推荐:具体的选择器
{job="api", service="user", namespace="production"}
# ❌ 避免:过于宽泛
{job=~".*"}
2. 早过滤
# ✅ 推荐:尽早过滤
{job="api"} |= "error" | json | status >= 500
# ❌ 避免:晚过滤
{job="api"} | json | status >= 500 |= "error"
3. 使用正则优化
# ✅ 推荐:简单匹配
{job="api"} |~ "error.*timeout"
# ❌ 避免:复杂正则
{job="api"} |~ "(.*)error(.*)"
下一步
接下来让我们学习告警规则配置。
👉 告警规则