65 lines
2.1 KiB
Python
65 lines
2.1 KiB
Python
#!/usr/bin/env python3
|
|||
|
|
"""nginx 访问日志慢请求/大响应分析(只读)
|
||
|
|
用法: python3 nginx-slow.py <logfile> [host子串] [最近N行] [起始时间标记]
|
||
|
|
"""
|
||
|
|
import re
|
||
|
|
import sys
|
||
|
|
|
||
|
|
log = sys.argv[1]
|
||
|
|
host = sys.argv[2] if len(sys.argv) > 2 else ''
|
||
|
|
tail_lines = int(sys.argv[3]) if len(sys.argv) > 3 else 20000
|
||
|
|
since = sys.argv[4] if len(sys.argv) > 4 else ''
|
||
|
|
|
||
|
|
LINE = re.compile(
|
||
|
|
r'^(?P<ip>\S+) \S+ \S+ \[(?P<time>[^\]]+)\] "(?P<req>[^"]*)" (?P<status>\d{3}) (?P<bytes>\d+)'
|
||
|
|
r'(?: "[^"]*" "[^"]*")?(?P<rest>.*)$'
|
||
|
|
)
|
||
|
|
|
||
|
|
rows = []
|
||
|
|
with open(log, 'r', errors='replace') as fh:
|
||
|
|
lines = fh.readlines()[-tail_lines:]
|
||
|
|
|
||
|
|
for line in lines:
|
||
|
|
if host and host not in line:
|
||
|
|
continue
|
||
|
|
if since and since not in line:
|
||
|
|
continue
|
||
|
|
m = LINE.match(line)
|
||
|
|
if not m:
|
||
|
|
continue
|
||
|
|
times = re.findall(r'(\d+\.\d+)', m.group('rest'))
|
||
|
|
rt = float(times[-1]) if times else None
|
||
|
|
rows.append(
|
||
|
|
{
|
||
|
|
'ip': m.group('ip'),
|
||
|
|
'time': m.group('time'),
|
||
|
|
'req': m.group('req'),
|
||
|
|
'status': int(m.group('status')),
|
||
|
|
'bytes': int(m.group('bytes')),
|
||
|
|
'rt': rt,
|
||
|
|
}
|
||
|
|
)
|
||
|
|
|
||
|
|
print(f'匹配请求数: {len(rows)}')
|
||
|
|
if not rows:
|
||
|
|
sys.exit(0)
|
||
|
|
|
||
|
|
withrt = [r for r in rows if r['rt'] is not None]
|
||
|
|
if withrt:
|
||
|
|
ts = [r['rt'] for r in withrt]
|
||
|
|
slow = [r for r in withrt if r['rt'] > 1]
|
||
|
|
print(f'耗时: 平均 {sum(ts)/len(ts):.3f}s 中位 {sorted(ts)[len(ts)//2]:.3f}s 最大 {max(ts):.2f}s >1s 的 {len(slow)} 个')
|
||
|
|
print('\n== 最慢 12 个 ==')
|
||
|
|
for r in sorted(withrt, key=lambda x: -x['rt'])[:12]:
|
||
|
|
print(f" {r['rt']:8.2f}s {r['status']} {r['bytes']:>9} B {r['time'][12:]} {r['req']}")
|
||
|
|
|
||
|
|
print('\n== 响应体最大 8 个 ==')
|
||
|
|
for r in sorted(rows, key=lambda x: -x['bytes'])[:8]:
|
||
|
|
rt = f"{r['rt']:.2f}s" if r['rt'] is not None else ' ? '
|
||
|
|
print(f" {r['bytes']:>9} B {rt} {r['status']} {r['req']}")
|
||
|
|
|
||
|
|
from collections import Counter
|
||
|
|
c = Counter(r['req'].split(' ')[1].split('?')[0] if ' ' in r['req'] else r['req'] for r in rows)
|
||
|
|
print('\n== 路径 TOP 10 ==')
|
||
|
|
for path, n in c.most_common(10):
|
||
|
|
print(f' {n:5} {path}')
|