Nginx 访问日志分析实战:GoAccess 实时统计、GeoIP 归属地解析与自动化报表


网站跑起来之后,很多站长只看"有没有人访问",却不看"访问的人是谁、从哪来、看了什么、卡在哪"。Nginx 的 access.log 里其实存着海量的用户行为数据,但原始日志动辄几百 MB,用 grep 翻根本看不过来。GoAccess 就是一款开源的实时 Web 日志分析工具,纯命令行、零数据库依赖,几秒钟就能把几十万行日志解析成可视化的 HTML 报表,非常适合在 VPS 上自建轻量级的访问统计分析。

本文从日志格式定制讲起,一步步完成 GoAccess 的安装、实时分析、GeoIP 归属地解析和自动化报表生成,最后附上常见排错。

一、第一步:定制 Nginx 日志格式

GoAccess 解析日志的前提是"格式对得上"。虽然它内置了 Nginx 的常见格式(combined),但更推荐直接把 access.log 输出成 JSON,这样字段完整、解析零歧义,后续还能直接给其他程序消费。

在 Nginx 的 http 块中定义 JSON 格式:

http {
    log_format json_combined escape=json '{'
        '"time_local":"$time_local",'
        '"remote_addr":"$remote_addr",'
        '"remote_user":"$remote_user",'
        '"request":"$request",'
        '"status":$status,'
        '"body_bytes_sent":$body_bytes_sent,'
        '"request_time":$request_time,'
        '"http_referer":"$http_referer",'
        '"http_user_agent":"$http_user_agent",'
        '"http_x_forwarded_for":"$http_x_forwarded_for"'
        '}';

    access_log /var/log/nginx/access.log json_combined;
}

然后重载 Nginx:

nginx -t && nginx -s reload

之后产生的日志形如:

{"time_local":"24/Oct/2026:10:00:00 +0800","remote_addr":"1.2.3.4","request":"GET / HTTP/1.1","status":200,"body_bytes_sent":5210,"request_time":0.013,"http_referer":"-","http_user_agent":"Mozilla/5.0 ..."}

二、安装 GoAccess

Debian/Ubuntu 直接使用官方源安装(自带的旧版本不带 GeoIP,建议走官方源):

# 添加官方仓库
echo "deb https://deb.goaccess.io/ $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/goaccess.list
wget -O - https://deb.goaccess.io/gnugpg.key | sudo apt-key add -
sudo apt-get update
sudo apt-get install -y goaccess

CentOS/Rocky 使用 yum 源:

cat > /etc/yum.repos.d/goaccess.repo <<'EOF'
[goaccess]
name=GoAccess repo
baseurl=https://repo.goaccess.io/centos/$releasever/$basearch
gpgcheck=1
enabled=1
gpgkey=https://repo.goaccess.io/gnugpg.key
EOF
yum install -y goaccess

验证安装版本(需 >= 1.5 才有较完整的 JSON 支持):

goaccess --version | head -1
# GoAccess - 1.9.3.

三、实时分析与生成 HTML 报表

直接用交互式终端界面实时查看(需要 TTY):

goaccess /var/log/nginx/access.log --log-format=JSON --date-format=%d/%b/%Y --time-format=%T

更适合自动化的是生成静态 HTML 报表:

goaccess /var/log/nginx/access.log \
  --log-format=JSON \
  --date-format=%d/%b/%Y \
  --time-format=%T \
  -o /var/www/html/report.html \
  --real-time-html \
  --port=7890

参数说明:

--log-format=JSON    # 日志格式为 JSON
--date-format        # 日期格式,与日志中 $time_local 对应
--time-format        # 时间格式
-o                   # 输出 HTML 文件路径
--real-time-html     # 开启实时刷新模式(浏览器端轮询)
--port               # 实时刷新用的 WebSocket 端口

生成的 report.html 可以直接用 Nginx 托管,浏览器打开 http://你的域名/report.html 即可看到包含访问量趋势、来源国家/地区、热门页面、状态码分布、访客 IP 排行等完整报表。

四、GeoIP 归属地解析

默认安装的 GoAccess 没有地理位置库,报表里"来源地区"会是空的。需要安装 GeoIP 相关依赖:

# Debian/Ubuntu
sudo apt-get install -y libmaxminddb-dev libgeoip-dev geoip-database
# 或 CentOS
yum install -y geoip-devel GeoIP-data

安装时需重新编译 GoAccess 才会带上 GeoIP 支持:

wget https://github.com/allinurl/goaccess/archive/refs/tags/v1.9.3.tar.gz
tar xzf v1.9.3.tar.gz && cd goaccess-1.9.3
./configure --enable-utf8 --enable-geoip=mmdb --with-libmaxminddb
make && sudo make install

