You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
crate/log/logsdk/logtext/processor.go

147 lines
3.1 KiB
Go

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("<nil>")
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"
)