package logger import ( "fmt" "io" "runtime" "strings" "github.com/sirupsen/logrus" ) type LogFields map[string]interface{} // Add adds new fields to the list of LogFields. func (l LogFields) Add(newFields LogFields) LogFields { resultFields := make(LogFields, len(l)+len(newFields)) for field, value := range l { resultFields[field] = value } for field, value := range newFields { resultFields[field] = value } return resultFields } // Copy copies the LogFields. func (l LogFields) Copy() LogFields { cpy := make(LogFields, len(l)) for k, v := range l { cpy[k] = v } return cpy } type logger struct { log *logrus.Logger } // Fatal implements LoggerAdapter fatal level func (l *logger) Fatal(msg string, err error, fields LogFields) { lf := cpyMap(fields) lf["error"] = err.Error() l.log.WithFields(lf).Fatal(msg) } // Panic implements LoggerAdapter panic level func (l *logger) Panic(msg string, err error, fields LogFields) { lf := cpyMap(fields) lf["error"] = err.Error() l.log.WithFields(lf).Panic(msg) } // Warn implements LoggerAdapter warning level func (l *logger) Warn(msg string, fields LogFields) { lf := cpyMap(fields) l.log.WithFields(lf).Warn(msg) } // Debug implements LoggerAdapter debug level func (l *logger) Debug(msg string, fields LogFields) { lf := cpyMap(fields) l.log.WithFields(lf).Debug(msg) } // Error implements LoggerAdapter error level func (l *logger) Error(msg string, err error, fields LogFields) { lf := cpyMap(fields) lf["error"] = err.Error() l.log.WithFields(lf).Error(msg) } // Info implements LoggerAdapter info level func (l *logger) Info(msg string, fields LogFields) { lf := cpyMap(fields) l.log.WithFields(lf).Info(msg) } // Trace implements LoggerAdapter trace level func (l *logger) Trace(msg string, fields LogFields) { lf := cpyMap(fields) l.log.WithFields(lf).Trace(msg) } type LoggerAdapter interface { Info(msg string, fields LogFields) Debug(msg string, fields LogFields) Trace(msg string, fields LogFields) Warn(msg string, fields LogFields) Error(msg string, err error, fields LogFields) Fatal(msg string, err error, fields LogFields) Panic(msg string, err error, fields LogFields) } func New(out io.Writer, lvl string) LoggerAdapter { level, err := logrus.ParseLevel(lvl) if err != nil { level = logrus.InfoLevel } if lvl == "" { level = logrus.InfoLevel } var log = logrus.New() log.Formatter = new(logrus.JSONFormatter) log.SetFormatter(&logrus.TextFormatter{ TimestampFormat: "02-01-2006 15:04:05", // the "time" field configuration FullTimestamp: true, DisableLevelTruncation: true, // log level field configuration CallerPrettyfier: func(f *runtime.Frame) (string, string) { // this function is required when you want to introduce your custom format. // In my case I wanted file and line to look like this `file="engine.go:141` // but f.File provides a full path along with the file name. // So in `formatFilePath()` function I just trimmed everything before the file name // and added a line number in the end return "", fmt.Sprintf("%s:%d", formatFilePath(f.File), f.Line) }, }) log.Out = out log.Level = level return &logger{ log: log, } } func formatFilePath(path string) string { arr := strings.Split(path, "/") return arr[len(arr)-1] } func cpyMap(fields LogFields) logrus.Fields { copied := make(logrus.Fields) for i, e := range fields { copied[i] = e } return copied }