【夜莺监控】从日志中提取指标的瑞士军刀

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
日志服务 SLS,月写入数据量 50GB 1个月
简介: 【夜莺监控】从日志中提取指标的瑞士军刀

mtail是谷歌开源的一款从应用日志提取 metrics 的工具,它会实时读取应用程序的日志,然后通过自己编写的脚本分析日志,最终生成时间序列的指标

夜莺的Categraf对日志指标的收集也是采用的 mtail,不过做了一些优化,具体优化了什么我们慢慢道来。

现在,我们先从谷歌的mtail开始聊起,再慢慢聊到夜莺的 mtail 插件。

mtail 的安装

前面已经对mtail做了简短的介绍,其实那就是全部。

所以,我们直接从安装开始。

操作如下:

# 下载
$ wget https://github.com/google/mtail/releases/download/v3.0.0-rc51/mtail_3.0.0-rc51_Linux_x86_64.tar.gz
$ tar xf mtail_3.0.0-rc51_Linux_x86_64.tar.gz
$ cp mtail /usr/local/bin
# 查看mtail版本
$ mtail --version
mtail version 3.0.0-rc51 git revision 6fdbf8ec96a63c674c53148eeb9ec96043a2ec9c go version go1.19.4 go arch amd64 go os linux
# mtail后台启动
$ nohup mtail -port 3903 -logtostderr -progs test.mtail -logs test.log &
# 默认端口是3903
$ nohup ./mtail -progs test.mtail -logs test.log &
# 查看是否启动成功
$ ps -ef | grep mtail
# 查看mtail的帮助文档
$ mtail -h

mtail 参数详解

安装完mtail之后,如果对mtail的参数一无所知的话,也就不知道如何下手了,本小节就带大家来了解一下 mtail 有哪些参数。

我们可以通过mtail -h来查看mtail支持的参数列表,下面我对这些参数加一些中文注释,应该能够帮助你了解它们的意思了。

