logs.go 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. package logx
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "io/ioutil"
  8. "log"
  9. "os"
  10. "path"
  11. "runtime"
  12. "runtime/debug"
  13. "strconv"
  14. "strings"
  15. "sync"
  16. "sync/atomic"
  17. "github.com/tal-tech/go-zero/core/iox"
  18. "github.com/tal-tech/go-zero/core/sysx"
  19. "github.com/tal-tech/go-zero/core/timex"
  20. )
  21. const (
  22. // InfoLevel logs everything
  23. InfoLevel = iota
  24. // ErrorLevel includes errors, slows, stacks
  25. ErrorLevel
  26. // SevereLevel only log severe messages
  27. SevereLevel
  28. )
  29. const (
  30. timeFormat = "2006-01-02T15:04:05.000Z07"
  31. accessFilename = "access.log"
  32. errorFilename = "error.log"
  33. severeFilename = "severe.log"
  34. slowFilename = "slow.log"
  35. statFilename = "stat.log"
  36. consoleMode = "console"
  37. volumeMode = "volume"
  38. levelInfo = "info"
  39. levelError = "error"
  40. levelSevere = "severe"
  41. levelFatal = "fatal"
  42. levelSlow = "slow"
  43. levelStat = "stat"
  44. backupFileDelimiter = "-"
  45. callerInnerDepth = 5
  46. flags = 0x0
  47. )
  48. var (
  49. ErrLogPathNotSet = errors.New("log path must be set")
  50. ErrLogNotInitialized = errors.New("log not initialized")
  51. ErrLogServiceNameNotSet = errors.New("log service name must be set")
  52. writeConsole bool
  53. logLevel uint32
  54. infoLog io.WriteCloser
  55. errorLog io.WriteCloser
  56. severeLog io.WriteCloser
  57. slowLog io.WriteCloser
  58. statLog io.WriteCloser
  59. stackLog io.Writer
  60. once sync.Once
  61. initialized uint32
  62. options logOptions
  63. )
  64. type (
  65. logEntry struct {
  66. Timestamp string `json:"@timestamp"`
  67. Level string `json:"level"`
  68. Duration string `json:"duration,omitempty"`
  69. Content string `json:"content"`
  70. }
  71. logOptions struct {
  72. gzipEnabled bool
  73. logStackCooldownMills int
  74. keepDays int
  75. }
  76. LogOption func(options *logOptions)
  77. Logger interface {
  78. Error(...interface{})
  79. Errorf(string, ...interface{})
  80. Info(...interface{})
  81. Infof(string, ...interface{})
  82. Slow(...interface{})
  83. Slowf(string, ...interface{})
  84. }
  85. )
  86. func MustSetup(c LogConf) {
  87. Must(SetUp(c))
  88. }
  89. // SetUp sets up the logx. If already set up, just return nil.
  90. // we allow SetUp to be called multiple times, because for example
  91. // we need to allow different service frameworks to initialize logx respectively.
  92. // the same logic for SetUp
  93. func SetUp(c LogConf) error {
  94. switch c.Mode {
  95. case consoleMode:
  96. setupWithConsole(c)
  97. return nil
  98. case volumeMode:
  99. return setupWithVolume(c)
  100. default:
  101. return setupWithFiles(c)
  102. }
  103. }
  104. func Close() error {
  105. if writeConsole {
  106. return nil
  107. }
  108. if atomic.LoadUint32(&initialized) == 0 {
  109. return ErrLogNotInitialized
  110. }
  111. atomic.StoreUint32(&initialized, 0)
  112. if infoLog != nil {
  113. if err := infoLog.Close(); err != nil {
  114. return err
  115. }
  116. }
  117. if errorLog != nil {
  118. if err := errorLog.Close(); err != nil {
  119. return err
  120. }
  121. }
  122. if severeLog != nil {
  123. if err := severeLog.Close(); err != nil {
  124. return err
  125. }
  126. }
  127. if slowLog != nil {
  128. if err := slowLog.Close(); err != nil {
  129. return err
  130. }
  131. }
  132. if statLog != nil {
  133. if err := statLog.Close(); err != nil {
  134. return err
  135. }
  136. }
  137. return nil
  138. }
  139. func Disable() {
  140. once.Do(func() {
  141. atomic.StoreUint32(&initialized, 1)
  142. infoLog = iox.NopCloser(ioutil.Discard)
  143. errorLog = iox.NopCloser(ioutil.Discard)
  144. severeLog = iox.NopCloser(ioutil.Discard)
  145. slowLog = iox.NopCloser(ioutil.Discard)
  146. statLog = iox.NopCloser(ioutil.Discard)
  147. stackLog = ioutil.Discard
  148. })
  149. }
  150. func Error(v ...interface{}) {
  151. ErrorCaller(1, v...)
  152. }
  153. func Errorf(format string, v ...interface{}) {
  154. ErrorCallerf(1, format, v...)
  155. }
  156. func ErrorCaller(callDepth int, v ...interface{}) {
  157. errorSync(fmt.Sprint(v...), callDepth+callerInnerDepth)
  158. }
  159. func ErrorCallerf(callDepth int, format string, v ...interface{}) {
  160. errorSync(fmt.Sprintf(format, v...), callDepth+callerInnerDepth)
  161. }
  162. func ErrorStack(v ...interface{}) {
  163. // there is newline in stack string
  164. stackSync(fmt.Sprint(v...))
  165. }
  166. func ErrorStackf(format string, v ...interface{}) {
  167. // there is newline in stack string
  168. stackSync(fmt.Sprintf(format, v...))
  169. }
  170. func Info(v ...interface{}) {
  171. infoSync(fmt.Sprint(v...))
  172. }
  173. func Infof(format string, v ...interface{}) {
  174. infoSync(fmt.Sprintf(format, v...))
  175. }
  176. func Must(err error) {
  177. if err != nil {
  178. msg := formatWithCaller(err.Error(), 3)
  179. output(severeLog, levelFatal, msg)
  180. os.Exit(1)
  181. }
  182. }
  183. func SetLevel(level uint32) {
  184. atomic.StoreUint32(&logLevel, level)
  185. }
  186. func Severe(v ...interface{}) {
  187. severeSync(fmt.Sprint(v...))
  188. }
  189. func Severef(format string, v ...interface{}) {
  190. severeSync(fmt.Sprintf(format, v...))
  191. }
  192. func Slow(v ...interface{}) {
  193. slowSync(fmt.Sprint(v...))
  194. }
  195. func Slowf(format string, v ...interface{}) {
  196. slowSync(fmt.Sprintf(format, v...))
  197. }
  198. func Stat(v ...interface{}) {
  199. statSync(fmt.Sprint(v...))
  200. }
  201. func Statf(format string, v ...interface{}) {
  202. statSync(fmt.Sprintf(format, v...))
  203. }
  204. func WithCooldownMillis(millis int) LogOption {
  205. return func(opts *logOptions) {
  206. opts.logStackCooldownMills = millis
  207. }
  208. }
  209. func WithKeepDays(days int) LogOption {
  210. return func(opts *logOptions) {
  211. opts.keepDays = days
  212. }
  213. }
  214. func WithGzip() LogOption {
  215. return func(opts *logOptions) {
  216. opts.gzipEnabled = true
  217. }
  218. }
  219. func createOutput(path string) (io.WriteCloser, error) {
  220. if len(path) == 0 {
  221. return nil, ErrLogPathNotSet
  222. }
  223. return NewLogger(path, DefaultRotateRule(path, backupFileDelimiter, options.keepDays,
  224. options.gzipEnabled), options.gzipEnabled)
  225. }
  226. func errorSync(msg string, callDepth int) {
  227. if shouldLog(ErrorLevel) {
  228. outputError(errorLog, msg, callDepth)
  229. }
  230. }
  231. func formatWithCaller(msg string, callDepth int) string {
  232. var buf strings.Builder
  233. caller := getCaller(callDepth)
  234. if len(caller) > 0 {
  235. buf.WriteString(caller)
  236. buf.WriteByte(' ')
  237. }
  238. buf.WriteString(msg)
  239. return buf.String()
  240. }
  241. func getCaller(callDepth int) string {
  242. var buf strings.Builder
  243. _, file, line, ok := runtime.Caller(callDepth)
  244. if ok {
  245. short := file
  246. for i := len(file) - 1; i > 0; i-- {
  247. if file[i] == '/' {
  248. short = file[i+1:]
  249. break
  250. }
  251. }
  252. buf.WriteString(short)
  253. buf.WriteByte(':')
  254. buf.WriteString(strconv.Itoa(line))
  255. }
  256. return buf.String()
  257. }
  258. func getTimestamp() string {
  259. return timex.Time().Format(timeFormat)
  260. }
  261. func handleOptions(opts []LogOption) {
  262. for _, opt := range opts {
  263. opt(&options)
  264. }
  265. }
  266. func infoSync(msg string) {
  267. if shouldLog(InfoLevel) {
  268. output(infoLog, levelInfo, msg)
  269. }
  270. }
  271. func output(writer io.Writer, level, msg string) {
  272. info := logEntry{
  273. Timestamp: getTimestamp(),
  274. Level: level,
  275. Content: msg,
  276. }
  277. outputJson(writer, info)
  278. }
  279. func outputError(writer io.Writer, msg string, callDepth int) {
  280. content := formatWithCaller(msg, callDepth)
  281. output(writer, levelError, content)
  282. }
  283. func outputJson(writer io.Writer, info interface{}) {
  284. if content, err := json.Marshal(info); err != nil {
  285. log.Println(err.Error())
  286. } else if atomic.LoadUint32(&initialized) == 0 || writer == nil {
  287. log.Println(string(content))
  288. } else {
  289. writer.Write(append(content, '\n'))
  290. }
  291. }
  292. func setupLogLevel(c LogConf) {
  293. switch c.Level {
  294. case levelInfo:
  295. SetLevel(InfoLevel)
  296. case levelError:
  297. SetLevel(ErrorLevel)
  298. case levelSevere:
  299. SetLevel(SevereLevel)
  300. }
  301. }
  302. func setupWithConsole(c LogConf) {
  303. once.Do(func() {
  304. atomic.StoreUint32(&initialized, 1)
  305. writeConsole = true
  306. setupLogLevel(c)
  307. infoLog = newLogWriter(log.New(os.Stdout, "", flags))
  308. errorLog = newLogWriter(log.New(os.Stderr, "", flags))
  309. severeLog = newLogWriter(log.New(os.Stderr, "", flags))
  310. slowLog = newLogWriter(log.New(os.Stderr, "", flags))
  311. stackLog = NewLessWriter(errorLog, options.logStackCooldownMills)
  312. statLog = infoLog
  313. })
  314. }
  315. func setupWithFiles(c LogConf) error {
  316. var opts []LogOption
  317. var err error
  318. if len(c.Path) == 0 {
  319. return ErrLogPathNotSet
  320. }
  321. opts = append(opts, WithCooldownMillis(c.StackCooldownMillis))
  322. if c.Compress {
  323. opts = append(opts, WithGzip())
  324. }
  325. if c.KeepDays > 0 {
  326. opts = append(opts, WithKeepDays(c.KeepDays))
  327. }
  328. accessFile := path.Join(c.Path, accessFilename)
  329. errorFile := path.Join(c.Path, errorFilename)
  330. severeFile := path.Join(c.Path, severeFilename)
  331. slowFile := path.Join(c.Path, slowFilename)
  332. statFile := path.Join(c.Path, statFilename)
  333. once.Do(func() {
  334. atomic.StoreUint32(&initialized, 1)
  335. handleOptions(opts)
  336. setupLogLevel(c)
  337. if infoLog, err = createOutput(accessFile); err != nil {
  338. return
  339. }
  340. if errorLog, err = createOutput(errorFile); err != nil {
  341. return
  342. }
  343. if severeLog, err = createOutput(severeFile); err != nil {
  344. return
  345. }
  346. if slowLog, err = createOutput(slowFile); err != nil {
  347. return
  348. }
  349. if statLog, err = createOutput(statFile); err != nil {
  350. return
  351. }
  352. stackLog = NewLessWriter(errorLog, options.logStackCooldownMills)
  353. })
  354. return err
  355. }
  356. func setupWithVolume(c LogConf) error {
  357. if len(c.ServiceName) == 0 {
  358. return ErrLogServiceNameNotSet
  359. }
  360. c.Path = path.Join(c.Path, c.ServiceName, sysx.Hostname())
  361. return setupWithFiles(c)
  362. }
  363. func severeSync(msg string) {
  364. if shouldLog(SevereLevel) {
  365. output(severeLog, levelSevere, fmt.Sprintf("%s\n%s", msg, string(debug.Stack())))
  366. }
  367. }
  368. func shouldLog(level uint32) bool {
  369. return atomic.LoadUint32(&logLevel) <= level
  370. }
  371. func slowSync(msg string) {
  372. if shouldLog(ErrorLevel) {
  373. output(slowLog, levelSlow, msg)
  374. }
  375. }
  376. func stackSync(msg string) {
  377. if shouldLog(ErrorLevel) {
  378. output(stackLog, levelError, fmt.Sprintf("%s\n%s", msg, string(debug.Stack())))
  379. }
  380. }
  381. func statSync(msg string) {
  382. if shouldLog(InfoLevel) {
  383. output(statLog, levelStat, msg)
  384. }
  385. }
  386. type logWriter struct {
  387. logger *log.Logger
  388. }
  389. func newLogWriter(logger *log.Logger) logWriter {
  390. return logWriter{
  391. logger: logger,
  392. }
  393. }
  394. func (lw logWriter) Close() error {
  395. return nil
  396. }
  397. func (lw logWriter) Write(data []byte) (int, error) {
  398. lw.logger.Print(string(data))
  399. return len(data), nil
  400. }