1
0

writer.go 8.4 KB

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