3
0
2022-11-14 09:26:39 +01:00

155 lines
2.9 KiB
Go

package logger
import (
"fmt"
"io"
"sync"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
type (
rrBufEntry struct {
num int
rec []byte
}
// simple round-robin struct that holds our buffer with log entries
rr struct {
mux sync.RWMutex
num int
buf []*rrBufEntry
}
debugBufferingLogger struct {
zapcore.LevelEnabler
out *rr
enc zapcore.Encoder
}
)
const (
// allowing 10k entries (no limiting the entry size)
debugLogCap = 10240
)
var (
// initialize debug logger round robin db
debugLogRR = &rr{
buf: make([]*rrBufEntry, 0),
}
)
// WriteLogBuffer provides access to default debug log buffer
func WriteLogBuffer(w io.Writer, after, limit int) (_ int, err error) {
return writeLogBuffer(w, debugLogRR, after, limit)
}
// writes stream of entries from log buffer into provided writer array of JSON objects.
func writeLogBuffer(w io.Writer, logBuf *rr, after, limit int) (_ int, err error) {
var (
// was at least one entry outputted?
has bool
)
debugLogRR.mux.RLock()
defer debugLogRR.mux.RUnlock()
if _, err = w.Write([]byte{'['}); err != nil {
return
}
for _, e := range logBuf.buf {
if after >= e.num {
continue
}
// count back to zero
limit--
if has {
if _, err = w.Write([]byte{','}); err != nil {
return
}
}
if _, err = w.Write(e.rec); err != nil {
return
}
if limit == 0 {
break
}
has = true
}
if _, err = w.Write([]byte{']'}); err != nil {
return
}
return
}
func (r *rr) append(ent []byte) {
r.mux.Lock()
defer r.mux.Unlock()
r.num++
// modify serialized json
var bufEnt = &rrBufEntry{r.num, append(ent[:len(ent)-2], []byte(fmt.Sprintf(`,"index":%d}`, r.num))...)}
if len(r.buf) >= debugLogCap {
r.buf = append(r.buf[1:], bufEnt)
} else {
r.buf = append(r.buf, bufEnt)
}
}
// DebugBufferedLogger provides buffered logger compatible with zap.
func DebugBufferedLogger(out *rr) *debugBufferingLogger {
var encConf = zap.NewProductionEncoderConfig()
encConf.EncodeTime = zapcore.RFC3339NanoTimeEncoder
return &debugBufferingLogger{
LevelEnabler: zapcore.DebugLevel,
out: out,
enc: zapcore.NewJSONEncoder(encConf),
}
}
func (c *debugBufferingLogger) With(fields []zapcore.Field) zapcore.Core {
clone := c.clone()
for i := range fields {
fields[i].AddTo(clone.enc)
}
return clone
}
func (c *debugBufferingLogger) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {
if c.Enabled(ent.Level) {
return ce.AddCore(ent, c)
}
return ce
}
func (c *debugBufferingLogger) Write(ent zapcore.Entry, fields []zapcore.Field) error {
encbuf, err := c.enc.EncodeEntry(ent, fields)
if err != nil {
return err
}
c.out.append(encbuf.Bytes())
return nil
}
func (c *debugBufferingLogger) Sync() error { return nil }
func (c *debugBufferingLogger) clone() *debugBufferingLogger {
return &debugBufferingLogger{
LevelEnabler: c.LevelEnabler,
enc: c.enc.Clone(),
out: c.out,
}
}