seek-tune/utils/logger.go
2024-05-15 04:59:03 +01:00

98 lines
1.8 KiB
Go

package utils
import (
"context"
"log/slog"
"os"
"path/filepath"
"github.com/mdobak/go-xerrors"
)
type stackFrame struct {
Func string `json:"func"`
Source string `json:"source"`
Line int `json:"line"`
}
func replaceAttr(_ []string, a slog.Attr) slog.Attr {
switch a.Value.Kind() {
case slog.KindAny:
switch v := a.Value.Any().(type) {
case error:
a.Value = fmtErr(v)
}
}
return a
}
// marshalStack extracts stack frames from the error
func marshalStack(err error) []stackFrame {
trace := xerrors.StackTrace(err)
if len(trace) == 0 {
return nil
}
frames := trace.Frames()
s := make([]stackFrame, len(frames))
for i, v := range frames {
f := stackFrame{
Source: filepath.Join(
filepath.Base(filepath.Dir(v.File)),
filepath.Base(v.File),
),
Func: filepath.Base(v.Function),
Line: v.Line,
}
s[i] = f
}
return s
}
// fmtErr returns a slog.Value with keys `msg` and `trace`. If the error
// does not implement interface { StackTrace() errors.StackTrace }, the `trace`
// key is omitted.
func fmtErr(err error) slog.Value {
var groupValues []slog.Attr
groupValues = append(groupValues, slog.String("msg", err.Error()))
frames := marshalStack(err)
if frames != nil {
groupValues = append(groupValues,
slog.Any("trace", frames),
)
}
return slog.GroupValue(groupValues...)
}
func GetLogger() *slog.Logger {
h := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
ReplaceAttr: replaceAttr,
})
logger := slog.New(h)
return logger
}
func main() {
h := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
ReplaceAttr: replaceAttr,
})
logger := slog.New(h)
ctx := context.Background()
err := xerrors.New("something happened")
logger.ErrorContext(ctx, "image uploaded", slog.Any("error", err))
}