rotatefilewriter.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. package loggerutils
  2. import (
  3. "os"
  4. "strconv"
  5. "sync"
  6. "github.com/docker/docker/pkg/pubsub"
  7. )
  8. // RotateFileWriter is Logger implementation for default Docker logging.
  9. type RotateFileWriter struct {
  10. f *os.File // store for closing
  11. mu sync.Mutex
  12. capacity int64 //maximum size of each file
  13. currentSize int64 // current size of the latest file
  14. maxFiles int //maximum number of files
  15. notifyRotate *pubsub.Publisher
  16. }
  17. //NewRotateFileWriter creates new RotateFileWriter
  18. func NewRotateFileWriter(logPath string, capacity int64, maxFiles int) (*RotateFileWriter, error) {
  19. log, err := os.OpenFile(logPath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0640)
  20. if err != nil {
  21. return nil, err
  22. }
  23. size, err := log.Seek(0, os.SEEK_END)
  24. if err != nil {
  25. return nil, err
  26. }
  27. return &RotateFileWriter{
  28. f: log,
  29. capacity: capacity,
  30. currentSize: size,
  31. maxFiles: maxFiles,
  32. notifyRotate: pubsub.NewPublisher(0, 1),
  33. }, nil
  34. }
  35. //WriteLog write log message to File
  36. func (w *RotateFileWriter) Write(message []byte) (int, error) {
  37. w.mu.Lock()
  38. if err := w.checkCapacityAndRotate(); err != nil {
  39. w.mu.Unlock()
  40. return -1, err
  41. }
  42. n, err := w.f.Write(message)
  43. if err == nil {
  44. w.currentSize += int64(n)
  45. }
  46. w.mu.Unlock()
  47. return n, err
  48. }
  49. func (w *RotateFileWriter) checkCapacityAndRotate() error {
  50. if w.capacity == -1 {
  51. return nil
  52. }
  53. if w.currentSize >= w.capacity {
  54. name := w.f.Name()
  55. if err := w.f.Close(); err != nil {
  56. return err
  57. }
  58. if err := rotate(name, w.maxFiles); err != nil {
  59. return err
  60. }
  61. file, err := os.OpenFile(name, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 06400)
  62. if err != nil {
  63. return err
  64. }
  65. w.f = file
  66. w.currentSize = 0
  67. w.notifyRotate.Publish(struct{}{})
  68. }
  69. return nil
  70. }
  71. func rotate(name string, maxFiles int) error {
  72. if maxFiles < 2 {
  73. return nil
  74. }
  75. for i := maxFiles - 1; i > 1; i-- {
  76. toPath := name + "." + strconv.Itoa(i)
  77. fromPath := name + "." + strconv.Itoa(i-1)
  78. if err := os.Rename(fromPath, toPath); err != nil && !os.IsNotExist(err) {
  79. return err
  80. }
  81. }
  82. if err := os.Rename(name, name+".1"); err != nil && !os.IsNotExist(err) {
  83. return err
  84. }
  85. return nil
  86. }
  87. // LogPath returns the location the given writer logs to.
  88. func (w *RotateFileWriter) LogPath() string {
  89. return w.f.Name()
  90. }
  91. // MaxFiles return maximum number of files
  92. func (w *RotateFileWriter) MaxFiles() int {
  93. return w.maxFiles
  94. }
  95. //NotifyRotate returns the new subscriber
  96. func (w *RotateFileWriter) NotifyRotate() chan interface{} {
  97. return w.notifyRotate.Subscribe()
  98. }
  99. //NotifyRotateEvict removes the specified subscriber from receiving any more messages.
  100. func (w *RotateFileWriter) NotifyRotateEvict(sub chan interface{}) {
  101. w.notifyRotate.Evict(sub)
  102. }
  103. // Close closes underlying file and signals all readers to stop.
  104. func (w *RotateFileWriter) Close() error {
  105. return w.f.Close()
  106. }