Merge branch 'v3' into #335-v3
# Conflicts: # logger/options.go
This commit is contained in:
		| @@ -10,15 +10,15 @@ jobs: | |||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|     - name: setup-go |     - name: setup-go | ||||||
|       uses: https://gitea.com/actions/setup-go@v3 |       uses: actions/setup-go@v3 | ||||||
|       with: |       with: | ||||||
|         go-version: 1.18 |         go-version: 1.21 | ||||||
|     - name: checkout |     - name: checkout | ||||||
|       uses: https://gitea.com/actions/checkout@v3 |       uses: actions/checkout@v3 | ||||||
|     - name: deps |     - name: deps | ||||||
|       run: go get -v -d ./... |       run: go get -v -d ./... | ||||||
|     - name: lint |     - name: lint | ||||||
|       uses: https://github.com/golangci/golangci-lint-action@v3.4.0 |       uses: https://github.com/golangci/golangci-lint-action@v3.4.0 | ||||||
|       continue-on-error: true |       continue-on-error: true | ||||||
|       with: |       with: | ||||||
|         version: v1.52 |         version: v1.52 | ||||||
|   | |||||||
| @@ -10,14 +10,14 @@ jobs: | |||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|     - name: checkout |     - name: checkout | ||||||
|       uses: https://gitea.com/actions/checkout@v3 |       uses: actions/checkout@v3 | ||||||
|     - name: setup-go |     - name: setup-go | ||||||
|       uses: https://gitea.com/actions/setup-go@v3 |       uses: actions/setup-go@v3 | ||||||
|       with: |       with: | ||||||
|         go-version: 1.18 |         go-version: 1.21 | ||||||
|     - name: deps |     - name: deps | ||||||
|       run: go get -v -t -d ./... |       run: go get -v -t -d ./... | ||||||
|     - name: test |     - name: test | ||||||
|       env: |       env: | ||||||
|         INTEGRATION_TESTS: yes |         INTEGRATION_TESTS: yes | ||||||
|       run: go test -mod readonly -v ./... |       run: go test -mod readonly -v ./... | ||||||
|   | |||||||
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,8 @@ | |||||||
| # Develop tools | # Develop tools | ||||||
| /.vscode/ | /.vscode/ | ||||||
| /.idea/ | /.idea/ | ||||||
|  | .idea | ||||||
|  | .vscode | ||||||
|  |  | ||||||
| # Binaries for programs and plugins | # Binaries for programs and plugins | ||||||
| *.exe | *.exe | ||||||
| @@ -13,6 +15,7 @@ | |||||||
| _obj | _obj | ||||||
| _test | _test | ||||||
| _build | _build | ||||||
|  | .DS_Store | ||||||
|  |  | ||||||
| # Architecture specific extensions/prefixes | # Architecture specific extensions/prefixes | ||||||
| *.[568vq] | *.[568vq] | ||||||
|   | |||||||
| @@ -4,19 +4,22 @@ package broker // import "go.unistack.org/micro/v3/broker" | |||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"errors" | 	"errors" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
| 	"go.unistack.org/micro/v3/codec" | 	"go.unistack.org/micro/v3/codec" | ||||||
| 	"go.unistack.org/micro/v3/metadata" | 	"go.unistack.org/micro/v3/metadata" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // DefaultBroker default memory broker | // DefaultBroker default memory broker | ||||||
| var DefaultBroker = NewBroker() | var DefaultBroker Broker = NewBroker() | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	// ErrNotConnected returns when broker used but not connected yet | 	// ErrNotConnected returns when broker used but not connected yet | ||||||
| 	ErrNotConnected = errors.New("broker not connected") | 	ErrNotConnected = errors.New("broker not connected") | ||||||
| 	// ErrDisconnected returns when broker disconnected | 	// ErrDisconnected returns when broker disconnected | ||||||
| 	ErrDisconnected = errors.New("broker disconnected") | 	ErrDisconnected = errors.New("broker disconnected") | ||||||
|  | 	// DefaultGracefulTimeout | ||||||
|  | 	DefaultGracefulTimeout = 5 * time.Second | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // Broker is an interface used for asynchronous messaging. | // Broker is an interface used for asynchronous messaging. | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ import ( | |||||||
| 	"context" | 	"context" | ||||||
| 	"sync" | 	"sync" | ||||||
| 
 | 
 | ||||||