$ mtail -h
mtail version 3.0.0-rc51 git revision 6fdbf8ec96a63c674c53148eeb9ec96043a2ec9c go version go1.19.4 go arch amd64 go os linux
Usage:
  -address string # 绑定HTTP监听器的主机或者IP地址
  -alsologtostderr # 记录标准错误和文件
  -block_profile_rate int # 报告goroutine阻塞事件之前的阻塞时间的纳秒数。0表示关闭。
  -collectd_prefix string # 发送给collectd的指标前缀
  -collectd_socketpath string # collectd socket路径,用于向其写入metrics
  -compile_only # 仅禅师编译mtail脚本程序,不执行
  -disable_fsnotify # 是否禁用文件动态发现机制。为true时,不会监听动态加载发现的新文件,只会监听程序启动时的文件。
  -dump_ast # 解析后dump程序的AST(默认到/tmp/mtail.INFO)
  -dump_ast_types # 在类型检查之后dump带有类型注释的程序的AST(默认到/tmp/mtail.INFO)
  -dump_bytecode # dump程序字节码
  -emit_metric_timestamp # 发出metric的记录时间戳。如果禁用(默认设置),则不会向收集器发送显式时间戳。
  -emit_prog_label # 在导出的变量里面展示prog对应的标签。默认为true
  -expired_metrics_gc_interval duration # metric的垃圾收集器运行间隔(默认为1h0m0s)
  -graphite_host_port string # graphite carbon服务器地址,格式Host:port。用于向graphite carbon服务器写入metrics
  -graphite_prefix string # 发送给graphite指标的metrics前缀
  -http_debugging_endpoint # 是否开启调式接口(/debug/*),默认开启
  -http_info_endpoint # 是否开始info接口(/progz,/varz),默认开启
  -ignore_filename_regex_pattern string # 需要忽略的日志文件名字,支持正则表达式。使用场景:当-logs参数指定的为一个目录时,可以使用ignore_filename_regex_pattern 参数来忽略一部分文件
  -jaeger_endpoint string # 如果设为true,可以将跟踪导出到Jaeger跟踪收集器。使用–jaeger_endpoint标志指定Jaeger端点URL
  -log_backtrace_at value # 当日志记录命中设置的行N时,发出堆栈跟踪
  -log_dir string # mtail程序的日志文件的目录,与logtostderr作用类似,如果同时配置了logtostderr参数,则log_dir参数无效
  -logs value # 监控的日志文件列表,可以使用,分隔多个文件,也可以多次使用-logs参数,也可以指定一个文件目录,支持通配符*,指定文件目录时需要对目录使用单引号。
  -logtostderr # 直接输出标准错误信息,编译问题也直接输出
  -max_recursion_depth int # 以解析的标记来衡量mtail语句的最大长度。过长的mtail表达式可能会导致编译和运行时的性能问题。(默认为100)
  -max_regexp_length int # 一个mtail regexp表达式的最大长度。过长的模式可能会导致编译和运行时的性能问题。(默认为1024)
  -metric_push_interval duration # metric推送时间间隔,单位:秒,默认60秒
  -metric_push_interval_seconds int # 弃用,用--metric_push_interval代替
  -metric_push_write_deadline duration # 在出现错误退出之前等待推送成功的时间。(默认10s)
  -mtailDebug int # 设置解析器debug级别
  -mutex_profile_fraction int # 报告的互斥争夺事件的比例。 0将关闭
  -one_shot # 此参数将编译并运行mtail程序,然后从指定的文件开头开始读取日志(从头开始读取日志,不是实时tail),然后将收集的所有metrics打印到日志中。此参数用于验证mtail程序是否有预期输出,不用于生产环境。
  -one_shot_format string # 与-one_shot一起使用的格式。这只是一个调试标志,不适合生产使用。支持的格式: json, prometheus. (默认为 "json")
  -override_timezone string # 设置时区,如果使用此参数,将在时间戳转换中使用指定的时区来替代UTC
  -poll_interval duration # 设置轮询所有日志文件以获取数据的间隔;必须为正,如果为零将禁用轮询。使用轮询模式,将仅轮询在mtail启动时找到的文件
  -poll_log_interval duration # 设置找到所有匹配的日志文件进行轮询的时间间隔;必须是正数,或者是0来禁用轮询。 在轮询模式下,只有在mtail启动时发现的文件会被轮询。(默认250ms)
  -port string # 监听的http端口,默认3903
  -progs string # mtail脚本程序所在路径
  -stale_log_gc_interval duration # stale的垃圾收集器运行间隔(默认为1h0m0s)
  -statsd_hostport string # statsd地址,格式Host:port。用于向statsd写入metrics
  -statsd_prefix string # 发送给statsd指标的metrics前缀
  -stderrthreshold value # 严重性级别达到阈值以上的日志信息除了写入日志文件以外,还要输出到stderr。各严重性级别对应的数值:INFO—0,WARNING—1,ERROR—2,FATAL—3,默认值为2.
  -syslog_use_current_year # 如果时间戳没有年份,则用当前年替代。(默认为true)
  -trace_sample_period int # 用于设置跟踪的采样频率和发送到收集器的频率。将其设置为100,则100条收集一条追踪。
  -unix_socket string # socket监控地址
  -v value # v日志的日志级别,该设置可能被 vmodule标志给覆盖.默认为0.
  -version # 打印mtail版本
  -vm_logs_runtime_errors # 启用运行时错误的记录到标准日志。 如果设置为false,则只将错误打印到HTTP控制台。(默认为true)
  -vmodule value # 按文件或模块来设置日志级别,如:-vmodule=mapreduce=2,file=1,gfs*=3

配置参数非常多,一般情况下我们使用的也就那几个,如下:

nohup ./mtail -progs test.mtail -logs test.log &

指定 mtail 脚本以及日志目录即可。

mtail 脚本语法


脚本标准的格式如下:

COND {
  ACTION
}

其中COND是一个条件表达式,可以是正则表达式,也可以是 boolean 类型的条件语句,如下:

