|
|
|
package logjson
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
|
|
|
|
"git.blauwelle.com/go/crate/log/logsdk"
|
|
|
|
)
|
|
|
|
|
|
|
|
var _ logsdk.EntryProcessor = &Processor{}
|
|
|
|
|
|
|
|
// New 返回日志处理对象,
|
|
|
|
// 返回的对象是 [logsdk.EntryProcessor].
|
|
|
|
func New(opts ...Option) *Processor {
|
|
|
|
cfg := newConfig(opts...)
|
|
|
|
return &Processor{
|
|
|
|
bytesBufferPool: cfg.bytesBufferPool,
|
|
|
|
output: cfg.output,
|
|
|
|
timeFormat: cfg.timestampFormat,
|
|
|
|
disableTime: cfg.disableTime,
|
|
|
|
disableHTMLEscape: cfg.disableHTMLEscape,
|
|
|
|
prettyPrint: cfg.prettyPrint,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Processor 日志处理对象, 把日志处理成 JSON.
|
|
|
|
type Processor struct {
|
|
|
|
bytesBufferPool BytesBufferPool
|
|
|
|
output io.Writer
|
|
|
|
timeFormat string
|
|
|
|
disableTime bool
|
|
|
|
disableHTMLEscape bool
|
|
|
|
prettyPrint bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// Process 处理日志
|
|
|
|
func (processor *Processor) Process(_ context.Context, entry logsdk.ReadonlyEntry) {
|
|
|
|
m := Entry{
|
|
|
|
Stack: entry.Stack,
|
|
|
|
Fields: entry.Fields,
|
|
|
|
Level: entry.Level,
|
|
|
|
Message: entry.Message,
|
|
|
|
}
|
|
|
|
if !processor.disableTime {
|
|
|
|
m.Time = entry.Time.Format(processor.timeFormat) // 1次分配
|
|
|
|
}
|
|
|
|
if entry.Caller.IsValid() {
|
|
|
|
// 1次分配
|
|
|
|
// 直接取 &entry.Caller 会增加堆内存分配
|
|
|
|
m.Caller = &logsdk.Frame{
|
|
|
|
Function: entry.Caller.Function,
|
|
|
|
File: entry.Caller.File,
|
|
|
|
Line: entry.Caller.Line,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
buf := processor.bytesBufferPool.Get()
|
|
|
|
buf.Reset()
|
|
|
|
defer processor.bytesBufferPool.Put(buf)
|
|
|
|
|
|
|
|
encoder := json.NewEncoder(buf)
|
|
|
|
if processor.prettyPrint {
|
|
|
|
encoder.SetIndent("", " ")
|
|
|
|
}
|
|
|
|
encoder.SetEscapeHTML(!processor.disableHTMLEscape)
|
|
|
|
|
|
|
|
// Encode 2次分配
|
|
|
|
if err := encoder.Encode(m); err != nil {
|
|
|
|
_, _ = fmt.Fprintf(os.Stderr, "JSON processor cannot encode log %#v: %s\n", m, err.Error())
|
|
|
|
}
|
|
|
|
|
|
|
|
if _, err := buf.WriteTo(processor.output); err != nil {
|
|
|
|
_, _ = fmt.Fprintf(os.Stderr, "JSON processor cannot write log: %s\n", err.Error())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Entry 被用来 JSON 序列化
|
|
|
|
type Entry struct {
|
|
|
|
Message string `json:"msg"`
|
|
|
|
Time string `json:"time,omitempty"`
|
|
|
|
Caller *logsdk.Frame `json:"caller,omitempty"`
|
|
|
|
Stack []logsdk.Frame `json:"stack,omitempty"`
|
|
|
|
Fields []logsdk.KV `json:"fields,omitempty"`
|
|
|
|
Level logsdk.Level `json:"level"`
|
|
|
|
}
|