file.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package log
  5. import (
  6. "errors"
  7. "fmt"
  8. "io/ioutil"
  9. "os"
  10. "path/filepath"
  11. "strings"
  12. "sync"
  13. "time"
  14. "github.com/inconshreveable/log15"
  15. )
  16. // FileLogWriter implements LoggerInterface.
  17. // It writes messages by lines limit, file size limit, or time frequency.
  18. type FileLogWriter struct {
  19. mw *MuxWriter
  20. Format log15.Format
  21. Filename string
  22. Maxlines int
  23. maxlines_curlines int
  24. // Rotate at size
  25. Maxsize int
  26. maxsize_cursize int
  27. // Rotate daily
  28. Daily bool
  29. Maxdays int64
  30. daily_opendate int
  31. Rotate bool
  32. startLock sync.Mutex
  33. }
  34. // an *os.File writer with locker.
  35. type MuxWriter struct {
  36. sync.Mutex
  37. fd *os.File
  38. }
  39. // write to os.File.
  40. func (l *MuxWriter) Write(b []byte) (int, error) {
  41. l.Lock()
  42. defer l.Unlock()
  43. return l.fd.Write(b)
  44. }
  45. // set os.File in writer.
  46. func (l *MuxWriter) SetFd(fd *os.File) {
  47. if l.fd != nil {
  48. l.fd.Close()
  49. }
  50. l.fd = fd
  51. }
  52. // create a FileLogWriter returning as LoggerInterface.
  53. func NewFileWriter() *FileLogWriter {
  54. w := &FileLogWriter{
  55. Filename: "",
  56. Format: log15.LogfmtFormat(),
  57. Maxlines: 1000000,
  58. Maxsize: 1 << 28, //256 MB
  59. Daily: true,
  60. Maxdays: 7,
  61. Rotate: true,
  62. }
  63. // use MuxWriter instead direct use os.File for lock write when rotate
  64. w.mw = new(MuxWriter)
  65. return w
  66. }
  67. func (w *FileLogWriter) Log(r *log15.Record) error {
  68. data := w.Format.Format(r)
  69. w.docheck(len(data))
  70. _, err := w.mw.Write(data)
  71. return err
  72. }
  73. func (w *FileLogWriter) Init() error {
  74. if len(w.Filename) == 0 {
  75. return errors.New("config must have filename")
  76. }
  77. return w.StartLogger()
  78. }
  79. // start file logger. create log file and set to locker-inside file writer.
  80. func (w *FileLogWriter) StartLogger() error {
  81. fd, err := w.createLogFile()
  82. if err != nil {
  83. return err
  84. }
  85. w.mw.SetFd(fd)
  86. if err = w.initFd(); err != nil {
  87. return err
  88. }
  89. return nil
  90. }
  91. func (w *FileLogWriter) docheck(size int) {
  92. w.startLock.Lock()
  93. defer w.startLock.Unlock()
  94. if w.Rotate && ((w.Maxlines > 0 && w.maxlines_curlines >= w.Maxlines) ||
  95. (w.Maxsize > 0 && w.maxsize_cursize >= w.Maxsize) ||
  96. (w.Daily && time.Now().Day() != w.daily_opendate)) {
  97. if err := w.DoRotate(); err != nil {
  98. fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err)
  99. return
  100. }
  101. }
  102. w.maxlines_curlines++
  103. w.maxsize_cursize += size
  104. }
  105. func (w *FileLogWriter) createLogFile() (*os.File, error) {
  106. // Open the log file
  107. return os.OpenFile(w.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
  108. }
  109. func (w *FileLogWriter) initFd() error {
  110. fd := w.mw.fd
  111. finfo, err := fd.Stat()
  112. if err != nil {
  113. return fmt.Errorf("get stat: %s\n", err)
  114. }
  115. w.maxsize_cursize = int(finfo.Size())
  116. w.daily_opendate = time.Now().Day()
  117. if finfo.Size() > 0 {
  118. content, err := ioutil.ReadFile(w.Filename)
  119. if err != nil {
  120. return err
  121. }
  122. w.maxlines_curlines = len(strings.Split(string(content), "\n"))
  123. } else {
  124. w.maxlines_curlines = 0
  125. }
  126. return nil
  127. }
  128. // DoRotate means it need to write file in new file.
  129. // new file name like xx.log.2013-01-01.2
  130. func (w *FileLogWriter) DoRotate() error {
  131. _, err := os.Lstat(w.Filename)
  132. if err == nil { // file exists
  133. // Find the next available number
  134. num := 1
  135. fname := ""
  136. for ; err == nil && num <= 999; num++ {
  137. fname = w.Filename + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), num)
  138. _, err = os.Lstat(fname)
  139. }
  140. // return error if the last file checked still existed
  141. if err == nil {
  142. return fmt.Errorf("rotate: cannot find free log number to rename %s\n", w.Filename)
  143. }
  144. // block Logger's io.Writer
  145. w.mw.Lock()
  146. defer w.mw.Unlock()
  147. fd := w.mw.fd
  148. fd.Close()
  149. // close fd before rename
  150. // Rename the file to its newfound home
  151. if err = os.Rename(w.Filename, fname); err != nil {
  152. return fmt.Errorf("Rotate: %s\n", err)
  153. }
  154. // re-start logger
  155. if err = w.StartLogger(); err != nil {
  156. return fmt.Errorf("Rotate StartLogger: %s\n", err)
  157. }
  158. go w.deleteOldLog()
  159. }
  160. return nil
  161. }
  162. func (w *FileLogWriter) deleteOldLog() {
  163. dir := filepath.Dir(w.Filename)
  164. filepath.Walk(dir, func(path string, info os.FileInfo, err error) (returnErr error) {
  165. defer func() {
  166. if r := recover(); r != nil {
  167. returnErr = fmt.Errorf("Unable to delete old log '%s', error: %+v", path, r)
  168. }
  169. }()
  170. if !info.IsDir() && info.ModTime().Unix() < (time.Now().Unix()-60*60*24*w.Maxdays) {
  171. if strings.HasPrefix(filepath.Base(path), filepath.Base(w.Filename)) {
  172. os.Remove(path)
  173. }
  174. }
  175. return returnErr
  176. })
  177. }
  178. // destroy file logger, close file writer.
  179. func (w *FileLogWriter) Close() {
  180. w.mw.fd.Close()
  181. }
  182. // flush file logger.
  183. // there are no buffering messages in file logger in memory.
  184. // flush file means sync file from disk.
  185. func (w *FileLogWriter) Flush() {
  186. w.mw.fd.Sync()
  187. }