简介
ngxtop 类似于 top,用于短时间的实时监测 nginx server 的运行状态,做 trouble shooting。
ngxin 原理
ngxtop 将 nginx 的 log 逐行读出,每一行逐个字段解析,并将结果插入 sqlite 数据库。
读完 log 文件后,使用 sql 语句查询数据,得到统计结果。
主要功能
分为 follow 和 no-follow 两种模式。
follow 只分析脚本执行时刻以后到来的 nginx log,进程等在那里不退出,实时统计。
no-follow 值分析文件里有的 log,对脚本执行后到来的 log 不统计。
常用的功能:
1. 统计访问次数最多的请求
ngxtop -l access_15.log --no-follow
2. 自定义排序的字段:
按照 avg_bytes_sent 排序:
ngxtop -l access_14.log --no-follow -o avg_bytes_sent
3. 列出前 n 个,默认是10个:
ngxtop -l access_14.log --no-follow -n 15
4. 自定义过滤条件
只统计成功的请求
ngxtop -l access_14.log --no-follow -i 'status==200'
只统计出错的请求
ngxtop -l access_14.log --no-follow -i 'status==200'
搜索客户端为 iPhone 的 log
ngxtop -l access_14.log --no-follow -i 'http_user_agent.find('iPhone')'
条件里是可以嵌 python 语句的,这些语句被 eval() 动态翻译并执行。需要注意的是这些语句如果抛出异常会导致 ngxtop 异常退出。
5. 分组过滤
ngxtop 的处理原理是将所有的 log 解析后存入 sqlite,然后使用 sql 查询想要的结果。
所以理论上 sql 能支持的查询,ngxtop 都能支持。
-g 相当于 group by, -a 相当于 having。
ngxtop 已经默认提供了 avg_bytes_sent, 2xx, 3xx, 4xx, 5xx 这些聚合字段,一般这些字段已经足够了。
如果业务复杂了,想做出更多的查询,可以利用这两个参数自己组合条件。
查看访问量最大的用户ip
ngxtop -g remote_addr
安装
pip install ngxtop
安装后的执行脚本位于 /usr/local/bin/ngxtop
本身是一个 Python脚本,只是一个启动的入口:
from ngxtop.ngxtop import main
主要逻辑都在 ngxtop 库里,文件位于:
/usr/local/lib/python2.7/site-packages/ngxtop
遇到的问题
1. python2.7 缺乏 _sqlite3 库。
在开发机上运行 ngxtop 会报 ImportError: No module named _sqlite3
原因是没有 python2.7 对应的 _sqlite3.so
对于开发机31上的 python2.6 则有对应的库:
/usr/lib64/python2.6/lib-dynload/_sqlite3.so
解决思路是编译 python 2.7,把这个 _sqlite3.so 拷过去
进入开发机 /data/install/Python-2.7.6/
./configure
make
编译完成后找到编译好的 _sqlite3.so
find . -name _sqlite3.so
显示文件位置 ./build/lib.linux-x86_64-2.7/_sqlite3.so,这就是我们要的文件。
将 _sqlite3.so 拷贝到 /usr/lib64/python2.7/lib-dynload/ 目录下:
sudo cp /data/install/Python-2.7.6/build/lib.linux-x86_64-2.7/_sqlite3.so ./
执行 python,进入交互模式,输入:
import sqlite3
没有提示异常,说明成功。
2. 运行 ngxtop 没有统计值
在虚机上测试 ngxtop 是没有问题的,对比虚机的 ngxlog 的格式,发现开发机的 ngxlog 多了一些属性。
将开发机的 ngxlog tail 出1行存为一 t.log,只保留到 $user_agent,后面多出的属性删掉。
用 t.log 测试 ngxtop,可以显示出统计值,测试通过。
通过调试发现,如果字段增多,ngxtop 的匹配是有问题的,匹配的正则式 pattern 如下:
(?P<remote_addr>.*) - (?P<remote_user>.*) \[(?P<time_local>.*)\] "(?P<request>.*)" (?P<status>.*) (?P<body_bytes_sent>.*) "(?P<http_referer>.*)" "(?P<http_user_agent>.*)"
要匹配的 ngxlog 如下:
10.18.110.79 - - [31/Mar/2016:14:19:00 +0800] "GET /favicon.ico HTTP/1.1" 200 1406 "-" "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36 SE 2.X MetaSr 1.0" "-" "yangchao-g.jifen.360.cn" "80" 0.000
匹配的结果:
{
'remote_addr': '10.18.110.79',
'remote_user': '-',
'time_local': '31/Mar/2016:14:19:00 +0800',
'request': 'GET /favicon.ico HTTP/1.1" 200 1406 "-',
'status': '"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36 SE 2.X MetaSr 1.0"',
'body_bytes_sent': '"-"',
'http_referer': 'yangchao-g.jifen.360.cn',
'http_user_agent': '80',
}
匹配过程如下:
{remote_add:10.18.110.79} - {remote_user:-} [{time_local:31/Mar/2016:14:19:00 +0800}] "{request:GET /favicon.ico HTTP/1.1" 200 1406 "-}" "{status:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36 SE 2.X MetaSr 1.0}" "{body_bytes_sent:-}" "{http_referer:yangchao-g.jifen.360.cn}" "{http_user_agent:80}" 0.000
很明显,request 多匹配的内容,应该加入非贪婪的标记,让匹配以最小的方式进行。
匹配的正则式是在 config_parser.py 中的 build_pattern() 函数中生成的。
关键的一句:
pattern = re.sub(REGEX_LOG_FORMAT_VARIABLE, '(?P<\\1>.*)', pattern)
这里的匹配用的是 .*,是贪婪匹配,我们要做的是用非贪婪模式匹配:
pattern = re.sub(REGEX_LOG_FORMAT_VARIABLE, '(?P<\\1>.*
?)', pattern)
再测试,匹配结果如下:
{
'remote_addr': '10.18.110.79',
'remote_user': '-',
'time_local': '31/Mar/2016:14:19:00 +0800',
'request': 'GET /favicon.ico HTTP/1.1',
'status': '200',
'body_bytes_sent': '1406',
'http_referer': '-',
'http_user_agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.122 Safari/537.36 SE 2.X MetaSr 1.0',
}
已经是正确的结果了。
去掉所有的 pprint 调试语句,重新统计ngxlog,统计的结果如下:
~$ ngxtop -l ngxlog.log --no-follow
running for 0 seconds, 5 records processed: 12101.28 req/sec
Summary:
| count | avg_bytes_sent | 2xx | 3xx | 4xx | 5xx |
|---------+------------------+-------+-------+-------+-------|
| 5 | 4901.800 | 5 | 0 | 0 | 0 |
Detailed:
| request_path | count | avg_bytes_sent | 2xx | 3xx | 4xx | 5xx |
|------------------------------------+---------+------------------+-------+-------+-------+-------|
| / | 1 | 19469.000 | 1 | 0 | 0 | 0 |
| /favicon.ico | 1 | 1406.000 | 1 | 0 | 0 | 0 |
| /theme/index/lottery/css/style.css | 1 | 1883.000 | 1 | 0 | 0 | 0 |
| /theme/v3/css/conf/360_head.css | 1 | 763.000 | 1 | 0 | 0 | 0 |
| /theme/v3/css/conf/mall.css | 1 | 988.000 | 1 | 0 | 0 | 0 |