/foo/ {
  ACTION1
}
variable > 0{
  ACTION2
}
/foo/ && variable > 0{
  ACTION3
}

COND表达式可用的运算符如下:

  1. 关系运算符:< , <= , > , >= , == , != , =~ , !~ , || , && , !
  2. 算术运算符:| , & , ^ , + , - , * , /, << , >> , **

另外,ACTION是具体的操作,如下表示从日志中匹配到 foo 字段,就给相应的指标 foo_total 的值就加 1:

counter foo_total
/foo/ {
   foo_total++
}

对于指标,可以用= , += , ++ , –等运算符进行操作。

mtail的目的是从日志中提取信息并将其传递到监控系统。因此,必须导出指标变量并命名,命名可以使用counterhistogramgauge指标类型,并且命名的变量必须在COND脚本之前。

  1. Counter(计数器):用于记录单调递增的值,例如请求数、错误数等。
  2. Gauge(仪表):用于记录可增可减的值,例如 CPU 使用率、内存使用量等。
  3. Histogram(直方图):用于记录数据的分布情况,例如请求延迟、响应大小等。

我们知道,拿 Prometheus 来说,除了上面的三种指标类型之外还有一个Summary的指标类型,为什么 mtail 没有呢?

因为在 Prometheus 中,summary 指标类型用于记录数据的分布情况,并计算出更多的统计信息,例如平均值、中位数、标准差等。但是,由于 mtail 是从日志文件中提取指标,而不是直接从应用程序中提取指标,因此没有必要使用 summary 指标类型。

高阶用法

变量定义

对于在一个脚本中需要重复使用的表达式,可以将其定义为一个变量,后续可以直接使用变量。

counter duplicate_lease
const IP /\d+(\.\d+){3}/
const MATCH_IP /(?P<ip>/ + IP + /)/
/uid lease / + MATCH_IP + / for client .* is duplicate on / {
   duplicate_lease++
}

这是开发中常用的手段。

解析时间戳

mtail 会为每一个日志事件都赋予一个时间戳,如果日志里没有时间戳,mtail 会为本次日志事件赋予一个当前的日志时间。

除此之外,如果日志里的时间戳不是标准时间或者其他情况,可以使用 strptime 对其进行解析,如下:

/^/ +
/(?P<date>\d{4}\/\d{2}\/\d{2} \d{2}:\d{2}:\d{2}) / +
/.*/ +
/$/ {
    strptime($date, "2006/01/02 15:04:05")
}

条件判断

/pattern/ { action }是 mtail 程序中正常的条件控制流结构。

如果模式匹配,那么该块中的动作就会被执行。如果模式不匹配,则跳过该块。

else关键字允许程序在模式不匹配的情况下执行动作。

/pattern/ {
  action
} else {
  alternative
}

除此之外,还可以使用 otherwise 来处理没有匹配到的规则,如下:

{
/pattern1/ { _action1_ }
/pattern2/ { _action2_ }
otherwise { _action3_ }
}

这种语法类似于switch case default语法。

精准匹配

上面的/pattern/ { _action_ }形式隐含地匹配了当前的输入日志行。

如果想与另一个字符串变量匹配,可以使用=~操作符,或者用!~来否定匹配,像这样:

$1 =~ /GET/ {
    ...
  }

解析非数字类型的数字字段

有时候遇到的日志里输出的数字是字符串,而非数字,mtail 可以对其进行解析,如下:

counter total
/^[a-z]+ ((?P<response_size>\d+)|-)$/ {
  $1 != "-" {
    total = $response_size
  }
}

解析带有额外字符的数字

一些日志包含除了包含数字,还包含分隔符,我们可以用 subst 函数删除它们:

/sent (?P<sent>[\d,]+) bytes  received (?P<received>[\d,]+) bytes/ {
    # Sum total bytes across all sessions for this process
    bytes_total["sent"] += int(subst(",", "", $sent))
    bytes_total["received"] += int(subst(",", "", $received))
}

过滤操作

