mtail 是用于从应用程序日志中提取指标以导出到时间序列数据库或时间序列计算器以进行警报和仪表板显示的工具。简单来说,就是实时读取应用程序的日志,并且通过自己编写的脚本实时分析,最终生成时间序列指标的工具。
官网地址:点击访问
下载地址:点击访问
CSDN下载地址:点击访问
参数 | 描述 |
---|---|
-address | 绑定HTTP监听器的主机或IP地址 |
-alsologtostderr | 记录标准错误和文件 |
-block_profile_rate | 报告goroutine阻塞事件之前的纳秒时间 |
-collectd_prefix | 发送给collectd的指标的metrics前缀 |
-collectd_socketpath | collectd unixsock路径,用于向其写入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 | metric的垃圾收集器运行间隔(默认为1h0m0s) |
-graphite_host_port | graphite carbon服务器地址,格式Host:port。用于向graphite carbon服务器写入metrics |
-graphite_prefix | 发送给graphite指标的metrics前缀 |
-ignore_filename_regex_pattern | 需要忽略的日志文件名字,支持正则表达式。使用场景:当-logs参数指定的为一个目录时,可以使用ignore_filename_regex_pattern 参数来忽略一部分文件 |
-jaeger_endpoint | 如果设为true,可以将跟踪导出到Jaeger跟踪收集器。使用–jaeger_endpoint标志指定Jaeger端点URL |
-log_backtrace_at | 当日志记录命中设置的行N时,发出堆栈跟踪 |
-log_dir | mtail程序的日志文件的目录,与logtostderr作用类似,如果同时配置了logtostderr参数,则log_dir参数无效 |
-logs | 监控的日志文件列表,可以使用,分隔多个文件,也可以多次使用-logs参数,也可以指定一个文件目录,支持通配符*,指定文件目录时需要对目录使用单引号。如: -logs a.log,b.log,c.log -logs a.log -logs b.log -logs c.log -logs ‘/export/logs/*.log’ |
-logtostderr | 直接输出标准错误信息,编译问题也直接输出 |
-metric_push_interval_seconds | metric推送时间间隔,单位:秒,默认60秒 |
-metric_push_write_deadline | 在出现错误退出之前等待推送成功的时间。(默认10s) |
-mtailDebug | 设置解析器debug级别 |
-mutex_profile_fraction | 报告的互斥锁争用事件的分数。0关闭。(此参数为直译,不太理解啥意思) |
-one_shot | 此参数将编译并运行mtail程序,然后从指定的文件开头开始读取日志(从头开始读取日志,不是实时tail),然后将收集的所有metrics打印到日志中。此参数用于验证mtail程序是否有预期输出,不用于生产环境。 |
-override_timezone | 设置时区,如果使用此参数,将在时间戳转换中使用指定的时区来替代UTC |
-poll_interval | 设置轮询所有日志文件以获取数据的间隔;必须为正,如果为零将禁用轮询。使用轮询模式,将仅轮询在mtail启动时找到的文件 |
-port | 监听的http端口,默认3903 |
-progs | mtail脚本程序所在路径 |
-stale_log_gc_interval | stale的垃圾收集器运行间隔(默认为1h0m0s) |
-statsd_hostport | statsd地址,格式Host:port。用于向statsd写入metrics |
-statsd_prefix | 发送给statsd指标的metrics前缀 |
-stderrthreshold | 严重性级别达到阈值以上的日志信息除了写入日志文件以外,还要输出到stderr。各严重性级别对应的数值:INFO—0,WARNING—1,ERROR—2,FATAL—3,默认值为2. |
-syslog_use_current_year | 如果时间戳没有年份,则用当前年替代。(默认为true) |
-trace_sample_period | 用于设置跟踪的采样频率和发送到收集器的频率。将其设置为100,则100条收集一条追踪。 |
-v | v日志的日志级别,该设置可能被 vmodule标志给覆盖.默认为0. |
-version | 打印mtail版本 |
-vmodule | 按文件或模块来设置日志级别,如:-vmodule=mapreduce=2,file=1,gfs*=3 |
安装包为可执行二进制文件,官网目前(2020-04-19)最新版本为mtail_v3.0.0-rc35_linux_amd64,下载地址见本文概述。将其重命名为mtail,移动至/usr/local/sbin下即可,验证是否可用:
mtail -version
安装成功输出如下:
mtail version v3.0.0-rc34 git revision 7b4270cb6e8b6ed1f2febe48e370cb68d38681f9 go version go1.12.15 go arch amd64 go os linux
mtail后台启动:
nohup mtail -port 3903 -logtostderr -progs /root/chuangmi.mtail -logs /home/ubuntu/video_server_for_ai/media_server.log &
查看是否启动成功:
ps -ef | grep mtail
标准格式为:
COND {
ACTION
}
其中COND
是一个条件表达式。它可以是正则表达式,也可以boolean类型的条件语句。如下:
/foo/ {
ACTION1
}
variable > 0 {
ACTION2
}
/foo/ && variable > 0 {
ACTION3
}
COND
表达式可用的运算符如下:
< , <= , > , >= , == , != , =~ , !~ , || , && , !
| , & , ^ , + , - , * , /, << , >> , **
导出的指标变量可用的运算符如下:
= , += , ++ , –
mtail
的目的是从日志中提取信息并将其传递到监控系统。因此,必须导出指标变量并命名,命名可以使用counter、、gauge等指标类型,并且命名的变量必须在COND
脚本之前。
如,导出一个counter类型的指标lines_total:统计日志行数,脚本内容如下:
# simple line counter
counter lines_total
/$/ {
lines_total++
}
mtail中的counter、gauge、histogram三种类型与prometheus类型中描述的作用一致。
counter 类型的数据是单调递增的指标,即只增不减。如,你可以使用 counter 类型的指标来表示服务的请求数、成功任务数、失败的任务数等。
gauge类型的数据是指可以任意变化的指标,可增可减。如,可以提取正则匹配到的数据,直接赋值给指标变量返回,或者计算后返回。
histogram(直方图)将数据分段统计,引用prometheus中对histogram的描述:
在大多数情况下人们都倾向于使用某些量化指标的平均值,例如 CPU 的平均使用率、页面的平均响应时间。这种方式的问题很明显,以系统 API 调用的平均响应时间为例:如果大多数 API 请求都维持在 100ms 的响应时间范围内,而个别请求的响应时间需要 5s,那么就会导致某些 WEB 页面的响应时间落到中位数的情况,而这种现象被称为长尾问题。
为了区分是平均的慢还是长尾的慢,最简单的方式就是按照请求延迟的范围进行分组。例如,统计延迟在 0~10ms 之间的请求数有多少而 10~20ms 之间的请求数又有多少。通过这种方式可以快速分析系统慢的原因。Histogram 和 Summary 都是为了能够解决这样问题的存在,通过 Histogram 和 Summary 类型的监控指标,我们可以快速了解监控样本的分布情况。
Histogram 在一段时间范围内对数据进行采样(通常是请求持续时间或响应大小等),并将其计入可配置的存储桶(bucket)中,后续可通过指定区间筛选样本,也可以统计样本总数,最后一般将数据展示为直方图。
mtail
支持将直方图作为第一类度量标准,使用桶(bucket)范围列表创建histogram,如下:
histogram foo buckets 1, 2, 4, 8
以上脚本创建了一个新的直方图foo,其桶的范围为[0-1),[1-2),[2-4),[4-8),[8, +∞)。
注意:0-1和8-无穷大是自动创建的。
如下一个例子,展示使用Histogram 统计服务器延迟情况:
GET /foo/bar.html latency=1s httpcode=200
GET /foo/bar.html latency=2s httpcode=200
GET /foo/bar.html latency=1s httpcode=200
GET /foo/baz.html latency=0s httpcode=200
histogram webserver_latency buckets 0, 1, 2, 4, 8
/latency=(?P<latency>\d+)/ {
webserver_latency = $latency
}
webserver_latency_by_code_bucket{httpcode="200",prog="software_errors.mtail",le="1"} 1
webserver_latency_by_code_bucket{httpcode="200",prog="software_errors.mtail",le="2"} 1
webserver_latency_by_code_bucket{httpcode="200",prog="software_errors.mtail",le="4"} 1
webserver_latency_by_code_bucket{httpcode="200",prog="software_errors.mtail",le="8"} 1
webserver_latency_by_code_bucket{httpcode="200",prog="software_errors.mtail",le="+Inf"} 1
webserver_latency_by_code_sum{httpcode="200",prog="software_errors.mtail"} 1
webserver_latency_by_code_count{httpcode="200",prog="software_errors.mtail"} 2
像sql中给字段起别名一样,此处放在定义变量的地方,如下:
counter lines_total as "lines-total"
如匹配ip等,可能会有多个地方调用的,可以配置成常量,如下:
const IP /\d+(\.\d+){3}/
const MATCH_IP /(?P<ip>/ + IP + /)/
...
# Duplicate lease
/uid lease / + MATCH_IP + / for client .* is duplicate on / {
duplicate_lease++
}
与标准正则一样,小括号()内的内容可以使用$1,$2等方式获取到,mtail
脚本中此处MATCH_IP 中使用的?P<ip>则用来代替$1,$2等,可以直接使用$ip来获取匹配到的内容.
counter request_total
/(?P<date>\d{4}\/\d{2}\/\d{2} \d{2}:\d{2}:\d{2}\.\d{3}).*/ {
strptime($date, "2006-01-02 15:04:05")
request_total++
}
可复用常量解决了表达式中重复使用子表达式的问题,而通用时间戳解析则是为了解决,多个变量之中重复解析设置时间戳的问题。
def syslog {
/(?P<date>\w+\s+\d+\s+\d+:\d+:\d+)/ {
strptime($date, "Jan 2 15:04:05")
next
}
}
使用def
关键字定义名称,使用大括号块定义内容,使用next
关键字,跳转到导出变量的代码块执行。
@syslog {
/some event/ {
variable++
}
}
@syslog表示该块首先由syslog装饰器执行,先提取日志的时间戳,然后使用next
跳转到
“/some event/”块内执行。
例:
def syslog {
/(?P<date>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}).*/ {
strptime($date, "2006-01-02 15:04:05")
next
}
}
gauge sumVal
@syslog {
/-------(?P<val>\d)+--------/ {
sumVal+=$val
}
}
通常情况下,当条件表达式不匹配时,会直接跳过。而通过else
关键字可实现表达式不匹配时执行其他的动作。而通过otherwise
关键字则可以实现像java中的switch一样的多条件判断的功能,如下:
#如果foo不匹配,则执行ACTION2
/foo/ {
ACTION1
} else {
ACTION2
}
/foo/ {
#当匹配foo时,才会进行foo1、foo2、otherwise的比配
#当匹配foo1时,执行ACTION1,匹配foo2时执行ACTION2,否则执行ACTION3
/foo1/ {
ACTION1
}
/foo2/ {
ACTION2
}
otherwise {
ACTION3
}
}
临时变量就是只在mtail脚本程序中使用,不导出为metrics指标的变量。通过在gauge
定义时前面加上hidden
关键字来定义。临时变量使用完后,需要使用del
关键字来删除临时变量控制内存。
hidden gauge connection_time by pid
gauge connection_time_total by pid
...
# 记录连接开始时间
/connect from \S+ \(\d+\.\d+\.\d+\.\d+\)/ {
# 此处记录开始时间,需要先使用strptime或者settime函数设置时间戳,timestamp才能正常执行
connection_time[$pid] = timestamp()
}
...
# 记录连接关闭时间
/sent (?P<sent>\d+) bytes received (?P<received>\d+) bytes total size \d+/ {
# 记录总时间
connection_time_total[$pid] += timestamp() - connection_time[$pid]
# 删除中间变量,控制内存
del connection_time[$pid]
}
此示例中,连接时间戳记录在临时变量connection_time
中,并且connection_time
通过pid来区分不同的链接.在链接结束时,通过当前时间戳-开始时间戳来计算增量,并且删除临时变量connection_time
。
函数 | 描述 |
---|---|
timestamp() | 获取时间戳。 注:需要使用strptime或者settime函数设置时间戳之后才能使用timestamp函数 |
strptime($date, “2006-01-02 15:04:05”) | 第一个参数为日志中解析的时间格式,第二个参数为格式模板,此函数作用为使用第二个参数的格式来解析日志中的时间戳,并设置当前时间戳,将变量导出至上游收集器时使用此时间戳。 注:如果第二个参数格式不对,则编译抛出异常;如果日志解析出的时间格式不匹配,则抛出运行时异常。第二个参数必须使用这个特定的时间:2006-01-02 15:04:05。若需要其他格式,可参考Go的时间.parse()格式字符串中的const 部分下的ANSIC等格式。 |
len(str) | 获取字符串长度 |
getfilename() | 获取文件名 |
tolower(x) | 字符串转小写 |
int(x) | 将x转换为整数,如果x的类型支持转为整型,则转为整型;如果x的类型不支持转为整型,则触发编译错误;如果x的值不支持转为整型,则触发运行时错误。 |
float(x) | 将x转换为浮点数,规则同int(x) |
string(x) | 将x转为字符串 |
strtol(x, y) | 使用base将字符串x转为整数y。用于转换日志消息中的八进制、十六进制值 |
settime(x) | x为整型,用于设置当前时间戳,将变量导出至上游收集器时使用此时间戳 |
此场景针对监听目录,或者多个文件的情况,需要使用到内置函数getfilename(),脚本如下:
counter request_total by filename
/-------(?P<val>\d)+--------/ {
request_total[getfilename()]+=$val
}
目前网上关于mtail的文档,除了github官网之外,几乎没有其他的文档,而网上的中文文档几乎都是简单的介绍几句。而我最近刚好在搞mtail相关的,就写一些经验总结,文中例子有官网的demo,也有自己写的demo。如有错漏,望留言指正。