使用 MaxMind GeoLite2 免费库(需要注册账号下载,或使用镜像):

# 下载 GeoLite2-City 数据库(示例使用镜像,正式使用请注册 MaxMind 账号)
curl -L -o GeoLite2-City.mmdb https://github.com/P3TERX/GeoLite2-City.mmdb/raw/download/GeoLite2-City.mmdb
sudo mkdir -p /usr/share/GeoIP
sudo mv GeoLite2-City.mmdb /usr/share/GeoIP/GeoLite2-City.mmdb

生成报表时指定数据库:

goaccess /var/log/nginx/access.log \
  --log-format=JSON \
  --date-format=%d/%b/%Y --time-format=%T \
  --geoip-database=/usr/share/GeoIP/GeoLite2-City.mmdb \
  -o /var/www/html/report.html

这样报表里就能看到访客来自哪个城市、运营商等信息。

五、自动化:定时生成报表并保护访问

把报表生成写进 cron,每小时更新一次:

# /etc/cron.d/goaccess-report
0 * * * * root /usr/local/bin/goaccess /var/log/nginx/access.log \
  --log-format=JSON --date-format=%d/%b/%Y --time-format=%T \
  --geoip-database=/usr/share/GeoIP/GeoLite2-City.mmdb \
  -o /var/www/html/report.html >/dev/null 2>&1

报表页包含访问者来源等数据,不建议裸奔公开,给 Nginx 加一层 Basic Auth 保护:

server {
    listen 80;
    server_name stats.example.com;

    location / {
        auth_basic "Restricted";
        auth_basic_user_file /etc/nginx/.htpasswd;
        alias /var/www/html/;
        index report.html;
    }
}
# 生成账号密码
htpasswd -c /etc/nginx/.htpasswd admin

六、常见排错

报错:Log format not recognized / 解析出的字段为空
90% 是日志格式和 --log-format 对不上。如果 access.log 里还是旧格式的混合日志,先确认新格式已生效:

tail -1 /var/log/nginx/access.log
# 如果末尾还是 "1.2.3.4 - - [24/Oct/...] \"GET ..." 这种 common 格式,
# 说明 access_log 指令没生效,检查是否写在了对的 server 块里并已 reload。

报错:date/time format 不匹配
JSON 日志里 $time_local 形如 24/Oct/2026:10:00:00 +0800,对应 --date-format=%d/%b/%Y --time-format=%T。如果自定义了其他时间格式,必须同步修改这两个参数,否则 GoAccess 会跳过整行日志。

报表里 IP 全是 CDN 节点
和 Nginx 限流场景一样,日志里记录的是 $remote_addr(CDN 节点 IP)。把 JSON 格式里的字段换成真实 IP 即可:

'"remote_addr":"$http_x_forwarded_for"'

(前提是已用 realip 模块或确认 CDN 传了可信的 X-Forwarded-For 头,防止伪造头注入。)

日志轮转后报表出现重复/缺失
logrotate 切出旧日志后,cron 里 goaccess access.log 只解析当前文件,历史数据会丢。可以用 --restore / --persist 增量合并,或把归档日志统一 glob 进去:

goaccess /var/log/nginx/access.log /var/log/nginx/access.log.* \
  --log-format=JSON --date-format=%d/%b/%Y --time-format=%T \
  -o /var/www/html/report.html

报表生成很慢
第一次解析几百 MB 历史日志属于正常现象。可以先用 --ignore-crawlers 忽略爬虫、用 --anonymize-ip 做 IP 脱敏,后续配合 logrotate 按天切割 + 定时全量重建即可保持速度。

七、总结

GoAccess 的价值在于"零依赖、秒级出报表":把 Nginx 日志改成 JSON 格式后,一条命令就能得到可视化访问分析,配合 GeoIP 库可以看清访客来源,配合 cron 和 Basic Auth 就能搭建一套私有的轻量访问统计服务。它替代不了 Matomo 这类完整埋点分析,但用来快速了解"谁在访问、访问了什么、服务器性能如何",在 VPS 上是性价比极高的选择。日常运维记得先做小流量验证格式,再放开全量解析,避免一次解析卡死服务器。


手机扫码阅读

微信或手机浏览器扫一扫,随时随地随心阅读与分享

Keepalived + Nginx 高可用实战:VRRP 虚拟 IP 漂移、双机热备与故障排错

Nginx 限流与防 CC 攻击实战:limit_req、limit_conn 配置与封禁策略全解析

评 论
更换验证码