rotatefilewriter.go 3.1 KB

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