Compare commits

...

4 Commits
v3.10.97 ... v3

Author SHA1 Message Date
7cd7fb0c0a disable logging for automaxprocs
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-11-20 22:35:36 +03:00
77eb5b5264 add yaml support
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-11-01 11:23:29 +03:00
929e46c087 improve slog
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-11-01 00:56:40 +03:00
1fb5673d27 fixup graceful stop
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2024-10-25 17:21:54 +03:00
6 changed files with 115 additions and 29 deletions

View File

@ -192,27 +192,34 @@ func (s *slogLogger) String() string {
return "slog" return "slog"
} }
func (s *slogLogger) printLog(ctx context.Context, lvl logger.Level, msg string, attrs ...interface{}) { func (s *slogLogger) printLog(ctx context.Context, lvl logger.Level, msg string, args ...interface{}) {
if !s.V(lvl) { if !s.V(lvl) {
return return
} }
var argError error
s.opts.Meter.Counter(semconv.LoggerMessageTotal, "level", lvl.String()).Inc() s.opts.Meter.Counter(semconv.LoggerMessageTotal, "level", lvl.String()).Inc()
attrs = prepareAttributes(attrs) attrs, err := s.argsAttrs(args)
if err != nil {
for _, fn := range s.opts.ContextAttrFuncs { argError = err
a := prepareAttributes(fn(ctx)) }
attrs = append(attrs, a...) if argError != nil {
if span, ok := tracer.SpanFromContext(ctx); ok {
span.SetStatus(tracer.SpanStatusError, argError.Error())
}
} }
for _, attr := range attrs { for _, fn := range s.opts.ContextAttrFuncs {
if ve, hasErr := attr.(error); hasErr && ve != nil { ctxAttrs, err := s.argsAttrs(fn(ctx))
attrs = append(attrs, slog.String(s.opts.ErrorKey, ve.Error())) if err != nil {
if span, ok := tracer.SpanFromContext(ctx); ok { argError = err
span.SetStatus(tracer.SpanStatusError, ve.Error()) }
} attrs = append(attrs, ctxAttrs...)
break }
if argError != nil {
if span, ok := tracer.SpanFromContext(ctx); ok {
span.SetStatus(tracer.SpanStatusError, argError.Error())
} }
} }
@ -229,7 +236,7 @@ func (s *slogLogger) printLog(ctx context.Context, lvl logger.Level, msg string,
var pcs [1]uintptr var pcs [1]uintptr
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, printLog, LogLvlMethod] runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, printLog, LogLvlMethod]
r := slog.NewRecord(s.opts.TimeFunc(), loggerToSlogLevel(lvl), msg, pcs[0]) r := slog.NewRecord(s.opts.TimeFunc(), loggerToSlogLevel(lvl), msg, pcs[0])
r.Add(attrs...) r.AddAttrs(attrs...)
_ = s.handler.Handle(ctx, r) _ = s.handler.Handle(ctx, r)
} }
@ -276,11 +283,26 @@ func slogToLoggerLevel(level slog.Level) logger.Level {
} }
} }
func prepareAttributes(attrs []interface{}) []interface{} { func (s *slogLogger) argsAttrs(args []interface{}) ([]slog.Attr, error) {
if len(attrs)%2 == 1 { attrs := make([]slog.Attr, 0, len(args))
attrs = append(attrs, badKey) var err error
attrs[len(attrs)-1], attrs[len(attrs)-2] = attrs[len(attrs)-2], attrs[len(attrs)-1]
for idx := 0; idx < len(args); idx++ {
switch arg := args[idx].(type) {
case slog.Attr:
attrs = append(attrs, arg)
case string:
if idx+1 < len(args) {
attrs = append(attrs, slog.Any(arg, args[idx+1]))
idx += 1
} else {
attrs = append(attrs, slog.String(badKey, arg))
}
case error:
attrs = append(attrs, slog.String(s.opts.ErrorKey, arg.Error()))
err = arg
}
} }
return attrs return attrs, err
} }

View File

@ -5,12 +5,13 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"github.com/google/uuid"
"go.unistack.org/micro/v3/metadata"
"log" "log"
"strings" "strings"
"testing" "testing"
"github.com/google/uuid"
"go.unistack.org/micro/v3/metadata"
"go.unistack.org/micro/v3/logger" "go.unistack.org/micro/v3/logger"
) )
@ -43,9 +44,6 @@ func TestErrorf(t *testing.T) {
} }
l.Log(ctx, logger.ErrorLevel, "message", errors.New("error msg")) l.Log(ctx, logger.ErrorLevel, "message", errors.New("error msg"))
if !bytes.Contains(buf.Bytes(), []byte(`"!BADKEY":"`)) {
t.Fatalf("logger BADKEY not works, buf contains: %s", buf.Bytes())
}
l.Log(ctx, logger.ErrorLevel, "", errors.New("error msg")) l.Log(ctx, logger.ErrorLevel, "", errors.New("error msg"))
if !bytes.Contains(buf.Bytes(), []byte(`"error":"error msg"`)) { if !bytes.Contains(buf.Bytes(), []byte(`"error":"error msg"`)) {
@ -236,5 +234,4 @@ 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())
} }
} }

