file.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. // Copyright 2014 beego Author. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package logs
  15. import (
  16. "bytes"
  17. "encoding/json"
  18. "errors"
  19. "fmt"
  20. "io"
  21. "log"
  22. "os"
  23. "path/filepath"
  24. "strings"
  25. "sync"
  26. "time"
  27. )
  28. // FileLogWriter implements LoggerInterface.
  29. // It writes messages by lines limit, file size limit, or time frequency.
  30. type FileLogWriter struct {
  31. *log.Logger
  32. mw *MuxWriter
  33. // The opened file
  34. Filename string `json:"filename"`
  35. Maxlines int `json:"maxlines"`
  36. maxlines_curlines int
  37. // Rotate at size
  38. Maxsize int `json:"maxsize"`
  39. maxsize_cursize int
  40. // Rotate daily
  41. Daily bool `json:"daily"`
  42. Maxdays int64 `json:"maxdays"`
  43. daily_opendate int
  44. Rotate bool `json:"rotate"`
  45. startLock sync.Mutex // Only one log can write to the file
  46. Level int `json:"level"`
  47. }
  48. // an *os.File writer with locker.
  49. type MuxWriter struct {
  50. sync.Mutex
  51. fd *os.File
  52. }
  53. // write to os.File.
  54. func (l *MuxWriter) Write(b []byte) (int, error) {
  55. l.Lock()
  56. defer l.Unlock()
  57. return l.fd.Write(b)
  58. }
  59. // set os.File in writer.
  60. func (l *MuxWriter) SetFd(fd *os.File) {
  61. if l.fd != nil {
  62. l.fd.Close()
  63. }
  64. l.fd = fd
  65. }
  66. // create a FileLogWriter returning as LoggerInterface.
  67. func NewFileWriter() LoggerInterface {
  68. w := &FileLogWriter{
  69. Filename: "",
  70. Maxlines: 1000000,
  71. Maxsize: 1 << 28, //256 MB
  72. Daily: true,
  73. Maxdays: 7,
  74. Rotate: true,
  75. Level: LevelTrace,
  76. }
  77. // use MuxWriter instead direct use os.File for lock write when rotate
  78. w.mw = new(MuxWriter)
  79. // set MuxWriter as Logger's io.Writer
  80. w.Logger = log.New(w.mw, "", log.Ldate|log.Ltime)
  81. return w
  82. }
  83. // Init file logger with json config.
  84. // jsonconfig like:
  85. // {
  86. // "filename":"logs/beego.log",
  87. // "maxlines":10000,
  88. // "maxsize":1<<30,
  89. // "daily":true,
  90. // "maxdays":15,
  91. // "rotate":true
  92. // }
  93. func (w *FileLogWriter) Init(jsonconfig string) error {
  94. err := json.Unmarshal([]byte(jsonconfig), w)
  95. if err != nil {
  96. return err
  97. }
  98. if len(w.Filename) == 0 {
  99. return errors.New("jsonconfig must have filename")
  100. }
  101. err = w.startLogger()
  102. return err
  103. }
  104. // start file logger. create log file and set to locker-inside file writer.
  105. func (w *FileLogWriter) startLogger() error {
  106. fd, err := w.createLogFile()
  107. if err != nil {
  108. return err
  109. }
  110. w.mw.SetFd(fd)
  111. return w.initFd()
  112. }
  113. func (w *FileLogWriter) docheck(size int) {
  114. w.startLock.Lock()
  115. defer w.startLock.Unlock()
  116. if w.Rotate && ((w.Maxlines > 0 && w.maxlines_curlines >= w.Maxlines) ||
  117. (w.Maxsize > 0 && w.maxsize_cursize >= w.Maxsize) ||
  118. (w.Daily && time.Now().Day() != w.daily_opendate)) {
  119. if err := w.DoRotate(); err != nil {
  120. fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.Filename, err)
  121. return
  122. }
  123. }
  124. w.maxlines_curlines++
  125. w.maxsize_cursize += size
  126. }
  127. // write logger message into file.
  128. func (w *FileLogWriter) WriteMsg(msg string, level int) error {
  129. if level > w.Level {
  130. return nil
  131. }
  132. n := 24 + len(msg) // 24 stand for the length "2013/06/23 21:00:22 [T] "
  133. w.docheck(n)
  134. w.Logger.Println(msg)
  135. return nil
  136. }
  137. func (w *FileLogWriter) createLogFile() (*os.File, error) {
  138. // Open the log file
  139. fd, err := os.OpenFile(w.Filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
  140. return fd, err
  141. }
  142. func (w *FileLogWriter) initFd() error {
  143. fd := w.mw.fd
  144. finfo, err := fd.Stat()
  145. if err != nil {
  146. return fmt.Errorf("get stat err: %s\n", err)
  147. }
  148. w.maxsize_cursize = int(finfo.Size())
  149. w.daily_opendate = time.Now().Day()
  150. w.maxlines_curlines = 0
  151. if finfo.Size() > 0 {
  152. count, err := w.lines()
  153. if err != nil {
  154. return err
  155. }
  156. w.maxlines_curlines = count
  157. }
  158. return nil
  159. }
  160. func (w *FileLogWriter) lines() (int, error) {
  161. fd, err := os.Open(w.Filename)
  162. if err != nil {
  163. return 0, err
  164. }
  165. defer fd.Close()
  166. buf := make([]byte, 32768) // 32k
  167. count := 0
  168. lineSep := []byte{'\n'}
  169. for {
  170. c, err := fd.Read(buf)
  171. if err != nil && err != io.EOF {
  172. return count, err
  173. }
  174. count += bytes.Count(buf[:c], lineSep)
  175. if err == io.EOF {
  176. break
  177. }
  178. }
  179. return count, nil
  180. }
  181. // DoRotate means it need to write file in new file.
  182. // new file name like xx.log.2013-01-01.2
  183. func (w *FileLogWriter) DoRotate() error {
  184. _, err := os.Lstat(w.Filename)
  185. if err == nil { // file exists
  186. // Find the next available number
  187. num := 1
  188. fname := ""
  189. for ; err == nil && num <= 999; num++ {
  190. fname = w.Filename + fmt.Sprintf(".%s.%03d", time.Now().Format("2006-01-02"), num)
  191. _, err = os.Lstat(fname)
  192. }
  193. // return error if the last file checked still existed
  194. if err == nil {
  195. return fmt.Errorf("Rotate: Cannot find free log number to rename %s\n", w.Filename)
  196. }
  197. // block Logger's io.Writer
  198. w.mw.Lock()
  199. defer w.mw.Unlock()
  200. fd := w.mw.fd
  201. fd.Close()
  202. // close fd before rename
  203. // Rename the file to its newfound home
  204. err = os.Rename(w.Filename, fname)
  205. if err != nil {
  206. return fmt.Errorf("Rotate: %s\n", err)
  207. }
  208. // re-start logger
  209. err = w.startLogger()
  210. if err != nil {
  211. return fmt.Errorf("Rotate StartLogger: %s\n", err)
  212. }
  213. go w.deleteOldLog()
  214. }
  215. return nil
  216. }
  217. func (w *FileLogWriter) deleteOldLog() {
  218. dir := filepath.Dir(w.Filename)
  219. filepath.Walk(dir, func(path string, info os.FileInfo, err error) (returnErr error) {
  220. defer func() {
  221. if r := recover(); r != nil {
  222. returnErr = fmt.Errorf("Unable to delete old log '%s', error: %+v", path, r)
  223. fmt.Println(returnErr)
  224. }
  225. }()
  226. if !info.IsDir() && info.ModTime().Unix() < (time.Now().Unix()-60*60*24*w.Maxdays) {
  227. if strings.HasPrefix(filepath.Base(path), filepath.Base(w.Filename)) {
  228. os.Remove(path)
  229. }
  230. }
  231. return
  232. })
  233. }
  234. // destroy file logger, close file writer.
  235. func (w *FileLogWriter) Destroy() {
  236. w.mw.fd.Close()
  237. }
  238. // flush file logger.
  239. // there are no buffering messages in file logger in memory.
  240. // flush file means sync file from disk.
  241. func (w *FileLogWriter) Flush() {
  242. w.mw.fd.Sync()
  243. }
  244. func init() {
  245. Register("file", NewFileWriter)
  246. }