AI 文章摘要
NginxPulse是一款轻量级的Nginx日志分析面板,支持实时查看PV/UV数据、IP归属地查询、客户端/浏览器解析等功能。它使用Go和Vue3技术栈,支持多站点和自定义日志格式。通过Docker或Docker Compose可以轻松部署,并提供远端日志拉取、Push Agent和自定义日志格式等实用功能。...
这是啥?
一句话: 轻量级的 Nginx 日志分析面板 。
能干啥:
- 实时看 PV/UV 数据
- IP 归属地查询(国内用 ip2region 本地库,国外走 ip-api)
- 客户端/浏览器解析
- 支持多站点
- 支持自定义日志格式
看下效果图:


技术栈
后端是 Go + Gin,前端是 Vue3 + Vite + PrimeVue,数据库就一个 SQLite,够轻量。
IP 归属地这块挺聪明的:先查内存缓存,然后走远程 API 批量查,失败了再用本地 ip2region 兜底。既保证速度又保证准确率。
怎么跑起来
Docker 一把梭
最简单的方式,一行命令搞定:
docker run -d --name nginxpulse \
-p 8088:8088 \
-p 8089:8089 \
-e WEBSITES='[{"name":"itpno博客","logPath":"/share/www/wwwlogs/itpno.com.log","domains":["itpno.com"]}]' \
-v /www/wwwlogs:/share/www/wwwlogs:ro \
-v $(pwd)/var/nginxpulse_pgdata:/app/var/pgdata \
-v $(pwd)/var/nginxpulse_data:/app/var/nginxpulse_data \
--user root \
--restart unless-stopped \
magiccoders/nginxpulse:latest
把 /www/wwwlogs 换成你自己的日志目录就行。
Docker Compose
如果喜欢 compose(比如我),也行:
version: "3.8"
services:
nginxpulse: # 正确缩进(2个空格)
image: magiccoders/nginxpulse:latest # 镜像名前后加空格
container_name: nginxpulse # 小写(规范),且冒号后加空格
user: root # 新增:以root用户运行容器
ports:
- "8088:8088" # - 后加空格,端口映射格式正确
- "8089:8089"
environment:
# JSON 字符串用单引号包裹,内部双引号无需转义,冒号后加空格
WEBSITES: '[{"name":"iTPno.博客","logPath":"/share/www/wwwlogs/itpno.com.log","domains":["itpno.com,www.itpno.com"]}]'
ACCESS_KEYS: '["access-key"]'
volumes:
# 宿主机路径:/容器内路径,冒号后加空格,ro(只读)格式正确
# 日志文件挂载(宿主机路径 -> 容器内路径)
- /www/wwwlogs:/share/www/wwwlogs:ro
# 内置PostgreSQL数据目录挂载(必须配置,否则启动失败)
- ./app/var/nginxpulse_pgdata:/app/var/pgdata
# 原有的应用数据目录(保留)
- ./app/var/nginxpulse_data:/app/var/nginxpulse_data
restart: unless-stopped # 冒号后加空格
跑起来之后:
- 前端面板: http://localhost:8088
- 后端 API: http://localhost:8089
多网站怎么配?
如果你有多个站点, WEBSITES 传数组就行:
WEBSITES='[
{"name":"主站","logPath":"/logs/main.log","domains":["www.example.com"]},
{"name":"博客","logPath":"/logs/blog.log","domains":["blog.example.com"]}
]'
日志按天切割的话,支持通配符:
{"logPath": "/logs/access-*.log"}
.gz 压缩日志也能直接解析,不用手动解压。
但是这样会导致,/www/wwwlogs下的日志文件,docker无法访问,我用root、www的1000都试了,都不行!!!
官方方案(推荐!!!)
docker-compose.yml:
version: "3.8"
services:
nginxpulse:
image: magiccoders/nginxpulse:latest
container_name: local_nginxpulse
ports:
- "8088:8088"
- "8089:8089"
volumes:
- ./docker_local/logs:/share/logs
- ./docker_local/nginxpulse_data:/app/var/nginxpulse_data
- ./docker_local/pgdata:/app/var/pgdata
- ./docker_local/configs:/app/configs
- /etc/localtime:/etc/localtime
restart: unless-stopped
运行:
docker-compose up -d
打开:host:8088,根据提示配置日志
找AI写一个日志复制的sh,放到宝塔自动任务上:
#!/bin/bash
# 定义源文件和目标目录路径
SOURCE_FILE="/www/wwwlogs/itpno.com.log"
TARGET_DIR="/www/dk_project/dk_app/nginxpulse/docker_local/logs"
# 复制后的目标文件完整路径
TARGET_FILE="$TARGET_DIR/itpno.com.log"
# 脚本执行日志(可选,方便排查问题)
LOG_FILE="/tmp/copy_nginx_logs.log"
# 函数:打印带时间戳的日志
log_info() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] INFO: $1" >> "$LOG_FILE"
echo "[$(date +'%Y-%m-%d %H:%M:%S')] INFO: $1"
}
log_error() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $1" >> "$LOG_FILE"
echo "[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $1"
exit 1
}
# 1. 检查源文件是否存在且可读
if [ ! -f "$SOURCE_FILE" ]; then
log_error "源文件 $SOURCE_FILE 不存在!"
fi
if [ ! -r "$SOURCE_FILE" ]; then
log_error "没有读取源文件 $SOURCE_FILE 的权限!"
fi
# 2. 检查目标目录是否存在,不存在则创建
if [ ! -d "$TARGET_DIR" ]; then
log_info "目标目录 $TARGET_DIR 不存在,正在创建..."
mkdir -p "$TARGET_DIR" || log_error "创建目标目录 $TARGET_DIR 失败!"
fi
# 3. 检查目标目录是否可写
if [ ! -w "$TARGET_DIR" ]; then
log_error "没有写入目标目录 $TARGET_DIR 的权限!"
fi
# 4. 执行复制操作(-p 保留文件属性,-f 强制覆盖已存在的目标文件)
log_info "开始复制文件: $SOURCE_FILE -> $TARGET_DIR"
cp -pf "$SOURCE_FILE" "$TARGET_DIR/" || log_error "文件复制失败!"
# 5. 修改目标文件权限(核心新增步骤)
# 方案1:通用可读权限(推荐,Docker 容器几乎都能读取)
log_info "正在修改文件权限,确保 Docker 可读取..."
chmod 644 "$TARGET_FILE" || log_error "修改文件权限失败!"
# (可选)方案2:指定 Docker 用户/组(如果知道 Docker 运行的用户ID/组ID)
# chown 1000:1000 "$TARGET_FILE" # 替换为实际的 Docker 用户ID:组ID
# chmod 644 "$TARGET_FILE"
# 6. 验证复制和权限修改结果
if [ -f "$TARGET_FILE" ]; then
# 打印最终权限,方便验证
FILE_PERM=$(ls -l "$TARGET_FILE" | awk '{print $1}')
log_info "文件复制成功!目标文件路径:$TARGET_FILE,当前权限:$FILE_PERM"
else
log_error "文件复制后验证失败,目标文件不存在!"
fi
exit 0


