logs.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  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 (c.DisableStat){
  198. DisableStat()
  199. }
  200. if len(c.TimeFormat) > 0 {
  201. timeFormat = c.TimeFormat
  202. }
  203. switch c.Encoding {
  204. case plainEncoding:
  205. atomic.StoreUint32(&encoding, plainEncodingType)
  206. default:
  207. atomic.StoreUint32(&encoding, jsonEncodingType)
  208. }
  209. switch c.Mode {
  210. case fileMode:
  211. err = setupWithFiles(c)
  212. case volumeMode:
  213. err = setupWithVolume(c)
  214. default:
  215. setupWithConsole()
  216. }
  217. })
  218. return
  219. }
  220. // Severe writes v into severe log.
  221. func Severe(v ...interface{}) {
  222. writeSevere(fmt.Sprint(v...))
  223. }
  224. // Severef writes v with format into severe log.
  225. func Severef(format string, v ...interface{}) {
  226. writeSevere(fmt.Sprintf(format, v...))
  227. }
  228. // Slow writes v into slow log.
  229. func Slow(v ...interface{}) {
  230. writeSlow(fmt.Sprint(v...))
  231. }
  232. // Slowf writes v with format into slow log.
  233. func Slowf(format string, v ...interface{}) {
  234. writeSlow(fmt.Sprintf(format, v...))
  235. }
  236. // Slowv writes v into slow log with json content.
  237. func Slowv(v interface{}) {
  238. writeSlow(v)
  239. }
  240. // Sloww writes msg along with fields into slow log.
  241. func Sloww(msg string, fields ...LogField) {
  242. writeSlow(msg, fields...)
  243. }
  244. // Stat writes v into stat log.
  245. func Stat(v ...interface{}) {
  246. writeStat(fmt.Sprint(v...))
  247. }
  248. // Statf writes v with format into stat log.
  249. func Statf(format string, v ...interface{}) {
  250. writeStat(fmt.Sprintf(format, v...))
  251. }
  252. // WithCooldownMillis customizes logging on writing call stack interval.
  253. func WithCooldownMillis(millis int) LogOption {
  254. return func(opts *logOptions) {
  255. opts.logStackCooldownMills = millis
  256. }
  257. }
  258. // WithKeepDays customizes logging to keep logs with days.
  259. func WithKeepDays(days int) LogOption {
  260. return func(opts *logOptions) {
  261. opts.keepDays = days
  262. }
  263. }
  264. // WithGzip customizes logging to automatically gzip the log files.
  265. func WithGzip() LogOption {
  266. return func(opts *logOptions) {
  267. opts.gzipEnabled = true
  268. }
  269. }
  270. // WithMaxBackups customizes how many log files backups will be kept.
  271. func WithMaxBackups(count int) LogOption {
  272. return func(opts *logOptions) {
  273. opts.maxBackups = count
  274. }
  275. }
  276. // WithMaxSize customizes how much space the writing log file can take up.
  277. func WithMaxSize(size int) LogOption {
  278. return func(opts *logOptions) {
  279. opts.maxSize = size
  280. }
  281. }
  282. // WithRotation customizes which log rotation rule to use.
  283. func WithRotation(r string) LogOption {
  284. return func(opts *logOptions) {
  285. opts.rotationRule = r
  286. }
  287. }
  288. func addCaller(fields ...LogField) []LogField {
  289. return append(fields, Field(callerKey, getCaller(callerDepth)))
  290. }
  291. func createOutput(path string) (io.WriteCloser, error) {
  292. if len(path) == 0 {
  293. return nil, ErrLogPathNotSet
  294. }
  295. switch options.rotationRule {
  296. case sizeRotationRule:
  297. return NewLogger(path, NewSizeLimitRotateRule(path, backupFileDelimiter, options.keepDays,
  298. options.maxSize, options.maxBackups, options.gzipEnabled), options.gzipEnabled)
  299. default:
  300. return NewLogger(path, DefaultRotateRule(path, backupFileDelimiter, options.keepDays,
  301. options.gzipEnabled), options.gzipEnabled)
  302. }
  303. }
  304. func getWriter() Writer {
  305. w := writer.Load()
  306. if w == nil {
  307. w = writer.StoreIfNil(newConsoleWriter())
  308. }
  309. return w
  310. }
  311. func handleOptions(opts []LogOption) {
  312. for _, opt := range opts {
  313. opt(&options)
  314. }
  315. }
  316. func setupLogLevel(c LogConf) {
  317. switch c.Level {
  318. case levelDebug:
  319. SetLevel(DebugLevel)
  320. case levelInfo:
  321. SetLevel(InfoLevel)
  322. case levelError:
  323. SetLevel(ErrorLevel)
  324. case levelSevere:
  325. SetLevel(SevereLevel)
  326. }
  327. }
  328. func setupWithConsole() {
  329. SetWriter(newConsoleWriter())
  330. }
  331. func setupWithFiles(c LogConf) error {
  332. w, err := newFileWriter(c)
  333. if err != nil {
  334. return err
  335. }
  336. SetWriter(w)
  337. return nil
  338. }
  339. func setupWithVolume(c LogConf) error {
  340. if len(c.ServiceName) == 0 {
  341. return ErrLogServiceNameNotSet
  342. }
  343. c.Path = path.Join(c.Path, c.ServiceName, sysx.Hostname())
  344. return setupWithFiles(c)
  345. }
  346. func shallLog(level uint32) bool {
  347. return atomic.LoadUint32(&logLevel) <= level
  348. }
  349. func shallLogStat() bool {
  350. return atomic.LoadUint32(&disableStat) == 0
  351. }
  352. func writeDebug(val interface{}, fields ...LogField) {
  353. if shallLog(DebugLevel) {
  354. getWriter().Debug(val, addCaller(fields...)...)
  355. }
  356. }
  357. func writeError(val interface{}, fields ...LogField) {
  358. if shallLog(ErrorLevel) {
  359. getWriter().Error(val, addCaller(fields...)...)
  360. }
  361. }
  362. func writeInfo(val interface{}, fields ...LogField) {
  363. if shallLog(InfoLevel) {
  364. getWriter().Info(val, addCaller(fields...)...)
  365. }
  366. }
  367. func writeSevere(msg string) {
  368. if shallLog(SevereLevel) {
  369. getWriter().Severe(fmt.Sprintf("%s\n%s", msg, string(debug.Stack())))
  370. }
  371. }
  372. func writeSlow(val interface{}, fields ...LogField) {
  373. if shallLog(ErrorLevel) {
  374. getWriter().Slow(val, addCaller(fields...)...)
  375. }
  376. }
  377. func writeStack(msg string) {
  378. if shallLog(ErrorLevel) {
  379. getWriter().Stack(fmt.Sprintf("%s\n%s", msg, string(debug.Stack())))
  380. }
  381. }
  382. func writeStat(msg string) {
  383. if shallLogStat() && shallLog(InfoLevel) {
  384. getWriter().Stat(msg, addCaller()...)
  385. }
  386. }