2
0

cutter.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. package internal
  2. import (
  3. "os"
  4. "path/filepath"
  5. "sync"
  6. "time"
  7. )
  8. // Cutter 实现 io.Writer 接口
  9. // 用于日志切割, strings.Join([]string{director,layout, formats..., level+".log"}, os.PathSeparator)
  10. type Cutter struct {
  11. level string // 日志级别(debug, info, warn, error, dpanic, panic, fatal)
  12. layout string // 时间格式 2006-01-02 15:04:05
  13. formats []string // 自定义参数([]string{Director,"2006-01-02", "business"(此参数可不写), level+".log"}
  14. director string // 日志文件夹
  15. retentionDay int //日志保留天数
  16. file *os.File // 文件句柄
  17. mutex *sync.RWMutex // 读写锁
  18. }
  19. type CutterOption func(*Cutter)
  20. // CutterWithLayout 时间格式
  21. func CutterWithLayout(layout string) CutterOption {
  22. return func(c *Cutter) {
  23. c.layout = layout
  24. }
  25. }
  26. // CutterWithFormats 格式化参数
  27. func CutterWithFormats(format ...string) CutterOption {
  28. return func(c *Cutter) {
  29. if len(format) > 0 {
  30. c.formats = format
  31. }
  32. }
  33. }
  34. func NewCutter(director string, level string, retentionDay int, options ...CutterOption) *Cutter {
  35. rotate := &Cutter{
  36. level: level,
  37. director: director,
  38. retentionDay: retentionDay,
  39. mutex: new(sync.RWMutex),
  40. }
  41. for i := 0; i < len(options); i++ {
  42. options[i](rotate)
  43. }
  44. return rotate
  45. }
  46. // Write satisfies the io.Writer interface. It writes to the
  47. // appropriate file handle that is currently being used.
  48. // If we have reached rotation time, the target file gets
  49. // automatically rotated, and also purged if necessary.
  50. func (c *Cutter) Write(bytes []byte) (n int, err error) {
  51. c.mutex.Lock()
  52. defer func() {
  53. if c.file != nil {
  54. _ = c.file.Close()
  55. c.file = nil
  56. }
  57. c.mutex.Unlock()
  58. }()
  59. length := len(c.formats)
  60. values := make([]string, 0, 3+length)
  61. values = append(values, c.director)
  62. if c.layout != "" {
  63. values = append(values, time.Now().Format(c.layout))
  64. }
  65. for i := 0; i < length; i++ {
  66. values = append(values, c.formats[i])
  67. }
  68. values = append(values, c.level+".log")
  69. filename := filepath.Join(values...)
  70. director := filepath.Dir(filename)
  71. err = os.MkdirAll(director, os.ModePerm)
  72. if err != nil {
  73. return 0, err
  74. }
  75. err = removeNDaysFolders(c.director, c.retentionDay)
  76. if err != nil {
  77. return 0, err
  78. }
  79. c.file, err = os.OpenFile(filename, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
  80. if err != nil {
  81. return 0, err
  82. }
  83. return c.file.Write(bytes)
  84. }
  85. func (c *Cutter) Sync() error {
  86. c.mutex.Lock()
  87. defer c.mutex.Unlock()
  88. if c.file != nil {
  89. return c.file.Sync()
  90. }
  91. return nil
  92. }
  93. // 增加日志目录文件清理 小于等于零的值默认忽略不再处理
  94. func removeNDaysFolders(dir string, days int) error {
  95. if days <= 0 {
  96. return nil
  97. }
  98. cutoff := time.Now().AddDate(0, 0, -days)
  99. return filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
  100. if err != nil {
  101. return err
  102. }
  103. if info.IsDir() && info.ModTime().Before(cutoff) && path != dir {
  104. err = os.RemoveAll(path)
  105. if err != nil {
  106. return err
  107. }
  108. }
  109. return nil
  110. })
  111. }