gin-blog/pkg/logger/logger.go

171 lines
3.3 KiB
Go

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...))
}