如果你想过滤一些不必要的日志被mtail采集,你可以使用stop,如下:

getfilename() !~ /apache.access.?log/ {
  stop
}

重写操作

一些日志,如网络服务器日志,描述了一些常见的元素,其中有独特的标识符,如果不加处理,会导致大量的度量衡键,而没有有用的计数。要重写这些捕获组,可以使用 subst(),将模式作为第一个参数:

hidden text route
counter http_requests_total by method, route
/(?P<method\S+) (?P<url>\S+)/ {
  route = subst(/\/d+/, "/:num", $url)
  http_requests_total[method][route]++
}

这里我们把$url/后面的任何数字部分替换为字面字符串/:num,所以我们最终只计算 URL 路由的静态部分。

mtail 实操

说一千,道一万,不如真正来一遍。

当然,我这里也不会把上面说的都来一次。

为了方便阐述,我把本次操作的脚本都放到~/Desktop/mtail目录中。

单日志采集

# 创建prog1,里面用于保存日志处理的规则脚本
$ mkdir prog1
# 在prog1里创建prog1.mtail文件并写入以下内容
$ cat prog1.mtail
counter foo_count
/foo/{
  foo_count++
}
# 创建log1目录
$ mkdir log1
# 在log1中创建a.log文件
¥ touch a.log
# 启动mtail
$ mtail -progs ~/Desktop/mtail/prog1 -logs ~/Desktop/mtail/log1/a.log
# 向a.log中写入foo
$ echo "foo" > ~/Desktop/mtail/log1/a.log
# 查看指标明细
$ curl 127.0.0.1:3903/metrics
# HELP foo_count defined at prog1.mtail:1:9-17
# TYPE foo_count counter
foo_count{prog="prog1.mtail"} 1 # 可以看到foo_count指标数为1了

多日志采集

如果多日志在同一个文件夹里,这时候采集的指标就可能混淆。

# 在log1目录中创建b.log
$ touch b.log
# 然后为b.log重新创建一个指标脚本
$ cat prog1/prog2.mtail
counter bar_count
/bar/{
  bar_count++
}
# 启动mtail
$ mtail -progs ~/Desktop/mtail/prog1 -logs ~/Desktop/mtail/log1/a.log -logs ~/Desktop/mtail/log1/b.log
# 向b.log写入日志
$ echo "bar" >>  ~/Desktop/mtail/log1/b.log
# 查看指标
$ curl 127.0.0.1:3903/metrics
# HELP bar_count defined at prog2.mtail:1:9-17
# TYPE bar_count counter
bar_count{prog="prog2.mtail"} 2
# HELP foo_count defined at prog1.mtail:1:9-17
# TYPE foo_count counter
foo_count{prog="prog1.mtail"} 0

可以看到能正常收集指标,但是如果我们向 a.log 也写入 bar 日志,指标会增加吗?

# 向a.log写入日志
$ echo "bar" >>  ~/Desktop/mtail/log1/a.log
# 查看指标
$ curl 127.0.0.1:3903/metrics
# HELP bar_count defined at prog2.mtail:1:9-17
# TYPE bar_count counter
bar_count{prog="prog2.mtail"} 3
# HELP foo_count defined at prog1.mtail:1:9-17
# TYPE foo_count counter
foo_count{prog="prog1.mtail"} 0

可以看到指标依然会增加。其实我们的期望是prog1.mtail只收集a.log的日志指标,prog2.mtail只收集b.log的指标,不要相互影响。

如果要解决这个问题,就需要启动不同的mtail才行。换句话说有多少日志文件,如果想分开收集,则要启动多少个mtail,可以想想这是一个非常恐怖的事情。

鉴于此,Categraf 对 mtail 插件做了一些优化,优化后的 mtail 插件可以做到一个 Categraf 进程同时解析多个服务的日志,改造后的示例图如下:

image.png

Categraf 操作

在前面的夜莺监控系列中,对 Categraf 基本都有一个印象。默认情况下它的配置都在 conf 目录下,其中插件都在以 input 开头的文件夹里。

