add using lazy connect #361
| @@ -8,6 +8,7 @@ import ( | |||||||
| 	"runtime" | 	"runtime" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"sync" | 	"sync" | ||||||
|  | 	"sync/atomic" | ||||||
|  |  | ||||||
| 	"go.unistack.org/micro/v3/logger" | 	"go.unistack.org/micro/v3/logger" | ||||||
| 	"go.unistack.org/micro/v3/semconv" | 	"go.unistack.org/micro/v3/semconv" | ||||||
| @@ -31,6 +32,27 @@ var ( | |||||||
| 	fatalValue = slog.StringValue("fatal") | 	fatalValue = slog.StringValue("fatal") | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | type wrapper struct { | ||||||
|  | 	h     slog.Handler | ||||||
|  | 	level atomic.Int64 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (h *wrapper) Enabled(ctx context.Context, level slog.Level) bool { | ||||||
|  | 	return level >= slog.Level(int(h.level.Load())) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (h *wrapper) Handle(ctx context.Context, rec slog.Record) error { | ||||||
|  | 	return h.h.Handle(ctx, rec) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (h *wrapper) WithAttrs(attrs []slog.Attr) slog.Handler { | ||||||
|  | 	return h.WithAttrs(attrs) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (h *wrapper) WithGroup(name string) slog.Handler { | ||||||
|  | 	return h.WithGroup(name) | ||||||
|  | } | ||||||
|  |  | ||||||
| func (s *slogLogger) renameAttr(_ []string, a slog.Attr) slog.Attr { | func (s *slogLogger) renameAttr(_ []string, a slog.Attr) slog.Attr { | ||||||
| 	switch a.Key { | 	switch a.Key { | ||||||
| 	case slog.SourceKey: | 	case slog.SourceKey: | ||||||
| @@ -68,7 +90,7 @@ func (s *slogLogger) renameAttr(_ []string, a slog.Attr) slog.Attr { | |||||||
|  |  | ||||||
| type slogLogger struct { | type slogLogger struct { | ||||||
| 	leveler *slog.LevelVar | 	leveler *slog.LevelVar | ||||||
| 	handler slog.Handler | 	handler *wrapper | ||||||
| 	opts    logger.Options | 	opts    logger.Options | ||||||
| 	mu      sync.RWMutex | 	mu      sync.RWMutex | ||||||
| } | } | ||||||
| @@ -82,51 +104,52 @@ func (s *slogLogger) Clone(opts ...logger.Option) logger.Logger { | |||||||
| 		o(&options) | 		o(&options) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	l := &slogLogger{ | 	if len(options.ContextAttrFuncs) == 0 { | ||||||
| 		opts: options, | 		options.ContextAttrFuncs = logger.DefaultContextAttrFuncs | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	l.leveler = new(slog.LevelVar) | 	attrs, _ := s.argsAttrs(options.Fields) | ||||||
| 	handleOpt := &slog.HandlerOptions{ | 	l := &slogLogger{ | ||||||
| 		ReplaceAttr: l.renameAttr, | 		handler: &wrapper{h: s.handler.h.WithAttrs(attrs)}, | ||||||
| 		Level:       l.leveler, | 		opts:    options, | ||||||
| 		AddSource:   l.opts.AddSource, |  | ||||||
| 	} | 	} | ||||||
| 	l.leveler.Set(loggerToSlogLevel(l.opts.Level)) | 	l.handler.level.Store(int64(loggerToSlogLevel(options.Level))) | ||||||
| 	l.handler = slog.New(slog.NewJSONHandler(options.Out, handleOpt)).With(options.Fields...).Handler() |  | ||||||
|  |  | ||||||
| 	return l | 	return l | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *slogLogger) V(level logger.Level) bool { | func (s *slogLogger) V(level logger.Level) bool { | ||||||
| 	return s.opts.Level.Enabled(level) | 	s.mu.Lock() | ||||||
|  | 	v := s.opts.Level.Enabled(level) | ||||||
|  | 	s.mu.Unlock() | ||||||
|  | 	return v | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *slogLogger) Level(level logger.Level) { | func (s *slogLogger) Level(level logger.Level) { | ||||||
| 	s.leveler.Set(loggerToSlogLevel(level)) | 	s.mu.Lock() | ||||||
|  | 	s.opts.Level = level | ||||||
|  | 	s.handler.level.Store(int64(loggerToSlogLevel(level))) | ||||||
|  | 	s.mu.Unlock() | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *slogLogger) Options() logger.Options { | func (s *slogLogger) Options() logger.Options { | ||||||
| 	return s.opts | 	return s.opts | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *slogLogger) Fields(attrs ...interface{}) logger.Logger { | func (s *slogLogger) Fields(fields ...interface{}) logger.Logger { | ||||||
| 	s.mu.RLock() | 	s.mu.RLock() | ||||||
| 	level := s.leveler.Level() |  | ||||||
| 	options := s.opts | 	options := s.opts | ||||||
| 	s.mu.RUnlock() | 	s.mu.RUnlock() | ||||||
|  |  | ||||||
| 	l := &slogLogger{opts: options} | 	l := &slogLogger{opts: options} | ||||||
| 	l.leveler = new(slog.LevelVar) |  | ||||||
| 	l.leveler.Set(level) |  | ||||||
|  |  | ||||||
| 	handleOpt := &slog.HandlerOptions{ | 	if len(options.ContextAttrFuncs) == 0 { | ||||||
| 		ReplaceAttr: l.renameAttr, | 		options.ContextAttrFuncs = logger.DefaultContextAttrFuncs | ||||||
| 		Level:       l.leveler, |  | ||||||
| 		AddSource:   l.opts.AddSource, |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	l.handler = slog.New(slog.NewJSONHandler(l.opts.Out, handleOpt)).With(attrs...).Handler() | 	attrs, _ := s.argsAttrs(fields) | ||||||
|  | 	l.handler = &wrapper{h: s.handler.h.WithAttrs(attrs)} | ||||||
|  | 	l.handler.level.Store(int64(loggerToSlogLevel(l.opts.Level))) | ||||||
|  |  | ||||||
| 	return l | 	return l | ||||||
| } | } | ||||||
| @@ -134,22 +157,23 @@ func (s *slogLogger) Fields(attrs ...interface{}) logger.Logger { | |||||||
| func (s *slogLogger) Init(opts ...logger.Option) error { | func (s *slogLogger) Init(opts ...logger.Option) error { | ||||||
| 	s.mu.Lock() | 	s.mu.Lock() | ||||||
|  |  | ||||||
| 	if len(s.opts.ContextAttrFuncs) == 0 { |  | ||||||
| 		s.opts.ContextAttrFuncs = logger.DefaultContextAttrFuncs |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for _, o := range opts { | 	for _, o := range opts { | ||||||
| 		o(&s.opts) | 		o(&s.opts) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	s.leveler = new(slog.LevelVar) | 	if len(s.opts.ContextAttrFuncs) == 0 { | ||||||
|  | 		s.opts.ContextAttrFuncs = logger.DefaultContextAttrFuncs | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	handleOpt := &slog.HandlerOptions{ | 	handleOpt := &slog.HandlerOptions{ | ||||||
| 		ReplaceAttr: s.renameAttr, | 		ReplaceAttr: s.renameAttr, | ||||||
| 		Level:       s.leveler, | 		Level:       loggerToSlogLevel(logger.TraceLevel), | ||||||
| 		AddSource:   s.opts.AddSource, | 		AddSource:   s.opts.AddSource, | ||||||
| 	} | 	} | ||||||
| 	s.leveler.Set(loggerToSlogLevel(s.opts.Level)) |  | ||||||
| 	s.handler = slog.New(slog.NewJSONHandler(s.opts.Out, handleOpt)).With(s.opts.Fields...).Handler() | 	attrs, _ := s.argsAttrs(s.opts.Fields) | ||||||
|  | 	s.handler = &wrapper{h: slog.NewJSONHandler(s.opts.Out, handleOpt).WithAttrs(attrs)} | ||||||
|  | 	s.handler.level.Store(int64(loggerToSlogLevel(s.opts.Level))) | ||||||
| 	s.mu.Unlock() | 	s.mu.Unlock() | ||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
|   | |||||||
| @@ -15,6 +15,56 @@ import ( | |||||||
| 	"go.unistack.org/micro/v3/logger" | 	"go.unistack.org/micro/v3/logger" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | func TestMultipleFieldsWithLevel(t *testing.T) { | ||||||
|  | 	ctx := context.TODO() | ||||||
|  | 	buf := bytes.NewBuffer(nil) | ||||||
|  | 	l := NewLogger(logger.WithLevel(logger.InfoLevel), logger.WithOutput(buf)) | ||||||
|  | 	if err := l.Init(); err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	l = l.Fields("key", "val") | ||||||
|  |  | ||||||
|  | 	l.Info(ctx, "msg1") | ||||||
|  | 	nl := l.Clone(logger.WithLevel(logger.DebugLevel)) | ||||||
|  | 	nl.Debug(ctx, "msg2") | ||||||
|  | 	l.Debug(ctx, "msg3") | ||||||
|  | 	if !bytes.Contains(buf.Bytes(), []byte(`"key":"val"`)) { | ||||||
|  | 		t.Fatalf("logger error not works, buf contains: %s", buf.Bytes()) | ||||||
|  | 	} | ||||||
|  | 	if !bytes.Contains(buf.Bytes(), []byte(`"msg1"`)) { | ||||||
|  | 		t.Fatalf("logger error not works, buf contains: %s", buf.Bytes()) | ||||||
|  | 	} | ||||||
|  | 	if !bytes.Contains(buf.Bytes(), []byte(`"msg2"`)) { | ||||||
|  | 		t.Fatalf("logger error not works, buf contains: %s", buf.Bytes()) | ||||||
|  | 	} | ||||||
|  | 	if bytes.Contains(buf.Bytes(), []byte(`"msg3"`)) { | ||||||
|  | 		t.Fatalf("logger error not works, buf contains: %s", buf.Bytes()) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestMultipleFields(t *testing.T) { | ||||||
|  | 	ctx := context.TODO() | ||||||
|  | 	buf := bytes.NewBuffer(nil) | ||||||
|  | 	l := NewLogger(logger.WithLevel(logger.InfoLevel), logger.WithOutput(buf)) | ||||||
|  | 	if err := l.Init(); err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	l = l.Fields("key", "val") | ||||||
|  |  | ||||||
|  | 	l = l.Fields("key1", "val1") | ||||||
|  |  | ||||||
|  | 	l.Info(ctx, "msg") | ||||||
|  |  | ||||||
|  | 	if !bytes.Contains(buf.Bytes(), []byte(`"key":"val"`)) { | ||||||
|  | 		t.Fatalf("logger error not works, buf contains: %s", buf.Bytes()) | ||||||
|  | 	} | ||||||
|  | 	if !bytes.Contains(buf.Bytes(), []byte(`"key1":"val1"`)) { | ||||||
|  | 		t.Fatalf("logger error not works, buf contains: %s", buf.Bytes()) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func TestError(t *testing.T) { | func TestError(t *testing.T) { | ||||||
| 	ctx := context.TODO() | 	ctx := context.TODO() | ||||||
| 	buf := bytes.NewBuffer(nil) | 	buf := bytes.NewBuffer(nil) | ||||||
| @@ -234,4 +284,11 @@ func Test_WithContextAttrFunc(t *testing.T) { | |||||||
| 	if !(bytes.Contains(buf.Bytes(), []byte(`"source-service":"Test-System"`))) { | 	if !(bytes.Contains(buf.Bytes(), []byte(`"source-service":"Test-System"`))) { | ||||||
| 		t.Fatalf("logger info, buf %s", buf.Bytes()) | 		t.Fatalf("logger info, buf %s", buf.Bytes()) | ||||||
| 	} | 	} | ||||||
|  | 	buf.Reset() | ||||||
|  | 	imd, _ := metadata.FromIncomingContext(ctx) | ||||||
|  | 	l.Info(ctx, "test message1") | ||||||
|  | 	imd.Set("Source-Service", "Test-System2") | ||||||
|  | 	l.Info(ctx, "test message2") | ||||||
|  |  | ||||||
|  | 	// t.Logf("xxx %s", buf.Bytes()) | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user