package logger import ( "context" "encoding/json" "fmt" "io" "log" "runtime" "time" ) type Level int8 type Fields map[string]interface{} const ( LevelDebug Level = iota LevelInfo LevelWarn LevelError LevelFatal LevelPanic ) func (l Level) String() string { switch l { case LevelDebug: return "debug" case LevelInfo: return "info" case LevelWarn: return "warn" case LevelError: return "error" case LevelFatal: return "fatal" case LevelPanic: return "panic" } return "" } type Logger struct { newLogger *log.Logger ctx context.Context fields Fields callers []string } func NewLogger(w io.Writer, prefix string, flag int) *Logger { l := log.New(w, prefix, flag) return &Logger{newLogger: l} } func (l *Logger) clone() *Logger { nl := *l return &nl } func (l *Logger) WithFields(f Fields) *Logger { ll := l.clone() if ll.fields == nil { ll.fields = make(Fields) } for k, v := range f { ll.fields[k] = v } return ll } func (l *Logger) WithContext(ctx context.Context) *Logger { ll := l.clone() ll.ctx = ctx return ll } func (l *Logger) WithCaller(skip int) *Logger { ll := l.clone() pc, file, line, ok := runtime.Caller(skip) if ok { f := runtime.FuncForPC(pc) ll.callers = []string{fmt.Sprintf("%s: %d %s", file, line, f.Name())} } return ll } func (l *Logger) WithCallersFrames() *Logger { maxCallerDepth := 25 minCallerDepth := 1 callers := []string{} pcs := make([]uintptr, maxCallerDepth) depth := runtime.Callers(minCallerDepth, pcs) frames := runtime.CallersFrames(pcs[:depth]) for frame, more := frames.Next(); more; frame, more = frames.Next() { callers = append(callers, fmt.Sprintf("%s: %d %s", frame.File, frame.Line, frame.Function)) if !more { break } } ll := l.clone() ll.callers = callers return ll } func (l *Logger) JSONFormat(level Level, message string) map[string]interface{} { data := make(Fields, len(l.fields)+4) data["level"] = level.String() data["time"] = time.Now().Local().UnixNano() data["message"] = message data["callers"] = l.callers if len(l.fields) > 0 { for k, v := range l.fields { if _, ok := data[k]; !ok { data[k] = v } } } return data } func (l *Logger) Output(level Level, message string) { body, _ := json.Marshal(l.JSONFormat(level, message)) content := string(body) switch level { case LevelDebug: l.newLogger.Print(content) case LevelInfo: l.newLogger.Print(content) case LevelWarn: l.newLogger.Print(content) case LevelError: l.newLogger.Print(content) case LevelFatal: l.newLogger.Fatal(content) case LevelPanic: l.newLogger.Panic(content) } } func (l *Logger) Info(v ...interface{}) { l.Output(LevelInfo, fmt.Sprint(v...)) } func (l *Logger) Infof(format string, v ...interface{}) { l.Output(LevelInfo, fmt.Sprintf(format, v...)) } func (l *Logger) Fatal(v ...interface{}) { l.Output(LevelFatal, fmt.Sprint(v...)) } func (l *Logger) Fatalf(format string, v ...interface{}) { l.Output(LevelFatal, fmt.Sprintf(format, v...)) } func (l *Logger) Error(v ...interface{}) { l.Output(LevelError, fmt.Sprint(v...)) } func (l *Logger) Errorf(format string, v ...interface{}) { l.Output(LevelError, fmt.Sprintf(format, v...)) } func (l *Logger) Panicf(format string, v ...interface{}) { l.Output(LevelPanic, fmt.Sprintf(format, v...)) }