Compare commits
No commits in common. "v3" and "v3.11.18" have entirely different histories.
@ -1,51 +0,0 @@
|
|||||||
name: coverage
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ main, v3, v4 ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ main, v3, v4 ]
|
|
||||||
# Allows you to run this workflow manually from the Actions tab
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
filter: 'blob:none'
|
|
||||||
|
|
||||||
- name: setup go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
cache-dependency-path: "**/*.sum"
|
|
||||||
go-version: 'stable'
|
|
||||||
|
|
||||||
- name: test coverage
|
|
||||||
run: |
|
|
||||||
go test -v -cover ./... -coverprofile coverage.out -coverpkg ./...
|
|
||||||
go tool cover -func coverage.out -o coverage.out
|
|
||||||
|
|
||||||
- name: coverage badge
|
|
||||||
uses: tj-actions/coverage-badge-go@v1
|
|
||||||
with:
|
|
||||||
green: 80
|
|
||||||
filename: coverage.out
|
|
||||||
|
|
||||||
- uses: stefanzweifel/git-auto-commit-action@v4
|
|
||||||
id: auto-commit-action
|
|
||||||
with:
|
|
||||||
commit_message: Apply Code Coverage Badge
|
|
||||||
skip_fetch: true
|
|
||||||
skip_checkout: true
|
|
||||||
file_pattern: ./README.md
|
|
||||||
|
|
||||||
- name: Push Changes
|
|
||||||
if: steps.auto-commit-action.outputs.changes_detected == 'true'
|
|
||||||
uses: ad-m/github-push-action@master
|
|
||||||
with:
|
|
||||||
github_token: ${{ github.token }}
|
|
||||||
branch: ${{ github.ref }}
|
|
@ -1,5 +1,4 @@
|
|||||||
# Micro
|
# Micro [![License](https://img.shields.io/:license-apache-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![Doc](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/unistack-org/micro/v3?tab=overview) [![Status](https://git.unistack.org/unistack-org/micro/actions/workflows/job_tests.yml/badge.svg?branch=v3)](https://git.unistack.org/unistack-org/micro/actions?query=workflow%3Abuild+branch%3Av3+event%3Apush) [![Lint](https://goreportcard.com/badge/go.unistack.org/micro/v3)](https://goreportcard.com/report/go.unistack.org/micro/v3)
|
||||||
![Coverage](https://img.shields.io/badge/Coverage-44.7%25-yellow)
|
|
||||||
|
|
||||||
Micro is a standard library for microservices.
|
Micro is a standard library for microservices.
|
||||||
|
|
||||||
|
@ -15,15 +15,6 @@ func FromContext(ctx context.Context) (Broker, bool) {
|
|||||||
return c, ok
|
return c, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustContext returns broker from passed context
|
|
||||||
func MustContext(ctx context.Context) Broker {
|
|
||||||
b, ok := FromContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
panic("missing broker")
|
|
||||||
}
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewContext savess broker in context
|
// NewContext savess broker in context
|
||||||
func NewContext(ctx context.Context, s Broker) context.Context {
|
func NewContext(ctx context.Context, s Broker) context.Context {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
|
@ -109,7 +109,7 @@ func (m *memoryBroker) Init(opts ...broker.Option) error {
|
|||||||
m.funcSubscribe = m.fnSubscribe
|
m.funcSubscribe = m.fnSubscribe
|
||||||
m.funcBatchSubscribe = m.fnBatchSubscribe
|
m.funcBatchSubscribe = m.fnBatchSubscribe
|
||||||
|
|
||||||
m.opts.Hooks.EachPrev(func(hook options.Hook) {
|
m.opts.Hooks.EachNext(func(hook options.Hook) {
|
||||||
switch h := hook.(type) {
|
switch h := hook.(type) {
|
||||||
case broker.HookPublish:
|
case broker.HookPublish:
|
||||||
m.funcPublish = h(m.funcPublish)
|
m.funcPublish = h(m.funcPublish)
|
||||||
|
@ -59,7 +59,7 @@ func (b *NoopBroker) Init(opts ...Option) error {
|
|||||||
b.funcSubscribe = b.fnSubscribe
|
b.funcSubscribe = b.fnSubscribe
|
||||||
b.funcBatchSubscribe = b.fnBatchSubscribe
|
b.funcBatchSubscribe = b.fnBatchSubscribe
|
||||||
|
|
||||||
b.opts.Hooks.EachPrev(func(hook options.Hook) {
|
b.opts.Hooks.EachNext(func(hook options.Hook) {
|
||||||
switch h := hook.(type) {
|
switch h := hook.(type) {
|
||||||
case HookPublish:
|
case HookPublish:
|
||||||
b.funcPublish = h(b.funcPublish)
|
b.funcPublish = h(b.funcPublish)
|
||||||
|
@ -15,15 +15,6 @@ func FromContext(ctx context.Context) (Client, bool) {
|
|||||||
return c, ok
|
return c, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustContext get client from context
|
|
||||||
func MustContext(ctx context.Context) Client {
|
|
||||||
c, ok := FromContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
panic("missing client")
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewContext put client in context
|
// NewContext put client in context
|
||||||
func NewContext(ctx context.Context, c Client) context.Context {
|
func NewContext(ctx context.Context, c Client) context.Context {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
|
@ -194,7 +194,7 @@ func (n *noopClient) Init(opts ...Option) error {
|
|||||||
n.funcPublish = n.fnPublish
|
n.funcPublish = n.fnPublish
|
||||||
n.funcBatchPublish = n.fnBatchPublish
|
n.funcBatchPublish = n.fnBatchPublish
|
||||||
|
|
||||||
n.opts.Hooks.EachPrev(func(hook options.Hook) {
|
n.opts.Hooks.EachNext(func(hook options.Hook) {
|
||||||
switch h := hook.(type) {
|
switch h := hook.(type) {
|
||||||
case HookCall:
|
case HookCall:
|
||||||
n.funcCall = h(n.funcCall)
|
n.funcCall = h(n.funcCall)
|
||||||
|
@ -3,8 +3,6 @@ package codec
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -56,22 +54,3 @@ func (m *RawMessage) UnmarshalJSON(data []byte) error {
|
|||||||
*m = append((*m)[0:0], data...)
|
*m = append((*m)[0:0], data...)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalYAML returns m as the JSON encoding of m.
|
|
||||||
func (m *RawMessage) MarshalYAML() ([]byte, error) {
|
|
||||||
if m == nil {
|
|
||||||
return []byte("null"), nil
|
|
||||||
} else if len(*m) == 0 {
|
|
||||||
return []byte("null"), nil
|
|
||||||
}
|
|
||||||
return *m, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalYAML sets *m to a copy of data.
|
|
||||||
func (m *RawMessage) UnmarshalYAML(n *yaml.Node) error {
|
|
||||||
if m == nil {
|
|
||||||
return errors.New("RawMessage UnmarshalYAML on nil pointer")
|
|
||||||
}
|
|
||||||
*m = append((*m)[0:0], []byte(n.Value)...)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
@ -15,15 +15,6 @@ func FromContext(ctx context.Context) (Codec, bool) {
|
|||||||
return c, ok
|
return c, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustContext returns codec from context
|
|
||||||
func MustContext(ctx context.Context) Codec {
|
|
||||||
c, ok := FromContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
panic("missing codec")
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewContext put codec in context
|
// NewContext put codec in context
|
||||||
func NewContext(ctx context.Context, c Codec) context.Context {
|
func NewContext(ctx context.Context, c Codec) context.Context {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package codec
|
package codec
|
||||||
|
|
||||||
import "gopkg.in/yaml.v3"
|
|
||||||
|
|
||||||
// Frame gives us the ability to define raw data to send over the pipes
|
// Frame gives us the ability to define raw data to send over the pipes
|
||||||
type Frame struct {
|
type Frame struct {
|
||||||
Data []byte
|
Data []byte
|
||||||
@ -22,17 +20,6 @@ func (m *Frame) UnmarshalJSON(data []byte) error {
|
|||||||
return m.Unmarshal(data)
|
return m.Unmarshal(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalYAML returns frame data
|
|
||||||
func (m *Frame) MarshalYAML() ([]byte, error) {
|
|
||||||
return m.Marshal()
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalYAML set frame data
|
|
||||||
func (m *Frame) UnmarshalYAML(n *yaml.Node) error {
|
|
||||||
m.Data = []byte(n.Value)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProtoMessage noop func
|
// ProtoMessage noop func
|
||||||
func (m *Frame) ProtoMessage() {}
|
func (m *Frame) ProtoMessage() {}
|
||||||
|
|
||||||
|
@ -15,15 +15,6 @@ func FromContext(ctx context.Context) (Config, bool) {
|
|||||||
return c, ok
|
return c, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustContext returns store from context
|
|
||||||
func MustContext(ctx context.Context) Config {
|
|
||||||
c, ok := FromContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
panic("missing config")
|
|
||||||
}
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewContext put store in context
|
// NewContext put store in context
|
||||||
func NewContext(ctx context.Context, c Config) context.Context {
|
func NewContext(ctx context.Context, c Config) context.Context {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
|
@ -37,7 +37,7 @@ func (c *defaultConfig) Init(opts ...Option) error {
|
|||||||
c.funcLoad = c.fnLoad
|
c.funcLoad = c.fnLoad
|
||||||
c.funcSave = c.fnSave
|
c.funcSave = c.fnSave
|
||||||
|
|
||||||
c.opts.Hooks.EachPrev(func(hook options.Hook) {
|
c.opts.Hooks.EachNext(func(hook options.Hook) {
|
||||||
switch h := hook.(type) {
|
switch h := hook.(type) {
|
||||||
case HookLoad:
|
case HookLoad:
|
||||||
c.funcLoad = h(c.funcLoad)
|
c.funcLoad = h(c.funcLoad)
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.unistack.org/micro/v3/config"
|
"go.unistack.org/micro/v3/config"
|
||||||
|
mid "go.unistack.org/micro/v3/util/id"
|
||||||
mtime "go.unistack.org/micro/v3/util/time"
|
mtime "go.unistack.org/micro/v3/util/time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -114,6 +115,8 @@ func TestDefault(t *testing.T) {
|
|||||||
|
|
||||||
if conf.IDValue == "" {
|
if conf.IDValue == "" {
|
||||||
t.Fatalf("id value empty")
|
t.Fatalf("id value empty")
|
||||||
|
} else if len(conf.IDValue) != mid.DefaultSize {
|
||||||
|
t.Fatalf("id value invalid: %s", conf.IDValue)
|
||||||
}
|
}
|
||||||
_ = conf
|
_ = conf
|
||||||
// t.Logf("%#+v\n", conf)
|
// t.Logf("%#+v\n", conf)
|
||||||
|
@ -15,15 +15,6 @@ func FromContext(ctx context.Context) (Flow, bool) {
|
|||||||
return c, ok
|
return c, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustContext returns Flow from context
|
|
||||||
func MustContext(ctx context.Context) Flow {
|
|
||||||
f, ok := FromContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
panic("missing flow")
|
|
||||||
}
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewContext stores Flow to context
|
// NewContext stores Flow to context
|
||||||
func NewContext(ctx context.Context, f Flow) context.Context {
|
func NewContext(ctx context.Context, f Flow) context.Context {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
|
15
go.mod
15
go.mod
@ -2,20 +2,20 @@ module go.unistack.org/micro/v3
|
|||||||
|
|
||||||
go 1.22.0
|
go 1.22.0
|
||||||
|
|
||||||
|
toolchain go1.23.4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
dario.cat/mergo v1.0.1
|
dario.cat/mergo v1.0.1
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.2
|
github.com/DATA-DOG/go-sqlmock v1.5.0
|
||||||
github.com/KimMachineGun/automemlimit v0.6.1
|
github.com/KimMachineGun/automemlimit v0.6.1
|
||||||
github.com/ash3in/uuidv8 v1.2.0
|
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/matoous/go-nanoid v1.5.1
|
|
||||||
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
|
||||||
go.uber.org/automaxprocs v1.6.0
|
go.uber.org/automaxprocs v1.6.0
|
||||||
go.unistack.org/micro-proto/v3 v3.4.1
|
go.unistack.org/micro-proto/v3 v3.4.1
|
||||||
golang.org/x/sync v0.10.0
|
golang.org/x/sync v0.10.0
|
||||||
google.golang.org/grpc v1.69.2
|
google.golang.org/grpc v1.68.1
|
||||||
google.golang.org/protobuf v1.36.1
|
google.golang.org/protobuf v1.35.2
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -31,13 +31,12 @@ require (
|
|||||||
github.com/opencontainers/runtime-spec v1.2.0 // indirect
|
github.com/opencontainers/runtime-spec v1.2.0 // indirect
|
||||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.13.1 // indirect
|
|
||||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||||
github.com/stretchr/testify v1.10.0 // indirect
|
github.com/stretchr/testify v1.10.0 // indirect
|
||||||
go.uber.org/goleak v1.3.0 // indirect
|
go.uber.org/goleak v1.3.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20241210194714-1829a127f884 // indirect
|
golang.org/x/exp v0.0.0-20241210194714-1829a127f884 // indirect
|
||||||
golang.org/x/net v0.33.0 // indirect
|
golang.org/x/net v0.32.0 // indirect
|
||||||
golang.org/x/sys v0.28.0 // indirect
|
golang.org/x/sys v0.28.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241216192217-9240e9c98484 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||||
)
|
)
|
||||||
|
29
go.sum
29
go.sum
@ -1,11 +1,9 @@
|
|||||||
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
||||||
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
|
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
|
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||||
github.com/KimMachineGun/automemlimit v0.6.1 h1:ILa9j1onAAMadBsyyUJv5cack8Y1WT26yLj/V+ulKp8=
|
github.com/KimMachineGun/automemlimit v0.6.1 h1:ILa9j1onAAMadBsyyUJv5cack8Y1WT26yLj/V+ulKp8=
|
||||||
github.com/KimMachineGun/automemlimit v0.6.1/go.mod h1:T7xYht7B8r6AG/AqFcUdc7fzd2bIdBKmepfP2S1svPY=
|
github.com/KimMachineGun/automemlimit v0.6.1/go.mod h1:T7xYht7B8r6AG/AqFcUdc7fzd2bIdBKmepfP2S1svPY=
|
||||||
github.com/ash3in/uuidv8 v1.2.0 h1:2oogGdtCPwaVtyvPPGin4TfZLtOGE5F+W++E880G6SI=
|
|
||||||
github.com/ash3in/uuidv8 v1.2.0/go.mod h1:BnU0wJBxnzdEKmVg4xckBkD+VZuecTFTUP3M0dWgyY4=
|
|
||||||
github.com/cilium/ebpf v0.16.0 h1:+BiEnHL6Z7lXnlGUsXQPPAE7+kenAd4ES8MQ5min0Ok=
|
github.com/cilium/ebpf v0.16.0 h1:+BiEnHL6Z7lXnlGUsXQPPAE7+kenAd4ES8MQ5min0Ok=
|
||||||
github.com/cilium/ebpf v0.16.0/go.mod h1:L7u2Blt2jMM/vLAVgjxluxtBKlz3/GWjB0dMOEngfwE=
|
github.com/cilium/ebpf v0.16.0/go.mod h1:L7u2Blt2jMM/vLAVgjxluxtBKlz3/GWjB0dMOEngfwE=
|
||||||
github.com/containerd/cgroups/v3 v3.0.4 h1:2fs7l3P0Qxb1nKWuJNFiwhp2CqiKzho71DQkDrHJIo4=
|
github.com/containerd/cgroups/v3 v3.0.4 h1:2fs7l3P0Qxb1nKWuJNFiwhp2CqiKzho71DQkDrHJIo4=
|
||||||
@ -35,7 +33,6 @@ github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtL
|
|||||||
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||||
github.com/jsimonetti/rtnetlink/v2 v2.0.1 h1:xda7qaHDSVOsADNouv7ukSuicKZO7GgVUCXxpaIEIlM=
|
github.com/jsimonetti/rtnetlink/v2 v2.0.1 h1:xda7qaHDSVOsADNouv7ukSuicKZO7GgVUCXxpaIEIlM=
|
||||||
github.com/jsimonetti/rtnetlink/v2 v2.0.1/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE=
|
github.com/jsimonetti/rtnetlink/v2 v2.0.1/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE=
|
||||||
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
|
|
||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
@ -43,8 +40,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/matoous/go-nanoid v1.5.1 h1:aCjdvTyO9LLnTIi0fgdXhOPPvOHjpXN6Ik9DaNjIct4=
|
|
||||||
github.com/matoous/go-nanoid v1.5.1/go.mod h1:zyD2a71IubI24efhpvkJz+ZwfwagzgSO6UNiFsZKN7U=
|
|
||||||
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
|
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
|
||||||
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
|
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
|
||||||
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
|
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
|
||||||
@ -62,8 +57,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
|
|||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||||
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=
|
||||||
github.com/silas/dag v0.0.0-20220518035006-a7e85ada93c5/go.mod h1:7RTUFBdIRC9nZ7/3RyRNH1bdqIShrDejd1YbLwgPS+I=
|
github.com/silas/dag v0.0.0-20220518035006-a7e85ada93c5/go.mod h1:7RTUFBdIRC9nZ7/3RyRNH1bdqIShrDejd1YbLwgPS+I=
|
||||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
@ -80,8 +75,8 @@ go.unistack.org/micro-proto/v3 v3.4.1 h1:UTjLSRz2YZuaHk9iSlVqqsA50JQNAEK2ZFboGqt
|
|||||||
go.unistack.org/micro-proto/v3 v3.4.1/go.mod h1:okx/cnOhzuCX0ggl/vToatbCupi0O44diiiLLsZ93Zo=
|
go.unistack.org/micro-proto/v3 v3.4.1/go.mod h1:okx/cnOhzuCX0ggl/vToatbCupi0O44diiiLLsZ93Zo=
|
||||||
golang.org/x/exp v0.0.0-20241210194714-1829a127f884 h1:Y/Mj/94zIQQGHVSv1tTtQBDaQaJe62U9bkDZKKyhPCU=
|
golang.org/x/exp v0.0.0-20241210194714-1829a127f884 h1:Y/Mj/94zIQQGHVSv1tTtQBDaQaJe62U9bkDZKKyhPCU=
|
||||||
golang.org/x/exp v0.0.0-20241210194714-1829a127f884/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
|
golang.org/x/exp v0.0.0-20241210194714-1829a127f884/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
|
||||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
|
||||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
|
||||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@ -89,12 +84,12 @@ golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
|||||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241216192217-9240e9c98484 h1:Z7FRVJPSMaHQxD0uXU8WdgFh8PseLM8Q8NzhnpMrBhQ=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241216192217-9240e9c98484/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU=
|
||||||
google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU=
|
google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0=
|
||||||
google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
|
google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw=
|
||||||
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
|
||||||
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
|
@ -4,6 +4,17 @@ import "context"
|
|||||||
|
|
||||||
type loggerKey struct{}
|
type loggerKey struct{}
|
||||||
|
|
||||||
|
// MustContext returns logger from passed context or DefaultLogger if empty
|
||||||
|
func MustContext(ctx context.Context) Logger {
|
||||||
|
if ctx == nil {
|
||||||
|
return DefaultLogger.Clone()
|
||||||
|
}
|
||||||
|
if l, ok := ctx.Value(loggerKey{}).(Logger); ok && l != nil {
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
return DefaultLogger.Clone()
|
||||||
|
}
|
||||||
|
|
||||||
// FromContext returns logger from passed context
|
// FromContext returns logger from passed context
|
||||||
func FromContext(ctx context.Context) (Logger, bool) {
|
func FromContext(ctx context.Context) (Logger, bool) {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
@ -13,15 +24,6 @@ func FromContext(ctx context.Context) (Logger, bool) {
|
|||||||
return l, ok
|
return l, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustContext returns logger from passed context or DefaultLogger if empty
|
|
||||||
func MustContext(ctx context.Context) Logger {
|
|
||||||
l, ok := FromContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
panic("missing logger")
|
|
||||||
}
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewContext stores logger into passed context
|
// NewContext stores logger into passed context
|
||||||
func NewContext(ctx context.Context, l Logger) context.Context {
|
func NewContext(ctx context.Context, l Logger) context.Context {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
|
@ -30,6 +30,7 @@ type Options struct {
|
|||||||
StacktraceKey string
|
StacktraceKey string
|
||||||
// Name holds the logger name
|
// Name holds the logger name
|
||||||
Name string
|
Name string
|
||||||
|
|
||||||
// Out holds the output writer
|
// Out holds the output writer
|
||||||
Out io.Writer
|
Out io.Writer
|
||||||
// Context holds exernal options
|
// Context holds exernal options
|
||||||
@ -38,10 +39,12 @@ type Options struct {
|
|||||||
Meter meter.Meter
|
Meter meter.Meter
|
||||||
// TimeFunc used to obtain current time
|
// TimeFunc used to obtain current time
|
||||||
TimeFunc func() time.Time
|
TimeFunc func() time.Time
|
||||||
|
|
||||||
// Fields holds additional metadata
|
// Fields holds additional metadata
|
||||||
Fields []interface{}
|
Fields []interface{}
|
||||||
// ContextAttrFuncs contains funcs that executed before log func on context
|
// ContextAttrFuncs contains funcs that executed before log func on context
|
||||||
ContextAttrFuncs []ContextAttrFunc
|
ContextAttrFuncs []ContextAttrFunc
|
||||||
|
|
||||||
// callerSkipCount number of frmaes to skip
|
// callerSkipCount number of frmaes to skip
|
||||||
CallerSkipCount int
|
CallerSkipCount int
|
||||||
// The logging level the logger should log
|
// The logging level the logger should log
|
||||||
|
@ -2,7 +2,6 @@ package slog
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"io"
|
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
@ -11,7 +10,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.unistack.org/micro/v3/logger"
|
"go.unistack.org/micro/v3/logger"
|
||||||
"go.unistack.org/micro/v3/semconv"
|
"go.unistack.org/micro/v3/semconv"
|
||||||
@ -22,7 +20,6 @@ const (
|
|||||||
badKey = "!BADKEY"
|
badKey = "!BADKEY"
|
||||||
// defaultCallerSkipCount used by logger
|
// defaultCallerSkipCount used by logger
|
||||||
defaultCallerSkipCount = 3
|
defaultCallerSkipCount = 3
|
||||||
timeFormat = "2006-01-02T15:04:05.000000000Z07:00"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var reTrace = regexp.MustCompile(`.*/slog/logger\.go.*\n`)
|
var reTrace = regexp.MustCompile(`.*/slog/logger\.go.*\n`)
|
||||||
@ -65,7 +62,6 @@ func (s *slogLogger) renameAttr(_ []string, a slog.Attr) slog.Attr {
|
|||||||
a.Key = s.opts.SourceKey
|
a.Key = s.opts.SourceKey
|
||||||
case slog.TimeKey:
|
case slog.TimeKey:
|
||||||
a.Key = s.opts.TimeKey
|
a.Key = s.opts.TimeKey
|
||||||
a.Value = slog.StringValue(a.Value.Time().Format(timeFormat))
|
|
||||||
case slog.MessageKey:
|
case slog.MessageKey:
|
||||||
a.Key = s.opts.MessageKey
|
a.Key = s.opts.MessageKey
|
||||||
case slog.LevelKey:
|
case slog.LevelKey:
|
||||||
@ -228,10 +224,6 @@ func (s *slogLogger) Error(ctx context.Context, msg string, attrs ...interface{}
|
|||||||
|
|
||||||
func (s *slogLogger) Fatal(ctx context.Context, msg string, attrs ...interface{}) {
|
func (s *slogLogger) Fatal(ctx context.Context, msg string, attrs ...interface{}) {
|
||||||
s.printLog(ctx, logger.FatalLevel, msg, attrs...)
|
s.printLog(ctx, logger.FatalLevel, msg, attrs...)
|
||||||
if closer, ok := s.opts.Out.(io.Closer); ok {
|
|
||||||
closer.Close()
|
|
||||||
}
|
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,7 +270,7 @@ func (s *slogLogger) printLog(ctx context.Context, lvl logger.Level, msg string,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s.opts.AddStacktrace || lvl == logger.FatalLevel) || (s.opts.AddStacktrace && lvl == logger.ErrorLevel) {
|
if s.opts.AddStacktrace && lvl == logger.ErrorLevel {
|
||||||
stackInfo := make([]byte, 1024*1024)
|
stackInfo := make([]byte, 1024*1024)
|
||||||
if stackSize := runtime.Stack(stackInfo, false); stackSize > 0 {
|
if stackSize := runtime.Stack(stackInfo, false); stackSize > 0 {
|
||||||
traceLines := reTrace.Split(string(stackInfo[:stackSize]), -1)
|
traceLines := reTrace.Split(string(stackInfo[:stackSize]), -1)
|
||||||
|
@ -9,74 +9,12 @@ import (
|
|||||||
"log/slog"
|
"log/slog"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"go.unistack.org/micro/v3/logger"
|
"go.unistack.org/micro/v3/logger"
|
||||||
"go.unistack.org/micro/v3/metadata"
|
"go.unistack.org/micro/v3/metadata"
|
||||||
"go.unistack.org/micro/v3/util/buffer"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// always first to have proper check
|
|
||||||
func TestStacktrace(t *testing.T) {
|
|
||||||
ctx := context.TODO()
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
l := NewLogger(logger.WithLevel(logger.ErrorLevel), logger.WithOutput(buf),
|
|
||||||
WithHandlerFunc(slog.NewTextHandler),
|
|
||||||
logger.WithAddStacktrace(true),
|
|
||||||
)
|
|
||||||
if err := l.Init(logger.WithFields("key1", "val1")); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Error(ctx, "msg1", errors.New("err"))
|
|
||||||
|
|
||||||
if !bytes.Contains(buf.Bytes(), []byte(`slog_test.go:32`)) {
|
|
||||||
t.Fatalf("logger error not works, buf contains: %s", buf.Bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDelayedBuffer(t *testing.T) {
|
|
||||||
ctx := context.TODO()
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
dbuf := buffer.NewDelayedBuffer(100, 100*time.Millisecond, buf)
|
|
||||||
l := NewLogger(logger.WithLevel(logger.ErrorLevel), logger.WithOutput(dbuf),
|
|
||||||
WithHandlerFunc(slog.NewTextHandler),
|
|
||||||
logger.WithAddStacktrace(true),
|
|
||||||
)
|
|
||||||
if err := l.Init(logger.WithFields("key1", "val1")); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Error(ctx, "msg1", errors.New("err"))
|
|
||||||
time.Sleep(120 * time.Millisecond)
|
|
||||||
if !bytes.Contains(buf.Bytes(), []byte(`key1=val1`)) {
|
|
||||||
t.Fatalf("logger delayed buffer not works, buf contains: %s", buf.Bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTime(t *testing.T) {
|
|
||||||
ctx := context.TODO()
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
l := NewLogger(logger.WithLevel(logger.ErrorLevel), logger.WithOutput(buf),
|
|
||||||
WithHandlerFunc(slog.NewTextHandler),
|
|
||||||
logger.WithAddStacktrace(true),
|
|
||||||
logger.WithTimeFunc(func() time.Time {
|
|
||||||
return time.Unix(0, 0)
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
if err := l.Init(logger.WithFields("key1", "val1")); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Error(ctx, "msg1", errors.New("err"))
|
|
||||||
|
|
||||||
if !bytes.Contains(buf.Bytes(), []byte(`timestamp=1970-01-01T03:00:00.000000000+03:00`)) &&
|
|
||||||
!bytes.Contains(buf.Bytes(), []byte(`timestamp=1970-01-01T00:00:00.000000000Z`)) {
|
|
||||||
t.Fatalf("logger error not works, buf contains: %s", buf.Bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWithFields(t *testing.T) {
|
func TestWithFields(t *testing.T) {
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
buf := bytes.NewBuffer(nil)
|
buf := bytes.NewBuffer(nil)
|
||||||
|
@ -11,6 +11,34 @@ type (
|
|||||||
mdKey struct{}
|
mdKey struct{}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// MustIncomingContext returns metadata from incoming ctx
|
||||||
|
// returned metadata shoud not be modified or race condition happens.
|
||||||
|
// If metadata not exists panics.
|
||||||
|
func MustIncomingContext(ctx context.Context) Metadata {
|
||||||
|
if ctx == nil {
|
||||||
|
panic("missing metadata")
|
||||||
|
}
|
||||||
|
md, ok := ctx.Value(mdIncomingKey{}).(*rawMetadata)
|
||||||
|
if !ok {
|
||||||
|
panic("missing metadata")
|
||||||
|
}
|
||||||
|
return md.md
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustOutgoingContext returns metadata from outgoing ctx
|
||||||
|
// returned metadata shoud not be modified or race condition happens.
|
||||||
|
// If metadata not exists panics.
|
||||||
|
func MustOutgoingContext(ctx context.Context) Metadata {
|
||||||
|
if ctx == nil {
|
||||||
|
panic("missing metadata")
|
||||||
|
}
|
||||||
|
md, ok := ctx.Value(mdOutgoingKey{}).(*rawMetadata)
|
||||||
|
if !ok {
|
||||||
|
panic("missing metadata")
|
||||||
|
}
|
||||||
|
return md.md
|
||||||
|
}
|
||||||
|
|
||||||
// FromIncomingContext returns metadata from incoming ctx
|
// FromIncomingContext returns metadata from incoming ctx
|
||||||
// returned metadata shoud not be modified or race condition happens
|
// returned metadata shoud not be modified or race condition happens
|
||||||
func FromIncomingContext(ctx context.Context) (Metadata, bool) {
|
func FromIncomingContext(ctx context.Context) (Metadata, bool) {
|
||||||
@ -24,17 +52,6 @@ func FromIncomingContext(ctx context.Context) (Metadata, bool) {
|
|||||||
return md.md, ok
|
return md.md, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustIncomingContext returns metadata from incoming ctx
|
|
||||||
// returned metadata shoud not be modified or race condition happens.
|
|
||||||
// If metadata not exists panics.
|
|
||||||
func MustIncomingContext(ctx context.Context) Metadata {
|
|
||||||
md, ok := FromIncomingContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
panic("missing metadata")
|
|
||||||
}
|
|
||||||
return md
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromOutgoingContext returns metadata from outgoing ctx
|
// FromOutgoingContext returns metadata from outgoing ctx
|
||||||
// returned metadata shoud not be modified or race condition happens
|
// returned metadata shoud not be modified or race condition happens
|
||||||
func FromOutgoingContext(ctx context.Context) (Metadata, bool) {
|
func FromOutgoingContext(ctx context.Context) (Metadata, bool) {
|
||||||
@ -48,17 +65,6 @@ func FromOutgoingContext(ctx context.Context) (Metadata, bool) {
|
|||||||
return md.md, ok
|
return md.md, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustOutgoingContext returns metadata from outgoing ctx
|
|
||||||
// returned metadata shoud not be modified or race condition happens.
|
|
||||||
// If metadata not exists panics.
|
|
||||||
func MustOutgoingContext(ctx context.Context) Metadata {
|
|
||||||
md, ok := FromOutgoingContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
panic("missing metadata")
|
|
||||||
}
|
|
||||||
return md
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromContext returns metadata from the given context
|
// FromContext returns metadata from the given context
|
||||||
// returned metadata shoud not be modified or race condition happens
|
// returned metadata shoud not be modified or race condition happens
|
||||||
func FromContext(ctx context.Context) (Metadata, bool) {
|
func FromContext(ctx context.Context) (Metadata, bool) {
|
||||||
@ -72,16 +78,6 @@ func FromContext(ctx context.Context) (Metadata, bool) {
|
|||||||
return md.md, ok
|
return md.md, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustContext returns metadata from the given context
|
|
||||||
// returned metadata shoud not be modified or race condition happens
|
|
||||||
func MustContext(ctx context.Context) Metadata {
|
|
||||||
md, ok := FromContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
panic("missing metadata")
|
|
||||||
}
|
|
||||||
return md
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewContext creates a new context with the given metadata
|
// NewContext creates a new context with the given metadata
|
||||||
func NewContext(ctx context.Context, md Metadata) context.Context {
|
func NewContext(ctx context.Context, md Metadata) context.Context {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
@ -143,7 +139,7 @@ func AppendOutgoingContext(ctx context.Context, kv ...string) context.Context {
|
|||||||
for k, v := range md {
|
for k, v := range md {
|
||||||
omd.Set(k, v)
|
omd.Set(k, v)
|
||||||
}
|
}
|
||||||
return ctx
|
return NewOutgoingContext(ctx, omd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppendIncomingContext apends new md to context
|
// AppendIncomingContext apends new md to context
|
||||||
@ -159,21 +155,5 @@ func AppendIncomingContext(ctx context.Context, kv ...string) context.Context {
|
|||||||
for k, v := range md {
|
for k, v := range md {
|
||||||
omd.Set(k, v)
|
omd.Set(k, v)
|
||||||
}
|
}
|
||||||
return ctx
|
return NewIncomingContext(ctx, omd)
|
||||||
}
|
|
||||||
|
|
||||||
// AppendContext apends new md to context
|
|
||||||
func AppendContext(ctx context.Context, kv ...string) context.Context {
|
|
||||||
md, ok := Pairs(kv...)
|
|
||||||
if !ok {
|
|
||||||
return ctx
|
|
||||||
}
|
|
||||||
omd, ok := FromContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
return NewContext(ctx, md)
|
|
||||||
}
|
|
||||||
for k, v := range md {
|
|
||||||
omd.Set(k, v)
|
|
||||||
}
|
|
||||||
return ctx
|
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,15 @@ func (md Metadata) Iterator() *Iterator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (md Metadata) MustGet(key string) string {
|
func (md Metadata) MustGet(key string) string {
|
||||||
val, ok := md.Get(key)
|
// fast path
|
||||||
|
val, ok := md[key]
|
||||||
|
if !ok {
|
||||||
|
// slow path
|
||||||
|
val, ok = md[textproto.CanonicalMIMEHeaderKey(key)]
|
||||||
|
if !ok {
|
||||||
|
val, ok = md[strings.ToLower(key)]
|
||||||
|
}
|
||||||
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
panic("missing metadata key")
|
panic("missing metadata key")
|
||||||
}
|
}
|
||||||
@ -111,18 +119,11 @@ func (md Metadata) Del(keys ...string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy makes a copy of the metadata
|
|
||||||
func (md Metadata) CopyTo(dst Metadata) {
|
|
||||||
for k, v := range md {
|
|
||||||
dst[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy makes a copy of the metadata
|
// Copy makes a copy of the metadata
|
||||||
func Copy(md Metadata, exclude ...string) Metadata {
|
func Copy(md Metadata, exclude ...string) Metadata {
|
||||||
nmd := New(len(md))
|
nmd := New(len(md))
|
||||||
for k, v := range md {
|
for key, val := range md {
|
||||||
nmd[k] = v
|
nmd.Set(key, val)
|
||||||
}
|
}
|
||||||
nmd.Del(exclude...)
|
nmd.Del(exclude...)
|
||||||
return nmd
|
return nmd
|
||||||
@ -146,7 +147,7 @@ func Merge(omd Metadata, mmd Metadata, overwrite bool) Metadata {
|
|||||||
case ok && !overwrite:
|
case ok && !overwrite:
|
||||||
continue
|
continue
|
||||||
case val != "":
|
case val != "":
|
||||||
nmd[key] = val
|
nmd.Set(key, val)
|
||||||
case ok && val == "":
|
case ok && val == "":
|
||||||
nmd.Del(key)
|
nmd.Del(key)
|
||||||
}
|
}
|
||||||
@ -160,8 +161,6 @@ func Pairs(kv ...string) (Metadata, bool) {
|
|||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
md := New(len(kv) / 2)
|
md := New(len(kv) / 2)
|
||||||
for idx := 0; idx < len(kv); idx += 2 {
|
md.Set(kv...)
|
||||||
md[kv[idx]] = kv[idx+1]
|
|
||||||
}
|
|
||||||
return md, true
|
return md, true
|
||||||
}
|
}
|
||||||
|
@ -32,8 +32,8 @@ func TestMultipleUsage(t *testing.T) {
|
|||||||
m.Del("key1_3")
|
m.Del("key1_3")
|
||||||
return ctx
|
return ctx
|
||||||
}(ctx)
|
}(ctx)
|
||||||
_ = imd
|
t.Logf("imd %#+v", imd)
|
||||||
_ = omd
|
t.Logf("omd %#+v", omd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMetadataSetMultiple(t *testing.T) {
|
func TestMetadataSetMultiple(t *testing.T) {
|
||||||
|
@ -15,15 +15,6 @@ func FromContext(ctx context.Context) (Meter, bool) {
|
|||||||
return c, ok
|
return c, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustContext get meter from context
|
|
||||||
func MustContext(ctx context.Context) Meter {
|
|
||||||
m, ok := FromContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
panic("missing meter")
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewContext put meter in context
|
// NewContext put meter in context
|
||||||
func NewContext(ctx context.Context, c Meter) context.Context {
|
func NewContext(ctx context.Context, c Meter) context.Context {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
|
@ -119,7 +119,7 @@ func Tracer(t tracer.Tracer) Option {
|
|||||||
// NewOptions returns network default options
|
// NewOptions returns network default options
|
||||||
func NewOptions(opts ...Option) Options {
|
func NewOptions(opts ...Option) Options {
|
||||||
options := Options{
|
options := Options{
|
||||||
ID: id.MustNew(),
|
ID: id.Must(),
|
||||||
Name: "go.micro",
|
Name: "go.micro",
|
||||||
Address: ":0",
|
Address: ":0",
|
||||||
Logger: logger.DefaultLogger,
|
Logger: logger.DefaultLogger,
|
||||||
|
@ -164,7 +164,7 @@ func DialWait(b bool) DialOption {
|
|||||||
// NewOptions returns router default options with filled values
|
// NewOptions returns router default options with filled values
|
||||||
func NewOptions(opts ...Option) Options {
|
func NewOptions(opts ...Option) Options {
|
||||||
options := Options{
|
options := Options{
|
||||||
ID: id.MustNew(),
|
ID: id.Must(),
|
||||||
Address: DefaultAddress,
|
Address: DefaultAddress,
|
||||||
Token: DefaultToken,
|
Token: DefaultToken,
|
||||||
Logger: logger.DefaultLogger,
|
Logger: logger.DefaultLogger,
|
||||||
|
@ -15,15 +15,6 @@ func FromContext(ctx context.Context) (Register, bool) {
|
|||||||
return c, ok
|
return c, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustContext get register from context
|
|
||||||
func MustContext(ctx context.Context) Register {
|
|
||||||
r, ok := FromContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
panic("missing register")
|
|
||||||
}
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewContext put register in context
|
// NewContext put register in context
|
||||||
func NewContext(ctx context.Context, c Register) context.Context {
|
func NewContext(ctx context.Context, c Register) context.Context {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
package register
|
package register
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"unicode"
|
"unicode"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"go.unistack.org/micro/v3/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExtractValue from reflect.Type from specified depth
|
// ExtractValue from reflect.Type from specified depth
|
||||||
@ -35,6 +38,53 @@ func ExtractValue(v reflect.Type, d int) string {
|
|||||||
return v.Name()
|
return v.Name()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExtractEndpoint extract *Endpoint from reflect.Method
|
||||||
|
func ExtractEndpoint(method reflect.Method) *Endpoint {
|
||||||
|
if method.PkgPath != "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var rspType, reqType reflect.Type
|
||||||
|
var stream bool
|
||||||
|
mt := method.Type
|
||||||
|
|
||||||
|
switch mt.NumIn() {
|
||||||
|
case 3:
|
||||||
|
reqType = mt.In(1)
|
||||||
|
rspType = mt.In(2)
|
||||||
|
case 4:
|
||||||
|
reqType = mt.In(2)
|
||||||
|
rspType = mt.In(3)
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// are we dealing with a stream?
|
||||||
|
switch rspType.Kind() {
|
||||||
|
case reflect.Func, reflect.Interface:
|
||||||
|
stream = true
|
||||||
|
}
|
||||||
|
|
||||||
|
request := ExtractValue(reqType, 0)
|
||||||
|
response := ExtractValue(rspType, 0)
|
||||||
|
if request == "" || response == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ep := &Endpoint{
|
||||||
|
Name: method.Name,
|
||||||
|
Request: request,
|
||||||
|
Response: response,
|
||||||
|
Metadata: metadata.New(0),
|
||||||
|
}
|
||||||
|
|
||||||
|
if stream {
|
||||||
|
ep.Metadata.Set("stream", fmt.Sprintf("%v", stream))
|
||||||
|
}
|
||||||
|
|
||||||
|
return ep
|
||||||
|
}
|
||||||
|
|
||||||
// ExtractSubValue exctact *Value from reflect.Type
|
// ExtractSubValue exctact *Value from reflect.Type
|
||||||
func ExtractSubValue(typ reflect.Type) string {
|
func ExtractSubValue(typ reflect.Type) string {
|
||||||
var reqType reflect.Type
|
var reqType reflect.Type
|
||||||
|
@ -2,6 +2,8 @@ package register
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TestHandler struct{}
|
type TestHandler struct{}
|
||||||
@ -13,3 +15,40 @@ type TestResponse struct{}
|
|||||||
func (t *TestHandler) Test(ctx context.Context, req *TestRequest, rsp *TestResponse) error {
|
func (t *TestHandler) Test(ctx context.Context, req *TestRequest, rsp *TestResponse) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestExtractEndpoint(t *testing.T) {
|
||||||
|
handler := &TestHandler{}
|
||||||
|
typ := reflect.TypeOf(handler)
|
||||||
|
|
||||||
|
var endpoints []*Endpoint
|
||||||
|
|
||||||
|
for m := 0; m < typ.NumMethod(); m++ {
|
||||||
|
if e := ExtractEndpoint(typ.Method(m)); e != nil {
|
||||||
|
endpoints = append(endpoints, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if i := len(endpoints); i != 1 {
|
||||||
|
t.Fatalf("Expected 1 endpoint, have %d", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
if endpoints[0].Name != "Test" {
|
||||||
|
t.Fatalf("Expected handler Test, got %s", endpoints[0].Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if endpoints[0].Request == "" {
|
||||||
|
t.Fatal("Expected non nil Request")
|
||||||
|
}
|
||||||
|
|
||||||
|
if endpoints[0].Response == "" {
|
||||||
|
t.Fatal("Expected non nil Request")
|
||||||
|
}
|
||||||
|
|
||||||
|
if endpoints[0].Request != "TestRequest" {
|
||||||
|
t.Fatalf("Expected TestRequest got %s", endpoints[0].Request)
|
||||||
|
}
|
||||||
|
|
||||||
|
if endpoints[0].Response != "TestResponse" {
|
||||||
|
t.Fatalf("Expected TestResponse got %s", endpoints[0].Response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -23,10 +23,11 @@ type node struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type record struct {
|
type record struct {
|
||||||
Name string
|
Name string
|
||||||
Version string
|
Version string
|
||||||
Metadata map[string]string
|
Metadata map[string]string
|
||||||
Nodes map[string]*node
|
Nodes map[string]*node
|
||||||
|
Endpoints []*register.Endpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
type memory struct {
|
type memory struct {
|
||||||
@ -58,7 +59,7 @@ func (m *memory) ttlPrune() {
|
|||||||
|
|
||||||
for range prune.C {
|
for range prune.C {
|
||||||
m.Lock()
|
m.Lock()
|
||||||
for namespace, services := range m.records {
|
for domain, services := range m.records {
|
||||||
for service, versions := range services {
|
for service, versions := range services {
|
||||||
for version, record := range versions {
|
for version, record := range versions {
|
||||||
for id, n := range record.Nodes {
|
for id, n := range record.Nodes {
|
||||||
@ -66,7 +67,7 @@ func (m *memory) ttlPrune() {
|
|||||||
if m.opts.Logger.V(logger.DebugLevel) {
|
if m.opts.Logger.V(logger.DebugLevel) {
|
||||||
m.opts.Logger.Debug(m.opts.Context, fmt.Sprintf("Register TTL expired for node %s of service %s", n.ID, service))
|
m.opts.Logger.Debug(m.opts.Context, fmt.Sprintf("Register TTL expired for node %s of service %s", n.ID, service))
|
||||||
}
|
}
|
||||||
delete(m.records[namespace][service][version].Nodes, id)
|
delete(m.records[domain][service][version].Nodes, id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -130,12 +131,17 @@ func (m *memory) Register(_ context.Context, s *register.Service, opts ...regist
|
|||||||
options := register.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.Namespace]
|
srvs, ok := m.records[options.Domain]
|
||||||
if !ok {
|
if !ok {
|
||||||
srvs = make(services)
|
srvs = make(services)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Namespace = options.Namespace
|
// domain is set in metadata so it can be passed to watchers
|
||||||
|
if s.Metadata == nil {
|
||||||
|
s.Metadata = map[string]string{"domain": options.Domain}
|
||||||
|
} else {
|
||||||
|
s.Metadata["domain"] = options.Domain
|
||||||
|
}
|
||||||
|
|
||||||
// ensure the service name exists
|
// ensure the service name exists
|
||||||
r := serviceToRecord(s, options.TTL)
|
r := serviceToRecord(s, options.TTL)
|
||||||
@ -148,8 +154,8 @@ func (m *memory) Register(_ context.Context, s *register.Service, opts ...regist
|
|||||||
if m.opts.Logger.V(logger.DebugLevel) {
|
if m.opts.Logger.V(logger.DebugLevel) {
|
||||||
m.opts.Logger.Debug(m.opts.Context, fmt.Sprintf("Register added new service: %s, version: %s", s.Name, s.Version))
|
m.opts.Logger.Debug(m.opts.Context, fmt.Sprintf("Register added new service: %s, version: %s", s.Name, s.Version))
|
||||||
}
|
}
|
||||||
m.records[options.Namespace] = srvs
|
m.records[options.Domain] = srvs
|
||||||
go m.sendEvent(®ister.Result{Action: register.EventCreate, Service: s})
|
go m.sendEvent(®ister.Result{Action: "create", Service: s})
|
||||||
}
|
}
|
||||||
|
|
||||||
var addedNodes bool
|
var addedNodes bool
|
||||||
@ -167,6 +173,9 @@ func (m *memory) Register(_ context.Context, s *register.Service, opts ...regist
|
|||||||
metadata[k] = v
|
metadata[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set the domain
|
||||||
|
metadata["domain"] = options.Domain
|
||||||
|
|
||||||
// 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: ®ister.Node{
|
Node: ®ister.Node{
|
||||||
@ -185,7 +194,7 @@ func (m *memory) Register(_ context.Context, s *register.Service, opts ...regist
|
|||||||
if m.opts.Logger.V(logger.DebugLevel) {
|
if m.opts.Logger.V(logger.DebugLevel) {
|
||||||
m.opts.Logger.Debug(m.opts.Context, fmt.Sprintf("Register added new node to service: %s, version: %s", s.Name, s.Version))
|
m.opts.Logger.Debug(m.opts.Context, fmt.Sprintf("Register added new node to service: %s, version: %s", s.Name, s.Version))
|
||||||
}
|
}
|
||||||
go m.sendEvent(®ister.Result{Action: register.EventUpdate, 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 {
|
||||||
@ -197,7 +206,7 @@ func (m *memory) Register(_ context.Context, s *register.Service, opts ...regist
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m.records[options.Namespace] = srvs
|
m.records[options.Domain] = srvs
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,8 +216,15 @@ func (m *memory) Deregister(ctx context.Context, s *register.Service, opts ...re
|
|||||||
|
|
||||||
options := register.NewDeregisterOptions(opts...)
|
options := register.NewDeregisterOptions(opts...)
|
||||||
|
|
||||||
|
// domain is set in metadata so it can be passed to watchers
|
||||||
|
if s.Metadata == nil {
|
||||||
|
s.Metadata = map[string]string{"domain": options.Domain}
|
||||||
|
} else {
|
||||||
|
s.Metadata["domain"] = options.Domain
|
||||||
|
}
|
||||||
|
|
||||||
// if the domain doesn't exist, there is nothing to deregister
|
// if the domain doesn't exist, there is nothing to deregister
|
||||||
services, ok := m.records[options.Namespace]
|
services, ok := m.records[options.Domain]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -237,16 +253,16 @@ func (m *memory) Deregister(ctx context.Context, s *register.Service, opts ...re
|
|||||||
// if the nodes not empty, we replace the version in the store and exist, the rest of the logic
|
// if the nodes not empty, we replace the version in the store and exist, the rest of the logic
|
||||||
// is cleanup
|
// is cleanup
|
||||||
if len(version.Nodes) > 0 {
|
if len(version.Nodes) > 0 {
|
||||||
m.records[options.Namespace][s.Name][s.Version] = version
|
m.records[options.Domain][s.Name][s.Version] = version
|
||||||
go m.sendEvent(®ister.Result{Action: register.EventUpdate, Service: s})
|
go m.sendEvent(®ister.Result{Action: "update", Service: s})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// if this version was the only version of the service, we can remove the whole service from the
|
// if this version was the only version of the service, we can remove the whole service from the
|
||||||
// register and exit
|
// register and exit
|
||||||
if len(versions) == 1 {
|
if len(versions) == 1 {
|
||||||
delete(m.records[options.Namespace], s.Name)
|
delete(m.records[options.Domain], s.Name)
|
||||||
go m.sendEvent(®ister.Result{Action: register.EventDelete, 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.Debug(m.opts.Context, fmt.Sprintf("Register removed service: %s", s.Name))
|
m.opts.Logger.Debug(m.opts.Context, fmt.Sprintf("Register removed service: %s", s.Name))
|
||||||
@ -255,8 +271,8 @@ func (m *memory) Deregister(ctx context.Context, s *register.Service, opts ...re
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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.Namespace][s.Name], s.Version)
|
delete(m.records[options.Domain][s.Name], s.Version)
|
||||||
go m.sendEvent(®ister.Result{Action: register.EventDelete, 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.Debug(m.opts.Context, fmt.Sprintf("Register removed service: %s, version: %s", s.Name, s.Version))
|
m.opts.Logger.Debug(m.opts.Context, fmt.Sprintf("Register removed service: %s, version: %s", s.Name, s.Version))
|
||||||
}
|
}
|
||||||
@ -268,15 +284,15 @@ func (m *memory) LookupService(ctx context.Context, name string, opts ...registe
|
|||||||
options := register.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.Namespace == register.WildcardNamespace {
|
if options.Domain == register.WildcardDomain {
|
||||||
m.RLock()
|
m.RLock()
|
||||||
recs := m.records
|
recs := m.records
|
||||||
m.RUnlock()
|
m.RUnlock()
|
||||||
|
|
||||||
var services []*register.Service
|
var services []*register.Service
|
||||||
|
|
||||||
for namespace := range recs {
|
for domain := range recs {
|
||||||
srvs, err := m.LookupService(ctx, name, append(opts, register.LookupNamespace(namespace))...)
|
srvs, err := m.LookupService(ctx, name, append(opts, register.LookupDomain(domain))...)
|
||||||
if err == register.ErrNotFound {
|
if err == register.ErrNotFound {
|
||||||
continue
|
continue
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
@ -295,7 +311,7 @@ func (m *memory) LookupService(ctx context.Context, name string, opts ...registe
|
|||||||
defer m.RUnlock()
|
defer m.RUnlock()
|
||||||
|
|
||||||
// check the domain exists
|
// check the domain exists
|
||||||
services, ok := m.records[options.Namespace]
|
services, ok := m.records[options.Domain]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, register.ErrNotFound
|
return nil, register.ErrNotFound
|
||||||
}
|
}
|
||||||
@ -312,7 +328,7 @@ func (m *memory) LookupService(ctx context.Context, name string, opts ...registe
|
|||||||
var i int
|
var i int
|
||||||
|
|
||||||
for _, r := range versions {
|
for _, r := range versions {
|
||||||
result[i] = recordToService(r, options.Namespace)
|
result[i] = recordToService(r, options.Domain)
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,15 +339,15 @@ func (m *memory) ListServices(ctx context.Context, opts ...register.ListOption)
|
|||||||
options := register.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.Namespace == register.WildcardNamespace {
|
if options.Domain == register.WildcardDomain {
|
||||||
m.RLock()
|
m.RLock()
|
||||||
recs := m.records
|
recs := m.records
|
||||||
m.RUnlock()
|
m.RUnlock()
|
||||||
|
|
||||||
var services []*register.Service
|
var services []*register.Service
|
||||||
|
|
||||||
for namespace := range recs {
|
for domain := range recs {
|
||||||
srvs, err := m.ListServices(ctx, append(opts, register.ListNamespace(namespace))...)
|
srvs, err := m.ListServices(ctx, append(opts, register.ListDomain(domain))...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -345,7 +361,7 @@ func (m *memory) ListServices(ctx context.Context, opts ...register.ListOption)
|
|||||||
defer m.RUnlock()
|
defer m.RUnlock()
|
||||||
|
|
||||||
// ensure the domain exists
|
// ensure the domain exists
|
||||||
services, ok := m.records[options.Namespace]
|
services, ok := m.records[options.Domain]
|
||||||
if !ok {
|
if !ok {
|
||||||
return make([]*register.Service, 0), nil
|
return make([]*register.Service, 0), nil
|
||||||
}
|
}
|
||||||
@ -355,7 +371,7 @@ func (m *memory) ListServices(ctx context.Context, opts ...register.ListOption)
|
|||||||
|
|
||||||
for _, service := range services {
|
for _, service := range services {
|
||||||
for _, version := range service {
|
for _, version := range service {
|
||||||
result = append(result, recordToService(version, options.Namespace))
|
result = append(result, recordToService(version, options.Domain))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -410,13 +426,16 @@ func (m *watcher) Next() (*register.Result, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace := register.DefaultNamespace
|
// extract domain from service metadata
|
||||||
if r.Service.Namespace != "" {
|
var domain string
|
||||||
namespace = r.Service.Namespace
|
if r.Service.Metadata != nil && len(r.Service.Metadata["domain"]) > 0 {
|
||||||
|
domain = r.Service.Metadata["domain"]
|
||||||
|
} else {
|
||||||
|
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.Namespace == register.WildcardNamespace || m.wo.Namespace == namespace {
|
if m.wo.Domain == register.WildcardDomain || m.wo.Domain == domain {
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
case <-m.exit:
|
case <-m.exit:
|
||||||
@ -435,6 +454,11 @@ func (m *watcher) Stop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func serviceToRecord(s *register.Service, ttl time.Duration) *record {
|
func serviceToRecord(s *register.Service, ttl time.Duration) *record {
|
||||||
|
metadata := make(map[string]string, len(s.Metadata))
|
||||||
|
for k, v := range s.Metadata {
|
||||||
|
metadata[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
nodes := make(map[string]*node, len(s.Nodes))
|
nodes := make(map[string]*node, len(s.Nodes))
|
||||||
for _, n := range s.Nodes {
|
for _, n := range s.Nodes {
|
||||||
nodes[n.ID] = &node{
|
nodes[n.ID] = &node{
|
||||||
@ -444,19 +468,42 @@ func serviceToRecord(s *register.Service, ttl time.Duration) *record {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
endpoints := make([]*register.Endpoint, len(s.Endpoints))
|
||||||
|
copy(endpoints, s.Endpoints)
|
||||||
|
|
||||||
return &record{
|
return &record{
|
||||||
Name: s.Name,
|
Name: s.Name,
|
||||||
Version: s.Version,
|
Version: s.Version,
|
||||||
Nodes: nodes,
|
Metadata: metadata,
|
||||||
|
Nodes: nodes,
|
||||||
|
Endpoints: endpoints,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func recordToService(r *record, namespace string) *register.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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set the domain in metadata so it can be determined when a wildcard query is performed
|
||||||
|
metadata["domain"] = domain
|
||||||
|
|
||||||
|
endpoints := make([]*register.Endpoint, len(r.Endpoints))
|
||||||
|
for i, e := range r.Endpoints {
|
||||||
|
md := make(map[string]string, len(e.Metadata))
|
||||||
|
for k, v := range e.Metadata {
|
||||||
|
md[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoints[i] = ®ister.Endpoint{
|
||||||
|
Name: e.Name,
|
||||||
|
Request: e.Request,
|
||||||
|
Response: e.Response,
|
||||||
|
Metadata: md,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
nodes := make([]*register.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 {
|
||||||
@ -476,7 +523,8 @@ func recordToService(r *record, namespace string) *register.Service {
|
|||||||
return ®ister.Service{
|
return ®ister.Service{
|
||||||
Name: r.Name,
|
Name: r.Name,
|
||||||
Version: r.Version,
|
Version: r.Version,
|
||||||
|
Metadata: metadata,
|
||||||
|
Endpoints: endpoints,
|
||||||
Nodes: nodes,
|
Nodes: nodes,
|
||||||
Namespace: namespace,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -253,32 +253,32 @@ func TestMemoryWildcard(t *testing.T) {
|
|||||||
|
|
||||||
testSrv := ®ister.Service{Name: "foo", Version: "1.0.0"}
|
testSrv := ®ister.Service{Name: "foo", Version: "1.0.0"}
|
||||||
|
|
||||||
if err := m.Register(ctx, testSrv, register.RegisterNamespace("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, register.RegisterNamespace("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, register.ListNamespace("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, register.ListNamespace("*")); 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, register.LookupNamespace("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, register.LookupNamespace("*")); 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))
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.unistack.org/micro/v3/codec"
|
|
||||||
"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/tracer"
|
"go.unistack.org/micro/v3/tracer"
|
||||||
@ -27,8 +26,6 @@ type Options struct {
|
|||||||
Name string
|
Name string
|
||||||
// Addrs specifies register addrs
|
// Addrs specifies register addrs
|
||||||
Addrs []string
|
Addrs []string
|
||||||
// Codec used to marshal/unmarshal data in register
|
|
||||||
Codec codec.Codec
|
|
||||||
// Timeout specifies timeout
|
// Timeout specifies timeout
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
}
|
}
|
||||||
@ -40,7 +37,6 @@ func NewOptions(opts ...Option) Options {
|
|||||||
Meter: meter.DefaultMeter,
|
Meter: meter.DefaultMeter,
|
||||||
Tracer: tracer.DefaultTracer,
|
Tracer: tracer.DefaultTracer,
|
||||||
Context: context.Background(),
|
Context: context.Background(),
|
||||||
Codec: codec.NewCodec(),
|
|
||||||
}
|
}
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&options)
|
o(&options)
|
||||||
@ -50,17 +46,17 @@ func NewOptions(opts ...Option) Options {
|
|||||||
|
|
||||||
// RegisterOptions holds options for register method
|
// RegisterOptions holds options for register method
|
||||||
type RegisterOptions struct { // nolint: golint,revive
|
type RegisterOptions struct { // nolint: golint,revive
|
||||||
Context context.Context
|
Context context.Context
|
||||||
Namespace string
|
Domain string
|
||||||
TTL time.Duration
|
TTL time.Duration
|
||||||
Attempts int
|
Attempts int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewRegisterOptions returns register options struct filled by opts
|
// NewRegisterOptions returns register options struct filled by opts
|
||||||
func NewRegisterOptions(opts ...RegisterOption) RegisterOptions {
|
func NewRegisterOptions(opts ...RegisterOption) RegisterOptions {
|
||||||
options := RegisterOptions{
|
options := RegisterOptions{
|
||||||
Namespace: DefaultNamespace,
|
Domain: DefaultDomain,
|
||||||
Context: context.Background(),
|
Context: context.Background(),
|
||||||
}
|
}
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&options)
|
o(&options)
|
||||||
@ -76,15 +72,15 @@ type WatchOptions struct {
|
|||||||
// Other options for implementations of the interface
|
// Other options for implementations of the interface
|
||||||
// can be stored in a context
|
// can be stored in a context
|
||||||
Context context.Context
|
Context context.Context
|
||||||
// Namespace to watch
|
// Domain to watch
|
||||||
Namespace string
|
Domain string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewWatchOptions returns watch options filled by opts
|
// NewWatchOptions returns watch options filled by opts
|
||||||
func NewWatchOptions(opts ...WatchOption) WatchOptions {
|
func NewWatchOptions(opts ...WatchOption) WatchOptions {
|
||||||
options := WatchOptions{
|
options := WatchOptions{
|
||||||
Namespace: DefaultNamespace,
|
Domain: DefaultDomain,
|
||||||
Context: context.Background(),
|
Context: context.Background(),
|
||||||
}
|
}
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&options)
|
o(&options)
|
||||||
@ -95,8 +91,8 @@ func NewWatchOptions(opts ...WatchOption) WatchOptions {
|
|||||||
// DeregisterOptions holds options for deregister method
|
// DeregisterOptions holds options for deregister method
|
||||||
type DeregisterOptions struct {
|
type DeregisterOptions struct {
|
||||||
Context context.Context
|
Context context.Context
|
||||||
// Namespace the service was registered in
|
// Domain the service was registered in
|
||||||
Namespace string
|
Domain string
|
||||||
// Atempts specify max attempts for deregister
|
// Atempts specify max attempts for deregister
|
||||||
Attempts int
|
Attempts int
|
||||||
}
|
}
|
||||||
@ -104,8 +100,8 @@ type DeregisterOptions struct {
|
|||||||
// NewDeregisterOptions returns options for deregister filled by opts
|
// NewDeregisterOptions returns options for deregister filled by opts
|
||||||
func NewDeregisterOptions(opts ...DeregisterOption) DeregisterOptions {
|
func NewDeregisterOptions(opts ...DeregisterOption) DeregisterOptions {
|
||||||
options := DeregisterOptions{
|
options := DeregisterOptions{
|
||||||
Namespace: DefaultNamespace,
|
Domain: DefaultDomain,
|
||||||
Context: context.Background(),
|
Context: context.Background(),
|
||||||
}
|
}
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&options)
|
o(&options)
|
||||||
@ -116,15 +112,15 @@ func NewDeregisterOptions(opts ...DeregisterOption) DeregisterOptions {
|
|||||||
// LookupOptions holds lookup options
|
// LookupOptions holds lookup options
|
||||||
type LookupOptions struct {
|
type LookupOptions struct {
|
||||||
Context context.Context
|
Context context.Context
|
||||||
// Namespace to scope the request to
|
// Domain to scope the request to
|
||||||
Namespace string
|
Domain string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLookupOptions returns lookup options filled by opts
|
// NewLookupOptions returns lookup options filled by opts
|
||||||
func NewLookupOptions(opts ...LookupOption) LookupOptions {
|
func NewLookupOptions(opts ...LookupOption) LookupOptions {
|
||||||
options := LookupOptions{
|
options := LookupOptions{
|
||||||
Namespace: DefaultNamespace,
|
Domain: DefaultDomain,
|
||||||
Context: context.Background(),
|
Context: context.Background(),
|
||||||
}
|
}
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&options)
|
o(&options)
|
||||||
@ -134,17 +130,16 @@ func NewLookupOptions(opts ...LookupOption) LookupOptions {
|
|||||||
|
|
||||||
// ListOptions holds the list options for list method
|
// ListOptions holds the list options for list method
|
||||||
type ListOptions struct {
|
type ListOptions struct {
|
||||||
// Context used to store additional options
|
|
||||||
Context context.Context
|
Context context.Context
|
||||||
// Namespace to scope the request to
|
// Domain to scope the request to
|
||||||
Namespace string
|
Domain string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewListOptions returns list options filled by opts
|
// NewListOptions returns list options filled by opts
|
||||||
func NewListOptions(opts ...ListOption) ListOptions {
|
func NewListOptions(opts ...ListOption) ListOptions {
|
||||||
options := ListOptions{
|
options := ListOptions{
|
||||||
Namespace: DefaultNamespace,
|
Domain: DefaultDomain,
|
||||||
Context: context.Background(),
|
Context: context.Background(),
|
||||||
}
|
}
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&options)
|
o(&options)
|
||||||
@ -222,10 +217,10 @@ func RegisterContext(ctx context.Context) RegisterOption { // nolint: golint,rev
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterNamespace secifies register Namespace
|
// RegisterDomain secifies register domain
|
||||||
func RegisterNamespace(d string) RegisterOption { // nolint: golint,revive
|
func RegisterDomain(d string) RegisterOption { // nolint: golint,revive
|
||||||
return func(o *RegisterOptions) {
|
return func(o *RegisterOptions) {
|
||||||
o.Namespace = d
|
o.Domain = d
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,10 +238,10 @@ func WatchContext(ctx context.Context) WatchOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WatchNamespace sets the Namespace for watch
|
// WatchDomain sets the domain for watch
|
||||||
func WatchNamespace(d string) WatchOption {
|
func WatchDomain(d string) WatchOption {
|
||||||
return func(o *WatchOptions) {
|
return func(o *WatchOptions) {
|
||||||
o.Namespace = d
|
o.Domain = d
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,10 +259,10 @@ func DeregisterContext(ctx context.Context) DeregisterOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeregisterNamespace specifies deregister Namespace
|
// DeregisterDomain specifies deregister domain
|
||||||
func DeregisterNamespace(d string) DeregisterOption {
|
func DeregisterDomain(d string) DeregisterOption {
|
||||||
return func(o *DeregisterOptions) {
|
return func(o *DeregisterOptions) {
|
||||||
o.Namespace = d
|
o.Domain = d
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,10 +273,10 @@ func LookupContext(ctx context.Context) LookupOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// LookupNamespace sets the Namespace for lookup
|
// LookupDomain sets the domain for lookup
|
||||||
func LookupNamespace(d string) LookupOption {
|
func LookupDomain(d string) LookupOption {
|
||||||
return func(o *LookupOptions) {
|
return func(o *LookupOptions) {
|
||||||
o.Namespace = d
|
o.Domain = d
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,10 +287,10 @@ func ListContext(ctx context.Context) ListOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListNamespace sets the Namespace for list method
|
// ListDomain sets the domain for list method
|
||||||
func ListNamespace(d string) ListOption {
|
func ListDomain(d string) ListOption {
|
||||||
return func(o *ListOptions) {
|
return func(o *ListOptions) {
|
||||||
o.Namespace = d
|
o.Domain = d
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,9 +300,3 @@ func Name(n string) Option {
|
|||||||
o.Name = n
|
o.Name = n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Codec(c codec.Codec) Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.Codec = c
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -9,12 +9,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// WildcardNamespace indicates any Namespace
|
// WildcardDomain indicates any domain
|
||||||
WildcardNamespace = "*"
|
WildcardDomain = "*"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DefaultNamespace to use if none was provided in options
|
// DefaultDomain to use if none was provided in options
|
||||||
var DefaultNamespace = "micro"
|
var DefaultDomain = "micro"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// DefaultRegister is the global default register
|
// DefaultRegister is the global default register
|
||||||
@ -59,17 +59,26 @@ type Register interface {
|
|||||||
|
|
||||||
// Service holds service register info
|
// Service holds service register info
|
||||||
type Service struct {
|
type Service struct {
|
||||||
Name string `json:"name,omitempty"`
|
Name string `json:"name"`
|
||||||
Version string `json:"version,omitempty"`
|
Version string `json:"version"`
|
||||||
Nodes []*Node `json:"nodes,omitempty"`
|
Metadata metadata.Metadata `json:"metadata"`
|
||||||
Namespace string `json:"namespace,omitempty"`
|
Endpoints []*Endpoint `json:"endpoints"`
|
||||||
|
Nodes []*Node `json:"nodes"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Node holds node register info
|
// Node holds node register info
|
||||||
type Node struct {
|
type Node struct {
|
||||||
Metadata metadata.Metadata `json:"metadata,omitempty"`
|
Metadata metadata.Metadata `json:"metadata"`
|
||||||
ID string `json:"id,omitempty"`
|
ID string `json:"id"`
|
||||||
Address string `json:"address,omitempty"`
|
Address string `json:"address"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Endpoint holds endpoint register info
|
||||||
|
type Endpoint struct {
|
||||||
|
Request string `json:"request"`
|
||||||
|
Response string `json:"response"`
|
||||||
|
Metadata metadata.Metadata `json:"metadata"`
|
||||||
|
Name string `json:"name"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Option func signature
|
// Option func signature
|
||||||
|
@ -15,31 +15,31 @@ type Watcher interface {
|
|||||||
// the watcher. Actions can be create, update, delete
|
// the watcher. Actions can be create, update, delete
|
||||||
type Result struct {
|
type Result struct {
|
||||||
// Service holds register service
|
// Service holds register service
|
||||||
Service *Service `json:"service,omitempty"`
|
Service *Service
|
||||||
// Action holds the action
|
// Action holds the action
|
||||||
Action EventType `json:"action,omitempty"`
|
Action string
|
||||||
}
|
}
|
||||||
|
|
||||||
// EventType defines register event type
|
// EventType defines register event type
|
||||||
type EventType int
|
type EventType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// EventCreate is emitted when a new service is registered
|
// Create is emitted when a new service is registered
|
||||||
EventCreate EventType = iota
|
Create EventType = iota
|
||||||
// EventDelete is emitted when an existing service is deregistered
|
// Delete is emitted when an existing service is deregistered
|
||||||
EventDelete
|
Delete
|
||||||
// EventUpdate is emitted when an existing service is updated
|
// Update is emitted when an existing service is updated
|
||||||
EventUpdate
|
Update
|
||||||
)
|
)
|
||||||
|
|
||||||
// String returns human readable event type
|
// String returns human readable event type
|
||||||
func (t EventType) String() string {
|
func (t EventType) String() string {
|
||||||
switch t {
|
switch t {
|
||||||
case EventCreate:
|
case Create:
|
||||||
return "create"
|
return "create"
|
||||||
case EventDelete:
|
case Delete:
|
||||||
return "delete"
|
return "delete"
|
||||||
case EventUpdate:
|
case Update:
|
||||||
return "update"
|
return "update"
|
||||||
default:
|
default:
|
||||||
return "unknown"
|
return "unknown"
|
||||||
@ -49,11 +49,11 @@ func (t EventType) String() string {
|
|||||||
// Event is register event
|
// Event is register event
|
||||||
type Event struct {
|
type Event struct {
|
||||||
// Timestamp is event timestamp
|
// Timestamp is event timestamp
|
||||||
Timestamp time.Time `json:"timestamp,omitempty"`
|
Timestamp time.Time
|
||||||
// Service is register service
|
// Service is register service
|
||||||
Service *Service `json:"service,omitempty"`
|
Service *Service
|
||||||
// ID is register id
|
// ID is register id
|
||||||
ID string `json:"id,omitempty"`
|
ID string
|
||||||
// Type defines type of event
|
// Type defines type of event
|
||||||
Type EventType `json:"type,omitempty"`
|
Type EventType
|
||||||
}
|
}
|
||||||
|
@ -15,15 +15,6 @@ func FromContext(ctx context.Context) (Router, bool) {
|
|||||||
return c, ok
|
return c, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustContext get router from context
|
|
||||||
func MustContext(ctx context.Context) Router {
|
|
||||||
r, ok := FromContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
panic("missing router")
|
|
||||||
}
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewContext put router in context
|
// NewContext put router in context
|
||||||
func NewContext(ctx context.Context, c Router) context.Context {
|
func NewContext(ctx context.Context, c Router) context.Context {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
|
@ -80,7 +80,7 @@ func Name(n string) Option {
|
|||||||
// NewOptions returns router default options
|
// NewOptions returns router default options
|
||||||
func NewOptions(opts ...Option) Options {
|
func NewOptions(opts ...Option) Options {
|
||||||
options := Options{
|
options := Options{
|
||||||
ID: id.MustNew(),
|
ID: id.Must(),
|
||||||
Network: DefaultNetwork,
|
Network: DefaultNetwork,
|
||||||
Register: register.DefaultRegister,
|
Register: register.DefaultRegister,
|
||||||
Logger: logger.DefaultLogger,
|
Logger: logger.DefaultLogger,
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
package semconv
|
|
||||||
|
|
||||||
var (
|
|
||||||
// HeaderTopic is the header name that contains topic name
|
|
||||||
HeaderTopic = "Micro-Topic"
|
|
||||||
// HeaderContentType specifies content type of message
|
|
||||||
HeaderContentType = "Content-Type"
|
|
||||||
// HeaderEndpoint specifies endpoint in service
|
|
||||||
HeaderEndpoint = "Micro-Endpoint"
|
|
||||||
// HeaderService specifies service
|
|
||||||
HeaderService = "Micro-Service"
|
|
||||||
// HeaderTimeout specifies timeout of operation
|
|
||||||
HeaderTimeout = "Micro-Timeout"
|
|
||||||
// HeaderAuthorization specifies Authorization header
|
|
||||||
HeaderAuthorization = "Authorization"
|
|
||||||
// HeaderXRequestID specifies request id
|
|
||||||
HeaderXRequestID = "X-Request-Id"
|
|
||||||
)
|
|
@ -15,15 +15,6 @@ func FromContext(ctx context.Context) (Server, bool) {
|
|||||||
return c, ok
|
return c, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustContext returns Server from context
|
|
||||||
func MustContext(ctx context.Context) Server {
|
|
||||||
s, ok := FromContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
panic("missing server")
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewContext stores Server to context
|
// NewContext stores Server to context
|
||||||
func NewContext(ctx context.Context, s Server) context.Context {
|
func NewContext(ctx context.Context, s Server) context.Context {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@ -31,21 +32,38 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type rpcHandler struct {
|
type rpcHandler struct {
|
||||||
opts HandlerOptions
|
opts HandlerOptions
|
||||||
handler interface{}
|
handler interface{}
|
||||||
name string
|
name string
|
||||||
|
endpoints []*register.Endpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRPCHandler(handler interface{}, opts ...HandlerOption) Handler {
|
func newRPCHandler(handler interface{}, opts ...HandlerOption) Handler {
|
||||||
options := NewHandlerOptions(opts...)
|
options := NewHandlerOptions(opts...)
|
||||||
|
|
||||||
|
typ := reflect.TypeOf(handler)
|
||||||
hdlr := reflect.ValueOf(handler)
|
hdlr := reflect.ValueOf(handler)
|
||||||
name := reflect.Indirect(hdlr).Type().Name()
|
name := reflect.Indirect(hdlr).Type().Name()
|
||||||
|
|
||||||
|
var endpoints []*register.Endpoint
|
||||||
|
|
||||||
|
for m := 0; m < typ.NumMethod(); m++ {
|
||||||
|
if e := register.ExtractEndpoint(typ.Method(m)); e != nil {
|
||||||
|
e.Name = name + "." + e.Name
|
||||||
|
|
||||||
|
for k, v := range options.Metadata[e.Name] {
|
||||||
|
e.Metadata[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoints = append(endpoints, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return &rpcHandler{
|
return &rpcHandler{
|
||||||
name: name,
|
name: name,
|
||||||
handler: handler,
|
handler: handler,
|
||||||
opts: options,
|
endpoints: endpoints,
|
||||||
|
opts: options,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,6 +75,10 @@ func (r *rpcHandler) Handler() interface{} {
|
|||||||
return r.handler
|
return r.handler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *rpcHandler) Endpoints() []*register.Endpoint {
|
||||||
|
return r.endpoints
|
||||||
|
}
|
||||||
|
|
||||||
func (r *rpcHandler) Options() HandlerOptions {
|
func (r *rpcHandler) Options() HandlerOptions {
|
||||||
return r.opts
|
return r.opts
|
||||||
}
|
}
|
||||||
@ -227,6 +249,35 @@ func (n *noopServer) Register() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
n.RLock()
|
||||||
|
handlerList := make([]string, 0, len(n.handlers))
|
||||||
|
for n := range n.handlers {
|
||||||
|
handlerList = append(handlerList, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(handlerList)
|
||||||
|
|
||||||
|
subscriberList := make([]*subscriber, 0, len(n.subscribers))
|
||||||
|
for e := range n.subscribers {
|
||||||
|
subscriberList = append(subscriberList, e)
|
||||||
|
}
|
||||||
|
sort.Slice(subscriberList, func(i, j int) bool {
|
||||||
|
return subscriberList[i].topic > subscriberList[j].topic
|
||||||
|
})
|
||||||
|
|
||||||
|
endpoints := make([]*register.Endpoint, 0, len(handlerList)+len(subscriberList))
|
||||||
|
for _, h := range handlerList {
|
||||||
|
endpoints = append(endpoints, n.handlers[h].Endpoints()...)
|
||||||
|
}
|
||||||
|
for _, e := range subscriberList {
|
||||||
|
endpoints = append(endpoints, e.Endpoints()...)
|
||||||
|
}
|
||||||
|
n.RUnlock()
|
||||||
|
|
||||||
|
service.Nodes[0].Metadata["protocol"] = "noop"
|
||||||
|
service.Nodes[0].Metadata["transport"] = service.Nodes[0].Metadata["protocol"]
|
||||||
|
service.Endpoints = endpoints
|
||||||
|
|
||||||
n.RLock()
|
n.RLock()
|
||||||
registered := n.registered
|
registered := n.registered
|
||||||
n.RUnlock()
|
n.RUnlock()
|
||||||
@ -525,6 +576,7 @@ func (n *noopServer) Stop() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newSubscriber(topic string, sub interface{}, opts ...SubscriberOption) Subscriber {
|
func newSubscriber(topic string, sub interface{}, opts ...SubscriberOption) Subscriber {
|
||||||
|
var endpoints []*register.Endpoint
|
||||||
var handlers []*handler
|
var handlers []*handler
|
||||||
|
|
||||||
options := NewSubscriberOptions(opts...)
|
options := NewSubscriberOptions(opts...)
|
||||||
@ -543,7 +595,18 @@ func newSubscriber(topic string, sub interface{}, opts ...SubscriberOption) Subs
|
|||||||
}
|
}
|
||||||
|
|
||||||
handlers = append(handlers, h)
|
handlers = append(handlers, h)
|
||||||
|
ep := ®ister.Endpoint{
|
||||||
|
Name: "Func",
|
||||||
|
Request: register.ExtractSubValue(typ),
|
||||||
|
Metadata: metadata.New(2),
|
||||||
|
}
|
||||||
|
ep.Metadata.Set("topic", topic)
|
||||||
|
ep.Metadata.Set("subscriber", "true")
|
||||||
|
endpoints = append(endpoints, ep)
|
||||||
} else {
|
} else {
|
||||||
|
hdlr := reflect.ValueOf(sub)
|
||||||
|
name := reflect.Indirect(hdlr).Type().Name()
|
||||||
|
|
||||||
for m := 0; m < typ.NumMethod(); m++ {
|
for m := 0; m < typ.NumMethod(); m++ {
|
||||||
method := typ.Method(m)
|
method := typ.Method(m)
|
||||||
h := &handler{
|
h := &handler{
|
||||||
@ -559,6 +622,14 @@ func newSubscriber(topic string, sub interface{}, opts ...SubscriberOption) Subs
|
|||||||
}
|
}
|
||||||
|
|
||||||
handlers = append(handlers, h)
|
handlers = append(handlers, h)
|
||||||
|
ep := ®ister.Endpoint{
|
||||||
|
Name: name + "." + method.Name,
|
||||||
|
Request: register.ExtractSubValue(method.Type),
|
||||||
|
Metadata: metadata.New(2),
|
||||||
|
}
|
||||||
|
ep.Metadata.Set("topic", topic)
|
||||||
|
ep.Metadata.Set("subscriber", "true")
|
||||||
|
endpoints = append(endpoints, ep)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -568,6 +639,7 @@ func newSubscriber(topic string, sub interface{}, opts ...SubscriberOption) Subs
|
|||||||
topic: topic,
|
topic: topic,
|
||||||
subscriber: sub,
|
subscriber: sub,
|
||||||
handlers: handlers,
|
handlers: handlers,
|
||||||
|
endpoints: endpoints,
|
||||||
opts: options,
|
opts: options,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -651,7 +723,7 @@ func (n *noopServer) createSubHandler(sb *subscriber, opts Options) broker.Handl
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
opts.Hooks.EachPrev(func(hook options.Hook) {
|
opts.Hooks.EachNext(func(hook options.Hook) {
|
||||||
if h, ok := hook.(HookSubHandler); ok {
|
if h, ok := hook.(HookSubHandler); ok {
|
||||||
fn = h(fn)
|
fn = h(fn)
|
||||||
}
|
}
|
||||||
@ -694,6 +766,10 @@ func (s *subscriber) Subscriber() interface{} {
|
|||||||
return s.subscriber
|
return s.subscriber
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *subscriber) Endpoints() []*register.Endpoint {
|
||||||
|
return s.endpoints
|
||||||
|
}
|
||||||
|
|
||||||
func (s *subscriber) Options() SubscriberOptions {
|
func (s *subscriber) Options() SubscriberOptions {
|
||||||
return s.opts
|
return s.opts
|
||||||
}
|
}
|
||||||
@ -704,7 +780,8 @@ type subscriber struct {
|
|||||||
typ reflect.Type
|
typ reflect.Type
|
||||||
subscriber interface{}
|
subscriber interface{}
|
||||||
|
|
||||||
handlers []*handler
|
endpoints []*register.Endpoint
|
||||||
|
handlers []*handler
|
||||||
|
|
||||||
rcvr reflect.Value
|
rcvr reflect.Value
|
||||||
opts SubscriberOptions
|
opts SubscriberOptions
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"go.unistack.org/micro/v3/client"
|
"go.unistack.org/micro/v3/client"
|
||||||
"go.unistack.org/micro/v3/codec"
|
"go.unistack.org/micro/v3/codec"
|
||||||
"go.unistack.org/micro/v3/logger"
|
"go.unistack.org/micro/v3/logger"
|
||||||
"go.unistack.org/micro/v3/options"
|
|
||||||
"go.unistack.org/micro/v3/server"
|
"go.unistack.org/micro/v3/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -85,40 +84,3 @@ func TestNoopSub(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHooks_Wrap(t *testing.T) {
|
|
||||||
n := 5
|
|
||||||
fn1 := func(next server.FuncSubHandler) server.FuncSubHandler {
|
|
||||||
return func(ctx context.Context, msg server.Message) (err error) {
|
|
||||||
n *= 2
|
|
||||||
return next(ctx, msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn2 := func(next server.FuncSubHandler) server.FuncSubHandler {
|
|
||||||
return func(ctx context.Context, msg server.Message) (err error) {
|
|
||||||
n -= 10
|
|
||||||
return next(ctx, msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hs := &options.Hooks{}
|
|
||||||
hs.Append(server.HookSubHandler(fn1), server.HookSubHandler(fn2))
|
|
||||||
|
|
||||||
var fn = func(ctx context.Context, msg server.Message) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
hs.EachPrev(func(hook options.Hook) {
|
|
||||||
if h, ok := hook.(server.HookSubHandler); ok {
|
|
||||||
fn = h(fn)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if err := fn(nil, nil); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if n != 0 {
|
|
||||||
t.Fatalf("uncorrected hooks call")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -100,7 +100,7 @@ func NewOptions(opts ...Option) Options {
|
|||||||
Address: DefaultAddress,
|
Address: DefaultAddress,
|
||||||
Name: DefaultName,
|
Name: DefaultName,
|
||||||
Version: DefaultVersion,
|
Version: DefaultVersion,
|
||||||
ID: id.MustNew(),
|
ID: id.Must(),
|
||||||
Namespace: DefaultNamespace,
|
Namespace: DefaultNamespace,
|
||||||
GracefulTimeout: DefaultGracefulTimeout,
|
GracefulTimeout: DefaultGracefulTimeout,
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ var (
|
|||||||
|
|
||||||
opts := []register.RegisterOption{
|
opts := []register.RegisterOption{
|
||||||
register.RegisterTTL(config.RegisterTTL),
|
register.RegisterTTL(config.RegisterTTL),
|
||||||
register.RegisterNamespace(config.Namespace),
|
register.RegisterDomain(config.Namespace),
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i <= config.RegisterAttempts; i++ {
|
for i := 0; i <= config.RegisterAttempts; i++ {
|
||||||
@ -36,7 +36,7 @@ var (
|
|||||||
var err error
|
var err error
|
||||||
|
|
||||||
opts := []register.DeregisterOption{
|
opts := []register.DeregisterOption{
|
||||||
register.DeregisterNamespace(config.Namespace),
|
register.DeregisterDomain(config.Namespace),
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i <= config.DeregisterAttempts; i++ {
|
for i := 0; i <= config.DeregisterAttempts; i++ {
|
||||||
@ -82,8 +82,9 @@ func NewRegisterService(s Server) (*register.Service, error) {
|
|||||||
node.Metadata["register"] = opts.Register.String()
|
node.Metadata["register"] = opts.Register.String()
|
||||||
|
|
||||||
return ®ister.Service{
|
return ®ister.Service{
|
||||||
Name: opts.Name,
|
Name: opts.Name,
|
||||||
Version: opts.Version,
|
Version: opts.Version,
|
||||||
Nodes: []*register.Node{node},
|
Nodes: []*register.Node{node},
|
||||||
|
Metadata: metadata.New(0),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"go.unistack.org/micro/v3/codec"
|
"go.unistack.org/micro/v3/codec"
|
||||||
"go.unistack.org/micro/v3/metadata"
|
"go.unistack.org/micro/v3/metadata"
|
||||||
|
"go.unistack.org/micro/v3/register"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DefaultServer default server
|
// DefaultServer default server
|
||||||
@ -169,6 +170,7 @@ type Stream interface {
|
|||||||
type Handler interface {
|
type Handler interface {
|
||||||
Name() string
|
Name() string
|
||||||
Handler() interface{}
|
Handler() interface{}
|
||||||
|
Endpoints() []*register.Endpoint
|
||||||
Options() HandlerOptions
|
Options() HandlerOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,5 +180,6 @@ type Handler interface {
|
|||||||
type Subscriber interface {
|
type Subscriber interface {
|
||||||
Topic() string
|
Topic() string
|
||||||
Subscriber() interface{}
|
Subscriber() interface{}
|
||||||
|
Endpoints() []*register.Endpoint
|
||||||
Options() SubscriberOptions
|
Options() SubscriberOptions
|
||||||
}
|
}
|
||||||
|
@ -15,15 +15,6 @@ func FromContext(ctx context.Context) (Store, bool) {
|
|||||||
return c, ok
|
return c, ok
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustContext get store from context
|
|
||||||
func MustContext(ctx context.Context) Store {
|
|
||||||
s, ok := FromContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
panic("missing store")
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewContext put store in context
|
// NewContext put store in context
|
||||||
func NewContext(ctx context.Context, c Store) context.Context {
|
func NewContext(ctx context.Context, c Store) context.Context {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
|
@ -123,7 +123,7 @@ func (m *memoryStore) Init(opts ...store.Option) error {
|
|||||||
m.funcList = m.fnList
|
m.funcList = m.fnList
|
||||||
m.funcDelete = m.fnDelete
|
m.funcDelete = m.fnDelete
|
||||||
|
|
||||||
m.opts.Hooks.EachPrev(func(hook options.Hook) {
|
m.opts.Hooks.EachNext(func(hook options.Hook) {
|
||||||
switch h := hook.(type) {
|
switch h := hook.(type) {
|
||||||
case store.HookRead:
|
case store.HookRead:
|
||||||
m.funcRead = h(m.funcRead)
|
m.funcRead = h(m.funcRead)
|
||||||
|
@ -54,7 +54,7 @@ func (n *noopStore) Init(opts ...Option) error {
|
|||||||
n.funcList = n.fnList
|
n.funcList = n.fnList
|
||||||
n.funcDelete = n.fnDelete
|
n.funcDelete = n.fnDelete
|
||||||
|
|
||||||
n.opts.Hooks.EachPrev(func(hook options.Hook) {
|
n.opts.Hooks.EachNext(func(hook options.Hook) {
|
||||||
switch h := hook.(type) {
|
switch h := hook.(type) {
|
||||||
case HookRead:
|
case HookRead:
|
||||||
n.funcRead = h(n.funcRead)
|
n.funcRead = h(n.funcRead)
|
||||||
|
@ -18,15 +18,6 @@ func FromContext(ctx context.Context) (Tracer, bool) {
|
|||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustContext returns a tracer from context
|
|
||||||
func MustContext(ctx context.Context) Tracer {
|
|
||||||
t, ok := FromContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
panic("missing tracer")
|
|
||||||
}
|
|
||||||
return t
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewContext saves the tracer in the context
|
// NewContext saves the tracer in the context
|
||||||
func NewContext(ctx context.Context, tracer Tracer) context.Context {
|
func NewContext(ctx context.Context, tracer Tracer) context.Context {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
@ -37,15 +28,6 @@ func NewContext(ctx context.Context, tracer Tracer) context.Context {
|
|||||||
|
|
||||||
type spanKey struct{}
|
type spanKey struct{}
|
||||||
|
|
||||||
// SpanFromContext returns a span from context
|
|
||||||
func SpanMustContext(ctx context.Context) Span {
|
|
||||||
sp, ok := SpanFromContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
panic("missing span")
|
|
||||||
}
|
|
||||||
return sp
|
|
||||||
}
|
|
||||||
|
|
||||||
// SpanFromContext returns a span from context
|
// SpanFromContext returns a span from context
|
||||||
func SpanFromContext(ctx context.Context) (Span, bool) {
|
func SpanFromContext(ctx context.Context) (Span, bool) {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
|
@ -25,7 +25,6 @@ func (t *Tracer) Start(ctx context.Context, name string, opts ...tracer.SpanOpti
|
|||||||
name: name,
|
name: name,
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
tracer: t,
|
tracer: t,
|
||||||
labels: options.Labels,
|
|
||||||
kind: options.Kind,
|
kind: options.Kind,
|
||||||
startTime: time.Now(),
|
startTime: time.Now(),
|
||||||
}
|
}
|
||||||
@ -38,14 +37,6 @@ func (t *Tracer) Start(ctx context.Context, name string, opts ...tracer.SpanOpti
|
|||||||
return tracer.NewSpanContext(ctx, span), span
|
return tracer.NewSpanContext(ctx, span), span
|
||||||
}
|
}
|
||||||
|
|
||||||
type memoryStringer struct {
|
|
||||||
s string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s memoryStringer) String() string {
|
|
||||||
return s.s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Tracer) Flush(_ context.Context) error {
|
func (t *Tracer) Flush(_ context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -61,6 +52,14 @@ func (t *Tracer) Name() string {
|
|||||||
return t.opts.Name
|
return t.opts.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type noopStringer struct {
|
||||||
|
s string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s noopStringer) String() string {
|
||||||
|
return s.s
|
||||||
|
}
|
||||||
|
|
||||||
type Span struct {
|
type Span struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
tracer tracer.Tracer
|
tracer tracer.Tracer
|
||||||
@ -68,8 +67,8 @@ type Span struct {
|
|||||||
statusMsg string
|
statusMsg string
|
||||||
startTime time.Time
|
startTime time.Time
|
||||||
finishTime time.Time
|
finishTime time.Time
|
||||||
traceID memoryStringer
|
traceID noopStringer
|
||||||
spanID memoryStringer
|
spanID noopStringer
|
||||||
events []*Event
|
events []*Event
|
||||||
labels []interface{}
|
labels []interface{}
|
||||||
logs []interface{}
|
logs []interface{}
|
||||||
|
@ -2,7 +2,6 @@ package tracer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.unistack.org/micro/v3/util/id"
|
"go.unistack.org/micro/v3/util/id"
|
||||||
)
|
)
|
||||||
@ -21,18 +20,18 @@ func (t *noopTracer) Spans() []Span {
|
|||||||
func (t *noopTracer) Start(ctx context.Context, name string, opts ...SpanOption) (context.Context, Span) {
|
func (t *noopTracer) Start(ctx context.Context, name string, opts ...SpanOption) (context.Context, Span) {
|
||||||
options := NewSpanOptions(opts...)
|
options := NewSpanOptions(opts...)
|
||||||
span := &noopSpan{
|
span := &noopSpan{
|
||||||
name: name,
|
name: name,
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
tracer: t,
|
tracer: t,
|
||||||
startTime: time.Now(),
|
labels: options.Labels,
|
||||||
labels: options.Labels,
|
kind: options.Kind,
|
||||||
kind: options.Kind,
|
|
||||||
}
|
}
|
||||||
span.spanID.s, _ = id.New()
|
span.spanID.s, _ = id.New()
|
||||||
span.traceID.s, _ = id.New()
|
span.traceID.s, _ = id.New()
|
||||||
if span.ctx == nil {
|
if span.ctx == nil {
|
||||||
span.ctx = context.Background()
|
span.ctx = context.Background()
|
||||||
}
|
}
|
||||||
|
t.spans = append(t.spans, span)
|
||||||
return NewSpanContext(ctx, span), span
|
return NewSpanContext(ctx, span), span
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,18 +58,23 @@ func (t *noopTracer) Name() string {
|
|||||||
return t.opts.Name
|
return t.opts.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type noopEvent struct {
|
||||||
|
name string
|
||||||
|
labels []interface{}
|
||||||
|
}
|
||||||
|
|
||||||
type noopSpan struct {
|
type noopSpan struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
tracer Tracer
|
tracer Tracer
|
||||||
name string
|
name string
|
||||||
statusMsg string
|
statusMsg string
|
||||||
startTime time.Time
|
traceID noopStringer
|
||||||
finishTime time.Time
|
spanID noopStringer
|
||||||
traceID noopStringer
|
events []*noopEvent
|
||||||
spanID noopStringer
|
labels []interface{}
|
||||||
labels []interface{}
|
logs []interface{}
|
||||||
kind SpanKind
|
kind SpanKind
|
||||||
status SpanStatus
|
status SpanStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *noopSpan) TraceID() string {
|
func (s *noopSpan) TraceID() string {
|
||||||
@ -82,7 +86,6 @@ func (s *noopSpan) SpanID() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *noopSpan) Finish(_ ...SpanOption) {
|
func (s *noopSpan) Finish(_ ...SpanOption) {
|
||||||
s.finishTime = time.Now()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *noopSpan) Context() context.Context {
|
func (s *noopSpan) Context() context.Context {
|
||||||
@ -94,6 +97,8 @@ func (s *noopSpan) Tracer() Tracer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *noopSpan) AddEvent(name string, opts ...EventOption) {
|
func (s *noopSpan) AddEvent(name string, opts ...EventOption) {
|
||||||
|
options := NewEventOptions(opts...)
|
||||||
|
s.events = append(s.events, &noopEvent{name: name, labels: options.Labels})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *noopSpan) SetName(name string) {
|
func (s *noopSpan) SetName(name string) {
|
||||||
@ -101,6 +106,7 @@ func (s *noopSpan) SetName(name string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *noopSpan) AddLogs(kv ...interface{}) {
|
func (s *noopSpan) AddLogs(kv ...interface{}) {
|
||||||
|
s.logs = append(s.logs, kv...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *noopSpan) AddLabels(kv ...interface{}) {
|
func (s *noopSpan) AddLabels(kv ...interface{}) {
|
||||||
|
27
util/buf/buf.go
Normal file
27
util/buf/buf.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package buf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ io.Closer = &Buffer{}
|
||||||
|
|
||||||
|
// Buffer bytes.Buffer wrapper to satisfie io.Closer interface
|
||||||
|
type Buffer struct {
|
||||||
|
*bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close reset buffer contents
|
||||||
|
func (b *Buffer) Close() error {
|
||||||
|
b.Buffer.Reset()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates new buffer that satisfies Closer interface
|
||||||
|
func New(b *bytes.Buffer) *Buffer {
|
||||||
|
if b == nil {
|
||||||
|
b = bytes.NewBuffer(nil)
|
||||||
|
}
|
||||||
|
return &Buffer{b}
|
||||||
|
}
|
@ -1,85 +0,0 @@
|
|||||||
package buffer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ io.WriteCloser = (*DelayedBuffer)(nil)
|
|
||||||
|
|
||||||
// DelayedBuffer is the buffer that holds items until either the buffer filled or a specified time limit is reached
|
|
||||||
type DelayedBuffer struct {
|
|
||||||
mu sync.Mutex
|
|
||||||
maxWait time.Duration
|
|
||||||
flushTime time.Time
|
|
||||||
buffer chan []byte
|
|
||||||
ticker *time.Ticker
|
|
||||||
w io.Writer
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDelayedBuffer(size int, maxWait time.Duration, w io.Writer) *DelayedBuffer {
|
|
||||||
b := &DelayedBuffer{
|
|
||||||
buffer: make(chan []byte, size),
|
|
||||||
ticker: time.NewTicker(maxWait),
|
|
||||||
w: w,
|
|
||||||
flushTime: time.Now(),
|
|
||||||
maxWait: maxWait,
|
|
||||||
}
|
|
||||||
b.loop()
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *DelayedBuffer) loop() {
|
|
||||||
go func() {
|
|
||||||
for range b.ticker.C {
|
|
||||||
b.mu.Lock()
|
|
||||||
if time.Since(b.flushTime) > b.maxWait {
|
|
||||||
b.flush()
|
|
||||||
}
|
|
||||||
b.mu.Unlock()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *DelayedBuffer) flush() {
|
|
||||||
bufLen := len(b.buffer)
|
|
||||||
if bufLen > 0 {
|
|
||||||
tmp := make([][]byte, bufLen)
|
|
||||||
for i := 0; i < bufLen; i++ {
|
|
||||||
tmp[i] = <-b.buffer
|
|
||||||
}
|
|
||||||
for _, t := range tmp {
|
|
||||||
_, b.err = b.w.Write(t)
|
|
||||||
}
|
|
||||||
b.flushTime = time.Now()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *DelayedBuffer) Put(items ...[]byte) {
|
|
||||||
b.mu.Lock()
|
|
||||||
for _, item := range items {
|
|
||||||
select {
|
|
||||||
case b.buffer <- item:
|
|
||||||
default:
|
|
||||||
b.flush()
|
|
||||||
b.buffer <- item
|
|
||||||
}
|
|
||||||
}
|
|
||||||
b.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *DelayedBuffer) Close() error {
|
|
||||||
b.mu.Lock()
|
|
||||||
b.flush()
|
|
||||||
close(b.buffer)
|
|
||||||
b.ticker.Stop()
|
|
||||||
b.mu.Unlock()
|
|
||||||
return b.err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *DelayedBuffer) Write(data []byte) (int, error) {
|
|
||||||
b.Put(data)
|
|
||||||
return len(data), b.err
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
package buffer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestTimedBuffer(t *testing.T) {
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
b := NewDelayedBuffer(100, 300*time.Millisecond, buf)
|
|
||||||
for i := 0; i < 100; i++ {
|
|
||||||
_, _ = b.Write([]byte(`test`))
|
|
||||||
}
|
|
||||||
if buf.Len() != 0 {
|
|
||||||
t.Fatal("delayed write not worked")
|
|
||||||
}
|
|
||||||
time.Sleep(400 * time.Millisecond)
|
|
||||||
if buf.Len() == 0 {
|
|
||||||
t.Fatal("delayed write not worked")
|
|
||||||
}
|
|
||||||
}
|
|
22
util/id/LICENSE
Normal file
22
util/id/LICENSE
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2018-2021 Matous Dzivjak <matousdzivjak@gmail.com>
|
||||||
|
Copyright (c) 2021 Unistack LLC <v.tolstov@unistack.org>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
163
util/id/id.go
163
util/id/id.go
@ -1,159 +1,112 @@
|
|||||||
package id
|
package id
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"math"
|
||||||
"time"
|
|
||||||
|
|
||||||
uuidv8 "github.com/ash3in/uuidv8"
|
"go.unistack.org/micro/v3/logger"
|
||||||
"github.com/google/uuid"
|
|
||||||
nanoid "github.com/matoous/go-nanoid"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var generatedNode [6]byte
|
// DefaultAlphabet is the alphabet used for ID characters by default
|
||||||
|
var DefaultAlphabet = []rune("6789BCDFGHJKLMNPQRTWbcdfghjkmnpqrtwz")
|
||||||
|
|
||||||
func init() {
|
// DefaultSize is the size used for ID by default
|
||||||
if _, err := rand.Read(generatedNode[:]); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Type int
|
|
||||||
|
|
||||||
const (
|
|
||||||
TypeUnspecified Type = iota
|
|
||||||
TypeNanoid
|
|
||||||
TypeUUIDv8
|
|
||||||
)
|
|
||||||
|
|
||||||
// DefaultNanoidAlphabet is the alphabet used for ID characters by default
|
|
||||||
var DefaultNanoidAlphabet = "6789BCDFGHJKLMNPQRTWbcdfghjkmnpqrtwz"
|
|
||||||
|
|
||||||
// DefaultNanoidSize is the size used for ID by default
|
|
||||||
// To get uuid like collision specify 21
|
// To get uuid like collision specify 21
|
||||||
var DefaultNanoidSize = 16
|
var DefaultSize = 16
|
||||||
|
|
||||||
type Generator struct {
|
// getMask generates bit mask used to obtain bits from the random bytes that are used to get index of random character
|
||||||
opts Options
|
// from the alphabet. Example: if the alphabet has 6 = (110)_2 characters it is sufficient to use mask 7 = (111)_2
|
||||||
}
|
func getMask(alphabetSize int) int {
|
||||||
|
for i := 1; i <= 8; i++ {
|
||||||
func (g *Generator) MustNew() string {
|
mask := (2 << uint(i)) - 1
|
||||||
id, err := g.New()
|
if mask >= alphabetSize-1 {
|
||||||
if err != nil {
|
return mask
|
||||||
panic(err)
|
}
|
||||||
}
|
}
|
||||||
return id
|
return 0
|
||||||
}
|
|
||||||
|
|
||||||
func (g *Generator) New() (string, error) {
|
|
||||||
switch g.opts.Type {
|
|
||||||
case TypeNanoid:
|
|
||||||
if len(g.opts.NanoidAlphabet) == 0 || len(g.opts.NanoidAlphabet) > 255 {
|
|
||||||
return "", errors.New("invalid option, NanoidAlphabet must not be empty and contain no more than 255 chars")
|
|
||||||
}
|
|
||||||
if g.opts.NanoidSize <= 0 {
|
|
||||||
return "", errors.New("invalid option, NanoidSize must be positive integer")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nanoid.Generate(g.opts.NanoidAlphabet, g.opts.NanoidSize)
|
|
||||||
case TypeUUIDv8:
|
|
||||||
timestamp := uint64(time.Now().UnixNano())
|
|
||||||
clockSeq := make([]byte, 2)
|
|
||||||
if _, err := rand.Read(clockSeq); err != nil {
|
|
||||||
return "", fmt.Errorf("failed to generate random clock sequence: %w", err)
|
|
||||||
}
|
|
||||||
clockSeqValue := binary.BigEndian.Uint16(clockSeq) & 0x0FFF // Mask to 12 bits
|
|
||||||
return uuidv8.NewWithParams(timestamp, clockSeqValue, g.opts.UUIDNode[:], uuidv8.TimestampBits48)
|
|
||||||
}
|
|
||||||
return "", errors.New("invalid option, Type unspecified")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns new id or error
|
// New returns new id or error
|
||||||
func New(opts ...Option) (string, error) {
|
func New(opts ...Option) (string, error) {
|
||||||
options := NewOptions(opts...)
|
options := NewOptions(opts...)
|
||||||
|
|
||||||
switch options.Type {
|
if len(options.Alphabet) == 0 || len(options.Alphabet) > 255 {
|
||||||
case TypeNanoid:
|
return "", errors.New("alphabet must not be empty and contain no more than 255 chars")
|
||||||
if len(options.NanoidAlphabet) == 0 || len(options.NanoidAlphabet) > 255 {
|
}
|
||||||
return "", errors.New("invalid option, NanoidAlphabet must not be empty and contain no more than 255 chars")
|
if options.Size <= 0 {
|
||||||
}
|
return "", errors.New("size must be positive integer")
|
||||||
if options.NanoidSize <= 0 {
|
|
||||||
return "", errors.New("invalid option, NanoidSize must be positive integer")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nanoid.Generate(options.NanoidAlphabet, options.NanoidSize)
|
|
||||||
case TypeUUIDv8:
|
|
||||||
timestamp := uint64(time.Now().UnixNano())
|
|
||||||
clockSeq := make([]byte, 2)
|
|
||||||
if _, err := rand.Read(clockSeq); err != nil {
|
|
||||||
return "", fmt.Errorf("failed to generate random clock sequence: %w", err)
|
|
||||||
}
|
|
||||||
clockSeqValue := binary.BigEndian.Uint16(clockSeq) & 0x0FFF // Mask to 12 bits
|
|
||||||
return uuidv8.NewWithParams(timestamp, clockSeqValue, options.UUIDNode[:], uuidv8.TimestampBits48)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", errors.New("invalid option, Type unspecified")
|
chars := options.Alphabet
|
||||||
}
|
|
||||||
|
|
||||||
func ToUUID(s string) uuid.UUID {
|
mask := getMask(len(chars))
|
||||||
return uuid.MustParse(s)
|
// estimate how many random bytes we will need for the ID, we might actually need more but this is tradeoff
|
||||||
|
// between average case and worst case
|
||||||
|
ceilArg := 1.6 * float64(mask*options.Size) / float64(len(options.Alphabet))
|
||||||
|
step := int(math.Ceil(ceilArg))
|
||||||
|
|
||||||
|
id := make([]rune, options.Size)
|
||||||
|
bytes := make([]byte, step)
|
||||||
|
for j := 0; ; {
|
||||||
|
_, err := rand.Read(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
for i := 0; i < step; i++ {
|
||||||
|
currByte := bytes[i] & byte(mask)
|
||||||
|
if currByte < byte(len(chars)) {
|
||||||
|
id[j] = chars[currByte]
|
||||||
|
j++
|
||||||
|
if j == options.Size {
|
||||||
|
return string(id[:options.Size]), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must is the same as New but fatals on error
|
// Must is the same as New but fatals on error
|
||||||
func MustNew(opts ...Option) string {
|
func Must(opts ...Option) string {
|
||||||
id, err := New(opts...)
|
id, err := New(opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
logger.DefaultLogger.Fatal(context.TODO(), "Must call is failed", err)
|
||||||
}
|
}
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options contains id deneration options
|
// Options contains id deneration options
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Type Type
|
Alphabet []rune
|
||||||
NanoidAlphabet string
|
Size int
|
||||||
NanoidSize int
|
|
||||||
UUIDNode [6]byte
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Option func signature
|
// Option func signature
|
||||||
type Option func(*Options)
|
type Option func(*Options)
|
||||||
|
|
||||||
// WithNanoidAlphabet specifies alphabet to use
|
// Alphabet specifies alphabet to use
|
||||||
func WithNanoidAlphabet(alphabet string) Option {
|
func Alphabet(alphabet string) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.NanoidAlphabet = alphabet
|
o.Alphabet = []rune(alphabet)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithNanoidSize specifies generated id size
|
// Size specifies id size
|
||||||
func WithNanoidSize(size int) Option {
|
func Size(size int) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.NanoidSize = size
|
o.Size = size
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithUUIDNode specifies node component for UUIDv8
|
|
||||||
func WithUUIDNode(node [6]byte) Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.UUIDNode = node
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOptions returns new Options struct filled by opts
|
// NewOptions returns new Options struct filled by opts
|
||||||
func NewOptions(opts ...Option) Options {
|
func NewOptions(opts ...Option) Options {
|
||||||
options := Options{
|
options := Options{
|
||||||
Type: TypeUUIDv8,
|
Alphabet: DefaultAlphabet,
|
||||||
NanoidAlphabet: DefaultNanoidAlphabet,
|
Size: DefaultSize,
|
||||||
NanoidSize: DefaultNanoidSize,
|
|
||||||
UUIDNode: generatedNode,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&options)
|
o(&options)
|
||||||
}
|
}
|
||||||
|
|
||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
package id
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestUUIDv8(t *testing.T) {
|
|
||||||
id, err := New()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
_ = id
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestToUUID(t *testing.T) {
|
|
||||||
id, err := New()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
u := ToUUID(id)
|
|
||||||
_ = u
|
|
||||||
}
|
|
@ -71,6 +71,14 @@ func CopyService(service *register.Service) *register.Service {
|
|||||||
}
|
}
|
||||||
s.Nodes = nodes
|
s.Nodes = nodes
|
||||||
|
|
||||||
|
// copy endpoints
|
||||||
|
eps := make([]*register.Endpoint, len(service.Endpoints))
|
||||||
|
for j, ep := range service.Endpoints {
|
||||||
|
e := ®ister.Endpoint{}
|
||||||
|
*e = *ep
|
||||||
|
eps[j] = e
|
||||||
|
}
|
||||||
|
s.Endpoints = eps
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ func (b *Buffer) Stream() (<-chan *Entry, chan bool) {
|
|||||||
defer b.Unlock()
|
defer b.Unlock()
|
||||||
|
|
||||||
entries := make(chan *Entry, 128)
|
entries := make(chan *Entry, 128)
|
||||||
id := id.MustNew()
|
id := id.Must()
|
||||||
stop := make(chan bool)
|
stop := make(chan bool)
|
||||||
|
|
||||||
b.streams[id] = &Stream{
|
b.streams[id] = &Stream{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user