123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279 |
- // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
- //
- // This Source Code Form is subject to the terms of the MIT License.
- // If a copy of the MIT was not distributed with this file,
- // You can obtain one at https://github.com/gogf/gf.
- package glog
- import (
- "fmt"
- "github.com/gogf/gf/container/garray"
- "github.com/gogf/gf/encoding/gcompress"
- "github.com/gogf/gf/internal/intlog"
- "github.com/gogf/gf/os/gfile"
- "github.com/gogf/gf/os/gmlock"
- "github.com/gogf/gf/os/gtime"
- "github.com/gogf/gf/os/gtimer"
- "github.com/gogf/gf/text/gregex"
- "time"
- )
- const (
- memoryLockPrefixForRotating = "glog.rotateChecksTimely:"
- )
- // rotateFileBySize rotates the current logging file according to the
- // configured rotation size.
- func (l *Logger) rotateFileBySize(now time.Time) {
- if l.config.RotateSize <= 0 {
- return
- }
- if err := l.doRotateFile(l.getFilePath(now)); err != nil {
- // panic(err)
- intlog.Error(l.ctx, err)
- }
- }
- // doRotateFile rotates the given logging file.
- func (l *Logger) doRotateFile(filePath string) error {
- memoryLockKey := "glog.doRotateFile:" + filePath
- if !gmlock.TryLock(memoryLockKey) {
- return nil
- }
- defer gmlock.Unlock(memoryLockKey)
- intlog.PrintFunc(l.ctx, func() string {
- return fmt.Sprintf(`start rotating file by size: %s, file: %s`, gfile.SizeFormat(filePath), filePath)
- })
- defer intlog.PrintFunc(l.ctx, func() string {
- return fmt.Sprintf(`done rotating file by size: %s, size: %s`, gfile.SizeFormat(filePath), filePath)
- })
- // No backups, it then just removes the current logging file.
- if l.config.RotateBackupLimit == 0 {
- if err := gfile.Remove(filePath); err != nil {
- return err
- }
- intlog.Printf(
- l.ctx,
- `%d size exceeds, no backups set, remove original logging file: %s`,
- l.config.RotateSize, filePath,
- )
- return nil
- }
- // Else it creates new backup files.
- var (
- dirPath = gfile.Dir(filePath)
- fileName = gfile.Name(filePath)
- fileExtName = gfile.ExtName(filePath)
- newFilePath = ""
- )
- // Rename the logging file by adding extra datetime information to microseconds, like:
- // access.log -> access.20200326101301899002.log
- // access.20200326.log -> access.20200326.20200326101301899002.log
- for {
- var (
- now = gtime.Now()
- micro = now.Microsecond() % 1000
- )
- if micro == 0 {
- micro = 101
- } else {
- for micro < 100 {
- micro *= 10
- }
- }
- newFilePath = gfile.Join(
- dirPath,
- fmt.Sprintf(
- `%s.%s%d.%s`,
- fileName, now.Format("YmdHisu"), micro, fileExtName,
- ),
- )
- if !gfile.Exists(newFilePath) {
- break
- } else {
- intlog.Printf(l.ctx, `rotation file exists, continue: %s`, newFilePath)
- }
- }
- intlog.Printf(l.ctx, "rotating file by size from %s to %s", filePath, newFilePath)
- if err := gfile.Rename(filePath, newFilePath); err != nil {
- return err
- }
- return nil
- }
- // rotateChecksTimely timely checks the backups expiration and the compression.
- func (l *Logger) rotateChecksTimely() {
- defer gtimer.AddOnce(l.config.RotateCheckInterval, l.rotateChecksTimely)
- // Checks whether file rotation not enabled.
- if l.config.RotateSize <= 0 && l.config.RotateExpire == 0 {
- intlog.Printf(
- l.ctx,
- "logging rotation ignore checks: RotateSize: %d, RotateExpire: %s",
- l.config.RotateSize, l.config.RotateExpire.String(),
- )
- return
- }
- // It here uses memory lock to guarantee the concurrent safety.
- memoryLockKey := memoryLockPrefixForRotating + l.config.Path
- if !gmlock.TryLock(memoryLockKey) {
- return
- }
- defer gmlock.Unlock(memoryLockKey)
- var (
- now = time.Now()
- pattern = "*.log, *.gz"
- files, err = gfile.ScanDirFile(l.config.Path, pattern, true)
- )
- if err != nil {
- intlog.Error(l.ctx, err)
- }
- intlog.Printf(l.ctx, "logging rotation start checks: %+v", files)
- // =============================================================
- // Rotation of expired file checks.
- // =============================================================
- if l.config.RotateExpire > 0 {
- var (
- mtime time.Time
- subDuration time.Duration
- expireRotated bool
- )
- for _, file := range files {
- if gfile.ExtName(file) == "gz" {
- continue
- }
- mtime = gfile.MTime(file)
- subDuration = now.Sub(mtime)
- if subDuration > l.config.RotateExpire {
- expireRotated = true
- intlog.Printf(
- l.ctx,
- `%v - %v = %v > %v, rotation expire logging file: %s`,
- now, mtime, subDuration, l.config.RotateExpire, file,
- )
- if err := l.doRotateFile(file); err != nil {
- intlog.Error(l.ctx, err)
- }
- }
- }
- if expireRotated {
- // Update the files array.
- files, err = gfile.ScanDirFile(l.config.Path, pattern, true)
- if err != nil {
- intlog.Error(l.ctx, err)
- }
- }
- }
- // =============================================================
- // Rotated file compression.
- // =============================================================
- needCompressFileArray := garray.NewStrArray()
- if l.config.RotateBackupCompress > 0 {
- for _, file := range files {
- // Eg: access.20200326101301899002.log.gz
- if gfile.ExtName(file) == "gz" {
- continue
- }
- // Eg:
- // access.20200326101301899002.log
- if gregex.IsMatchString(`.+\.\d{20}\.log`, gfile.Basename(file)) {
- needCompressFileArray.Append(file)
- }
- }
- if needCompressFileArray.Len() > 0 {
- needCompressFileArray.Iterator(func(_ int, path string) bool {
- err := gcompress.GzipFile(path, path+".gz")
- if err == nil {
- intlog.Printf(l.ctx, `compressed done, remove original logging file: %s`, path)
- if err = gfile.Remove(path); err != nil {
- intlog.Print(l.ctx, err)
- }
- } else {
- intlog.Print(l.ctx, err)
- }
- return true
- })
- // Update the files array.
- files, err = gfile.ScanDirFile(l.config.Path, pattern, true)
- if err != nil {
- intlog.Error(l.ctx, err)
- }
- }
- }
- // =============================================================
- // Backups count limitation and expiration checks.
- // =============================================================
- var (
- backupFilesMap = make(map[string]*garray.SortedArray)
- originalLoggingFilePath = ""
- )
- 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
- })
- }
- // Check if this file a rotated/backup file.
- if gregex.IsMatchString(`.+\.\d{20}\.log`, gfile.Basename(file)) {
- backupFilesMap[originalLoggingFilePath].Add(file)
- }
- }
- intlog.Printf(l.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(l.ctx, `remove exceeded backup limit file: %s`, path)
- if err := gfile.Remove(path.(string)); err != nil {
- intlog.Error(l.ctx, err)
- }
- }
- }
- // Backups expiration checking.
- if l.config.RotateBackupExpire > 0 {
- var (
- 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(
- l.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.Error(l.ctx, err)
- }
- return true
- } else {
- return false
- }
- })
- }
- }
- }
- }
|