View File

@ -6,7 +6,7 @@ import (
"sync" "sync"
"github.com/KimMachineGun/automemlimit/memlimit" "github.com/KimMachineGun/automemlimit/memlimit"
_ "go.uber.org/automaxprocs" "go.uber.org/automaxprocs/maxprocs"
"go.unistack.org/micro/v3/broker" "go.unistack.org/micro/v3/broker"
"go.unistack.org/micro/v3/client" "go.unistack.org/micro/v3/client"
"go.unistack.org/micro/v3/config" "go.unistack.org/micro/v3/config"
@ -20,6 +20,7 @@ import (
) )
func init() { func init() {
maxprocs.Set()
memlimit.SetGoMemLimitWithOpts( memlimit.SetGoMemLimitWithOpts(
memlimit.WithRatio(0.9), memlimit.WithRatio(0.9),
memlimit.WithProvider( memlimit.WithProvider(
@ -86,13 +87,14 @@ func RegisterSubscriber(topic string, s server.Server, h interface{}, opts ...se
} }
type service struct { type service struct {
done chan struct{}
opts Options opts Options
sync.RWMutex sync.RWMutex
} }
// NewService creates and returns a new Service based on the packages within. // NewService creates and returns a new Service based on the packages within.
func NewService(opts ...Option) Service { func NewService(opts ...Option) Service {
return &service{opts: NewOptions(opts...)} return &service{opts: NewOptions(opts...), done: make(chan struct{})}
} }
func (s *service) Name() string { func (s *service) Name() string {
@ -362,6 +364,8 @@ func (s *service) Stop() error {
} }
} }
close(s.done)
return nil return nil
} }
@ -385,7 +389,7 @@ func (s *service) Run() error {
} }
// wait on context cancel // wait on context cancel
<-s.opts.Context.Done() <-s.done
return s.Stop() return s.Stop()
} }

View File

@ -134,7 +134,7 @@ func TestNewService(t *testing.T) {
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
if got := NewService(tt.args.opts...); !reflect.DeepEqual(got, tt.want) { if got := NewService(tt.args.opts...); got.Name() != tt.want.Name() {
t.Errorf("NewService() = %v, want %v", got.Options().Name, tt.want.Options().Name) t.Errorf("NewService() = %v, want %v", got.Options().Name, tt.want.Options().Name)
} }
}) })

View File

@ -6,6 +6,8 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"time" "time"
"gopkg.in/yaml.v3"
) )
type Duration int64 type Duration int64
@ -53,6 +55,31 @@ loop:
return time.ParseDuration(fmt.Sprintf("%dh%s", hours, s[p:])) return time.ParseDuration(fmt.Sprintf("%dh%s", hours, s[p:]))
} }
func (d Duration) MarshalYAML() (interface{}, error) {
return time.Duration(d).String(), nil
}
func (d *Duration) UnmarshalYAML(n *yaml.Node) error {
var v interface{}
if err := yaml.Unmarshal([]byte(n.Value), &v); err != nil {
return err
}
switch value := v.(type) {
case float64:
*d = Duration(time.Duration(value))
return nil
case string:
dv, err := ParseDuration(value)
if err != nil {
return err
}
*d = Duration(dv)
return nil
default:
return fmt.Errorf("invalid duration")
}
}
func (d Duration) MarshalJSON() ([]byte, error) { func (d Duration) MarshalJSON() ([]byte, error) {
return json.Marshal(time.Duration(d).String()) return json.Marshal(time.Duration(d).String())
} }

View File

@ -5,8 +5,44 @@ import (
"encoding/json" "encoding/json"
"testing" "testing"
"time" "time"
"gopkg.in/yaml.v3"
) )
func TestMarshalYAML(t *testing.T) {
d := Duration(10000000)
buf, err := yaml.Marshal(d)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(buf, []byte(`10ms
`)) {
t.Fatalf("invalid duration: %s != %s", buf, `10ms`)
}
}
func TestUnmarshalYAML(t *testing.T) {
type str struct {
TTL Duration `yaml:"ttl"`
}
v := &str{}
var err error
err = yaml.Unmarshal([]byte(`{"ttl":"10ms"}`), v)
if err != nil {
t.Fatal(err)
} else if v.TTL != 10000000 {
t.Fatalf("invalid duration %v != 10000000", v.TTL)
}
err = yaml.Unmarshal([]byte(`{"ttl":"1y"}`), v)
if err != nil {
t.Fatal(err)
} else if v.TTL != 31622400000000000 {
t.Fatalf("invalid duration %v != 31622400000000000", v.TTL)
}
}
func TestMarshalJSON(t *testing.T) { func TestMarshalJSON(t *testing.T) {
d := Duration(10000000) d := Duration(10000000)
buf, err := json.Marshal(d) buf, err := json.Marshal(d)