logs.go 9.4 KB

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