logs.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. package logx
  2. import (
  3. "fmt"
  4. "io"
  5. "log"
  6. "os"
  7. "path"
  8. "runtime/debug"
  9. "sync"
  10. "sync/atomic"
  11. "time"
  12. "github.com/zeromicro/go-zero/core/sysx"
  13. )
  14. const callerDepth = 4
  15. var (
  16. timeFormat = "2006-01-02T15:04:05.000Z07:00"
  17. logLevel uint32
  18. encoding uint32 = jsonEncodingType
  19. // use uint32 for atomic operations
  20. disableLog uint32
  21. disableStat uint32
  22. options logOptions
  23. writer = new(atomicWriter)
  24. setupOnce sync.Once
  25. )
  26. type (
  27. // LogField is a key-value pair that will be added to the log entry.
  28. LogField struct {
  29. Key string
  30. Value interface{}
  31. }
  32. // LogOption defines the method to customize the logging.
  33. LogOption func(options *logOptions)
  34. logEntry map[string]interface{}
  35. logOptions struct {
  36. gzipEnabled bool
  37. logStackCooldownMills int
  38. keepDays int
  39. maxBackups int
  40. maxSize int
  41. rotationRule string
  42. }
  43. )
  44. // Alert alerts v in alert level, and the message is written to error log.
  45. func Alert(v string) {
  46. getWriter().Alert(v)
  47. }
  48. // Close closes the logging.
  49. func Close() error {
  50. if w := writer.Swap(nil); w != nil {
  51. return w.(io.Closer).Close()
  52. }
  53. return nil
  54. }
  55. // Debug writes v into access log.
  56. func Debug(v ...interface{}) {
  57. writeDebug(fmt.Sprint(v...))
  58. }
  59. // Debugf writes v with format into access log.
  60. func Debugf(format string, v ...interface{}) {
  61. writeDebug(fmt.Sprintf(format, v...))
  62. }
  63. // Debugv writes v into access log with json content.
  64. func Debugv(v interface{}) {
  65. writeDebug(v)
  66. }
  67. // Debugw writes msg along with fields into access log.
  68. func Debugw(msg string, fields ...LogField) {
  69. writeDebug(msg, fields...)
  70. }
  71. // Disable disables the logging.
  72. func Disable() {
  73. atomic.StoreUint32(&disableLog, 1)
  74. writer.Store(nopWriter{})
  75. }
  76. // DisableStat disables the stat logs.
  77. func DisableStat() {
  78. atomic.StoreUint32(&disableStat, 1)
  79. }
  80. // Error writes v into error log.
  81. func Error(v ...interface{}) {
  82. writeError(fmt.Sprint(v...))
  83. }
  84. // Errorf writes v with format into error log.
  85. func Errorf(format string, v ...interface{}) {
  86. writeError(fmt.Errorf(format, v...).Error())
  87. }
  88. // ErrorStack writes v along with call stack into error log.
  89. func ErrorStack(v ...interface{}) {
  90. // there is newline in stack string
  91. writeStack(fmt.Sprint(v...))
  92. }
  93. // ErrorStackf writes v along with call stack in format into error log.
  94. func ErrorStackf(format string, v ...interface{}) {
  95. // there is newline in stack string
  96. writeStack(fmt.Sprintf(format, v...))
  97. }
  98. // Errorv writes v into error log with json content.
  99. // No call stack attached, because not elegant to pack the messages.
  100. func Errorv(v interface{}) {
  101. writeError(v)
  102. }
  103. // Errorw writes msg along with fields into error log.
  104. func Errorw(msg string, fields ...LogField) {
  105. writeError(msg, fields...)
  106. }
  107. // Field returns a LogField for the given key and value.
  108. func Field(key string, value interface{}) LogField {
  109. switch val := value.(type) {
  110. case error:
  111. return LogField{Key: key, Value: val.Error()}
  112. case []error:
  113. var errs []string
  114. for _, err := range val {
  115. errs = append(errs, err.Error())
  116. }
  117. return LogField{Key: key, Value: errs}
  118. case time.Duration:
  119. return LogField{Key: key, Value: fmt.Sprint(val)}
  120. case []time.Duration:
  121. var durs []string
  122. for _, dur := range val {
  123. durs = append(durs, fmt.Sprint(dur))
  124. }
  125. return LogField{Key: key, Value: durs}
  126. case []time.Time:
  127. var times []string
  128. for _, t := range val {
  129. times = append(times, fmt.Sprint(t))
  130. }
  131. return LogField{Key: key, Value: times}
  132. case fmt.Stringer:
  133. return LogField{Key: key, Value: val.String()}
  134. case []fmt.Stringer:
  135. var strs []string
  136. for _, str := range val {
  137. strs = append(strs, str.String())
  138. }
  139. return LogField{Key: key, Value: strs}
  140. default:
  141. return LogField{Key: key, Value: val}
  142. }
  143. }
  144. // Info writes v into access log.
  145. func Info(v ...interface{}) {
  146. writeInfo(fmt.Sprint(v...))
  147. }
  148. // Infof writes v with format into access log.
  149. func Infof(format string, v ...interface{}) {
  150. writeInfo(fmt.Sprintf(format, v...))
  151. }
  152. // Infov writes v into access log with json content.
  153. func Infov(v interface{}) {
  154. writeInfo(v)
  155. }
  156. // Infow writes msg along with fields into access log.
  157. func Infow(msg string, fields ...LogField) {
  158. writeInfo(msg, fields...)
  159. }
  160. // Must checks if err is nil, otherwise logs the error and exits.
  161. func Must(err error) {
  162. if err == nil {
  163. return
  164. }
  165. msg := err.Error()
  166. log.Print(msg)
  167. getWriter().Severe(msg)
  168. os.Exit(1)
  169. }
  170. // MustSetup sets up logging with given config c. It exits on error.
  171. func MustSetup(c LogConf) {
  172. Must(SetUp(c))
  173. }
  174. // Reset clears the writer and resets the log level.
  175. func Reset() Writer {
  176. return writer.Swap(nil)
  177. }
  178. // SetLevel sets the logging level. It can be used to suppress some logs.
  179. func SetLevel(level uint32) {
  180. atomic.StoreUint32(&logLevel, level)
  181. }
  182. // SetWriter sets the logging writer. It can be used to customize the logging.
  183. func SetWriter(w Writer) {
  184. if atomic.LoadUint32(&disableLog) == 0 {
  185. writer.Store(w)
  186. }
  187. }
  188. // SetUp sets up the logx. If already set up, just return nil.
  189. // we allow SetUp to be called multiple times, because for example
  190. // we need to allow different service frameworks to initialize logx respectively.
  191. func SetUp(c LogConf) (err error) {
  192. // Just ignore the subsequent SetUp calls.
  193. // Because multiple services in one process might call SetUp respectively.
  194. // Need to wait for the first caller to complete the execution.
  195. setupOnce.Do(func() {
  196. setupLogLevel(c)
  197. if len(c.TimeFormat) > 0 {
  198. timeFormat = c.TimeFormat
  199. }
  200. switch c.Encoding {
  201. case plainEncoding:
  202. atomic.StoreUint32(&encoding, plainEncodingType)
  203. default:
  204. atomic.StoreUint32(&encoding, jsonEncodingType)
  205. }
  206. switch c.Mode {
  207. case fileMode:
  208. err = setupWithFiles(c)
  209. case volumeMode:
  210. err = setupWithVolume(c)
  211. default:
  212. setupWithConsole()
  213. }
  214. })
  215. return
  216. }
  217. // Severe writes v into severe log.
  218. func Severe(v ...interface{}) {
  219. writeSevere(fmt.Sprint(v...))
  220. }
  221. // Severef writes v with format into severe log.
  222. func Severef(format string, v ...interface{}) {
  223. writeSevere(fmt.Sprintf(format, v...))
  224. }
  225. // Slow writes v into slow log.
  226. func Slow(v ...interface{}) {
  227. writeSlow(fmt.Sprint(v...))
  228. }
  229. // Slowf writes v with format into slow log.
  230. func Slowf(format string, v ...interface{}) {
  231. writeSlow(fmt.Sprintf(format, v...))
  232. }
  233. // Slowv writes v into slow log with json content.
  234. func Slowv(v interface{}) {
  235. writeSlow(v)
  236. }
  237. // Sloww writes msg along with fields into slow log.
  238. func Sloww(msg string, fields ...LogField) {
  239. writeSlow(msg, fields...)
  240. }
  241. // Stat writes v into stat log.
  242. func Stat(v ...interface{}) {
  243. writeStat(fmt.Sprint(v...))
  244. }
  245. // Statf writes v with format into stat log.
  246. func Statf(format string, v ...interface{}) {
  247. writeStat(fmt.Sprintf(format, v...))
  248. }
  249. // WithCooldownMillis customizes logging on writing call stack interval.
  250. func WithCooldownMillis(millis int) LogOption {
  251. return func(opts *logOptions) {
  252. opts.logStackCooldownMills = millis
  253. }
  254. }
  255. // WithKeepDays customizes logging to keep logs with days.
  256. func WithKeepDays(days int) LogOption {
  257. return func(opts *logOptions) {
  258. opts.keepDays = days
  259. }
  260. }
  261. // WithGzip customizes logging to automatically gzip the log files.
  262. func WithGzip() LogOption {
  263. return func(opts *logOptions) {
  264. opts.gzipEnabled = true
  265. }
  266. }
  267. // WithMaxBackups customizes how many log files backups will be kept.
  268. func WithMaxBackups(count int) LogOption {
  269. return func(opts *logOptions) {
  270. opts.maxBackups = count
  271. }
  272. }
  273. // WithMaxSize customizes how much space the writing log file can take up.
  274. func WithMaxSize(size int) LogOption {
  275. return func(opts *logOptions) {
  276. opts.maxSize = size
  277. }
  278. }
  279. // WithRotation customizes which log rotation rule to use.
  280. func WithRotation(r string) LogOption {
  281. return func(opts *logOptions) {
  282. opts.rotationRule = r
  283. }
  284. }
  285. func addCaller(fields ...LogField) []LogField {
  286. return append(fields, Field(callerKey, getCaller(callerDepth)))
  287. }
  288. func createOutput(path string) (io.WriteCloser, error) {
  289. if len(path) == 0 {
  290. return nil, ErrLogPathNotSet
  291. }
  292. switch options.rotationRule {
  293. case sizeRotationRule:
  294. return NewLogger(path, NewSizeLimitRotateRule(path, backupFileDelimiter, options.keepDays,
  295. options.maxSize, options.maxBackups, options.gzipEnabled), options.gzipEnabled)
  296. default:
  297. return NewLogger(path, DefaultRotateRule(path, backupFileDelimiter, options.keepDays,
  298. options.gzipEnabled), options.gzipEnabled)
  299. }
  300. }
  301. func getWriter() Writer {
  302. w := writer.Load()
  303. if w == nil {
  304. w = writer.StoreIfNil(newConsoleWriter())
  305. }
  306. return w
  307. }
  308. func handleOptions(opts []LogOption) {
  309. for _, opt := range opts {
  310. opt(&options)
  311. }
  312. }
  313. func setupLogLevel(c LogConf) {
  314. switch c.Level {
  315. case levelDebug:
  316. SetLevel(DebugLevel)
  317. case levelInfo:
  318. SetLevel(InfoLevel)
  319. case levelError:
  320. SetLevel(ErrorLevel)
  321. case levelSevere:
  322. SetLevel(SevereLevel)
  323. }
  324. }
  325. func setupWithConsole() {
  326. SetWriter(newConsoleWriter())
  327. }
  328. func setupWithFiles(c LogConf) error {
  329. w, err := newFileWriter(c)
  330. if err != nil {
  331. return err
  332. }
  333. SetWriter(w)
  334. return nil
  335. }
  336. func setupWithVolume(c LogConf) error {
  337. if len(c.ServiceName) == 0 {
  338. return ErrLogServiceNameNotSet
  339. }
  340. c.Path = path.Join(c.Path, c.ServiceName, sysx.Hostname())
  341. return setupWithFiles(c)
  342. }
  343. func shallLog(level uint32) bool {
  344. return atomic.LoadUint32(&logLevel) <= level
  345. }
  346. func shallLogStat() bool {
  347. return atomic.LoadUint32(&disableStat) == 0
  348. }
  349. func writeDebug(val interface{}, fields ...LogField) {
  350. if shallLog(DebugLevel) {
  351. getWriter().Debug(val, addCaller(fields...)...)
  352. }
  353. }
  354. func writeError(val interface{}, fields ...LogField) {
  355. if shallLog(ErrorLevel) {
  356. getWriter().Error(val, addCaller(fields...)...)
  357. }
  358. }
  359. func writeInfo(val interface{}, fields ...LogField) {
  360. if shallLog(InfoLevel) {
  361. getWriter().Info(val, addCaller(fields...)...)
  362. }
  363. }
  364. func writeSevere(msg string) {
  365. if shallLog(SevereLevel) {
  366. getWriter().Severe(fmt.Sprintf("%s\n%s", msg, string(debug.Stack())))
  367. }
  368. }
  369. func writeSlow(val interface{}, fields ...LogField) {
  370. if shallLog(ErrorLevel) {
  371. getWriter().Slow(val, addCaller(fields...)...)
  372. }
  373. }
  374. func writeStack(msg string) {
  375. if shallLog(ErrorLevel) {
  376. getWriter().Stack(fmt.Sprintf("%s\n%s", msg, string(debug.Stack())))
  377. }
  378. }
  379. func writeStat(msg string) {
  380. if shallLogStat() && shallLog(InfoLevel) {
  381. getWriter().Stat(msg, addCaller()...)
  382. }
  383. }