glog_logger_rotate.go 7.1 KB

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