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"` }