github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/pkg/log/log.go (about) 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 14 package log 15 16 import ( 17 "context" 18 "fmt" 19 "strings" 20 21 "github.com/pingcap/errors" 22 pclog "github.com/pingcap/log" 23 lightningLog "github.com/pingcap/tidb/pkg/lightning/log" 24 "github.com/pingcap/tidb/pkg/util/logutil" 25 "github.com/pingcap/tiflow/dm/pkg/helper" 26 "github.com/pingcap/tiflow/dm/pkg/terror" 27 "go.uber.org/zap" 28 "go.uber.org/zap/zapcore" 29 ) 30 31 const ( 32 defaultLogLevel = "info" 33 defaultLogMaxDays = 7 34 defaultLogMaxSize = 512 // MB 35 ) 36 37 // Config serializes log related config in toml/json. 38 type Config struct { 39 // Log level. 40 Level string `toml:"level" json:"level"` 41 // the format of the log, "text" or "json" 42 Format string `toml:"format" json:"format"` 43 // Log filename, leave empty to disable file log. 44 File string `toml:"file" json:"file"` 45 // Max size for a single file, in MB. 46 FileMaxSize int `toml:"max-size" json:"max-size"` 47 // Max log keep days, default is never deleting. 48 FileMaxDays int `toml:"max-days" json:"max-days"` 49 // Maximum number of old log files to retain. 50 FileMaxBackups int `toml:"max-backups" json:"max-backups"` 51 } 52 53 // Adjust adjusts config. 54 func (cfg *Config) Adjust() { 55 if len(cfg.Level) == 0 { 56 cfg.Level = defaultLogLevel 57 } 58 if cfg.Level == "warning" { 59 cfg.Level = "warn" 60 } 61 if cfg.FileMaxSize == 0 { 62 cfg.FileMaxSize = defaultLogMaxSize 63 } 64 if cfg.FileMaxDays == 0 { 65 cfg.FileMaxDays = defaultLogMaxDays 66 } 67 } 68 69 // Logger is a simple wrapper around *zap.Logger which provides some extra 70 // methods to simplify DM's log usage. 71 type Logger struct { 72 *zap.Logger 73 } 74 75 // WithFields return new Logger with specified fields. 76 func (l Logger) WithFields(fields ...zap.Field) Logger { 77 return Logger{l.With(fields...)} 78 } 79 80 // ErrorFilterContextCanceled wraps Logger.Error() and will filter error log when error is context.Canceled. 81 func (l Logger) ErrorFilterContextCanceled(msg string, fields ...zap.Field) { 82 for _, field := range fields { 83 switch field.Type { 84 case zapcore.StringType: 85 if field.Key == "error" && strings.Contains(field.String, context.Canceled.Error()) { 86 return 87 } 88 case zapcore.ErrorType: 89 err, ok := field.Interface.(error) 90 if ok && errors.Cause(err) == context.Canceled { 91 return 92 } 93 } 94 } 95 l.Logger.WithOptions(zap.AddCallerSkip(1)).Error(msg, fields...) 96 } 97 98 // logger for DM. 99 var ( 100 appLogger = Logger{zap.NewNop()} 101 appLevel zap.AtomicLevel 102 appProps *pclog.ZapProperties 103 ) 104 105 // InitLogger initializes DM's and also the TiDB library's loggers. 106 func InitLogger(cfg *Config) error { 107 inDev := strings.ToLower(cfg.Level) == "debug" 108 // init DM logger 109 logger, props, err := pclog.InitLogger(&pclog.Config{ 110 Level: cfg.Level, 111 Format: cfg.Format, 112 File: pclog.FileLogConfig{ 113 Filename: cfg.File, 114 MaxSize: cfg.FileMaxSize, 115 MaxDays: cfg.FileMaxDays, 116 MaxBackups: cfg.FileMaxBackups, 117 }, 118 Development: inDev, 119 }) 120 if err != nil { 121 return terror.ErrInitLoggerFail.Delegate(err) 122 } 123 124 // Do not log stack traces at all, as we'll get the stack trace from the 125 // error itself. 126 logger = logger.WithOptions(zap.AddStacktrace(zap.DPanicLevel)) 127 pclog.ReplaceGlobals(logger, props) 128 129 lightningLogger := logger.With(zap.String("component", "lightning")) 130 lightningLog.SetAppLogger(lightningLogger) 131 132 appLogger = Logger{logger} 133 appLevel = props.Level 134 appProps = props 135 // init and set tidb slow query logger to stdout if log level is debug 136 if inDev { 137 slowQueryLogger := zap.NewExample() 138 slowQueryLogger = slowQueryLogger.With(zap.String("component", "slow query logger")) 139 logutil.SlowQueryLogger = slowQueryLogger 140 } else { 141 logutil.SlowQueryLogger = zap.NewNop() 142 } 143 return nil 144 } 145 146 // With creates a child logger from the global logger and adds structured 147 // context to it. 148 func With(fields ...zap.Field) Logger { 149 return Logger{appLogger.With(fields...)} 150 } 151 152 // SetLevel modifies the log level of the global logger. Returns the previous 153 // level. 154 func SetLevel(level zapcore.Level) zapcore.Level { 155 oldLevel := appLevel.Level() 156 appLevel.SetLevel(level) 157 return oldLevel 158 } 159 160 // ShortError contructs a field which only records the error message without the 161 // verbose text (i.e. excludes the stack trace). 162 // 163 // In DM, all errors are almost always propagated back to `main()` where 164 // the error stack is written. Including the stack in the middle thus usually 165 // just repeats known information. You should almost always use `ShortError` 166 // instead of `zap.Error`, unless the error is no longer propagated upwards. 167 func ShortError(err error) zap.Field { 168 if err == nil { 169 return zap.Skip() 170 } 171 return zap.String("error", err.Error()) 172 } 173 174 // L returns the current logger for DM. 175 func L() Logger { 176 return appLogger 177 } 178 179 // Props returns the current logger's props. 180 func Props() *pclog.ZapProperties { 181 return appProps 182 } 183 184 // WrapStringerField returns a wrap stringer field. 185 func WrapStringerField(message string, object fmt.Stringer) zap.Field { 186 if helper.IsNil(object) { 187 return zap.String(message, "NULL") 188 } 189 190 return zap.Stringer(message, object) 191 } 192 193 // WithCtx adds fields from ctx to the logger. 194 func WithCtx(ctx context.Context) Logger { 195 return Logger{appLogger.With(getZapFieldsFromCtx(ctx)...)} 196 }