github.com/gogf/gf@v1.16.9/os/glog/glog_logger_rotate.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/gogf/gf. 6 7 package glog 8 9 import ( 10 "fmt" 11 "github.com/gogf/gf/container/garray" 12 "github.com/gogf/gf/encoding/gcompress" 13 "github.com/gogf/gf/internal/intlog" 14 "github.com/gogf/gf/os/gfile" 15 "github.com/gogf/gf/os/gmlock" 16 "github.com/gogf/gf/os/gtime" 17 "github.com/gogf/gf/os/gtimer" 18 "github.com/gogf/gf/text/gregex" 19 "time" 20 ) 21 22 const ( 23 memoryLockPrefixForRotating = "glog.rotateChecksTimely:" 24 ) 25 26 // rotateFileBySize rotates the current logging file according to the 27 // configured rotation size. 28 func (l *Logger) rotateFileBySize(now time.Time) { 29 if l.config.RotateSize <= 0 { 30 return 31 } 32 if err := l.doRotateFile(l.getFilePath(now)); err != nil { 33 // panic(err) 34 intlog.Error(l.ctx, err) 35 } 36 } 37 38 // doRotateFile rotates the given logging file. 39 func (l *Logger) doRotateFile(filePath string) error { 40 memoryLockKey := "glog.doRotateFile:" + filePath 41 if !gmlock.TryLock(memoryLockKey) { 42 return nil 43 } 44 defer gmlock.Unlock(memoryLockKey) 45 46 intlog.PrintFunc(l.ctx, func() string { 47 return fmt.Sprintf(`start rotating file by size: %s, file: %s`, gfile.SizeFormat(filePath), filePath) 48 }) 49 defer intlog.PrintFunc(l.ctx, func() string { 50 return fmt.Sprintf(`done rotating file by size: %s, size: %s`, gfile.SizeFormat(filePath), filePath) 51 }) 52 53 // No backups, it then just removes the current logging file. 54 if l.config.RotateBackupLimit == 0 { 55 if err := gfile.Remove(filePath); err != nil { 56 return err 57 } 58 intlog.Printf( 59 l.ctx, 60 `%d size exceeds, no backups set, remove original logging file: %s`, 61 l.config.RotateSize, filePath, 62 ) 63 return nil 64 } 65 // Else it creates new backup files. 66 var ( 67 dirPath = gfile.Dir(filePath) 68 fileName = gfile.Name(filePath) 69 fileExtName = gfile.ExtName(filePath) 70 newFilePath = "" 71 ) 72 // Rename the logging file by adding extra datetime information to microseconds, like: 73 // access.log -> access.20200326101301899002.log 74 // access.20200326.log -> access.20200326.20200326101301899002.log 75 for { 76 var ( 77 now = gtime.Now() 78 micro = now.Microsecond() % 1000 79 ) 80 if micro == 0 { 81 micro = 101 82 } else { 83 for micro < 100 { 84 micro *= 10 85 } 86 } 87 newFilePath = gfile.Join( 88 dirPath, 89 fmt.Sprintf( 90 `%s.%s%d.%s`, 91 fileName, now.Format("YmdHisu"), micro, fileExtName, 92 ), 93 ) 94 if !gfile.Exists(newFilePath) { 95 break 96 } else { 97 intlog.Printf(l.ctx, `rotation file exists, continue: %s`, newFilePath) 98 } 99 } 100 intlog.Printf(l.ctx, "rotating file by size from %s to %s", filePath, newFilePath) 101 if err := gfile.Rename(filePath, newFilePath); err != nil { 102 return err 103 } 104 return nil 105 } 106 107 // rotateChecksTimely timely checks the backups expiration and the compression. 108 func (l *Logger) rotateChecksTimely() { 109 defer gtimer.AddOnce(l.config.RotateCheckInterval, l.rotateChecksTimely) 110 111 // Checks whether file rotation not enabled. 112 if l.config.RotateSize <= 0 && l.config.RotateExpire == 0 { 113 intlog.Printf( 114 l.ctx, 115 "logging rotation ignore checks: RotateSize: %d, RotateExpire: %s", 116 l.config.RotateSize, l.config.RotateExpire.String(), 117 ) 118 return 119 } 120 121 // It here uses memory lock to guarantee the concurrent safety. 122 memoryLockKey := memoryLockPrefixForRotating + l.config.Path 123 if !gmlock.TryLock(memoryLockKey) { 124 return 125 } 126 defer gmlock.Unlock(memoryLockKey) 127 128 var ( 129 now = time.Now() 130 pattern = "*.log, *.gz" 131 files, err = gfile.ScanDirFile(l.config.Path, pattern, true) 132 ) 133 if err != nil { 134 intlog.Error(l.ctx, err) 135 } 136 intlog.Printf(l.ctx, "logging rotation start checks: %+v", files) 137 // ============================================================= 138 // Rotation of expired file checks. 139 // ============================================================= 140 if l.config.RotateExpire > 0 { 141 var ( 142 mtime time.Time 143 subDuration time.Duration 144 expireRotated bool 145 ) 146 for _, file := range files { 147 if gfile.ExtName(file) == "gz" { 148 continue 149 } 150 mtime = gfile.MTime(file) 151 subDuration = now.Sub(mtime) 152 if subDuration > l.config.RotateExpire { 153 expireRotated = true 154 intlog.Printf( 155 l.ctx, 156 `%v - %v = %v > %v, rotation expire logging file: %s`, 157 now, mtime, subDuration, l.config.RotateExpire, file, 158 ) 159 if err := l.doRotateFile(file); err != nil { 160 intlog.Error(l.ctx, err) 161 } 162 } 163 } 164 if expireRotated { 165 // Update the files array. 166 files, err = gfile.ScanDirFile(l.config.Path, pattern, true) 167 if err != nil { 168 intlog.Error(l.ctx, err) 169 } 170 } 171 } 172 173 // ============================================================= 174 // Rotated file compression. 175 // ============================================================= 176 needCompressFileArray := garray.NewStrArray() 177 if l.config.RotateBackupCompress > 0 { 178 for _, file := range files { 179 // Eg: access.20200326101301899002.log.gz 180 if gfile.ExtName(file) == "gz" { 181 continue 182 } 183 // Eg: 184 // access.20200326101301899002.log 185 if gregex.IsMatchString(`.+\.\d{20}\.log`, gfile.Basename(file)) { 186 needCompressFileArray.Append(file) 187 } 188 } 189 if needCompressFileArray.Len() > 0 { 190 needCompressFileArray.Iterator(func(_ int, path string) bool { 191 err := gcompress.GzipFile(path, path+".gz") 192 if err == nil { 193 intlog.Printf(l.ctx, `compressed done, remove original logging file: %s`, path) 194 if err = gfile.Remove(path); err != nil { 195 intlog.Print(l.ctx, err) 196 } 197 } else { 198 intlog.Print(l.ctx, err) 199 } 200 return true 201 }) 202 // Update the files array. 203 files, err = gfile.ScanDirFile(l.config.Path, pattern, true) 204 if err != nil { 205 intlog.Error(l.ctx, err) 206 } 207 } 208 } 209 210 // ============================================================= 211 // Backups count limitation and expiration checks. 212 // ============================================================= 213 var ( 214 backupFilesMap = make(map[string]*garray.SortedArray) 215 originalLoggingFilePath = "" 216 ) 217 if l.config.RotateBackupLimit > 0 || l.config.RotateBackupExpire > 0 { 218 for _, file := range files { 219 originalLoggingFilePath, _ = gregex.ReplaceString(`\.\d{20}`, "", file) 220 if backupFilesMap[originalLoggingFilePath] == nil { 221 backupFilesMap[originalLoggingFilePath] = garray.NewSortedArray(func(a, b interface{}) int { 222 // Sorted by rotated/backup file mtime. 223 // The older rotated/backup file is put in the head of array. 224 var ( 225 file1 = a.(string) 226 file2 = b.(string) 227 result = gfile.MTimestampMilli(file1) - gfile.MTimestampMilli(file2) 228 ) 229 if result <= 0 { 230 return -1 231 } 232 return 1 233 }) 234 } 235 // Check if this file a rotated/backup file. 236 if gregex.IsMatchString(`.+\.\d{20}\.log`, gfile.Basename(file)) { 237 backupFilesMap[originalLoggingFilePath].Add(file) 238 } 239 } 240 intlog.Printf(l.ctx, `calculated backup files map: %+v`, backupFilesMap) 241 for _, array := range backupFilesMap { 242 diff := array.Len() - l.config.RotateBackupLimit 243 for i := 0; i < diff; i++ { 244 path, _ := array.PopLeft() 245 intlog.Printf(l.ctx, `remove exceeded backup limit file: %s`, path) 246 if err := gfile.Remove(path.(string)); err != nil { 247 intlog.Error(l.ctx, err) 248 } 249 } 250 } 251 // Backups expiration checking. 252 if l.config.RotateBackupExpire > 0 { 253 var ( 254 mtime time.Time 255 subDuration time.Duration 256 ) 257 for _, array := range backupFilesMap { 258 array.Iterator(func(_ int, v interface{}) bool { 259 path := v.(string) 260 mtime = gfile.MTime(path) 261 subDuration = now.Sub(mtime) 262 if subDuration > l.config.RotateBackupExpire { 263 intlog.Printf( 264 l.ctx, 265 `%v - %v = %v > %v, remove expired backup file: %s`, 266 now, mtime, subDuration, l.config.RotateBackupExpire, path, 267 ) 268 if err := gfile.Remove(path); err != nil { 269 intlog.Error(l.ctx, err) 270 } 271 return true 272 } else { 273 return false 274 } 275 }) 276 } 277 } 278 } 279 }