rotatefilewriter.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. package loggerutils
  2. import (
  3. "os"
  4. "strconv"
  5. "sync"
  6. "github.com/docker/docker/daemon/logger"
  7. "github.com/docker/docker/pkg/pubsub"
  8. "github.com/pkg/errors"
  9. )
  10. // RotateFileWriter is Logger implementation for default Docker logging.
  11. type RotateFileWriter struct {
  12. f *os.File // store for closing
  13. closed bool
  14. mu sync.Mutex
  15. capacity int64 //maximum size of each file
  16. currentSize int64 // current size of the latest file
  17. maxFiles int //maximum number of files
  18. notifyRotate *pubsub.Publisher
  19. marshal logger.MarshalFunc
  20. }
  21. //NewRotateFileWriter creates new RotateFileWriter
  22. func NewRotateFileWriter(logPath string, capacity int64, maxFiles int, marshaller logger.MarshalFunc) (*RotateFileWriter, error) {
  23. log, err := os.OpenFile(logPath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0640)
  24. if err != nil {
  25. return nil, err
  26. }
  27. size, err := log.Seek(0, os.SEEK_END)
  28. if err != nil {
  29. return nil, err
  30. }
  31. return &RotateFileWriter{
  32. f: log,
  33. capacity: capacity,
  34. currentSize: size,
  35. maxFiles: maxFiles,
  36. notifyRotate: pubsub.NewPublisher(0, 1),
  37. marshal: marshaller,
  38. }, nil
  39. }
  40. // WriteLogEntry writes the provided log message to the current log file.
  41. // This may trigger a rotation event if the max file/capacity limits are hit.
  42. func (w *RotateFileWriter) WriteLogEntry(msg *logger.Message) error {
  43. b, err := w.marshal(msg)
  44. if err != nil {
  45. return errors.Wrap(err, "error marshalling log message")
  46. }
  47. logger.PutMessage(msg)
  48. w.mu.Lock()
  49. if w.closed {
  50. w.mu.Unlock()
  51. return errors.New("cannot write because the output file was closed")
  52. }
  53. if err := w.checkCapacityAndRotate(); err != nil {
  54. w.mu.Unlock()
  55. return err
  56. }
  57. n, err := w.f.Write(b)
  58. if err == nil {
  59. w.currentSize += int64(n)
  60. }
  61. w.mu.Unlock()
  62. return err
  63. }
  64. func (w *RotateFileWriter) checkCapacityAndRotate() error {
  65. if w.capacity == -1 {
  66. return nil
  67. }
  68. if w.currentSize >= w.capacity {
  69. name := w.f.Name()
  70. if err := w.f.Close(); err != nil {
  71. return errors.Wrap(err, "error closing file")
  72. }
  73. if err := rotate(name, w.maxFiles); err != nil {
  74. return err
  75. }
  76. file, err := os.OpenFile(name, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0640)
  77. if err != nil {
  78. return err
  79. }
  80. w.f = file
  81. w.currentSize = 0
  82. w.notifyRotate.Publish(struct{}{})
  83. }
  84. return nil
  85. }
  86. func rotate(name string, maxFiles int) error {
  87. if maxFiles < 2 {
  88. return nil
  89. }
  90. for i := maxFiles - 1; i > 1; i-- {
  91. toPath := name + "." + strconv.Itoa(i)
  92. fromPath := name + "." + strconv.Itoa(i-1)
  93. if err := os.Rename(fromPath, toPath); err != nil && !os.IsNotExist(err) {
  94. return errors.Wrap(err, "error rotating old log entries")
  95. }
  96. }
  97. if err := os.Rename(name, name+".1"); err != nil && !os.IsNotExist(err) {
  98. return errors.Wrap(err, "error rotating current log")
  99. }
  100. return nil
  101. }
  102. // LogPath returns the location the given writer logs to.
  103. func (w *RotateFileWriter) LogPath() string {
  104. w.mu.Lock()
  105. defer w.mu.Unlock()
  106. return w.f.Name()
  107. }
  108. // MaxFiles return maximum number of files
  109. func (w *RotateFileWriter) MaxFiles() int {
  110. return w.maxFiles
  111. }
  112. //NotifyRotate returns the new subscriber
  113. func (w *RotateFileWriter) NotifyRotate() chan interface{} {
  114. return w.notifyRotate.Subscribe()
  115. }
  116. //NotifyRotateEvict removes the specified subscriber from receiving any more messages.
  117. func (w *RotateFileWriter) NotifyRotateEvict(sub chan interface{}) {
  118. w.notifyRotate.Evict(sub)
  119. }
  120. // Close closes underlying file and signals all readers to stop.
  121. func (w *RotateFileWriter) Close() error {
  122. w.mu.Lock()
  123. defer w.mu.Unlock()
  124. if w.closed {
  125. return nil
  126. }
  127. if err := w.f.Close(); err != nil {
  128. return err
  129. }
  130. w.closed = true
  131. return nil
  132. }