log.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. // Package lfshook is hook for sirupsen/logrus that used for writing the logs to local files.
  2. package lfshook
  3. import (
  4. "fmt"
  5. "io"
  6. "reflect"
  7. "sync"
  8. "github.com/sirupsen/logrus"
  9. "gopkg.in/natefinch/lumberjack.v2"
  10. )
  11. // We are logging to file, strip colors to make the output more readable.
  12. var defaultFormatter = &logrus.TextFormatter{DisableColors: true}
  13. // PathMap is map for mapping a log level to a file's path.
  14. // Multiple levels may share a file, but multiple files may not be used for one level.
  15. // type PathMap map[logrus.Level]string
  16. // 一个 level 对应 一个 logger
  17. type LoggerMap map[logrus.Level]*lumberjack.Logger
  18. // WriterMap is map for mapping a log level to an io.Writer.
  19. // Multiple levels may share a writer, but multiple writers may not be used for one level.
  20. type WriterMap map[logrus.Level]io.Writer
  21. type RotateFileConfig struct {
  22. Filename string
  23. MaxSize int
  24. MaxBackups int
  25. MaxAge int
  26. Level logrus.Level
  27. Formatter logrus.Formatter
  28. }
  29. // LfsHook is a hook to handle writing to local log files.
  30. type LfsHook struct {
  31. loggers LoggerMap
  32. writers WriterMap
  33. levels []logrus.Level
  34. lock *sync.Mutex
  35. formatter logrus.Formatter
  36. Config RotateFileConfig
  37. defaultPath string
  38. defaultWriter io.Writer
  39. hasDefaultPath bool
  40. hasDefaultWriter bool
  41. }
  42. // NewHook returns new LFS hook.
  43. // Output can be a string, io.Writer, WriterMap or PathMap.
  44. // If using io.Writer or WriterMap, user is responsible for closing the used io.Writer.
  45. func NewHook(output interface{}, formatter logrus.Formatter) *LfsHook {
  46. hook := &LfsHook{
  47. lock: new(sync.Mutex),
  48. }
  49. hook.SetFormatter(formatter)
  50. switch output.(type) {
  51. case string:
  52. hook.SetDefaultPath(output.(string))
  53. break
  54. case io.Writer:
  55. hook.SetDefaultWriter(output.(io.Writer))
  56. break
  57. case LoggerMap:
  58. hook.loggers = output.(LoggerMap)
  59. for level := range output.(LoggerMap) {
  60. hook.levels = append(hook.levels, level)
  61. }
  62. break
  63. case WriterMap:
  64. hook.writers = output.(WriterMap)
  65. for level := range output.(WriterMap) {
  66. hook.levels = append(hook.levels, level)
  67. }
  68. break
  69. default:
  70. panic(fmt.Sprintf("unsupported level map type: %v", reflect.TypeOf(output)))
  71. }
  72. return hook
  73. }
  74. // SetFormatter sets the format that will be used by hook.
  75. // If using text formatter, this method will disable color output to make the log file more readable.
  76. func (hook *LfsHook) SetFormatter(formatter logrus.Formatter) {
  77. hook.lock.Lock()
  78. defer hook.lock.Unlock()
  79. if formatter == nil {
  80. formatter = defaultFormatter
  81. } else {
  82. switch formatter.(type) {
  83. case *logrus.TextFormatter:
  84. textFormatter := formatter.(*logrus.TextFormatter)
  85. textFormatter.DisableColors = true
  86. }
  87. }
  88. hook.formatter = formatter
  89. }
  90. // SetDefaultPath sets default path for levels that don't have any defined output path.
  91. func (hook *LfsHook) SetDefaultPath(defaultPath string) {
  92. hook.lock.Lock()
  93. defer hook.lock.Unlock()
  94. hook.defaultPath = defaultPath
  95. hook.hasDefaultPath = true
  96. }
  97. // SetDefaultWriter sets default writer for levels that don't have any defined writer.
  98. func (hook *LfsHook) SetDefaultWriter(defaultWriter io.Writer) {
  99. hook.lock.Lock()
  100. defer hook.lock.Unlock()
  101. hook.defaultWriter = defaultWriter
  102. hook.hasDefaultWriter = true
  103. }
  104. // Fire writes the log file to defined path or using the defined writer.
  105. // User who run this function needs write permissions to the file or directory if the file does not yet exist.
  106. func (hook *LfsHook) Fire(entry *logrus.Entry) error {
  107. hook.lock.Lock()
  108. defer hook.lock.Unlock()
  109. if hook.writers != nil || hook.hasDefaultWriter {
  110. return hook.ioWrite(entry)
  111. } else if hook.loggers != nil || hook.hasDefaultPath {
  112. return hook.fileWrite(entry)
  113. }
  114. return nil
  115. }
  116. // Write a log line to an io.Writer.
  117. func (hook *LfsHook) ioWrite(entry *logrus.Entry) error {
  118. var (
  119. writer io.Writer
  120. msg []byte
  121. err error
  122. ok bool
  123. )
  124. if writer, ok = hook.writers[entry.Level]; !ok {
  125. if hook.hasDefaultWriter {
  126. writer = hook.defaultWriter
  127. } else {
  128. return nil
  129. }
  130. }
  131. // use our formatter instead of entry.String()
  132. msg, err = hook.formatter.Format(entry)
  133. if err != nil {
  134. log.Println("failed to generate string for entry:", err)
  135. return err
  136. }
  137. _, err = writer.Write(msg)
  138. return err
  139. }
  140. // Write a log line directly to a file.
  141. func (hook *LfsHook) fileWrite(entry *logrus.Entry) error {
  142. var (
  143. logger *lumberjack.Logger
  144. msg []byte
  145. err error
  146. ok bool
  147. )
  148. if logger, ok = hook.loggers[entry.Level]; !ok {
  149. if hook.hasDefaultPath {
  150. logger = &lumberjack.Logger{
  151. Filename: hook.defaultPath,
  152. MaxSize: 10, // maxSize M
  153. MaxBackups: 5, // keep 5 file
  154. MaxAge: 7, // 7 day
  155. }
  156. } else {
  157. return nil
  158. }
  159. }
  160. // use our formatter instead of entry.String()
  161. msg, err = hook.formatter.Format(entry)
  162. if err != nil {
  163. log.Println("failed to generate string for entry:", err)
  164. return err
  165. }
  166. defer logger.Close()
  167. logger.Write(msg)
  168. return nil
  169. }
  170. // Levels returns configured log levels.
  171. func (hook *LfsHook) Levels() []logrus.Level {
  172. return logrus.AllLevels
  173. }
  174. var log *logrus.Entry
  175. var once sync.Once
  176. //NewLogger 初始化 logger
  177. func NewLogger() *logrus.Entry {
  178. once.Do(func() {
  179. log = logrus.New().WithFields(
  180. logrus.Fields{
  181. "appname": "pbx-api-v4",
  182. },
  183. )
  184. })
  185. return log
  186. }