package logtext import ( "bytes" "context" "fmt" "io" "strconv" "git.blauwelle.com/go/crate/log/logsdk" "git.blauwelle.com/go/crate/log/logsdk/logjson" ) var _ logsdk.EntryProcessor = &Processor{} func New(opts ...Option) *Processor { cfg := newConfig(opts...) return &Processor{ bytesBufferPool: cfg.bytesBufferPool, output: cfg.output, timeFormat: cfg.timeFormat, timePadding: cfg.timePadding, disableTime: cfg.disableTime, } } type Processor struct { bytesBufferPool logjson.BytesBufferPool output io.Writer timeFormat string timePadding int disableTime bool } func (processor *Processor) Process(_ context.Context, entry logsdk.ReadonlyEntry) { buf := processor.bytesBufferPool.Get() buf.Reset() defer processor.bytesBufferPool.Put(buf) buf.Write(formatLevel(entry.Level)) if !processor.disableTime { buf.WriteByte(' ') timeValue := entry.Time.Format(processor.timeFormat) buf.WriteString(timeValue) writeSpace(buf, processor.timePadding-len(timeValue)) } for _, field := range entry.Fields { buf.WriteByte(' ') buf.WriteString(field.Key) buf.WriteByte('=') writeValue(buf, field.Value) } if entry.Message != "" { buf.WriteByte(' ') buf.WriteString(entry.Message) } buf.WriteByte('\n') if entry.Caller.IsValid() && len(entry.Stack) == 0 { writeFrame(buf, entry.Caller) } if len(entry.Stack) > 0 { for _, frame := range entry.Stack { writeFrame(buf, frame) } } if _, err := buf.WriteTo(processor.output); err != nil { _, _ = fmt.Fprintf(processor.output, "TEXT processor cannot write log: %s\n", err.Error()) } } func writeSpace(buf *bytes.Buffer, n int) { for i := 0; i < n; i++ { buf.WriteByte(' ') } } func writeFrame(buf *bytes.Buffer, frame logsdk.Frame) { buf.WriteByte('|') buf.WriteByte(' ') buf.WriteString(frame.Function) buf.WriteByte('\n') buf.WriteByte('|') buf.WriteByte(' ') buf.WriteByte('\t') buf.WriteString(frame.File) buf.WriteByte(':') buf.WriteString(strconv.Itoa(frame.Line)) buf.WriteByte('\n') } func writeValue(buf *bytes.Buffer, value any) { switch value := value.(type) { case nil: buf.WriteString("") case string: buf.WriteString(value) case error: buf.WriteString(value.Error()) case fmt.Stringer: buf.WriteString(value.String()) default: _, _ = fmt.Fprintf(buf, "%v", value) } } func formatLevel(level logsdk.Level) []byte { switch level { case logsdk.LevelPanic: return []byte(LevelPanicValue) case logsdk.LevelFatal: return []byte(LevelFatalValue) case logsdk.LevelError: return []byte(LevelErrorValue) case logsdk.LevelWarn: return []byte(LevelWarnValue) case logsdk.LevelInfo: return []byte(LevelInfoValue) case logsdk.LevelDebug: return []byte(LevelDebugValue) case logsdk.LevelTrace: return []byte(LevelTraceValue) case logsdk.LevelDisabled: fallthrough default: return []byte(levelUnknownValue) } } const ( LevelPanicValue = "PNC" LevelFatalValue = "FTL" LevelErrorValue = "ERR" LevelWarnValue = "WRN" LevelInfoValue = "INF" LevelDebugValue = "DBG" LevelTraceValue = "TRC" levelUnknownValue = "UNK" )