github.com/iotexproject/iotex-core@v1.14.1-rc1/pkg/log/log.go (about) 1 // Copyright (c) 2019 IoTeX Foundation 2 // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability 3 // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. 4 // This source code is governed by Apache License 2.0 that can be found in the LICENSE file. 5 6 package log 7 8 import ( 9 "log" 10 "net/http" 11 "os" 12 "sync" 13 14 "go.elastic.co/ecszap" 15 "go.uber.org/zap" 16 "go.uber.org/zap/zapcore" 17 18 "github.com/pkg/errors" 19 ) 20 21 // GlobalConfig defines the global logger configurations. 22 type GlobalConfig struct { 23 Zap *zap.Config `json:"zap" yaml:"zap"` 24 Trace *TraceConfig `json:"trace" yaml:"trace"` 25 StderrRedirectFile *string `json:"stderrRedirectFile" yaml:"stderrRedirectFile"` 26 RedirectStdLog bool `json:"stdLogRedirect" yaml:"stdLogRedirect"` 27 EcsIntegration bool `json:"ecsIntegration" yaml:"ecsIntegration"` 28 } 29 30 var ( 31 _globalCfg GlobalConfig 32 _logMu sync.RWMutex 33 _logServeMux = http.NewServeMux() 34 _subLoggers map[string]*zap.Logger 35 _globalLoggerName = "global" 36 ) 37 38 func init() { 39 zapCfg := zap.NewDevelopmentConfig() 40 zapCfg.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder 41 zapCfg.Level.SetLevel(zap.InfoLevel) 42 l, err := zapCfg.Build() 43 if err != nil { 44 log.Println("Failed to init zap global logger, no zap log will be shown till zap is properly initialized: ", err) 45 return 46 } 47 _logMu.Lock() 48 _globalCfg.Zap = &zapCfg 49 _subLoggers = make(map[string]*zap.Logger) 50 _logMu.Unlock() 51 zap.ReplaceGlobals(l) 52 } 53 54 // L wraps zap.L(). 55 func L() *zap.Logger { return zap.L() } 56 57 // S wraps zap.S(). 58 func S() *zap.SugaredLogger { return zap.S() } 59 60 // Logger returns logger of the given name 61 func Logger(name string) *zap.Logger { 62 logger, ok := _subLoggers[name] 63 if !ok { 64 return L() 65 } 66 return logger 67 } 68 69 // InitLoggers initializes the global logger and other sub loggers. 70 func InitLoggers(globalCfg GlobalConfig, subCfgs map[string]GlobalConfig, opts ...zap.Option) error { 71 if _, exists := subCfgs[_globalLoggerName]; exists { 72 return errors.New("'" + _globalLoggerName + "' is a reserved name for global logger") 73 } 74 subCfgs[_globalLoggerName] = globalCfg 75 for name, cfg := range subCfgs { 76 if _, exists := _subLoggers[name]; exists { 77 return errors.Errorf("duplicate sub logger name: %s", name) 78 } 79 if cfg.Zap == nil { 80 zapCfg := zap.NewProductionConfig() 81 cfg.Zap = &zapCfg 82 } else { 83 cfg.Zap.EncoderConfig = zap.NewProductionEncoderConfig() 84 } 85 if globalCfg.EcsIntegration { 86 cfg.Zap.EncoderConfig = ecszap.ECSCompatibleEncoderConfig(cfg.Zap.EncoderConfig) 87 } 88 89 var cores []zapcore.Core 90 if cfg.StderrRedirectFile != nil { 91 stderrF, err := os.OpenFile(*cfg.StderrRedirectFile, os.O_WRONLY|os.O_CREATE|os.O_SYNC|os.O_APPEND, 0600) 92 if err != nil { 93 return err 94 } 95 96 cores = append(cores, zapcore.NewCore( 97 zapcore.NewJSONEncoder(cfg.Zap.EncoderConfig), 98 zapcore.AddSync(stderrF), 99 cfg.Zap.Level)) 100 } 101 switch cfg.Zap.Encoding { 102 case "console": 103 consoleCfg := zap.NewDevelopmentConfig() 104 consoleCfg.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder 105 cores = append(cores, zapcore.NewCore( 106 zapcore.NewConsoleEncoder(consoleCfg.EncoderConfig), 107 zapcore.AddSync(os.Stdout), 108 cfg.Zap.Level)) 109 case "json": 110 cfg.Zap.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder 111 cores = append(cores, zapcore.NewCore( 112 zapcore.NewJSONEncoder(cfg.Zap.EncoderConfig), 113 zapcore.AddSync(os.Stdout), 114 cfg.Zap.Level)) 115 default: 116 return errors.Errorf("unknown encoding: %s", cfg.Zap.Encoding) 117 } 118 119 core := zapcore.NewTee(cores...) 120 logger := zap.New(core, opts...) 121 122 _logMu.Lock() 123 if name == _globalLoggerName { 124 _globalCfg = cfg 125 if cfg.RedirectStdLog { 126 zap.RedirectStdLog(logger) 127 } 128 zap.ReplaceGlobals(logger) 129 if err := initTraceLogger(logger, cfg.Trace); err != nil { 130 return err 131 } 132 } else { 133 _subLoggers[name] = logger 134 } 135 _logServeMux.HandleFunc("/"+name, cfg.Zap.Level.ServeHTTP) 136 _logMu.Unlock() 137 } 138 139 return nil 140 } 141 142 // RegisterLevelConfigMux registers log's level config http mux. 143 func RegisterLevelConfigMux(root *http.ServeMux) { 144 _logMu.Lock() 145 root.Handle("/logging/", http.StripPrefix("/logging", _logServeMux)) 146 _logMu.Unlock() 147 }