我们进入input.mtail文件夹,编辑mtail.toml并增加如下配置:

[[instances]]
progs = "/home/jokerbai/Desktop/categraf-v0.2.38-linux-amd64/conf/input.mtail/prog1"
logs = ["/home/jokerbai/Desktop/categraf-v0.2.38-linux-amd64/conf/input.mtail/log1/a.log"]
# override_timezone = "Asia/Shanghai"
# emit_metric_timestamp = "true" #string type
[[instances]]
progs = "/home/jokerbai/Desktop/categraf-v0.2.38-linux-amd64/conf/input.mtail/prog2"
logs = ["/home/jokerbai/Desktop/categraf-v0.2.38-linux-amd64/conf/input.mtail/log1/b.log"]
# override_timezone = "Asia/Shanghai"
# emit_metric_timestamp = "true" # string type

然后添加需要的目录以及脚本:

# 创建文件夹
$ mkdir {prog1,prog2,log1}
# 增加规则文件
$ cat prog1/a.mtail
counter foo_count
/foo/ {
  foo_count++
}
$ cat prog2/b.mtail
counter bar_count
/bar/ {
  bar_count++
}
# 增加日志文件
$ touch {log1/a.log,log1/b.log}

启动 categraf:

# 使用测试模式启动
$ ./categraf -test -inputs mtail

然后往a.log写入foo日志。

echo "foo" >> log1/a.log

然后看到指标增加了:

再往b.log写入bar日志。

echo "bar" >> log1/b.log

bar_count的指标也相应增加了。640.png

那如果我们向a.log增加bar的日志,bar_count会增加么?我们来测试一下:

echo "bar" >> log1/a.log

通过观察bar_count指标不会增加。

Categraf 就完美解决了不同日志指标错乱的问题。

除了正常的处理指标,如果想给不同的instance指定label,也是可以的,如下:

[[instances]]
progs = "/home/jokerbai/Desktop/categraf-v0.2.38-linux-amd64/conf/input.mtail/prog1"
logs = ["/home/jokerbai/Desktop/categraf-v0.2.38-linux-amd64/conf/input.mtail/log1/a.log"]
labels = {"app"= "foo"}
# override_timezone = "Asia/Shanghai"
# emit_metric_timestamp = "true" #string type
[[instances]]
progs = "/home/jokerbai/Desktop/categraf-v0.2.38-linux-amd64/conf/input.mtail/prog2"
logs = ["/home/jokerbai/Desktop/categraf-v0.2.38-linux-amd64/conf/input.mtail/log1/b.log"]
labels = {"app"= "bar"}
# override_timezone = "Asia/Shanghai"
# emit_metric_timestamp = "true" # string type

重启 Categraf 就可以看到指标多了一个 label。

其他的脚本语法和原生的 mtail 一致,这里不再追溯了。

总结

相比于谷歌的mtailcategrafmtail做了一些优化,可以更好的处理多日志的问题。而且 categraf 本身集成了很多插件,都可以统一使用它实现。

另外,还是相同的问题,假设插件开启比较多,categraf 的具体性能如何以及会不会影响主机的整体性能,这还有待研究。


