mirror of
https://gitee.com/johng/gf
synced 2026-06-06 02:25:47 +08:00
feat(glog): add log rotation support for short-running process (#2658)
This commit is contained in:
@ -27,7 +27,6 @@ import (
|
||||
"github.com/gogf/gf/v2/os/gfpool"
|
||||
"github.com/gogf/gf/v2/os/gmlock"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/os/gtimer"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
@ -100,7 +99,7 @@ func (l *Logger) print(ctx context.Context, level int, stack string, values ...i
|
||||
// It just initializes once for each logger.
|
||||
if l.config.RotateSize > 0 || l.config.RotateExpire > 0 {
|
||||
if !l.config.rotatedHandlerInitialized.Val() && l.config.rotatedHandlerInitialized.Cas(false, true) {
|
||||
gtimer.AddOnce(context.Background(), l.config.RotateCheckInterval, l.rotateChecksTimely)
|
||||
l.rotateChecksTimely(ctx)
|
||||
intlog.Printf(ctx, "logger rotation initialized: every %s", l.config.RotateCheckInterval.String())
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
@ -137,6 +138,11 @@ func (l *Logger) rotateChecksTimely(ctx context.Context) {
|
||||
intlog.Errorf(ctx, `%+v`, err)
|
||||
}
|
||||
intlog.Printf(ctx, "logging rotation start checks: %+v", files)
|
||||
// get file name regex pattern
|
||||
// access-{y-m-d}-test.log => access-$-test.log => access-\$-test\.log => access-(.+?)-test\.log
|
||||
fileNameRegexPattern, _ := gregex.ReplaceString(`{.+?}`, "$", l.config.File)
|
||||
fileNameRegexPattern = gregex.Quote(fileNameRegexPattern)
|
||||
fileNameRegexPattern = strings.ReplaceAll(fileNameRegexPattern, "\\$", "(.+?)")
|
||||
// =============================================================
|
||||
// Rotation of expired file checks.
|
||||
// =============================================================
|
||||
@ -147,7 +153,12 @@ func (l *Logger) rotateChecksTimely(ctx context.Context) {
|
||||
expireRotated bool
|
||||
)
|
||||
for _, file := range files {
|
||||
if gfile.ExtName(file) == "gz" {
|
||||
// ignore backup file
|
||||
if gregex.IsMatchString(`.+\.\d{20}\.log`, gfile.Basename(file)) {
|
||||
continue
|
||||
}
|
||||
// ignore not matching file
|
||||
if !gregex.IsMatchString(fileNameRegexPattern, file) {
|
||||
continue
|
||||
}
|
||||
mtime = gfile.MTime(file)
|
||||
@ -159,19 +170,16 @@ func (l *Logger) rotateChecksTimely(ctx context.Context) {
|
||||
return
|
||||
}
|
||||
defer gmlock.Unlock(memoryLockFileKey)
|
||||
|
||||
fp := l.getOpenedFilePointer(ctx, file)
|
||||
if fp == nil {
|
||||
intlog.Errorf(ctx, `got nil file pointer for: %s`, file)
|
||||
return
|
||||
}
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
fp := l.getOpenedFilePointer(ctx, file)
|
||||
if fp == nil {
|
||||
intlog.Errorf(ctx, `got nil file pointer for: %s`, file)
|
||||
return
|
||||
}
|
||||
if err := fp.Close(true); err != nil {
|
||||
intlog.Errorf(ctx, `%+v`, err)
|
||||
}
|
||||
}
|
||||
|
||||
expireRotated = true
|
||||
intlog.Printf(
|
||||
ctx,
|
||||
@ -203,6 +211,11 @@ func (l *Logger) rotateChecksTimely(ctx context.Context) {
|
||||
if gfile.ExtName(file) == "gz" {
|
||||
continue
|
||||
}
|
||||
// ignore not matching file
|
||||
originalLoggingFilePath, _ := gregex.ReplaceString(`\.\d{20}`, "", file)
|
||||
if !gregex.IsMatchString(fileNameRegexPattern, originalLoggingFilePath) {
|
||||
continue
|
||||
}
|
||||
// Eg:
|
||||
// access.20200326101301899002.log
|
||||
if gregex.IsMatchString(`.+\.\d{20}\.log`, gfile.Basename(file)) {
|
||||
@ -233,42 +246,37 @@ func (l *Logger) rotateChecksTimely(ctx context.Context) {
|
||||
// =============================================================
|
||||
// Backups count limitation and expiration checks.
|
||||
// =============================================================
|
||||
var (
|
||||
backupFilesMap = make(map[string]*garray.SortedArray)
|
||||
originalLoggingFilePath string
|
||||
)
|
||||
backupFiles := garray.NewSortedArray(func(a, b interface{}) int {
|
||||
// Sorted by rotated/backup file mtime.
|
||||
// The older rotated/backup file is put in the head of array.
|
||||
var (
|
||||
file1 = a.(string)
|
||||
file2 = b.(string)
|
||||
result = gfile.MTimestampMilli(file1) - gfile.MTimestampMilli(file2)
|
||||
)
|
||||
if result <= 0 {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
})
|
||||
if l.config.RotateBackupLimit > 0 || l.config.RotateBackupExpire > 0 {
|
||||
for _, file := range files {
|
||||
originalLoggingFilePath, _ = gregex.ReplaceString(`\.\d{20}`, "", file)
|
||||
if backupFilesMap[originalLoggingFilePath] == nil {
|
||||
backupFilesMap[originalLoggingFilePath] = garray.NewSortedArray(func(a, b interface{}) int {
|
||||
// Sorted by rotated/backup file mtime.
|
||||
// The older rotated/backup file is put in the head of array.
|
||||
var (
|
||||
file1 = a.(string)
|
||||
file2 = b.(string)
|
||||
result = gfile.MTimestampMilli(file1) - gfile.MTimestampMilli(file2)
|
||||
)
|
||||
if result <= 0 {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
})
|
||||
// ignore not matching file
|
||||
originalLoggingFilePath, _ := gregex.ReplaceString(`\.\d{20}`, "", file)
|
||||
if !gregex.IsMatchString(fileNameRegexPattern, originalLoggingFilePath) {
|
||||
continue
|
||||
}
|
||||
// Check if this file a rotated/backup file.
|
||||
if gregex.IsMatchString(`.+\.\d{20}\.log`, gfile.Basename(file)) {
|
||||
backupFilesMap[originalLoggingFilePath].Add(file)
|
||||
backupFiles.Add(file)
|
||||
}
|
||||
}
|
||||
intlog.Printf(ctx, `calculated backup files map: %+v`, backupFilesMap)
|
||||
for _, array := range backupFilesMap {
|
||||
diff := array.Len() - l.config.RotateBackupLimit
|
||||
for i := 0; i < diff; i++ {
|
||||
path, _ := array.PopLeft()
|
||||
intlog.Printf(ctx, `remove exceeded backup limit file: %s`, path)
|
||||
if err := gfile.Remove(path.(string)); err != nil {
|
||||
intlog.Errorf(ctx, `%+v`, err)
|
||||
}
|
||||
intlog.Printf(ctx, `calculated backup files array: %+v`, backupFiles)
|
||||
diff := backupFiles.Len() - l.config.RotateBackupLimit
|
||||
for i := 0; i < diff; i++ {
|
||||
path, _ := backupFiles.PopLeft()
|
||||
intlog.Printf(ctx, `remove exceeded backup limit file: %s`, path)
|
||||
if err := gfile.Remove(path.(string)); err != nil {
|
||||
intlog.Errorf(ctx, `%+v`, err)
|
||||
}
|
||||
}
|
||||
// Backups expiration checking.
|
||||
@ -277,26 +285,24 @@ func (l *Logger) rotateChecksTimely(ctx context.Context) {
|
||||
mtime time.Time
|
||||
subDuration time.Duration
|
||||
)
|
||||
for _, array := range backupFilesMap {
|
||||
array.Iterator(func(_ int, v interface{}) bool {
|
||||
path := v.(string)
|
||||
mtime = gfile.MTime(path)
|
||||
subDuration = now.Sub(mtime)
|
||||
if subDuration > l.config.RotateBackupExpire {
|
||||
intlog.Printf(
|
||||
ctx,
|
||||
`%v - %v = %v > %v, remove expired backup file: %s`,
|
||||
now, mtime, subDuration, l.config.RotateBackupExpire, path,
|
||||
)
|
||||
if err := gfile.Remove(path); err != nil {
|
||||
intlog.Errorf(ctx, `%+v`, err)
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
backupFiles.Iterator(func(_ int, v interface{}) bool {
|
||||
path := v.(string)
|
||||
mtime = gfile.MTime(path)
|
||||
subDuration = now.Sub(mtime)
|
||||
if subDuration > l.config.RotateBackupExpire {
|
||||
intlog.Printf(
|
||||
ctx,
|
||||
`%v - %v = %v > %v, remove expired backup file: %s`,
|
||||
now, mtime, subDuration, l.config.RotateBackupExpire, path,
|
||||
)
|
||||
if err := gfile.Remove(path); err != nil {
|
||||
intlog.Errorf(ctx, `%+v`, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user