github.com/polarismesh/polaris@v1.17.8/common/log/config.go (about) 1 /** 2 * Tencent is pleased to support the open source community by making Polaris available. 3 * 4 * Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. 5 * 6 * Licensed under the BSD 3-Clause License (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * https://opensource.org/licenses/BSD-3-Clause 11 * 12 * Unless required by applicable law or agreed to in writing, software distributed 13 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 14 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 15 * specific language governing permissions and limitations under the License. 16 */ 17 18 // Package log 19 // Once configured, this package intercepts the output of the standard golang "log" package as well as anything 20 // sent to the global zap logger (zap.L()). 21 package log 22 23 import ( 24 "fmt" 25 "net/url" 26 "os" 27 "path/filepath" 28 "time" 29 30 "github.com/hashicorp/go-multierror" 31 "github.com/natefinch/lumberjack" 32 "go.uber.org/zap" 33 "go.uber.org/zap/zapcore" 34 "go.uber.org/zap/zapgrpc" 35 "google.golang.org/grpc/grpclog" 36 37 "github.com/polarismesh/polaris/common/timewheel" 38 ) 39 40 // none is used to disable logging output as well as to disable stack tracing. 41 const none zapcore.Level = 100 42 43 var levelToZap = map[Level]zapcore.Level{ 44 DebugLevel: zapcore.DebugLevel, 45 InfoLevel: zapcore.InfoLevel, 46 WarnLevel: zapcore.WarnLevel, 47 ErrorLevel: zapcore.ErrorLevel, 48 FatalLevel: zapcore.FatalLevel, 49 NoneLevel: none, 50 } 51 52 // functions that can be replaced in a test setting 53 type patchTable struct { 54 write func(ent zapcore.Entry, fields []zapcore.Field) error 55 sync func() error 56 exitProcess func(code int) 57 errorSink zapcore.WriteSyncer 58 } 59 60 func init() { 61 // use our defaults for starters so that logging works even before everything is fully configured 62 _ = Configure(DefaultOptions()) 63 } 64 65 // prepZap is a utility function used by the Configure function. 66 func prepZap(options *Options) ([]zapcore.Core, zapcore.Core, zapcore.WriteSyncer, *lumberjack.Logger, error) { 67 encCfg := zapcore.EncoderConfig{ 68 TimeKey: "time", 69 LevelKey: "level", 70 NameKey: "scope", 71 CallerKey: "caller", 72 MessageKey: "msg", 73 StacktraceKey: "stack", 74 LineEnding: zapcore.DefaultLineEnding, 75 EncodeLevel: zapcore.LowercaseLevelEncoder, 76 EncodeCaller: zapcore.ShortCallerEncoder, 77 EncodeDuration: zapcore.StringDurationEncoder, 78 EncodeTime: formatDate, 79 } 80 81 if options.OnlyContent { 82 encCfg.EncodeLevel = func(l zapcore.Level, pae zapcore.PrimitiveArrayEncoder) {} 83 encCfg.EncodeCaller = func(ec zapcore.EntryCaller, pae zapcore.PrimitiveArrayEncoder) {} 84 encCfg.EncodeName = func(s string, pae zapcore.PrimitiveArrayEncoder) {} 85 encCfg.EncodeTime = func(t time.Time, pae zapcore.PrimitiveArrayEncoder) {} 86 } 87 88 var enc zapcore.Encoder 89 if options.JSONEncoding { 90 enc = zapcore.NewJSONEncoder(encCfg) 91 } else { 92 enc = zapcore.NewConsoleEncoder(encCfg) 93 } 94 95 var rotateSink zapcore.WriteSyncer 96 var l *lumberjack.Logger 97 if len(options.RotateOutputPath) > 0 { 98 l = &lumberjack.Logger{ 99 Filename: options.RotateOutputPath, 100 MaxSize: options.RotationMaxSize, 101 MaxBackups: options.RotationMaxBackups, 102 MaxAge: options.RotationMaxAge, 103 Compress: options.Compress, 104 } 105 rotateSink = zapcore.AddSync(l) 106 } 107 108 err := createPathIfNotExist(options.ErrorOutputPaths...) 109 if err != nil { 110 return nil, nil, nil, nil, err 111 } 112 errSink, closeErrorSink, err := zap.Open(options.ErrorOutputPaths...) 113 if err != nil { 114 return nil, nil, nil, nil, err 115 } 116 117 var outputSink zapcore.WriteSyncer 118 if len(options.OutputPaths) > 0 { 119 err := createPathIfNotExist(options.OutputPaths...) 120 if err != nil { 121 return nil, nil, nil, nil, err 122 } 123 outputSink, _, err = zap.Open(options.OutputPaths...) 124 if err != nil { 125 closeErrorSink() 126 return nil, nil, nil, nil, err 127 } 128 } 129 130 var sink zapcore.WriteSyncer 131 if rotateSink != nil && outputSink != nil { 132 sink = zapcore.NewMultiWriteSyncer(outputSink, rotateSink) 133 } else if rotateSink != nil { 134 sink = rotateSink 135 } else if outputSink != nil { 136 sink = outputSink 137 } else { 138 sink = zapcore.AddSync(os.Stdout) 139 } 140 141 var enabler zap.LevelEnablerFunc = func(lvl zapcore.Level) bool { 142 switch lvl { 143 case zapcore.FatalLevel: 144 return defaultScope.FatalEnabled() 145 case zapcore.ErrorLevel: 146 return defaultScope.ErrorEnabled() 147 case zapcore.WarnLevel: 148 return defaultScope.WarnEnabled() 149 case zapcore.InfoLevel: 150 return defaultScope.InfoEnabled() 151 } 152 return defaultScope.DebugEnabled() 153 } 154 155 var errCore zapcore.Core 156 if len(options.ErrorRotateOutputPath) > 0 { 157 errRotateSink := zapcore.AddSync(&lumberjack.Logger{ 158 Filename: options.ErrorRotateOutputPath, 159 MaxSize: options.RotationMaxSize, 160 MaxBackups: options.RotationMaxBackups, 161 MaxAge: options.RotationMaxAge, 162 }) 163 errCore = zapcore.NewCore(enc, errRotateSink, zap.NewAtomicLevelAt(zapcore.ErrorLevel)) 164 } 165 166 cores := make([]zapcore.Core, 0) 167 cores = append(cores, zapcore.NewCore(enc, sink, zap.NewAtomicLevelAt(zapcore.DebugLevel))) 168 if errCore != nil { 169 cores = append(cores, errCore) 170 } 171 return cores, zapcore.NewCore(enc, sink, enabler), errSink, l, nil 172 } 173 174 func formatDate(t time.Time, enc zapcore.PrimitiveArrayEncoder) { 175 // t = t.UTC() 不用utc时间 176 year, month, day := t.Date() 177 hour, minute, second := t.Clock() 178 micros := t.Nanosecond() / 1000 179 180 buf := make([]byte, 27) 181 182 buf[0] = byte((year/1000)%10) + '0' 183 buf[1] = byte((year/100)%10) + '0' 184 buf[2] = byte((year/10)%10) + '0' 185 buf[3] = byte(year%10) + '0' 186 buf[4] = '-' 187 buf[5] = byte((month)/10) + '0' 188 buf[6] = byte((month)%10) + '0' 189 buf[7] = '-' 190 buf[8] = byte((day)/10) + '0' 191 buf[9] = byte((day)%10) + '0' 192 buf[10] = 'T' 193 buf[11] = byte((hour)/10) + '0' 194 buf[12] = byte((hour)%10) + '0' 195 buf[13] = ':' 196 buf[14] = byte((minute)/10) + '0' 197 buf[15] = byte((minute)%10) + '0' 198 buf[16] = ':' 199 buf[17] = byte((second)/10) + '0' 200 buf[18] = byte((second)%10) + '0' 201 buf[19] = '.' 202 buf[20] = byte((micros/100000)%10) + '0' 203 buf[21] = byte((micros/10000)%10) + '0' 204 buf[22] = byte((micros/1000)%10) + '0' 205 buf[23] = byte((micros/100)%10) + '0' 206 buf[24] = byte((micros/10)%10) + '0' 207 buf[25] = byte((micros)%10) + '0' 208 buf[26] = 'Z' 209 210 enc.AppendString(string(buf)) 211 } 212 213 func updateScopes(typeName string, options *Options, cores []zapcore.Core, errSink zapcore.WriteSyncer) error { 214 scope := FindScope(typeName) 215 if scope == nil { 216 // 对还没注册的日志配置进行注册 217 scope = RegisterScope(typeName, fmt.Sprintf("%s logging messages.", typeName), 0) 218 } 219 220 // update the output levels of all listed scopes 221 outPutLevel, ok := stringToLevel[options.OutputLevel] 222 if !ok { 223 return fmt.Errorf("unknown outPutLevel '%s' specified", options.OutputLevel) 224 } 225 226 // update the stack tracing levels of all listed scopes 227 stackTraceLevel, ok := stringToLevel[options.StackTraceLevel] 228 if !ok { 229 return fmt.Errorf("unknown stackTraceLevel '%s' specified", options.StackTraceLevel) 230 } 231 232 lock.Lock() 233 defer lock.Unlock() 234 235 scope.SetOutputLevel(outPutLevel) 236 scope.SetStackTraceLevel(stackTraceLevel) 237 238 // update patchTable 239 pt := &patchTable{ 240 write: func(ent zapcore.Entry, fields []zapcore.Field) error { 241 var errs error 242 for _, core := range cores { 243 if core.Enabled(ent.Level) { 244 if err := core.Write(ent, fields); err != nil { 245 errs = multierror.Append(errs, err) 246 } 247 } 248 } 249 if ent.Level == zapcore.FatalLevel { 250 scope.getPathTable().exitProcess(1) 251 } 252 253 return errs 254 }, 255 sync: func() error { 256 var errs error 257 for _, core := range cores { 258 if err := core.Sync(); err != nil { 259 errs = multierror.Append(errs, err) 260 } 261 } 262 return errs 263 }, 264 exitProcess: os.Exit, 265 errorSink: errSink, 266 } 267 268 scope.pt = pt 269 // update the caller location setting of all listed scopes 270 scope.SetDisableLogCaller(options.DisableLogCaller) 271 272 return nil 273 } 274 275 func logRotationSyncCallback(tw *timewheel.TimeWheel, rotationMaxDurationForHour int64, log *lumberjack.Logger) { 276 tw.AddTask(uint32(rotationMaxDurationForHour*time.Hour.Milliseconds()), nil, func(i interface{}) { 277 if err := log.Rotate(); err != nil { 278 return 279 } 280 logRotationSyncCallback(tw, rotationMaxDurationForHour, log) 281 }) 282 } 283 284 // Configure . 285 // You typically call this once at process startup. 286 // Configure Once this call returns, the logging system is ready to accept data. 287 func Configure(optionsMap map[string]*Options) error { 288 tw := timewheel.New(time.Minute, 5, "log rotation sync") 289 tw.Start() 290 for typeName, options := range optionsMap { 291 setDefaultOption(options) 292 cores, captureCore, errSink, log, err := prepZap(options) 293 if err != nil { 294 return err 295 } 296 if options.RotationMaxDurationForHour != 0 { 297 logRotationSyncCallback(tw, int64(options.RotationMaxDurationForHour), log) 298 } 299 if err = updateScopes(typeName, options, cores, errSink); err != nil { 300 return err 301 } 302 303 targetScope := scopes[typeName] 304 305 opts := []zap.Option{ 306 zap.ErrorOutput(errSink), 307 } 308 309 if !targetScope.GetDisableLogCaller() { 310 opts = append(opts, 311 zap.AddCallerSkip(1), 312 zap.AddCaller(), 313 ) 314 } 315 316 l := targetScope.GetStackTraceLevel() 317 if l != NoneLevel { 318 opts = append(opts, zap.AddStacktrace(levelToZap[l])) 319 } 320 321 captureLogger := zap.New(captureCore, opts...) 322 323 if typeName == DefaultLoggerName { 324 // capture global zap logging and force it through our logger 325 _ = zap.ReplaceGlobals(captureLogger) 326 // capture standard golang "log" package output and force it through our logger 327 _ = zap.RedirectStdLog(captureLogger) 328 } 329 330 // capture gRPC logging 331 if options.LogGrpc { 332 grpclog.SetLogger(zapgrpc.NewLogger(captureLogger.WithOptions(zap.AddCallerSkip(2)))) 333 } 334 335 // if typeName == DefaultLoggerName { 336 // opts := []zap.Option{ 337 // zap.ErrorOutput(errSink), 338 // zap.AddCallerSkip(1), 339 // } 340 341 // if defaultScope.GetLogCallers() { 342 // opts = append(opts, zap.AddCaller()) 343 // } 344 345 // l := defaultScope.GetStackTraceLevel() 346 // if l != NoneLevel { 347 // opts = append(opts, zap.AddStacktrace(levelToZap[l])) 348 // } 349 350 // captureLogger := zap.New(captureCore, opts...) 351 352 // // capture global zap logging and force it through our logger 353 // _ = zap.ReplaceGlobals(captureLogger) 354 355 // // capture standard golang "log" package output and force it through our logger 356 // _ = zap.RedirectStdLog(captureLogger) 357 358 // // capture gRPC logging 359 // if options.LogGrpc { 360 // grpclog.SetLogger(zapgrpc.NewLogger(captureLogger.WithOptions(zap.AddCallerSkip(2)))) 361 // } 362 // } 363 } 364 return nil 365 } 366 367 // setDefaultOption 设置日志配置的默认值 368 func setDefaultOption(options *Options) { 369 if options.RotationMaxSize == 0 { 370 options.RotationMaxSize = defaultRotationMaxSize 371 } 372 if options.RotationMaxAge == 0 { 373 options.RotationMaxAge = defaultRotationMaxAge 374 } 375 if options.RotationMaxBackups == 0 { 376 options.RotationMaxBackups = defaultRotationMaxBackups 377 } 378 if options.OutputLevel == "" { 379 options.OutputLevel = levelToString[defaultOutputLevel] 380 } 381 if options.StackTraceLevel == "" { 382 options.StackTraceLevel = levelToString[defaultStackTraceLevel] 383 } 384 } 385 386 // Sync flushes any buffered log entries. 387 // Processes should normally take care to call Sync before exiting. 388 func Sync() error { 389 return defaultScope.getPathTable().sync() 390 } 391 392 // createPathIfNotExist 如果判断为本地文件,检查目录是否存在,不存在创建父级目录 393 func createPathIfNotExist(paths ...string) error { 394 for _, path := range paths { 395 u, err := url.Parse(path) 396 if err != nil { 397 return fmt.Errorf("can't parse %q as a URL: %v", path, err) 398 } 399 if (u.Scheme == "" || u.Scheme == "file") && u.Path != "stdout" && u.Path != "stderr" { 400 dir := filepath.Dir(u.Path) 401 err := os.MkdirAll(dir, os.ModePerm) 402 if err != nil { 403 return fmt.Errorf("can't create %q directory: %v", dir, err) 404 } 405 } 406 } 407 return nil 408 }