logs.go 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  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. log.Print(msg)
  180. output(severeLog, levelFatal, msg)
  181. os.Exit(1)
  182. }
  183. }
  184. func SetLevel(level uint32) {
  185. atomic.StoreUint32(&logLevel, level)
  186. }
  187. func Severe(v ...interface{}) {
  188. severeSync(fmt.Sprint(v...))
  189. }
  190. func Severef(format string, v ...interface{}) {
  191. severeSync(fmt.Sprintf(format, v...))
  192. }
  193. func Slow(v ...interface{}) {
  194. slowSync(fmt.Sprint(v...))
  195. }
  196. func Slowf(format string, v ...interface{}) {
  197. slowSync(fmt.Sprintf(format, v...))
  198. }
  199. func Stat(v ...interface{}) {
  200. statSync(fmt.Sprint(v...))
  201. }
  202. func Statf(format string, v ...interface{}) {
  203. statSync(fmt.Sprintf(format, v...))
  204. }
  205. func WithCooldownMillis(millis int) LogOption {
  206. return func(opts *logOptions) {
  207. opts.logStackCooldownMills = millis
  208. }
  209. }
  210. func WithKeepDays(days int) LogOption {
  211. return func(opts *logOptions) {
  212. opts.keepDays = days
  213. }
  214. }
  215. func WithGzip() LogOption {
  216. return func(opts *logOptions) {
  217. opts.gzipEnabled = true
  218. }
  219. }
  220. func createOutput(path string) (io.WriteCloser, error) {
  221. if len(path) == 0 {
  222. return nil, ErrLogPathNotSet
  223. }
  224. return NewLogger(path, DefaultRotateRule(path, backupFileDelimiter, options.keepDays,
  225. options.gzipEnabled), options.gzipEnabled)
  226. }
  227. func errorSync(msg string, callDepth int) {
  228. if shouldLog(ErrorLevel) {
  229. outputError(errorLog, msg, callDepth)
  230. }
  231. }
  232. func formatWithCaller(msg string, callDepth int) string {
  233. var buf strings.Builder
  234. caller := getCaller(callDepth)
  235. if len(caller) > 0 {
  236. buf.WriteString(caller)
  237. buf.WriteByte(' ')
  238. }
  239. buf.WriteString(msg)
  240. return buf.String()
  241. }
  242. func getCaller(callDepth int) string {
  243. var buf strings.Builder
  244. _, file, line, ok := runtime.Caller(callDepth)
  245. if ok {
  246. short := file
  247. for i := len(file) - 1; i > 0; i-- {
  248. if file[i] == '/' {
  249. short = file[i+1:]
  250. break
  251. }
  252. }
  253. buf.WriteString(short)
  254. buf.WriteByte(':')
  255. buf.WriteString(strconv.Itoa(line))
  256. }
  257. return buf.String()
  258. }
  259. func getTimestamp() string {
  260. return timex.Time().Format(timeFormat)
  261. }
  262. func handleOptions(opts []LogOption) {
  263. for _, opt := range opts {
  264. opt(&options)
  265. }
  266. }
  267. func infoSync(msg string) {
  268. if shouldLog(InfoLevel) {
  269. output(infoLog, levelInfo, msg)
  270. }
  271. }
  272. func output(writer io.Writer, level, msg string) {
  273. info := logEntry{
  274. Timestamp: getTimestamp(),
  275. Level: level,
  276. Content: msg,
  277. }
  278. outputJson(writer, info)
  279. }
  280. func outputError(writer io.Writer, msg string, callDepth int) {
  281. content := formatWithCaller(msg, callDepth)
  282. output(writer, levelError, content)
  283. }
  284. func outputJson(writer io.Writer, info interface{}) {
  285. if content, err := json.Marshal(info); err != nil {
  286. log.Println(err.Error())
  287. } else if atomic.LoadUint32(&initialized) == 0 || writer == nil {
  288. log.Println(string(content))
  289. } else {
  290. writer.Write(append(content, '\n'))
  291. }
  292. }
  293. func setupLogLevel(c LogConf) {
  294. switch c.Level {
  295. case levelInfo:
  296. SetLevel(InfoLevel)
  297. case levelError:
  298. SetLevel(ErrorLevel)
  299. case levelSevere:
  300. SetLevel(SevereLevel)
  301. }
  302. }
  303. func setupWithConsole(c LogConf) {
  304. once.Do(func() {
  305. atomic.StoreUint32(&initialized, 1)
  306. writeConsole = true
  307. setupLogLevel(c)
  308. infoLog = newLogWriter(log.New(os.Stdout, "", flags))
  309. errorLog = newLogWriter(log.New(os.Stderr, "", flags))
  310. severeLog = newLogWriter(log.New(os.Stderr, "", flags))
  311. slowLog = newLogWriter(log.New(os.Stderr, "", flags))
  312. stackLog = NewLessWriter(errorLog, options.logStackCooldownMills)
  313. statLog = infoLog
  314. })
  315. }
  316. func setupWithFiles(c LogConf) error {
  317. var opts []LogOption
  318. var err error
  319. if len(c.Path) == 0 {
  320. return ErrLogPathNotSet
  321. }
  322. opts = append(opts, WithCooldownMillis(c.StackCooldownMillis))
  323. if c.Compress {
  324. opts = append(opts, WithGzip())
  325. }
  326. if c.KeepDays > 0 {
  327. opts = append(opts, WithKeepDays(c.KeepDays))
  328. }
  329. accessFile := path.Join(c.Path, accessFilename)
  330. errorFile := path.Join(c.Path, errorFilename)
  331. severeFile := path.Join(c.Path, severeFilename)
  332. slowFile := path.Join(c.Path, slowFilename)
  333. statFile := path.Join(c.Path, statFilename)
  334. once.Do(func() {
  335. atomic.StoreUint32(&initialized, 1)
  336. handleOptions(opts)
  337. setupLogLevel(c)
  338. if infoLog, err = createOutput(accessFile); err != nil {
  339. return
  340. }
  341. if errorLog, err = createOutput(errorFile); err != nil {
  342. return
  343. }
  344. if severeLog, err = createOutput(severeFile); err != nil {
  345. return
  346. }
  347. if slowLog, err = createOutput(slowFile); err != nil {
  348. return
  349. }
  350. if statLog, err = createOutput(statFile); err != nil {
  351. return
  352. }
  353. stackLog = NewLessWriter(errorLog, options.logStackCooldownMills)
  354. })
  355. return err
  356. }
  357. func setupWithVolume(c LogConf) error {
  358. if len(c.ServiceName) == 0 {
  359. return ErrLogServiceNameNotSet
  360. }
  361. c.Path = path.Join(c.Path, c.ServiceName, sysx.Hostname())
  362. return setupWithFiles(c)
  363. }
  364. func severeSync(msg string) {
  365. if shouldLog(SevereLevel) {
  366. output(severeLog, levelSevere, fmt.Sprintf("%s\n%s", msg, string(debug.Stack())))
  367. }
  368. }
  369. func shouldLog(level uint32) bool {
  370. return atomic.LoadUint32(&logLevel) <= level
  371. }
  372. func slowSync(msg string) {
  373. if shouldLog(ErrorLevel) {
  374. output(slowLog, levelSlow, msg)
  375. }
  376. }
  377. func stackSync(msg string) {
  378. if shouldLog(ErrorLevel) {
  379. output(stackLog, levelError, fmt.Sprintf("%s\n%s", msg, string(debug.Stack())))
  380. }
  381. }
  382. func statSync(msg string) {
  383. if shouldLog(InfoLevel) {
  384. output(statLog, levelStat, msg)
  385. }
  386. }
  387. type logWriter struct {
  388. logger *log.Logger
  389. }
  390. func newLogWriter(logger *log.Logger) logWriter {
  391. return logWriter{
  392. logger: logger,
  393. }
  394. }
  395. func (lw logWriter) Close() error {
  396. return nil
  397. }
  398. func (lw logWriter) Write(data []byte) (int, error) {
  399. lw.logger.Print(string(data))
  400. return len(data), nil
  401. }