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