mtail笔记
# 简介
- Google 开源的一款可从日志文件中解析指标的 prometheus exporter
- 使用正则语法提取日志中的指标
# 部署
mtail/docs/Deploying.md at main · google/mtail (opens new window)
# 二进制文件
Releases · google/mtail (opens new window)
wget https://github.com/google/mtail/releases/download/v3.0.8/mtail_3.0.8_linux_amd64.tar.gz
wget https://ghp.ci/https://github.com/google/mtail/releases/download/v3.0.8/mtail_3.0.8_linux_amd64.tar.gz
tar -xf mtail_3.0.8_linux_amd64.tar.gz mtail
# 作为系统命令
sudo mv ./mtail /usr/local/bin/mtail
# 启动
mtail --progs /etc/mtail --logs /var/log/syslog --logs /var/log/ntp/peerstats
# or
nohup mtail --progs /etc/mtail --logs /var/log/syslog --logs /var/log/ntp/peerstats > /dev/null 2>&1 &
--progs
:.mtail
文件目录路径(这里的 mtail 是解析规则文件后缀,不是二进制文件mtail
)--logs
:要解析的日志文件,可多次传参-poll_log_interval
:轮询时间,默认250ms
nohup journalctl -u ceremonyclient.service -f --no-hostname -o cat > /var/log/ceremonyclient.log &
# Docker
docker run -dP \
--name my-mtail \
--volumes-from myapp \
-v examples:/etc/mtail \
mtail --logs /var/log/myapp --progs /etc/mtail
# 测试
curl http://localhost:3903/metrics
# 解析语法
mtail/docs/Language.md at main · google/mtail (opens new window) mtail/docs/Programming-Guide.md at main · google/mtail (opens new window)
- mtail 的解析规则存储
.mtail
后缀文件 - mtail 通过启动参数
--progs
传递解析规则文件路径 - mtail 的解析逻辑是单行处理的,因此无法多行匹配解析。
# 文件结构
xxx.mtail
:文件名会作为指标的属性 prog
# 导出变量 exported variable
counter lines_total
gauge queue_length
# 匹配与行为
pattern {
# 匹配后执行的动作
action statements
}
# 函数(Decorated actions)
def decorator {
pattern and action statements
}
# 变量
- 变量使用前必须先声明,因此变量定义在最前面
- 这些变量是用于给 Prometheus 爬取用的,即导出变量
counter lines_total
gauge queue_length
# 添加指标属性gpu_index
counter aleo_hashrate by gpu_index
# aleo_hashrate[$1] = $2
类型跟 Prometheus 的类型相同:
counter
:计数器,只能递增gauge
:仪表盘,可增可减
把 gauge
理解为 int
, counter
理解为只能 ++
的 int
# 匹配与行为
# 匹配(流程控制)
类似于一个个 if
语句的便是 mtail 解析规则
/foo/ {
ACTION1
}
variable > 0 {
ACTION2
}
/foo/ && variable > 0 {
ACTION3
}
在上面的程序中,跟其他语言的 if
语句执行逻辑是类似的:
- 如果该行与单词
foo
匹配,则对每一行输入执行 ACTION1; - 如果读取该行时,变量
variable
大于 0,则对每一行执行 ACTION2。 - 如果两者都为 true,则发生 ACTION3。
同样也有 else
子句
/foo/ {
ACTION1
} else {
ACTION2
}
还有类似 switch.default 的 otherwise
子句
/foo/ {
/foo1/ {
ACTION1
}
/foo2/ {
ACTION2
}
otherwise {
ACTION3
}
}
如果匹配 /foo/
,但不匹配 /foo1/
或 /foo2/
,则将执行 ACTION3
# 行为 - 正则匹配
counter transfers_total by operation, module
/(?P<operation>\S+) (\S+) \[\S+\] (\S+) \(\S*\) \S+ (?P<bytes>\d+)/ {
transfers_total[$operation][$3]++
}
- 匹配组编号变量:
$1...$n
(从 1 开始),使用编号获取匹配内容 - 匹配组别名变量:使用
?P<name>
定义匹配组别名<name>
后面跟随的正则表达式将会赋值到name
变量中,可在下方的代码块中引用- 也可在匹配后的表达式中使用,如下
# 执行到&&时,前面的正则匹配也完成,因此此时已经可以调用其变量
/(?P<x>\d+)/ && $x > 1 {
nonzero_positives++
}
# 行为 - 常见函数
counter lines_total
# 增加计数器
/$/ {
# 记录行数
lines_total++
}
# 自定义事件时间戳
/^(?P<date>\w+\s+\d+\s+\d+:\d+:\d+)/ {
# 解析日期作为事件时间戳;和Go的time.Parse()参数一致
strptime($date, "Jan 02 15:04:05")
# 如果没有`strptime()`调用,默认使用当前系统时间作为事件的时间戳
...
}
# 函数(修饰器)
可提取相同逻辑的操作作为函数
# 定义
使用 def
定义
def syslog {
/(?P<date>\w+\s+\d+\s+\d+:\d+:\d+)/ {
strptime($date, "Jan 2 15:04:05")
next
}
}
next
关键字为跳转到下一个修饰器
# 使用
@syslog {
/some event/ {
variable++
}
}
@
表示该代码块被 syslog
装饰器 “包装”,可理解为 def syslog
中 next
替换为此代码块执行。
# 示例 1
2024/11/08 18:10:49 Broadcasting block 359645 to 0 stratum miners
INFO [11-08|18:10:50.204] New block to mine on cyprus1 at height [76667 187687 359646]
INFO [11-08|18:10:50.204] Difficulty: 232408.262 Mh
INFO [11-08|18:10:50.204] Sealhash: 0x63a326ec8f61b259d1df239ebe69e10c67dd3f3907faa734d2c877df7a2280f8
2024/11/08 18:10:50 Broadcasting block 359646 to 0 stratum miners
INFO [11-08|18:10:51.205] New block to mine on cyprus1 at height [76667 187688 359647]
INFO [11-08|18:10:51.205] Difficulty: 232666.493 Mh
INFO [11-08|18:10:51.205] Sealhash: 0x216415c6b26fc669719777bed521d3b9b5976fbbf974ceac6f5505d6cdece523
目标:提取日志中的 Difficulty
后的数值
实践:
"11-08|18:10:51.205"
在 go 语言中的时间格式为"01-02|15:04:05.000"
- 正则语句为
\[(\d{2}-\d{2}\|\d{2}:\d{2}:\d{2}\.\d{3})\] Difficulty: (\d+\.\d+) Mh
counter difficulty_value
/\[(\d{2}-\d{2}\|\d{2}:\d{2}:\d{2}\.\d{3})\] Difficulty: (\d+\.\d+) Mh/{
#strptime($1, "01-02|15:04:05.000")
difficulty_value=$2
}
由于日志中当前行时间确实年份,无法解析
# 示例 2
+-------------------------------------------------------------------------------------------+
| 2024-11-08T17:09:18 |
| |
| gpu[0]: (1m - 904777 5m - 903315 15m - 907702 30m - 923360 60m - 874557 ) |
| gpu[1]: (1m - 943162 5m - 943162 15m - 947670 30m - 963511 60m - 909408 ) |
| gpu[2]: (1m - 924883 5m - 924883 15m - 931585 30m - 946817 60m - 894876 ) |
| gpu[3]: (1m - 928539 5m - 928539 15m - 933413 30m - 948767 60m - 894968 ) |
| gpu[4]: (1m - 926711 5m - 927442 15m - 932926 30m - 947975 60m - 895760 ) |
| gpu[5]: (1m - 946817 5m - 946086 15m - 950473 30m - 966741 60m - 913612 ) |
| gpu[*]: (1m - 5574891 5m - 5573429 15m - 5603771 30m - 5697173 60m - 5383182 ) |
| |
+-------------------------------------------------------------------------------------------+
counter aleo_hashrate by gpu
/gpu\[(\d)\]: \(1m - (\d+)/{
aleo_hashrate[$1] = $2
}
# 日志
mtail 的日志默认存储在 /tmp/mtail.INFO
tail -f /tmp/mtail.INFO