diff --git a/core/zcaller/zcaller.go b/core/zcaller/zcaller.go index 5fe2656..bb5a1a9 100644 --- a/core/zcaller/zcaller.go +++ b/core/zcaller/zcaller.go @@ -1,7 +1,10 @@ package zcaller import ( + "log/slog" + "os" "runtime" + "strings" ) // Current returns the ProgramCounter of the caller. @@ -38,3 +41,48 @@ func Frame(pc uintptr) runtime.Frame { frame, _ := frames.Next() return frame } + +// 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) + } + + return ca.Frame().LogValue() +} + +func (ca Caller) Frame() RuntimeFrame { + return RuntimeFrame{Frame(uintptr(ca))} +} + +type RuntimeFrame struct { + runtime.Frame +} + +func (ru RuntimeFrame) LogValue() slog.Value { + return slog.GroupValue( + slog.String("file", ru.ShortFile()), + slog.Int("line", ru.Line), + slog.String("function", ru.ShortFunction()), + ) +} + +func (f RuntimeFrame) 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 RuntimeFrame) ShortFunction() string { + split := strings.Split(f.Function, string(os.PathSeparator)) + return split[len(split)-1] +} diff --git a/core/zerr/err.go b/core/zerr/err.go index c1b4d7a..e4ede52 100644 --- a/core/zerr/err.go +++ b/core/zerr/err.go @@ -11,6 +11,7 @@ import ( "connectrpc.com/connect" "github.com/pborman/indent" + "gitlab.bareksa.com/backend/zen/core/zcaller" "gitlab.bareksa.com/backend/zen/core/zoptions" ) @@ -91,6 +92,9 @@ func (mu *Err) Error() string { // LogValue returns log fields to be consumed by logger. func (mu *Err) LogValue() slog.Value { + if len(mu.errs) == 0 { + return slog.AnyValue(nil) + } attrs := make([]slog.Attr, 0, 8) if len(mu.message) > 0 { attrs = append(attrs, slog.String("message", mu.message)) @@ -98,13 +102,46 @@ func (mu *Err) LogValue() slog.Value { if len(mu.publicMessage) > 0 { attrs = append(attrs, slog.String("public", mu.message)) } + if len(mu.id) > 0 { + attrs = append(attrs, slog.String("id", mu.id)) + } if mu.code != connect.CodeUnknown && mu.code != connect.CodeInternal { attrs = append(attrs, slog.String("code", mu.code.String())) } - if mu.sequence.IsRoot() { + if mu.caller != 0 { + attrs = append(attrs, slog.Attr{Key: "caller", Value: zcaller.Caller(mu.caller).LogValue()}) + } + attrs = append(attrs, slog.Attr{Key: "error", Value: mu.RootErrorLog()}) + return slog.GroupValue(attrs...) +} + +func (mu *Err) LogRecord() slog.Record { + record := slog.NewRecord(mu.GetTime(), slog.LevelError, mu.GetMessage(), mu.GetCaller()) + code := mu.GetCode() + if public := mu.GetPublicMessage(); public != "" { + record.AddAttrs(slog.String("public", public)) + } + if code != 0 && code != connect.CodeUnknown && code != connect.CodeInternal { + record.AddAttrs(slog.String("code", code.String())) + } + if id := mu.GetID(); id != "" { + record.AddAttrs(slog.String("id", id)) + } + if len(mu.GetDetails()) > 0 { + record.AddAttrs(slog.Group("details", mu.GetDetails()...)) + } + record.AddAttrs(slog.Attr{Key: "error", Value: mu.RootErrorLog()}) + return record +} + +func (mu *Err) RootErrorLog() slog.Value { + if len(mu.errs) == 0 { + return slog.AnyValue(nil) } if len(mu.errs) == 1 { + return errorValuer{mu.errs[0]}.LogValue() } + attrs := make([]slog.Attr, 0, len(mu.errs)) i := 0 for _, err := range mu.errs { if err == nil { diff --git a/core/zerr/error.go b/core/zerr/error.go index 08802de..2935954 100644 --- a/core/zerr/error.go +++ b/core/zerr/error.go @@ -13,6 +13,8 @@ type Error interface { error // LogValue returns log fields to be consumed by logger. LogValue() slog.Value + // LogRecord returns log records to be consumed by logger. + LogRecord() slog.Record // Code sets error code. Code(code connect.Code) Error diff --git a/core/zlog/caller.go b/core/zlog/caller.go index f2851c5..d3e6811 100644 --- a/core/zlog/caller.go +++ b/core/zlog/caller.go @@ -1,53 +1 @@ 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] -} diff --git a/core/zlog/error.go b/core/zlog/error.go new file mode 100644 index 0000000..923381c --- /dev/null +++ b/core/zlog/error.go @@ -0,0 +1,11 @@ +package zlog + +import ( + "context" + + "gitlab.bareksa.com/backend/zen/core/zerr" +) + +func (zl *ZLog) LogError(ctx context.Context, err zerr.Error) { + LogRecord(ctx, err.LogRecord()) +} diff --git a/core/zlog/zlog.go b/core/zlog/zlog.go index 83f1628..f249326 100644 --- a/core/zlog/zlog.go +++ b/core/zlog/zlog.go @@ -12,6 +12,7 @@ import ( "github.com/fatih/color" "github.com/mattn/go-isatty" "github.com/tidwall/pretty" + "gitlab.bareksa.com/backend/zen/core/zcaller" "gitlab.bareksa.com/backend/zen/internal/bufferpool" ) @@ -122,7 +123,7 @@ func (lo *ZLog) Handle(ctx context.Context, record slog.Record) error { defer buf.Close() if record.PC != 0 && lo.opts.HandlerOptions.AddSource { - f := frame{Caller(record.PC).Frame()} + f := zcaller.Caller(record.PC).Frame() levelColor.Fprint(buf, f.ShortFile()) levelColor.Fprint(buf, ":") levelColor.Fprint(buf, f.Line) diff --git a/core/ztelemetry/service.go b/core/ztelemetry/service.go new file mode 100644 index 0000000..c209ef1 --- /dev/null +++ b/core/ztelemetry/service.go @@ -0,0 +1,3 @@ +package ztelemetry + +type ServiceMetadata struct{}