logger/slog: add stacktrace support
Some checks failed
pr / test (pull_request) Failing after 1m28s
lint / lint (pull_request) Successful in 10m40s

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
Василий Толстов 2024-03-04 22:54:11 +03:00
parent 897be419b4
commit 4c7e1607d4
2 changed files with 35 additions and 0 deletions

View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"log/slog" "log/slog"
"os" "os"
"regexp"
"runtime" "runtime"
"strconv" "strconv"
"sync" "sync"
@ -14,6 +15,8 @@ import (
"go.unistack.org/micro/v3/tracer" "go.unistack.org/micro/v3/tracer"
) )
var reTrace = regexp.MustCompile(`.*/slog/logger\.go.*\n`)
var ( var (
DefaultSourceKey string = slog.SourceKey DefaultSourceKey string = slog.SourceKey
DefaultTimeKey string = slog.TimeKey DefaultTimeKey string = slog.TimeKey
@ -290,6 +293,15 @@ func (s *slogLogger) Error(ctx context.Context, attrs ...interface{}) {
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
r := slog.NewRecord(time.Now(), slog.LevelError, fmt.Sprintf("%s", attrs[0]), pcs[0]) r := slog.NewRecord(time.Now(), slog.LevelError, fmt.Sprintf("%s", attrs[0]), pcs[0])
// r.Add(attrs[1:]...) // r.Add(attrs[1:]...)
if s.opts.Stacktrace {
stackInfo := make([]byte, 1024*1024)
if stackSize := runtime.Stack(stackInfo, false); stackSize > 0 {
traceLines := reTrace.Split(string(stackInfo[:stackSize]), -1)
if len(traceLines) != 0 {
r.AddAttrs(slog.String("stacktrace", traceLines[len(traceLines)-1]))
}
}
}
r.Attrs(func(a slog.Attr) bool { r.Attrs(func(a slog.Attr) bool {
if a.Key == "error" { if a.Key == "error" {
if span, ok := tracer.SpanFromContext(ctx); ok { if span, ok := tracer.SpanFromContext(ctx); ok {
@ -310,6 +322,15 @@ func (s *slogLogger) Errorf(ctx context.Context, msg string, attrs ...interface{
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof]
r := slog.NewRecord(time.Now(), slog.LevelError, fmt.Sprintf(msg, attrs...), pcs[0]) r := slog.NewRecord(time.Now(), slog.LevelError, fmt.Sprintf(msg, attrs...), pcs[0])
// r.Add(attrs...) // r.Add(attrs...)
if s.opts.Stacktrace {
stackInfo := make([]byte, 1024*1024)
if stackSize := runtime.Stack(stackInfo, false); stackSize > 0 {
traceLines := reTrace.Split(string(stackInfo[:stackSize]), -1)
if len(traceLines) != 0 {
r.AddAttrs(slog.String("stacktrace", traceLines[len(traceLines)-1]))
}
}
}
r.Attrs(func(a slog.Attr) bool { r.Attrs(func(a slog.Attr) bool {
if a.Key == "error" { if a.Key == "error" {
if span, ok := tracer.SpanFromContext(ctx); ok { if span, ok := tracer.SpanFromContext(ctx); ok {

View File

@ -9,6 +9,20 @@ import (
"go.unistack.org/micro/v3/logger" "go.unistack.org/micro/v3/logger"
) )
func TestError(t *testing.T) {
ctx := context.TODO()
buf := bytes.NewBuffer(nil)
l := NewLogger(logger.WithLevel(logger.ErrorLevel), logger.WithOutput(buf), logger.WithStacktrace(true))
if err := l.Init(); err != nil {
t.Fatal(err)
}
l.Error(ctx, "message")
if !bytes.Contains(buf.Bytes(), []byte(`"stacktrace":"`)) {
t.Fatalf("logger stacktrace not works, buf contains: %s", buf.Bytes())
}
}
func TestContext(t *testing.T) { func TestContext(t *testing.T) {
ctx := context.TODO() ctx := context.TODO()
buf := bytes.NewBuffer(nil) buf := bytes.NewBuffer(nil)