log.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. // Copyright 2019 PingCAP, Inc.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // See the License for the specific language governing permissions and
  12. // limitations under the License.
  13. package log
  14. import (
  15. "errors"
  16. "os"
  17. "sync"
  18. "sync/atomic"
  19. "go.uber.org/zap"
  20. "go.uber.org/zap/zapcore"
  21. lumberjack "gopkg.in/natefinch/lumberjack.v2"
  22. )
  23. var _globalL, _globalP, _globalS atomic.Value
  24. var registerOnce sync.Once
  25. func init() {
  26. l, p := newStdLogger()
  27. _globalL.Store(l)
  28. _globalP.Store(p)
  29. s := _globalL.Load().(*zap.Logger).Sugar()
  30. _globalS.Store(s)
  31. }
  32. // InitLogger initializes a zap logger.
  33. func InitLogger(cfg *Config, opts ...zap.Option) (*zap.Logger, *ZapProperties, error) {
  34. var output zapcore.WriteSyncer
  35. if len(cfg.File.Filename) > 0 {
  36. lg, err := initFileLog(&cfg.File)
  37. if err != nil {
  38. return nil, nil, err
  39. }
  40. output = zapcore.AddSync(lg)
  41. } else {
  42. stdOut, _, err := zap.Open([]string{"stdout"}...)
  43. if err != nil {
  44. return nil, nil, err
  45. }
  46. output = stdOut
  47. }
  48. return InitLoggerWithWriteSyncer(cfg, output, opts...)
  49. }
  50. // InitLoggerWithWriteSyncer initializes a zap logger with specified write syncer.
  51. func InitLoggerWithWriteSyncer(cfg *Config, output zapcore.WriteSyncer, opts ...zap.Option) (*zap.Logger, *ZapProperties, error) {
  52. level := zap.NewAtomicLevel()
  53. err := level.UnmarshalText([]byte(cfg.Level))
  54. if err != nil {
  55. return nil, nil, err
  56. }
  57. encoder := newZapTextEncoder(cfg)
  58. registerOnce.Do(func() {
  59. err = zap.RegisterEncoder(ZapEncodingName, func(zapcore.EncoderConfig) (zapcore.Encoder, error) {
  60. return encoder, nil
  61. })
  62. })
  63. if err != nil {
  64. return nil, nil, err
  65. }
  66. core := NewTextCore(encoder, output, level)
  67. opts = append(cfg.buildOptions(output), opts...)
  68. lg := zap.New(core, opts...)
  69. r := &ZapProperties{
  70. Core: core,
  71. Syncer: output,
  72. Level: level,
  73. }
  74. return lg, r, nil
  75. }
  76. // initFileLog initializes file based logging options.
  77. func initFileLog(cfg *FileLogConfig) (*lumberjack.Logger, error) {
  78. if st, err := os.Stat(cfg.Filename); err == nil {
  79. if st.IsDir() {
  80. return nil, errors.New("can't use directory as log file name")
  81. }
  82. }
  83. if cfg.MaxSize == 0 {
  84. cfg.MaxSize = defaultLogMaxSize
  85. }
  86. // use lumberjack to logrotate
  87. return &lumberjack.Logger{
  88. Filename: cfg.Filename,
  89. MaxSize: cfg.MaxSize,
  90. MaxBackups: cfg.MaxBackups,
  91. MaxAge: cfg.MaxDays,
  92. LocalTime: true,
  93. }, nil
  94. }
  95. func newStdLogger() (*zap.Logger, *ZapProperties) {
  96. conf := &Config{Level: "info", File: FileLogConfig{}}
  97. lg, r, _ := InitLogger(conf)
  98. return lg, r
  99. }
  100. // L returns the global Logger, which can be reconfigured with ReplaceGlobals.
  101. // It's safe for concurrent use.
  102. func L() *zap.Logger {
  103. return _globalL.Load().(*zap.Logger)
  104. }
  105. // S returns the global SugaredLogger, which can be reconfigured with
  106. // ReplaceGlobals. It's safe for concurrent use.
  107. func S() *zap.SugaredLogger {
  108. return _globalS.Load().(*zap.SugaredLogger)
  109. }
  110. // ReplaceGlobals replaces the global Logger and SugaredLogger.
  111. // It's safe for concurrent use.
  112. func ReplaceGlobals(logger *zap.Logger, props *ZapProperties) {
  113. _globalL.Store(logger)
  114. _globalS.Store(logger.Sugar())
  115. _globalP.Store(props)
  116. }
  117. // Sync flushes any buffered log entries.
  118. func Sync() error {
  119. err := L().Sync()
  120. if err != nil {
  121. return err
  122. }
  123. return S().Sync()
  124. }