zlog: update implementations

This commit is contained in:
Tigor Hutasuhut 2024-08-23 12:29:47 +07:00
parent bfaf829142
commit 0256f75bfb
7 changed files with 175 additions and 33 deletions

40
core/zcaller/zcaller.go Normal file
View file

@ -0,0 +1,40 @@
package zcaller
import (
"runtime"
)
// Current returns the ProgramCounter of the caller.
//
// It gives information about the function that called
// this.
//
// Returns 0 if caller information is not available.
func Current() uintptr {
return Get(3)
}
// Get works just like Current, but it allows you to
// specify the number of frames to skip.
//
// For reference, current is equivalent to Get(3).
// (use Get(2) because Current wraps this function and has to add +1 to the skip value)
//
// Returns 0 if caller information is not available.
func Get(skip int) uintptr {
pcs := make([]uintptr, 1)
n := runtime.Callers(skip, pcs)
if n == 0 {
return 0
}
return pcs[0]
}
// Frame creates a runtime.Frame from a ProgramCounter.
//
// Return zero value if the frame is not available.
func Frame(pc uintptr) runtime.Frame {
frames := runtime.CallersFrames([]uintptr{pc})
frame, _ := frames.Next()
return frame
}

View file

@ -16,7 +16,7 @@ func newJsonHandlerPool(handlerOption *slog.HandlerOptions) *jsonHandlerPool {
New: func() any {
buf := new(bytes.Buffer)
return &jsonHandler{
Buffer: buf,
buf: buf,
pool: handlerPool,
JSONHandler: slog.NewJSONHandler(buf, handlerOption),
}
@ -34,15 +34,15 @@ func (jhp *jsonHandlerPool) Get() *jsonHandler {
}
type jsonHandler struct {
*bytes.Buffer
buf *bytes.Buffer
pool *jsonHandlerPool
*slog.JSONHandler
}
func (j *jsonHandler) Put() {
// Only put back to pool if buffer is smaller than 4MB in RAM size.
if j.Buffer.Cap() < 1024*1024*4 {
j.Buffer.Reset()
if j.buf.Cap() < 1024*1024*4 {
j.buf.Reset()
j.pool.Put(j)
}
}

View file

@ -1 +1,53 @@
package zlog
import (
"log/slog"
"os"
"runtime"
"strings"
"gitlab.bareksa.com/backend/zen/core/zcaller"
)
// Caller represents the caller of a function.
//
// It gives helper methods to get the caller's information.
type Caller uintptr
func (ca Caller) LogValue() slog.Value {
if ca == 0 {
return slog.AnyValue(nil)
}
f := frame{ca.Frame()}
return slog.GroupValue(
slog.String("file", f.ShortFile()),
slog.Int("line", f.Line),
slog.String("function", f.ShortFunction()),
)
}
func (ca Caller) Frame() runtime.Frame {
return zcaller.Frame(uintptr(ca))
}
type frame struct {
runtime.Frame
}
func (f frame) ShortFile() string {
wd, err := os.Getwd()
if err != nil {
return f.File
}
if after, found := strings.CutPrefix(f.File, wd); found {
return strings.TrimPrefix(after, string(os.PathSeparator))
}
return f.File
}
func (f frame) ShortFunction() string {
split := strings.Split(f.Function, string(os.PathSeparator))
return split[len(split)-1]
}

View file

@ -1 +1,14 @@
package zlog
import (
"context"
"log/slog"
)
// Null is a no-op log handler.
type Null struct{}
func (Null) Enabled(context.Context, slog.Level) bool { return false }
func (Null) Handle(context.Context, slog.Record) error { return nil }
func (Null) WithAttrs([]slog.Attr) slog.Handler { return Null{} }
func (nu Null) WithGroup(string) slog.Handler { return Null{} }

View file

@ -5,8 +5,10 @@ import (
"io"
"log/slog"
"sync"
"time"
"github.com/fatih/color"
"github.com/tidwall/pretty"
)
type WriteLocker interface {
@ -32,7 +34,7 @@ type Log struct {
jsonPool *jsonHandlerPool
withAttrs []slog.Attr
withGroup []string
handlerOption *slog.HandlerOptions
opts *slog.HandlerOptions
pretty bool
color bool
writer WriteLocker
@ -56,10 +58,10 @@ func (log *Log) Clone(w io.Writer) *Log {
level: log.level,
withAttrs: withAttrs,
withGroup: withGroup,
handlerOption: log.handlerOption,
opts: log.opts,
pretty: log.pretty,
bufPool: newBufferPool(),
jsonPool: newJsonHandlerPool(log.handlerOption),
jsonPool: newJsonHandlerPool(log.opts),
writer: output,
}
}
@ -94,19 +96,51 @@ func (lo *Log) Handle(ctx context.Context, record slog.Record) error {
buf := lo.bufPool.Get()
defer lo.bufPool.Put(buf)
panic("not implemented")
if record.PC != 0 && lo.opts.AddSource {
f := frame{Caller(record.PC).Frame()}
levelColor.Fprint(buf, f.ShortFile())
levelColor.Fprint(buf, ":")
levelColor.Fprint(buf, f.Line)
levelColor.Fprint(buf, " -- ")
levelColor.Fprint(buf, f.ShortFunction())
buf.WriteByte('\n')
}
// if record.PC != 0 && pr.opts.AddSource {
// frame := caller.From(record.PC).Frame
// levelColor.Fprint(buf, frame.File)
// levelColor.Fprint(buf, ":")
// levelColor.Fprint(buf, frame.Line)
// levelColor.Fprint(buf, " -- ")
// split := strings.Split(frame.Function, string(os.PathSeparator))
// fnName := split[len(split)-1]
// levelColor.Fprint(buf, fnName)
// buf.WriteByte('\n')
// }
if !record.Time.IsZero() {
const format = `[` + time.DateTime + `] `
b := record.Time.AppendFormat(nil, format)
buf.Write(b)
}
buf.WriteByte('[')
levelColor.Add(color.Bold).Fprint(buf, record.Level.String())
buf.WriteString("] ")
if record.Message != "" {
buf.WriteString(record.Message)
}
buf.WriteByte('\n')
jHandler := lo.jsonPool.Get()
defer jHandler.Put()
_ = jHandler.Handle(ctx, record)
if jHandler.buf.Len() > 3 { // Skip empty objects like "{}\n"
jsonData := jHandler.buf.Bytes()
jsonData = pretty.Pretty(jsonData)
if lo.color {
jsonData = pretty.Color(jsonData, nil)
}
buf.Write(jsonData)
}
buf.WriteByte('\n')
lo.writer.Lock()
defer lo.writer.Unlock()
_, err := buf.WriteTo(lo.writer)
return err
}
// WithAttrs implements the slog.Handler interface.

1
go.mod
View file

@ -6,6 +6,7 @@ require (
connectrpc.com/connect v1.16.2
connectrpc.com/grpcreflect v1.2.0
github.com/fatih/color v1.17.0
github.com/tidwall/pretty v1.2.1
golang.org/x/net v0.23.0
google.golang.org/protobuf v1.34.2
)

2
go.sum
View file

@ -11,6 +11,8 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=