From e2ef0f5a8406b03dd2ae2014323ac8aa52f0ed3f Mon Sep 17 00:00:00 2001 From: Duc Nguyen Date: Sat, 6 Nov 2021 22:05:26 +0700 Subject: [PATCH] Refactor code --- config.go | 1 + logrus.go | 32 +++++++++++++++++++++------- rotatelogs/rotation_writer.go | 15 ++++++++++++++ util.go | 39 +++++++++++++++++++++++++++++++++++ zap/config.go | 1 + zap/file.go | 39 +++++++++++++++++++++++++++++++++++ zap/util.go | 39 +++++++++++++++++++++++++++++++++++ zap/zap.go | 38 +++++++++++++++++++++++----------- 8 files changed, 184 insertions(+), 20 deletions(-) create mode 100644 rotatelogs/rotation_writer.go create mode 100644 util.go create mode 100644 zap/file.go create mode 100644 zap/util.go diff --git a/config.go b/config.go index 47ff754..9d5b7a7 100644 --- a/config.go +++ b/config.go @@ -4,6 +4,7 @@ import "github.com/sirupsen/logrus" type Config struct { Level string `yaml:"level" mapstructure:"level" json:"level,omitempty" gorm:"column:level" bson:"level,omitempty" dynamodbav:"level,omitempty" firestore:"level,omitempty"` + Output string `yaml:"output" mapstructure:"output" json:"output,omitempty" gorm:"column:output" bson:"output,omitempty" dynamodbav:"output,omitempty" firestore:"output,omitempty"` Duration string `yaml:"duration" mapstructure:"duration" json:"duration,omitempty" gorm:"column:duration" bson:"duration,omitempty" dynamodbav:"duration,omitempty" firestore:"duration,omitempty"` Fields string `yaml:"fields" mapstructure:"fields" json:"fields,omitempty" gorm:"column:fields" bson:"fields,omitempty" dynamodbav:"fields,omitempty" firestore:"fields,omitempty"` FieldMap string `yaml:"field_map" mapstructure:"field_map" json:"fieldMap,omitempty" gorm:"column:fieldmap" bson:"fieldMap,omitempty" dynamodbav:"fieldMap,omitempty" firestore:"fieldMap,omitempty"` diff --git a/logrus.go b/logrus.go index 323daab..53243fe 100644 --- a/logrus.go +++ b/logrus.go @@ -5,6 +5,8 @@ import ( "encoding/json" "fmt" "github.com/sirupsen/logrus" + "io" + "os" "strings" "time" ) @@ -12,7 +14,11 @@ import ( var fieldConfig FieldConfig var logger *logrus.Logger -func Initialize(c Config) *logrus.Logger { +func Initialize(c Config, opts...func(logLocation string, rotationTime time.Duration) (io.Writer, func() error)) *logrus.Logger { + var getWriter func(logLocation string, rotationTime time.Duration) (io.Writer, func() error) + if len(opts) > 0 && opts[0] != nil { + getWriter = opts[0] + } fieldConfig.FieldMap = c.FieldMap if len(c.Duration) > 0 { fieldConfig.Duration = c.Duration @@ -36,6 +42,16 @@ func Initialize(c Config) *logrus.Logger { x := &formatter l.SetFormatter(x) logrus.SetFormatter(x) + if len(c.Output) > 0 && getWriter != nil { + CreatePath(c.Output) + w, close := getWriter(c.Output, 24*time.Hour) + l.SetOutput(io.MultiWriter(os.Stderr, w)) + logrus.SetOutput(io.MultiWriter(os.Stderr, w)) + handler := func() { + close() + } + logrus.RegisterExitHandler(handler) + } if len(c.Level) > 0 { if level, err := logrus.ParseLevel(c.Level); err == nil { l.SetLevel(level) @@ -340,49 +356,49 @@ func PanicFields(ctx context.Context, msg string, fields map[string]interface{}) LogWithFields(ctx, logrus.PanicLevel, msg, fields) } -func LogTrace(ctx context.Context, msg string, opts...map[string]interface{}) { +func LogTrace(ctx context.Context, msg string, opts ...map[string]interface{}) { if len(opts) > 0 { TraceWithFields(ctx, msg, opts[0]) } else { TraceWithFields(ctx, msg, nil) } } -func LogDebug(ctx context.Context, msg string, opts...map[string]interface{}) { +func LogDebug(ctx context.Context, msg string, opts ...map[string]interface{}) { if len(opts) > 0 { DebugWithFields(ctx, msg, opts[0]) } else { DebugWithFields(ctx, msg, nil) } } -func LogInfo(ctx context.Context, msg string, opts... map[string]interface{}) { +func LogInfo(ctx context.Context, msg string, opts ...map[string]interface{}) { if len(opts) > 0 { InfoWithFields(ctx, msg, opts[0]) } else { InfoWithFields(ctx, msg, nil) } } -func LogWarn(ctx context.Context, msg string, opts...map[string]interface{}) { +func LogWarn(ctx context.Context, msg string, opts ...map[string]interface{}) { if len(opts) > 0 { WarnWithFields(ctx, msg, opts[0]) } else { WarnWithFields(ctx, msg, nil) } } -func LogError(ctx context.Context, msg string, opts...map[string]interface{}) { +func LogError(ctx context.Context, msg string, opts ...map[string]interface{}) { if len(opts) > 0 { ErrorWithFields(ctx, msg, opts[0]) } else { ErrorWithFields(ctx, msg, nil) } } -func LogFatal(ctx context.Context, msg string, opts...map[string]interface{}) { +func LogFatal(ctx context.Context, msg string, opts ...map[string]interface{}) { if len(opts) > 0 { FatalWithFields(ctx, msg, opts[0]) } else { FatalWithFields(ctx, msg, nil) } } -func LogPanic(ctx context.Context, msg string, opts...map[string]interface{}) { +func LogPanic(ctx context.Context, msg string, opts ...map[string]interface{}) { if len(opts) > 0 { PanicWithFields(ctx, msg, opts[0]) } else { diff --git a/rotatelogs/rotation_writer.go b/rotatelogs/rotation_writer.go new file mode 100644 index 0000000..9e6eb8f --- /dev/null +++ b/rotatelogs/rotation_writer.go @@ -0,0 +1,15 @@ +package rotatelogs + +import ( + "github.com/lestrrat/go-file-rotatelogs" + "io" + "time" +) + +func GetWriter(logLocation string, rotationTime time.Duration) (io.Writer, func() error){ + writer, _ := rotatelogs.New( + logLocation, + rotatelogs.WithRotationTime(rotationTime), + ) + return writer, writer.Close +} diff --git a/util.go b/util.go new file mode 100644 index 0000000..fda7e3d --- /dev/null +++ b/util.go @@ -0,0 +1,39 @@ +package log + +import ( + "errors" + "os" + "path/filepath" + "regexp" + "strings" + "time" +) + +func GenerateFileNameLog(pathFileNameFormat string) string { + path := pathFileNameFormat + if strings.Contains(pathFileNameFormat, "$") { + s := getLayoutsFormat(pathFileNameFormat) + if len(s) > 1 { + timeFormat := time.Now().Format(s[1]) + path = strings.ReplaceAll(pathFileNameFormat, s[0], timeFormat) + } + } + CreatePath(path) + return path +} + +func CreatePath(path string) error { + dir := filepath.Dir(path) + if _, err := os.Stat(dir); errors.Is(err, os.ErrNotExist) { + err2 := os.Mkdir(dir, os.ModePerm) + if err2 != nil { + return err2 + } + } + return nil +} + +func getLayoutsFormat(s string) []string { + re := regexp.MustCompile("\\$\\{(.*?)\\}") + return re.FindStringSubmatch(s) +} diff --git a/zap/config.go b/zap/config.go index fb32125..0525079 100644 --- a/zap/config.go +++ b/zap/config.go @@ -8,6 +8,7 @@ type Config struct { Map *FieldMap `yaml:"map" mapstructure:"map" json:"map,omitempty" gorm:"column:map" bson:"map,omitempty" dynamodbav:"map,omitempty" firestore:"map,omitempty"` CallerLevel string `yaml:"caller_level" mapstructure:"caller_level" json:"callerLevel,omitempty" gorm:"column:callerlevel" bson:"callerLevel,omitempty" dynamodbav:"callerLevel,omitempty" firestore:"callerLevel,omitempty"` CallerSkip int `yaml:"caller_skip" mapstructure:"caller_skip" json:"callerSkip,omitempty" gorm:"column:callerskip" bson:"callerSkip,omitempty" dynamodbav:"callerSkip,omitempty" firestore:"callerSkip,omitempty"` + Output string `yaml:"output" mapstructure:"output" json:"output,omitempty" gorm:"column:output" bson:"output,omitempty" dynamodbav:"output,omitempty" firestore:"output,omitempty"` } type FieldMap struct { diff --git a/zap/file.go b/zap/file.go new file mode 100644 index 0000000..a674ee7 --- /dev/null +++ b/zap/file.go @@ -0,0 +1,39 @@ +package log + +import ( + "errors" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" + "os" + "path/filepath" +) + +func NewWriter(ws zapcore.WriteSyncer, conf zap.Config) zap.Option { + var enc zapcore.Encoder + switch conf.Encoding { + case "json": + enc = zapcore.NewJSONEncoder(conf.EncoderConfig) + case "console": + enc = zapcore.NewConsoleEncoder(conf.EncoderConfig) + default: + panic("unknown encoding") + } + return zap.WrapCore(func(core zapcore.Core) zapcore.Core { + return zapcore.NewCore(enc, ws, conf.Level) + }) +} + +func GetWriteSyncer(path string) zapcore.WriteSyncer { + dir := filepath.Dir(path) + if _, err := os.Stat(dir); errors.Is(err, os.ErrNotExist) { + err2 := os.Mkdir(dir, os.ModePerm) + if err2 != nil { + panic(err2) + } + } + file, err := os.OpenFile(path, os.O_RDWR|os.O_APPEND|os.O_CREATE, 644) + if err != nil { + panic(err) + } + return zapcore.AddSync(file) +} diff --git a/zap/util.go b/zap/util.go new file mode 100644 index 0000000..fda7e3d --- /dev/null +++ b/zap/util.go @@ -0,0 +1,39 @@ +package log + +import ( + "errors" + "os" + "path/filepath" + "regexp" + "strings" + "time" +) + +func GenerateFileNameLog(pathFileNameFormat string) string { + path := pathFileNameFormat + if strings.Contains(pathFileNameFormat, "$") { + s := getLayoutsFormat(pathFileNameFormat) + if len(s) > 1 { + timeFormat := time.Now().Format(s[1]) + path = strings.ReplaceAll(pathFileNameFormat, s[0], timeFormat) + } + } + CreatePath(path) + return path +} + +func CreatePath(path string) error { + dir := filepath.Dir(path) + if _, err := os.Stat(dir); errors.Is(err, os.ErrNotExist) { + err2 := os.Mkdir(dir, os.ModePerm) + if err2 != nil { + return err2 + } + } + return nil +} + +func getLayoutsFormat(s string) []string { + re := regexp.MustCompile("\\$\\{(.*?)\\}") + return re.FindStringSubmatch(s) +} diff --git a/zap/zap.go b/zap/zap.go index 52cba98..4d663e1 100644 --- a/zap/zap.go +++ b/zap/zap.go @@ -6,6 +6,8 @@ import ( "fmt" "go.uber.org/zap" "go.uber.org/zap/zapcore" + "io" + "os" "strings" "time" ) @@ -16,8 +18,10 @@ var logger *zap.Logger func SetLogger(logger0 *zap.Logger) { logger = logger0 } - -func Initialize(c Config, opts...zapcore.Core) (*zap.Logger, error) { +func Initialize(c Config, opts ...zapcore.Core) (*zap.Logger, error) { + return InitializeWithWriter(c, nil, opts...) +} +func InitializeWithWriter(c Config, getWriter func(logLocation string, rotationTime time.Duration) (io.Writer, func() error), opts ...zapcore.Core) (*zap.Logger, error) { fieldConfig.FieldMap = c.FieldMap if len(c.Duration) > 0 { fieldConfig.Duration = c.Duration @@ -45,15 +49,25 @@ func Initialize(c Config, opts...zapcore.Core) (*zap.Logger, error) { if err := level.Set(c.Level); err != nil { return nil, err } - - l, err := NewConfig(c).Build(zap.WrapCore(func(core zapcore.Core) zapcore.Core { + cfg := NewConfig(c) + options := []zap.Option{zap.WrapCore(func(core zapcore.Core) zapcore.Core { if len(opts) > 0 && opts[0] != nil { return opts[0] } else { c, _ := NewLogTraceLevelCore(core, zap.DebugLevel, c.CallerSkip, showCallerLv...) return c } - })) + })} + if len(c.Output) > 0 && getWriter != nil { + err := CreatePath(c.Output) + if err != nil { + return nil, err + } + w, _ := getWriter(c.Output, 24*time.Hour) + syncer := zap.CombineWriteSyncers(os.Stdout, zapcore.AddSync(w)) + options = append(options, NewWriter(syncer, cfg)) + } + l, err := cfg.Build(options...) if err == nil { logger = l } @@ -563,49 +577,49 @@ func DPanicFields(ctx context.Context, msg string, fields map[string]interface{} DPanicWithFields(ctx, msg, fields) } -func LogDebug(ctx context.Context, msg string, opts...map[string]interface{}) { +func LogDebug(ctx context.Context, msg string, opts ...map[string]interface{}) { if len(opts) > 0 { DebugWithFields(ctx, msg, opts[0]) } else { DebugWithFields(ctx, msg, nil) } } -func LogInfo(ctx context.Context, msg string, opts... map[string]interface{}) { +func LogInfo(ctx context.Context, msg string, opts ...map[string]interface{}) { if len(opts) > 0 { InfoWithFields(ctx, msg, opts[0]) } else { InfoWithFields(ctx, msg, nil) } } -func LogWarn(ctx context.Context, msg string, opts...map[string]interface{}) { +func LogWarn(ctx context.Context, msg string, opts ...map[string]interface{}) { if len(opts) > 0 { WarnWithFields(ctx, msg, opts[0]) } else { WarnWithFields(ctx, msg, nil) } } -func LogError(ctx context.Context, msg string, opts...map[string]interface{}) { +func LogError(ctx context.Context, msg string, opts ...map[string]interface{}) { if len(opts) > 0 { ErrorWithFields(ctx, msg, opts[0]) } else { ErrorWithFields(ctx, msg, nil) } } -func LogFatal(ctx context.Context, msg string, opts...map[string]interface{}) { +func LogFatal(ctx context.Context, msg string, opts ...map[string]interface{}) { if len(opts) > 0 { FatalWithFields(ctx, msg, opts[0]) } else { FatalWithFields(ctx, msg, nil) } } -func LogPanic(ctx context.Context, msg string, opts...map[string]interface{}) { +func LogPanic(ctx context.Context, msg string, opts ...map[string]interface{}) { if len(opts) > 0 { PanicWithFields(ctx, msg, opts[0]) } else { PanicWithFields(ctx, msg, nil) } } -func LogDPanic(ctx context.Context, msg string, opts...map[string]interface{}) { +func LogDPanic(ctx context.Context, msg string, opts ...map[string]interface{}) { if len(opts) > 0 { DPanicWithFields(ctx, msg, opts[0]) } else {