|
|
|
|
|
|
|
|
|
package logger |
|
|
|
import ( |
|
"fmt" |
|
"os" |
|
"path/filepath" |
|
"strconv" |
|
|
|
"github.com/GoAdminGroup/go-admin/context" |
|
"github.com/GoAdminGroup/go-admin/modules/trace" |
|
"github.com/GoAdminGroup/go-admin/modules/utils" |
|
"go.uber.org/zap" |
|
"go.uber.org/zap/zapcore" |
|
"gopkg.in/natefinch/lumberjack.v2" |
|
) |
|
|
|
var ( |
|
defaultEncoderCfg = EncoderCfg{ |
|
TimeKey: "ts", |
|
LevelKey: "level", |
|
NameKey: "logger", |
|
CallerKey: "caller", |
|
MessageKey: "msg", |
|
StacktraceKey: "stacktrace", |
|
Level: "capitalColor", |
|
Time: "ISO8601", |
|
Duration: "seconds", |
|
Caller: "short", |
|
Encoding: "console", |
|
} |
|
defaultRotateCfg = RotateCfg{ |
|
MaxSize: 10, |
|
MaxBackups: 5, |
|
MaxAge: 30, |
|
Compress: false, |
|
} |
|
|
|
logger = &Logger{ |
|
rotate: defaultRotateCfg, |
|
encoder: defaultEncoderCfg, |
|
Level: zapcore.InfoLevel, |
|
} |
|
|
|
infoLevelEnabler = zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { |
|
return lvl == zapcore.InfoLevel |
|
}) |
|
|
|
errorLevelEnabler = zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { |
|
return lvl >= zapcore.ErrorLevel |
|
}) |
|
|
|
accessLevelEnabler = zap.LevelEnablerFunc(func(lvl zapcore.Level) bool { |
|
return lvl == zapcore.WarnLevel |
|
}) |
|
) |
|
|
|
func init() { |
|
logger.Init() |
|
} |
|
|
|
type Logger struct { |
|
logger *zap.Logger |
|
sugaredLogger *zap.SugaredLogger |
|
|
|
infoLogOff bool |
|
errorLogOff bool |
|
accessLogOff bool |
|
|
|
accessAssetsLogOff bool |
|
|
|
debug bool |
|
|
|
sqlLogOpen bool |
|
|
|
infoLogPath string |
|
errorLogPath string |
|
accessLogPath string |
|
|
|
rotate RotateCfg |
|
encoder EncoderCfg |
|
|
|
Level zapcore.Level |
|
} |
|
|
|
type EncoderCfg struct { |
|
TimeKey string |
|
LevelKey string |
|
NameKey string |
|
CallerKey string |
|
MessageKey string |
|
StacktraceKey string |
|
Level string |
|
Time string |
|
Duration string |
|
Caller string |
|
Encoding string |
|
} |
|
|
|
type RotateCfg struct { |
|
MaxSize int |
|
MaxBackups int |
|
MaxAge int |
|
Compress bool |
|
} |
|
|
|
func (l *Logger) Init() { |
|
zapLogger := zap.New(zapcore.NewTee( |
|
zapcore.NewCore(l.getEncoder(l.encoder.LevelKey), l.getLogWriter(l.infoLogPath), infoLevelEnabler), |
|
zapcore.NewCore(l.getEncoder(l.encoder.LevelKey), l.getLogWriter(l.errorLogPath), errorLevelEnabler), |
|
zapcore.NewCore(l.getEncoder(""), l.getLogWriter(l.accessLogPath), accessLevelEnabler), |
|
), zap.AddCaller(), zap.AddCallerSkip(1), zap.AddStacktrace(errorLevelEnabler)) |
|
l.sugaredLogger = zapLogger.Sugar() |
|
l.logger = zapLogger |
|
} |
|
|
|
func (l *Logger) getEncoder(levelKey string) zapcore.Encoder { |
|
|
|
var ( |
|
timeEncoder = new(zapcore.TimeEncoder) |
|
durationEncoder = new(zapcore.DurationEncoder) |
|
callerEncoder = new(zapcore.CallerEncoder) |
|
nameEncoder = new(zapcore.NameEncoder) |
|
levelEncoder = new(zapcore.LevelEncoder) |
|
) |
|
|
|
_ = timeEncoder.UnmarshalText([]byte(l.encoder.Time)) |
|
_ = durationEncoder.UnmarshalText([]byte(l.encoder.Duration)) |
|
_ = callerEncoder.UnmarshalText([]byte(l.encoder.Caller)) |
|
_ = nameEncoder.UnmarshalText([]byte("full")) |
|
_ = levelEncoder.UnmarshalText([]byte(l.encoder.Level)) |
|
|
|
encoderConfig := zapcore.EncoderConfig{ |
|
TimeKey: l.encoder.TimeKey, |
|
LevelKey: levelKey, |
|
NameKey: l.encoder.NameKey, |
|
CallerKey: l.encoder.CallerKey, |
|
MessageKey: l.encoder.MessageKey, |
|
StacktraceKey: l.encoder.StacktraceKey, |
|
LineEnding: zapcore.DefaultLineEnding, |
|
EncodeLevel: *levelEncoder, |
|
EncodeTime: *timeEncoder, |
|
EncodeDuration: *durationEncoder, |
|
EncodeCaller: *callerEncoder, |
|
EncodeName: *nameEncoder, |
|
} |
|
|
|
return filterZapEncoder(l.encoder.Encoding, encoderConfig) |
|
} |
|
|
|
func (l *Logger) getLogWriter(path string) zapcore.WriteSyncer { |
|
if path != "" { |
|
lumberJackLogger := &lumberjack.Logger{ |
|
Filename: path, |
|
MaxSize: l.rotate.MaxSize, |
|
MaxBackups: l.rotate.MaxBackups, |
|
MaxAge: l.rotate.MaxAge, |
|
Compress: l.rotate.Compress, |
|
} |
|
if l.debug { |
|
return zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(lumberJackLogger)) |
|
} |
|
return zapcore.AddSync(lumberJackLogger) |
|
} |
|
return zapcore.AddSync(os.Stdout) |
|
} |
|
|
|
func (l *Logger) SetRotate(cfg RotateCfg) { |
|
if cfg.MaxSize != 0 && cfg.MaxAge != 0 && cfg.MaxBackups != 0 { |
|
l.rotate = cfg |
|
} |
|
} |
|
|
|
func (l *Logger) SetEncoder(cfg EncoderCfg) { |
|
cfg.TimeKey = utils.SetDefault(cfg.TimeKey, "", defaultEncoderCfg.TimeKey) |
|
cfg.LevelKey = utils.SetDefault(cfg.LevelKey, "", defaultEncoderCfg.LevelKey) |
|
cfg.NameKey = utils.SetDefault(cfg.NameKey, "", defaultEncoderCfg.NameKey) |
|
cfg.CallerKey = utils.SetDefault(cfg.CallerKey, "", defaultEncoderCfg.CallerKey) |
|
cfg.MessageKey = utils.SetDefault(cfg.MessageKey, "", defaultEncoderCfg.MessageKey) |
|
cfg.StacktraceKey = utils.SetDefault(cfg.StacktraceKey, "", defaultEncoderCfg.StacktraceKey) |
|
cfg.Level = utils.SetDefault(cfg.Level, "", defaultEncoderCfg.Level) |
|
cfg.Time = utils.SetDefault(cfg.Time, "", defaultEncoderCfg.Time) |
|
cfg.Duration = utils.SetDefault(cfg.Duration, "", defaultEncoderCfg.Duration) |
|
cfg.Caller = utils.SetDefault(cfg.Caller, "", defaultEncoderCfg.Caller) |
|
cfg.Encoding = utils.SetDefault(cfg.Encoding, "", defaultEncoderCfg.Encoding) |
|
l.encoder = cfg |
|
} |
|
|
|
type Config struct { |
|
InfoLogOff bool |
|
ErrorLogOff bool |
|
AccessLogOff bool |
|
|
|
SqlLogOpen bool |
|
|
|
InfoLogPath string |
|
ErrorLogPath string |
|
AccessLogPath string |
|
|
|
AccessAssetsLogOff bool |
|
|
|
Rotate RotateCfg |
|
Encode EncoderCfg |
|
|
|
Level int8 |
|
|
|
Debug bool |
|
} |
|
|
|
func InitWithConfig(cfg Config) { |
|
logger.infoLogPath = cfg.InfoLogPath |
|
logger.infoLogOff = cfg.InfoLogOff |
|
logger.errorLogPath = cfg.ErrorLogPath |
|
logger.errorLogOff = cfg.ErrorLogOff |
|
logger.accessLogPath = cfg.AccessLogPath |
|
logger.accessLogOff = cfg.AccessLogOff |
|
logger.sqlLogOpen = cfg.SqlLogOpen |
|
logger.accessAssetsLogOff = cfg.AccessAssetsLogOff |
|
logger.debug = cfg.Debug |
|
logger.SetRotate(cfg.Rotate) |
|
logger.SetEncoder(cfg.Encode) |
|
logger.Level = filterZapAtomicLevelByViper(cfg.Level) |
|
logger.Init() |
|
} |
|
|
|
func SetRotate(cfg RotateCfg) { |
|
logger.rotate = cfg |
|
logger.Init() |
|
} |
|
|
|
|
|
func OpenSQLLog() { |
|
logger.sqlLogOpen = true |
|
} |
|
|
|
|
|
func Debug(info ...interface{}) { |
|
if !logger.infoLogOff { |
|
if logger.Level <= zapcore.DebugLevel { |
|
logger.sugaredLogger.Info(info...) |
|
} |
|
} |
|
} |
|
|
|
|
|
func Debugf(template string, args ...interface{}) { |
|
if !logger.infoLogOff && logger.Level <= zapcore.DebugLevel { |
|
logger.sugaredLogger.Infof(template, args...) |
|
} |
|
} |
|
|
|
|
|
func Info(info ...interface{}) { |
|
if !logger.infoLogOff && logger.Level <= zapcore.InfoLevel { |
|
logger.sugaredLogger.Info(info...) |
|
} |
|
} |
|
|
|
|
|
func InfoCtx(ctx *context.Context, format string, args ...interface{}) { |
|
if !logger.infoLogOff && logger.Level <= zapcore.InfoLevel { |
|
logCtx(ctx, logger.logger.Info, format, args...) |
|
} |
|
} |
|
|
|
type logFunc func(msg string, fields ...zapcore.Field) |
|
|
|
func logCtx(ctx *context.Context, logFunc logFunc, format string, args ...interface{}) { |
|
logFunc(fmt.Sprintf(format, args...), zap.String("traceID", trace.GetTraceID(ctx))) |
|
} |
|
|
|
|
|
func Infof(template string, args ...interface{}) { |
|
if !logger.infoLogOff && logger.Level <= zapcore.InfoLevel { |
|
logger.sugaredLogger.Infof(template, args...) |
|
} |
|
} |
|
|
|
|
|
func Warn(info ...interface{}) { |
|
if !logger.infoLogOff && logger.Level <= zapcore.WarnLevel { |
|
logger.sugaredLogger.Warn(info...) |
|
} |
|
} |
|
|
|
|
|
func WarnCtx(ctx *context.Context, format string, args ...interface{}) { |
|
if !logger.infoLogOff && logger.Level <= zapcore.WarnLevel { |
|
logCtx(ctx, logger.logger.Warn, format, args...) |
|
} |
|
} |
|
|
|
|
|
func Warnf(template string, args ...interface{}) { |
|
if !logger.infoLogOff && logger.Level <= zapcore.WarnLevel { |
|
logger.sugaredLogger.Warnf(template, args...) |
|
} |
|
} |
|
|
|
|
|
func Error(err ...interface{}) { |
|
if !logger.errorLogOff && logger.Level <= zapcore.ErrorLevel { |
|
logger.sugaredLogger.Error(err...) |
|
} |
|
} |
|
|
|
|
|
func ErrorCtx(ctx *context.Context, format string, args ...interface{}) { |
|
if !logger.errorLogOff && logger.Level <= zapcore.ErrorLevel { |
|
logCtx(ctx, logger.logger.Error, format, args...) |
|
} |
|
} |
|
|
|
|
|
func Errorf(template string, args ...interface{}) { |
|
if !logger.errorLogOff && logger.Level <= zapcore.ErrorLevel { |
|
logger.sugaredLogger.Errorf(template, args...) |
|
} |
|
} |
|
|
|
|
|
func Fatal(info ...interface{}) { |
|
if !logger.errorLogOff && logger.Level <= zapcore.ErrorLevel { |
|
logger.sugaredLogger.Fatal(info...) |
|
} |
|
} |
|
|
|
|
|
func FatalCtx(ctx *context.Context, format string, args ...interface{}) { |
|
if !logger.errorLogOff && logger.Level <= zapcore.FatalLevel { |
|
logCtx(ctx, logger.logger.Fatal, format, args...) |
|
} |
|
} |
|
|
|
|
|
func Fatalf(template string, args ...interface{}) { |
|
if !logger.errorLogOff && logger.Level <= zapcore.ErrorLevel { |
|
logger.sugaredLogger.Fatalf(template, args...) |
|
} |
|
} |
|
|
|
|
|
func Panic(info ...interface{}) { |
|
logger.sugaredLogger.Panic(info...) |
|
} |
|
|
|
|
|
func PanicCtx(ctx *context.Context, format string, args ...interface{}) { |
|
logCtx(ctx, logger.logger.Panic, format, args...) |
|
} |
|
|
|
|
|
func Panicf(template string, args ...interface{}) { |
|
logger.sugaredLogger.Panicf(template, args...) |
|
} |
|
|
|
|
|
func Access(ctx *context.Context) { |
|
if !logger.accessLogOff && logger.Level <= zapcore.InfoLevel { |
|
if logger.accessAssetsLogOff { |
|
if filepath.Ext(ctx.Path()) == "" { |
|
logger.logger.Info("[GoAdmin] access log", |
|
zap.String("traceID", trace.GetTraceID(ctx)), |
|
zap.String("statuscode", strconv.Itoa(ctx.Response.StatusCode)), |
|
zap.String("method", string(ctx.Method())), |
|
zap.String("path", ctx.Path())) |
|
} |
|
} else { |
|
logger.logger.Info("[GoAdmin] access log", |
|
zap.String("traceID", trace.GetTraceID(ctx)), |
|
zap.String("statuscode", strconv.Itoa(ctx.Response.StatusCode)), |
|
zap.String("method", string(ctx.Method())), |
|
zap.String("path", ctx.Path())) |
|
} |
|
} |
|
} |
|
|
|
|
|
func LogSQL(statement string, args []interface{}) { |
|
if !logger.infoLogOff && logger.sqlLogOpen && statement != "" { |
|
if logger.Level <= zapcore.InfoLevel { |
|
logger.sugaredLogger.With("statement", statement, "args", args).Info("[GoAdmin]") |
|
} |
|
} |
|
} |
|
|
|
func filterZapEncoder(encoding string, encoderConfig zapcore.EncoderConfig) zapcore.Encoder { |
|
var encoder zapcore.Encoder |
|
switch encoding { |
|
default: |
|
encoder = zapcore.NewConsoleEncoder(encoderConfig) |
|
case "json": |
|
encoder = zapcore.NewJSONEncoder(encoderConfig) |
|
case "console": |
|
encoder = zapcore.NewConsoleEncoder(encoderConfig) |
|
} |
|
return encoder |
|
} |
|
|
|
func filterZapAtomicLevelByViper(level int8) zapcore.Level { |
|
var atomViper zapcore.Level |
|
switch level { |
|
default: |
|
atomViper = zap.InfoLevel |
|
case -1: |
|
atomViper = zap.DebugLevel |
|
case 0: |
|
atomViper = zap.InfoLevel |
|
case 1: |
|
atomViper = zap.WarnLevel |
|
case 2: |
|
atomViper = zap.ErrorLevel |
|
} |
|
return atomViper |
|
} |
|
|