package log import ( "bytes" "encoding/json" "fmt" "runtime" "strings" "sync" "time" "github.com/sirupsen/logrus" ) type levelSkip struct { Skip int Once sync.Once } // JSONFormatter formats logs into parsable json type JSONFormatter struct { diffSkip int skip []*levelSkip once sync.Once } // Format renders a single log entry func (h *JSONFormatter) Format(e *logrus.Entry) ([]byte, error) { skipOnce := h.skip[int(e.Level)] skipOnce.Once.Do(func() { for i := 4; i < 100; i++ { // log.Println(i) if pc, _, _, ok := runtime.Caller(i); ok { funcStruct := runtime.FuncForPC(pc) // log.Println(funcStruct.Name(), file, line) if !strings.Contains(funcStruct.Name(), "github.com/sirupsen/logrus.") { skipOnce.Skip++ if skipOnce.Skip >= 2 { skipOnce.Skip = i - 3 break } } } else { break } } }) var fileinfo string if _, file, line, ok := runtime.Caller(skipOnce.Skip + h.diffSkip); ok { if e.Level == logrus.InfoLevel { fileinfo = fmt.Sprintf("%s:%d", file, line) } else { ps := strings.Split(file, "/") fileinfo = fmt.Sprintf("%s:%d", strings.Join(ps, "/"), line) } } var Data map[string]any = make(map[string]any, 4) Data[logrus.FieldKeyTime] = e.Time.Format(time.RFC3339) Data[logrus.FieldKeyMsg] = e.Message Data[logrus.FieldKeyLevel] = e.Level Data[logrus.FieldKeyFile] = fileinfo var b *bytes.Buffer if e.Buffer != nil { b = e.Buffer } else { b = &bytes.Buffer{} } encoder := json.NewEncoder(b) encoder.SetEscapeHTML(false) if err := encoder.Encode(Data); err != nil { return nil, fmt.Errorf("failed to marshal fields to JSON, %w", err) } return b.Bytes(), nil }