logs.go 9.6 KB

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