分析log日志
前言
分析log日志,对一个服务器端开发工程师的重要性就不细谈了。
Linux 为什么允许一个进程观察另一个进程?
为什么一个进程能够”看见”另一个进程?很多人认为Process A/B完全隔离。实际上Linux 内核一直知道:PID/Registers/Memory/Open Files/Threads/Signals 等每一个进程的全部状态。例如ps 为何能工作?因为kernel 知道PID/CPU/RSS/COMMAND。
一个进程真正长什么样?真正管理进程的是kernel,真正拥有Memory Map(包括Page Table等)的是kernel, task_struct可以理解为Process Descriptor。
+-----------------------+
| User Space |
| |
| Code |
| Heap |
| Stack |
| mmap |
+-----------------------+
──────── System Call ────────
+-----------------------+
| Kernel |
| |
| task_struct |
| page table |
| registers |
| fd table |
+-----------------------+
gdb(ptrace 用户态前端,正如 ip 是 netlink 的用户态前端)不是直接读Process而是请求Kernel。为什么不是所有人都能看?安全。Linux有ptrace权限,例如只能同一用户CAP_SYS_PTRACE,否则kernel拒绝。所以 有时gdb -p PID 直接Permission denied。为什么很多工具(strace/ltrace)都和 gdb 很像?因为它们实际上都是 ptrace 的不同封装。
ptrace——Linux 的”远程控制协议”/系统调用,允许一个进程(Tracer)控制另一个进程(Tracee)。
- 很多人看到
gdb -p 12345输出:Attaching to process 12345...,很多人以为gdb 直接连接目标进程,真正发生的是:gdb ==> ptrace() ==> kernel ==> 目标进程,gdb 从来没有直接操作另一个进程,都是kernel 做的。 - Linux 提供了一个系统调用: ptrace,名字来自 Process Trace,最开始:它就是为了Debugger。
- Kernel 做了什么?
- 检查权限,CAP_SYS_PTRACE 或同用户。
- 如果允许,kernel 会暂停目标进程(SIGSTOP)。为什么要停?因为如果程序一边运行,一边调试器读取内存,就可能读到一半数据发生变化。目标进程静止后,于是 gdb 可以不断请求 Kernel:读取寄存器;读取内存;读取线程;设置断点;修改变量。ptrace几乎提供了调试器需要的全部基础能力,所以很多 Linux 调试工具其实都是在封装 ptrace。
- 执行
gdb continueKernel 解除暂停,程序继续正常执行。
attach 后能做什么?不同工具不同。Read-only Attach/Active Attach.
- gdb读内存,改寄存器,单步,断点
- strace,观察syscall。
strace python app.py每次系统调用都会发生程序进入 syscall ↓ Kernel 暂停 ↓ 通知 strace ↓ strace 打印 ↓ 继续运行所以 strace 在 syscall 非常频繁时会明显变慢。
- perf,采样
- py-spy,读python Frame,经典实现思路是
- ptrace 暂停 Python
- 读取 Python 进程内存
- 找到 PyThreadState
- 找到当前 PyFrameObject
- 顺着 Frame 链一直往上走
- 恢复运行 所以 py-spy 并没有修改 Python,它只是”读”了 Python 解释器的数据结构。
为什么现代很多 profiler 都尽量不用 ptrace?
- 必须暂停进程。对于高 QPS 服务,即使暂停几十毫秒,也可能影响延迟。
- 需要较高权限。很多 Kubernetes 集群默认禁止 CAP_SYS_PTRACE。
- 频繁采样成本高。如果每秒暂停 100 次:
STOP → READ → CONTINUE STOP → READ → CONTINUE ...对性能影响会越来越明显。
怎么让一个已经运行的进程,开始执行新的代码?
假设 Python 已经跑了三天 python server.py, 现在你执行:memray attach <pid>,然后它开始记录:PyObject_Malloc(),Memray 的代码什么时候 import 进去的?不是启动时,是attach 的时候。
假设
int main() {
while (1) {
sleep(1);
}
}
已经运行了。如果我希望让它执行foo(), 最简单的方法改程序计数器(PC),让它跳到foo()执行,执行完回来。
列出当前目录下最大的10个文件
du -hsx * | sort -rh | head -10
分析web服务的日志
-
查看特定区域的日志
程序运行了一阵,莫名其妙停了。
cat test.log | grep 'Exception' grep 'Exception' test.log cat test.log | grep -n 'Exception' 显示行号但只能看到单行,如果想看到多行,就得借助sed了
sed -n '/2014-12-17 16:17:20/,/2014-12-17 16:17:36/p' test.log // 查看文件的第5行到第10行 sed -n '5,10p' filename // 关键字所在行及之后5行 after grep 关键字 filename -A 5 // 关键字所在行及之前5行 before grep 关键字 filename -B 5 // 关键字所在行及前后5行 before grep 关键字 filename -C 5 -
查看部分日志
如果我们查找的日志很多,打印在屏幕上不方便查看, 有两个方法:
-
使用more和less命令, 如:
grep -n "地形" test.log | more这样就分页打印了,通过点击空格键翻页
-
使用
>xxx.txt将其保存到文件中,到时可以拉下这个文件分析.如:cat -n test.log grep “地形” >xxx.txt
-
-
从某行开始看
tail -n +5 xxx.log
查找项目所在的服务器位置
一般公司的测试主机上会部署多个tomcat目录,如果tomcat目录的命名没有规范,则很难确认一个项目运行在哪个目录上。
-
根据端口查找
// 找到端口对应进程的pid号 netstat -anp | grep port // 根据进程的pid找到可执行文件所在的目录 ps -ef | grep pid -
假设已知该项目的日志在某个目录下,根据日志文件查找
// 找到日志文件对应进程的pid号 fuser logfile // 根据进程的pid找到可执行文件所在的目录 ps -ef | grep pid
lsof (list open file)
// 输出所有使用pid打开的文件
lsof -p pid
其它
-
将应用程序后台运行,并记录日志
笔者经常使用python写一些脚本,会碰到一种情况:一个脚本会运行较长时间(在远端服务器上),因此会趁中午出去吃饭等时间执行它。但个人电脑会自动锁屏,导致个人电脑与远端服务器失联,进而导致脚本执行中断。因此,我们要将脚本后台运行,并将输出导到某个文件上。
python test.py > test.log 2>&1 &
一个好用的shell
直接安装即可
sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)"
留下评论