logs.go 9.7 KB

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