|  | 	"go.unistack.org/micro/v3/broker" | ||||||
| 	"go.unistack.org/micro/v3/logger" | 	"go.unistack.org/micro/v3/logger" | ||||||
| 	"go.unistack.org/micro/v3/metadata" | 	"go.unistack.org/micro/v3/metadata" | ||||||
| 	maddr "go.unistack.org/micro/v3/util/addr" | 	maddr "go.unistack.org/micro/v3/util/addr" | ||||||
| @@ -15,7 +16,7 @@ import ( | |||||||
| type memoryBroker struct { | type memoryBroker struct { | ||||||
| 	subscribers map[string][]*memorySubscriber | 	subscribers map[string][]*memorySubscriber | ||||||
| 	addr        string | 	addr        string | ||||||
| 	opts        Options | 	opts        broker.Options | ||||||
| 	sync.RWMutex | 	sync.RWMutex | ||||||
| 	connected bool | 	connected bool | ||||||
| } | } | ||||||
| @@ -24,20 +25,20 @@ type memoryEvent struct { | |||||||
| 	err     error | 	err     error | ||||||
| 	message interface{} | 	message interface{} | ||||||
| 	topic   string | 	topic   string | ||||||
| 	opts    Options | 	opts    broker.Options | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type memorySubscriber struct { | type memorySubscriber struct { | ||||||
| 	ctx          context.Context | 	ctx          context.Context | ||||||
| 	exit         chan bool | 	exit         chan bool | ||||||
| 	handler      Handler | 	handler      broker.Handler | ||||||
| 	batchhandler BatchHandler | 	batchhandler broker.BatchHandler | ||||||
| 	id           string | 	id           string | ||||||
| 	topic        string | 	topic        string | ||||||
| 	opts         SubscribeOptions | 	opts         broker.SubscribeOptions | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (m *memoryBroker) Options() Options { | func (m *memoryBroker) Options() broker.Options { | ||||||
| 	return m.opts | 	return m.opts | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @@ -46,6 +47,12 @@ func (m *memoryBroker) Address() string { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (m *memoryBroker) Connect(ctx context.Context) error { | func (m *memoryBroker) Connect(ctx context.Context) error { | ||||||
|  | 	select { | ||||||
|  | 	case <-ctx.Done(): | ||||||
|  | 		return ctx.Err() | ||||||
|  | 	default: | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	m.Lock() | 	m.Lock() | ||||||
| 	defer m.Unlock() | 	defer m.Unlock() | ||||||
| 
 | 
 | ||||||
| @@ -70,6 +77,12 @@ func (m *memoryBroker) Connect(ctx context.Context) error { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (m *memoryBroker) Disconnect(ctx context.Context) error { | func (m *memoryBroker) Disconnect(ctx context.Context) error { | ||||||
|  | 	select { | ||||||
|  | 	case <-ctx.Done(): | ||||||
|  | 		return ctx.Err() | ||||||
|  | 	default: | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	m.Lock() | 	m.Lock() | ||||||
| 	defer m.Unlock() | 	defer m.Unlock() | ||||||
| 
 | 
 | ||||||
| @@ -81,27 +94,27 @@ func (m *memoryBroker) Disconnect(ctx context.Context) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (m *memoryBroker) Init(opts ...Option) error { | func (m *memoryBroker) Init(opts ...broker.Option) error { | ||||||
| 	for _, o := range opts { | 	for _, o := range opts { | ||||||
| 		o(&m.opts) | 		o(&m.opts) | ||||||
| 	} | 	} | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (m *memoryBroker) Publish(ctx context.Context, topic string, msg *Message, opts ...PublishOption) error { | func (m *memoryBroker) Publish(ctx context.Context, topic string, msg *broker.Message, opts ...broker.PublishOption) error { | ||||||
| 	msg.Header.Set(metadata.HeaderTopic, topic) | 	msg.Header.Set(metadata.HeaderTopic, topic) | ||||||
| 	return m.publish(ctx, []*Message{msg}, opts...) | 	return m.publish(ctx, []*broker.Message{msg}, opts...) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (m *memoryBroker) BatchPublish(ctx context.Context, msgs []*Message, opts ...PublishOption) error { | func (m *memoryBroker) BatchPublish(ctx context.Context, msgs []*broker.Message, opts ...broker.PublishOption) error { | ||||||
| 	return m.publish(ctx, msgs, opts...) | 	return m.publish(ctx, msgs, opts...) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (m *memoryBroker) publish(ctx context.Context, msgs []*Message, opts ...PublishOption) error { | func (m *memoryBroker) publish(ctx context.Context, msgs []*broker.Message, opts ...broker.PublishOption) error { | ||||||
| 	m.RLock() | 	m.RLock() | ||||||
| 	if !m.connected { | 	if !m.connected { | ||||||
| 		m.RUnlock() | 		m.RUnlock() | ||||||
| 		return ErrNotConnected | 		return broker.ErrNotConnected | ||||||
| 	} | 	} | ||||||
| 	m.RUnlock() | 	m.RUnlock() | ||||||
| 
 | 
 | ||||||
| @@ -111,9 +124,9 @@ func (m *memoryBroker) publish(ctx context.Context, msgs []*Message, opts ...Pub | |||||||
| 	case <-ctx.Done(): | 	case <-ctx.Done(): | ||||||
| 		return ctx.Err() | 		return ctx.Err() | ||||||
| 	default: | 	default: | ||||||
| 		options := NewPublishOptions(opts...) | 		options := broker.NewPublishOptions(opts...) | ||||||
| 
 | 
 | ||||||
| 		msgTopicMap := make(map[string]Events) | 		msgTopicMap := make(map[string]broker.Events) | ||||||
| 		for _, v := range msgs { | 		for _, v := range msgs { | ||||||
| 			p := &memoryEvent{opts: m.opts} | 			p := &memoryEvent{opts: m.opts} | ||||||
| 
 | 
 | ||||||
| @@ -188,11 +201,11 @@ func (m *memoryBroker) publish(ctx context.Context, msgs []*Message, opts ...Pub | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (m *memoryBroker) BatchSubscribe(ctx context.Context, topic string, handler BatchHandler, opts ...SubscribeOption) (Subscriber, error) { | func (m *memoryBroker) BatchSubscribe(ctx context.Context, topic string, handler broker.BatchHandler, opts ...broker.SubscribeOption) (broker.Subscriber, error) { | ||||||
| 	m.RLock() | 	m.RLock() | ||||||
| 	if !m.connected { | 	if !m.connected { | ||||||
| 		m.RUnlock() | 		m.RUnlock() | ||||||
| 		return nil, ErrNotConnected | 		return nil, broker.ErrNotConnected | ||||||
| 	} | 	} | ||||||
| 	m.RUnlock() | 	m.RUnlock() | ||||||
| 
 | 
 | ||||||
| @@ -201,7 +214,7 @@ func (m *memoryBroker) BatchSubscribe(ctx context.Context, topic string, handler | |||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	options := NewSubscribeOptions(opts...) | 	options := broker.NewSubscribeOptions(opts...) | ||||||
| 
 | 
 | ||||||
| 	sub := &memorySubscriber{ | 	sub := &memorySubscriber{ | ||||||
| 		exit:         make(chan bool, 1), | 		exit:         make(chan bool, 1), | ||||||
| @@ -233,11 +246,11 @@ func (m *memoryBroker) BatchSubscribe(ctx context.Context, topic string, handler | |||||||
| 	return sub, nil | 	return sub, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (m *memoryBroker) Subscribe(ctx context.Context, topic string, handler Handler, opts ...SubscribeOption) (Subscriber, error) { | func (m *memoryBroker) Subscribe(ctx context.Context, topic string, handler broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) { | ||||||
| 	m.RLock() | 	m.RLock() | ||||||
| 	if !m.connected { | 	if !m.connected { | ||||||
| 		m.RUnlock() | 		m.RUnlock() | ||||||
| 		return nil, ErrNotConnected | 		return nil, broker.ErrNotConnected | ||||||
| 	} | 	} | ||||||
| 	m.RUnlock() | 	m.RUnlock() | ||||||
| 
 | 
 | ||||||
| @@ -246,7 +259,7 @@ func (m *memoryBroker) Subscribe(ctx context.Context, topic string, handler Hand | |||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	options := NewSubscribeOptions(opts...) | 	options := broker.NewSubscribeOptions(opts...) | ||||||
| 
 | 
 | ||||||
| 	sub := &memorySubscriber{ | 	sub := &memorySubscriber{ | ||||||
| 		exit:    make(chan bool, 1), | 		exit:    make(chan bool, 1), | ||||||
| @@ -290,12 +303,12 @@ func (m *memoryEvent) Topic() string { | |||||||
| 	return m.topic | 	return m.topic | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (m *memoryEvent) Message() *Message { | func (m *memoryEvent) Message() *broker.Message { | ||||||
| 	switch v := m.message.(type) { | 	switch v := m.message.(type) { | ||||||
| 	case *Message: | 	case *broker.Message: | ||||||
| 		return v | 		return v | ||||||
| 	case []byte: | 	case []byte: | ||||||
| 		msg := &Message{} | 		msg := &broker.Message{} | ||||||
| 		if err := m.opts.Codec.Unmarshal(v, msg); err != nil { | 		if err := m.opts.Codec.Unmarshal(v, msg); err != nil { | ||||||
| 			if m.opts.Logger.V(logger.ErrorLevel) { | 			if m.opts.Logger.V(logger.ErrorLevel) { | ||||||
| 				m.opts.Logger.Error(m.opts.Context, "[memory]: failed to unmarshal: %v", err) | 				m.opts.Logger.Error(m.opts.Context, "[memory]: failed to unmarshal: %v", err) | ||||||
| @@ -320,7 +333,7 @@ func (m *memoryEvent) SetError(err error) { | |||||||
| 	m.err = err | 	m.err = err | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (m *memorySubscriber) Options() SubscribeOptions { | func (m *memorySubscriber) Options() broker.SubscribeOptions { | ||||||
| 	return m.opts | 	return m.opts | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @@ -334,9 +347,9 @@ func (m *memorySubscriber) Unsubscribe(ctx context.Context) error { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // NewBroker return new memory broker | // NewBroker return new memory broker | ||||||
| func NewBroker(opts ...Option) Broker { | func NewBroker(opts ...broker.Option) broker.Broker { | ||||||
| 	return &memoryBroker{ | 	return &memoryBroker{ | ||||||
| 		opts:        NewOptions(opts...), | 		opts:        broker.NewOptions(opts...), | ||||||
| 		subscribers: make(map[string][]*memorySubscriber), | 		subscribers: make(map[string][]*memorySubscriber), | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -5,6 +5,7 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
|  | 	"go.unistack.org/micro/v3/broker" | ||||||
| 	"go.unistack.org/micro/v3/metadata" | 	"go.unistack.org/micro/v3/metadata" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| @@ -19,7 +20,7 @@ func TestMemoryBatchBroker(t *testing.T) { | |||||||
| 	topic := "test" | 	topic := "test" | ||||||
| 	count := 10 | 	count := 10 | ||||||
| 
 | 
 | ||||||
| 	fn := func(evts Events) error { | 	fn := func(evts broker.Events) error { | ||||||
| 		return evts.Ack() | 		return evts.Ack() | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @@ -28,9 +29,9 @@ func TestMemoryBatchBroker(t *testing.T) { | |||||||
| 		t.Fatalf("Unexpected error subscribing %v", err) | 		t.Fatalf("Unexpected error subscribing %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	msgs := make([]*Message, 0, count) | 	msgs := make([]*broker.Message, 0, count) | ||||||
| 	for i := 0; i < count; i++ { | 	for i := 0; i < count; i++ { | ||||||
| 		message := &Message{ | 		message := &broker.Message{ | ||||||
| 			Header: map[string]string{ | 			Header: map[string]string{ | ||||||
| 				metadata.HeaderTopic: topic, | 				metadata.HeaderTopic: topic, | ||||||
| 				"foo":                "bar", | 				"foo":                "bar", | ||||||
| @@ -65,7 +66,7 @@ func TestMemoryBroker(t *testing.T) { | |||||||
| 	topic := "test" | 	topic := "test" | ||||||
| 	count := 10 | 	count := 10 | ||||||
| 
 | 
 | ||||||
| 	fn := func(p Event) error { | 	fn := func(p broker.Event) error { | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @@ -74,9 +75,9 @@ func TestMemoryBroker(t *testing.T) { | |||||||
| 		t.Fatalf("Unexpected error subscribing %v", err) | 		t.Fatalf("Unexpected error subscribing %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	msgs := make([]*Message, 0, count) | 	msgs := make([]*broker.Message, 0, count) | ||||||
| 	for i := 0; i < count; i++ { | 	for i := 0; i < count; i++ { | ||||||
| 		message := &Message{ | 		message := &broker.Message{ | ||||||
| 			Header: map[string]string{ | 			Header: map[string]string{ | ||||||
| 				metadata.HeaderTopic: topic, | 				metadata.HeaderTopic: topic, | ||||||
| 				"foo":                "bar", | 				"foo":                "bar", | ||||||
							
								
								
									
										82
									
								
								broker/noop.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								broker/noop.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | |||||||
|  | package broker | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type NoopBroker struct { | ||||||
|  | 	opts Options | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewBroker(opts ...Option) *NoopBroker { | ||||||
|  | 	b := &NoopBroker{opts: NewOptions(opts...)} | ||||||
|  | 	return b | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *NoopBroker) Name() string { | ||||||
|  | 	return b.opts.Name | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *NoopBroker) String() string { | ||||||
|  | 	return "noop" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *NoopBroker) Options() Options { | ||||||
|  | 	return b.opts | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *NoopBroker) Init(opts ...Option) error { | ||||||
|  | 	for _, opt := range opts { | ||||||
|  | 		opt(&b.opts) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *NoopBroker) Connect(_ context.Context) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *NoopBroker) Disconnect(_ context.Context) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *NoopBroker) Address() string { | ||||||
|  | 	return strings.Join(b.opts.Addrs, ",") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *NoopBroker) BatchPublish(_ context.Context, _ []*Message, _ ...PublishOption) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *NoopBroker) Publish(_ context.Context, _ string, _ *Message, _ ...PublishOption) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type NoopSubscriber struct { | ||||||
|  | 	ctx          context.Context | ||||||
|  | 	topic        string | ||||||
|  | 	handler      Handler | ||||||
|  | 	batchHandler BatchHandler | ||||||
|  | 	opts         SubscribeOptions | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *NoopBroker) BatchSubscribe(ctx context.Context, topic string, handler BatchHandler, opts ...SubscribeOption) (Subscriber, error) { | ||||||
|  | 	return &NoopSubscriber{ctx: ctx, topic: topic, opts: NewSubscribeOptions(opts...), batchHandler: handler}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (b *NoopBroker) Subscribe(ctx context.Context, topic string, handler Handler, opts ...SubscribeOption) (Subscriber, error) { | ||||||
|  | 	return &NoopSubscriber{ctx: ctx, topic: topic, opts: NewSubscribeOptions(opts...), handler: handler}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *NoopSubscriber) Options() SubscribeOptions { | ||||||
|  | 	return s.opts | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *NoopSubscriber) Topic() string { | ||||||
|  | 	return s.topic | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *NoopSubscriber) Unsubscribe(ctx context.Context) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
| @@ -9,6 +9,7 @@ import ( | |||||||
| 	"go.unistack.org/micro/v3/logger" | 	"go.unistack.org/micro/v3/logger" | ||||||
| 	"go.unistack.org/micro/v3/meter" | 	"go.unistack.org/micro/v3/meter" | ||||||
| 	"go.unistack.org/micro/v3/register" | 	"go.unistack.org/micro/v3/register" | ||||||
|  | 	"go.unistack.org/micro/v3/sync" | ||||||
| 	"go.unistack.org/micro/v3/tracer" | 	"go.unistack.org/micro/v3/tracer" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -36,17 +37,22 @@ type Options struct { | |||||||
| 	Name string | 	Name string | ||||||
| 	// Addrs holds the broker address | 	// Addrs holds the broker address | ||||||
| 	Addrs []string | 	Addrs []string | ||||||
|  |  | ||||||
|  | 	Wait *sync.WaitGroup | ||||||
|  |  | ||||||
|  | 	GracefulTimeout time.Duration | ||||||
| } | } | ||||||
|  |  | ||||||
| // NewOptions create new Options | // NewOptions create new Options | ||||||
| func NewOptions(opts ...Option) Options { | func NewOptions(opts ...Option) Options { | ||||||
| 	options := Options{ | 	options := Options{ | ||||||
| 		Register: register.DefaultRegister, | 		Register:        register.DefaultRegister, | ||||||
| 		Logger:   logger.DefaultLogger, | 		Logger:          logger.DefaultLogger, | ||||||
| 		Context:  context.Background(), | 		Context:         context.Background(), | ||||||
| 		Meter:    meter.DefaultMeter, | 		Meter:           meter.DefaultMeter, | ||||||
| 		Codec:    codec.DefaultCodec, | 		Codec:           codec.DefaultCodec, | ||||||
| 		Tracer:   tracer.DefaultTracer, | 		Tracer:          tracer.DefaultTracer, | ||||||
|  | 		GracefulTimeout: DefaultGracefulTimeout, | ||||||
| 	} | 	} | ||||||
| 	for _, o := range opts { | 	for _, o := range opts { | ||||||
| 		o(&options) | 		o(&options) | ||||||
|   | |||||||
							
								
								
									
										41
									
								
								cluster/cluster.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								cluster/cluster.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | |||||||
|  | package cluster | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  |  | ||||||
|  | 	"go.unistack.org/micro/v3/metadata" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Message sent to member in cluster | ||||||
|  | type Message interface { | ||||||
|  | 	// Header returns message headers | ||||||
|  | 	Header() metadata.Metadata | ||||||
|  | 	// Body returns broker message may be []byte slice or some go struct or interface | ||||||
|  | 	Body() interface{} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type Node interface { | ||||||
|  | 	// Name returns node name | ||||||
|  | 	Name() string | ||||||
|  | 	// Address returns node address | ||||||
|  | 	Address() string | ||||||
|  | 	// Metadata returns node metadata | ||||||
|  | 	Metadata() metadata.Metadata | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Cluster interface used for cluster communication across nodes | ||||||
|  | type Cluster interface { | ||||||
|  | 	// Join is used to take an existing members and performing state sync | ||||||
|  | 	Join(ctx context.Context, addr ...string) error | ||||||
|  | 	// Leave broadcast a leave message and stop listeners | ||||||
|  | 	Leave(ctx context.Context) error | ||||||
|  | 	// Ping is used to probe live status of the node | ||||||
|  | 	Ping(ctx context.Context, node Node, payload []byte) error | ||||||
|  | 	// Members returns the cluster members | ||||||
|  | 	Members() ([]Node, error) | ||||||
|  | 	// Broadcast send message for all members in cluster, if filter is not nil, nodes may be filtered | ||||||
|  | 	// by key/value pairs | ||||||
|  | 	Broadcast(ctx context.Context, msg Message, filter ...string) error | ||||||
|  | 	// Unicast send message to single member in cluster | ||||||
|  | 	Unicast(ctx context.Context, node Node, msg Message) error | ||||||
|  | } | ||||||
| @@ -13,7 +13,7 @@ type Validator interface { | |||||||
| } | } | ||||||
|  |  | ||||||
| // DefaultConfig default config | // DefaultConfig default config | ||||||
| var DefaultConfig = NewConfig() | var DefaultConfig Config = NewConfig() | ||||||
|  |  | ||||||
| // DefaultWatcherMinInterval default min interval for poll changes | // DefaultWatcherMinInterval default min interval for poll changes | ||||||
| var DefaultWatcherMinInterval = 5 * time.Second | var DefaultWatcherMinInterval = 5 * time.Second | ||||||
|   | |||||||
| @@ -7,8 +7,8 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
|  | 	"dario.cat/mergo" | ||||||
| 	"github.com/google/uuid" | 	"github.com/google/uuid" | ||||||
| 	"github.com/imdario/mergo" |  | ||||||
| 	mid "go.unistack.org/micro/v3/util/id" | 	mid "go.unistack.org/micro/v3/util/id" | ||||||
| 	rutil "go.unistack.org/micro/v3/util/reflect" | 	rutil "go.unistack.org/micro/v3/util/reflect" | ||||||
| 	mtime "go.unistack.org/micro/v3/util/time" | 	mtime "go.unistack.org/micro/v3/util/time" | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.mod
									
									
									
									
									
								
							| @@ -1,11 +1,11 @@ | |||||||
| module go.unistack.org/micro/v3 | module go.unistack.org/micro/v3 | ||||||
|  |  | ||||||
| go 1.19 | go 1.20 | ||||||
|  |  | ||||||
| require ( | require ( | ||||||
|  | 	dario.cat/mergo v1.0.0 | ||||||
| 	github.com/DATA-DOG/go-sqlmock v1.5.0 | 	github.com/DATA-DOG/go-sqlmock v1.5.0 | ||||||
| 	github.com/google/uuid v1.3.0 | 	github.com/google/uuid v1.3.0 | ||||||
| 	github.com/imdario/mergo v0.3.15 |  | ||||||
| 	github.com/patrickmn/go-cache v2.1.0+incompatible | 	github.com/patrickmn/go-cache v2.1.0+incompatible | ||||||
| 	github.com/silas/dag v0.0.0-20220518035006-a7e85ada93c5 | 	github.com/silas/dag v0.0.0-20220518035006-a7e85ada93c5 | ||||||
| 	golang.org/x/sync v0.3.0 | 	golang.org/x/sync v0.3.0 | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.sum
									
									
									
									
									
								
							| @@ -1,3 +1,5 @@ | |||||||
|  | dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= | ||||||
|  | dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= | ||||||
| github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= | github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60= | ||||||
| github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= | github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= | ||||||
| github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= | ||||||
| @@ -7,8 +9,6 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ | |||||||
| github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= | ||||||
| github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= | ||||||
| github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||||
| github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= |  | ||||||
| github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= |  | ||||||
| github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= | github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= | ||||||
| github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= | github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= | ||||||
| github.com/silas/dag v0.0.0-20220518035006-a7e85ada93c5 h1:G/FZtUu7a6NTWl3KUHMV9jkLAh/Rvtf03NWMHaEDl+E= | github.com/silas/dag v0.0.0-20220518035006-a7e85ada93c5 h1:G/FZtUu7a6NTWl3KUHMV9jkLAh/Rvtf03NWMHaEDl+E= | ||||||
|   | |||||||
| @@ -3,12 +3,15 @@ package logger // import "go.unistack.org/micro/v3/logger" | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"os" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | type ContextAttrFunc func(ctx context.Context) []interface{} | ||||||
|  |  | ||||||
|  | var DefaultContextAttrFuncs []ContextAttrFunc | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	// DefaultLogger variable | 	// DefaultLogger variable | ||||||
| 	DefaultLogger = NewLogger(WithLevel(ParseLevel(os.Getenv("MICRO_LOG_LEVEL")))) | 	DefaultLogger Logger = NewLogger() | ||||||
| 	// DefaultLevel used by logger | 	// DefaultLevel used by logger | ||||||
| 	DefaultLevel = InfoLevel | 	DefaultLevel = InfoLevel | ||||||
| 	// DefaultCallerSkipCount used by logger | 	// DefaultCallerSkipCount used by logger | ||||||
| @@ -57,7 +60,9 @@ type Logger interface { | |||||||
| 	Log(ctx context.Context, level Level, args ...interface{}) | 	Log(ctx context.Context, level Level, args ...interface{}) | ||||||
| 	// Logf logs message with needed level | 	// Logf logs message with needed level | ||||||
| 	Logf(ctx context.Context, level Level, msg string, args ...interface{}) | 	Logf(ctx context.Context, level Level, msg string, args ...interface{}) | ||||||
| 	// String returns the name of logger | 	// Name returns broker instance name | ||||||
|  | 	Name() string | ||||||
|  | 	// String returns the type of logger | ||||||
| 	String() string | 	String() string | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -65,76 +70,106 @@ type Logger interface { | |||||||
| type Field interface{} | type Field interface{} | ||||||
|  |  | ||||||
| // Info writes msg to default logger on info level | // Info writes msg to default logger on info level | ||||||
|  | // | ||||||
|  | // Deprecated: Dont use logger methods directly, use instance of logger to avoid additional allocations | ||||||
| func Info(ctx context.Context, args ...interface{}) { | func Info(ctx context.Context, args ...interface{}) { | ||||||
| 	DefaultLogger.Info(ctx, args...) | 	DefaultLogger.Clone(WithCallerSkipCount(DefaultCallerSkipCount+1)).Info(ctx, args...) | ||||||
| } | } | ||||||
|  |  | ||||||
| // Error writes msg to default logger on error level | // Error writes msg to default logger on error level | ||||||
|  | // | ||||||
|  | // Deprecated: Dont use logger methods directly, use instance of logger to avoid additional allocations | ||||||
| func Error(ctx context.Context, args ...interface{}) { | func Error(ctx context.Context, args ...interface{}) { | ||||||
| 	DefaultLogger.Error(ctx, args...) | 	DefaultLogger.Clone(WithCallerSkipCount(DefaultCallerSkipCount+1)).Error(ctx, args...) | ||||||
| } | } | ||||||
|  |  | ||||||
| // Debug writes msg to default logger on debug level | // Debug writes msg to default logger on debug level | ||||||
|  | // | ||||||
|  | // Deprecated: Dont use logger methods directly, use instance of logger to avoid additional allocations | ||||||
| func Debug(ctx context.Context, args ...interface{}) { | func Debug(ctx context.Context, args ...interface{}) { | ||||||
| 	DefaultLogger.Debug(ctx, args...) | 	DefaultLogger.Clone(WithCallerSkipCount(DefaultCallerSkipCount+1)).Debug(ctx, args...) | ||||||
| } | } | ||||||
|  |  | ||||||
| // Warn writes msg to default logger on warn level | // Warn writes msg to default logger on warn level | ||||||
|  | // | ||||||
|  | // Deprecated: Dont use logger methods directly, use instance of logger to avoid additional allocations | ||||||
| func Warn(ctx context.Context, args ...interface{}) { | func Warn(ctx context.Context, args ...interface{}) { | ||||||
| 	DefaultLogger.Warn(ctx, args...) | 	DefaultLogger.Clone(WithCallerSkipCount(DefaultCallerSkipCount+1)).Warn(ctx, args...) | ||||||
| } | } | ||||||
|  |  | ||||||
| // Trace writes msg to default logger on trace level | // Trace writes msg to default logger on trace level | ||||||
|  | // | ||||||
|  | // Deprecated: Dont use logger methods directly, use instance of logger to avoid additional allocations | ||||||
| func Trace(ctx context.Context, args ...interface{}) { | func Trace(ctx context.Context, args ...interface{}) { | ||||||
| 	DefaultLogger.Trace(ctx, args...) | 	DefaultLogger.Clone(WithCallerSkipCount(DefaultCallerSkipCount+1)).Trace(ctx, args...) | ||||||
| } | } | ||||||
|  |  | ||||||
| // Fatal writes msg to default logger on fatal level | // Fatal writes msg to default logger on fatal level | ||||||
|  | // | ||||||
|  | // Deprecated: Dont use logger methods directly, use instance of logger to avoid additional allocations | ||||||
| func Fatal(ctx context.Context, args ...interface{}) { | func Fatal(ctx context.Context, args ...interface{}) { | ||||||
| 	DefaultLogger.Fatal(ctx, args...) | 	DefaultLogger.Clone(WithCallerSkipCount(DefaultCallerSkipCount+1)).Fatal(ctx, args...) | ||||||
| } | } | ||||||
|  |  | ||||||
| // Infof writes formatted msg to default logger on info level | // Infof writes formatted msg to default logger on info level | ||||||
|  | // | ||||||
|  | // Deprecated: Dont use logger methods directly, use instance of logger to avoid additional allocations | ||||||
| func Infof(ctx context.Context, msg string, args ...interface{}) { | func Infof(ctx context.Context, msg string, args ...interface{}) { | ||||||
| 	DefaultLogger.Infof(ctx, msg, args...) | 	DefaultLogger.Clone(WithCallerSkipCount(DefaultCallerSkipCount+1)).Infof(ctx, msg, args...) | ||||||
| } | } | ||||||
|  |  | ||||||
| // Errorf writes formatted msg to default logger on error level | // Errorf writes formatted msg to default logger on error level | ||||||
|  | // | ||||||
|  | // Deprecated: Dont use logger methods directly, use instance of logger to avoid additional allocations | ||||||
| func Errorf(ctx context.Context, msg string, args ...interface{}) { | func Errorf(ctx context.Context, msg string, args ...interface{}) { | ||||||
| 	DefaultLogger.Errorf(ctx, msg, args...) | 	DefaultLogger.Clone(WithCallerSkipCount(DefaultCallerSkipCount+1)).Errorf(ctx, msg, args...) | ||||||
| } | } | ||||||
|  |  | ||||||
| // Debugf writes formatted msg to default logger on debug level | // Debugf writes formatted msg to default logger on debug level | ||||||
|  | // | ||||||
|  | // Deprecated: Dont use logger methods directly, use instance of logger to avoid additional allocations | ||||||
| func Debugf(ctx context.Context, msg string, args ...interface{}) { | func Debugf(ctx context.Context, msg string, args ...interface{}) { | ||||||
| 	DefaultLogger.Debugf(ctx, msg, args...) | 	DefaultLogger.Clone(WithCallerSkipCount(DefaultCallerSkipCount+1)).Debugf(ctx, msg, args...) | ||||||
| } | } | ||||||
|  |  | ||||||
| // Warnf writes formatted msg to default logger on warn level | // Warnf writes formatted msg to default logger on warn level | ||||||
|  | // | ||||||
|  | // Deprecated: Dont use logger methods directly, use instance of logger to avoid additional allocations | ||||||
| func Warnf(ctx context.Context, msg string, args ...interface{}) { | func Warnf(ctx context.Context, msg string, args ...interface{}) { | ||||||
| 	DefaultLogger.Warnf(ctx, msg, args...) | 	DefaultLogger.Clone(WithCallerSkipCount(DefaultCallerSkipCount+1)).Warnf(ctx, msg, args...) | ||||||
| } | } | ||||||
|  |  | ||||||
| // Tracef writes formatted msg to default logger on trace level | // Tracef writes formatted msg to default logger on trace level | ||||||
|  | // | ||||||
|  | // Deprecated: Dont use logger methods directly, use instance of logger to avoid additional allocations | ||||||
| func Tracef(ctx context.Context, msg string, args ...interface{}) { | func Tracef(ctx context.Context, msg string, args ...interface{}) { | ||||||
| 	DefaultLogger.Tracef(ctx, msg, args...) | 	DefaultLogger.Clone(WithCallerSkipCount(DefaultCallerSkipCount+1)).Tracef(ctx, msg, args...) | ||||||
| } | } | ||||||
|  |  | ||||||
| // Fatalf writes formatted msg to default logger on fatal level | // Fatalf writes formatted msg to default logger on fatal level | ||||||
|  | // | ||||||
|  | // Deprecated: Dont use logger methods directly, use instance of logger to avoid additional allocations | ||||||
| func Fatalf(ctx context.Context, msg string, args ...interface{}) { | func Fatalf(ctx context.Context, msg string, args ...interface{}) { | ||||||
| 	DefaultLogger.Fatalf(ctx, msg, args...) | 	DefaultLogger.Clone(WithCallerSkipCount(DefaultCallerSkipCount+1)).Fatalf(ctx, msg, args...) | ||||||
| } | } | ||||||
|  |  | ||||||
| // V returns true if passed level enabled in default logger | // V returns true if passed level enabled in default logger | ||||||
|  | // | ||||||
|  | // Deprecated: Dont use logger methods directly, use instance of logger to avoid additional allocations | ||||||
| func V(level Level) bool { | func V(level Level) bool { | ||||||
| 	return DefaultLogger.V(level) | 	return DefaultLogger.V(level) | ||||||
| } | } | ||||||
|  |  | ||||||
| // Init initialize logger | // Init initialize logger | ||||||
|  | // | ||||||
|  | // Deprecated: Dont use logger methods directly, use instance of logger to avoid additional allocations | ||||||
| func Init(opts ...Option) error { | func Init(opts ...Option) error { | ||||||
| 	return DefaultLogger.Init(opts...) | 	return DefaultLogger.Init(opts...) | ||||||
| } | } | ||||||
|  |  | ||||||
| // Fields create logger with specific fields | // Fields create logger with specific fields | ||||||
|  | // | ||||||
|  | // Deprecated: Dont use logger methods directly, use instance of logger to avoid additional allocations | ||||||
| func Fields(fields ...interface{}) Logger { | func Fields(fields ...interface{}) Logger { | ||||||
| 	return DefaultLogger.Fields(fields...) | 	return DefaultLogger.Fields(fields...) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -13,11 +13,15 @@ func NewLogger(opts ...Option) Logger { | |||||||
| 	return &noopLogger{opts: options} | 	return &noopLogger{opts: options} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (l *noopLogger) V(lvl Level) bool { | func (l *noopLogger) V(_ Level) bool { | ||||||
| 	return false | 	return false | ||||||
| } | } | ||||||
|  |  | ||||||
| func (l *noopLogger) Level(lvl Level) { | func (l *noopLogger) Level(_ Level) { | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (l *noopLogger) Name() string { | ||||||
|  | 	return l.opts.Name | ||||||
| } | } | ||||||
|  |  | ||||||
| func (l *noopLogger) Init(opts ...Option) error { | func (l *noopLogger) Init(opts ...Option) error { | ||||||
| @@ -35,7 +39,7 @@ func (l *noopLogger) Clone(opts ...Option) Logger { | |||||||
| 	return nl | 	return nl | ||||||
| } | } | ||||||
|  |  | ||||||
| func (l *noopLogger) Fields(attrs ...interface{}) Logger { | func (l *noopLogger) Fields(_ ...interface{}) Logger { | ||||||
| 	return l | 	return l | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,10 +3,12 @@ package logger | |||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"io" | 	"io" | ||||||
|  | 	"log/slog" | ||||||
| 	"os" | 	"os" | ||||||
|  | 	"time" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // Option func | // Option func signature | ||||||
| type Option func(*Options) | type Option func(*Options) | ||||||
|  |  | ||||||
| // Options holds logger options | // Options holds logger options | ||||||
| @@ -15,31 +17,65 @@ type Options struct { | |||||||
| 	Out io.Writer | 	Out io.Writer | ||||||
| 	// Context holds exernal options | 	// Context holds exernal options | ||||||
| 	Context context.Context | 	Context context.Context | ||||||
| 	// Fields holds additional metadata |  | ||||||
| 	Fields []interface{} |  | ||||||
| 	// Name holds the logger name | 	// Name holds the logger name | ||||||
| 	Name string | 	Name string | ||||||
| 	// The logging level the logger should log | 	// Fields holds additional metadata | ||||||
| 	Level Level | 	Fields []interface{} | ||||||
| 	// CallerSkipCount number of frmaes to skip | 	// CallerSkipCount number of frmaes to skip | ||||||
| 	CallerSkipCount int | 	CallerSkipCount int | ||||||
|  | 	// ContextAttrFuncs contains funcs that executed before log func on context | ||||||
|  | 	ContextAttrFuncs []ContextAttrFunc | ||||||
|  | 	// TimeKey is the key used for the time of the log call | ||||||
|  | 	TimeKey string | ||||||
|  | 	// LevelKey is the key used for the level of the log call | ||||||
|  | 	LevelKey string | ||||||
|  | 	// ErroreKey is the key used for the error of the log call | ||||||
|  | 	ErrorKey string | ||||||
|  | 	// MessageKey is the key used for the message of the log call | ||||||
|  | 	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 | ||||||
|  | 	// AddStacktrace controls writing of stacktaces on error | ||||||
|  | 	AddStacktrace bool | ||||||
|  | 	// AddSource enabled writing source file and position in log | ||||||
|  | 	AddSource bool | ||||||
|  | 	// The logging level the logger should log | ||||||
|  | 	Level Level | ||||||
|  | 	// TimeFunc used to obtain current time | ||||||
|  | 	TimeFunc func() time.Time | ||||||
| } | } | ||||||
|  |  | ||||||
| // NewOptions creates new options struct | // NewOptions creates new options struct | ||||||
| func NewOptions(opts ...Option) Options { | func NewOptions(opts ...Option) Options { | ||||||
| 	options := Options{ | 	options := Options{ | ||||||
| 		Level:           DefaultLevel, | 		Level:            DefaultLevel, | ||||||
| 		Fields:          make([]interface{}, 0, 6), | 		Fields:           make([]interface{}, 0, 6), | ||||||
| 		Out:             os.Stderr, | 		Out:              os.Stderr, | ||||||
| 		CallerSkipCount: DefaultCallerSkipCount, | 		CallerSkipCount:  DefaultCallerSkipCount, | ||||||
| 		Context:         context.Background(), | 		Context:          context.Background(), | ||||||
|  | 		ContextAttrFuncs: DefaultContextAttrFuncs, | ||||||
|  | 		AddSource:        true, | ||||||
|  | 		TimeFunc:         time.Now, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	WithMicroKeys()(&options) | ||||||
|  |  | ||||||
| 	for _, o := range opts { | 	for _, o := range opts { | ||||||
| 		o(&options) | 		o(&options) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return options | 	return options | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // WithContextAttrFuncs appends default funcs for the context arrts filler | ||||||
|  | func WithContextAttrFuncs(fncs ...ContextAttrFunc) Option { | ||||||
|  | 	return func(o *Options) { | ||||||
|  | 		o.ContextAttrFuncs = append(o.ContextAttrFuncs, fncs...) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| // WithFields set default fields for the logger | // WithFields set default fields for the logger | ||||||
| func WithFields(fields ...interface{}) Option { | func WithFields(fields ...interface{}) Option { | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
| @@ -61,6 +97,20 @@ func WithOutput(out io.Writer) Option { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // WitAddStacktrace controls writing stacktrace on error | ||||||
|  | func WithAddStacktrace(v bool) Option { | ||||||
|  | 	return func(o *Options) { | ||||||
|  | 		o.AddStacktrace = v | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // WitAddSource controls writing source file and pos in log | ||||||
|  | func WithAddSource(v bool) Option { | ||||||
|  | 	return func(o *Options) { | ||||||
|  | 		o.AddSource = v | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| // WithCallerSkipCount set frame count to skip | // WithCallerSkipCount set frame count to skip | ||||||
| func WithCallerSkipCount(c int) Option { | func WithCallerSkipCount(c int) Option { | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
| @@ -82,6 +132,57 @@ func WithName(n string) Option { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // WithTimeFunc sets the func to obtain current time | ||||||
|  | func WithTimeFunc(fn func() time.Time) Option { | ||||||
|  | 	return func(o *Options) { | ||||||
|  | 		o.TimeFunc = fn | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func WithZapKeys() Option { | ||||||
|  | 	return func(o *Options) { | ||||||
|  | 		o.TimeKey = "@timestamp" | ||||||
|  | 		o.LevelKey = "level" | ||||||
|  | 		o.MessageKey = "msg" | ||||||
|  | 		o.SourceKey = "caller" | ||||||
|  | 		o.StacktraceKey = "stacktrace" | ||||||
|  | 		o.ErrorKey = "error" | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func WithZerologKeys() Option { | ||||||
|  | 	return func(o *Options) { | ||||||
|  | 		o.TimeKey = "time" | ||||||
|  | 		o.LevelKey = "level" | ||||||
|  | 		o.MessageKey = "message" | ||||||
|  | 		o.SourceKey = "caller" | ||||||
|  | 		o.StacktraceKey = "stacktrace" | ||||||
|  | 		o.ErrorKey = "error" | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func WithSlogKeys() Option { | ||||||
|  | 	return func(o *Options) { | ||||||
|  | 		o.TimeKey = slog.TimeKey | ||||||
|  | 		o.LevelKey = slog.LevelKey | ||||||
|  | 		o.MessageKey = slog.MessageKey | ||||||
|  | 		o.SourceKey = slog.SourceKey | ||||||
|  | 		o.StacktraceKey = "stacktrace" | ||||||
|  | 		o.ErrorKey = "error" | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func WithMicroKeys() Option { | ||||||
|  | 	return func(o *Options) { | ||||||
|  | 		o.TimeKey = "timestamp" | ||||||
|  | 		o.LevelKey = "level" | ||||||
|  | 		o.MessageKey = "msg" | ||||||
|  | 		o.SourceKey = "caller" | ||||||
|  | 		o.StacktraceKey = "stacktrace" | ||||||
|  | 		o.ErrorKey = "error" | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| func WithIncCallerSkipCount(n int) Option { | func WithIncCallerSkipCount(n int) Option { | ||||||
| 	return func(o *Options) { | 	return func(o *Options) { | ||||||
| 		o.CallerSkipCount += n | 		o.CallerSkipCount += n | ||||||
|   | |||||||
| @@ -1,27 +0,0 @@ | |||||||
| package slog |  | ||||||
|  |  | ||||||
| import "go.unistack.org/micro/v3/logger" |  | ||||||
|  |  | ||||||
| type sourceKey struct{} |  | ||||||
|  |  | ||||||
| func WithSourceKey(v string) logger.Option { |  | ||||||
| 	return logger.SetOption(sourceKey{}, v) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type timeKey struct{} |  | ||||||
|  |  | ||||||
| func WithTimeKey(v string) logger.Option { |  | ||||||
| 	return logger.SetOption(timeKey{}, v) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type messageKey struct{} |  | ||||||
|  |  | ||||||
| func WithMessageKey(v string) logger.Option { |  | ||||||
| 	return logger.SetOption(messageKey{}, v) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type levelKey struct{} |  | ||||||
|  |  | ||||||
| func WithLevelKey(v string) logger.Option { |  | ||||||
| 	return logger.SetOption(levelKey{}, v) |  | ||||||
| } |  | ||||||
| @@ -5,21 +5,16 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"log/slog" | 	"log/slog" | ||||||
| 	"os" | 	"os" | ||||||
|  | 	"regexp" | ||||||
| 	"runtime" | 	"runtime" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"sync" | 	"sync" | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"go.unistack.org/micro/v3/logger" | 	"go.unistack.org/micro/v3/logger" | ||||||
| 	"go.unistack.org/micro/v3/tracer" | 	"go.unistack.org/micro/v3/tracer" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var ( | var reTrace = regexp.MustCompile(`.*/slog/logger\.go.*\n`) | ||||||
| 	DefaultSourceKey  string = slog.SourceKey |  | ||||||
| 	DefaultTimeKey    string = slog.TimeKey |  | ||||||
| 	DefaultMessageKey string = slog.MessageKey |  | ||||||
| 	DefaultLevelKey   string = slog.LevelKey |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	traceValue = slog.StringValue("trace") | 	traceValue = slog.StringValue("trace") | ||||||
| @@ -35,15 +30,15 @@ func (s *slogLogger) renameAttr(_ []string, a slog.Attr) slog.Attr { | |||||||
| 	case slog.SourceKey: | 	case slog.SourceKey: | ||||||
| 		source := a.Value.Any().(*slog.Source) | 		source := a.Value.Any().(*slog.Source) | ||||||
| 		a.Value = slog.StringValue(source.File + ":" + strconv.Itoa(source.Line)) | 		a.Value = slog.StringValue(source.File + ":" + strconv.Itoa(source.Line)) | ||||||
| 		a.Key = s.sourceKey | 		a.Key = s.opts.SourceKey | ||||||
| 	case slog.TimeKey: | 	case slog.TimeKey: | ||||||
| 		a.Key = s.timeKey | 		a.Key = s.opts.TimeKey | ||||||
| 	case slog.MessageKey: | 	case slog.MessageKey: | ||||||
| 		a.Key = s.messageKey | 		a.Key = s.opts.MessageKey | ||||||
| 	case slog.LevelKey: | 	case slog.LevelKey: | ||||||
| 		level := a.Value.Any().(slog.Level) | 		level := a.Value.Any().(slog.Level) | ||||||
| 		lvl := slogToLoggerLevel(level) | 		lvl := slogToLoggerLevel(level) | ||||||
| 		a.Key = s.levelKey | 		a.Key = s.opts.LevelKey | ||||||
| 		switch { | 		switch { | ||||||
| 		case lvl < logger.DebugLevel: | 		case lvl < logger.DebugLevel: | ||||||
| 			a.Value = traceValue | 			a.Value = traceValue | ||||||
| @@ -66,56 +61,33 @@ func (s *slogLogger) renameAttr(_ []string, a slog.Attr) slog.Attr { | |||||||
| } | } | ||||||
|  |  | ||||||
| type slogLogger struct { | type slogLogger struct { | ||||||
| 	slog       *slog.Logger | 	leveler *slog.LevelVar | ||||||
| 	leveler    *slog.LevelVar | 	handler slog.Handler | ||||||
| 	levelKey   string | 	opts    logger.Options | ||||||
| 	messageKey string | 	mu      sync.RWMutex | ||||||
| 	sourceKey  string |  | ||||||
| 	timeKey    string |  | ||||||
| 	opts       logger.Options |  | ||||||
| 	mu         sync.RWMutex |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *slogLogger) Clone(opts ...logger.Option) logger.Logger { | func (s *slogLogger) Clone(opts ...logger.Option) logger.Logger { | ||||||
| 	s.mu.RLock() | 	s.mu.RLock() | ||||||
| 	options := s.opts | 	options := s.opts | ||||||
|  | 	s.mu.RUnlock() | ||||||
|  |  | ||||||
| 	for _, o := range opts { | 	for _, o := range opts { | ||||||
| 		o(&options) | 		o(&options) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	l := &slogLogger{ | 	l := &slogLogger{ | ||||||
| 		opts:       options, | 		opts: options, | ||||||
| 		levelKey:   s.levelKey, |  | ||||||
| 		messageKey: s.messageKey, |  | ||||||
| 		sourceKey:  s.sourceKey, |  | ||||||
| 		timeKey:    s.timeKey, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if v, ok := l.opts.Context.Value(levelKey{}).(string); ok && v != "" { |  | ||||||
| 		l.levelKey = v |  | ||||||
| 	} |  | ||||||
| 	if v, ok := l.opts.Context.Value(messageKey{}).(string); ok && v != "" { |  | ||||||
| 		l.messageKey = v |  | ||||||
| 	} |  | ||||||
| 	if v, ok := l.opts.Context.Value(sourceKey{}).(string); ok && v != "" { |  | ||||||
| 		l.sourceKey = v |  | ||||||
| 	} |  | ||||||
| 	if v, ok := l.opts.Context.Value(timeKey{}).(string); ok && v != "" { |  | ||||||
| 		l.timeKey = v |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	l.leveler = new(slog.LevelVar) | 	l.leveler = new(slog.LevelVar) | ||||||
| 	handleOpt := &slog.HandlerOptions{ | 	handleOpt := &slog.HandlerOptions{ | ||||||
| 		ReplaceAttr: s.renameAttr, | 		ReplaceAttr: l.renameAttr, | ||||||
| 		Level:       l.leveler, | 		Level:       l.leveler, | ||||||
| 		AddSource:   true, | 		AddSource:   l.opts.AddSource, | ||||||
| 	} | 	} | ||||||
| 	l.leveler.Set(loggerToSlogLevel(l.opts.Level)) | 	l.leveler.Set(loggerToSlogLevel(l.opts.Level)) | ||||||
| 	handler := slog.NewJSONHandler(options.Out, handleOpt) | 	l.handler = slog.New(slog.NewJSONHandler(options.Out, handleOpt)).With(options.Fields...).Handler() | ||||||
| 	l.slog = slog.New(handler).With(options.Fields...) |  | ||||||
|  |  | ||||||
| 	s.mu.RUnlock() |  | ||||||
|  |  | ||||||
| 	return l | 	return l | ||||||
| } | } | ||||||
| @@ -134,61 +106,44 @@ func (s *slogLogger) Options() logger.Options { | |||||||
|  |  | ||||||
| func (s *slogLogger) Fields(attrs ...interface{}) logger.Logger { | func (s *slogLogger) Fields(attrs ...interface{}) logger.Logger { | ||||||
| 	s.mu.RLock() | 	s.mu.RLock() | ||||||
| 	nl := &slogLogger{ | 	level := s.leveler.Level() | ||||||
| 		opts:       s.opts, | 	options := s.opts | ||||||
| 		levelKey:   s.levelKey, |  | ||||||
| 		messageKey: s.messageKey, |  | ||||||
| 		sourceKey:  s.sourceKey, |  | ||||||
| 		timeKey:    s.timeKey, |  | ||||||
| 	} |  | ||||||
| 	nl.leveler = new(slog.LevelVar) |  | ||||||
| 	nl.leveler.Set(s.leveler.Level()) |  | ||||||
|  |  | ||||||
| 	handleOpt := &slog.HandlerOptions{ |  | ||||||
| 		ReplaceAttr: nl.renameAttr, |  | ||||||
| 		Level:       nl.leveler, |  | ||||||
| 		AddSource:   true, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	handler := slog.NewJSONHandler(s.opts.Out, handleOpt) |  | ||||||
| 	nl.slog = slog.New(handler).With(attrs...) |  | ||||||
|  |  | ||||||
| 	s.mu.RUnlock() | 	s.mu.RUnlock() | ||||||
|  |  | ||||||
| 	return nl | 	l := &slogLogger{opts: options} | ||||||
|  | 	l.leveler = new(slog.LevelVar) | ||||||
|  | 	l.leveler.Set(level) | ||||||
|  |  | ||||||
|  | 	handleOpt := &slog.HandlerOptions{ | ||||||
|  | 		ReplaceAttr: l.renameAttr, | ||||||
|  | 		Level:       l.leveler, | ||||||
|  | 		AddSource:   l.opts.AddSource, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	l.handler = slog.New(slog.NewJSONHandler(l.opts.Out, handleOpt)).With(attrs...).Handler() | ||||||
|  |  | ||||||
|  | 	return l | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *slogLogger) Init(opts ...logger.Option) error { | func (s *slogLogger) Init(opts ...logger.Option) error { | ||||||
| 	s.mu.Lock() | 	s.mu.Lock() | ||||||
| 	for _, o := range opts { |  | ||||||
| 		o(&s.opts) | 	if len(s.opts.ContextAttrFuncs) == 0 { | ||||||
|  | 		s.opts.ContextAttrFuncs = logger.DefaultContextAttrFuncs | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if v, ok := s.opts.Context.Value(levelKey{}).(string); ok && v != "" { | 	for _, o := range opts { | ||||||
| 		s.levelKey = v | 		o(&s.opts) | ||||||
| 	} |  | ||||||
| 	if v, ok := s.opts.Context.Value(messageKey{}).(string); ok && v != "" { |  | ||||||
| 		s.messageKey = v |  | ||||||
| 	} |  | ||||||
| 	if v, ok := s.opts.Context.Value(sourceKey{}).(string); ok && v != "" { |  | ||||||
| 		s.sourceKey = v |  | ||||||
| 	} |  | ||||||
| 	if v, ok := s.opts.Context.Value(timeKey{}).(string); ok && v != "" { |  | ||||||
| 		s.timeKey = v |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	s.leveler = new(slog.LevelVar) | 	s.leveler = new(slog.LevelVar) | ||||||
| 	handleOpt := &slog.HandlerOptions{ | 	handleOpt := &slog.HandlerOptions{ | ||||||
| 		ReplaceAttr: s.renameAttr, | 		ReplaceAttr: s.renameAttr, | ||||||
| 		Level:       s.leveler, | 		Level:       s.leveler, | ||||||
| 		AddSource:   true, | 		AddSource:   s.opts.AddSource, | ||||||
| 	} | 	} | ||||||
| 	s.leveler.Set(loggerToSlogLevel(s.opts.Level)) | 	s.leveler.Set(loggerToSlogLevel(s.opts.Level)) | ||||||
| 	handler := slog.NewJSONHandler(s.opts.Out, handleOpt) | 	s.handler = slog.New(slog.NewJSONHandler(s.opts.Out, handleOpt)).With(s.opts.Fields...).Handler() | ||||||
| 	s.slog = slog.New(handler).With(s.opts.Fields...) |  | ||||||
|  |  | ||||||
| 	slog.SetDefault(s.slog) |  | ||||||
|  |  | ||||||
| 	s.mu.Unlock() | 	s.mu.Unlock() | ||||||
|  |  | ||||||
| 	return nil | 	return nil | ||||||
| @@ -200,9 +155,37 @@ func (s *slogLogger) Log(ctx context.Context, lvl logger.Level, attrs ...interfa | |||||||
| 	} | 	} | ||||||
| 	var pcs [1]uintptr | 	var pcs [1]uintptr | ||||||
| 	runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] | 	runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] | ||||||
| 	r := slog.NewRecord(time.Now(), loggerToSlogLevel(lvl), fmt.Sprintf("%s", attrs[0]), pcs[0]) | 	r := slog.NewRecord(s.opts.TimeFunc(), loggerToSlogLevel(lvl), fmt.Sprintf("%s", attrs[0]), pcs[0]) | ||||||
| 	// r.Add(attrs[1:]...) | 	for _, fn := range s.opts.ContextAttrFuncs { | ||||||
| 	_ = s.slog.Handler().Handle(ctx, r) | 		attrs = append(attrs, fn(ctx)...) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for idx, attr := range attrs { | ||||||
|  | 		if ve, ok := attr.(error); ok && ve != nil { | ||||||
|  | 			attrs[idx] = slog.String(s.opts.ErrorKey, ve.Error()) | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if s.opts.AddStacktrace && lvl == logger.ErrorLevel { | ||||||
|  | 		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(s.opts.StacktraceKey, traceLines[len(traceLines)-1])) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	r.Add(attrs[1:]...) | ||||||
|  | 	r.Attrs(func(a slog.Attr) bool { | ||||||
|  | 		if a.Key == s.opts.ErrorKey { | ||||||
|  | 			if span, ok := tracer.SpanFromContext(ctx); ok { | ||||||
|  | 				span.SetStatus(tracer.SpanStatusError, a.Value.String()) | ||||||
|  | 				return false | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		return true | ||||||
|  | 	}) | ||||||
|  | 	_ = s.handler.Handle(ctx, r) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *slogLogger) Logf(ctx context.Context, lvl logger.Level, msg string, attrs ...interface{}) { | func (s *slogLogger) Logf(ctx context.Context, lvl logger.Level, msg string, attrs ...interface{}) { | ||||||
| @@ -211,9 +194,37 @@ func (s *slogLogger) Logf(ctx context.Context, lvl logger.Level, msg string, att | |||||||
| 	} | 	} | ||||||
| 	var pcs [1]uintptr | 	var pcs [1]uintptr | ||||||
| 	runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] | 	runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] | ||||||
| 	r := slog.NewRecord(time.Now(), loggerToSlogLevel(lvl), fmt.Sprintf(msg, attrs...), pcs[0]) | 	r := slog.NewRecord(s.opts.TimeFunc(), loggerToSlogLevel(lvl), msg, pcs[0]) | ||||||
| 	// r.Add(attrs...) | 	for _, fn := range s.opts.ContextAttrFuncs { | ||||||
| 	_ = s.slog.Handler().Handle(ctx, r) | 		attrs = append(attrs, fn(ctx)...) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for idx, attr := range attrs { | ||||||
|  | 		if ve, ok := attr.(error); ok && ve != nil { | ||||||
|  | 			attrs[idx] = slog.String(s.opts.ErrorKey, ve.Error()) | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if s.opts.AddStacktrace && lvl == logger.ErrorLevel { | ||||||
|  | 		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(s.opts.StacktraceKey, traceLines[len(traceLines)-1]))) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	r.Add(attrs[1:]...) | ||||||
|  | 	r.Attrs(func(a slog.Attr) bool { | ||||||
|  | 		if a.Key == s.opts.ErrorKey { | ||||||
|  | 			if span, ok := tracer.SpanFromContext(ctx); ok { | ||||||
|  | 				span.SetStatus(tracer.SpanStatusError, a.Value.String()) | ||||||
|  | 				return false | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		return true | ||||||
|  | 	}) | ||||||
|  | 	_ = s.handler.Handle(ctx, r) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *slogLogger) Info(ctx context.Context, attrs ...interface{}) { | func (s *slogLogger) Info(ctx context.Context, attrs ...interface{}) { | ||||||
| @@ -222,9 +233,19 @@ func (s *slogLogger) Info(ctx context.Context, attrs ...interface{}) { | |||||||
| 	} | 	} | ||||||
| 	var pcs [1]uintptr | 	var pcs [1]uintptr | ||||||
| 	runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] | 	runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] | ||||||
| 	r := slog.NewRecord(time.Now(), slog.LevelInfo, fmt.Sprintf("%s", attrs[0]), pcs[0]) | 	r := slog.NewRecord(s.opts.TimeFunc(), slog.LevelInfo, fmt.Sprintf("%s", attrs[0]), pcs[0]) | ||||||
| 	// r.Add(attrs[1:]...) | 	for _, fn := range s.opts.ContextAttrFuncs { | ||||||
| 	_ = s.slog.Handler().Handle(ctx, r) | 		attrs = append(attrs, fn(ctx)...) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for idx, attr := range attrs { | ||||||
|  | 		if ve, ok := attr.(error); ok && ve != nil { | ||||||
|  | 			attrs[idx] = slog.String(s.opts.ErrorKey, ve.Error()) | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	r.Add(attrs[1:]...) | ||||||
|  | 	_ = s.handler.Handle(ctx, r) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *slogLogger) Infof(ctx context.Context, msg string, attrs ...interface{}) { | func (s *slogLogger) Infof(ctx context.Context, msg string, attrs ...interface{}) { | ||||||
| @@ -233,9 +254,19 @@ func (s *slogLogger) Infof(ctx context.Context, msg string, attrs ...interface{} | |||||||
| 	} | 	} | ||||||
| 	var pcs [1]uintptr | 	var pcs [1]uintptr | ||||||
| 	runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] | 	runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] | ||||||
| 	r := slog.NewRecord(time.Now(), slog.LevelInfo, fmt.Sprintf(msg, attrs...), pcs[0]) | 	r := slog.NewRecord(s.opts.TimeFunc(), slog.LevelInfo, msg, pcs[0]) | ||||||
| 	// r.Add(attrs...) | 	for _, fn := range s.opts.ContextAttrFuncs { | ||||||
| 	_ = s.slog.Handler().Handle(ctx, r) | 		attrs = append(attrs, fn(ctx)...) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for idx, attr := range attrs { | ||||||
|  | 		if ve, ok := attr.(error); ok && ve != nil { | ||||||
|  | 			attrs[idx] = slog.String(s.opts.ErrorKey, ve.Error()) | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	r.Add(attrs...) | ||||||
|  | 	_ = s.handler.Handle(ctx, r) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *slogLogger) Debug(ctx context.Context, attrs ...interface{}) { | func (s *slogLogger) Debug(ctx context.Context, attrs ...interface{}) { | ||||||
| @@ -244,9 +275,19 @@ func (s *slogLogger) Debug(ctx context.Context, attrs ...interface{}) { | |||||||
| 	} | 	} | ||||||
| 	var pcs [1]uintptr | 	var pcs [1]uintptr | ||||||
| 	runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] | 	runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] | ||||||
| 	r := slog.NewRecord(time.Now(), slog.LevelDebug, fmt.Sprintf("%s", attrs[0]), pcs[0]) | 	r := slog.NewRecord(s.opts.TimeFunc(), slog.LevelDebug, fmt.Sprintf("%s", attrs[0]), pcs[0]) | ||||||
| 	// r.Add(attrs[1:]...) | 	for _, fn := range s.opts.ContextAttrFuncs { | ||||||
| 	_ = s.slog.Handler().Handle(ctx, r) | 		attrs = append(attrs, fn(ctx)...) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for idx, attr := range attrs { | ||||||
|  | 		if ve, ok := attr.(error); ok && ve != nil { | ||||||
|  | 			attrs[idx] = slog.String(s.opts.ErrorKey, ve.Error()) | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	r.Add(attrs[1:]...) | ||||||
|  | 	_ = s.handler.Handle(ctx, r) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *slogLogger) Debugf(ctx context.Context, msg string, attrs ...interface{}) { | func (s *slogLogger) Debugf(ctx context.Context, msg string, attrs ...interface{}) { | ||||||
| @@ -255,9 +296,19 @@ func (s *slogLogger) Debugf(ctx context.Context, msg string, attrs ...interface{ | |||||||
| 	} | 	} | ||||||
| 	var pcs [1]uintptr | 	var pcs [1]uintptr | ||||||
| 	runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] | 	runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] | ||||||
| 	r := slog.NewRecord(time.Now(), slog.LevelDebug, fmt.Sprintf(msg, attrs...), pcs[0]) | 	r := slog.NewRecord(s.opts.TimeFunc(), slog.LevelDebug, msg, pcs[0]) | ||||||
| 	// r.Add(attrs...) | 	for _, fn := range s.opts.ContextAttrFuncs { | ||||||
| 	_ = s.slog.Handler().Handle(ctx, r) | 		attrs = append(attrs, fn(ctx)...) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for idx, attr := range attrs { | ||||||
|  | 		if ve, ok := attr.(error); ok && ve != nil { | ||||||
|  | 			attrs[idx] = slog.String(s.opts.ErrorKey, ve.Error()) | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	r.Add(attrs...) | ||||||
|  | 	_ = s.handler.Handle(ctx, r) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *slogLogger) Trace(ctx context.Context, attrs ...interface{}) { | func (s *slogLogger) Trace(ctx context.Context, attrs ...interface{}) { | ||||||
| @@ -266,9 +317,19 @@ func (s *slogLogger) Trace(ctx context.Context, attrs ...interface{}) { | |||||||
| 	} | 	} | ||||||
| 	var pcs [1]uintptr | 	var pcs [1]uintptr | ||||||
| 	runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] | 	runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] | ||||||
| 	r := slog.NewRecord(time.Now(), slog.LevelDebug-1, fmt.Sprintf("%s", attrs[0]), pcs[0]) | 	r := slog.NewRecord(s.opts.TimeFunc(), slog.LevelDebug-1, fmt.Sprintf("%s", attrs[0]), pcs[0]) | ||||||
| 	// r.Add(attrs[1:]...) | 	for _, fn := range s.opts.ContextAttrFuncs { | ||||||
| 	_ = s.slog.Handler().Handle(ctx, r) | 		attrs = append(attrs, fn(ctx)...) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for idx, attr := range attrs { | ||||||
|  | 		if ve, ok := attr.(error); ok && ve != nil { | ||||||
|  | 			attrs[idx] = slog.String(s.opts.ErrorKey, ve.Error()) | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	r.Add(attrs[1:]...) | ||||||
|  | 	_ = s.handler.Handle(ctx, r) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *slogLogger) Tracef(ctx context.Context, msg string, attrs ...interface{}) { | func (s *slogLogger) Tracef(ctx context.Context, msg string, attrs ...interface{}) { | ||||||
| @@ -277,9 +338,19 @@ func (s *slogLogger) Tracef(ctx context.Context, msg string, attrs ...interface{ | |||||||
| 	} | 	} | ||||||
| 	var pcs [1]uintptr | 	var pcs [1]uintptr | ||||||
| 	runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] | 	runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] | ||||||
| 	r := slog.NewRecord(time.Now(), slog.LevelDebug-1, fmt.Sprintf(msg, attrs...), pcs[0]) | 	r := slog.NewRecord(s.opts.TimeFunc(), slog.LevelDebug-1, msg, pcs[0]) | ||||||
| 	// r.Add(attrs...) | 	for _, fn := range s.opts.ContextAttrFuncs { | ||||||
| 	_ = s.slog.Handler().Handle(ctx, r) | 		attrs = append(attrs, fn(ctx)...) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for idx, attr := range attrs { | ||||||
|  | 		if ve, ok := attr.(error); ok && ve != nil { | ||||||
|  | 			attrs[idx] = slog.String(s.opts.ErrorKey, ve.Error()) | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	r.Add(attrs[1:]...) | ||||||
|  | 	_ = s.handler.Handle(ctx, r) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *slogLogger) Error(ctx context.Context, attrs ...interface{}) { | func (s *slogLogger) Error(ctx context.Context, attrs ...interface{}) { | ||||||
| @@ -288,10 +359,29 @@ func (s *slogLogger) Error(ctx context.Context, attrs ...interface{}) { | |||||||
| 	} | 	} | ||||||
| 	var pcs [1]uintptr | 	var pcs [1]uintptr | ||||||
| 	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(s.opts.TimeFunc(), slog.LevelError, fmt.Sprintf("%s", attrs[0]), pcs[0]) | ||||||
| 	//	r.Add(attrs[1:]...) | 	for _, fn := range s.opts.ContextAttrFuncs { | ||||||
|  | 		attrs = append(attrs, fn(ctx)...) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for idx, attr := range attrs { | ||||||
|  | 		if ve, ok := attr.(error); ok && ve != nil { | ||||||
|  | 			attrs[idx] = slog.String(s.opts.ErrorKey, ve.Error()) | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if s.opts.AddStacktrace { | ||||||
|  | 		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[1:]...) | ||||||
| 	r.Attrs(func(a slog.Attr) bool { | 	r.Attrs(func(a slog.Attr) bool { | ||||||
| 		if a.Key == "error" { | 		if a.Key == s.opts.ErrorKey { | ||||||
| 			if span, ok := tracer.SpanFromContext(ctx); ok { | 			if span, ok := tracer.SpanFromContext(ctx); ok { | ||||||
| 				span.SetStatus(tracer.SpanStatusError, a.Value.String()) | 				span.SetStatus(tracer.SpanStatusError, a.Value.String()) | ||||||
| 				return false | 				return false | ||||||
| @@ -299,7 +389,7 @@ func (s *slogLogger) Error(ctx context.Context, attrs ...interface{}) { | |||||||
| 		} | 		} | ||||||
| 		return true | 		return true | ||||||
| 	}) | 	}) | ||||||
| 	_ = s.slog.Handler().Handle(ctx, r) | 	_ = s.handler.Handle(ctx, r) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *slogLogger) Errorf(ctx context.Context, msg string, attrs ...interface{}) { | func (s *slogLogger) Errorf(ctx context.Context, msg string, attrs ...interface{}) { | ||||||
| @@ -308,10 +398,29 @@ func (s *slogLogger) Errorf(ctx context.Context, msg string, attrs ...interface{ | |||||||
| 	} | 	} | ||||||
| 	var pcs [1]uintptr | 	var pcs [1]uintptr | ||||||
| 	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(s.opts.TimeFunc(), slog.LevelError, msg, pcs[0]) | ||||||
| 	// r.Add(attrs...) | 	for _, fn := range s.opts.ContextAttrFuncs { | ||||||
|  | 		attrs = append(attrs, fn(ctx)...) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for idx, attr := range attrs { | ||||||
|  | 		if ve, ok := attr.(error); ok && ve != nil { | ||||||
|  | 			attrs[idx] = slog.String(s.opts.ErrorKey, ve.Error()) | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if s.opts.AddStacktrace { | ||||||
|  | 		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 { | 	r.Attrs(func(a slog.Attr) bool { | ||||||
| 		if a.Key == "error" { | 		if a.Key == s.opts.ErrorKey { | ||||||
| 			if span, ok := tracer.SpanFromContext(ctx); ok { | 			if span, ok := tracer.SpanFromContext(ctx); ok { | ||||||
| 				span.SetStatus(tracer.SpanStatusError, a.Value.String()) | 				span.SetStatus(tracer.SpanStatusError, a.Value.String()) | ||||||
| 				return false | 				return false | ||||||
| @@ -319,7 +428,7 @@ func (s *slogLogger) Errorf(ctx context.Context, msg string, attrs ...interface{ | |||||||
| 		} | 		} | ||||||
| 		return true | 		return true | ||||||
| 	}) | 	}) | ||||||
| 	_ = s.slog.Handler().Handle(ctx, r) | 	_ = s.handler.Handle(ctx, r) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *slogLogger) Fatal(ctx context.Context, attrs ...interface{}) { | func (s *slogLogger) Fatal(ctx context.Context, attrs ...interface{}) { | ||||||
| @@ -328,9 +437,19 @@ func (s *slogLogger) Fatal(ctx context.Context, attrs ...interface{}) { | |||||||
| 	} | 	} | ||||||
| 	var pcs [1]uintptr | 	var pcs [1]uintptr | ||||||
| 	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+1, fmt.Sprintf("%s", attrs[0]), pcs[0]) | 	r := slog.NewRecord(s.opts.TimeFunc(), slog.LevelError+1, fmt.Sprintf("%s", attrs[0]), pcs[0]) | ||||||
| 	// r.Add(attrs[1:]...) | 	for _, fn := range s.opts.ContextAttrFuncs { | ||||||
| 	_ = s.slog.Handler().Handle(ctx, r) | 		attrs = append(attrs, fn(ctx)...) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for idx, attr := range attrs { | ||||||
|  | 		if ve, ok := attr.(error); ok && ve != nil { | ||||||
|  | 			attrs[idx] = slog.String(s.opts.ErrorKey, ve.Error()) | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	r.Add(attrs[1:]...) | ||||||
|  | 	_ = s.handler.Handle(ctx, r) | ||||||
| 	os.Exit(1) | 	os.Exit(1) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -340,9 +459,19 @@ func (s *slogLogger) Fatalf(ctx context.Context, msg string, attrs ...interface{ | |||||||
| 	} | 	} | ||||||
| 	var pcs [1]uintptr | 	var pcs [1]uintptr | ||||||
| 	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+1, fmt.Sprintf(msg, attrs...), pcs[0]) | 	r := slog.NewRecord(s.opts.TimeFunc(), slog.LevelError+1, msg, pcs[0]) | ||||||
| 	// r.Add(attrs...) | 	for _, fn := range s.opts.ContextAttrFuncs { | ||||||
| 	_ = s.slog.Handler().Handle(ctx, r) | 		attrs = append(attrs, fn(ctx)...) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for idx, attr := range attrs { | ||||||
|  | 		if ve, ok := attr.(error); ok && ve != nil { | ||||||
|  | 			attrs[idx] = slog.String(s.opts.ErrorKey, ve.Error()) | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	r.Add(attrs...) | ||||||
|  | 	_ = s.handler.Handle(ctx, r) | ||||||
| 	os.Exit(1) | 	os.Exit(1) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -352,9 +481,19 @@ func (s *slogLogger) Warn(ctx context.Context, attrs ...interface{}) { | |||||||
| 	} | 	} | ||||||
| 	var pcs [1]uintptr | 	var pcs [1]uintptr | ||||||
| 	runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] | 	runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] | ||||||
| 	r := slog.NewRecord(time.Now(), slog.LevelWarn, fmt.Sprintf("%s", attrs[0]), pcs[0]) | 	r := slog.NewRecord(s.opts.TimeFunc(), slog.LevelWarn, fmt.Sprintf("%s", attrs[0]), pcs[0]) | ||||||
| 	// r.Add(attrs[1:]...) | 	for _, fn := range s.opts.ContextAttrFuncs { | ||||||
| 	_ = s.slog.Handler().Handle(ctx, r) | 		attrs = append(attrs, fn(ctx)...) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for idx, attr := range attrs { | ||||||
|  | 		if ve, ok := attr.(error); ok && ve != nil { | ||||||
|  | 			attrs[idx] = slog.String(s.opts.ErrorKey, ve.Error()) | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	r.Add(attrs[1:]...) | ||||||
|  | 	_ = s.handler.Handle(ctx, r) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *slogLogger) Warnf(ctx context.Context, msg string, attrs ...interface{}) { | func (s *slogLogger) Warnf(ctx context.Context, msg string, attrs ...interface{}) { | ||||||
| @@ -363,9 +502,23 @@ func (s *slogLogger) Warnf(ctx context.Context, msg string, attrs ...interface{} | |||||||
| 	} | 	} | ||||||
| 	var pcs [1]uintptr | 	var pcs [1]uintptr | ||||||
| 	runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] | 	runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, Infof] | ||||||
| 	r := slog.NewRecord(time.Now(), slog.LevelWarn, fmt.Sprintf(msg, attrs...), pcs[0]) | 	r := slog.NewRecord(s.opts.TimeFunc(), slog.LevelWarn, msg, pcs[0]) | ||||||
| 	// r.Add(attrs...) | 	for _, fn := range s.opts.ContextAttrFuncs { | ||||||
| 	_ = s.slog.Handler().Handle(ctx, r) | 		attrs = append(attrs, fn(ctx)...) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	for idx, attr := range attrs { | ||||||
|  | 		if ve, ok := attr.(error); ok && ve != nil { | ||||||
|  | 			attrs[idx] = slog.String(s.opts.ErrorKey, ve.Error()) | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	r.Add(attrs[1:]...) | ||||||
|  | 	_ = s.handler.Handle(ctx, r) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *slogLogger) Name() string { | ||||||
|  | 	return s.opts.Name | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *slogLogger) String() string { | func (s *slogLogger) String() string { | ||||||
| @@ -374,24 +527,9 @@ func (s *slogLogger) String() string { | |||||||
|  |  | ||||||
| func NewLogger(opts ...logger.Option) logger.Logger { | func NewLogger(opts ...logger.Option) logger.Logger { | ||||||
| 	s := &slogLogger{ | 	s := &slogLogger{ | ||||||
| 		opts:       logger.NewOptions(opts...), | 		opts: logger.NewOptions(opts...), | ||||||
| 		sourceKey:  DefaultSourceKey, |  | ||||||
| 		timeKey:    DefaultTimeKey, |  | ||||||
| 		messageKey: DefaultMessageKey, |  | ||||||
| 		levelKey:   DefaultLevelKey, |  | ||||||
| 	} |  | ||||||
| 	if v, ok := s.opts.Context.Value(levelKey{}).(string); ok && v != "" { |  | ||||||
| 		s.levelKey = v |  | ||||||
| 	} |  | ||||||
| 	if v, ok := s.opts.Context.Value(messageKey{}).(string); ok && v != "" { |  | ||||||
| 		s.messageKey = v |  | ||||||
| 	} |  | ||||||
| 	if v, ok := s.opts.Context.Value(sourceKey{}).(string); ok && v != "" { |  | ||||||
| 		s.sourceKey = v |  | ||||||
| 	} |  | ||||||
| 	if v, ok := s.opts.Context.Value(timeKey{}).(string); ok && v != "" { |  | ||||||
| 		s.timeKey = v |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return s | 	return s | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,12 +3,47 @@ package slog | |||||||
| import ( | import ( | ||||||
| 	"bytes" | 	"bytes" | ||||||
| 	"context" | 	"context" | ||||||
|  | 	"fmt" | ||||||
| 	"log" | 	"log" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"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.WithAddStacktrace(true)) | ||||||
|  | 	if err := l.Init(); err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	l.Error(ctx, "message", fmt.Errorf("error message")) | ||||||
|  | 	if !bytes.Contains(buf.Bytes(), []byte(`"stacktrace":"`)) { | ||||||
|  | 		t.Fatalf("logger stacktrace not works, buf contains: %s", buf.Bytes()) | ||||||
|  | 	} | ||||||
|  | 	if !bytes.Contains(buf.Bytes(), []byte(`"error":"`)) { | ||||||
|  | 		t.Fatalf("logger error not works, buf contains: %s", buf.Bytes()) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestErrorf(t *testing.T) { | ||||||
|  | 	ctx := context.TODO() | ||||||
|  | 	buf := bytes.NewBuffer(nil) | ||||||
|  | 	l := NewLogger(logger.WithLevel(logger.ErrorLevel), logger.WithOutput(buf), logger.WithAddStacktrace(true)) | ||||||
|  | 	if err := l.Init(); err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	l.Errorf(ctx, "message", fmt.Errorf("error message")) | ||||||
|  | 	if !bytes.Contains(buf.Bytes(), []byte(`"stacktrace":"`)) { | ||||||
|  | 		t.Fatalf("logger stacktrace not works, buf contains: %s", buf.Bytes()) | ||||||
|  | 	} | ||||||
|  | 	if !bytes.Contains(buf.Bytes(), []byte(`"error":"`)) { | ||||||
|  | 		t.Fatalf("logger error 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) | ||||||
|   | |||||||
| @@ -98,11 +98,12 @@ func (md Metadata) Del(keys ...string) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // Copy makes a copy of the metadata | // Copy makes a copy of the metadata | ||||||
| func Copy(md Metadata) Metadata { | func Copy(md Metadata, exclude ...string) Metadata { | ||||||
| 	nmd := New(len(md)) | 	nmd := New(len(md)) | ||||||
| 	for key, val := range md { | 	for key, val := range md { | ||||||
| 		nmd.Set(key, val) | 		nmd.Set(key, val) | ||||||
| 	} | 	} | ||||||
|  | 	nmd.Del(exclude...) | ||||||
| 	return nmd | 	return nmd | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -190,3 +190,14 @@ func TestMetadataContext(t *testing.T) { | |||||||
| 		t.Errorf("Expected metadata length 1 got %d", i) | 		t.Errorf("Expected metadata length 1 got %d", i) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func TestCopy(t *testing.T) { | ||||||
|  | 	md := New(2) | ||||||
|  | 	md.Set("key1", "val1", "key2", "val2") | ||||||
|  | 	nmd := Copy(md, "key2") | ||||||
|  | 	if len(nmd) != 1 { | ||||||
|  | 		t.Fatal("Copy exclude not works") | ||||||
|  | 	} else if nmd["Key1"] != "val1" { | ||||||
|  | 		t.Fatal("Copy exclude not works") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| // Package meter is for instrumentation | // Package meter is for instrumentation | ||||||
| package meter // import "go.unistack.org/micro/v3/meter" | package meter | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"io" | 	"io" | ||||||
| @@ -11,7 +11,7 @@ import ( | |||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	// DefaultMeter is the default meter | 	// DefaultMeter is the default meter | ||||||
| 	DefaultMeter = NewMeter() | 	DefaultMeter Meter = NewMeter() | ||||||
| 	// DefaultAddress data will be made available on this host:port | 	// DefaultAddress data will be made available on this host:port | ||||||
| 	DefaultAddress = ":9090" | 	DefaultAddress = ":9090" | ||||||
| 	// DefaultPath the meter endpoint where the Meter data will be made available | 	// DefaultPath the meter endpoint where the Meter data will be made available | ||||||
|   | |||||||
							
								
								
									
										94
									
								
								micro.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								micro.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | |||||||
|  | package micro | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"reflect" | ||||||
|  |  | ||||||
|  | 	"go.unistack.org/micro/v3/broker" | ||||||
|  | 	"go.unistack.org/micro/v3/client" | ||||||
|  | 	"go.unistack.org/micro/v3/codec" | ||||||
|  | 	"go.unistack.org/micro/v3/flow" | ||||||
|  | 	"go.unistack.org/micro/v3/fsm" | ||||||
|  | 	"go.unistack.org/micro/v3/logger" | ||||||
|  | 	"go.unistack.org/micro/v3/meter" | ||||||
|  | 	"go.unistack.org/micro/v3/register" | ||||||
|  | 	"go.unistack.org/micro/v3/resolver" | ||||||
|  | 	"go.unistack.org/micro/v3/router" | ||||||
|  | 	"go.unistack.org/micro/v3/selector" | ||||||
|  | 	"go.unistack.org/micro/v3/server" | ||||||
|  | 	"go.unistack.org/micro/v3/store" | ||||||
|  | 	"go.unistack.org/micro/v3/sync" | ||||||
|  | 	"go.unistack.org/micro/v3/tracer" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func As(b any, target any) bool { | ||||||
|  | 	if b == nil { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	if target == nil { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	val := reflect.ValueOf(target) | ||||||
|  | 	typ := val.Type() | ||||||
|  | 	if typ.Kind() != reflect.Ptr || val.IsNil() { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	targetType := typ.Elem() | ||||||
|  | 	if targetType.Kind() != reflect.Interface { | ||||||
|  | 		switch { | ||||||
|  | 		case targetType.Implements(brokerType): | ||||||
|  | 			break | ||||||
|  | 		case targetType.Implements(loggerType): | ||||||
|  | 			break | ||||||
|  | 		case targetType.Implements(clientType): | ||||||
|  | 			break | ||||||
|  | 		case targetType.Implements(serverType): | ||||||
|  | 			break | ||||||
|  | 		case targetType.Implements(codecType): | ||||||
|  | 			break | ||||||
|  | 		case targetType.Implements(flowType): | ||||||
|  | 			break | ||||||
|  | 		case targetType.Implements(fsmType): | ||||||
|  | 			break | ||||||
|  | 		case targetType.Implements(meterType): | ||||||
|  | 			break | ||||||
|  | 		case targetType.Implements(registerType): | ||||||
|  | 			break | ||||||
|  | 		case targetType.Implements(resolverType): | ||||||
|  | 			break | ||||||
|  | 		case targetType.Implements(selectorType): | ||||||
|  | 			break | ||||||
|  | 		case targetType.Implements(storeType): | ||||||
|  | 			break | ||||||
|  | 		case targetType.Implements(syncType): | ||||||
|  | 			break | ||||||
|  | 		case targetType.Implements(serviceType): | ||||||
|  | 			break | ||||||
|  | 		case targetType.Implements(routerType): | ||||||
|  | 			break | ||||||
|  | 		default: | ||||||
|  | 			return false | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if reflect.TypeOf(b).AssignableTo(targetType) { | ||||||
|  | 		val.Elem().Set(reflect.ValueOf(b)) | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var brokerType = reflect.TypeOf((*broker.Broker)(nil)).Elem() | ||||||
|  | var loggerType = reflect.TypeOf((*logger.Logger)(nil)).Elem() | ||||||
|  | var clientType = reflect.TypeOf((*client.Client)(nil)).Elem() | ||||||
|  | var serverType = reflect.TypeOf((*server.Server)(nil)).Elem() | ||||||
|  | var codecType = reflect.TypeOf((*codec.Codec)(nil)).Elem() | ||||||
|  | var flowType = reflect.TypeOf((*flow.Flow)(nil)).Elem() | ||||||
|  | var fsmType = reflect.TypeOf((*fsm.FSM)(nil)).Elem() | ||||||
|  | var meterType = reflect.TypeOf((*meter.Meter)(nil)).Elem() | ||||||
|  | var registerType = reflect.TypeOf((*register.Register)(nil)).Elem() | ||||||
|  | var resolverType = reflect.TypeOf((*resolver.Resolver)(nil)).Elem() | ||||||
|  | var routerType = reflect.TypeOf((*router.Router)(nil)).Elem() | ||||||
|  | var selectorType = reflect.TypeOf((*selector.Selector)(nil)).Elem() | ||||||
|  | var storeType = reflect.TypeOf((*store.Store)(nil)).Elem() | ||||||
|  | var syncType = reflect.TypeOf((*sync.Sync)(nil)).Elem() | ||||||
|  | var tracerType = reflect.TypeOf((*tracer.Tracer)(nil)).Elem() | ||||||
|  | var serviceType = reflect.TypeOf((*Service)(nil)).Elem() | ||||||
							
								
								
									
										115
									
								
								micro_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								micro_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,115 @@ | |||||||
|  | package micro | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"fmt" | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"go.unistack.org/micro/v3/broker" | ||||||
|  | 	"go.unistack.org/micro/v3/fsm" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestAs(t *testing.T) { | ||||||
|  | 	var b *bro | ||||||
|  | 	broTarget := &bro{name: "kafka"} | ||||||
|  | 	fsmTarget := &fsmT{name: "fsm"} | ||||||
|  |  | ||||||
|  | 	testCases := []struct { | ||||||
|  | 		b      any | ||||||
|  | 		target any | ||||||
|  | 		match  bool | ||||||
|  | 		want   any | ||||||
|  | 	}{ | ||||||
|  | 		{ | ||||||
|  | 			broTarget, | ||||||
|  | 			&b, | ||||||
|  | 			true, | ||||||
|  | 			broTarget, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			nil, | ||||||
|  | 			&b, | ||||||
|  | 			false, | ||||||
|  | 			nil, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			fsmTarget, | ||||||
|  | 			&b, | ||||||
|  | 			false, | ||||||
|  | 			nil, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	for i, tc := range testCases { | ||||||
|  | 		name := fmt.Sprintf("%d:As(Errorf(..., %v), %v)", i, tc.b, tc.target) | ||||||
|  | 		// Clear the target pointer, in case it was set in a previous test. | ||||||
|  | 		rtarget := reflect.ValueOf(tc.target) | ||||||
|  | 		rtarget.Elem().Set(reflect.Zero(reflect.TypeOf(tc.target).Elem())) | ||||||
|  | 		t.Run(name, func(t *testing.T) { | ||||||
|  | 			match := As(tc.b, tc.target) | ||||||
|  | 			if match != tc.match { | ||||||
|  | 				t.Fatalf("match: got %v; want %v", match, tc.match) | ||||||
|  | 			} | ||||||
|  | 			if !match { | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			if got := rtarget.Elem().Interface(); got != tc.want { | ||||||
|  | 				t.Fatalf("got %#v, want %#v", got, tc.want) | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type bro struct { | ||||||
|  | 	name string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p *bro) Name() string { return p.name } | ||||||
|  |  | ||||||
|  | func (p *bro) Init(opts ...broker.Option) error { return nil } | ||||||
|  |  | ||||||
|  | // Options returns broker options | ||||||
|  | func (p *bro) Options() broker.Options { return broker.Options{} } | ||||||
|  |  | ||||||
|  | // Address return configured address | ||||||
|  | func (p *bro) Address() string { return "" } | ||||||
|  |  | ||||||
|  | // Connect connects to broker | ||||||
|  | func (p *bro) Connect(ctx context.Context) error { return nil } | ||||||
|  |  | ||||||
|  | // Disconnect disconnect from broker | ||||||
|  | func (p *bro) Disconnect(ctx context.Context) error { return nil } | ||||||
|  |  | ||||||
|  | // Publish message, msg can be single broker.Message or []broker.Message | ||||||
|  | func (p *bro) Publish(ctx context.Context, topic string, msg *broker.Message, opts ...broker.PublishOption) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // BatchPublish messages to broker with multiple topics | ||||||
|  | func (p *bro) BatchPublish(ctx context.Context, msgs []*broker.Message, opts ...broker.PublishOption) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // BatchSubscribe subscribes to topic messages via handler | ||||||
|  | func (p *bro) BatchSubscribe(ctx context.Context, topic string, h broker.BatchHandler, opts ...broker.SubscribeOption) (broker.Subscriber, error) { | ||||||
|  | 	return nil, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Subscribe subscribes to topic message via handler | ||||||
|  | func (p *bro) Subscribe(ctx context.Context, topic string, handler broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) { | ||||||
|  | 	return nil, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // String type of broker | ||||||
|  | func (p *bro) String() string { return p.name } | ||||||
|  |  | ||||||
|  | type fsmT struct { | ||||||
|  | 	name string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *fsmT) Start(ctx context.Context, a interface{}, o ...Option) (interface{}, error) { | ||||||
|  | 	return nil, nil | ||||||
|  | } | ||||||
|  | func (f *fsmT) Current() string                  { return f.name } | ||||||
|  | func (f *fsmT) Reset()                           {} | ||||||
|  | func (f *fsmT) State(s string, sf fsm.StateFunc) {} | ||||||
| @@ -6,6 +6,7 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"go.unistack.org/micro/v3/logger" | 	"go.unistack.org/micro/v3/logger" | ||||||
|  | 	"go.unistack.org/micro/v3/register" | ||||||
| 	"go.unistack.org/micro/v3/util/id" | 	"go.unistack.org/micro/v3/util/id" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| @@ -16,7 +17,7 @@ var ( | |||||||
| 
 | 
 | ||||||
| type node struct { | type node struct { | ||||||
| 	LastSeen time.Time | 	LastSeen time.Time | ||||||
| 	*Node | 	*register.Node | ||||||
| 	TTL time.Duration | 	TTL time.Duration | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @@ -25,23 +26,23 @@ type record struct { | |||||||
| 	Version   string | 	Version   string | ||||||
| 	Metadata  map[string]string | 	Metadata  map[string]string | ||||||
| 	Nodes     map[string]*node | 	Nodes     map[string]*node | ||||||
| 	Endpoints []*Endpoint | 	Endpoints []*register.Endpoint | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type memory struct { | type memory struct { | ||||||
| 	sync.RWMutex | 	sync.RWMutex | ||||||
| 	records  map[string]services | 	records  map[string]services | ||||||
| 	watchers map[string]*watcher | 	watchers map[string]*watcher | ||||||
| 	opts     Options | 	opts     register.Options | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // services is a KV map with service name as the key and a map of records as the value | // services is a KV map with service name as the key and a map of records as the value | ||||||
| type services map[string]map[string]*record | type services map[string]map[string]*record | ||||||
| 
 | 
 | ||||||
| // NewRegister returns an initialized in-memory register | // NewRegister returns an initialized in-memory register | ||||||
| func NewRegister(opts ...Option) Register { | func NewRegister(opts ...register.Option) register.Register { | ||||||
| 	r := &memory{ | 	r := &memory{ | ||||||
| 		opts:     NewOptions(opts...), | 		opts:     register.NewOptions(opts...), | ||||||
| 		records:  make(map[string]services), | 		records:  make(map[string]services), | ||||||
| 		watchers: make(map[string]*watcher), | 		watchers: make(map[string]*watcher), | ||||||
| 	} | 	} | ||||||
| @@ -75,7 +76,7 @@ func (m *memory) ttlPrune() { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (m *memory) sendEvent(r *Result) { | func (m *memory) sendEvent(r *register.Result) { | ||||||
| 	m.RLock() | 	m.RLock() | ||||||
| 	watchers := make([]*watcher, 0, len(m.watchers)) | 	watchers := make([]*watcher, 0, len(m.watchers)) | ||||||
| 	for _, w := range m.watchers { | 	for _, w := range m.watchers { | ||||||
| @@ -106,7 +107,7 @@ func (m *memory) Disconnect(ctx context.Context) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (m *memory) Init(opts ...Option) error { | func (m *memory) Init(opts ...register.Option) error { | ||||||
| 	for _, o := range opts { | 	for _, o := range opts { | ||||||
| 		o(&m.opts) | 		o(&m.opts) | ||||||
| 	} | 	} | ||||||
| @@ -118,15 +119,15 @@ func (m *memory) Init(opts ...Option) error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (m *memory) Options() Options { | func (m *memory) Options() register.Options { | ||||||
| 	return m.opts | 	return m.opts | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (m *memory) Register(ctx context.Context, s *Service, opts ...RegisterOption) error { | func (m *memory) Register(ctx context.Context, s *register.Service, opts ...register.RegisterOption) error { | ||||||
| 	m.Lock() | 	m.Lock() | ||||||
| 	defer m.Unlock() | 	defer m.Unlock() | ||||||
| 
 | 
 | ||||||
| 	options := NewRegisterOptions(opts...) | 	options := register.NewRegisterOptions(opts...) | ||||||
| 
 | 
 | ||||||
| 	// get the services for this domain from the register | 	// get the services for this domain from the register | ||||||
| 	srvs, ok := m.records[options.Domain] | 	srvs, ok := m.records[options.Domain] | ||||||
| @@ -153,7 +154,7 @@ func (m *memory) Register(ctx context.Context, s *Service, opts ...RegisterOptio | |||||||
| 			m.opts.Logger.Debugf(m.opts.Context, "Register added new service: %s, version: %s", s.Name, s.Version) | 			m.opts.Logger.Debugf(m.opts.Context, "Register added new service: %s, version: %s", s.Name, s.Version) | ||||||
| 		} | 		} | ||||||
| 		m.records[options.Domain] = srvs | 		m.records[options.Domain] = srvs | ||||||
| 		go m.sendEvent(&Result{Action: "create", Service: s}) | 		go m.sendEvent(®ister.Result{Action: "create", Service: s}) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	var addedNodes bool | 	var addedNodes bool | ||||||
| @@ -176,7 +177,7 @@ func (m *memory) Register(ctx context.Context, s *Service, opts ...RegisterOptio | |||||||
| 
 | 
 | ||||||
| 		// add the node | 		// add the node | ||||||
| 		srvs[s.Name][s.Version].Nodes[n.ID] = &node{ | 		srvs[s.Name][s.Version].Nodes[n.ID] = &node{ | ||||||
| 			Node: &Node{ | 			Node: ®ister.Node{ | ||||||
| 				ID:       n.ID, | 				ID:       n.ID, | ||||||
| 				Address:  n.Address, | 				Address:  n.Address, | ||||||
| 				Metadata: metadata, | 				Metadata: metadata, | ||||||
| @@ -192,7 +193,7 @@ func (m *memory) Register(ctx context.Context, s *Service, opts ...RegisterOptio | |||||||
| 		if m.opts.Logger.V(logger.DebugLevel) { | 		if m.opts.Logger.V(logger.DebugLevel) { | ||||||
| 			m.opts.Logger.Debugf(m.opts.Context, "Register added new node to service: %s, version: %s", s.Name, s.Version) | 			m.opts.Logger.Debugf(m.opts.Context, "Register added new node to service: %s, version: %s", s.Name, s.Version) | ||||||
| 		} | 		} | ||||||
| 		go m.sendEvent(&Result{Action: "update", Service: s}) | 		go m.sendEvent(®ister.Result{Action: "update", Service: s}) | ||||||
| 	} else { | 	} else { | ||||||
| 		// refresh TTL and timestamp | 		// refresh TTL and timestamp | ||||||
| 		for _, n := range s.Nodes { | 		for _, n := range s.Nodes { | ||||||
| @@ -208,11 +209,11 @@ func (m *memory) Register(ctx context.Context, s *Service, opts ...RegisterOptio | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (m *memory) Deregister(ctx context.Context, s *Service, opts ...DeregisterOption) error { | func (m *memory) Deregister(ctx context.Context, s *register.Service, opts ...register.DeregisterOption) error { | ||||||
| 	m.Lock() | 	m.Lock() | ||||||
| 	defer m.Unlock() | 	defer m.Unlock() | ||||||
| 
 | 
 | ||||||
| 	options := NewDeregisterOptions(opts...) | 	options := register.NewDeregisterOptions(opts...) | ||||||
| 
 | 
 | ||||||
| 	// domain is set in metadata so it can be passed to watchers | 	// domain is set in metadata so it can be passed to watchers | ||||||
| 	if s.Metadata == nil { | 	if s.Metadata == nil { | ||||||
| @@ -252,7 +253,7 @@ func (m *memory) Deregister(ctx context.Context, s *Service, opts ...DeregisterO | |||||||
| 	// is cleanup | 	// is cleanup | ||||||
| 	if len(version.Nodes) > 0 { | 	if len(version.Nodes) > 0 { | ||||||
| 		m.records[options.Domain][s.Name][s.Version] = version | 		m.records[options.Domain][s.Name][s.Version] = version | ||||||
| 		go m.sendEvent(&Result{Action: "update", Service: s}) | 		go m.sendEvent(®ister.Result{Action: "update", Service: s}) | ||||||
| 		return nil | 		return nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| @@ -260,7 +261,7 @@ func (m *memory) Deregister(ctx context.Context, s *Service, opts ...DeregisterO | |||||||
| 	// register and exit | 	// register and exit | ||||||
| 	if len(versions) == 1 { | 	if len(versions) == 1 { | ||||||
| 		delete(m.records[options.Domain], s.Name) | 		delete(m.records[options.Domain], s.Name) | ||||||
| 		go m.sendEvent(&Result{Action: "delete", Service: s}) | 		go m.sendEvent(®ister.Result{Action: "delete", Service: s}) | ||||||
| 
 | 
 | ||||||
| 		if m.opts.Logger.V(logger.DebugLevel) { | 		if m.opts.Logger.V(logger.DebugLevel) { | ||||||
| 			m.opts.Logger.Debugf(m.opts.Context, "Register removed service: %s", s.Name) | 			m.opts.Logger.Debugf(m.opts.Context, "Register removed service: %s", s.Name) | ||||||
| @@ -270,7 +271,7 @@ func (m *memory) Deregister(ctx context.Context, s *Service, opts ...DeregisterO | |||||||
| 
 | 
 | ||||||
| 	// there are other versions of the service running, so only remove this version of it | 	// there are other versions of the service running, so only remove this version of it | ||||||
| 	delete(m.records[options.Domain][s.Name], s.Version) | 	delete(m.records[options.Domain][s.Name], s.Version) | ||||||
| 	go m.sendEvent(&Result{Action: "delete", Service: s}) | 	go m.sendEvent(®ister.Result{Action: "delete", Service: s}) | ||||||
| 	if m.opts.Logger.V(logger.DebugLevel) { | 	if m.opts.Logger.V(logger.DebugLevel) { | ||||||
| 		m.opts.Logger.Debugf(m.opts.Context, "Register removed service: %s, version: %s", s.Name, s.Version) | 		m.opts.Logger.Debugf(m.opts.Context, "Register removed service: %s, version: %s", s.Name, s.Version) | ||||||
| 	} | 	} | ||||||
| @@ -278,20 +279,20 @@ func (m *memory) Deregister(ctx context.Context, s *Service, opts ...DeregisterO | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (m *memory) LookupService(ctx context.Context, name string, opts ...LookupOption) ([]*Service, error) { | func (m *memory) LookupService(ctx context.Context, name string, opts ...register.LookupOption) ([]*register.Service, error) { | ||||||
| 	options := NewLookupOptions(opts...) | 	options := register.NewLookupOptions(opts...) | ||||||
| 
 | 
 | ||||||
| 	// if it's a wildcard domain, return from all domains | 	// if it's a wildcard domain, return from all domains | ||||||
| 	if options.Domain == WildcardDomain { | 	if options.Domain == register.WildcardDomain { | ||||||
| 		m.RLock() | 		m.RLock() | ||||||
| 		recs := m.records | 		recs := m.records | ||||||
| 		m.RUnlock() | 		m.RUnlock() | ||||||
| 
 | 
 | ||||||
| 		var services []*Service | 		var services []*register.Service | ||||||
| 
 | 
 | ||||||
| 		for domain := range recs { | 		for domain := range recs { | ||||||
| 			srvs, err := m.LookupService(ctx, name, append(opts, LookupDomain(domain))...) | 			srvs, err := m.LookupService(ctx, name, append(opts, register.LookupDomain(domain))...) | ||||||
| 			if err == ErrNotFound { | 			if err == register.ErrNotFound { | ||||||
| 				continue | 				continue | ||||||
| 			} else if err != nil { | 			} else if err != nil { | ||||||
| 				return nil, err | 				return nil, err | ||||||
| @@ -300,7 +301,7 @@ func (m *memory) LookupService(ctx context.Context, name string, opts ...LookupO | |||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if len(services) == 0 { | 		if len(services) == 0 { | ||||||
| 			return nil, ErrNotFound | 			return nil, register.ErrNotFound | ||||||
| 		} | 		} | ||||||
| 		return services, nil | 		return services, nil | ||||||
| 	} | 	} | ||||||
| @@ -311,17 +312,17 @@ func (m *memory) LookupService(ctx context.Context, name string, opts ...LookupO | |||||||
| 	// check the domain exists | 	// check the domain exists | ||||||
| 	services, ok := m.records[options.Domain] | 	services, ok := m.records[options.Domain] | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		return nil, ErrNotFound | 		return nil, register.ErrNotFound | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// check the service exists | 	// check the service exists | ||||||
| 	versions, ok := services[name] | 	versions, ok := services[name] | ||||||
| 	if !ok || len(versions) == 0 { | 	if !ok || len(versions) == 0 { | ||||||
| 		return nil, ErrNotFound | 		return nil, register.ErrNotFound | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// serialize the response | 	// serialize the response | ||||||
| 	result := make([]*Service, len(versions)) | 	result := make([]*register.Service, len(versions)) | ||||||
| 
 | 
 | ||||||
| 	var i int | 	var i int | ||||||
| 
 | 
 | ||||||
| @@ -333,19 +334,19 @@ func (m *memory) LookupService(ctx context.Context, name string, opts ...LookupO | |||||||
| 	return result, nil | 	return result, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (m *memory) ListServices(ctx context.Context, opts ...ListOption) ([]*Service, error) { | func (m *memory) ListServices(ctx context.Context, opts ...register.ListOption) ([]*register.Service, error) { | ||||||
| 	options := NewListOptions(opts...) | 	options := register.NewListOptions(opts...) | ||||||
| 
 | 
 | ||||||
| 	// if it's a wildcard domain, list from all domains | 	// if it's a wildcard domain, list from all domains | ||||||
| 	if options.Domain == WildcardDomain { | 	if options.Domain == register.WildcardDomain { | ||||||
| 		m.RLock() | 		m.RLock() | ||||||
| 		recs := m.records | 		recs := m.records | ||||||
| 		m.RUnlock() | 		m.RUnlock() | ||||||
| 
 | 
 | ||||||
| 		var services []*Service | 		var services []*register.Service | ||||||
| 
 | 
 | ||||||
| 		for domain := range recs { | 		for domain := range recs { | ||||||
| 			srvs, err := m.ListServices(ctx, append(opts, ListDomain(domain))...) | 			srvs, err := m.ListServices(ctx, append(opts, register.ListDomain(domain))...) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return nil, err | 				return nil, err | ||||||
| 			} | 			} | ||||||
| @@ -361,11 +362,11 @@ func (m *memory) ListServices(ctx context.Context, opts ...ListOption) ([]*Servi | |||||||
| 	// ensure the domain exists | 	// ensure the domain exists | ||||||
| 	services, ok := m.records[options.Domain] | 	services, ok := m.records[options.Domain] | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		return make([]*Service, 0), nil | 		return make([]*register.Service, 0), nil | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// serialize the result, each version counts as an individual service | 	// serialize the result, each version counts as an individual service | ||||||
| 	var result []*Service | 	var result []*register.Service | ||||||
| 
 | 
 | ||||||
| 	for _, service := range services { | 	for _, service := range services { | ||||||
| 		for _, version := range service { | 		for _, version := range service { | ||||||
| @@ -376,16 +377,16 @@ func (m *memory) ListServices(ctx context.Context, opts ...ListOption) ([]*Servi | |||||||
| 	return result, nil | 	return result, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (m *memory) Watch(ctx context.Context, opts ...WatchOption) (Watcher, error) { | func (m *memory) Watch(ctx context.Context, opts ...register.WatchOption) (register.Watcher, error) { | ||||||
| 	id, err := id.New() | 	id, err := id.New() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 	wo := NewWatchOptions(opts...) | 	wo := register.NewWatchOptions(opts...) | ||||||
| 	// construct the watcher | 	// construct the watcher | ||||||
| 	w := &watcher{ | 	w := &watcher{ | ||||||
| 		exit: make(chan bool), | 		exit: make(chan bool), | ||||||
| 		res:  make(chan *Result), | 		res:  make(chan *register.Result), | ||||||
| 		id:   id, | 		id:   id, | ||||||
| 		wo:   wo, | 		wo:   wo, | ||||||
| 	} | 	} | ||||||
| @@ -406,13 +407,13 @@ func (m *memory) String() string { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type watcher struct { | type watcher struct { | ||||||
| 	res  chan *Result | 	res  chan *register.Result | ||||||
| 	exit chan bool | 	exit chan bool | ||||||
| 	wo   WatchOptions | 	wo   register.WatchOptions | ||||||
| 	id   string | 	id   string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (m *watcher) Next() (*Result, error) { | func (m *watcher) Next() (*register.Result, error) { | ||||||
| 	for { | 	for { | ||||||
| 		select { | 		select { | ||||||
| 		case r := <-m.res: | 		case r := <-m.res: | ||||||
| @@ -429,15 +430,15 @@ func (m *watcher) Next() (*Result, error) { | |||||||
| 			if r.Service.Metadata != nil && len(r.Service.Metadata["domain"]) > 0 { | 			if r.Service.Metadata != nil && len(r.Service.Metadata["domain"]) > 0 { | ||||||
| 				domain = r.Service.Metadata["domain"] | 				domain = r.Service.Metadata["domain"] | ||||||
| 			} else { | 			} else { | ||||||
| 				domain = DefaultDomain | 				domain = register.DefaultDomain | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			// only send the event if watching the wildcard or this specific domain | 			// only send the event if watching the wildcard or this specific domain | ||||||
| 			if m.wo.Domain == WildcardDomain || m.wo.Domain == domain { | 			if m.wo.Domain == register.WildcardDomain || m.wo.Domain == domain { | ||||||
| 				return r, nil | 				return r, nil | ||||||
| 			} | 			} | ||||||
| 		case <-m.exit: | 		case <-m.exit: | ||||||
| 			return nil, ErrWatcherStopped | 			return nil, register.ErrWatcherStopped | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -451,7 +452,7 @@ func (m *watcher) Stop() { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func serviceToRecord(s *Service, ttl time.Duration) *record { | func serviceToRecord(s *register.Service, ttl time.Duration) *record { | ||||||
| 	metadata := make(map[string]string, len(s.Metadata)) | 	metadata := make(map[string]string, len(s.Metadata)) | ||||||
| 	for k, v := range s.Metadata { | 	for k, v := range s.Metadata { | ||||||
| 		metadata[k] = v | 		metadata[k] = v | ||||||
| @@ -466,7 +467,7 @@ func serviceToRecord(s *Service, ttl time.Duration) *record { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	endpoints := make([]*Endpoint, len(s.Endpoints)) | 	endpoints := make([]*register.Endpoint, len(s.Endpoints)) | ||||||
| 	for i, e := range s.Endpoints { | 	for i, e := range s.Endpoints { | ||||||
| 		endpoints[i] = e | 		endpoints[i] = e | ||||||
| 	} | 	} | ||||||
| @@ -480,7 +481,7 @@ func serviceToRecord(s *Service, ttl time.Duration) *record { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func recordToService(r *record, domain string) *Service { | func recordToService(r *record, domain string) *register.Service { | ||||||
| 	metadata := make(map[string]string, len(r.Metadata)) | 	metadata := make(map[string]string, len(r.Metadata)) | ||||||
| 	for k, v := range r.Metadata { | 	for k, v := range r.Metadata { | ||||||
| 		metadata[k] = v | 		metadata[k] = v | ||||||
| @@ -489,14 +490,14 @@ func recordToService(r *record, domain string) *Service { | |||||||
| 	// set the domain in metadata so it can be determined when a wildcard query is performed | 	// set the domain in metadata so it can be determined when a wildcard query is performed | ||||||
| 	metadata["domain"] = domain | 	metadata["domain"] = domain | ||||||
| 
 | 
 | ||||||
| 	endpoints := make([]*Endpoint, len(r.Endpoints)) | 	endpoints := make([]*register.Endpoint, len(r.Endpoints)) | ||||||
| 	for i, e := range r.Endpoints { | 	for i, e := range r.Endpoints { | ||||||
| 		md := make(map[string]string, len(e.Metadata)) | 		md := make(map[string]string, len(e.Metadata)) | ||||||
| 		for k, v := range e.Metadata { | 		for k, v := range e.Metadata { | ||||||
| 			md[k] = v | 			md[k] = v | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		endpoints[i] = &Endpoint{ | 		endpoints[i] = ®ister.Endpoint{ | ||||||
| 			Name:     e.Name, | 			Name:     e.Name, | ||||||
| 			Request:  e.Request, | 			Request:  e.Request, | ||||||
| 			Response: e.Response, | 			Response: e.Response, | ||||||
| @@ -504,7 +505,7 @@ func recordToService(r *record, domain string) *Service { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	nodes := make([]*Node, len(r.Nodes)) | 	nodes := make([]*register.Node, len(r.Nodes)) | ||||||
| 	i := 0 | 	i := 0 | ||||||
| 	for _, n := range r.Nodes { | 	for _, n := range r.Nodes { | ||||||
| 		md := make(map[string]string, len(n.Metadata)) | 		md := make(map[string]string, len(n.Metadata)) | ||||||
| @@ -512,7 +513,7 @@ func recordToService(r *record, domain string) *Service { | |||||||
| 			md[k] = v | 			md[k] = v | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		nodes[i] = &Node{ | 		nodes[i] = ®ister.Node{ | ||||||
| 			ID:       n.ID, | 			ID:       n.ID, | ||||||
| 			Address:  n.Address, | 			Address:  n.Address, | ||||||
| 			Metadata: md, | 			Metadata: md, | ||||||
| @@ -520,7 +521,7 @@ func recordToService(r *record, domain string) *Service { | |||||||
| 		i++ | 		i++ | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return &Service{ | 	return ®ister.Service{ | ||||||
| 		Name:      r.Name, | 		Name:      r.Name, | ||||||
| 		Version:   r.Version, | 		Version:   r.Version, | ||||||
| 		Metadata:  metadata, | 		Metadata:  metadata, | ||||||
| @@ -6,14 +6,16 @@ import ( | |||||||
| 	"sync" | 	"sync" | ||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
|  | 
 | ||||||
|  | 	"go.unistack.org/micro/v3/register" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var testData = map[string][]*Service{ | var testData = map[string][]*register.Service{ | ||||||
| 	"foo": { | 	"foo": { | ||||||
| 		{ | 		{ | ||||||
| 			Name:    "foo", | 			Name:    "foo", | ||||||
| 			Version: "1.0.0", | 			Version: "1.0.0", | ||||||
| 			Nodes: []*Node{ | 			Nodes: []*register.Node{ | ||||||
| 				{ | 				{ | ||||||
| 					ID:      "foo-1.0.0-123", | 					ID:      "foo-1.0.0-123", | ||||||
| 					Address: "localhost:9999", | 					Address: "localhost:9999", | ||||||
| @@ -27,7 +29,7 @@ var testData = map[string][]*Service{ | |||||||
| 		{ | 		{ | ||||||
| 			Name:    "foo", | 			Name:    "foo", | ||||||
| 			Version: "1.0.1", | 			Version: "1.0.1", | ||||||
| 			Nodes: []*Node{ | 			Nodes: []*register.Node{ | ||||||
| 				{ | 				{ | ||||||
| 					ID:      "foo-1.0.1-321", | 					ID:      "foo-1.0.1-321", | ||||||
| 					Address: "localhost:6666", | 					Address: "localhost:6666", | ||||||
| @@ -37,7 +39,7 @@ var testData = map[string][]*Service{ | |||||||
| 		{ | 		{ | ||||||
| 			Name:    "foo", | 			Name:    "foo", | ||||||
| 			Version: "1.0.3", | 			Version: "1.0.3", | ||||||
| 			Nodes: []*Node{ | 			Nodes: []*register.Node{ | ||||||
| 				{ | 				{ | ||||||
| 					ID:      "foo-1.0.3-345", | 					ID:      "foo-1.0.3-345", | ||||||
| 					Address: "localhost:8888", | 					Address: "localhost:8888", | ||||||
| @@ -49,7 +51,7 @@ var testData = map[string][]*Service{ | |||||||
| 		{ | 		{ | ||||||
| 			Name:    "bar", | 			Name:    "bar", | ||||||
| 			Version: "default", | 			Version: "default", | ||||||
| 			Nodes: []*Node{ | 			Nodes: []*register.Node{ | ||||||
| 				{ | 				{ | ||||||
| 					ID:      "bar-1.0.0-123", | 					ID:      "bar-1.0.0-123", | ||||||
| 					Address: "localhost:9999", | 					Address: "localhost:9999", | ||||||
| @@ -63,7 +65,7 @@ var testData = map[string][]*Service{ | |||||||
| 		{ | 		{ | ||||||
| 			Name:    "bar", | 			Name:    "bar", | ||||||
| 			Version: "latest", | 			Version: "latest", | ||||||
| 			Nodes: []*Node{ | 			Nodes: []*register.Node{ | ||||||
| 				{ | 				{ | ||||||
| 					ID:      "bar-1.0.1-321", | 					ID:      "bar-1.0.1-321", | ||||||
| 					Address: "localhost:6666", | 					Address: "localhost:6666", | ||||||
| @@ -78,7 +80,7 @@ func TestMemoryRegistry(t *testing.T) { | |||||||
| 	ctx := context.TODO() | 	ctx := context.TODO() | ||||||
| 	m := NewRegister() | 	m := NewRegister() | ||||||
| 
 | 
 | ||||||
| 	fn := func(k string, v []*Service) { | 	fn := func(k string, v []*register.Service) { | ||||||
| 		services, err := m.LookupService(ctx, k) | 		services, err := m.LookupService(ctx, k) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			t.Errorf("Unexpected error getting service %s: %v", k, err) | 			t.Errorf("Unexpected error getting service %s: %v", k, err) | ||||||
| @@ -155,8 +157,8 @@ func TestMemoryRegistry(t *testing.T) { | |||||||
| 	for _, v := range testData { | 	for _, v := range testData { | ||||||
| 		for _, service := range v { | 		for _, service := range v { | ||||||
| 			services, err := m.LookupService(ctx, service.Name) | 			services, err := m.LookupService(ctx, service.Name) | ||||||
| 			if err != ErrNotFound { | 			if err != register.ErrNotFound { | ||||||
| 				t.Errorf("Expected error: %v, got: %v", ErrNotFound, err) | 				t.Errorf("Expected error: %v, got: %v", register.ErrNotFound, err) | ||||||
| 			} | 			} | ||||||
| 			if len(services) != 0 { | 			if len(services) != 0 { | ||||||
| 				t.Errorf("Expected %d services for %s, got %d", 0, service.Name, len(services)) | 				t.Errorf("Expected %d services for %s, got %d", 0, service.Name, len(services)) | ||||||
| @@ -171,7 +173,7 @@ func TestMemoryRegistryTTL(t *testing.T) { | |||||||
| 
 | 
 | ||||||
| 	for _, v := range testData { | 	for _, v := range testData { | ||||||
| 		for _, service := range v { | 		for _, service := range v { | ||||||
| 			if err := m.Register(ctx, service, RegisterTTL(time.Millisecond)); err != nil { | 			if err := m.Register(ctx, service, register.RegisterTTL(time.Millisecond)); err != nil { | ||||||
| 				t.Fatal(err) | 				t.Fatal(err) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| @@ -200,7 +202,7 @@ func TestMemoryRegistryTTLConcurrent(t *testing.T) { | |||||||
| 	ctx := context.TODO() | 	ctx := context.TODO() | ||||||
| 	for _, v := range testData { | 	for _, v := range testData { | ||||||
| 		for _, service := range v { | 		for _, service := range v { | ||||||
| 			if err := m.Register(ctx, service, RegisterTTL(waitTime/2)); err != nil { | 			if err := m.Register(ctx, service, register.RegisterTTL(waitTime/2)); err != nil { | ||||||
| 				t.Fatal(err) | 				t.Fatal(err) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| @@ -249,34 +251,34 @@ func TestMemoryWildcard(t *testing.T) { | |||||||
| 	m := NewRegister() | 	m := NewRegister() | ||||||
| 	ctx := context.TODO() | 	ctx := context.TODO() | ||||||
| 
 | 
 | ||||||
| 	testSrv := &Service{Name: "foo", Version: "1.0.0"} | 	testSrv := ®ister.Service{Name: "foo", Version: "1.0.0"} | ||||||
| 
 | 
 | ||||||
| 	if err := m.Register(ctx, testSrv, RegisterDomain("one")); err != nil { | 	if err := m.Register(ctx, testSrv, register.RegisterDomain("one")); err != nil { | ||||||
| 		t.Fatalf("Register err: %v", err) | 		t.Fatalf("Register err: %v", err) | ||||||
| 	} | 	} | ||||||
| 	if err := m.Register(ctx, testSrv, RegisterDomain("two")); err != nil { | 	if err := m.Register(ctx, testSrv, register.RegisterDomain("two")); err != nil { | ||||||
| 		t.Fatalf("Register err: %v", err) | 		t.Fatalf("Register err: %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if recs, err := m.ListServices(ctx, ListDomain("one")); err != nil { | 	if recs, err := m.ListServices(ctx, register.ListDomain("one")); err != nil { | ||||||
| 		t.Errorf("List err: %v", err) | 		t.Errorf("List err: %v", err) | ||||||
| 	} else if len(recs) != 1 { | 	} else if len(recs) != 1 { | ||||||
| 		t.Errorf("Expected 1 record, got %v", len(recs)) | 		t.Errorf("Expected 1 record, got %v", len(recs)) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if recs, err := m.ListServices(ctx, ListDomain("*")); err != nil { | 	if recs, err := m.ListServices(ctx, register.ListDomain("*")); err != nil { | ||||||
| 		t.Errorf("List err: %v", err) | 		t.Errorf("List err: %v", err) | ||||||
| 	} else if len(recs) != 2 { | 	} else if len(recs) != 2 { | ||||||
| 		t.Errorf("Expected 2 records, got %v", len(recs)) | 		t.Errorf("Expected 2 records, got %v", len(recs)) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if recs, err := m.LookupService(ctx, testSrv.Name, LookupDomain("one")); err != nil { | 	if recs, err := m.LookupService(ctx, testSrv.Name, register.LookupDomain("one")); err != nil { | ||||||
| 		t.Errorf("Lookup err: %v", err) | 		t.Errorf("Lookup err: %v", err) | ||||||
| 	} else if len(recs) != 1 { | 	} else if len(recs) != 1 { | ||||||
| 		t.Errorf("Expected 1 record, got %v", len(recs)) | 		t.Errorf("Expected 1 record, got %v", len(recs)) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if recs, err := m.LookupService(ctx, testSrv.Name, LookupDomain("*")); err != nil { | 	if recs, err := m.LookupService(ctx, testSrv.Name, register.LookupDomain("*")); err != nil { | ||||||
| 		t.Errorf("Lookup err: %v", err) | 		t.Errorf("Lookup err: %v", err) | ||||||
| 	} else if len(recs) != 2 { | 	} else if len(recs) != 2 { | ||||||
| 		t.Errorf("Expected 2 records, got %v", len(recs)) | 		t.Errorf("Expected 2 records, got %v", len(recs)) | ||||||
| @@ -284,7 +286,7 @@ func TestMemoryWildcard(t *testing.T) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestWatcher(t *testing.T) { | func TestWatcher(t *testing.T) { | ||||||
| 	testSrv := &Service{Name: "foo", Version: "1.0.0"} | 	testSrv := ®ister.Service{Name: "foo", Version: "1.0.0"} | ||||||
| 
 | 
 | ||||||
| 	ctx := context.TODO() | 	ctx := context.TODO() | ||||||
| 	m := NewRegister() | 	m := NewRegister() | ||||||
							
								
								
									
										72
									
								
								register/noop.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								register/noop.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | |||||||
|  | package register | ||||||
|  |  | ||||||
|  | import "context" | ||||||
|  |  | ||||||
|  | type noop struct { | ||||||
|  | 	opts Options | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewRegister(opts ...Option) Register { | ||||||
|  | 	return &noop{ | ||||||
|  | 		opts: NewOptions(opts...), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noop) Name() string { | ||||||
|  | 	return n.opts.Name | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noop) Init(opts ...Option) error { | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&n.opts) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noop) Options() Options { | ||||||
|  | 	return n.opts | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noop) Connect(ctx context.Context) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noop) Disconnect(ctx context.Context) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noop) Register(ctx context.Context, service *Service, option ...RegisterOption) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noop) Deregister(ctx context.Context, service *Service, option ...DeregisterOption) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noop) LookupService(ctx context.Context, s string, option ...LookupOption) ([]*Service, error) { | ||||||
|  | 	return nil, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noop) ListServices(ctx context.Context, option ...ListOption) ([]*Service, error) { | ||||||
|  | 	return nil, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noop) Watch(ctx context.Context, opts ...WatchOption) (Watcher, error) { | ||||||
|  | 	wOpts := NewWatchOptions(opts...) | ||||||
|  |  | ||||||
|  | 	return &watcher{wo: wOpts}, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (n *noop) String() string { | ||||||
|  | 	return "noop" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type watcher struct { | ||||||
|  | 	wo WatchOptions | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *watcher) Next() (*Result, error) { | ||||||
|  | 	return nil, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (m *watcher) Stop() {} | ||||||
| @@ -18,7 +18,7 @@ var DefaultDomain = "micro" | |||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	// DefaultRegister is the global default register | 	// DefaultRegister is the global default register | ||||||
| 	DefaultRegister = NewRegister() | 	DefaultRegister Register = NewRegister() | ||||||
| 	// ErrNotFound returned when LookupService is called and no services found | 	// ErrNotFound returned when LookupService is called and no services found | ||||||
| 	ErrNotFound = errors.New("service not found") | 	ErrNotFound = errors.New("service not found") | ||||||
| 	// ErrWatcherStopped returned when when watcher is stopped | 	// ErrWatcherStopped returned when when watcher is stopped | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| // Package resolver resolves network names to addresses | // Package resolver resolves network names to addresses | ||||||
| package resolver // import "go.unistack.org/micro/v3/resolver" | package resolver | ||||||
|  |  | ||||||
| // Resolver is network resolver. It's used to find network nodes | // Resolver is network resolver. It's used to find network nodes | ||||||
| // via the name to connect to. This is done based on Network.Name(). | // via the name to connect to. This is done based on Network.Name(). | ||||||
|   | |||||||
| @@ -7,7 +7,7 @@ import ( | |||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	// DefaultRouter is the global default router | 	// DefaultRouter is the global default router | ||||||
| 	DefaultRouter = NewRouter() | 	DefaultRouter Router = NewRouter() | ||||||
| 	// DefaultNetwork is default micro network | 	// DefaultNetwork is default micro network | ||||||
| 	DefaultNetwork = "micro" | 	DefaultNetwork = "micro" | ||||||
| 	// ErrRouteNotFound is returned when no route was found in the routing table | 	// ErrRouteNotFound is returned when no route was found in the routing table | ||||||
|   | |||||||
							
								
								
									
										22
									
								
								semconv/broker.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								semconv/broker.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | package semconv | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	// PublishMessageDurationSeconds specifies meter metric name | ||||||
|  | 	PublishMessageDurationSeconds = "publish_message_duration_seconds" | ||||||
|  | 	// PublishMessageLatencyMicroseconds specifies meter metric name | ||||||
|  | 	PublishMessageLatencyMicroseconds = "publish_message_latency_microseconds" | ||||||
|  | 	// PublishMessageTotal specifies meter metric name | ||||||
|  | 	PublishMessageTotal = "publish_message_total" | ||||||
|  | 	// PublishMessageInflight specifies meter metric name | ||||||
|  | 	PublishMessageInflight = "publish_message_inflight" | ||||||
|  | 	// SubscribeMessageDurationSeconds specifies meter metric name | ||||||
|  | 	SubscribeMessageDurationSeconds = "subscribe_message_duration_seconds" | ||||||
|  | 	// SubscribeMessageLatencyMicroseconds specifies meter metric name | ||||||
|  | 	SubscribeMessageLatencyMicroseconds = "subscribe_message_latency_microseconds" | ||||||
|  | 	// SubscribeMessageTotal specifies meter metric name | ||||||
|  | 	SubscribeMessageTotal = "subscribe_message_total" | ||||||
|  | 	// SubscribeMessageInflight specifies meter metric name | ||||||
|  | 	SubscribeMessageInflight = "subscribe_message_inflight" | ||||||
|  | 	// BrokerGroupLag specifies broker lag | ||||||
|  | 	BrokerGroupLag = "broker_group_lag" | ||||||
|  | ) | ||||||
							
								
								
									
										12
									
								
								semconv/cache.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								semconv/cache.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | package semconv | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	// CacheRequestDurationSeconds specifies meter metric name | ||||||
|  | 	CacheRequestDurationSeconds = "cache_request_duration_seconds" | ||||||
|  | 	// ClientRequestLatencyMicroseconds specifies meter metric name | ||||||
|  | 	CacheRequestLatencyMicroseconds = "cache_request_latency_microseconds" | ||||||
|  | 	// CacheRequestTotal specifies meter metric name | ||||||
|  | 	CacheRequestTotal = "cache_request_total" | ||||||
|  | 	// CacheRequestInflight specifies meter metric name | ||||||
|  | 	CacheRequestInflight = "cache_request_inflight" | ||||||
|  | ) | ||||||
							
								
								
									
										12
									
								
								semconv/client.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								semconv/client.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | package semconv | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	// ClientRequestDurationSeconds specifies meter metric name | ||||||
|  | 	ClientRequestDurationSeconds = "client_request_duration_seconds" | ||||||
|  | 	// ClientRequestLatencyMicroseconds specifies meter metric name | ||||||
|  | 	ClientRequestLatencyMicroseconds = "client_request_latency_microseconds" | ||||||
|  | 	// ClientRequestTotal specifies meter metric name | ||||||
|  | 	ClientRequestTotal = "client_request_total" | ||||||
|  | 	// ClientRequestInflight specifies meter metric name | ||||||
|  | 	ClientRequestInflight = "client_request_inflight" | ||||||
|  | ) | ||||||
							
								
								
									
										12
									
								
								semconv/server.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								semconv/server.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | |||||||
|  | package semconv | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	// ServerRequestDurationSeconds specifies meter metric name | ||||||
|  | 	ServerRequestDurationSeconds = "server_request_duration_seconds" | ||||||
|  | 	// ServerRequestLatencyMicroseconds specifies meter metric name | ||||||
|  | 	ServerRequestLatencyMicroseconds = "server_request_latency_microseconds" | ||||||
|  | 	// ServerRequestTotal specifies meter metric name | ||||||
|  | 	ServerRequestTotal = "server_request_total" | ||||||
|  | 	// ServerRequestInflight specifies meter metric name | ||||||
|  | 	ServerRequestInflight = "server_request_inflight" | ||||||
|  | ) | ||||||
| @@ -15,6 +15,7 @@ import ( | |||||||
| 	"go.unistack.org/micro/v3/network/transport" | 	"go.unistack.org/micro/v3/network/transport" | ||||||
| 	"go.unistack.org/micro/v3/options" | 	"go.unistack.org/micro/v3/options" | ||||||
| 	"go.unistack.org/micro/v3/register" | 	"go.unistack.org/micro/v3/register" | ||||||
|  | 	msync "go.unistack.org/micro/v3/sync" | ||||||
| 	"go.unistack.org/micro/v3/tracer" | 	"go.unistack.org/micro/v3/tracer" | ||||||
| 	"go.unistack.org/micro/v3/util/id" | 	"go.unistack.org/micro/v3/util/id" | ||||||
| ) | ) | ||||||
| @@ -47,7 +48,7 @@ type Options struct { | |||||||
| 	// Listener may be passed if already created | 	// Listener may be passed if already created | ||||||
| 	Listener net.Listener | 	Listener net.Listener | ||||||
| 	// Wait group | 	// Wait group | ||||||
| 	Wait *sync.WaitGroup | 	Wait *msync.WaitGroup | ||||||
| 	// TLSConfig specifies tls.Config for secure serving | 	// TLSConfig specifies tls.Config for secure serving | ||||||
| 	TLSConfig *tls.Config | 	TLSConfig *tls.Config | ||||||
| 	// Metadata holds the server metadata | 	// Metadata holds the server metadata | ||||||
| @@ -86,6 +87,8 @@ type Options struct { | |||||||
| 	DeregisterAttempts int | 	DeregisterAttempts int | ||||||
| 	// Hooks may contains SubscriberWrapper, HandlerWrapper or Server func wrapper | 	// Hooks may contains SubscriberWrapper, HandlerWrapper or Server func wrapper | ||||||
| 	Hooks options.Hooks | 	Hooks options.Hooks | ||||||
|  | 	// GracefulTimeout timeout for graceful stop server | ||||||
|  | 	GracefulTimeout time.Duration | ||||||
| } | } | ||||||
|  |  | ||||||
| // NewOptions returns new options struct with default or passed values | // NewOptions returns new options struct with default or passed values | ||||||
| @@ -108,6 +111,7 @@ func NewOptions(opts ...Option) Options { | |||||||
| 		Version:          DefaultVersion, | 		Version:          DefaultVersion, | ||||||
| 		ID:               id.Must(), | 		ID:               id.Must(), | ||||||
| 		Namespace:        DefaultNamespace, | 		Namespace:        DefaultNamespace, | ||||||
|  | 		GracefulTimeout:  DefaultGracefulTimeout, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for _, o := range opts { | 	for _, o := range opts { | ||||||
| @@ -279,7 +283,7 @@ func Wait(wg *sync.WaitGroup) Option { | |||||||
| 		if wg == nil { | 		if wg == nil { | ||||||
| 			wg = new(sync.WaitGroup) | 			wg = new(sync.WaitGroup) | ||||||
| 		} | 		} | ||||||
| 		o.Wait = wg | 		o.Wait = msync.WrapWaitGroup(wg) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -321,6 +325,13 @@ func Listener(l net.Listener) Option { | |||||||
| // HandlerOption func | // HandlerOption func | ||||||
| type HandlerOption func(*HandlerOptions) | type HandlerOption func(*HandlerOptions) | ||||||
|  |  | ||||||
|  | // GracefulTimeout duration | ||||||
|  | func GracefulTimeout(td time.Duration) Option { | ||||||
|  | 	return func(o *Options) { | ||||||
|  | 		o.GracefulTimeout = td | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| // HandlerOptions struct | // HandlerOptions struct | ||||||
| type HandlerOptions struct { | type HandlerOptions struct { | ||||||
| 	// Context holds external options | 	// Context holds external options | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| // DefaultServer default server | // DefaultServer default server | ||||||
| var DefaultServer = NewServer() | var DefaultServer Server = NewServer() | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| 	// DefaultAddress will be used if no address passed, use secure localhost | 	// DefaultAddress will be used if no address passed, use secure localhost | ||||||
| @@ -34,6 +34,8 @@ var ( | |||||||
| 	DefaultMaxMsgRecvSize = 1024 * 1024 * 4 // 4Mb | 	DefaultMaxMsgRecvSize = 1024 * 1024 * 4 // 4Mb | ||||||
| 	// DefaultMaxMsgSendSize holds default max send size | 	// DefaultMaxMsgSendSize holds default max send size | ||||||
| 	DefaultMaxMsgSendSize = 1024 * 1024 * 4 // 4Mb | 	DefaultMaxMsgSendSize = 1024 * 1024 * 4 // 4Mb | ||||||
|  | 	// DefaultGracefulTimeout default time for graceful stop | ||||||
|  | 	DefaultGracefulTimeout = 5 * time.Second | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // Server is a simple micro server abstraction | // Server is a simple micro server abstraction | ||||||
|   | |||||||
							
								
								
									
										76
									
								
								service.go
									
									
									
									
									
								
							
							
						
						
									
										76
									
								
								service.go
									
									
									
									
									
								
							| @@ -72,8 +72,8 @@ func RegisterSubscriber(topic string, s server.Server, h interface{}, opts ...se | |||||||
| } | } | ||||||
|  |  | ||||||
| type service struct { | type service struct { | ||||||
| 	sync.RWMutex |  | ||||||
| 	opts Options | 	opts Options | ||||||
|  | 	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. | ||||||
| @@ -376,71 +376,15 @@ func (s *service) Run() error { | |||||||
| 	return s.Stop() | 	return s.Stop() | ||||||
| } | } | ||||||
|  |  | ||||||
| func getNameIndex(n string, ifaces interface{}) int { | type Namer interface { | ||||||
| 	switch values := ifaces.(type) { | 	Name() string | ||||||
| 	case []router.Router: | } | ||||||
| 		for idx, iface := range values { |  | ||||||
| 			if iface.Name() == n { |  | ||||||
| 				return idx |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	case []register.Register: |  | ||||||
| 		for idx, iface := range values { |  | ||||||
| 			if iface.Name() == n { |  | ||||||
| 				return idx |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	case []store.Store: |  | ||||||
| 		for idx, iface := range values { |  | ||||||
| 			if iface.Name() == n { |  | ||||||
| 				return idx |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	case []tracer.Tracer: |  | ||||||
| 		for idx, iface := range values { |  | ||||||
| 			if iface.Name() == n { |  | ||||||
| 				return idx |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	case []server.Server: |  | ||||||
| 		for idx, iface := range values { |  | ||||||
| 			if iface.Name() == n { |  | ||||||
| 				return idx |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	case []config.Config: |  | ||||||
| 		for idx, iface := range values { |  | ||||||
| 			if iface.Name() == n { |  | ||||||
| 				return idx |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	case []meter.Meter: |  | ||||||
| 		for idx, iface := range values { |  | ||||||
| 			if iface.Name() == n { |  | ||||||
| 				return idx |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	case []broker.Broker: |  | ||||||
| 		for idx, iface := range values { |  | ||||||
| 			if iface.Name() == n { |  | ||||||
| 				return idx |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	case []client.Client: |  | ||||||
| 		for idx, iface := range values { |  | ||||||
| 			if iface.Name() == n { |  | ||||||
| 				return idx |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		/* |  | ||||||
| 			case []logger.Logger: |  | ||||||
| 					for idx, iface := range values { |  | ||||||
| 						if iface.Name() == n { |  | ||||||
| 							return idx |  | ||||||
| 						} |  | ||||||
| 					} |  | ||||||
| 		*/ |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
|  | func getNameIndex[T Namer](n string, ifaces []T) int { | ||||||
|  | 	for idx, iface := range ifaces { | ||||||
|  | 		if iface.Name() == n { | ||||||
|  | 			return idx | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
| 	return 0 | 	return 0 | ||||||
| } | } | ||||||
|   | |||||||
| @@ -22,13 +22,14 @@ func TestClient(t *testing.T) { | |||||||
| 	c2 := client.NewClient(client.Name("test2")) | 	c2 := client.NewClient(client.Name("test2")) | ||||||
|  |  | ||||||
| 	svc := NewService(Client(c1, c2)) | 	svc := NewService(Client(c1, c2)) | ||||||
|  |  | ||||||
| 	if err := svc.Init(); err != nil { | 	if err := svc.Init(); err != nil { | ||||||
| 		t.Fatal(err) | 		t.Fatal(err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	x1 := svc.Client("test2") | 	x1 := svc.Client("test2") | ||||||
| 	if x1.Name() != "test2" { | 	if x1.Name() != "test2" { | ||||||
| 		t.Fatal("invalid client") | 		t.Fatalf("invalid client %#+v", svc.Options().Clients) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -40,12 +41,11 @@ func (ti *testItem) Name() string { | |||||||
| 	return ti.name | 	return ti.name | ||||||
| } | } | ||||||
|  |  | ||||||
| func TestGetNameIndex(t *testing.T) { | func Test_getNameIndex(t *testing.T) { | ||||||
| 	item1 := &testItem{name: "first"} | 	items := []*testItem{{name: "test1"}, {name: "test2"}} | ||||||
| 	item2 := &testItem{name: "second"} | 	idx := getNameIndex("test2", items) | ||||||
| 	items := []interface{}{item1, item2} | 	if items[idx].Name() != "test2" { | ||||||
| 	if idx := getNameIndex("second", items); idx != 1 { | 		t.Fatal("getNameIndex wrong") | ||||||
| 		t.Fatalf("getNameIndex func error, item not found") |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -144,6 +144,10 @@ type ReadOptions struct { | |||||||
| 	Context context.Context | 	Context context.Context | ||||||
| 	// Namespace holds namespace | 	// Namespace holds namespace | ||||||
| 	Namespace string | 	Namespace string | ||||||
|  | 	// Name holds mnemonic name | ||||||
|  | 	Name string | ||||||
|  | 	// Timeout specifies max timeout for operation | ||||||
|  | 	Timeout time.Duration | ||||||
| } | } | ||||||
|  |  | ||||||
| // NewReadOptions fills ReadOptions struct with opts slice | // NewReadOptions fills ReadOptions struct with opts slice | ||||||
| @@ -158,6 +162,20 @@ func NewReadOptions(opts ...ReadOption) ReadOptions { | |||||||
| // ReadOption sets values in ReadOptions | // ReadOption sets values in ReadOptions | ||||||
| type ReadOption func(r *ReadOptions) | type ReadOption func(r *ReadOptions) | ||||||
|  |  | ||||||
|  | // ReadTimeout pass timeout to ReadOptions | ||||||
|  | func ReadTimeout(td time.Duration) ReadOption { | ||||||
|  | 	return func(o *ReadOptions) { | ||||||
|  | 		o.Timeout = td | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ReadName pass name to ReadOptions | ||||||
|  | func ReadName(name string) ReadOption { | ||||||
|  | 	return func(o *ReadOptions) { | ||||||
|  | 		o.Name = name | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| // ReadContext pass context.Context to ReadOptions | // ReadContext pass context.Context to ReadOptions | ||||||
| func ReadContext(ctx context.Context) ReadOption { | func ReadContext(ctx context.Context) ReadOption { | ||||||
| 	return func(o *ReadOptions) { | 	return func(o *ReadOptions) { | ||||||
| @@ -180,6 +198,10 @@ type WriteOptions struct { | |||||||
| 	Metadata metadata.Metadata | 	Metadata metadata.Metadata | ||||||
| 	// Namespace holds namespace | 	// Namespace holds namespace | ||||||
| 	Namespace string | 	Namespace string | ||||||
|  | 	// Name holds mnemonic name | ||||||
|  | 	Name string | ||||||
|  | 	// Timeout specifies max timeout for operation | ||||||
|  | 	Timeout time.Duration | ||||||
| 	// TTL specifies key TTL | 	// TTL specifies key TTL | ||||||
| 	TTL time.Duration | 	TTL time.Duration | ||||||
| } | } | ||||||
| @@ -224,12 +246,30 @@ func WriteNamespace(ns string) WriteOption { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // WriteName pass name to WriteOptions | ||||||
|  | func WriteName(name string) WriteOption { | ||||||
|  | 	return func(o *WriteOptions) { | ||||||
|  | 		o.Name = name | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // WriteTimeout pass timeout to WriteOptions | ||||||
|  | func WriteTimeout(td time.Duration) WriteOption { | ||||||
|  | 	return func(o *WriteOptions) { | ||||||
|  | 		o.Timeout = td | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| // DeleteOptions configures an individual Delete operation | // DeleteOptions configures an individual Delete operation | ||||||
| type DeleteOptions struct { | type DeleteOptions struct { | ||||||
| 	// Context holds external options | 	// Context holds external options | ||||||
| 	Context context.Context | 	Context context.Context | ||||||
| 	// Namespace holds namespace | 	// Namespace holds namespace | ||||||
| 	Namespace string | 	Namespace string | ||||||
|  | 	// Name holds mnemonic name | ||||||
|  | 	Name string | ||||||
|  | 	// Timeout specifies max timeout for operation | ||||||
|  | 	Timeout time.Duration | ||||||
| } | } | ||||||
|  |  | ||||||
| // NewDeleteOptions fills DeleteOptions struct with opts slice | // NewDeleteOptions fills DeleteOptions struct with opts slice | ||||||
| @@ -258,14 +298,32 @@ func DeleteNamespace(ns string) DeleteOption { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // DeleteName pass name to DeleteOptions | ||||||
|  | func DeleteName(name string) DeleteOption { | ||||||
|  | 	return func(o *DeleteOptions) { | ||||||
|  | 		o.Name = name | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // DeleteTimeout pass timeout to DeleteOptions | ||||||
|  | func DeleteTimeout(td time.Duration) DeleteOption { | ||||||
|  | 	return func(o *DeleteOptions) { | ||||||
|  | 		o.Timeout = td | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| // ListOptions configures an individual List operation | // ListOptions configures an individual List operation | ||||||
| type ListOptions struct { | type ListOptions struct { | ||||||
| 	Context   context.Context | 	Context   context.Context | ||||||
| 	Prefix    string | 	Prefix    string | ||||||
| 	Suffix    string | 	Suffix    string | ||||||
| 	Namespace string | 	Namespace string | ||||||
| 	Limit     uint | 	// Name holds mnemonic name | ||||||
| 	Offset    uint | 	Name   string | ||||||
|  | 	Limit  uint | ||||||
|  | 	Offset uint | ||||||
|  | 	// Timeout specifies max timeout for operation | ||||||
|  | 	Timeout time.Duration | ||||||
| } | } | ||||||
|  |  | ||||||
| // NewListOptions fills ListOptions struct with opts slice | // NewListOptions fills ListOptions struct with opts slice | ||||||
| @@ -322,12 +380,23 @@ func ListNamespace(ns string) ListOption { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // ListTimeout pass timeout to ListOptions | ||||||
|  | func ListTimeout(td time.Duration) ListOption { | ||||||
|  | 	return func(o *ListOptions) { | ||||||
|  | 		o.Timeout = td | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| // ExistsOptions holds options for Exists method | // ExistsOptions holds options for Exists method | ||||||
| type ExistsOptions struct { | type ExistsOptions struct { | ||||||
| 	// Context holds external options | 	// Context holds external options | ||||||
| 	Context context.Context | 	Context context.Context | ||||||
| 	// Namespace contains namespace | 	// Namespace contains namespace | ||||||
| 	Namespace string | 	Namespace string | ||||||
|  | 	// Name holds mnemonic name | ||||||
|  | 	Name string | ||||||
|  | 	// Timeout specifies max timeout for operation | ||||||
|  | 	Timeout time.Duration | ||||||
| } | } | ||||||
|  |  | ||||||
| // ExistsOption specifies Exists call options | // ExistsOption specifies Exists call options | ||||||
| @@ -358,6 +427,20 @@ func ExistsNamespace(ns string) ExistsOption { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // ExistsName pass name to exist options | ||||||
|  | func ExistsName(name string) ExistsOption { | ||||||
|  | 	return func(o *ExistsOptions) { | ||||||
|  | 		o.Name = name | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ExistsTimeout timeout to ListOptions | ||||||
|  | func ExistsTimeout(td time.Duration) ExistsOption { | ||||||
|  | 	return func(o *ExistsOptions) { | ||||||
|  | 		o.Timeout = td | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
| // WrapStore adds a store Wrapper to a list of options passed into the store | // WrapStore adds a store Wrapper to a list of options passed into the store | ||||||
| func WrapStore(w Wrapper) Option { | func WrapStore(w Wrapper) Option { | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ var ( | |||||||
| 	// ErrInvalidKey is returned when a key has empty or have invalid format | 	// ErrInvalidKey is returned when a key has empty or have invalid format | ||||||
| 	ErrInvalidKey = errors.New("invalid key") | 	ErrInvalidKey = errors.New("invalid key") | ||||||
| 	// DefaultStore is the global default store | 	// DefaultStore is the global default store | ||||||
| 	DefaultStore = NewStore() | 	DefaultStore Store = NewStore() | ||||||
| 	// DefaultSeparator is the gloabal default key parts separator | 	// DefaultSeparator is the gloabal default key parts separator | ||||||
| 	DefaultSeparator = "/" | 	DefaultSeparator = "/" | ||||||
| ) | ) | ||||||
|   | |||||||
							
								
								
									
										69
									
								
								sync/waitgroup.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								sync/waitgroup.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | |||||||
|  | package sync | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"sync" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type WaitGroup struct { | ||||||
|  | 	wg *sync.WaitGroup | ||||||
|  | 	c  int | ||||||
|  | 	mu sync.Mutex | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func WrapWaitGroup(wg *sync.WaitGroup) *WaitGroup { | ||||||
|  | 	g := &WaitGroup{ | ||||||
|  | 		wg: wg, | ||||||
|  | 	} | ||||||
|  | 	return g | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewWaitGroup() *WaitGroup { | ||||||
|  | 	var wg sync.WaitGroup | ||||||
|  | 	return WrapWaitGroup(&wg) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (g *WaitGroup) Add(n int) { | ||||||
|  | 	g.mu.Lock() | ||||||
|  | 	g.c += n | ||||||
|  | 	g.wg.Add(n) | ||||||
|  | 	g.mu.Unlock() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (g *WaitGroup) Done() { | ||||||
|  | 	g.mu.Lock() | ||||||
|  | 	g.c += -1 | ||||||
|  | 	g.wg.Add(-1) | ||||||
|  | 	g.mu.Unlock() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (g *WaitGroup) Wait() { | ||||||
|  | 	g.wg.Wait() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (g *WaitGroup) WaitContext(ctx context.Context) { | ||||||
|  | 	done := make(chan struct{}) | ||||||
|  | 	go func() { | ||||||
|  | 		g.wg.Wait() | ||||||
|  | 		close(done) | ||||||
|  | 	}() | ||||||
|  |  | ||||||
|  | 	select { | ||||||
|  | 	case <-ctx.Done(): | ||||||
|  | 		g.mu.Lock() | ||||||
|  | 		g.wg.Add(-g.c) | ||||||
|  | 		<-done | ||||||
|  | 		g.wg.Add(g.c) | ||||||
|  | 		g.mu.Unlock() | ||||||
|  | 		return | ||||||
|  | 	case <-done: | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (g *WaitGroup) Waiters() int { | ||||||
|  | 	g.mu.Lock() | ||||||
|  | 	c := g.c | ||||||
|  | 	g.mu.Unlock() | ||||||
|  | 	return c | ||||||
|  | } | ||||||
							
								
								
									
										37
									
								
								sync/waitgroup_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								sync/waitgroup_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | |||||||
|  | package sync | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"testing" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestWaitGroupContext(t *testing.T) { | ||||||
|  | 	wg := NewWaitGroup() | ||||||
|  | 	_ = t | ||||||
|  | 	wg.Add(1) | ||||||
|  | 	ctx, cancel := context.WithTimeout(context.TODO(), 1*time.Second) | ||||||
|  | 	defer cancel() | ||||||
|  | 	wg.WaitContext(ctx) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestWaitGroupReuse(t *testing.T) { | ||||||
|  | 	wg := NewWaitGroup() | ||||||
|  | 	defer func() { | ||||||
|  | 		if wg.Waiters() != 0 { | ||||||
|  | 			t.Fatal("lost goroutines") | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  |  | ||||||
|  | 	wg.Add(1) | ||||||
|  | 	defer wg.Done() | ||||||
|  | 	ctx, cancel := context.WithTimeout(context.TODO(), 1*time.Second) | ||||||
|  | 	defer cancel() | ||||||
|  | 	wg.WaitContext(ctx) | ||||||
|  |  | ||||||
|  | 	wg.Add(1) | ||||||
|  | 	defer wg.Done() | ||||||
|  | 	ctx, cancel = context.WithTimeout(context.TODO(), 1*time.Second) | ||||||
|  | 	defer cancel() | ||||||
|  | 	wg.WaitContext(ctx) | ||||||
|  | } | ||||||
							
								
								
									
										139
									
								
								tracer/memory/memory.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								tracer/memory/memory.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,139 @@ | |||||||
|  | package memory | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"go.unistack.org/micro/v3/tracer" | ||||||
|  | 	"go.unistack.org/micro/v3/util/id" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var _ tracer.Tracer = (*Tracer)(nil) | ||||||
|  |  | ||||||
|  | type Tracer struct { | ||||||
|  | 	opts  tracer.Options | ||||||
|  | 	spans []tracer.Span | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *Tracer) Spans() []tracer.Span { | ||||||
|  | 	return t.spans | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *Tracer) Start(ctx context.Context, name string, opts ...tracer.SpanOption) (context.Context, tracer.Span) { | ||||||
|  | 	options := tracer.NewSpanOptions(opts...) | ||||||
|  | 	span := &Span{ | ||||||
|  | 		name:      name, | ||||||
|  | 		ctx:       ctx, | ||||||
|  | 		tracer:    t, | ||||||
|  | 		kind:      options.Kind, | ||||||
|  | 		startTime: time.Now(), | ||||||
|  | 	} | ||||||
|  | 	span.spanID.s, _ = id.New() | ||||||
|  | 	span.traceID.s, _ = id.New() | ||||||
|  | 	if span.ctx == nil { | ||||||
|  | 		span.ctx = context.Background() | ||||||
|  | 	} | ||||||
|  | 	t.spans = append(t.spans, span) | ||||||
|  | 	return tracer.NewSpanContext(ctx, span), span | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *Tracer) Flush(_ context.Context) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *Tracer) Init(opts ...tracer.Option) error { | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&t.opts) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (t *Tracer) Name() string { | ||||||
|  | 	return t.opts.Name | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type noopStringer struct { | ||||||
|  | 	s string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s noopStringer) String() string { | ||||||
|  | 	return s.s | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type Span struct { | ||||||
|  | 	ctx        context.Context | ||||||
|  | 	tracer     tracer.Tracer | ||||||
|  | 	name       string | ||||||
|  | 	statusMsg  string | ||||||
|  | 	startTime  time.Time | ||||||
|  | 	finishTime time.Time | ||||||
|  | 	traceID    noopStringer | ||||||
|  | 	spanID     noopStringer | ||||||
|  | 	events     []*Event | ||||||
|  | 	labels     []interface{} | ||||||
|  | 	logs       []interface{} | ||||||
|  | 	kind       tracer.SpanKind | ||||||
|  | 	status     tracer.SpanStatus | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *Span) Finish(_ ...tracer.SpanOption) { | ||||||
|  | 	s.finishTime = time.Now() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *Span) Context() context.Context { | ||||||
|  | 	return s.ctx | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *Span) Tracer() tracer.Tracer { | ||||||
|  | 	return s.tracer | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type Event struct { | ||||||
|  | 	name   string | ||||||
|  | 	labels []interface{} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *Span) AddEvent(name string, opts ...tracer.EventOption) { | ||||||
|  | 	options := tracer.NewEventOptions(opts...) | ||||||
|  | 	s.events = append(s.events, &Event{name: name, labels: options.Labels}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *Span) SetName(name string) { | ||||||
|  | 	s.name = name | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *Span) AddLogs(kv ...interface{}) { | ||||||
|  | 	s.logs = append(s.logs, kv...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *Span) AddLabels(kv ...interface{}) { | ||||||
|  | 	s.labels = append(s.labels, kv...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *Span) Kind() tracer.SpanKind { | ||||||
|  | 	return s.kind | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *Span) TraceID() string { | ||||||
|  | 	return s.traceID.String() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *Span) SpanID() string { | ||||||
|  | 	return s.spanID.String() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *Span) Status() (tracer.SpanStatus, string) { | ||||||
|  | 	return s.status, s.statusMsg | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *Span) SetStatus(st tracer.SpanStatus, msg string) { | ||||||
|  | 	s.status = st | ||||||
|  | 	s.statusMsg = msg | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // NewTracer returns new memory tracer | ||||||
|  | func NewTracer(opts ...tracer.Option) *Tracer { | ||||||
|  | 	return &Tracer{ | ||||||
|  | 		opts: tracer.NewOptions(opts...), | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										38
									
								
								tracer/memory/memory_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								tracer/memory/memory_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | package memory | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"context" | ||||||
|  | 	"fmt" | ||||||
|  | 	"strings" | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"go.unistack.org/micro/v3/logger" | ||||||
|  | 	"go.unistack.org/micro/v3/logger/slog" | ||||||
|  | 	"go.unistack.org/micro/v3/tracer" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestLoggerWithTracer(t *testing.T) { | ||||||
|  | 	ctx := context.TODO() | ||||||
|  | 	buf := bytes.NewBuffer(nil) | ||||||
|  | 	logger.DefaultLogger = slog.NewLogger(logger.WithOutput(buf)) | ||||||
|  |  | ||||||
|  | 	if err := logger.Init(); err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	var span tracer.Span | ||||||
|  | 	tr := NewTracer() | ||||||
|  | 	ctx, span = tr.Start(ctx, "test1") | ||||||
|  |  | ||||||
|  | 	logger.Error(ctx, "my test error", fmt.Errorf("error")) | ||||||
|  |  | ||||||
|  | 	if !strings.Contains(buf.String(), span.TraceID()) { | ||||||
|  | 		t.Fatalf("log does not contains trace id: %s", buf.Bytes()) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	_, _ = tr.Start(ctx, "test2") | ||||||
|  |  | ||||||
|  | 	for _, s := range tr.Spans() { | ||||||
|  | 		_ = s | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -2,6 +2,8 @@ package tracer | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
|  |  | ||||||
|  | 	"go.unistack.org/micro/v3/util/id" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var _ Tracer = (*noopTracer)(nil) | var _ Tracer = (*noopTracer)(nil) | ||||||
| @@ -24,6 +26,8 @@ func (t *noopTracer) Start(ctx context.Context, name string, opts ...SpanOption) | |||||||
| 		labels: options.Labels, | 		labels: options.Labels, | ||||||
| 		kind:   options.Kind, | 		kind:   options.Kind, | ||||||
| 	} | 	} | ||||||
|  | 	span.spanID.s, _ = id.New() | ||||||
|  | 	span.traceID.s, _ = id.New() | ||||||
| 	if span.ctx == nil { | 	if span.ctx == nil { | ||||||
| 		span.ctx = context.Background() | 		span.ctx = context.Background() | ||||||
| 	} | 	} | ||||||
| @@ -31,6 +35,14 @@ func (t *noopTracer) Start(ctx context.Context, name string, opts ...SpanOption) | |||||||
| 	return NewSpanContext(ctx, span), span | 	return NewSpanContext(ctx, span), span | ||||||
| } | } | ||||||
|  |  | ||||||
|  | type noopStringer struct { | ||||||
|  | 	s string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s noopStringer) String() string { | ||||||
|  | 	return s.s | ||||||
|  | } | ||||||
|  |  | ||||||
| func (t *noopTracer) Flush(ctx context.Context) error { | func (t *noopTracer) Flush(ctx context.Context) error { | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| @@ -56,6 +68,8 @@ type noopSpan struct { | |||||||
| 	tracer    Tracer | 	tracer    Tracer | ||||||
| 	name      string | 	name      string | ||||||
| 	statusMsg string | 	statusMsg string | ||||||
|  | 	traceID   noopStringer | ||||||
|  | 	spanID    noopStringer | ||||||
| 	events    []*noopEvent | 	events    []*noopEvent | ||||||
| 	labels    []interface{} | 	labels    []interface{} | ||||||
| 	logs      []interface{} | 	logs      []interface{} | ||||||
| @@ -63,7 +77,15 @@ type noopSpan struct { | |||||||
| 	status    SpanStatus | 	status    SpanStatus | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *noopSpan) Finish(opts ...SpanOption) { | func (s *noopSpan) TraceID() string { | ||||||
|  | 	return s.traceID.String() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *noopSpan) SpanID() string { | ||||||
|  | 	return s.spanID.String() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (s *noopSpan) Finish(_ ...SpanOption) { | ||||||
| } | } | ||||||
|  |  | ||||||
| func (s *noopSpan) Context() context.Context { | func (s *noopSpan) Context() context.Context { | ||||||
|   | |||||||
| @@ -100,13 +100,13 @@ type EventOption func(o *EventOptions) | |||||||
|  |  | ||||||
| func WithEventLabels(kv ...interface{}) EventOption { | func WithEventLabels(kv ...interface{}) EventOption { | ||||||
| 	return func(o *EventOptions) { | 	return func(o *EventOptions) { | ||||||
| 		o.Labels = kv | 		o.Labels = append(o.Labels, kv...) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func WithSpanLabels(kv ...interface{}) SpanOption { | func WithSpanLabels(kv ...interface{}) SpanOption { | ||||||
| 	return func(o *SpanOptions) { | 	return func(o *SpanOptions) { | ||||||
| 		o.Labels = kv | 		o.Labels = append(o.Labels, kv...) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -159,7 +159,8 @@ func NewSpanOptions(opts ...SpanOption) SpanOptions { | |||||||
| // NewOptions returns default options | // NewOptions returns default options | ||||||
| func NewOptions(opts ...Option) Options { | func NewOptions(opts ...Option) Options { | ||||||
| 	options := Options{ | 	options := Options{ | ||||||
| 		Logger: logger.DefaultLogger, | 		Logger:  logger.DefaultLogger, | ||||||
|  | 		Context: context.Background(), | ||||||
| 	} | 	} | ||||||
| 	for _, o := range opts { | 	for _, o := range opts { | ||||||
| 		o(&options) | 		o(&options) | ||||||
|   | |||||||
| @@ -3,12 +3,32 @@ package tracer // import "go.unistack.org/micro/v3/tracer" | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"context" | 	"context" | ||||||
| 	"fmt" |  | ||||||
| 	"sort" | 	"go.unistack.org/micro/v3/logger" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // DefaultTracer is the global default tracer | // DefaultTracer is the global default tracer | ||||||
| var DefaultTracer = NewTracer() | var DefaultTracer Tracer = NewTracer() | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	// TraceIDKey is the key used for the trace id in the log call | ||||||
|  | 	TraceIDKey = "trace-id" | ||||||
|  | 	// SpanIDKey is the key used for the span id in the log call | ||||||
|  | 	SpanIDKey = "span-id" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	logger.DefaultContextAttrFuncs = append(logger.DefaultContextAttrFuncs, | ||||||
|  | 		func(ctx context.Context) []interface{} { | ||||||
|  | 			if span, ok := SpanFromContext(ctx); ok { | ||||||
|  | 				return []interface{}{ | ||||||
|  | 					TraceIDKey, span.TraceID(), | ||||||
|  | 					SpanIDKey, span.SpanID(), | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			return nil | ||||||
|  | 		}) | ||||||
|  | } | ||||||
|  |  | ||||||
| // Tracer is an interface for distributed tracing | // Tracer is an interface for distributed tracing | ||||||
| type Tracer interface { | type Tracer interface { | ||||||
| @@ -43,38 +63,8 @@ type Span interface { | |||||||
| 	AddLogs(kv ...interface{}) | 	AddLogs(kv ...interface{}) | ||||||
| 	// Kind returns span kind | 	// Kind returns span kind | ||||||
| 	Kind() SpanKind | 	Kind() SpanKind | ||||||
| } | 	// TraceID returns trace id | ||||||
|  | 	TraceID() string | ||||||
| // sort labels alphabeticaly by label name | 	// SpanID returns span id | ||||||
| type byKey []interface{} | 	SpanID() string | ||||||
|  |  | ||||||
| func (k byKey) Len() int           { return len(k) / 2 } |  | ||||||
| func (k byKey) Less(i, j int) bool { return fmt.Sprintf("%s", k[i*2]) < fmt.Sprintf("%s", k[j*2]) } |  | ||||||
| func (k byKey) Swap(i, j int) { |  | ||||||
| 	k[i*2], k[j*2] = k[j*2], k[i*2] |  | ||||||
| 	k[i*2+1], k[j*2+1] = k[j*2+1], k[i*2+1] |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func UniqLabels(labels []interface{}) []interface{} { |  | ||||||
| 	if len(labels)%2 == 1 { |  | ||||||
| 		labels = labels[:len(labels)-1] |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(labels) > 2 { |  | ||||||
| 		sort.Sort(byKey(labels)) |  | ||||||
|  |  | ||||||
| 		idx := 0 |  | ||||||
| 		for { |  | ||||||
| 			if labels[idx] == labels[idx+2] { |  | ||||||
| 				copy(labels[idx:], labels[idx+2:]) |  | ||||||
| 				labels = labels[:len(labels)-2] |  | ||||||
| 			} else { |  | ||||||
| 				idx += 2 |  | ||||||
| 			} |  | ||||||
| 			if idx+2 >= len(labels) { |  | ||||||
| 				break |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return labels |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| package reflect // import "go.unistack.org/micro/v3/util/reflect" | package reflect | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"encoding/json" | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"reflect" | 	"reflect" | ||||||
| @@ -45,15 +46,23 @@ func SliceAppend(b bool) Option { | |||||||
|  |  | ||||||
| // Merge merges map[string]interface{} to destination struct | // Merge merges map[string]interface{} to destination struct | ||||||
| func Merge(dst interface{}, mp map[string]interface{}, opts ...Option) error { | func Merge(dst interface{}, mp map[string]interface{}, opts ...Option) error { | ||||||
| 	var err error |  | ||||||
| 	var sval reflect.Value |  | ||||||
| 	var fname string |  | ||||||
|  |  | ||||||
| 	options := Options{} | 	options := Options{} | ||||||
| 	for _, o := range opts { | 	for _, o := range opts { | ||||||
| 		o(&options) | 		o(&options) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if unmarshaler, ok := dst.(json.Unmarshaler); ok { | ||||||
|  | 		buf, err := json.Marshal(mp) | ||||||
|  | 		if err == nil { | ||||||
|  | 			err = unmarshaler.UnmarshalJSON(buf) | ||||||
|  | 		} | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var err error | ||||||
|  | 	var sval reflect.Value | ||||||
|  | 	var fname string | ||||||
|  |  | ||||||
| 	dviface := reflect.ValueOf(dst) | 	dviface := reflect.ValueOf(dst) | ||||||
| 	if dviface.Kind() == reflect.Ptr { | 	if dviface.Kind() == reflect.Ptr { | ||||||
| 		dviface = dviface.Elem() | 		dviface = dviface.Elem() | ||||||
|   | |||||||
							
								
								
									
										40
									
								
								util/sort/sort.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								util/sort/sort.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | |||||||
|  | package sort | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"sort" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // sort labels alphabeticaly by label name | ||||||
|  | type byKey []interface{} | ||||||
|  |  | ||||||
|  | func (k byKey) Len() int           { return len(k) / 2 } | ||||||
|  | func (k byKey) Less(i, j int) bool { return fmt.Sprintf("%s", k[i*2]) < fmt.Sprintf("%s", k[j*2]) } | ||||||
|  | func (k byKey) Swap(i, j int) { | ||||||
|  | 	k[i*2], k[j*2] = k[j*2], k[i*2] | ||||||
|  | 	k[i*2+1], k[j*2+1] = k[j*2+1], k[i*2+1] | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Uniq(labels []interface{}) []interface{} { | ||||||
|  | 	if len(labels)%2 == 1 { | ||||||
|  | 		labels = labels[:len(labels)-1] | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if len(labels) > 2 { | ||||||
|  | 		sort.Sort(byKey(labels)) | ||||||
|  |  | ||||||
|  | 		idx := 0 | ||||||
|  | 		for { | ||||||
|  | 			if labels[idx] == labels[idx+2] { | ||||||
|  | 				copy(labels[idx:], labels[idx+2:]) | ||||||
|  | 				labels = labels[:len(labels)-2] | ||||||
|  | 			} else { | ||||||
|  | 				idx += 2 | ||||||
|  | 			} | ||||||
|  | 			if idx+2 >= len(labels) { | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return labels | ||||||
|  | } | ||||||
| @@ -35,8 +35,8 @@ func TestUnmarshalJSON(t *testing.T) { | |||||||
| 	err = json.Unmarshal([]byte(`{"ttl":"1y"}`), v) | 	err = json.Unmarshal([]byte(`{"ttl":"1y"}`), v) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatal(err) | 		t.Fatal(err) | ||||||
| 	} else if v.TTL != 31536000000000000 { | 	} else if v.TTL != 31622400000000000 { | ||||||
| 		t.Fatalf("invalid duration %v != 31536000000000000", v.TTL) | 		t.Fatalf("invalid duration %v != 31622400000000000", v.TTL) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -55,7 +55,7 @@ func TestParseDuration(t *testing.T) { | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatalf("ParseDuration error: %v", err) | 		t.Fatalf("ParseDuration error: %v", err) | ||||||
| 	} | 	} | ||||||
| 	if td.String() != "8760h0m0s" { | 	if td.String() != "8784h0m0s" { | ||||||
| 		t.Fatalf("ParseDuration 1y != 8760h0m0s : %s", td.String()) | 		t.Fatalf("ParseDuration 1y != 8784h0m0s : %s", td.String()) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										25
									
								
								util/xpool/pool.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								util/xpool/pool.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | |||||||
|  | package pool | ||||||
|  |  | ||||||
|  | import "sync" | ||||||
|  |  | ||||||
|  | type Pool[T any] struct { | ||||||
|  | 	p *sync.Pool | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewPool[T any](fn func() T) Pool[T] { | ||||||
|  | 	return Pool[T]{ | ||||||
|  | 		p: &sync.Pool{ | ||||||
|  | 			New: func() interface{} { | ||||||
|  | 				return fn() | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p Pool[T]) Get() T { | ||||||
|  | 	return p.p.Get().(T) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (p Pool[T]) Put(t T) { | ||||||
|  | 	p.p.Put(t) | ||||||
|  | } | ||||||
							
								
								
									
										27
									
								
								util/xpool/pool_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								util/xpool/pool_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | package pool | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"strings" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func TestBytes(t *testing.T) { | ||||||
|  | 	p := NewPool(func() *bytes.Buffer { return bytes.NewBuffer(nil) }) | ||||||
|  | 	b := p.Get() | ||||||
|  | 	b.Write([]byte(`test`)) | ||||||
|  | 	if b.String() != "test" { | ||||||
|  | 		t.Fatal("pool not works") | ||||||
|  | 	} | ||||||
|  | 	p.Put(b) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func TestStrings(t *testing.T) { | ||||||
|  | 	p := NewPool(func() *strings.Builder { return &strings.Builder{} }) | ||||||
|  | 	b := p.Get() | ||||||
|  | 	b.Write([]byte(`test`)) | ||||||
|  | 	if b.String() != "test" { | ||||||
|  | 		t.Fatal("pool not works") | ||||||
|  | 	} | ||||||
|  | 	p.Put(b) | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user