如果想要实时的,那就改成1分钟执行一次,这样就能顺利读取文件了!
几个实用功能
远端日志拉取
日志不在本机?没关系,支持 SFTP、HTTP、S3/OSS 三种方式拉取远端日志。比如 SFTP:
{
"id": "sftp-main",
"type": "sftp",
"host": "1.2.3.4",
"port": 22,
"user": "nginx",
"auth": { "keyFile": "/secrets/id_rsa" },
"path": "/var/log/nginx/access.log"
}
Push Agent
如果服务器在内网或者边缘节点,可以用 Agent 主动推送日志。在日志服务器上跑一个轻量 agent,实时把日志推到 NginxPulse 主服务。
自定义日志格式
不是默认的 combined 格式?可以自定义。支持两种方式:
- 方式一:直接写 log_format 语法
{
"logFormat": "$remote_addr - $remote_user [$time_local] \"$request\" $status $body_bytes_sent"
}
- 方式二:正则(命名分组)
{
"logRegex": "^(?P<ip>\\S+) - (?P<user>\\S+) \\[(?P<time>[^\\]]+)\\]..."
}
Caddy 也支持
用 Caddy 的朋友也能用,配置 logType: “caddy” 就行,会按 JSON 格式解析。
访问控制
生产环境可以加个密钥:
ACCESS_KEYS='["your-secret-key"]'
访问时需要带上 X-NginxPulse-Key 请求头,前端会自动弹窗让你输入。
常见问题
Q:跑起来后日志明细是空的?
A:多半是权限问题。给日志目录和数据目录加个权限:
chmod -R 777 /path/to/logs /path/to/nginxpulse_data
Q:有访问但是 PV/UV 都是 0?
A:默认排除内网 IP。如果想统计内网流量,把 PVEXCLUDEIPS 设成空数组:
PV_EXCLUDE_IPS='[]'
单体部署
如果不想用 Docker,可以构建成单个可执行文件:
./scripts/build_single.sh
会生成一个内置前端的二进制文件,直接运行就能同时提供前后端服务。支持 amd64 和 arm64。
最后
GitHub 地址:
https://github.com/likaia/nginxpulse
在线演示:
https://nginx-pulse.kaisir.cn/
目前 1.4k star,MIT 协议,可以放心用。
如果你也在找 Nginx 日志分析工具,可以试试这个。比起重量级的 ELK 或者纯命令行的 GoAccess,这个算是个不错的中间选择。
参考:https://juejin.cn/post/7597080391880343562



