writer.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. package logx
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "log"
  8. "path"
  9. "reflect"
  10. "runtime/debug"
  11. "sync"
  12. "sync/atomic"
  13. fatihcolor "github.com/fatih/color"
  14. "github.com/zeromicro/go-zero/core/color"
  15. )
  16. type (
  17. Writer interface {
  18. Alert(v any)
  19. Close() error
  20. Debug(v any, fields ...LogField)
  21. Error(v any, fields ...LogField)
  22. Info(v any, fields ...LogField)
  23. Severe(v any)
  24. Slow(v any, fields ...LogField)
  25. Stack(v any)
  26. Stat(v any, fields ...LogField)
  27. }
  28. atomicWriter struct {
  29. writer Writer
  30. lock sync.RWMutex
  31. }
  32. concreteWriter struct {
  33. infoLog io.WriteCloser
  34. errorLog io.WriteCloser
  35. severeLog io.WriteCloser
  36. slowLog io.WriteCloser
  37. statLog io.WriteCloser
  38. stackLog io.Writer
  39. }
  40. )
  41. // NewWriter creates a new Writer with the given io.Writer.
  42. func NewWriter(w io.Writer) Writer {
  43. lw := newLogWriter(log.New(w, "", flags))
  44. return &concreteWriter{
  45. infoLog: lw,
  46. errorLog: lw,
  47. severeLog: lw,
  48. slowLog: lw,
  49. statLog: lw,
  50. stackLog: lw,
  51. }
  52. }
  53. func (w *atomicWriter) Load() Writer {
  54. w.lock.RLock()
  55. defer w.lock.RUnlock()
  56. return w.writer
  57. }
  58. func (w *atomicWriter) Store(v Writer) {
  59. w.lock.Lock()
  60. defer w.lock.Unlock()
  61. w.writer = v
  62. }
  63. func (w *atomicWriter) StoreIfNil(v Writer) Writer {
  64. w.lock.Lock()
  65. defer w.lock.Unlock()
  66. if w.writer == nil {
  67. w.writer = v
  68. }
  69. return w.writer
  70. }
  71. func (w *atomicWriter) Swap(v Writer) Writer {
  72. w.lock.Lock()
  73. defer w.lock.Unlock()
  74. old := w.writer
  75. w.writer = v
  76. return old
  77. }
  78. func newConsoleWriter() Writer {
  79. outLog := newLogWriter(log.New(fatihcolor.Output, "", flags))
  80. errLog := newLogWriter(log.New(fatihcolor.Error, "", flags))
  81. return &concreteWriter{
  82. infoLog: outLog,
  83. errorLog: errLog,
  84. severeLog: errLog,
  85. slowLog: errLog,
  86. stackLog: newLessWriter(errLog, options.logStackCooldownMills),
  87. statLog: outLog,
  88. }
  89. }
  90. func newFileWriter(c LogConf) (Writer, error) {
  91. var err error
  92. var opts []LogOption
  93. var infoLog io.WriteCloser
  94. var errorLog io.WriteCloser
  95. var severeLog io.WriteCloser
  96. var slowLog io.WriteCloser
  97. var statLog io.WriteCloser
  98. var stackLog io.Writer
  99. if len(c.Path) == 0 {
  100. return nil, ErrLogPathNotSet
  101. }
  102. opts = append(opts, WithCooldownMillis(c.StackCooldownMillis))
  103. if c.Compress {
  104. opts = append(opts, WithGzip())
  105. }
  106. if c.KeepDays > 0 {
  107. opts = append(opts, WithKeepDays(c.KeepDays))
  108. }
  109. if c.MaxBackups > 0 {
  110. opts = append(opts, WithMaxBackups(c.MaxBackups))
  111. }
  112. if c.MaxSize > 0 {
  113. opts = append(opts, WithMaxSize(c.MaxSize))
  114. }
  115. opts = append(opts, WithRotation(c.Rotation))
  116. accessFile := path.Join(c.Path, accessFilename)
  117. errorFile := path.Join(c.Path, errorFilename)
  118. severeFile := path.Join(c.Path, severeFilename)
  119. slowFile := path.Join(c.Path, slowFilename)
  120. statFile := path.Join(c.Path, statFilename)
  121. handleOptions(opts)
  122. setupLogLevel(c)
  123. if infoLog, err = createOutput(accessFile); err != nil {
  124. return nil, err
  125. }
  126. if errorLog, err = createOutput(errorFile); err != nil {
  127. return nil, err
  128. }
  129. if severeLog, err = createOutput(severeFile); err != nil {
  130. return nil, err
  131. }
  132. if slowLog, err = createOutput(slowFile); err != nil {
  133. return nil, err
  134. }
  135. if statLog, err = createOutput(statFile); err != nil {
  136. return nil, err
  137. }
  138. stackLog = newLessWriter(errorLog, options.logStackCooldownMills)
  139. return &concreteWriter{
  140. infoLog: infoLog,
  141. errorLog: errorLog,
  142. severeLog: severeLog,
  143. slowLog: slowLog,
  144. statLog: statLog,
  145. stackLog: stackLog,
  146. }, nil
  147. }
  148. func (w *concreteWriter) Alert(v any) {
  149. output(w.errorLog, levelAlert, v)
  150. }
  151. func (w *concreteWriter) Close() error {
  152. if err := w.infoLog.Close(); err != nil {
  153. return err
  154. }
  155. if err := w.errorLog.Close(); err != nil {
  156. return err
  157. }
  158. if err := w.severeLog.Close(); err != nil {
  159. return err
  160. }
  161. if err := w.slowLog.Close(); err != nil {
  162. return err
  163. }
  164. return w.statLog.Close()
  165. }
  166. func (w *concreteWriter) Debug(v any, fields ...LogField) {
  167. output(w.infoLog, levelDebug, v, fields...)
  168. }
  169. func (w *concreteWriter) Error(v any, fields ...LogField) {
  170. output(w.errorLog, levelError, v, fields...)
  171. }
  172. func (w *concreteWriter) Info(v any, fields ...LogField) {
  173. output(w.infoLog, levelInfo, v, fields...)
  174. }
  175. func (w *concreteWriter) Severe(v any) {
  176. output(w.severeLog, levelFatal, v)
  177. }
  178. func (w *concreteWriter) Slow(v any, fields ...LogField) {
  179. output(w.slowLog, levelSlow, v, fields...)
  180. }
  181. func (w *concreteWriter) Stack(v any) {
  182. output(w.stackLog, levelError, v)
  183. }
  184. func (w *concreteWriter) Stat(v any, fields ...LogField) {
  185. output(w.statLog, levelStat, v, fields...)
  186. }
  187. type nopWriter struct{}
  188. func (n nopWriter) Alert(_ any) {
  189. }
  190. func (n nopWriter) Close() error {
  191. return nil
  192. }
  193. func (n nopWriter) Debug(_ any, _ ...LogField) {
  194. }
  195. func (n nopWriter) Error(_ any, _ ...LogField) {
  196. }
  197. func (n nopWriter) Info(_ any, _ ...LogField) {
  198. }
  199. func (n nopWriter) Severe(_ any) {
  200. }
  201. func (n nopWriter) Slow(_ any, _ ...LogField) {
  202. }
  203. func (n nopWriter) Stack(_ any) {
  204. }
  205. func (n nopWriter) Stat(_ any, _ ...LogField) {
  206. }
  207. func buildPlainFields(fields ...LogField) []string {
  208. var items []string
  209. for _, field := range fields {
  210. items = append(items, fmt.Sprintf("%s=%+v", field.Key, field.Value))
  211. }
  212. return items
  213. }
  214. func combineGlobalFields(fields []LogField) []LogField {
  215. globals := globalFields.Load()
  216. if globals == nil {
  217. return fields
  218. }
  219. gf := globals.([]LogField)
  220. ret := make([]LogField, 0, len(gf)+len(fields))
  221. ret = append(ret, gf...)
  222. ret = append(ret, fields...)
  223. return ret
  224. }
  225. func output(writer io.Writer, level string, val any, fields ...LogField) {
  226. // only truncate string content, don't know how to truncate the values of other types.
  227. if v, ok := val.(string); ok {
  228. maxLen := atomic.LoadUint32(&maxContentLength)
  229. if maxLen > 0 && len(v) > int(maxLen) {
  230. val = v[:maxLen]
  231. fields = append(fields, truncatedField)
  232. }
  233. }
  234. fields = combineGlobalFields(fields)
  235. switch atomic.LoadUint32(&encoding) {
  236. case plainEncodingType:
  237. writePlainAny(writer, level, val, buildPlainFields(fields...)...)
  238. default:
  239. entry := make(logEntry)
  240. for _, field := range fields {
  241. entry[field.Key] = field.Value
  242. }
  243. entry[timestampKey] = getTimestamp()
  244. entry[levelKey] = level
  245. entry[contentKey] = val
  246. writeJson(writer, entry)
  247. }
  248. }
  249. func wrapLevelWithColor(level string) string {
  250. var colour color.Color
  251. switch level {
  252. case levelAlert:
  253. colour = color.FgRed
  254. case levelError:
  255. colour = color.FgRed
  256. case levelFatal:
  257. colour = color.FgRed
  258. case levelInfo:
  259. colour = color.FgBlue
  260. case levelSlow:
  261. colour = color.FgYellow
  262. case levelDebug:
  263. colour = color.FgYellow
  264. case levelStat:
  265. colour = color.FgGreen
  266. }
  267. if colour == color.NoColor {
  268. return level
  269. }
  270. return color.WithColorPadding(level, colour)
  271. }
  272. func writeJson(writer io.Writer, info any) {
  273. if content, err := json.Marshal(info); err != nil {
  274. log.Printf("err: %s, type: %s\n\n%s\n", err.Error(), reflect.TypeOf(info).Name(), debug.Stack())
  275. } else if writer == nil {
  276. log.Println(string(content))
  277. } else {
  278. writer.Write(append(content, '\n'))
  279. }
  280. }
  281. func writePlainAny(writer io.Writer, level string, val any, fields ...string) {
  282. level = wrapLevelWithColor(level)
  283. switch v := val.(type) {
  284. case string:
  285. writePlainText(writer, level, v, fields...)
  286. case error:
  287. writePlainText(writer, level, v.Error(), fields...)
  288. case fmt.Stringer:
  289. writePlainText(writer, level, v.String(), fields...)
  290. default:
  291. writePlainValue(writer, level, v, fields...)
  292. }
  293. }
  294. func writePlainText(writer io.Writer, level, msg string, fields ...string) {
  295. var buf bytes.Buffer
  296. buf.WriteString(getTimestamp())
  297. buf.WriteByte(plainEncodingSep)
  298. buf.WriteString(level)
  299. buf.WriteByte(plainEncodingSep)
  300. buf.WriteString(msg)
  301. for _, item := range fields {
  302. buf.WriteByte(plainEncodingSep)
  303. buf.WriteString(item)
  304. }
  305. buf.WriteByte('\n')
  306. if writer == nil {
  307. log.Println(buf.String())
  308. return
  309. }
  310. if _, err := writer.Write(buf.Bytes()); err != nil {
  311. log.Println(err.Error())
  312. }
  313. }
  314. func writePlainValue(writer io.Writer, level string, val any, fields ...string) {
  315. var buf bytes.Buffer
  316. buf.WriteString(getTimestamp())
  317. buf.WriteByte(plainEncodingSep)
  318. buf.WriteString(level)
  319. buf.WriteByte(plainEncodingSep)
  320. if err := json.NewEncoder(&buf).Encode(val); err != nil {
  321. log.Printf("err: %s, type: %s\n\n%s\n", err.Error(), reflect.TypeOf(val).Name(), debug.Stack())
  322. return
  323. }
  324. for _, item := range fields {
  325. buf.WriteByte(plainEncodingSep)
  326. buf.WriteString(item)
  327. }
  328. buf.WriteByte('\n')
  329. if writer == nil {
  330. log.Println(buf.String())
  331. return
  332. }
  333. if _, err := writer.Write(buf.Bytes()); err != nil {
  334. log.Println(err.Error())
  335. }
  336. }