github.com/jxskiss/gopkg@v0.17.3/zlog/global.go (about) 1 package zlog 2 3 import ( 4 "context" 5 "fmt" 6 "runtime" 7 8 "go.uber.org/zap" 9 ) 10 11 var ( 12 gL, gL_1 *zap.Logger 13 gS, gS_1 *zap.SugaredLogger 14 gP *Properties 15 ) 16 17 func init() { 18 ReplaceGlobals(mustNewGlobalLogger(&Config{})) 19 } 20 21 // Properties records some information about the global config. 22 type Properties struct { 23 cfg GlobalConfig 24 level atomicLevel 25 } 26 27 func (p *Properties) setup() func() { 28 if p.cfg.MethodNameKey == "" { 29 p.cfg.MethodNameKey = defaultMethodNameKey 30 } 31 var resetStdLog = func() {} 32 if p.cfg.RedirectStdLog { 33 resetStdLog = zap.RedirectStdLog(L()) 34 } 35 oldDisableTrace := disableTrace 36 var resetDisableTrace = func() { 37 disableTrace = oldDisableTrace 38 } 39 disableTrace = p.cfg.DisableTrace 40 return func() { 41 resetDisableTrace() 42 resetStdLog() 43 } 44 } 45 46 // GetLevel gets the logging level of the logger. 47 func (p *Properties) GetLevel() Level { return p.level.Level() } 48 49 // SetLevel modifies the logging level of the logger. 50 func (p *Properties) SetLevel(lvl Level) { p.level.SetLevel(lvl) } 51 52 // SetupGlobals setups the global loggers in this package and zap library. 53 // By default, global loggers are set with default configuration with info 54 // level and json format, you may use this function to change the default 55 // loggers. 56 // 57 // See Config and GlobalConfig for available configurations. 58 // 59 // It should be called at program startup, library code shall not touch 60 // this function. 61 func SetupGlobals(cfg *Config, opts ...zap.Option) { 62 ReplaceGlobals(mustNewGlobalLogger(cfg, opts...)) 63 } 64 65 func mustNewGlobalLogger(cfg *Config, opts ...zap.Option) (*zap.Logger, *Properties) { 66 logger, props, err := New(cfg, opts...) 67 if err != nil { 68 panic(fmt.Sprintf("invalid config to initialize logger: %v", err)) 69 } 70 return logger, props 71 } 72 73 // ReplaceGlobals replaces the global Logger and SugaredLogger, and returns a 74 // function to restore the original values. 75 // 76 // It should be called at program startup, library code shall not touch 77 // this function. 78 func ReplaceGlobals(logger *zap.Logger, props *Properties) func() { 79 oldL, oldP := gL, gP 80 81 gL = logger 82 gS = logger.Sugar() 83 gP = props 84 85 gL_1 = logger.WithOptions(zap.AddCallerSkip(1)) 86 gS_1 = gL_1.Sugar() 87 88 resetProps := props.setup() 89 zap.ReplaceGlobals(logger) 90 91 return func() { 92 resetProps() 93 ReplaceGlobals(oldL, oldP) 94 } 95 } 96 97 // SetDevelopment sets the global logger in development mode, and redirects 98 // output from the standard log library's package-global logger to the 99 // global logger in this package. 100 // 101 // It should only be called at program startup, when you run in development 102 // mode, for production mode, please check SetupGlobals and ReplaceGlobals. 103 func SetDevelopment() { 104 cfg := &Config{} 105 cfg.Development = true 106 cfg.RedirectStdLog = true 107 ReplaceGlobals(mustNewGlobalLogger(cfg)) 108 } 109 110 // GetLevel gets the global logging level. 111 func GetLevel() Level { return gP.GetLevel() } 112 113 // SetLevel modifies the global logging level on the fly. 114 // It's safe for concurrent use. 115 func SetLevel(lvl Level) { gP.SetLevel(lvl) } 116 117 // L returns the global Logger, which can be reconfigured with 118 // SetupGlobals and ReplaceGlobals. 119 func L() *zap.Logger { return gL } 120 121 // S returns the global SugaredLogger, which can be reconfigured with 122 // SetupGlobals and ReplaceGlobals. 123 func S() *zap.SugaredLogger { return gS } 124 125 // Sync flushes any buffered log entries. 126 func Sync() error { 127 if err := L().Sync(); err != nil { 128 return err 129 } 130 if err := S().Sync(); err != nil { 131 return err 132 } 133 if err := _l().Sync(); err != nil { 134 return err 135 } 136 if err := _s().Sync(); err != nil { 137 return err 138 } 139 return nil 140 } 141 142 // -------- global logging functions -------- // 143 144 func _l() *zap.Logger { return gL_1 } 145 func _s() *zap.SugaredLogger { return gS_1 } 146 147 func Debug(msg string, fields ...zap.Field) { _l().Debug(msg, fields...) } 148 func Info(msg string, fields ...zap.Field) { _l().Info(msg, fields...) } 149 func Warn(msg string, fields ...zap.Field) { _l().Warn(msg, fields...) } 150 func Error(msg string, fields ...zap.Field) { _l().Error(msg, fields...) } 151 func DPanic(msg string, fields ...zap.Field) { _l().DPanic(msg, fields...) } 152 func Panic(msg string, fields ...zap.Field) { _l().Panic(msg, fields...) } 153 func Fatal(msg string, fields ...zap.Field) { _l().Fatal(msg, fields...) } 154 155 func Debugf(format string, args ...interface{}) { _s().Debugf(format, args...) } 156 func Infof(format string, args ...interface{}) { _s().Infof(format, args...) } 157 func Warnf(format string, args ...interface{}) { _s().Warnf(format, args...) } 158 func Errorf(format string, args ...interface{}) { _s().Errorf(format, args...) } 159 func DPanicf(format string, args ...interface{}) { _s().DPanicf(format, args...) } 160 func Panicf(format string, args ...interface{}) { _s().Panicf(format, args...) } 161 func Fatalf(format string, args ...interface{}) { _s().Fatalf(format, args...) } 162 163 // Print uses fmt.Sprint to log a message at InfoLevel if it's enabled. 164 // 165 // It has same signature with log.Print, which helps to migrate from the 166 // standard library to this package. 167 func Print(args ...interface{}) { _l().Info(fmt.Sprint(args...)) } 168 169 // Printf logs a message at InfoLevel if it's enabled. 170 // 171 // It has same signature with log.Printf, which helps to migrate from the 172 // standard library to this package. 173 func Printf(format string, args ...interface{}) { _s().Infof(format, args...) } 174 175 // -------- utility functions -------- // 176 177 // With creates a child logger and adds structured context to it. 178 // Fields added to the child don't affect the parent, and vice versa. 179 func With(fields ...zap.Field) *zap.Logger { 180 return L().With(fields...) 181 } 182 183 // WithCtx creates a child logger and customizes its behavior using context 184 // data (e.g. adding fields, dynamically change logging level, etc.) 185 // 186 // If the ctx is created by WithBuilder, it carries a Builder instance, 187 // this function uses that Builder to build the logger, else it calls 188 // GlobalConfig.CtxFunc to get CtxResult from ctx. In case that 189 // GlobalConfig.CtxFunc is not configured globally, it logs an error 190 // message at DPANIC level. 191 // 192 // Also see GlobalConfig.CtxFunc, CtxArgs and CtxResult for more details. 193 func WithCtx(ctx context.Context, extra ...zap.Field) *zap.Logger { 194 if ctx == nil { 195 return L().With(extra...) 196 } 197 if builder := getCtxBuilder(ctx); builder != nil { 198 return builder.With(extra...).Build() 199 } 200 ctxFunc := gP.cfg.CtxFunc 201 if ctxFunc == nil { 202 L().DPanic("calling WithCtx without CtxFunc configured") 203 return L().With(extra...) 204 } 205 ctxResult := ctxFunc(ctx, CtxArgs{}) 206 return B(nil).withCtxResult(ctxResult).With(extra...).Build() 207 } 208 209 // WithMethod creates a child logger and adds the caller's method name 210 // to the logger. 211 // It also adds the given extra fields to the logger. 212 func WithMethod(extra ...zap.Field) *zap.Logger { 213 methodName, _, _, ok := getCaller(1) 214 if !ok { 215 return L().With(extra...) 216 } 217 methodNameKey := gP.cfg.MethodNameKey 218 if len(extra) == 0 { 219 return L().With(zap.String(methodNameKey, methodName)) 220 } 221 fields := append([]zap.Field{zap.String(methodNameKey, methodName)}, extra...) 222 return L().With(fields...) 223 } 224 225 // Named creates a child logger and adds a new name segment to the logger's 226 // name. By default, loggers are unnamed. 227 // It also adds the given extra fields to the logger. 228 func Named(name string, extra ...zap.Field) *zap.Logger { 229 return L().Named(name).With(extra...) 230 } 231 232 func getCaller(skip int) (name, file string, line int, ok bool) { 233 pc, file, line, ok := runtime.Caller(skip + 1) 234 if !ok { 235 return 236 } 237 name = runtime.FuncForPC(pc).Name() 238 for i := len(name) - 1; i >= 0; i-- { 239 if name[i] == '/' { 240 name = name[i+1:] 241 break 242 } 243 } 244 pathSepCnt := 0 245 for i := len(file) - 1; i >= 0; i-- { 246 if file[i] == '/' { 247 pathSepCnt++ 248 if pathSepCnt == 2 { 249 file = file[i+1:] 250 break 251 } 252 } 253 } 254 return 255 }