Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
		| @@ -48,8 +48,10 @@ type Logger interface { | ||||
| 	Fatal(ctx context.Context, msg string, attrs ...interface{}) | ||||
| 	// Log logs message with needed level | ||||
| 	Log(ctx context.Context, level Level, msg string, attrs ...interface{}) | ||||
| 	// String returns the name of logger | ||||
| 	// String returns the type name of logger | ||||
| 	String() string | ||||
| 	// String returns the name of logger | ||||
| 	Name() string | ||||
| } | ||||
|  | ||||
| // Info writes formatted msg to default logger on info level | ||||
|   | ||||
| @@ -35,6 +35,10 @@ type Options struct { | ||||
| 	MessageKey string | ||||
| 	// SourceKey is the key used for the source file and line of the log call | ||||
| 	SourceKey string | ||||
| 	// StacktraceKey is the key used for the stacktrace | ||||
| 	StacktraceKey string | ||||
| 	// Stacktrace controls writing of stacktaces on error | ||||
| 	Stacktrace bool | ||||
| } | ||||
|  | ||||
| // NewOptions creates new options struct | ||||
| @@ -116,6 +120,9 @@ func WithZapKeys() options.Option { | ||||
| 		if err = options.Set(src, "caller", ".SourceKey"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if err = options.Set(src, "stacktrace", ".StacktraceKey"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
| @@ -135,6 +142,9 @@ func WithZerologKeys() options.Option { | ||||
| 		if err = options.Set(src, "caller", ".SourceKey"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if err = options.Set(src, "stacktrace", ".StacktraceKey"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
| @@ -154,6 +164,9 @@ func WithSlogKeys() options.Option { | ||||
| 		if err = options.Set(src, slog.SourceKey, ".SourceKey"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if err = options.Set(src, "stacktrace", ".StacktraceKey"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
| @@ -173,6 +186,16 @@ func WithMicroKeys() options.Option { | ||||
| 		if err = options.Set(src, "caller", ".SourceKey"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if err = options.Set(src, "stacktrace", ".StacktraceKey"); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // WithStacktrace controls writing stacktrace on error | ||||
| func WithStacktrace(v bool) options.Option { | ||||
| 	return func(src interface{}) error { | ||||
| 		return options.Set(src, v, ".Stacktrace") | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -4,8 +4,10 @@ import ( | ||||
| 	"context" | ||||
| 	"log/slog" | ||||
| 	"os" | ||||
| 	"regexp" | ||||
| 	"runtime" | ||||
| 	"strconv" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"go.unistack.org/micro/v4/logger" | ||||
| @@ -13,6 +15,8 @@ import ( | ||||
| 	"go.unistack.org/micro/v4/tracer" | ||||
| ) | ||||
|  | ||||
| var reTrace = regexp.MustCompile(`.*/slog/logger\.go.*\n`) | ||||
|  | ||||
| var ( | ||||
| 	traceValue = slog.StringValue("trace") | ||||
| 	debugValue = slog.StringValue("debug") | ||||
| @@ -61,13 +65,15 @@ type slogLogger struct { | ||||
| 	slog    *slog.Logger | ||||
| 	leveler *slog.LevelVar | ||||
| 	opts    logger.Options | ||||
| 	mu      sync.RWMutex | ||||
| } | ||||
|  | ||||
| func (s *slogLogger) Clone(opts ...options.Option) logger.Logger { | ||||
| 	s.mu.RLock() | ||||
| 	options := s.opts | ||||
|  | ||||
| 	for _, o := range opts { | ||||
| 		o(&options) | ||||
| 		_ = o(&options) | ||||
| 	} | ||||
|  | ||||
| 	l := &slogLogger{ | ||||
| @@ -91,6 +97,8 @@ func (s *slogLogger) Clone(opts ...options.Option) logger.Logger { | ||||
| 	handler := slog.NewJSONHandler(options.Out, handleOpt) | ||||
| 	l.slog = slog.New(handler).With(options.Attrs...) | ||||
|  | ||||
| 	s.mu.RUnlock() | ||||
|  | ||||
| 	return l | ||||
| } | ||||
|  | ||||
| @@ -107,6 +115,7 @@ func (s *slogLogger) Options() logger.Options { | ||||
| } | ||||
|  | ||||
| func (s *slogLogger) Attrs(attrs ...interface{}) logger.Logger { | ||||
| 	s.mu.RLock() | ||||
| 	nl := &slogLogger{opts: s.opts} | ||||
| 	nl.leveler = new(slog.LevelVar) | ||||
| 	nl.leveler.Set(s.leveler.Level()) | ||||
| @@ -120,6 +129,8 @@ func (s *slogLogger) Attrs(attrs ...interface{}) logger.Logger { | ||||
| 	handler := slog.NewJSONHandler(s.opts.Out, handleOpt) | ||||
| 	nl.slog = slog.New(handler).With(attrs...) | ||||
|  | ||||
| 	s.mu.RUnlock() | ||||
|  | ||||
| 	return nl | ||||
| } | ||||
|  | ||||
| @@ -219,6 +230,15 @@ func (s *slogLogger) Error(ctx context.Context, msg string, attrs ...interface{} | ||||
| 	for _, fn := range s.opts.ContextAttrFuncs { | ||||
| 		attrs = append(attrs, fn(ctx)...) | ||||
| 	} | ||||
| 	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 { | ||||
| 				attrs = append(attrs, slog.String("stacktrace", traceLines[len(traceLines)-1])) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	r.Add(attrs...) | ||||
| 	r.Attrs(func(a slog.Attr) bool { | ||||
| 		if a.Key == "error" { | ||||
|   | ||||
| @@ -9,6 +9,19 @@ import ( | ||||
| 	"go.unistack.org/micro/v4/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) { | ||||
| 	ctx := context.TODO() | ||||
| 	buf := bytes.NewBuffer(nil) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user