/* * Copyright (c) 2019 VMware, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and * associated documentation files (the "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is furnished to do * so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all copies or substantial * portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // Note: The implementation comes from https://www.mountedthoughts.com/golang-logger-interface/ // https://github.com/amitrai48/logger package zap import ( "os" "github.com/vmware/vmware-go-kcl-v2/logger" uzap "go.uber.org/zap" "go.uber.org/zap/zapcore" lumberjack "gopkg.in/natefinch/lumberjack.v2" ) type ZapLogger struct { sugaredLogger *uzap.SugaredLogger } // NewZapLogger adapts existing sugared zap logger to Logger interface. // The call is responsible for configuring sugard zap logger appropriately. // // Note: Sugar wraps the Logger to provide a more ergonomic, but slightly slower, // API. Sugaring a Logger is quite inexpensive, so it's reasonable for a // single application to use both Loggers and SugaredLoggers, converting // between them on the boundaries of performance-sensitive code. // // Base zap logger can be convert to SugaredLogger by calling to add a wrapper: // sugaredLogger := log.Sugar() func NewZapLogger(logger *uzap.SugaredLogger) logger.Logger { return &ZapLogger{ sugaredLogger: logger, } } // NewZapLoggerWithConfig creates and configs Logger instance backed by // zap Sugared logger. func NewZapLoggerWithConfig(config logger.Configuration) logger.Logger { cores := []zapcore.Core{} if config.EnableConsole { level := getZapLevel(config.ConsoleLevel) writer := zapcore.Lock(os.Stdout) core := zapcore.NewCore(getEncoder(config.ConsoleJSONFormat), writer, level) cores = append(cores, core) } if config.EnableFile { level := getZapLevel(config.FileLevel) writer := zapcore.AddSync(&lumberjack.Logger{ Filename: config.Filename, MaxSize: config.MaxSizeMB, Compress: true, MaxAge: config.MaxAgeDays, MaxBackups: config.MaxBackups, LocalTime: config.LocalTime, }) core := zapcore.NewCore(getEncoder(config.FileJSONFormat), writer, level) cores = append(cores, core) } combinedCore := zapcore.NewTee(cores...) // AddCallerSkip skips 2 number of callers, this is important else the file that gets // logged will always be the wrapped file. In our case zap.go logger := uzap.New(combinedCore, uzap.AddCallerSkip(2), uzap.AddCaller(), ).Sugar() return &ZapLogger{ sugaredLogger: logger, } } func (l *ZapLogger) Debugf(format string, args ...interface{}) { l.sugaredLogger.Debugf(format, args...) } func (l *ZapLogger) Infof(format string, args ...interface{}) { l.sugaredLogger.Infof(format, args...) } func (l *ZapLogger) Warnf(format string, args ...interface{}) { l.sugaredLogger.Warnf(format, args...) } func (l *ZapLogger) Errorf(format string, args ...interface{}) { l.sugaredLogger.Errorf(format, args...) } func (l *ZapLogger) Fatalf(format string, args ...interface{}) { l.sugaredLogger.Fatalf(format, args...) } func (l *ZapLogger) Panicf(format string, args ...interface{}) { l.sugaredLogger.Fatalf(format, args...) } func (l *ZapLogger) WithFields(fields logger.Fields) logger.Logger { var f = make([]interface{}, 0) for k, v := range fields { f = append(f, k) f = append(f, v) } newLogger := l.sugaredLogger.With(f...) return &ZapLogger{newLogger} } func getEncoder(isJSON bool) zapcore.Encoder { encoderConfig := uzap.NewProductionEncoderConfig() encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder if isJSON { return zapcore.NewJSONEncoder(encoderConfig) } return zapcore.NewConsoleEncoder(encoderConfig) } func getZapLevel(level string) zapcore.Level { switch level { case logger.Info: return zapcore.InfoLevel case logger.Warn: return zapcore.WarnLevel case logger.Debug: return zapcore.DebugLevel case logger.Error: return zapcore.ErrorLevel case logger.Fatal: return zapcore.FatalLevel default: return zapcore.InfoLevel } }