相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
24天前
|
运维 监控 Cloud Native
一行代码都不改,Golang 应用链路指标日志全知道
本文将通过阿里云开源的 Golang Agent,帮助用户实现“一行代码都不改”就能获取到应用产生的各种观测数据,同时提升运维团队和研发团队的幸福感。
|
2月前
|
存储 监控 固态存储
如何监控和优化 WAL 日志文件的存储空间使用?
如何监控和优化 WAL 日志文件的存储空间使用?
|
2月前
|
监控 网络协议 CDN
阿里云国际监控查询流量、用量查询流量与日志统计流量有差异?
阿里云国际监控查询流量、用量查询流量与日志统计流量有差异?
|
3月前
|
运维 Kubernetes 监控
Loki+Promtail+Grafana监控K8s日志
综上,Loki+Promtail+Grafana 监控组合对于在 K8s 环境中优化日志管理至关重要,它不仅提供了强大且易于扩展的日志收集与汇总工具,还有可视化这些日志的能力。通过有效地使用这套工具,可以显著地提高对应用的运维监控能力和故障诊断效率。
408 0
|
4月前
|
SQL 数据库 Java
Hibernate 日志记录竟藏着这些秘密?快来一探究竟,解锁调试与监控最佳实践
【8月更文挑战第31天】在软件开发中,日志记录对调试和监控至关重要。使用持久化框架 Hibernate 时,合理配置日志可帮助理解其内部机制并优化性能。首先,需选择合适的日志框架,如 Log4j 或 Logback,并配置日志级别;理解 Hibernate 的多级日志,如 DEBUG 和 ERROR,以适应不同开发阶段需求;利用 Hibernate 统计功能监测数据库交互情况;记录自定义日志以跟踪业务逻辑;定期审查和清理日志避免占用过多磁盘空间。综上,有效日志记录能显著提升 Hibernate 应用的性能和稳定性。
55 0
|
4月前
|
开发者 前端开发 编解码
Vaadin解锁移动适配新境界:一招制胜,让你的应用征服所有屏幕!
【8月更文挑战第31天】在移动互联网时代,跨平台应用开发备受青睐。作为一款基于Java的Web应用框架,Vaadin凭借其组件化设计和强大的服务器端渲染能力,助力开发者轻松构建多设备适应的Web应用。本文探讨Vaadin与移动设备的适配策略,包括响应式布局、CSS媒体查询、TouchKit插件及服务器端优化,帮助开发者打造美观且实用的移动端体验。通过这些工具和策略的应用,可有效应对屏幕尺寸、分辨率及操作系统的多样性挑战,满足广大移动用户的使用需求。
71 0
|
4月前
|
存储 运维 监控
Entity Framework Core 实现审计日志记录超棒!多种方法助你跟踪数据变化、监控操作,超实用!
【8月更文挑战第31天】在软件开发中,审计日志记录对于跟踪数据变化、监控用户操作及故障排查至关重要。Entity Framework Core (EF Core) 作为强大的对象关系映射框架,提供了多种实现审计日志记录的方法。例如,可以使用 EF Core 的拦截器在数据库操作前后执行自定义逻辑,记录操作类型、时间和执行用户等信息。此外,也可通过在实体类中添加审计属性(如 `CreatedBy`、`CreatedDate` 等),并在保存实体时更新这些属性来记录审计信息。这两种方法都能有效帮助我们追踪数据变更并满足合规性和安全性需求。
116 0
|
4月前
|
存储 JSON 监控
FastAPI日志之谜:如何揭开Web应用监控与调试的面纱?
【8月更文挑战第31天】在现代Web开发中,日志记录对于监控应用状态、诊断问题和了解用户行为至关重要。FastAPI框架提供了强大的日志功能,使开发者能轻松集成日志记录。本文将详细介绍如何在FastAPI中设置和利用日志,包括基础配置、请求响应日志、错误处理和结构化日志等内容,帮助提升应用的可维护性和性能。
194 0
|
4月前
|
消息中间件 Prometheus 监控
Producer的监控与日志记录最佳实践
【8月更文第29天】在分布式系统中,消息队列作为关键组件之一,其稳定性和性能至关重要。生产者(Producer)负责生成并发送消息到消息队列中,因此确保生产者的健康运行是非常重要的。本文将探讨如何为生产者设置监控和日志记录,以跟踪其健康状况和性能指标。
82 0
|
4月前
|
Prometheus 监控 Cloud Native
Web服务器的日志分析与监控
【8月更文第28天】Web服务器日志提供了关于服务器活动的重要信息,包括访问记录、错误报告以及性能数据。有效地分析这些日志可以帮助我们了解用户行为、诊断问题、优化网站性能,并确保服务的高可用性。本文将介绍如何使用日志分析和实时监控工具来监测Web服务器的状态和性能指标,并提供具体的代码示例。
539 0