github.com/leovct/zkevm-bridge-service@v0.4.4/log/log.go (about) 1 package log 2 3 import ( 4 "fmt" 5 "os" 6 "strings" 7 "sync/atomic" 8 9 zkevmbridgeservice "github.com/0xPolygonHermez/zkevm-bridge-service" 10 "github.com/hermeznetwork/tracerr" 11 "go.uber.org/zap" 12 "go.uber.org/zap/zapcore" 13 ) 14 15 // LogEnvironment represents the possible log environments. 16 type LogEnvironment string 17 18 const ( 19 // EnvironmentProduction production log environment. 20 EnvironmentProduction = LogEnvironment("production") 21 // EnvironmentDevelopment development log environment. 22 EnvironmentDevelopment = LogEnvironment("development") 23 ) 24 25 // Logger is a wrapper providing logging facilities. 26 type Logger struct { 27 x *zap.SugaredLogger 28 } 29 30 // root logger 31 var log atomic.Pointer[Logger] 32 33 func getDefaultLog() *Logger { 34 l := log.Load() 35 if l != nil { 36 return l 37 } 38 // default level: debug 39 zapLogger, _, err := NewLogger(Config{ 40 Environment: EnvironmentDevelopment, 41 Level: "debug", 42 Outputs: []string{"stderr"}, 43 }) 44 if err != nil { 45 panic(err) 46 } 47 log.Store(&Logger{x: zapLogger}) 48 return log.Load() 49 } 50 51 // Init the logger with defined level. outputs defines the outputs where the 52 // logs will be sent. By default outputs contains "stdout", which prints the 53 // logs at the output of the process. To add a log file as output, the path 54 // should be added at the outputs array. To avoid printing the logs but storing 55 // them on a file, can use []string{"pathtofile.log"} 56 func Init(cfg Config) { 57 zapLogger, _, err := NewLogger(cfg) 58 if err != nil { 59 panic(err) 60 } 61 log.Store(&Logger{x: zapLogger}) 62 } 63 64 // NewLogger creates the logger with defined level. outputs defines the outputs where the 65 // logs will be sent. By default, outputs contains "stdout", which prints the 66 // logs at the output of the process. To add a log file as output, the path 67 // should be added at the outputs array. To avoid printing the logs but storing 68 // them on a file, can use []string{"pathtofile.log"} 69 func NewLogger(cfg Config) (*zap.SugaredLogger, *zap.AtomicLevel, error) { 70 var level zap.AtomicLevel 71 err := level.UnmarshalText([]byte(cfg.Level)) 72 if err != nil { 73 return nil, nil, fmt.Errorf("error on setting log level: %s", err) 74 } 75 76 var zapCfg zap.Config 77 78 switch cfg.Environment { 79 case EnvironmentProduction: 80 zapCfg = zap.NewProductionConfig() 81 default: 82 zapCfg = zap.NewDevelopmentConfig() 83 zapCfg.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder 84 } 85 zapCfg.Level = level 86 zapCfg.OutputPaths = cfg.Outputs 87 zapCfg.InitialFields = map[string]interface{}{ 88 "version": zkevmbridgeservice.Version, 89 "pid": os.Getpid(), 90 } 91 92 logger, err := zapCfg.Build() 93 if err != nil { 94 return nil, nil, err 95 } 96 defer logger.Sync() //nolint:gosec,errcheck 97 98 // skip 2 callers: one for our wrapper methods and one for the package functions 99 withOptions := logger.WithOptions(zap.AddCallerSkip(2)) //nolint:gomnd 100 return withOptions.Sugar(), &level, nil 101 } 102 103 // WithFields returns a new Logger (derived from the root one) with additional 104 // fields as per keyValuePairs. The root Logger instance is not affected. 105 func WithFields(keyValuePairs ...interface{}) *Logger { 106 l := getDefaultLog().WithFields(keyValuePairs...) 107 108 // since we are returning a new instance, remove one caller from the 109 // stack, because we'll be calling the retruned Logger methods 110 // directly, not the package functions. 111 x := l.x.WithOptions(zap.AddCallerSkip(-1)) 112 l.x = x 113 return l 114 } 115 116 // WithFields returns a new Logger with additional fields as per keyValuePairs. 117 // The original Logger instance is not affected. 118 func (l *Logger) WithFields(keyValuePairs ...interface{}) *Logger { 119 return &Logger{ 120 x: l.x.With(keyValuePairs...), 121 } 122 } 123 124 func sprintStackTrace(st []tracerr.Frame) string { 125 builder := strings.Builder{} 126 // Skip deepest frame because it belongs to the go runtime and we don't 127 // care about it. 128 if len(st) > 0 { 129 st = st[:len(st)-1] 130 } 131 for _, f := range st { 132 builder.WriteString(fmt.Sprintf("\n%s:%d %s()", f.Path, f.Line, f.Func)) 133 } 134 builder.WriteString("\n") 135 return builder.String() 136 } 137 138 // appendStackTraceMaybeArgs will append the stacktrace to the args 139 func appendStackTraceMaybeArgs(args []interface{}) []interface{} { 140 for i := range args { 141 if err, ok := args[i].(error); ok { 142 err = tracerr.Wrap(err) 143 st := tracerr.StackTrace(err) 144 return append(args, sprintStackTrace(st)) 145 } 146 } 147 return args 148 } 149 150 // Debug calls log.Debug 151 func (l *Logger) Debug(args ...interface{}) { 152 l.x.Debug(args...) 153 } 154 155 // Info calls log.Info 156 func (l *Logger) Info(args ...interface{}) { 157 l.x.Info(args...) 158 } 159 160 // Warn calls log.Warn 161 func (l *Logger) Warn(args ...interface{}) { 162 l.x.Warn(args...) 163 } 164 165 // Error calls log.Error 166 func (l *Logger) Error(args ...interface{}) { 167 l.x.Error(args...) 168 } 169 170 // Fatal calls log.Fatal 171 func (l *Logger) Fatal(args ...interface{}) { 172 l.x.Fatal(args...) 173 } 174 175 // Debugf calls log.Debugf 176 func (l *Logger) Debugf(template string, args ...interface{}) { 177 l.x.Debugf(template, args...) 178 } 179 180 // Infof calls log.Infof 181 func (l *Logger) Infof(template string, args ...interface{}) { 182 l.x.Infof(template, args...) 183 } 184 185 // Warnf calls log.Warnf 186 func (l *Logger) Warnf(template string, args ...interface{}) { 187 l.x.Warnf(template, args...) 188 } 189 190 // Fatalf calls log.Fatalf 191 func (l *Logger) Fatalf(template string, args ...interface{}) { 192 l.x.Fatalf(template, args...) 193 } 194 195 // Errorf calls log.Errorf and stores the error message into the ErrorFile 196 func (l *Logger) Errorf(template string, args ...interface{}) { 197 l.x.Errorf(template, args...) 198 } 199 200 // Debug calls log.Debug on the root Logger. 201 func Debug(args ...interface{}) { 202 getDefaultLog().Debug(args...) 203 } 204 205 // Info calls log.Info on the root Logger. 206 func Info(args ...interface{}) { 207 getDefaultLog().Info(args...) 208 } 209 210 // Warn calls log.Warn on the root Logger. 211 func Warn(args ...interface{}) { 212 getDefaultLog().Warn(args...) 213 } 214 215 // Error calls log.Error on the root Logger. 216 func Error(args ...interface{}) { 217 args = appendStackTraceMaybeArgs(args) 218 getDefaultLog().Error(args...) 219 } 220 221 // Fatal calls log.Fatal on the root Logger. 222 func Fatal(args ...interface{}) { 223 args = appendStackTraceMaybeArgs(args) 224 getDefaultLog().Fatal(args...) 225 } 226 227 // Debugf calls log.Debugf on the root Logger. 228 func Debugf(template string, args ...interface{}) { 229 getDefaultLog().Debugf(template, args...) 230 } 231 232 // Infof calls log.Infof on the root Logger. 233 func Infof(template string, args ...interface{}) { 234 getDefaultLog().Infof(template, args...) 235 } 236 237 // Warnf calls log.Warnf on the root Logger. 238 func Warnf(template string, args ...interface{}) { 239 getDefaultLog().Warnf(template, args...) 240 } 241 242 // Fatalf calls log.Fatalf on the root Logger. 243 func Fatalf(template string, args ...interface{}) { 244 args = appendStackTraceMaybeArgs(args) 245 getDefaultLog().Fatalf(template, args...) 246 } 247 248 // Errorf calls log.Errorf on the root logger and stores the error message into 249 // the ErrorFile. 250 func Errorf(template string, args ...interface{}) { 251 args = appendStackTraceMaybeArgs(args) 252 getDefaultLog().Errorf(template, args...) 253 } 254 255 // appendStackTraceMaybeKV will append the stacktrace to the KV 256 func appendStackTraceMaybeKV(msg string, kv []interface{}) string { 257 for i := range kv { 258 if i%2 == 0 { 259 continue 260 } 261 if err, ok := kv[i].(error); ok { 262 err = tracerr.Wrap(err) 263 st := tracerr.StackTrace(err) 264 return fmt.Sprintf("%v: %v%v\n", msg, err, sprintStackTrace(st)) 265 } 266 } 267 return msg 268 } 269 270 // Debugw calls log.Debugw 271 func (l *Logger) Debugw(msg string, kv ...interface{}) { 272 l.x.Debugw(msg, kv...) 273 } 274 275 // Infow calls log.Infow 276 func (l *Logger) Infow(msg string, kv ...interface{}) { 277 l.x.Infow(msg, kv...) 278 } 279 280 // Warnw calls log.Warnw 281 func (l *Logger) Warnw(msg string, kv ...interface{}) { 282 l.x.Warnw(msg, kv...) 283 } 284 285 // Errorw calls log.Errorw 286 func (l *Logger) Errorw(msg string, kv ...interface{}) { 287 l.x.Errorw(msg, kv...) 288 } 289 290 // Fatalw calls log.Fatalw 291 func (l *Logger) Fatalw(msg string, kv ...interface{}) { 292 l.x.Fatalw(msg, kv...) 293 } 294 295 // Debugw calls log.Debugw on the root Logger. 296 func Debugw(msg string, kv ...interface{}) { 297 getDefaultLog().Debugw(msg, kv...) 298 } 299 300 // Infow calls log.Infow on the root Logger. 301 func Infow(msg string, kv ...interface{}) { 302 getDefaultLog().Infow(msg, kv...) 303 } 304 305 // Warnw calls log.Warnw on the root Logger. 306 func Warnw(msg string, kv ...interface{}) { 307 getDefaultLog().Warnw(msg, kv...) 308 } 309 310 // Errorw calls log.Errorw on the root Logger. 311 func Errorw(msg string, kv ...interface{}) { 312 msg = appendStackTraceMaybeKV(msg, kv) 313 getDefaultLog().Errorw(msg, kv...) 314 } 315 316 // Fatalw calls log.Fatalw on the root Logger. 317 func Fatalw(msg string, kv ...interface{}) { 318 msg = appendStackTraceMaybeKV(msg, kv) 319 getDefaultLog().Fatalw(msg, kv...) 320 }