Nginx 日志轮询配置
使用 logrotate(系统级)
配置文件:/etc/logrotate.d/nginx
yaml
/var/log/nginx/*.log {
daily # 按天轮询
missingok # 如果日志文件不存在,忽略错误
rotate 30 # 保留最近30份(即30天)
compress # 压缩旧日志(gzip)
delaycompress # 延迟压缩(先保留一份未压缩的)
notifempty # 如果日志为空,不轮询
create 0640 nginx adm # 创建新日志文件,权限 0640
sharedscripts # 所有日志共享同一脚本
postrotate # 轮询后执行
[ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
endscript
}
| 参数 | 说明 | 示例 |
|---|---|---|
daily |
按天轮询 | weekly(每周)、monthly(每月) |
size 100M |
按大小轮询 | size 100M、size 1G |
rotate 30 |
保留数量 | 保留30份历史文件 |
compress |
压缩(gzip) | 生成 .log.gz |
delaycompress |
延迟压缩 | 最新一份不压缩 |
copytruncate |
截断模式 | 用于无法重启的进程 |
dateext |
日期扩展名 | 文件名带日期 |
missingok |
文件缺失不报错 | - |
notifempty |
空文件跳过 | - |
常见轮询策略:
按天 + 按大小(推荐)
yaml
/var/log/nginx/access.log {
daily
size 500M # 如果某天超过500M也切割
rotate 90 # 保留90天
compress
delaycompress
dateext # 文件名带日期:access.log-20250429.gz
dateformat -%Y%m%d # 日期格式
missingok
notifempty
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
endscript
}
按小时(高流量场景)
yaml
/var/log/nginx/access.log {
hourly # 每小时切分
rotate 168 # 保留7天(24x7)
compress
delaycompress
dateext
dateformat -%Y%m%d%H # 小时级:access.log-2025042914.gz
missingok
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
endscript
}
Nginx 自带配置方式
yaml
# nginx.conf
http {
# 自定义日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# 按天动态日志(需要 cron 配合清理)
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;
# 或者使用日期变量(需要写脚本清理)
# access_log /var/log/nginx/access-$year$month$day.log main;
}
Java/Spring Boot 日志轮询
Spring Boot 默认配置(Logback)
yaml
logging:
file:
path: /var/log/myapp # 日志目录
name: app.log # 日志文件名(如果设置了 path,则自动拼接)
# 日志格式
pattern:
file: "%d{yyyy-MM-dd HH:mm:ss.SSS} | %-5level | [%thread] | %logger{36} | %msg%n"
console: "%d{HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n"
# 日志级别
level:
root: WARN
com.mycompany: INFO
org.springframework: WARN
com.zaxxer.hikari: DEBUG
# 日志轮询配置(关键部分!)
logback:
rollingpolicy:
# 基于文件大小
max-file-size: 100MB # 每个文件最大 100MB
# 基于时间
file-name-pattern: ${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz # 文件名模板
# 保留策略
max-history: 30 # 保留最近 30 份历史文件
total-size-cap: 3GB # 所有归档文件总大小不超过 3GB
# 清理策略
clean-history-on-start: false # 启动时不清除历史日志
实际生成文件:
/var/log/myapp/
├── app.log # 当前日志(最新)
├── app.log.2026-04-28.0.gz # 4月28日第一份压缩
├── app.log.2026-04-28.1.gz # 4月28日第二份压缩
├── app.log.2026-04-27.0.gz # 4月27日压缩
└── app.log.2026-04-26.0.gz # 4月26日压缩(30天后自动删除)
自定义 Logback 配置(更精细控制)
创建 logback-spring.xml 放在 src/main/resources:
csharp
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 日志目录 -->
<property name="LOG_PATH" value="/var/log/myapp" />
<property name="APP_NAME" value="myapp" />
<!-- 控制台输出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} %-5level [%thread] %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<!-- 文件输出 + 轮询(基于时间) -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 当前日志文件 -->
<file>${LOG_PATH}/${APP_NAME}.log</file>
<!-- 轮询策略:基于时间和大小 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 文件名模板 -->
<fileNamePattern>${LOG_PATH}/${APP_NAME}.%d{yyyy-MM-dd}.%i.gz</fileNamePattern>
<!-- 每个文件最大 100MB -->
<maxFileSize>100MB</maxFileSize>
<!-- 保留最近 30 天 -->
<maxHistory>30</maxHistory>
<!-- 所有归档文件总大小 -->
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
<!-- 编码器 -->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} | %-5level | [%thread] | %logger{36} | %msg%n</pattern>
</encoder>
</appender>
<!-- 独立错误日志(只记 ERROR 级别) -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/error.log</file>
<!-- 只记录 ERROR 级别 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/error.%d{yyyy-MM-dd}.gz</fileNamePattern>
<maxHistory>60</maxHistory> <!-- 错误日志保留 60 天 -->
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} | %-5level | [%thread] | %logger{36} | %msg%n</pattern>
</encoder>
</appender>
<!-- 异步日志(提升性能) -->
<appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="FILE" />
<queueSize>1024</queueSize> <!-- 队列大小 -->
<discardingThreshold>0</discardingThreshold> <!-- 是否丢弃日志 -->
<includeCallerData>false</includeCallerData>
</appender>
<!-- 特殊业务日志(按模块分类) -->
<logger name="com.myapp.order" level="INFO">
<appender name="ORDER_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_PATH}/order.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_PATH}/order.%d{yyyy-MM-dd}.gz</fileNamePattern>
<maxHistory>90</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} | %-5level | %msg%n</pattern>
</encoder>
</appender>
</logger>
<!-- 根日志级别 -->
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="ASYNC_FILE" />
<appender-ref ref="ERROR_FILE" />
</root>
</configuration>
关键配置:
| 配置项 | 作用 | 场景 |
|---|---|---|
SizeAndTimeBasedRollingPolicy |
同时基于时间+大小 | 高流量应用 |
TimeBasedRollingPolicy |
仅基于时间 | 常规应用 |
maxFileSize |
单文件最大大小 | 避免单文件过大 |
maxHistory |
保留历史文件天数 | 存储空间限制 |
totalSizeCap |
归档文件总大小上限 | 磁盘空间保障 |
asyncAppender |
异步写入 | 高并发场景 |
使用 logrotate 管理 Java 日志
csharp
# /etc/logrotate.d/myapp-java
/var/log/myapp/*.log {
daily
missingok
rotate 30
compress
delaycompress
notifempty
copytruncate # Java 应用无需重启,直接截断
dateext
size 100M # 超过 100M 也切割
postrotate
# 可选:通知 Java 应用重新打开日志
# kill -HUP `cat /var/run/myapp.pid`
endscript
}
Java 应用使用 copytruncate 模式,不需要重启应用。这和 Nginx 不同(Nginx 需要 kill -USR1)。
日志策略对比
| 策略 | Nginx | Spring Boot默认 | 自定义 Logback | logrotate |
|---|---|---|---|---|
| 按天 | ✅ 需logrotate | ✅ 自动 | ✅ 配置 | ✅ 标准 |
| 按时 | ✅ 需logrotate | ❌ 不直接支持 | ✅ 配置 | ✅ 标准 |
| 按大小 | ✅ 需logrotate | ✅ 自动 | ✅ 配置 | ✅ 标准 |
| 压缩 | ✅ 自动 | ✅ 自动(gz) | ✅ 自动 | ✅ 自动 |
| 保留天数 | ✅ logrotate设置 | ✅ max-history | ✅ maxHistory | ✅ rotate设置 |
| 总大小限制 | ❌ | ✅ total-size-cap | ✅ totalSizeCap | ❌ |
| 异步写入 | ❌ | ❌ | ✅ asyncAppender | ❌ |
| 模块化 | ✅ 多文件 | ❌ 单文件 | ✅ 多文件 | ✅ 多文件 |
| 应用重启 | ✅ 需信号 | ❌ 不需要 | ❌ 不需要 | ❌ copytruncate |
常用命令:
csharp
# 查看 logrotate 状态
cat /var/lib/logrotate/status
# 手动执行轮询(debug模式)
logrotate -d /etc/logrotate.d/nginx
# 强制轮询(立即执行)
logrotate -f /etc/logrotate.d/nginx
# 查看日志目录大小
du -sh /var/log/nginx/
du -sh /var/log/myapp/
# 查看归档文件
ls -lh /var/log/nginx/*.gz
ls -lh /var/log/myapp/*.gz
# 解压查看历史日志
zcat /var/log/nginx/access.log-20250428.gz | tail -100
# 在压缩文件中搜索
zgrep "ERROR" /var/log/myapp/app.log.*.gz
日志轮询不是为了轮询而轮询,而是为了:
防止磁盘爆满(最紧急)
便于历史查询(时间范围清晰)
节省存储空间(压缩 + 自动清理)
问题排查方便(单个文件大小可控)