Documentation
¶
Overview ¶
Package logrotate 提供日志文件写入、切割、压缩和清理能力。
使用方式:
import "github.com/gtkit/logrotate"
logrotate 只负责日志文件输出端的管理,不负责日志格式化、日志级别或字段编码。 它可以作为任何接收 io.Writer 的日志库输出目标,例如标准库 log、slog、 zap 和 logrus。
同一份日志文件只能由一个进程写入。多个进程使用相同配置写入同一文件会导致 不正确的轮转行为。
使用约束与已知限制 ¶
以下都是有意的设计取舍或该类库的固有约束:
- 配置字段不可热改:导出字段必须在首次 Open/Write/Rotate/Cleanup 之前设置好, 运行中不要与这些操作并发修改,否则会发生 data race。需要不同配置请新建 Logger。
- Open 是同步的:它会同步清理并压缩历史积压文件,积压多时启动会变慢,属于预期行为。 不想承担该开销时可以不调用 Open,让首次 Write 走后台异步清理。
- 后台清理是全局单 worker:所有 Logger 实例共享一个后台清理/压缩 goroutine,多实例 同时压缩大文件会排队。副作用是 Close 等待本实例已调度的后台任务时,若该 worker 正 忙于其他实例的压缩,本实例的 Close 也会一并等待后返回。
- 保留判断只看文件名,不看 modTime:MaxAge 与 MaxBackups 按文件名中的日期/时间戳判断, 不符合本包命名规则的文件会被忽略,既不会被清理也不会被误删。迁移老目录前请确认文件名 符合规则,否则旧文件不会被自动回收。
- 轮转与清理由写入驱动:跨天切割、旧日志清理和压缩只在 Write、Rotate、Open 或 Cleanup 被调用时触发。跨天后若没有任何写入,旧文件保持原样;需要即时切割时 可自行调用 Rotate。
- 同一份日志文件只能单进程写:本包不加文件锁,多进程共用同一 Filename 会导致轮转竞争。
- 同步清理无超时:Open 与 Close 的清理/压缩没有超时控制,磁盘 IO 病态时理论上会阻塞, 属于已知边界。
Example ¶
New 可以用 Functional Options 创建 Logger。
l := New(
WithFilename("/var/log/myapp/foo.log"),
WithMaxSize(500),
WithMaxBackups(3),
WithMaxAge(28),
WithCompress(true),
)
fmt.Println(l.Filename, l.MaxSize, l.MaxBackups, l.MaxAge, l.Compress)
Output: /var/log/myapp/foo.log 500 3 28 true
Index ¶
- Constants
- type Logger
- type Option
- func WithCompress(compress bool) Option
- func WithDaily(daily bool) Option
- func WithDailyFilename(dailyFilename bool) Option
- func WithFilename(filename string) Option
- func WithLocalTime(localTime bool) Option
- func WithLocation(location *time.Location) Option
- func WithMaxAge(maxAge int) Option
- func WithMaxBackups(maxBackups int) Option
- func WithMaxSize(maxSize int) Option
- func WithNow(now func() time.Time) Option
- func WithOnError(onError func(error)) Option
Examples ¶
Constants ¶
const Version = "v1.1.3"
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Logger ¶
type Logger struct {
// Filename 是当前活跃日志文件路径。备份日志会保存在同一目录。
// 为空时使用 os.TempDir() 下的 <进程名>-logrotate.log。
Filename string `json:"filename" yaml:"filename"`
// MaxSize 是单个日志文件轮转前的最大大小,单位 MB。默认值为 100。
// 小于等于 0 时按未设置处理,使用默认值。
MaxSize int `json:"maxsize" yaml:"maxsize"`
// MaxAge 是旧日志最多保留天数,基于文件名里的时间戳计算。
// 一天按 24 小时计算,可能与夏令时、闰秒等日历边界不完全一致。
// 默认不按时间删除旧日志。
MaxAge int `json:"maxage" yaml:"maxage"`
// MaxBackups 是最多保留的旧日志数量。默认保留全部旧日志,但 MaxAge 仍可能删除它们。
MaxBackups int `json:"maxbackups" yaml:"maxbackups"`
// LocalTime 控制备份时间戳和日期文件名是否使用本地时间。默认使用 UTC。
LocalTime bool `json:"localtime" yaml:"localtime"`
// Compress 控制轮转后的旧日志是否使用 gzip 压缩。默认不压缩。
Compress bool `json:"compress" yaml:"compress"`
// Daily 控制是否在日期变化时轮转日志文件。日期边界默认按 UTC 判断,
// LocalTime 为 true 时按本地时区判断。默认不按天轮转。
// 与 DailyFilename 同时开启时,DailyFilename 生效,Daily 不再单独触发轮转。
Daily bool `json:"daily" yaml:"daily"`
// DailyFilename 控制活跃日志文件名是否包含当前日期,格式为 name-2006-01-02.ext。
// 日期默认使用 UTC,LocalTime 为 true 时使用本地时区。MaxSize 在每天的文件内仍然生效。
DailyFilename bool `json:"dailyfilename" yaml:"dailyfilename"`
// Now 返回当前时间。为空时使用 time.Now。
// 主要用于测试或需要固定业务时间源的场景。请在首次使用前设置。
Now func() time.Time `json:"-" yaml:"-"`
// Location 控制备份时间戳、日期文件名、日期边界和 MaxAge 截止时间使用的时区。
// 非空时优先于 LocalTime;为空且 LocalTime 为 true 时使用 time.Local;否则使用 UTC。
Location *time.Location `json:"-" yaml:"-"`
// OnError 接收后台清理、压缩或 panic recover 产生的错误。
// 为空时后台错误保持非致命并被忽略。Cleanup 返回的同步错误不会重复调用 OnError。
// 回调在后台清理 goroutine 中执行,回调内不要调用本 Logger 的 Close:
// Close 会等待该后台任务完成,而任务要等回调返回才算完成,互相等待造成死锁。
OnError func(error) `json:"-" yaml:"-"`
// contains filtered or unexported fields
}
Logger 是写入指定日志文件的 io.WriteCloser。
Logger 会在首次 Write 时打开或创建日志文件。如果文件已存在且追加本次写入后 不超过 MaxSize 兆字节,Logger 会追加写入该文件;否则会在文件扩展名前加入 当前时间戳作为备份文件名,并用原始文件名创建新的日志文件。
当一次写入会导致当前日志文件超过 MaxSize 时,Logger 会关闭当前文件、重命名为 备份文件,并用原始文件名创建新文件。因此 Filename 始终表示当前活跃日志文件。
如果 Daily 为 true,Logger 会在日期变化时轮转当前文件。日期边界默认使用 UTC, LocalTime 为 true 时使用本地时间。MaxSize 仍然生效,因此可以同时按天和按大小 轮转。
如果 DailyFilename 为 true,活跃日志文件名会包含当前日期,格式为 `name-2006-01-02.ext`。跨天时 Logger 会关闭旧日期文件,并开始写入新日期文件。 MaxSize 在每天的文件内仍然生效,按大小生成的备份仍使用普通时间戳格式。
备份文件使用 Logger 的日志文件名生成,格式为 `name-timestamp.ext`。其中 name 是不含扩展名的文件名,timestamp 是轮转时间,格式为 `2006-01-02T15-04-05.000`, ext 是原始扩展名。例如 Filename 为 `/var/log/foo/server.log` 时,备份文件可能是 `/var/log/foo/server-2026-06-09T10-30-00.000.log`。同一毫秒内多次轮转时, 时间戳按毫秒递增以保证备份名唯一,不会覆盖已有备份。
清理旧日志文件 ¶
每次创建新日志文件后,Logger 可能会删除旧日志。按文件名里的时间戳排序后,最多 保留 MaxBackups 组旧日志;MaxBackups 为 0 时不按数量删除。时间戳早于 MaxAge 天的旧日志会被删除;MaxAge 为 0 时不按时间删除。文件名里的时间是轮转时间, 可能不同于该文件最后一次写入的时间。
如果 MaxBackups 和 MaxAge 都是 0,不会自动删除旧日志。
Logger 会串行化并发写入,以保证大小统计、轮转判断和写入顺序一致。请在首次使用前 设置好导出的配置字段,使用过程中不要与 Write、Rotate 或 Close 并发修改这些字段。
Logger 内部持有锁,首次使用后不得复制;请始终通过指针(*Logger)传递和使用。
func New ¶ added in v1.1.2
New 返回按 opts 配置的 Logger。
不传选项时,New 返回的 Logger 与 &Logger{} 使用相同默认行为。nil 选项会被忽略。
func (*Logger) Cleanup ¶ added in v1.1.0
Cleanup 同步执行一次旧日志压缩和清理,并返回遇到的第一个错误。
Cleanup 不通过后台 worker 调度,也不会为返回的同步错误调用 OnError。需要启动即回收 历史积压文件时,应用可以在完成配置后显式调用 Cleanup 或 Open。
func (*Logger) Close ¶
Close 实现 io.Closer,关闭当前打开的日志文件,并等待本 Logger 已调度的后台 清理与压缩任务全部完成后才返回。
因此 Close 返回后,由本 Logger 触发的旧日志删除和压缩都已结束处理(清理或压缩 自身的失败会被忽略,不保证一定成功),不会有清理 goroutine 在 Close 之后继续运行。 Close 不会使 Logger 进入终止状态,之后的 Write 可以按现有配置重新打开日志文件。 Close 期间不要并发调用 Write 或 Rotate。
func (*Logger) CurrentFilename ¶ added in v1.1.0
CurrentFilename 返回当前活跃日志文件路径。
DailyFilename 关闭时返回 Filename 或默认文件名;DailyFilename 开启时返回当前日期对应 的活跃文件名。若内部已经切换到某个日期文件,则返回该文件名。
func (*Logger) Open ¶ added in v1.1.0
Open 打开或创建当前活跃日志文件,并同步执行一次旧日志清理/压缩。
调用 Open 可在应用启动时初始化目录、创建当前文件并清理历史积压文件,而无需等待首次 Write。Open 不会使 Logger 进入终止状态;Close 后仍可再次 Open 或 Write。 Open 期间不要并发调用 Write、Rotate 或 Close。
func (*Logger) Rotate ¶
Rotate 关闭现有日志文件并立即创建新文件。 应用可以用它在正常轮转规则之外主动触发轮转,例如响应 SIGHUP。 轮转后,Logger 会按配置触发旧日志压缩和清理。
Example ¶
响应 SIGHUP 时可以主动轮转日志文件。
l := &Logger{}
log.SetOutput(l)
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP)
go func() {
for {
<-c
if err := l.Rotate(); err != nil {
log.Printf("rotate log: %v", err)
}
}
}()
type Option ¶ added in v1.1.2
type Option func(*Logger)
Option 配置 New 创建的 Logger。
func WithCompress ¶ added in v1.1.2
WithCompress 设置轮转后的旧日志是否使用 gzip 压缩。
func WithDailyFilename ¶ added in v1.1.2
WithDailyFilename 设置活跃日志文件名是否包含当前日期。
func WithFilename ¶ added in v1.1.2
WithFilename 设置当前活跃日志文件路径。
func WithLocalTime ¶ added in v1.1.2
WithLocalTime 设置轮转时间和日期文件名是否使用本地时间。
func WithLocation ¶ added in v1.1.2
WithLocation 设置轮转时间、日期文件名、日期边界和 MaxAge 截止时间使用的时区。
func WithMaxAge ¶ added in v1.1.2
WithMaxAge 设置旧日志最多保留天数。 负数会按 0 处理,即不按时间删除。
func WithMaxBackups ¶ added in v1.1.2
WithMaxBackups 设置最多保留的旧日志数量。 负数会按 0 处理,即不按数量删除。
func WithMaxSize ¶ added in v1.1.2
WithMaxSize 设置单个日志文件轮转前的最大大小,单位 MB。 负数会按 0 处理,即使用 Logger 的默认大小。
func WithOnError ¶ added in v1.1.2
WithOnError 设置后台清理、压缩或 panic recover 产生错误时的回调。