打开服务器上那个叫 access.log 的网站文件,里面每一行就是日志一条日志。去年帮朋友查一个电商站点的网站异常流量,我让他把日志发过来,日志他直接截了张图——密密麻麻的网站文本,时间戳、日志IP、网站请求路径、日志状态码、网站User-Agent 挤在一起。日志他问这玩意儿怎么弄出来的网站,我说你服务器上本来就有,日志只是网站你没去看。
大部分网站日志分两种。日志一种是网站 Web 服务器自己记的,比如 Nginx、Apache,你装完默认就在写,位置通常在 /var/log/nginx/access.log 或 /var/log/httpd/access_log。另一种是应用层自己打的,PHP 的 error_log、Node.js 的 Winston、Python 的 logging 模块,这些得在代码里配置。很多人只盯着服务器日志,结果应用报错查半天查不到,其实错误早写进自己的日志文件了。
Nginx 的日志格式可以在配置文件里改。默认的 combined 格式长这样:
log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; 想加请求耗时?加 $request_time。想看上游响应时间?加 $upstream_response_time。我一般还会把 $http_x_real_ip 塞进去,因为前面挂了 CDN 之后,$remote_addr 拿到的全是 CDN 节点 IP,真实用户 IP 藏在头部里。这个坑不踩一次不会记得。
Apache 的用户看 CustomLog 指令,位置在 httpd.conf 或 apache2.conf。Windows 服务器上 IIS 的日志默认在 %SystemDrive%\inetpub\logs\LogFiles,按天分文件夹,W3C 格式,字段用空格隔开,跟 Linux 那套不太一样。
拿到日志文件只是第一步。直接 cat 出来看?几千行刷屏,眼睛根本跟不上。我习惯用 tail -f access.log 实时盯,或者 grep "500 " access.log | awk '{ print $1}' | sort | uniq -c | sort -rn | head -20 把报 500 最多的 IP 揪出来。这条命令组合我用了快八年,排查攻击和爬虫基本够用。
日志量大了之后,服务器磁盘会被吃满。见过一个日活几千的小站,Nginx 日志一天涨 2GB,因为没配切割。logrotate 是标配,在 /etc/logrotate.d/nginx 里写几行配置,每天转存、压缩、保留 14 天,老文件自动删。不配这个,迟早有一天网站挂掉是因为磁盘 100%。
有些场景拿不到服务器权限怎么办?虚拟主机、共享 hosting,后台控制面板里通常有“访问日志”或“原始日志”下载入口。再不行就自己埋点,前端用 navigator.sendBeacon 把关键行为发到自己的接口,后端记下来。这种方式拿不到状态码和响应字节,但用户点了什么、从哪来的、用什么设备,都能拿到。
CDN 的日志是另一回事。Cloudflare、阿里云 CDN 都提供日志推送,可以拉到自己的对象存储里。格式跟源站日志不同,字段更偏网络层,比如边缘节点、缓存命中状态、TLS 版本。要分析真实用户访问,CDN 日志比源站日志更准,因为源站看到的很多请求其实是 CDN 回源。
最后说个实际教训。有次排查一个偶发的 502,看 Nginx 错误日志只写了一行“upstream prematurely closed connection”,没更多信息。后来把 PHP-FPM 的慢日志打开,设置 request_slowlog_timeout = 5s,才抓到是某个数据库查询卡了 30 秒导致进程被 kill。日志这东西,平时觉得没用,出事的时候少一个文件都查不下去。别等网站挂了才去找日志在哪。