diff --git a/log/go.mod b/log/go.mod index eb0a315..f23fa03 100644 --- a/log/go.mod +++ b/log/go.mod @@ -1,8 +1,3 @@ module git.blauwelle.com/go/crate/log go 1.20 - -require ( - git.blauwelle.com/go/crate/runtimehelper v0.1.0 - git.blauwelle.com/go/crate/synchelper v0.1.0 -) diff --git a/log/go.sum b/log/go.sum deleted file mode 100644 index bb0b349..0000000 --- a/log/go.sum +++ /dev/null @@ -1,4 +0,0 @@ -git.blauwelle.com/go/crate/runtimehelper v0.1.0 h1:qNhtnt9YmHXNHKsGRbwD3AZ3pezpOwrbmX1o9Bz532I= -git.blauwelle.com/go/crate/runtimehelper v0.1.0/go.mod h1:yVMA0GkO9AS7iuPmalHKeWyv9en0JWj25rY1vpTuHhk= -git.blauwelle.com/go/crate/synchelper v0.1.0 h1:4yEXpshkklaws/57P94xN5bA3NmyyKGcZqYmzd6QIK4= -git.blauwelle.com/go/crate/synchelper v0.1.0/go.mod h1:2JkfH+7sF0Q0wiIaDOqG42ZLO5JxpcMfSoyy7db4Y2g= diff --git a/log/logsdk/entry.go b/log/logsdk/entry.go index f331579..8eddc4f 100644 --- a/log/logsdk/entry.go +++ b/log/logsdk/entry.go @@ -4,8 +4,6 @@ import ( "context" "fmt" "time" - - "git.blauwelle.com/go/crate/runtimehelper" ) // Entry 包含日志所需的全部中间信息并负责输出日志 @@ -198,10 +196,10 @@ func (entry Entry) log(ctx context.Context, level Level, message string) { readonlyEntry.Time = time.Now() } if newEntry.GetReportCaller() { - readonlyEntry.Caller = runtimehelper.Caller(newEntry.callerSkip) + readonlyEntry.Caller = getCaller(newEntry.callerSkip) } if newEntry.GetReportStack() { - readonlyEntry.Stack = runtimehelper.Stack(newEntry.callerSkip, 32) + readonlyEntry.Stack = getStack(newEntry.callerSkip, 32) } for _, processor := range newEntry.logger.getLevelProcessors(level) { processor.Process(readonlyEntry) @@ -211,8 +209,8 @@ func (entry Entry) log(ctx context.Context, level Level, message string) { // ReadonlyEntry 是日志系统收集到1条日志记录 type ReadonlyEntry struct { Context context.Context `json:"-"` - Caller runtimehelper.Frame - Stack []runtimehelper.Frame + Caller Frame + Stack []Frame Time time.Time Message string Fields []KV diff --git a/log/logsdk/logjson/option.go b/log/logsdk/logjson/option.go index 5aa5af5..8a22402 100644 --- a/log/logsdk/logjson/option.go +++ b/log/logsdk/logjson/option.go @@ -4,8 +4,6 @@ import ( "io" "os" "time" - - "git.blauwelle.com/go/crate/synchelper" ) // Option 配置日志处理对象 @@ -14,9 +12,9 @@ type Option interface { } // WithBufferPool 配置缓冲池 -func WithBufferPool(pool synchelper.BytesBufferPool) Option { +func WithBufferPool(pool BytesBufferPool) Option { return optionFunc(func(cfg *config) { - cfg.pool = pool + cfg.bytesBufferPool = pool cfg.hasPool = true }) } @@ -62,10 +60,10 @@ func newConfig(opts ...Option) *config { opt.apply(cfg) } if !cfg.hasPool { - cfg.pool = synchelper.NewBytesBufferPool(512, 4096) + cfg.bytesBufferPool = newBytesBufferPool(512, 4096) } if cfg.output == nil { - cfg.output = synchelper.NewSyncWriter(os.Stderr) + cfg.output = newSyncWriter(os.Stderr) } if cfg.timestampFormat == "" { cfg.timestampFormat = time.RFC3339Nano @@ -83,7 +81,7 @@ func defaultConfig() *config { } type config struct { - pool synchelper.BytesBufferPool + bytesBufferPool BytesBufferPool output io.Writer timestampFormat string hasPool bool diff --git a/log/logsdk/logjson/pool.go b/log/logsdk/logjson/pool.go new file mode 100644 index 0000000..cd99492 --- /dev/null +++ b/log/logsdk/logjson/pool.go @@ -0,0 +1,38 @@ +package logjson + +import ( + "bytes" + "sync" +) + +type BytesBufferPool interface { + Get() *bytes.Buffer + Put(buffer *bytes.Buffer) +} + +func newBytesBufferPool(initialSize, maximumSize int) *bytesBufferPool { + return &bytesBufferPool{ + pool: sync.Pool{ + New: func() any { + return bytes.NewBuffer(make([]byte, 0, initialSize)) + }, + }, + maximumSize: maximumSize, + } +} + +type bytesBufferPool struct { + pool sync.Pool + maximumSize int +} + +func (pool *bytesBufferPool) Get() *bytes.Buffer { + return pool.pool.Get().(*bytes.Buffer) +} + +func (pool *bytesBufferPool) Put(buf *bytes.Buffer) { + if buf.Cap() > pool.maximumSize { + return + } + pool.pool.Put(buf) +} diff --git a/log/logsdk/logjson/processor.go b/log/logsdk/logjson/processor.go index 0837a4c..39ad5e4 100644 --- a/log/logsdk/logjson/processor.go +++ b/log/logsdk/logjson/processor.go @@ -7,8 +7,6 @@ import ( "os" "git.blauwelle.com/go/crate/log/logsdk" - "git.blauwelle.com/go/crate/runtimehelper" - "git.blauwelle.com/go/crate/synchelper" ) var _ logsdk.EntryProcessor = &Processor{} @@ -18,7 +16,7 @@ var _ logsdk.EntryProcessor = &Processor{} func New(opts ...Option) *Processor { cfg := newConfig(opts...) return &Processor{ - bufferPool: cfg.pool, + bytesBufferPool: cfg.bytesBufferPool, output: cfg.output, timeFormat: cfg.timestampFormat, disableTime: cfg.disableTime, @@ -29,7 +27,7 @@ func New(opts ...Option) *Processor { // Processor 日志处理对象, 把日志处理成 JSON. type Processor struct { - bufferPool synchelper.BytesBufferPool + bytesBufferPool BytesBufferPool output io.Writer timeFormat string disableTime bool @@ -51,16 +49,16 @@ func (processor *Processor) Process(entry logsdk.ReadonlyEntry) { if entry.Caller.IsValid() { // 1次分配 // 直接取 &entry.Caller 会增加堆内存分配 - m.Caller = &runtimehelper.Frame{ + m.Caller = &logsdk.Frame{ Function: entry.Caller.Function, File: entry.Caller.File, Line: entry.Caller.Line, } } - buf := processor.bufferPool.Get() + buf := processor.bytesBufferPool.Get() buf.Reset() - defer processor.bufferPool.Put(buf) + defer processor.bytesBufferPool.Put(buf) encoder := json.NewEncoder(buf) if processor.prettyPrint { @@ -80,10 +78,10 @@ func (processor *Processor) Process(entry logsdk.ReadonlyEntry) { // Entry 被用来 JSON 序列化 type Entry struct { - Message string `json:"msg"` - Time string `json:"time,omitempty"` - Caller *runtimehelper.Frame `json:"caller,omitempty"` - Stack []runtimehelper.Frame `json:"stack,omitempty"` - Fields []logsdk.KV `json:"fields,omitempty"` - Level logsdk.Level `json:"level"` + 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"` } diff --git a/log/logsdk/logjson/writer.go b/log/logsdk/logjson/writer.go new file mode 100644 index 0000000..fd52d1f --- /dev/null +++ b/log/logsdk/logjson/writer.go @@ -0,0 +1,24 @@ +package logjson + +import ( + "io" + "sync" +) + +func newSyncWriter(writer io.Writer) io.Writer { + return &syncWriter{ + writer: writer, + lock: sync.Mutex{}, + } +} + +type syncWriter struct { + writer io.Writer + lock sync.Mutex +} + +func (w *syncWriter) Write(p []byte) (n int, err error) { + w.lock.Lock() + defer w.lock.Unlock() + return w.writer.Write(p) +} diff --git a/log/logsdk/runtime.go b/log/logsdk/runtime.go new file mode 100644 index 0000000..b7f537e --- /dev/null +++ b/log/logsdk/runtime.go @@ -0,0 +1,52 @@ +package logsdk + +import ( + "runtime" +) + +// Frame 调用相关信息 +type Frame struct { + Function string `json:"func"` + File string `json:"file"` + Line int `json:"line"` +} + +// IsValid 表示是否有效 +func (frame Frame) IsValid() bool { + return frame.Line > 0 +} + +func getCaller(skip int) Frame { + pc := make([]uintptr, 1) + n := runtime.Callers(skip+2, pc) + frame, _ := runtime.CallersFrames(pc[:n]).Next() + if frame.PC == 0 { + return Frame{} + } + return Frame{ + Function: frame.Function, + File: frame.File, + Line: frame.Line, + } +} + +func getStack(skip, maximumFrames int) []Frame { + pc := make([]uintptr, maximumFrames) + n := runtime.Callers(skip+2, pc) + stack := make([]Frame, 0, n) + frames := runtime.CallersFrames(pc[:n]) + for { + frame, more := frames.Next() + if frame.PC != 0 { + stack = append(stack, Frame{ + Function: frame.Function, + File: frame.File, + Line: frame.Line, + }) + } + if !more { + break + } + } + return stack +}