日志轮转是一种自动管理日志文件的机制,通过归档、压缩和删除旧记录来防止磁盘溢出。在移动应用中,日志会在用户设备上累积,如果没有轮转,几周的使用就会占用数千兆字节的存储。根据 Redis Documentation,正确配置日志轮转可将因磁盘满导致的系统故障风险与不受控制的日志增长相比降低99%。主要的轮转策略包括:按文件大小、按时间和按文件数量——每种策略根据使用场景选择:Linux 中的 logrotate、iOS 的 CocoaLumberjack 和 Android 的 Timber 都支持这三种方法。
要点
日志轮转是定期将活动日志文件切换为新文件,同时归档、压缩或删除旧文件的过程。如果没有轮转,单个日志文件将无限增长,直到填满整个磁盘分区,导致应用程序崩溃和数据丢失。
典型场景:应用程序将日志写入 app.log 文件。当 app.log 达到 100 MB 时,系统将其重命名为 app.log.1,压缩为 app.log.1.gz 并创建新的空 app.log 文件。下次填满时,app.log.1 变为 app.log.2,app.log.1.gz 变为 app.log.2.gz,旧的 app.log.2.gz 被删除。这种机制称为带保留计数的轮转——归档副本的数量是固定的。
根据 Splunk(2023)的数据,错误的轮转配置是应用服务器上40%的磁盘满相关事件的原因。对于移动设备,轮转更为关键,因为用户不能也不应该手动管理日志。
日志轮转支持三种可以组合使用的基本策略。策略的选择取决于应用程序类型:服务器系统更常使用按时间轮转,移动应用使用按大小轮转,嵌入式系统使用按文件数量轮转。
| 策略 | 触发条件 | 使用场景 |
|---|---|---|
| 按大小 | 文件达到 N 字节 | 日志量不可预测的高负载系统 |
| 按时间 | 经过 N 小时/天 | 每日转储、合规要求 |
| 按文件数量 | 已创建 N 个文件 | 磁盘空间有限的移动设备 |
按大小轮转保证没有任何日志文件超过设定的限制。根据可用磁盘空间和日志记录频率选择限制。对于服务器,典型的限制是每个文件 100–500 MB,对于移动设备为 1–10 MB。如果应用程序记录频繁,则需要降低限制,否则轮转将每隔几分钟发生一次。
按时间轮转与日志量无关——文件严格按照计划切换。适用于日志需要保存固定天数的系统:保留计数为 30 的每日轮转意味着 30 天的存储。缺点——在高负载下,单个文件每天可能增长到千兆字节。
logrotate 是用于自动轮转日志的标准 Linux 工具。它通过 cron 运行,处理来自 /etc/logrotate.d/ 的配置文件。每个服务(nginx、postgresql、应用程序)都会创建自己的配置文件,指定日志路径、轮转策略和轮转后的操作。
# /etc/logrotate.d/myapp — 应用程序日志轮转
/var/log/myapp/*.log {
daily
rotate 7
compress
delaycompress
missingok
notifempty
create 0640 www-data www-data
postrotate
kill -HUP $(cat /var/run/myapp.pid)
endscript
}
此配置每天轮转日志,保留 7 个归档副本,用 gzip 压缩旧文件(最后一个除外——延迟压缩),如果日志不存在则不报错(missingok),不轮转空文件(notifempty),并以 0640 权限重新创建文件。轮转后通过 postrotate 脚本向应用程序进程发送 HUP 信号。
size — 达到大小时轮转(size 100M)。rotate — 归档副本数(rotate 7)。compress — gzip 压缩。dateext — 在文件名中添加日期而不是序号。sharedscripts — 为所有文件执行一次 postrotate,而不是单独为每个文件执行。maxage — 删除超过 N 天的归档文件。
在移动设备上,日志轮转至关重要,因为用户不管理文件系统,也不期望应用程序占用数千兆字节的日志。iOS 和 Android 有内置机制:iOS 的 os_log 使用固定大小的环形缓冲区(通过覆盖进行轮转),Android Logcat 在内核中有受限的缓冲区。
对于 iOS 上的自定义文件日志,使用 CocoaLumberjack 的 DDFileLogger 类,它支持按大小和时间轮转。在 Android 上——Logback 或通过 RollingFileAppender 的自定义实现。两种工具都允许设置最大文件大小和归档数量。
// CocoaLumberjack — iOS 文件轮转
import CocoaLumberjack
let fileLogger = DDFileLogger()
fileLogger.maximumFileSize = 1024 * 1024 // 1 MB
fileLogger.logFileManager.maximumNumberOfLogFiles = 5
DDLog.add(fileLogger)
iOS:os_log 不需要轮转——消息在环形缓冲区中被覆盖。但如果应用程序写入自定义文件日志(例如用于调试或发送到服务器),则需要手动配置轮转。CocoaLumberjack 是 iOS 团队的标准选择,它会自动将归档压缩为 .gz 格式,并在超过限制时删除旧文件。
Android 不限制应用程序向自己的目录写入日志。如果开发人员在没有轮转的情况下将调试日志写入文件,一个月的活跃使用可能占用 500 MB 到 1 GB。当系统显示空间不足警告时,用户会发现此问题并删除应用程序。使用 RollingFileAppender 的 Logback 解决了这个问题:5 MB 的限制和 3 个归档保证日志永远不会占用超过 20 MB 的空间。
以下是两个平台上日志轮转的配置示例。iOS 使用 CocoaLumberjack,Android 使用通过 XML 配置的 Logback。
// Android 上的 Logback — logback.xml 中的轮转配置
// 文件大小 5MB,3 个归档副本
@file:Suppress("unused")
// 在 logback.xml 中:
// <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
// <file>${DATA_DIR}/logs/app.log</file>
// <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
// <fileNamePattern>app.%i.log.gz</fileNamePattern>
// <minIndex>1</minIndex>
// <maxIndex>3</maxIndex>
// </rollingPolicy>
// <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
// <maxFileSize>5MB</maxFileSize>
// </triggeringPolicy>
// </appender>
iOS 上的 CocoaLumberjack 不仅支持按大小轮转,还支持通过 logFileManager.maximumLogFiles 按日期删除旧日志。如果设置 maximumLogFiles = 0,限制将被取消——日志将无限期累积,这对生产环境很危险。
// 带总量检查的自定义轮转
class SizeAwareLogger {
let maxTotalSize: Int64 = 20 * 1024 * 1024
func enforceQuota(at logDirectory: URL) {
let files = (try? FileManager.default
.contentsOfDirectory(
at: logDirectory,
includingPropertiesForKeys: [.fileSize]
)) ?? []
let total = files.reduce(0) {
$0 + (try? $1.resourceValues(forKeys: [.fileSize])
.fileSize).map(Int64.init) ?? 0
}
if total > maxTotalSize {
// 删除最旧的文件
files.sorted { $0.path < $1.path }.first.map {
try? FileManager.default.removeItem(at: $0)
}
}
}
}
日志轮转不仅是自动归档,也是系统健康的指标。如果日志轮转过于频繁(每隔几分钟),则表明日志记录过多或出现了循环错误日志记录。设置轮转频率的告警:每小时超过 10 次轮转是进行检查的理由。
监控系统(Prometheus、Grafana、Datadog)可以通过文件系统导出器跟踪轮转指标。Prometheus node_exporter 提供文件大小及其修改时间的指标。在移动设备上,轮转监控通常内置于 SDK 中:CocoaLumberjack 通过 DDLog 记录轮转事件,Logback 通过 appender 发送状态。
告警:如果归档文件数量超出预期(轮转计数超过限制)或日志总体积超过配额——系统应通知管理员。对于服务器,标准阈值是分区大小的 80%,对于移动设备——每个应用程序超过 50 MB 时触发告警。
常见问题
对于服务器——100–500 MB,对于移动应用——1–10 MB。过小的限制(低于 1 MB)会导致频繁轮转和额外的 I/O 操作。过大的限制(超过 500 MB)会增加文件打开和搜索的时间。
对于生产环境——至少 7 天(每日轮转)或 3–5 个归档(按大小轮转)。对于合规要求——30–90 天,但此时应使用独立的带压缩和保留策略的存储,而不是在同一分区上进行轮转。
logrotate 是 Linux 工具,在 iOS 和 Android 上不可用。在移动设备上,轮转由库实现:iOS 的 CocoaLumberjack 和 Android 的 Logback。它们不需要 root 权限,在应用程序的沙箱环境中运行。
检查是否存在循环日志记录——即错误处理本身会产生新错误。添加保护措施:同一类型重复日志记录的计数器带阈值(每分钟不超过 100 条相同消息)和超过阈值后的时间锁定。
不是必须的,但建议这样做。gzip 将文本日志压缩 10–20 倍而不丢失数据。在移动设备上,压缩将占用的空间从 50 MB 减少到 3–5 MB。唯一的缺点——归档文件无法在不解压缩的情况下读取,但分析通常只需要当前文件。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。