Compare commits
No commits in common. "v3" and "v3.10.7" have entirely different histories.
@ -1,18 +0,0 @@
|
|||||||
---
|
|
||||||
name: Bug report
|
|
||||||
about: For reporting bugs in micro
|
|
||||||
title: "[BUG]"
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Describe the bug**
|
|
||||||
|
|
||||||
1. What are you trying to do?
|
|
||||||
2. What did you expect to happen?
|
|
||||||
3. What happens instead?
|
|
||||||
|
|
||||||
**How to reproduce the bug:**
|
|
||||||
|
|
||||||
If possible, please include a minimal code snippet here.
|
|
@ -1,17 +0,0 @@
|
|||||||
---
|
|
||||||
name: Feature request / Enhancement
|
|
||||||
about: If you have a need not served by micro
|
|
||||||
title: "[FEATURE]"
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
|
||||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
|
||||||
A clear and concise description of what you want to happen.
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context or screenshots about the feature request here.
|
|
@ -1,8 +0,0 @@
|
|||||||
---
|
|
||||||
name: Question
|
|
||||||
about: Ask a question about micro
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
@ -1,28 +0,0 @@
|
|||||||
name: "autoapprove"
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request_target:
|
|
||||||
types: [assigned, opened, synchronize, reopened]
|
|
||||||
workflow_run:
|
|
||||||
workflows: ["prbuild"]
|
|
||||||
types:
|
|
||||||
- completed
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
pull-requests: write
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
autoapprove:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: approve
|
|
||||||
run: [ "curl -o tea https://dl.gitea.com/tea/main/tea-main-linux-amd64",
|
|
||||||
"chmod +x ./tea",
|
|
||||||
"./tea login add --name unistack --token ${{ secrets.GITHUB_TOKEN }} --url https://git.unistack.org",
|
|
||||||
"./tea pr --repo ${{ github.event.repository.name }}"
|
|
||||||
]
|
|
||||||
if: github.actor == 'vtolstov'
|
|
||||||
id: approve
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
@ -1,24 +0,0 @@
|
|||||||
name: lint
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
- v3
|
|
||||||
jobs:
|
|
||||||
lint:
|
|
||||||
name: lint
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: setup-go
|
|
||||||
uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: 1.21
|
|
||||||
- name: checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: deps
|
|
||||||
run: go get -v -d ./...
|
|
||||||
- name: lint
|
|
||||||
uses: https://github.com/golangci/golangci-lint-action@v3.4.0
|
|
||||||
continue-on-error: true
|
|
||||||
with:
|
|
||||||
version: v1.52
|
|
@ -1,23 +0,0 @@
|
|||||||
name: pr
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
- v3
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
name: test
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: checkout
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: setup-go
|
|
||||||
uses: actions/setup-go@v3
|
|
||||||
with:
|
|
||||||
go-version: 1.21
|
|
||||||
- name: deps
|
|
||||||
run: go get -v -t -d ./...
|
|
||||||
- name: test
|
|
||||||
env:
|
|
||||||
INTEGRATION_TESTS: yes
|
|
||||||
run: go test -mod readonly -v ./...
|
|
@ -1,9 +0,0 @@
|
|||||||
## Pull Request template
|
|
||||||
Please, go through these steps before clicking submit on this PR.
|
|
||||||
|
|
||||||
1. Give a descriptive title to your PR.
|
|
||||||
2. Provide a description of your changes.
|
|
||||||
3. Make sure you have some relevant tests.
|
|
||||||
4. Put `closes #XXXX` in your comment to auto-close the issue that your PR fixes (if applicable).
|
|
||||||
|
|
||||||
**PLEASE REMOVE THIS TEMPLATE BEFORE SUBMITTING**
|
|
@ -37,4 +37,11 @@ jobs:
|
|||||||
uses: golangci/golangci-lint-action@v3.4.0
|
uses: golangci/golangci-lint-action@v3.4.0
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
version: v1.30
|
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
|
||||||
|
version: v1.30
|
||||||
|
# Optional: working directory, useful for monorepos
|
||||||
|
# working-directory: somedir
|
||||||
|
# Optional: golangci-lint command line arguments.
|
||||||
|
# args: --issues-exit-code=0
|
||||||
|
# Optional: show only new issues if it's a pull request. The default value is `false`.
|
||||||
|
# only-new-issues: true
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,8 +1,6 @@
|
|||||||
# Develop tools
|
# Develop tools
|
||||||
/.vscode/
|
/.vscode/
|
||||||
/.idea/
|
/.idea/
|
||||||
.idea
|
|
||||||
.vscode
|
|
||||||
|
|
||||||
# Binaries for programs and plugins
|
# Binaries for programs and plugins
|
||||||
*.exe
|
*.exe
|
||||||
@ -15,7 +13,6 @@
|
|||||||
_obj
|
_obj
|
||||||
_test
|
_test
|
||||||
_build
|
_build
|
||||||
.DS_Store
|
|
||||||
|
|
||||||
# Architecture specific extensions/prefixes
|
# Architecture specific extensions/prefixes
|
||||||
*.[568vq]
|
*.[568vq]
|
||||||
|
182
api/api.go
Normal file
182
api/api.go
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
package api // import "go.unistack.org/micro/v3/api"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"go.unistack.org/micro/v3/metadata"
|
||||||
|
"go.unistack.org/micro/v3/register"
|
||||||
|
"go.unistack.org/micro/v3/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
// nolint: revive
|
||||||
|
// Api interface
|
||||||
|
type Api interface {
|
||||||
|
// Initialise options
|
||||||
|
Init(...Option) error
|
||||||
|
// Get the options
|
||||||
|
Options() Options
|
||||||
|
// Register a http handler
|
||||||
|
Register(*Endpoint) error
|
||||||
|
// Register a route
|
||||||
|
Deregister(*Endpoint) error
|
||||||
|
// Implementation of api
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Options holds the options
|
||||||
|
type Options struct{}
|
||||||
|
|
||||||
|
// Option func signature
|
||||||
|
type Option func(*Options) error
|
||||||
|
|
||||||
|
// Endpoint is a mapping between an RPC method and HTTP endpoint
|
||||||
|
type Endpoint struct {
|
||||||
|
// Name Greeter.Hello
|
||||||
|
Name string
|
||||||
|
// Desciption for endpoint
|
||||||
|
Description string
|
||||||
|
// Handler e.g rpc, proxy
|
||||||
|
Handler string
|
||||||
|
// Body destination
|
||||||
|
// "*" or "" - top level message value
|
||||||
|
// "string" - inner message value
|
||||||
|
Body string
|
||||||
|
// Host e.g example.com
|
||||||
|
Host []string
|
||||||
|
// Method e.g GET, POST
|
||||||
|
Method []string
|
||||||
|
// Path e.g /greeter. Expect POSIX regex
|
||||||
|
Path []string
|
||||||
|
// Stream flag
|
||||||
|
Stream bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Service represents an API service
|
||||||
|
type Service struct {
|
||||||
|
// Name of service
|
||||||
|
Name string
|
||||||
|
// Endpoint for this service
|
||||||
|
Endpoint *Endpoint
|
||||||
|
// Services that provides service
|
||||||
|
Services []*register.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode encodes an endpoint to endpoint metadata
|
||||||
|
func Encode(e *Endpoint) map[string]string {
|
||||||
|
if e == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// endpoint map
|
||||||
|
ep := make(map[string]string)
|
||||||
|
|
||||||
|
// set vals only if they exist
|
||||||
|
set := func(k, v string) {
|
||||||
|
if len(v) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ep[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
set("endpoint", e.Name)
|
||||||
|
set("description", e.Description)
|
||||||
|
set("handler", e.Handler)
|
||||||
|
set("method", strings.Join(e.Method, ","))
|
||||||
|
set("path", strings.Join(e.Path, ","))
|
||||||
|
set("host", strings.Join(e.Host, ","))
|
||||||
|
set("body", e.Body)
|
||||||
|
|
||||||
|
return ep
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode decodes endpoint metadata into an endpoint
|
||||||
|
func Decode(e metadata.Metadata) *Endpoint {
|
||||||
|
if e == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ep := &Endpoint{}
|
||||||
|
ep.Name, _ = e.Get("endpoint")
|
||||||
|
ep.Description, _ = e.Get("description")
|
||||||
|
epmethod, _ := e.Get("method")
|
||||||
|
ep.Method = []string{epmethod}
|
||||||
|
eppath, _ := e.Get("path")
|
||||||
|
ep.Path = []string{eppath}
|
||||||
|
ephost, _ := e.Get("host")
|
||||||
|
ep.Host = []string{ephost}
|
||||||
|
ep.Handler, _ = e.Get("handler")
|
||||||
|
ep.Body, _ = e.Get("body")
|
||||||
|
|
||||||
|
return ep
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate validates an endpoint to guarantee it won't blow up when being served
|
||||||
|
func Validate(e *Endpoint) error {
|
||||||
|
if e == nil {
|
||||||
|
return errors.New("endpoint is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(e.Name) == 0 {
|
||||||
|
return errors.New("name required")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range e.Path {
|
||||||
|
ps := p[0]
|
||||||
|
pe := p[len(p)-1]
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case ps == '^' && pe == '$':
|
||||||
|
if _, err := regexp.CompilePOSIX(p); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case ps == '^' && pe != '$':
|
||||||
|
return errors.New("invalid path")
|
||||||
|
case ps != '^' && pe == '$':
|
||||||
|
return errors.New("invalid path")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(e.Handler) == 0 {
|
||||||
|
return errors.New("invalid handler")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Design ideas
|
||||||
|
|
||||||
|
// Gateway is an api gateway interface
|
||||||
|
type Gateway interface {
|
||||||
|
// Register a http handler
|
||||||
|
Handle(pattern string, http.Handler)
|
||||||
|
// Register a route
|
||||||
|
RegisterRoute(r Route)
|
||||||
|
// Init initialises the command line.
|
||||||
|
// It also parses further options.
|
||||||
|
Init(...Option) error
|
||||||
|
// Run the gateway
|
||||||
|
Run() error
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGateway returns a new api gateway
|
||||||
|
func NewGateway() Gateway {
|
||||||
|
return newGateway()
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// WithEndpoint returns a server.HandlerOption with endpoint metadata set
|
||||||
|
//
|
||||||
|
// Usage:
|
||||||
|
//
|
||||||
|
// proto.RegisterHandler(service.Server(), new(Handler), api.WithEndpoint(
|
||||||
|
// &api.Endpoint{
|
||||||
|
// Name: "Greeter.Hello",
|
||||||
|
// Path: []string{"/greeter"},
|
||||||
|
// },
|
||||||
|
// ))
|
||||||
|
func WithEndpoint(e *Endpoint) server.HandlerOption {
|
||||||
|
return server.EndpointMetadata(e.Name, Encode(e))
|
||||||
|
}
|
245
api/api_test.go
Normal file
245
api/api_test.go
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"go.unistack.org/micro/v3/metadata"
|
||||||
|
"go.unistack.org/micro/v3/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDecode(t *testing.T) {
|
||||||
|
md := metadata.New(0)
|
||||||
|
md.Set("host", "localhost", "method", "GET", "path", "/")
|
||||||
|
ep := Decode(md)
|
||||||
|
if md == nil {
|
||||||
|
t.Fatalf("failed to decode md %#+v", md)
|
||||||
|
} else if len(ep.Host) != 1 || len(ep.Method) != 1 || len(ep.Path) != 1 {
|
||||||
|
t.Fatalf("ep invalid after decode %#+v", ep)
|
||||||
|
}
|
||||||
|
if ep.Host[0] != "localhost" || ep.Method[0] != "GET" || ep.Path[0] != "/" {
|
||||||
|
t.Fatalf("ep invalid after decode %#+v", ep)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:gocyclo
|
||||||
|
func TestEncode(t *testing.T) {
|
||||||
|
testData := []*Endpoint{
|
||||||
|
nil,
|
||||||
|
{
|
||||||
|
Name: "Foo.Bar",
|
||||||
|
Description: "A test endpoint",
|
||||||
|
Handler: "meta",
|
||||||
|
Host: []string{"foo.com"},
|
||||||
|
Method: []string{"GET"},
|
||||||
|
Path: []string{"/test"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
compare := func(expect, got []string) bool {
|
||||||
|
// no data to compare, return true
|
||||||
|
if len(expect) == 0 && len(got) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// no data expected but got some return false
|
||||||
|
if len(expect) == 0 && len(got) > 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// compare expected with what we got
|
||||||
|
for _, e := range expect {
|
||||||
|
var seen bool
|
||||||
|
for _, g := range got {
|
||||||
|
if e == g {
|
||||||
|
seen = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !seen {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// we're done, return true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, d := range testData {
|
||||||
|
// encode
|
||||||
|
e := Encode(d)
|
||||||
|
// decode
|
||||||
|
de := Decode(e)
|
||||||
|
|
||||||
|
// nil endpoint returns nil
|
||||||
|
if d == nil {
|
||||||
|
if e != nil {
|
||||||
|
t.Fatalf("expected nil got %v", e)
|
||||||
|
}
|
||||||
|
if de != nil {
|
||||||
|
t.Fatalf("expected nil got %v", de)
|
||||||
|
}
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// check encoded map
|
||||||
|
name := e["endpoint"]
|
||||||
|
desc := e["description"]
|
||||||
|
method := strings.Split(e["method"], ",")
|
||||||
|
path := strings.Split(e["path"], ",")
|
||||||
|
host := strings.Split(e["host"], ",")
|
||||||
|
handler := e["handler"]
|
||||||
|
|
||||||
|
if name != d.Name {
|
||||||
|
t.Fatalf("expected %v got %v", d.Name, name)
|
||||||
|
}
|
||||||
|
if desc != d.Description {
|
||||||
|
t.Fatalf("expected %v got %v", d.Description, desc)
|
||||||
|
}
|
||||||
|
if handler != d.Handler {
|
||||||
|
t.Fatalf("expected %v got %v", d.Handler, handler)
|
||||||
|
}
|
||||||
|
if ok := compare(d.Method, method); !ok {
|
||||||
|
t.Fatalf("expected %v got %v", d.Method, method)
|
||||||
|
}
|
||||||
|
if ok := compare(d.Path, path); !ok {
|
||||||
|
t.Fatalf("expected %v got %v", d.Path, path)
|
||||||
|
}
|
||||||
|
if ok := compare(d.Host, host); !ok {
|
||||||
|
t.Fatalf("expected %v got %v", d.Host, host)
|
||||||
|
}
|
||||||
|
|
||||||
|
if de.Name != d.Name {
|
||||||
|
t.Fatalf("expected %v got %v", d.Name, de.Name)
|
||||||
|
}
|
||||||
|
if de.Description != d.Description {
|
||||||
|
t.Fatalf("expected %v got %v", d.Description, de.Description)
|
||||||
|
}
|
||||||
|
if de.Handler != d.Handler {
|
||||||
|
t.Fatalf("expected %v got %v", d.Handler, de.Handler)
|
||||||
|
}
|
||||||
|
if ok := compare(d.Method, de.Method); !ok {
|
||||||
|
t.Fatalf("expected %v got %v", d.Method, de.Method)
|
||||||
|
}
|
||||||
|
if ok := compare(d.Path, de.Path); !ok {
|
||||||
|
t.Fatalf("expected %v got %v", d.Path, de.Path)
|
||||||
|
}
|
||||||
|
if ok := compare(d.Host, de.Host); !ok {
|
||||||
|
t.Fatalf("expected %v got %v", d.Host, de.Host)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidate(t *testing.T) {
|
||||||
|
epPcre := &Endpoint{
|
||||||
|
Name: "Foo.Bar",
|
||||||
|
Description: "A test endpoint",
|
||||||
|
Handler: "meta",
|
||||||
|
Host: []string{"foo.com"},
|
||||||
|
Method: []string{"GET"},
|
||||||
|
Path: []string{"^/test/?$"},
|
||||||
|
}
|
||||||
|
if err := Validate(epPcre); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
epGpath := &Endpoint{
|
||||||
|
Name: "Foo.Bar",
|
||||||
|
Description: "A test endpoint",
|
||||||
|
Handler: "meta",
|
||||||
|
Host: []string{"foo.com"},
|
||||||
|
Method: []string{"GET"},
|
||||||
|
Path: []string{"/test/{id}"},
|
||||||
|
}
|
||||||
|
if err := Validate(epGpath); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
epPcreInvalid := &Endpoint{
|
||||||
|
Name: "Foo.Bar",
|
||||||
|
Description: "A test endpoint",
|
||||||
|
Handler: "meta",
|
||||||
|
Host: []string{"foo.com"},
|
||||||
|
Method: []string{"GET"},
|
||||||
|
Path: []string{"/test/?$"},
|
||||||
|
}
|
||||||
|
if err := Validate(epPcreInvalid); err == nil {
|
||||||
|
t.Fatalf("invalid pcre %v", epPcreInvalid.Path[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWithEndpoint(t *testing.T) {
|
||||||
|
ep := &Endpoint{
|
||||||
|
Name: "Foo.Bar",
|
||||||
|
Description: "A test endpoint",
|
||||||
|
Handler: "meta",
|
||||||
|
Host: []string{"foo.com"},
|
||||||
|
Method: []string{"GET"},
|
||||||
|
Path: []string{"/test/{id}"},
|
||||||
|
}
|
||||||
|
o := WithEndpoint(ep)
|
||||||
|
opts := server.NewHandlerOptions(o)
|
||||||
|
if opts.Metadata == nil {
|
||||||
|
t.Fatalf("WithEndpoint not works %#+v", opts)
|
||||||
|
}
|
||||||
|
md, ok := opts.Metadata[ep.Name]
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("WithEndpoint not works %#+v", opts)
|
||||||
|
}
|
||||||
|
if v, ok := md.Get("Endpoint"); !ok || v != "Foo.Bar" {
|
||||||
|
t.Fatalf("WithEndpoint not works %#+v", md)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateNilErr(t *testing.T) {
|
||||||
|
var ep *Endpoint
|
||||||
|
if err := Validate(ep); err == nil {
|
||||||
|
t.Fatalf("Validate not works")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateMissingNameErr(t *testing.T) {
|
||||||
|
ep := &Endpoint{}
|
||||||
|
if err := Validate(ep); err == nil {
|
||||||
|
t.Fatalf("Validate not works")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateMissingHandlerErr(t *testing.T) {
|
||||||
|
ep := &Endpoint{Name: "test"}
|
||||||
|
if err := Validate(ep); err == nil {
|
||||||
|
t.Fatalf("Validate not works")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateRegexpStartErr(t *testing.T) {
|
||||||
|
ep := &Endpoint{Name: "test", Handler: "test"}
|
||||||
|
ep.Path = []string{"^/"}
|
||||||
|
if err := Validate(ep); err == nil {
|
||||||
|
t.Fatalf("Validate not works")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateRegexpEndErr(t *testing.T) {
|
||||||
|
ep := &Endpoint{Name: "test", Handler: "test", Path: []string{""}}
|
||||||
|
ep.Path[0] = "/$"
|
||||||
|
if err := Validate(ep); err == nil {
|
||||||
|
t.Fatalf("Validate not works")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateRegexpNonErr(t *testing.T) {
|
||||||
|
ep := &Endpoint{Name: "test", Handler: "test", Path: []string{""}}
|
||||||
|
ep.Path[0] = "^/(.*)$"
|
||||||
|
if err := Validate(ep); err != nil {
|
||||||
|
t.Fatalf("Validate not works")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateRegexpErr(t *testing.T) {
|
||||||
|
ep := &Endpoint{Name: "test", Handler: "test", Path: []string{""}}
|
||||||
|
ep.Path[0] = "^/(.$"
|
||||||
|
if err := Validate(ep); err == nil {
|
||||||
|
t.Fatalf("Validate not works")
|
||||||
|
}
|
||||||
|
}
|
@ -1,25 +1,21 @@
|
|||||||
// Package broker is an interface used for asynchronous messaging
|
// Package broker is an interface used for asynchronous messaging
|
||||||
package broker
|
package broker // import "go.unistack.org/micro/v3/broker"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.unistack.org/micro/v3/codec"
|
|
||||||
"go.unistack.org/micro/v3/metadata"
|
"go.unistack.org/micro/v3/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DefaultBroker default memory broker
|
// DefaultBroker default memory broker
|
||||||
var DefaultBroker Broker = NewBroker()
|
var DefaultBroker = NewBroker()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// ErrNotConnected returns when broker used but not connected yet
|
// ErrNotConnected returns when broker used but not connected yet
|
||||||
ErrNotConnected = errors.New("broker not connected")
|
ErrNotConnected = errors.New("broker not connected")
|
||||||
// ErrDisconnected returns when broker disconnected
|
// ErrDisconnected returns when broker disconnected
|
||||||
ErrDisconnected = errors.New("broker disconnected")
|
ErrDisconnected = errors.New("broker disconnected")
|
||||||
// DefaultGracefulTimeout
|
|
||||||
DefaultGracefulTimeout = 5 * time.Second
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Broker is an interface used for asynchronous messaging.
|
// Broker is an interface used for asynchronous messaging.
|
||||||
@ -48,17 +44,6 @@ type Broker interface {
|
|||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
|
||||||
type (
|
|
||||||
FuncPublish func(ctx context.Context, topic string, msg *Message, opts ...PublishOption) error
|
|
||||||
HookPublish func(next FuncPublish) FuncPublish
|
|
||||||
FuncBatchPublish func(ctx context.Context, msgs []*Message, opts ...PublishOption) error
|
|
||||||
HookBatchPublish func(next FuncBatchPublish) FuncBatchPublish
|
|
||||||
FuncSubscribe func(ctx context.Context, topic string, h Handler, opts ...SubscribeOption) (Subscriber, error)
|
|
||||||
HookSubscribe func(next FuncSubscribe) FuncSubscribe
|
|
||||||
FuncBatchSubscribe func(ctx context.Context, topic string, h BatchHandler, opts ...SubscribeOption) (Subscriber, error)
|
|
||||||
HookBatchSubscribe func(next FuncBatchSubscribe) FuncBatchSubscribe
|
|
||||||
)
|
|
||||||
|
|
||||||
// Handler is used to process messages via a subscription of a topic.
|
// Handler is used to process messages via a subscription of a topic.
|
||||||
type Handler func(Event) error
|
type Handler func(Event) error
|
||||||
|
|
||||||
@ -88,8 +73,6 @@ type BatchHandler func(Events) error
|
|||||||
|
|
||||||
// Event is given to a subscription handler for processing
|
// Event is given to a subscription handler for processing
|
||||||
type Event interface {
|
type Event interface {
|
||||||
// Context return context.Context for event
|
|
||||||
Context() context.Context
|
|
||||||
// Topic returns event topic
|
// Topic returns event topic
|
||||||
Topic() string
|
Topic() string
|
||||||
// Message returns broker message
|
// Message returns broker message
|
||||||
@ -102,12 +85,33 @@ type Event interface {
|
|||||||
SetError(err error)
|
SetError(err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RawMessage is a raw encoded JSON value.
|
||||||
|
// It implements Marshaler and Unmarshaler and can be used to delay decoding or precompute a encoding.
|
||||||
|
type RawMessage []byte
|
||||||
|
|
||||||
|
// MarshalJSON returns m as the JSON encoding of m.
|
||||||
|
func (m *RawMessage) MarshalJSON() ([]byte, error) {
|
||||||
|
if m == nil {
|
||||||
|
return []byte("null"), nil
|
||||||
|
}
|
||||||
|
return *m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON sets *m to a copy of data.
|
||||||
|
func (m *RawMessage) UnmarshalJSON(data []byte) error {
|
||||||
|
if m == nil {
|
||||||
|
return errors.New("RawMessage UnmarshalJSON on nil pointer")
|
||||||
|
}
|
||||||
|
*m = append((*m)[0:0], data...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Message is used to transfer data
|
// Message is used to transfer data
|
||||||
type Message struct {
|
type Message struct {
|
||||||
// Header contains message metadata
|
// Header contains message metadata
|
||||||
Header metadata.Metadata
|
Header metadata.Metadata
|
||||||
// Body contains message body
|
// Body contains message body
|
||||||
Body codec.RawMessage
|
Body RawMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMessage create broker message with topic filled
|
// NewMessage create broker message with topic filled
|
||||||
|
@ -4,10 +4,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"go.unistack.org/micro/v3/broker"
|
|
||||||
"go.unistack.org/micro/v3/logger"
|
"go.unistack.org/micro/v3/logger"
|
||||||
"go.unistack.org/micro/v3/metadata"
|
"go.unistack.org/micro/v3/metadata"
|
||||||
"go.unistack.org/micro/v3/options"
|
|
||||||
maddr "go.unistack.org/micro/v3/util/addr"
|
maddr "go.unistack.org/micro/v3/util/addr"
|
||||||
"go.unistack.org/micro/v3/util/id"
|
"go.unistack.org/micro/v3/util/id"
|
||||||
mnet "go.unistack.org/micro/v3/util/net"
|
mnet "go.unistack.org/micro/v3/util/net"
|
||||||
@ -15,13 +13,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type memoryBroker struct {
|
type memoryBroker struct {
|
||||||
funcPublish broker.FuncPublish
|
subscribers map[string][]*memorySubscriber
|
||||||
funcBatchPublish broker.FuncBatchPublish
|
addr string
|
||||||
funcSubscribe broker.FuncSubscribe
|
opts Options
|
||||||
funcBatchSubscribe broker.FuncBatchSubscribe
|
|
||||||
subscribers map[string][]*memorySubscriber
|
|
||||||
addr string
|
|
||||||
opts broker.Options
|
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
connected bool
|
connected bool
|
||||||
}
|
}
|
||||||
@ -30,20 +24,20 @@ type memoryEvent struct {
|
|||||||
err error
|
err error
|
||||||
message interface{}
|
message interface{}
|
||||||
topic string
|
topic string
|
||||||
opts broker.Options
|
opts Options
|
||||||
}
|
}
|
||||||
|
|
||||||
type memorySubscriber struct {
|
type memorySubscriber struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
exit chan bool
|
exit chan bool
|
||||||
handler broker.Handler
|
handler Handler
|
||||||
batchhandler broker.BatchHandler
|
batchhandler BatchHandler
|
||||||
id string
|
id string
|
||||||
topic string
|
topic string
|
||||||
opts broker.SubscribeOptions
|
opts SubscribeOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memoryBroker) Options() broker.Options {
|
func (m *memoryBroker) Options() Options {
|
||||||
return m.opts
|
return m.opts
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,12 +46,6 @@ func (m *memoryBroker) Address() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *memoryBroker) Connect(ctx context.Context) error {
|
func (m *memoryBroker) Connect(ctx context.Context) error {
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
|
|
||||||
@ -82,12 +70,6 @@ func (m *memoryBroker) Connect(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *memoryBroker) Disconnect(ctx context.Context) error {
|
func (m *memoryBroker) Disconnect(ctx context.Context) error {
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
|
|
||||||
@ -99,54 +81,27 @@ func (m *memoryBroker) Disconnect(ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memoryBroker) Init(opts ...broker.Option) error {
|
func (m *memoryBroker) Init(opts ...Option) error {
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&m.opts)
|
o(&m.opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
m.funcPublish = m.fnPublish
|
|
||||||
m.funcBatchPublish = m.fnBatchPublish
|
|
||||||
m.funcSubscribe = m.fnSubscribe
|
|
||||||
m.funcBatchSubscribe = m.fnBatchSubscribe
|
|
||||||
|
|
||||||
m.opts.Hooks.EachNext(func(hook options.Hook) {
|
|
||||||
switch h := hook.(type) {
|
|
||||||
case broker.HookPublish:
|
|
||||||
m.funcPublish = h(m.funcPublish)
|
|
||||||
case broker.HookBatchPublish:
|
|
||||||
m.funcBatchPublish = h(m.funcBatchPublish)
|
|
||||||
case broker.HookSubscribe:
|
|
||||||
m.funcSubscribe = h(m.funcSubscribe)
|
|
||||||
case broker.HookBatchSubscribe:
|
|
||||||
m.funcBatchSubscribe = h(m.funcBatchSubscribe)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memoryBroker) Publish(ctx context.Context, topic string, msg *broker.Message, opts ...broker.PublishOption) error {
|
func (m *memoryBroker) Publish(ctx context.Context, topic string, msg *Message, opts ...PublishOption) error {
|
||||||
return m.funcPublish(ctx, topic, msg, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *memoryBroker) fnPublish(ctx context.Context, topic string, msg *broker.Message, opts ...broker.PublishOption) error {
|
|
||||||
msg.Header.Set(metadata.HeaderTopic, topic)
|
msg.Header.Set(metadata.HeaderTopic, topic)
|
||||||
return m.publish(ctx, []*broker.Message{msg}, opts...)
|
return m.publish(ctx, []*Message{msg}, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memoryBroker) BatchPublish(ctx context.Context, msgs []*broker.Message, opts ...broker.PublishOption) error {
|
func (m *memoryBroker) BatchPublish(ctx context.Context, msgs []*Message, opts ...PublishOption) error {
|
||||||
return m.funcBatchPublish(ctx, msgs, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *memoryBroker) fnBatchPublish(ctx context.Context, msgs []*broker.Message, opts ...broker.PublishOption) error {
|
|
||||||
return m.publish(ctx, msgs, opts...)
|
return m.publish(ctx, msgs, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memoryBroker) publish(ctx context.Context, msgs []*broker.Message, opts ...broker.PublishOption) error {
|
func (m *memoryBroker) publish(ctx context.Context, msgs []*Message, opts ...PublishOption) error {
|
||||||
m.RLock()
|
m.RLock()
|
||||||
if !m.connected {
|
if !m.connected {
|
||||||
m.RUnlock()
|
m.RUnlock()
|
||||||
return broker.ErrNotConnected
|
return ErrNotConnected
|
||||||
}
|
}
|
||||||
m.RUnlock()
|
m.RUnlock()
|
||||||
|
|
||||||
@ -156,9 +111,9 @@ func (m *memoryBroker) publish(ctx context.Context, msgs []*broker.Message, opts
|
|||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
default:
|
default:
|
||||||
options := broker.NewPublishOptions(opts...)
|
options := NewPublishOptions(opts...)
|
||||||
|
|
||||||
msgTopicMap := make(map[string]broker.Events)
|
msgTopicMap := make(map[string]Events)
|
||||||
for _, v := range msgs {
|
for _, v := range msgs {
|
||||||
p := &memoryEvent{opts: m.opts}
|
p := &memoryEvent{opts: m.opts}
|
||||||
|
|
||||||
@ -206,7 +161,7 @@ func (m *memoryBroker) publish(ctx context.Context, msgs []*broker.Message, opts
|
|||||||
}
|
}
|
||||||
} else if sub.opts.AutoAck {
|
} else if sub.opts.AutoAck {
|
||||||
if err = ms.Ack(); err != nil {
|
if err = ms.Ack(); err != nil {
|
||||||
m.opts.Logger.Error(m.opts.Context, "broker ack error", err)
|
m.opts.Logger.Errorf(m.opts.Context, "ack failed: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// single processing
|
// single processing
|
||||||
@ -217,11 +172,11 @@ func (m *memoryBroker) publish(ctx context.Context, msgs []*broker.Message, opts
|
|||||||
if eh != nil {
|
if eh != nil {
|
||||||
_ = eh(p)
|
_ = eh(p)
|
||||||
} else if m.opts.Logger.V(logger.ErrorLevel) {
|
} else if m.opts.Logger.V(logger.ErrorLevel) {
|
||||||
m.opts.Logger.Error(m.opts.Context, "broker handler error", err)
|
m.opts.Logger.Error(m.opts.Context, err.Error())
|
||||||
}
|
}
|
||||||
} else if sub.opts.AutoAck {
|
} else if sub.opts.AutoAck {
|
||||||
if err = p.Ack(); err != nil {
|
if err = p.Ack(); err != nil {
|
||||||
m.opts.Logger.Error(m.opts.Context, "broker ack error", err)
|
m.opts.Logger.Errorf(m.opts.Context, "ack failed: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -233,15 +188,11 @@ func (m *memoryBroker) publish(ctx context.Context, msgs []*broker.Message, opts
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memoryBroker) BatchSubscribe(ctx context.Context, topic string, handler broker.BatchHandler, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
|
func (m *memoryBroker) BatchSubscribe(ctx context.Context, topic string, handler BatchHandler, opts ...SubscribeOption) (Subscriber, error) {
|
||||||
return m.funcBatchSubscribe(ctx, topic, handler, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *memoryBroker) fnBatchSubscribe(ctx context.Context, topic string, handler broker.BatchHandler, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
|
|
||||||
m.RLock()
|
m.RLock()
|
||||||
if !m.connected {
|
if !m.connected {
|
||||||
m.RUnlock()
|
m.RUnlock()
|
||||||
return nil, broker.ErrNotConnected
|
return nil, ErrNotConnected
|
||||||
}
|
}
|
||||||
m.RUnlock()
|
m.RUnlock()
|
||||||
|
|
||||||
@ -250,7 +201,7 @@ func (m *memoryBroker) fnBatchSubscribe(ctx context.Context, topic string, handl
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
options := broker.NewSubscribeOptions(opts...)
|
options := NewSubscribeOptions(opts...)
|
||||||
|
|
||||||
sub := &memorySubscriber{
|
sub := &memorySubscriber{
|
||||||
exit: make(chan bool, 1),
|
exit: make(chan bool, 1),
|
||||||
@ -282,15 +233,11 @@ func (m *memoryBroker) fnBatchSubscribe(ctx context.Context, topic string, handl
|
|||||||
return sub, nil
|
return sub, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memoryBroker) Subscribe(ctx context.Context, topic string, handler broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
|
func (m *memoryBroker) Subscribe(ctx context.Context, topic string, handler Handler, opts ...SubscribeOption) (Subscriber, error) {
|
||||||
return m.funcSubscribe(ctx, topic, handler, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *memoryBroker) fnSubscribe(ctx context.Context, topic string, handler broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
|
|
||||||
m.RLock()
|
m.RLock()
|
||||||
if !m.connected {
|
if !m.connected {
|
||||||
m.RUnlock()
|
m.RUnlock()
|
||||||
return nil, broker.ErrNotConnected
|
return nil, ErrNotConnected
|
||||||
}
|
}
|
||||||
m.RUnlock()
|
m.RUnlock()
|
||||||
|
|
||||||
@ -299,7 +246,7 @@ func (m *memoryBroker) fnSubscribe(ctx context.Context, topic string, handler br
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
options := broker.NewSubscribeOptions(opts...)
|
options := NewSubscribeOptions(opts...)
|
||||||
|
|
||||||
sub := &memorySubscriber{
|
sub := &memorySubscriber{
|
||||||
exit: make(chan bool, 1),
|
exit: make(chan bool, 1),
|
||||||
@ -343,12 +290,12 @@ func (m *memoryEvent) Topic() string {
|
|||||||
return m.topic
|
return m.topic
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memoryEvent) Message() *broker.Message {
|
func (m *memoryEvent) Message() *Message {
|
||||||
switch v := m.message.(type) {
|
switch v := m.message.(type) {
|
||||||
case *broker.Message:
|
case *Message:
|
||||||
return v
|
return v
|
||||||
case []byte:
|
case []byte:
|
||||||
msg := &broker.Message{}
|
msg := &Message{}
|
||||||
if err := m.opts.Codec.Unmarshal(v, msg); err != nil {
|
if err := m.opts.Codec.Unmarshal(v, msg); err != nil {
|
||||||
if m.opts.Logger.V(logger.ErrorLevel) {
|
if m.opts.Logger.V(logger.ErrorLevel) {
|
||||||
m.opts.Logger.Error(m.opts.Context, "[memory]: failed to unmarshal: %v", err)
|
m.opts.Logger.Error(m.opts.Context, "[memory]: failed to unmarshal: %v", err)
|
||||||
@ -373,11 +320,7 @@ func (m *memoryEvent) SetError(err error) {
|
|||||||
m.err = err
|
m.err = err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memoryEvent) Context() context.Context {
|
func (m *memorySubscriber) Options() SubscribeOptions {
|
||||||
return m.opts.Context
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *memorySubscriber) Options() broker.SubscribeOptions {
|
|
||||||
return m.opts
|
return m.opts
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,9 +334,9 @@ func (m *memorySubscriber) Unsubscribe(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewBroker return new memory broker
|
// NewBroker return new memory broker
|
||||||
func NewBroker(opts ...broker.Option) broker.Broker {
|
func NewBroker(opts ...Option) Broker {
|
||||||
return &memoryBroker{
|
return &memoryBroker{
|
||||||
opts: broker.NewOptions(opts...),
|
opts: NewOptions(opts...),
|
||||||
subscribers: make(map[string][]*memorySubscriber),
|
subscribers: make(map[string][]*memorySubscriber),
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -5,7 +5,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"go.unistack.org/micro/v3/broker"
|
|
||||||
"go.unistack.org/micro/v3/metadata"
|
"go.unistack.org/micro/v3/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -13,10 +12,6 @@ func TestMemoryBatchBroker(t *testing.T) {
|
|||||||
b := NewBroker()
|
b := NewBroker()
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
if err := b.Init(); err != nil {
|
|
||||||
t.Fatalf("Unexpected init error %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := b.Connect(ctx); err != nil {
|
if err := b.Connect(ctx); err != nil {
|
||||||
t.Fatalf("Unexpected connect error %v", err)
|
t.Fatalf("Unexpected connect error %v", err)
|
||||||
}
|
}
|
||||||
@ -24,7 +19,7 @@ func TestMemoryBatchBroker(t *testing.T) {
|
|||||||
topic := "test"
|
topic := "test"
|
||||||
count := 10
|
count := 10
|
||||||
|
|
||||||
fn := func(evts broker.Events) error {
|
fn := func(evts Events) error {
|
||||||
return evts.Ack()
|
return evts.Ack()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,9 +28,9 @@ func TestMemoryBatchBroker(t *testing.T) {
|
|||||||
t.Fatalf("Unexpected error subscribing %v", err)
|
t.Fatalf("Unexpected error subscribing %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
msgs := make([]*broker.Message, 0, count)
|
msgs := make([]*Message, 0, count)
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
message := &broker.Message{
|
message := &Message{
|
||||||
Header: map[string]string{
|
Header: map[string]string{
|
||||||
metadata.HeaderTopic: topic,
|
metadata.HeaderTopic: topic,
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
@ -63,10 +58,6 @@ func TestMemoryBroker(t *testing.T) {
|
|||||||
b := NewBroker()
|
b := NewBroker()
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
if err := b.Init(); err != nil {
|
|
||||||
t.Fatalf("Unexpected init error %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := b.Connect(ctx); err != nil {
|
if err := b.Connect(ctx); err != nil {
|
||||||
t.Fatalf("Unexpected connect error %v", err)
|
t.Fatalf("Unexpected connect error %v", err)
|
||||||
}
|
}
|
||||||
@ -74,7 +65,7 @@ func TestMemoryBroker(t *testing.T) {
|
|||||||
topic := "test"
|
topic := "test"
|
||||||
count := 10
|
count := 10
|
||||||
|
|
||||||
fn := func(p broker.Event) error {
|
fn := func(p Event) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,9 +74,9 @@ func TestMemoryBroker(t *testing.T) {
|
|||||||
t.Fatalf("Unexpected error subscribing %v", err)
|
t.Fatalf("Unexpected error subscribing %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
msgs := make([]*broker.Message, 0, count)
|
msgs := make([]*Message, 0, count)
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
message := &broker.Message{
|
message := &Message{
|
||||||
Header: map[string]string{
|
Header: map[string]string{
|
||||||
metadata.HeaderTopic: topic,
|
metadata.HeaderTopic: topic,
|
||||||
"foo": "bar",
|
"foo": "bar",
|
128
broker/noop.go
128
broker/noop.go
@ -1,128 +0,0 @@
|
|||||||
package broker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"go.unistack.org/micro/v3/options"
|
|
||||||
)
|
|
||||||
|
|
||||||
type NoopBroker struct {
|
|
||||||
funcPublish FuncPublish
|
|
||||||
funcBatchPublish FuncBatchPublish
|
|
||||||
funcSubscribe FuncSubscribe
|
|
||||||
funcBatchSubscribe FuncBatchSubscribe
|
|
||||||
opts Options
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewBroker(opts ...Option) *NoopBroker {
|
|
||||||
b := &NoopBroker{opts: NewOptions(opts...)}
|
|
||||||
b.funcPublish = b.fnPublish
|
|
||||||
b.funcBatchPublish = b.fnBatchPublish
|
|
||||||
b.funcSubscribe = b.fnSubscribe
|
|
||||||
b.funcBatchSubscribe = b.fnBatchSubscribe
|
|
||||||
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *NoopBroker) Name() string {
|
|
||||||
return b.opts.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *NoopBroker) String() string {
|
|
||||||
return "noop"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *NoopBroker) Options() Options {
|
|
||||||
return b.opts
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *NoopBroker) Init(opts ...Option) error {
|
|
||||||
for _, opt := range opts {
|
|
||||||
opt(&b.opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
b.funcPublish = b.fnPublish
|
|
||||||
b.funcBatchPublish = b.fnBatchPublish
|
|
||||||
b.funcSubscribe = b.fnSubscribe
|
|
||||||
b.funcBatchSubscribe = b.fnBatchSubscribe
|
|
||||||
|
|
||||||
b.opts.Hooks.EachNext(func(hook options.Hook) {
|
|
||||||
switch h := hook.(type) {
|
|
||||||
case HookPublish:
|
|
||||||
b.funcPublish = h(b.funcPublish)
|
|
||||||
case HookBatchPublish:
|
|
||||||
b.funcBatchPublish = h(b.funcBatchPublish)
|
|
||||||
case HookSubscribe:
|
|
||||||
b.funcSubscribe = h(b.funcSubscribe)
|
|
||||||
case HookBatchSubscribe:
|
|
||||||
b.funcBatchSubscribe = h(b.funcBatchSubscribe)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *NoopBroker) Connect(_ context.Context) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *NoopBroker) Disconnect(_ context.Context) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *NoopBroker) Address() string {
|
|
||||||
return strings.Join(b.opts.Addrs, ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *NoopBroker) fnBatchPublish(_ context.Context, _ []*Message, _ ...PublishOption) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *NoopBroker) BatchPublish(ctx context.Context, msgs []*Message, opts ...PublishOption) error {
|
|
||||||
return b.funcBatchPublish(ctx, msgs, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *NoopBroker) fnPublish(_ context.Context, _ string, _ *Message, _ ...PublishOption) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *NoopBroker) Publish(ctx context.Context, topic string, msg *Message, opts ...PublishOption) error {
|
|
||||||
return b.funcPublish(ctx, topic, msg, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
type NoopSubscriber struct {
|
|
||||||
ctx context.Context
|
|
||||||
topic string
|
|
||||||
handler Handler
|
|
||||||
batchHandler BatchHandler
|
|
||||||
opts SubscribeOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *NoopBroker) fnBatchSubscribe(ctx context.Context, topic string, handler BatchHandler, opts ...SubscribeOption) (Subscriber, error) {
|
|
||||||
return &NoopSubscriber{ctx: ctx, topic: topic, opts: NewSubscribeOptions(opts...), batchHandler: handler}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *NoopBroker) BatchSubscribe(ctx context.Context, topic string, handler BatchHandler, opts ...SubscribeOption) (Subscriber, error) {
|
|
||||||
return b.funcBatchSubscribe(ctx, topic, handler, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *NoopBroker) fnSubscribe(ctx context.Context, topic string, handler Handler, opts ...SubscribeOption) (Subscriber, error) {
|
|
||||||
return &NoopSubscriber{ctx: ctx, topic: topic, opts: NewSubscribeOptions(opts...), handler: handler}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *NoopBroker) Subscribe(ctx context.Context, topic string, handler Handler, opts ...SubscribeOption) (Subscriber, error) {
|
|
||||||
return b.funcSubscribe(ctx, topic, handler, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *NoopSubscriber) Options() SubscribeOptions {
|
|
||||||
return s.opts
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *NoopSubscriber) Topic() string {
|
|
||||||
return s.topic
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *NoopSubscriber) Unsubscribe(_ context.Context) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
package broker
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type testHook struct {
|
|
||||||
f bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *testHook) Publish1(fn FuncPublish) FuncPublish {
|
|
||||||
return func(ctx context.Context, topic string, msg *Message, opts ...PublishOption) error {
|
|
||||||
t.f = true
|
|
||||||
return fn(ctx, topic, msg, opts...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNoopHook(t *testing.T) {
|
|
||||||
h := &testHook{}
|
|
||||||
|
|
||||||
b := NewBroker(Hooks(HookPublish(h.Publish1)))
|
|
||||||
|
|
||||||
if err := b.Init(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := b.Publish(context.TODO(), "", nil); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !h.f {
|
|
||||||
t.Fatal("hook not works")
|
|
||||||
}
|
|
||||||
}
|
|
@ -8,9 +8,7 @@ import (
|
|||||||
"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/meter"
|
"go.unistack.org/micro/v3/meter"
|
||||||
"go.unistack.org/micro/v3/options"
|
|
||||||
"go.unistack.org/micro/v3/register"
|
"go.unistack.org/micro/v3/register"
|
||||||
"go.unistack.org/micro/v3/sync"
|
|
||||||
"go.unistack.org/micro/v3/tracer"
|
"go.unistack.org/micro/v3/tracer"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -38,25 +36,17 @@ type Options struct {
|
|||||||
Name string
|
Name string
|
||||||
// Addrs holds the broker address
|
// Addrs holds the broker address
|
||||||
Addrs []string
|
Addrs []string
|
||||||
// Wait waits for a collection of goroutines to finish
|
|
||||||
Wait *sync.WaitGroup
|
|
||||||
// GracefulTimeout contains time to wait to finish in flight requests
|
|
||||||
GracefulTimeout time.Duration
|
|
||||||
// Hooks can be run before broker Publish/BatchPublish and
|
|
||||||
// Subscribe/BatchSubscribe methods
|
|
||||||
Hooks options.Hooks
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOptions create new Options
|
// NewOptions create new Options
|
||||||
func NewOptions(opts ...Option) Options {
|
func NewOptions(opts ...Option) Options {
|
||||||
options := Options{
|
options := Options{
|
||||||
Register: register.DefaultRegister,
|
Register: register.DefaultRegister,
|
||||||
Logger: logger.DefaultLogger,
|
Logger: logger.DefaultLogger,
|
||||||
Context: context.Background(),
|
Context: context.Background(),
|
||||||
Meter: meter.DefaultMeter,
|
Meter: meter.DefaultMeter,
|
||||||
Codec: codec.DefaultCodec,
|
Codec: codec.DefaultCodec,
|
||||||
Tracer: tracer.DefaultTracer,
|
Tracer: tracer.DefaultTracer,
|
||||||
GracefulTimeout: DefaultGracefulTimeout,
|
|
||||||
}
|
}
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&options)
|
o(&options)
|
||||||
@ -234,13 +224,6 @@ func Name(n string) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hooks sets hook runs before action
|
|
||||||
func Hooks(h ...options.Hook) Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.Hooks = append(o.Hooks, h...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SubscribeContext set context
|
// SubscribeContext set context
|
||||||
func SubscribeContext(ctx context.Context) SubscribeOption {
|
func SubscribeContext(ctx context.Context) SubscribeOption {
|
||||||
return func(o *SubscribeOptions) {
|
return func(o *SubscribeOptions) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Package client is an interface for an RPC client
|
// Package client is an interface for an RPC client
|
||||||
package client
|
package client // import "go.unistack.org/micro/v3/client"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@ -44,17 +44,6 @@ type Client interface {
|
|||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
|
||||||
type (
|
|
||||||
FuncCall func(ctx context.Context, req Request, rsp interface{}, opts ...CallOption) error
|
|
||||||
HookCall func(next FuncCall) FuncCall
|
|
||||||
FuncStream func(ctx context.Context, req Request, opts ...CallOption) (Stream, error)
|
|
||||||
HookStream func(next FuncStream) FuncStream
|
|
||||||
FuncPublish func(ctx context.Context, msg Message, opts ...PublishOption) error
|
|
||||||
HookPublish func(next FuncPublish) FuncPublish
|
|
||||||
FuncBatchPublish func(ctx context.Context, msg []Message, opts ...PublishOption) error
|
|
||||||
HookBatchPublish func(next FuncBatchPublish) FuncBatchPublish
|
|
||||||
)
|
|
||||||
|
|
||||||
// Message is the interface for publishing asynchronously
|
// Message is the interface for publishing asynchronously
|
||||||
type Message interface {
|
type Message interface {
|
||||||
Topic() string
|
Topic() string
|
||||||
|
26
client/client_call_options_test.go
Normal file
26
client/client_call_options_test.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNewClientCallOptions(t *testing.T) {
|
||||||
|
var flag bool
|
||||||
|
w := func(fn CallFunc) CallFunc {
|
||||||
|
flag = true
|
||||||
|
return fn
|
||||||
|
}
|
||||||
|
c := NewClientCallOptions(NewClient(),
|
||||||
|
WithAddress("127.0.0.1"),
|
||||||
|
WithCallWrapper(w),
|
||||||
|
WithRequestTimeout(1*time.Millisecond),
|
||||||
|
WithRetries(0),
|
||||||
|
WithBackoff(BackoffInterval(10*time.Millisecond, 100*time.Millisecond)),
|
||||||
|
)
|
||||||
|
_ = c.Call(context.TODO(), c.NewRequest("service", "endpoint", nil), nil)
|
||||||
|
if !flag {
|
||||||
|
t.Fatalf("NewClientCallOptions not works")
|
||||||
|
}
|
||||||
|
}
|
174
client/noop.go
174
client/noop.go
@ -3,18 +3,13 @@ package client
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.unistack.org/micro/v3/broker"
|
"go.unistack.org/micro/v3/broker"
|
||||||
"go.unistack.org/micro/v3/codec"
|
"go.unistack.org/micro/v3/codec"
|
||||||
"go.unistack.org/micro/v3/errors"
|
"go.unistack.org/micro/v3/errors"
|
||||||
"go.unistack.org/micro/v3/metadata"
|
"go.unistack.org/micro/v3/metadata"
|
||||||
"go.unistack.org/micro/v3/options"
|
|
||||||
"go.unistack.org/micro/v3/selector"
|
"go.unistack.org/micro/v3/selector"
|
||||||
"go.unistack.org/micro/v3/semconv"
|
|
||||||
"go.unistack.org/micro/v3/tracer"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// DefaultCodecs will be used to encode/decode data
|
// DefaultCodecs will be used to encode/decode data
|
||||||
@ -23,11 +18,7 @@ var DefaultCodecs = map[string]codec.Codec{
|
|||||||
}
|
}
|
||||||
|
|
||||||
type noopClient struct {
|
type noopClient struct {
|
||||||
funcPublish FuncPublish
|
opts Options
|
||||||
funcBatchPublish FuncBatchPublish
|
|
||||||
funcCall FuncCall
|
|
||||||
funcStream FuncStream
|
|
||||||
opts Options
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type noopMessage struct {
|
type noopMessage struct {
|
||||||
@ -48,14 +39,16 @@ type noopRequest struct {
|
|||||||
|
|
||||||
// NewClient returns new noop client
|
// NewClient returns new noop client
|
||||||
func NewClient(opts ...Option) Client {
|
func NewClient(opts ...Option) Client {
|
||||||
n := &noopClient{opts: NewOptions(opts...)}
|
nc := &noopClient{opts: NewOptions(opts...)}
|
||||||
|
// wrap in reverse
|
||||||
|
|
||||||
n.funcCall = n.fnCall
|
c := Client(nc)
|
||||||
n.funcStream = n.fnStream
|
|
||||||
n.funcPublish = n.fnPublish
|
|
||||||
n.funcBatchPublish = n.fnBatchPublish
|
|
||||||
|
|
||||||
return n
|
for i := len(nc.opts.Wrappers); i > 0; i-- {
|
||||||
|
c = nc.opts.Wrappers[i-1](c)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopClient) Name() string {
|
func (n *noopClient) Name() string {
|
||||||
@ -107,13 +100,10 @@ func (n *noopResponse) Read() ([]byte, error) {
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type noopStream struct {
|
type noopStream struct{}
|
||||||
err error
|
|
||||||
ctx context.Context
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noopStream) Context() context.Context {
|
func (n *noopStream) Context() context.Context {
|
||||||
return n.ctx
|
return context.Background()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopStream) Request() Request {
|
func (n *noopStream) Request() Request {
|
||||||
@ -141,21 +131,15 @@ func (n *noopStream) RecvMsg(interface{}) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopStream) Error() error {
|
func (n *noopStream) Error() error {
|
||||||
return n.err
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopStream) Close() error {
|
func (n *noopStream) Close() error {
|
||||||
if sp, ok := tracer.SpanFromContext(n.ctx); ok && sp != nil {
|
return nil
|
||||||
if n.err != nil {
|
|
||||||
sp.SetStatus(tracer.SpanStatusError, n.err.Error())
|
|
||||||
}
|
|
||||||
sp.Finish()
|
|
||||||
}
|
|
||||||
return n.err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopStream) CloseSend() error {
|
func (n *noopStream) CloseSend() error {
|
||||||
return n.err
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopMessage) Topic() string {
|
func (n *noopMessage) Topic() string {
|
||||||
@ -188,25 +172,6 @@ func (n *noopClient) Init(opts ...Option) error {
|
|||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&n.opts)
|
o(&n.opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
n.funcCall = n.fnCall
|
|
||||||
n.funcStream = n.fnStream
|
|
||||||
n.funcPublish = n.fnPublish
|
|
||||||
n.funcBatchPublish = n.fnBatchPublish
|
|
||||||
|
|
||||||
n.opts.Hooks.EachNext(func(hook options.Hook) {
|
|
||||||
switch h := hook.(type) {
|
|
||||||
case HookCall:
|
|
||||||
n.funcCall = h(n.funcCall)
|
|
||||||
case HookStream:
|
|
||||||
n.funcStream = h(n.funcStream)
|
|
||||||
case HookPublish:
|
|
||||||
n.funcPublish = h(n.funcPublish)
|
|
||||||
case HookBatchPublish:
|
|
||||||
n.funcBatchPublish = h(n.funcBatchPublish)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,31 +184,6 @@ func (n *noopClient) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopClient) Call(ctx context.Context, req Request, rsp interface{}, opts ...CallOption) error {
|
func (n *noopClient) Call(ctx context.Context, req Request, rsp interface{}, opts ...CallOption) error {
|
||||||
ts := time.Now()
|
|
||||||
n.opts.Meter.Counter(semconv.ClientRequestInflight, "endpoint", req.Endpoint()).Inc()
|
|
||||||
var sp tracer.Span
|
|
||||||
ctx, sp = n.opts.Tracer.Start(ctx, req.Endpoint()+" rpc-client",
|
|
||||||
tracer.WithSpanKind(tracer.SpanKindClient),
|
|
||||||
tracer.WithSpanLabels("endpoint", req.Endpoint()),
|
|
||||||
)
|
|
||||||
err := n.funcCall(ctx, req, rsp, opts...)
|
|
||||||
n.opts.Meter.Counter(semconv.ClientRequestInflight, "endpoint", req.Endpoint()).Dec()
|
|
||||||
te := time.Since(ts)
|
|
||||||
n.opts.Meter.Summary(semconv.ClientRequestLatencyMicroseconds, "endpoint", req.Endpoint()).Update(te.Seconds())
|
|
||||||
n.opts.Meter.Histogram(semconv.ClientRequestDurationSeconds, "endpoint", req.Endpoint()).Update(te.Seconds())
|
|
||||||
|
|
||||||
if me := errors.FromError(err); me == nil {
|
|
||||||
sp.Finish()
|
|
||||||
n.opts.Meter.Counter(semconv.ClientRequestTotal, "endpoint", req.Endpoint(), "status", "success", "code", strconv.Itoa(int(200))).Inc()
|
|
||||||
} else {
|
|
||||||
sp.SetStatus(tracer.SpanStatusError, err.Error())
|
|
||||||
n.opts.Meter.Counter(semconv.ClientRequestTotal, "endpoint", req.Endpoint(), "status", "failure", "code", strconv.Itoa(int(me.Code))).Inc()
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noopClient) fnCall(ctx context.Context, req Request, rsp interface{}, opts ...CallOption) error {
|
|
||||||
// make a copy of call opts
|
// make a copy of call opts
|
||||||
callOpts := n.opts.CallOptions
|
callOpts := n.opts.CallOptions
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
@ -272,8 +212,11 @@ func (n *noopClient) fnCall(ctx context.Context, req Request, rsp interface{}, o
|
|||||||
}
|
}
|
||||||
|
|
||||||
// make copy of call method
|
// make copy of call method
|
||||||
hcall := func(ctx context.Context, addr string, req Request, rsp interface{}, opts CallOptions) error {
|
hcall := n.call
|
||||||
return nil
|
|
||||||
|
// wrap the call in reverse
|
||||||
|
for i := len(callOpts.CallWrappers); i > 0; i-- {
|
||||||
|
hcall = callOpts.CallWrappers[i-1](hcall)
|
||||||
}
|
}
|
||||||
|
|
||||||
// use the router passed as a call option, or fallback to the rpc clients router
|
// use the router passed as a call option, or fallback to the rpc clients router
|
||||||
@ -372,6 +315,10 @@ func (n *noopClient) fnCall(ctx context.Context, req Request, rsp interface{}, o
|
|||||||
return gerr
|
return gerr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (n *noopClient) call(ctx context.Context, addr string, req Request, rsp interface{}, opts CallOptions) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (n *noopClient) NewRequest(service, endpoint string, req interface{}, opts ...RequestOption) Request {
|
func (n *noopClient) NewRequest(service, endpoint string, req interface{}, opts ...RequestOption) Request {
|
||||||
return &noopRequest{service: service, endpoint: endpoint}
|
return &noopRequest{service: service, endpoint: endpoint}
|
||||||
}
|
}
|
||||||
@ -382,31 +329,6 @@ func (n *noopClient) NewMessage(topic string, msg interface{}, opts ...MessageOp
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopClient) Stream(ctx context.Context, req Request, opts ...CallOption) (Stream, error) {
|
func (n *noopClient) Stream(ctx context.Context, req Request, opts ...CallOption) (Stream, error) {
|
||||||
ts := time.Now()
|
|
||||||
n.opts.Meter.Counter(semconv.ClientRequestInflight, "endpoint", req.Endpoint()).Inc()
|
|
||||||
var sp tracer.Span
|
|
||||||
ctx, sp = n.opts.Tracer.Start(ctx, req.Endpoint()+" rpc-client",
|
|
||||||
tracer.WithSpanKind(tracer.SpanKindClient),
|
|
||||||
tracer.WithSpanLabels("endpoint", req.Endpoint()),
|
|
||||||
)
|
|
||||||
stream, err := n.funcStream(ctx, req, opts...)
|
|
||||||
n.opts.Meter.Counter(semconv.ClientRequestInflight, "endpoint", req.Endpoint()).Dec()
|
|
||||||
te := time.Since(ts)
|
|
||||||
n.opts.Meter.Summary(semconv.ClientRequestLatencyMicroseconds, "endpoint", req.Endpoint()).Update(te.Seconds())
|
|
||||||
n.opts.Meter.Histogram(semconv.ClientRequestDurationSeconds, "endpoint", req.Endpoint()).Update(te.Seconds())
|
|
||||||
|
|
||||||
if me := errors.FromError(err); me == nil {
|
|
||||||
sp.Finish()
|
|
||||||
n.opts.Meter.Counter(semconv.ClientRequestTotal, "endpoint", req.Endpoint(), "status", "success", "code", strconv.Itoa(int(200))).Inc()
|
|
||||||
} else {
|
|
||||||
sp.SetStatus(tracer.SpanStatusError, err.Error())
|
|
||||||
n.opts.Meter.Counter(semconv.ClientRequestTotal, "endpoint", req.Endpoint(), "status", "failure", "code", strconv.Itoa(int(me.Code))).Inc()
|
|
||||||
}
|
|
||||||
|
|
||||||
return stream, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noopClient) fnStream(ctx context.Context, req Request, opts ...CallOption) (Stream, error) {
|
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// make a copy of call opts
|
// make a copy of call opts
|
||||||
@ -547,22 +469,14 @@ func (n *noopClient) fnStream(ctx context.Context, req Request, opts ...CallOpti
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopClient) stream(ctx context.Context, addr string, req Request, opts CallOptions) (Stream, error) {
|
func (n *noopClient) stream(ctx context.Context, addr string, req Request, opts CallOptions) (Stream, error) {
|
||||||
return &noopStream{ctx: ctx}, nil
|
return &noopStream{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopClient) BatchPublish(ctx context.Context, ps []Message, opts ...PublishOption) error {
|
func (n *noopClient) BatchPublish(ctx context.Context, ps []Message, opts ...PublishOption) error {
|
||||||
return n.funcBatchPublish(ctx, ps, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noopClient) fnBatchPublish(ctx context.Context, ps []Message, opts ...PublishOption) error {
|
|
||||||
return n.publish(ctx, ps, opts...)
|
return n.publish(ctx, ps, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopClient) Publish(ctx context.Context, p Message, opts ...PublishOption) error {
|
func (n *noopClient) Publish(ctx context.Context, p Message, opts ...PublishOption) error {
|
||||||
return n.funcPublish(ctx, p, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noopClient) fnPublish(ctx context.Context, p Message, opts ...PublishOption) error {
|
|
||||||
return n.publish(ctx, []Message{p}, opts...)
|
return n.publish(ctx, []Message{p}, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -571,34 +485,21 @@ func (n *noopClient) publish(ctx context.Context, ps []Message, opts ...PublishO
|
|||||||
|
|
||||||
msgs := make([]*broker.Message, 0, len(ps))
|
msgs := make([]*broker.Message, 0, len(ps))
|
||||||
|
|
||||||
// get proxy
|
|
||||||
exchange := ""
|
|
||||||
if v, ok := os.LookupEnv("MICRO_PROXY"); ok {
|
|
||||||
exchange = v
|
|
||||||
}
|
|
||||||
// get the exchange
|
|
||||||
if len(options.Exchange) > 0 {
|
|
||||||
exchange = options.Exchange
|
|
||||||
}
|
|
||||||
|
|
||||||
omd, ok := metadata.FromOutgoingContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
omd = metadata.New(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, p := range ps {
|
for _, p := range ps {
|
||||||
md := metadata.Copy(omd)
|
md, ok := metadata.FromOutgoingContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
md = metadata.New(0)
|
||||||
|
}
|
||||||
md[metadata.HeaderContentType] = p.ContentType()
|
md[metadata.HeaderContentType] = p.ContentType()
|
||||||
|
|
||||||
topic := p.Topic()
|
topic := p.Topic()
|
||||||
if len(exchange) > 0 {
|
|
||||||
topic = exchange
|
// get the exchange
|
||||||
|
if len(options.Exchange) > 0 {
|
||||||
|
topic = options.Exchange
|
||||||
}
|
}
|
||||||
|
|
||||||
md[metadata.HeaderTopic] = topic
|
md[metadata.HeaderTopic] = topic
|
||||||
iter := p.Metadata().Iterator()
|
|
||||||
var k, v string
|
|
||||||
for iter.Next(&k, &v) {
|
|
||||||
md.Set(k, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
var body []byte
|
var body []byte
|
||||||
|
|
||||||
@ -623,13 +524,6 @@ func (n *noopClient) publish(ctx context.Context, ps []Message, opts ...PublishO
|
|||||||
msgs = append(msgs, &broker.Message{Header: md, Body: body})
|
msgs = append(msgs, &broker.Message{Header: md, Body: body})
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(msgs) == 1 {
|
|
||||||
return n.opts.Broker.Publish(ctx, msgs[0].Header[metadata.HeaderTopic], msgs[0],
|
|
||||||
broker.PublishContext(options.Context),
|
|
||||||
broker.PublishBodyOnly(options.BodyOnly),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return n.opts.Broker.BatchPublish(ctx, msgs,
|
return n.opts.Broker.BatchPublish(ctx, msgs,
|
||||||
broker.PublishContext(options.Context),
|
broker.PublishContext(options.Context),
|
||||||
broker.PublishBodyOnly(options.BodyOnly),
|
broker.PublishBodyOnly(options.BodyOnly),
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
package client
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type testHook struct {
|
|
||||||
f bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *testHook) Publish(fn FuncPublish) FuncPublish {
|
|
||||||
return func(ctx context.Context, msg Message, opts ...PublishOption) error {
|
|
||||||
t.f = true
|
|
||||||
return fn(ctx, msg, opts...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNoopHook(t *testing.T) {
|
|
||||||
h := &testHook{}
|
|
||||||
|
|
||||||
c := NewClient(Hooks(HookPublish(h.Publish)))
|
|
||||||
|
|
||||||
if err := c.Init(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.Publish(context.TODO(), c.NewMessage("", nil, MessageContentType("application/octet-stream"))); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !h.f {
|
|
||||||
t.Fatal("hook not works")
|
|
||||||
}
|
|
||||||
}
|
|
@ -12,7 +12,6 @@ import (
|
|||||||
"go.unistack.org/micro/v3/metadata"
|
"go.unistack.org/micro/v3/metadata"
|
||||||
"go.unistack.org/micro/v3/meter"
|
"go.unistack.org/micro/v3/meter"
|
||||||
"go.unistack.org/micro/v3/network/transport"
|
"go.unistack.org/micro/v3/network/transport"
|
||||||
"go.unistack.org/micro/v3/options"
|
|
||||||
"go.unistack.org/micro/v3/register"
|
"go.unistack.org/micro/v3/register"
|
||||||
"go.unistack.org/micro/v3/router"
|
"go.unistack.org/micro/v3/router"
|
||||||
"go.unistack.org/micro/v3/selector"
|
"go.unistack.org/micro/v3/selector"
|
||||||
@ -60,9 +59,6 @@ type Options struct {
|
|||||||
PoolTTL time.Duration
|
PoolTTL time.Duration
|
||||||
// ContextDialer used to connect
|
// ContextDialer used to connect
|
||||||
ContextDialer func(context.Context, string) (net.Conn, error)
|
ContextDialer func(context.Context, string) (net.Conn, error)
|
||||||
// Hooks can be run before broker Publish/BatchPublish and
|
|
||||||
// Subscribe/BatchSubscribe methods
|
|
||||||
Hooks options.Hooks
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCallOptions creates new call options struct
|
// NewCallOptions creates new call options struct
|
||||||
@ -96,6 +92,8 @@ type CallOptions struct {
|
|||||||
Address []string
|
Address []string
|
||||||
// SelectOptions selector options
|
// SelectOptions selector options
|
||||||
SelectOptions []selector.SelectOption
|
SelectOptions []selector.SelectOption
|
||||||
|
// CallWrappers call wrappers
|
||||||
|
CallWrappers []CallWrapper
|
||||||
// StreamTimeout stream timeout
|
// StreamTimeout stream timeout
|
||||||
StreamTimeout time.Duration
|
StreamTimeout time.Duration
|
||||||
// RequestTimeout request timeout
|
// RequestTimeout request timeout
|
||||||
@ -187,7 +185,7 @@ func NewOptions(opts ...Option) Options {
|
|||||||
options := Options{
|
options := Options{
|
||||||
Context: context.Background(),
|
Context: context.Background(),
|
||||||
ContentType: DefaultContentType,
|
ContentType: DefaultContentType,
|
||||||
Codecs: DefaultCodecs,
|
Codecs: make(map[string]codec.Codec),
|
||||||
CallOptions: CallOptions{
|
CallOptions: CallOptions{
|
||||||
Context: context.Background(),
|
Context: context.Background(),
|
||||||
Backoff: DefaultBackoff,
|
Backoff: DefaultBackoff,
|
||||||
@ -308,6 +306,20 @@ func Selector(s selector.Selector) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wrap adds a wrapper to the list of options passed into the client
|
||||||
|
func Wrap(w Wrapper) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.Wrappers = append(o.Wrappers, w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WrapCall adds a wrapper to the list of CallFunc wrappers
|
||||||
|
func WrapCall(cw ...CallWrapper) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.CallOptions.CallWrappers = append(o.CallOptions.CallWrappers, cw...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Backoff is used to set the backoff function used when retrying Calls
|
// Backoff is used to set the backoff function used when retrying Calls
|
||||||
func Backoff(fn BackoffFunc) Option {
|
func Backoff(fn BackoffFunc) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
@ -438,6 +450,13 @@ func WithAddress(a ...string) CallOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithCallWrapper is a CallOption which adds to the existing CallFunc wrappers
|
||||||
|
func WithCallWrapper(cw ...CallWrapper) CallOption {
|
||||||
|
return func(o *CallOptions) {
|
||||||
|
o.CallWrappers = append(o.CallWrappers, cw...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// WithBackoff is a CallOption which overrides that which
|
// WithBackoff is a CallOption which overrides that which
|
||||||
// set in Options.CallOptions
|
// set in Options.CallOptions
|
||||||
func WithBackoff(fn BackoffFunc) CallOption {
|
func WithBackoff(fn BackoffFunc) CallOption {
|
||||||
@ -572,10 +591,3 @@ func RequestContentType(ct string) RequestOption {
|
|||||||
o.ContentType = ct
|
o.ContentType = ct
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hooks sets hook runs before action
|
|
||||||
func Hooks(h ...options.Hook) Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.Hooks = append(o.Hooks, h...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
package cluster
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"go.unistack.org/micro/v3/metadata"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Message sent to member in cluster
|
|
||||||
type Message interface {
|
|
||||||
// Header returns message headers
|
|
||||||
Header() metadata.Metadata
|
|
||||||
// Body returns broker message may be []byte slice or some go struct or interface
|
|
||||||
Body() interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Node interface {
|
|
||||||
// Name returns node name
|
|
||||||
Name() string
|
|
||||||
// Address returns node address
|
|
||||||
Address() string
|
|
||||||
// Metadata returns node metadata
|
|
||||||
Metadata() metadata.Metadata
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cluster interface used for cluster communication across nodes
|
|
||||||
type Cluster interface {
|
|
||||||
// Join is used to take an existing members and performing state sync
|
|
||||||
Join(ctx context.Context, addr ...string) error
|
|
||||||
// Leave broadcast a leave message and stop listeners
|
|
||||||
Leave(ctx context.Context) error
|
|
||||||
// Ping is used to probe live status of the node
|
|
||||||
Ping(ctx context.Context, node Node, payload []byte) error
|
|
||||||
// Members returns the cluster members
|
|
||||||
Members() ([]Node, error)
|
|
||||||
// Broadcast send message for all members in cluster, if filter is not nil, nodes may be filtered
|
|
||||||
// by key/value pairs
|
|
||||||
Broadcast(ctx context.Context, msg Message, filter ...string) error
|
|
||||||
// Unicast send message to single member in cluster
|
|
||||||
Unicast(ctx context.Context, node Node, msg Message) error
|
|
||||||
}
|
|
@ -1,8 +1,19 @@
|
|||||||
// Package codec is an interface for encoding messages
|
// Package codec is an interface for encoding messages
|
||||||
package codec
|
package codec // import "go.unistack.org/micro/v3/codec"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"go.unistack.org/micro/v3/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Message types
|
||||||
|
const (
|
||||||
|
Error MessageType = iota
|
||||||
|
Request
|
||||||
|
Response
|
||||||
|
Event
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -13,44 +24,63 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
// DefaultMaxMsgSize specifies how much data codec can handle
|
||||||
|
DefaultMaxMsgSize = 1024 * 1024 * 4 // 4Mb
|
||||||
// DefaultCodec is the global default codec
|
// DefaultCodec is the global default codec
|
||||||
DefaultCodec = NewCodec()
|
DefaultCodec = NewCodec()
|
||||||
// DefaultTagName specifies struct tag name to control codec Marshal/Unmarshal
|
// DefaultTagName specifies struct tag name to control codec Marshal/Unmarshal
|
||||||
DefaultTagName = "codec"
|
DefaultTagName = "codec"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Codec encodes/decodes various types of messages.
|
// MessageType specifies message type for codec
|
||||||
|
type MessageType int
|
||||||
|
|
||||||
|
// Codec encodes/decodes various types of messages used within micro.
|
||||||
|
// ReadHeader and ReadBody are called in pairs to read requests/responses
|
||||||
|
// from the connection. Close is called when finished with the
|
||||||
|
// connection. ReadBody may be called with a nil argument to force the
|
||||||
|
// body to be read and discarded.
|
||||||
type Codec interface {
|
type Codec interface {
|
||||||
|
ReadHeader(r io.Reader, m *Message, mt MessageType) error
|
||||||
|
ReadBody(r io.Reader, v interface{}) error
|
||||||
|
Write(w io.Writer, m *Message, v interface{}) error
|
||||||
Marshal(v interface{}, opts ...Option) ([]byte, error)
|
Marshal(v interface{}, opts ...Option) ([]byte, error)
|
||||||
Unmarshal(b []byte, v interface{}, opts ...Option) error
|
Unmarshal(b []byte, v interface{}, opts ...Option) error
|
||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
|
||||||
type CodecV2 interface {
|
// Message represents detailed information about
|
||||||
Marshal(buf []byte, v interface{}, opts ...Option) ([]byte, error)
|
// the communication, likely followed by the body.
|
||||||
Unmarshal(buf []byte, v interface{}, opts ...Option) error
|
// In the case of an error, body may be nil.
|
||||||
String() string
|
type Message struct {
|
||||||
|
Header metadata.Metadata
|
||||||
|
Target string
|
||||||
|
Method string
|
||||||
|
Endpoint string
|
||||||
|
Error string
|
||||||
|
ID string
|
||||||
|
Body []byte
|
||||||
|
Type MessageType
|
||||||
}
|
}
|
||||||
|
|
||||||
// RawMessage is a raw encoded JSON value.
|
// NewMessage creates new codec message
|
||||||
// It implements Marshaler and Unmarshaler and can be used to delay decoding or precompute a encoding.
|
func NewMessage(t MessageType) *Message {
|
||||||
type RawMessage []byte
|
return &Message{Type: t, Header: metadata.New(0)}
|
||||||
|
}
|
||||||
|
|
||||||
// MarshalJSON returns m as the JSON encoding of m.
|
// MarshalAppend calls codec.Marshal(v) and returns the data appended to buf.
|
||||||
func (m *RawMessage) MarshalJSON() ([]byte, error) {
|
// If codec implements MarshalAppend, that is called instead.
|
||||||
if m == nil {
|
func MarshalAppend(buf []byte, c Codec, v interface{}, opts ...Option) ([]byte, error) {
|
||||||
return []byte("null"), nil
|
if nc, ok := c.(interface {
|
||||||
} else if len(*m) == 0 {
|
MarshalAppend([]byte, interface{}, ...Option) ([]byte, error)
|
||||||
return []byte("null"), nil
|
}); ok {
|
||||||
|
return nc.MarshalAppend(buf, v, opts...)
|
||||||
}
|
}
|
||||||
return *m, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalJSON sets *m to a copy of data.
|
mbuf, err := c.Marshal(v, opts...)
|
||||||
func (m *RawMessage) UnmarshalJSON(data []byte) error {
|
if err != nil {
|
||||||
if m == nil {
|
return nil, err
|
||||||
return errors.New("RawMessage UnmarshalJSON on nil pointer")
|
|
||||||
}
|
}
|
||||||
*m = append((*m)[0:0], data...)
|
|
||||||
return nil
|
return append(buf, mbuf...), nil
|
||||||
}
|
}
|
||||||
|
@ -2,14 +2,70 @@ package codec
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"io"
|
||||||
codecpb "go.unistack.org/micro-proto/v3/codec"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type noopCodec struct {
|
type noopCodec struct {
|
||||||
opts Options
|
opts Options
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *noopCodec) ReadHeader(conn io.Reader, m *Message, t MessageType) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *noopCodec) ReadBody(conn io.Reader, b interface{}) error {
|
||||||
|
// read bytes
|
||||||
|
buf, err := io.ReadAll(conn)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if b == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v := b.(type) {
|
||||||
|
case *string:
|
||||||
|
*v = string(buf)
|
||||||
|
case *[]byte:
|
||||||
|
*v = buf
|
||||||
|
case *Frame:
|
||||||
|
v.Data = buf
|
||||||
|
default:
|
||||||
|
return json.Unmarshal(buf, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *noopCodec) Write(conn io.Writer, m *Message, b interface{}) error {
|
||||||
|
if b == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var v []byte
|
||||||
|
switch vb := b.(type) {
|
||||||
|
case *Frame:
|
||||||
|
v = vb.Data
|
||||||
|
case string:
|
||||||
|
v = []byte(vb)
|
||||||
|
case *string:
|
||||||
|
v = []byte(*vb)
|
||||||
|
case *[]byte:
|
||||||
|
v = *vb
|
||||||
|
case []byte:
|
||||||
|
v = vb
|
||||||
|
default:
|
||||||
|
var err error
|
||||||
|
v, err = json.Marshal(vb)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err := conn.Write(v)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (c *noopCodec) String() string {
|
func (c *noopCodec) String() string {
|
||||||
return "noop"
|
return "noop"
|
||||||
}
|
}
|
||||||
@ -35,8 +91,8 @@ func (c *noopCodec) Marshal(v interface{}, opts ...Option) ([]byte, error) {
|
|||||||
return ve, nil
|
return ve, nil
|
||||||
case *Frame:
|
case *Frame:
|
||||||
return ve.Data, nil
|
return ve.Data, nil
|
||||||
case *codecpb.Frame:
|
case *Message:
|
||||||
return ve.Data, nil
|
return ve.Body, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return json.Marshal(v)
|
return json.Marshal(v)
|
||||||
@ -59,8 +115,8 @@ func (c *noopCodec) Unmarshal(d []byte, v interface{}, opts ...Option) error {
|
|||||||
case *Frame:
|
case *Frame:
|
||||||
ve.Data = d
|
ve.Data = d
|
||||||
return nil
|
return nil
|
||||||
case *codecpb.Frame:
|
case *Message:
|
||||||
ve.Data = d
|
ve.Body = d
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,8 +23,15 @@ type Options struct {
|
|||||||
Context context.Context
|
Context context.Context
|
||||||
// TagName specifies tag name in struct to control codec
|
// TagName specifies tag name in struct to control codec
|
||||||
TagName string
|
TagName string
|
||||||
// Flatten specifies that struct must be analyzed for flatten tag
|
// MaxMsgSize specifies max messages size that reads by codec
|
||||||
Flatten bool
|
MaxMsgSize int
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaxMsgSize sets the max message size
|
||||||
|
func MaxMsgSize(n int) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.MaxMsgSize = n
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TagName sets the codec tag name in struct
|
// TagName sets the codec tag name in struct
|
||||||
@ -34,13 +41,6 @@ func TagName(n string) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flatten enables checking for flatten tag name
|
|
||||||
func Flatten(b bool) Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.Flatten = b
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Logger sets the logger
|
// Logger sets the logger
|
||||||
func Logger(l logger.Logger) Option {
|
func Logger(l logger.Logger) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
@ -65,12 +65,12 @@ func Meter(m meter.Meter) Option {
|
|||||||
// NewOptions returns new options
|
// NewOptions returns new options
|
||||||
func NewOptions(opts ...Option) Options {
|
func NewOptions(opts ...Option) Options {
|
||||||
options := Options{
|
options := Options{
|
||||||
Context: context.Background(),
|
Context: context.Background(),
|
||||||
Logger: logger.DefaultLogger,
|
Logger: logger.DefaultLogger,
|
||||||
Meter: meter.DefaultMeter,
|
Meter: meter.DefaultMeter,
|
||||||
Tracer: tracer.DefaultTracer,
|
Tracer: tracer.DefaultTracer,
|
||||||
TagName: DefaultTagName,
|
MaxMsgSize: DefaultMaxMsgSize,
|
||||||
Flatten: false,
|
TagName: DefaultTagName,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
// Package config is an interface for dynamic configuration.
|
// Package config is an interface for dynamic configuration.
|
||||||
package config
|
package config // import "go.unistack.org/micro/v3/config"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -14,7 +13,7 @@ type Validator interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DefaultConfig default config
|
// DefaultConfig default config
|
||||||
var DefaultConfig Config = NewConfig()
|
var DefaultConfig = NewConfig()
|
||||||
|
|
||||||
// DefaultWatcherMinInterval default min interval for poll changes
|
// DefaultWatcherMinInterval default min interval for poll changes
|
||||||
var DefaultWatcherMinInterval = 5 * time.Second
|
var DefaultWatcherMinInterval = 5 * time.Second
|
||||||
@ -51,13 +50,6 @@ type Config interface {
|
|||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
|
||||||
type (
|
|
||||||
FuncLoad func(ctx context.Context, opts ...LoadOption) error
|
|
||||||
HookLoad func(next FuncLoad) FuncLoad
|
|
||||||
FuncSave func(ctx context.Context, opts ...SaveOption) error
|
|
||||||
HookSave func(next FuncSave) FuncSave
|
|
||||||
)
|
|
||||||
|
|
||||||
// Watcher is the config watcher
|
// Watcher is the config watcher
|
||||||
type Watcher interface {
|
type Watcher interface {
|
||||||
// Next blocks until update happens or error returned
|
// Next blocks until update happens or error returned
|
||||||
@ -132,29 +124,11 @@ func Validate(ctx context.Context, cfg interface{}) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// DefaultBeforeLoad default func that runs before config Load
|
// DefaultAfterLoad default func that runs after config load
|
||||||
DefaultBeforeLoad = func(ctx context.Context, c Config) error {
|
|
||||||
for _, fn := range c.Options().BeforeLoad {
|
|
||||||
if fn == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err := fn(ctx, c); err != nil {
|
|
||||||
c.Options().Logger.Error(ctx, fmt.Sprintf("%s BeforeLoad error", c.String()), err)
|
|
||||||
if !c.Options().AllowFail {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// DefaultAfterLoad default func that runs after config Load
|
|
||||||
DefaultAfterLoad = func(ctx context.Context, c Config) error {
|
DefaultAfterLoad = func(ctx context.Context, c Config) error {
|
||||||
for _, fn := range c.Options().AfterLoad {
|
for _, fn := range c.Options().AfterLoad {
|
||||||
if fn == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err := fn(ctx, c); err != nil {
|
if err := fn(ctx, c); err != nil {
|
||||||
c.Options().Logger.Error(ctx, fmt.Sprintf("%s AfterLoad error", c.String()), err)
|
c.Options().Logger.Errorf(ctx, "%s AfterLoad err: %v", c.String(), err)
|
||||||
if !c.Options().AllowFail {
|
if !c.Options().AllowFail {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -162,29 +136,11 @@ var (
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// DefaultBeforeSave default func that runs befora config Save
|
// DefaultAfterSave default func that runs after config save
|
||||||
DefaultBeforeSave = func(ctx context.Context, c Config) error {
|
|
||||||
for _, fn := range c.Options().BeforeSave {
|
|
||||||
if fn == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err := fn(ctx, c); err != nil {
|
|
||||||
c.Options().Logger.Error(ctx, fmt.Sprintf("%s BeforeSave error", c.String()), err)
|
|
||||||
if !c.Options().AllowFail {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// DefaultAfterSave default func that runs after config Save
|
|
||||||
DefaultAfterSave = func(ctx context.Context, c Config) error {
|
DefaultAfterSave = func(ctx context.Context, c Config) error {
|
||||||
for _, fn := range c.Options().AfterSave {
|
for _, fn := range c.Options().AfterSave {
|
||||||
if fn == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err := fn(ctx, c); err != nil {
|
if err := fn(ctx, c); err != nil {
|
||||||
c.Options().Logger.Error(ctx, fmt.Sprintf("%s AfterSave error", c.String()), err)
|
c.Options().Logger.Errorf(ctx, "%s AfterSave err: %v", c.String(), err)
|
||||||
if !c.Options().AllowFail {
|
if !c.Options().AllowFail {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -192,14 +148,11 @@ var (
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// DefaultBeforeInit default func that runs befora config Init
|
// DefaultBeforeLoad default func that runs before config load
|
||||||
DefaultBeforeInit = func(ctx context.Context, c Config) error {
|
DefaultBeforeLoad = func(ctx context.Context, c Config) error {
|
||||||
for _, fn := range c.Options().BeforeInit {
|
for _, fn := range c.Options().BeforeLoad {
|
||||||
if fn == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err := fn(ctx, c); err != nil {
|
if err := fn(ctx, c); err != nil {
|
||||||
c.Options().Logger.Error(ctx, fmt.Sprintf("%s BeforeInit error", c.String()), err)
|
c.Options().Logger.Errorf(ctx, "%s BeforeLoad err: %v", c.String(), err)
|
||||||
if !c.Options().AllowFail {
|
if !c.Options().AllowFail {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -207,14 +160,11 @@ var (
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
// DefaultAfterInit default func that runs after config Init
|
// DefaultBeforeSave default func that runs befora config save
|
||||||
DefaultAfterInit = func(ctx context.Context, c Config) error {
|
DefaultBeforeSave = func(ctx context.Context, c Config) error {
|
||||||
for _, fn := range c.Options().AfterSave {
|
for _, fn := range c.Options().BeforeSave {
|
||||||
if fn == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err := fn(ctx, c); err != nil {
|
if err := fn(ctx, c); err != nil {
|
||||||
c.Options().Logger.Error(ctx, fmt.Sprintf("%s AfterInit error", c.String(), err), err)
|
c.Options().Logger.Errorf(ctx, "%s BeforeSave err: %v", c.String(), err)
|
||||||
if !c.Options().AllowFail {
|
if !c.Options().AllowFail {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -5,20 +5,13 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"dario.cat/mergo"
|
"github.com/imdario/mergo"
|
||||||
"github.com/google/uuid"
|
|
||||||
"go.unistack.org/micro/v3/options"
|
|
||||||
mid "go.unistack.org/micro/v3/util/id"
|
|
||||||
rutil "go.unistack.org/micro/v3/util/reflect"
|
rutil "go.unistack.org/micro/v3/util/reflect"
|
||||||
mtime "go.unistack.org/micro/v3/util/time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type defaultConfig struct {
|
type defaultConfig struct {
|
||||||
funcLoad FuncLoad
|
opts Options
|
||||||
funcSave FuncSave
|
|
||||||
opts Options
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *defaultConfig) Options() Options {
|
func (c *defaultConfig) Options() Options {
|
||||||
@ -29,42 +22,11 @@ func (c *defaultConfig) Init(opts ...Option) error {
|
|||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&c.opts)
|
o(&c.opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := DefaultBeforeInit(c.opts.Context, c); err != nil && !c.opts.AllowFail {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
c.funcLoad = c.fnLoad
|
|
||||||
c.funcSave = c.fnSave
|
|
||||||
|
|
||||||
c.opts.Hooks.EachNext(func(hook options.Hook) {
|
|
||||||
switch h := hook.(type) {
|
|
||||||
case HookLoad:
|
|
||||||
c.funcLoad = h(c.funcLoad)
|
|
||||||
case HookSave:
|
|
||||||
c.funcSave = h(c.funcSave)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if err := DefaultAfterInit(c.opts.Context, c); err != nil && !c.opts.AllowFail {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *defaultConfig) Load(ctx context.Context, opts ...LoadOption) error {
|
func (c *defaultConfig) Load(ctx context.Context, opts ...LoadOption) error {
|
||||||
return c.funcLoad(ctx, opts...)
|
if err := DefaultBeforeLoad(ctx, c); err != nil {
|
||||||
}
|
|
||||||
|
|
||||||
func (c *defaultConfig) fnLoad(ctx context.Context, opts ...LoadOption) error {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if c.opts.SkipLoad != nil && c.opts.SkipLoad(ctx, c) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = DefaultBeforeLoad(ctx, c); err != nil && !c.opts.AllowFail {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,20 +49,21 @@ func (c *defaultConfig) fnLoad(ctx context.Context, opts ...LoadOption) error {
|
|||||||
if !c.opts.AllowFail {
|
if !c.opts.AllowFail {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err = DefaultAfterLoad(ctx, c); err != nil && !c.opts.AllowFail {
|
return DefaultAfterLoad(ctx, c)
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = fillValues(reflect.ValueOf(src), c.opts.StructTag); err == nil {
|
if err = fillValues(reflect.ValueOf(src), c.opts.StructTag); err == nil {
|
||||||
err = mergo.Merge(dst, src, mopts...)
|
err = mergo.Merge(dst, src, mopts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil && !c.opts.AllowFail {
|
if err != nil {
|
||||||
return err
|
c.opts.Logger.Errorf(ctx, "default load error: %v", err)
|
||||||
|
if !c.opts.AllowFail {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := DefaultAfterLoad(ctx, c); err != nil && !c.opts.AllowFail {
|
if err := DefaultAfterLoad(ctx, c); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,7 +75,6 @@ func fillValue(value reflect.Value, val string) error {
|
|||||||
if !rutil.IsEmpty(value) {
|
if !rutil.IsEmpty(value) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
switch value.Kind() {
|
switch value.Kind() {
|
||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
t := value.Type()
|
t := value.Type()
|
||||||
@ -151,20 +113,6 @@ func fillValue(value reflect.Value, val string) error {
|
|||||||
}
|
}
|
||||||
value.Set(reflect.ValueOf(v))
|
value.Set(reflect.ValueOf(v))
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
switch val {
|
|
||||||
case "micro:generate uuid":
|
|
||||||
uid, err := uuid.NewRandom()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
val = uid.String()
|
|
||||||
case "micro:generate id":
|
|
||||||
uid, err := mid.New()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
val = uid
|
|
||||||
}
|
|
||||||
value.Set(reflect.ValueOf(val))
|
value.Set(reflect.ValueOf(val))
|
||||||
case reflect.Float32:
|
case reflect.Float32:
|
||||||
v, err := strconv.ParseFloat(val, 32)
|
v, err := strconv.ParseFloat(val, 32)
|
||||||
@ -203,26 +151,11 @@ func fillValue(value reflect.Value, val string) error {
|
|||||||
}
|
}
|
||||||
value.Set(reflect.ValueOf(int32(v)))
|
value.Set(reflect.ValueOf(int32(v)))
|
||||||
case reflect.Int64:
|
case reflect.Int64:
|
||||||
switch {
|
v, err := strconv.ParseInt(val, 10, 64)
|
||||||
case value.Type().String() == "time.Duration" && value.Type().PkgPath() == "time":
|
if err != nil {
|
||||||
v, err := time.ParseDuration(val)
|
return err
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
value.Set(reflect.ValueOf(v))
|
|
||||||
case value.Type().String() == "time.Duration" && value.Type().PkgPath() == "go.unistack.org/micro/v3/util/time":
|
|
||||||
v, err := mtime.ParseDuration(val)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
value.SetInt(int64(v))
|
|
||||||
default:
|
|
||||||
v, err := strconv.ParseInt(val, 10, 64)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
value.Set(reflect.ValueOf(v))
|
|
||||||
}
|
}
|
||||||
|
value.Set(reflect.ValueOf(v))
|
||||||
case reflect.Uint:
|
case reflect.Uint:
|
||||||
v, err := strconv.ParseUint(val, 10, 0)
|
v, err := strconv.ParseUint(val, 10, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -254,7 +187,6 @@ func fillValue(value reflect.Value, val string) error {
|
|||||||
}
|
}
|
||||||
value.Set(reflect.ValueOf(v))
|
value.Set(reflect.ValueOf(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,14 +250,6 @@ func fillValues(valueOf reflect.Value, tname string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *defaultConfig) Save(ctx context.Context, opts ...SaveOption) error {
|
func (c *defaultConfig) Save(ctx context.Context, opts ...SaveOption) error {
|
||||||
return c.funcSave(ctx, opts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *defaultConfig) fnSave(ctx context.Context, opts ...SaveOption) error {
|
|
||||||
if c.opts.SkipSave != nil && c.opts.SkipSave(ctx, c) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := DefaultBeforeSave(ctx, c); err != nil {
|
if err := DefaultBeforeSave(ctx, c); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -345,7 +269,7 @@ func (c *defaultConfig) Name() string {
|
|||||||
return c.opts.Name
|
return c.opts.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *defaultConfig) Watch(_ context.Context, _ ...WatchOption) (Watcher, error) {
|
func (c *defaultConfig) Watch(ctx context.Context, opts ...WatchOption) (Watcher, error) {
|
||||||
return nil, ErrWatcherNotImplemented
|
return nil, ErrWatcherNotImplemented
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -355,9 +279,5 @@ func NewConfig(opts ...Option) Config {
|
|||||||
if len(options.StructTag) == 0 {
|
if len(options.StructTag) == 0 {
|
||||||
options.StructTag = "default"
|
options.StructTag = "default"
|
||||||
}
|
}
|
||||||
c := &defaultConfig{opts: options}
|
return &defaultConfig{opts: options}
|
||||||
c.funcLoad = c.fnLoad
|
|
||||||
c.funcSave = c.fnSave
|
|
||||||
|
|
||||||
return c
|
|
||||||
}
|
}
|
||||||
|
@ -4,23 +4,15 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
"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"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type cfg struct {
|
type cfg struct {
|
||||||
StringValue string `default:"string_value"`
|
StringValue string `default:"string_value"`
|
||||||
IgnoreValue string `json:"-"`
|
IgnoreValue string `json:"-"`
|
||||||
StructValue *cfgStructValue
|
StructValue *cfgStructValue
|
||||||
IntValue int `default:"99"`
|
IntValue int `default:"99"`
|
||||||
DurationValue time.Duration `default:"10s"`
|
|
||||||
MDurationValue mtime.Duration `default:"10s"`
|
|
||||||
MapValue map[string]bool `default:"key1=true,key2=false"`
|
|
||||||
UUIDValue string `default:"micro:generate uuid"`
|
|
||||||
IDValue string `default:"micro:generate id"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type cfgStructValue struct {
|
type cfgStructValue struct {
|
||||||
@ -41,35 +33,6 @@ func (c *cfgStructValue) Validate() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type testHook struct {
|
|
||||||
f bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *testHook) Load(fn config.FuncLoad) config.FuncLoad {
|
|
||||||
return func(ctx context.Context, opts ...config.LoadOption) error {
|
|
||||||
t.f = true
|
|
||||||
return fn(ctx, opts...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHook(t *testing.T) {
|
|
||||||
h := &testHook{}
|
|
||||||
|
|
||||||
c := config.NewConfig(config.Struct(h), config.Hooks(config.HookLoad(h.Load)))
|
|
||||||
|
|
||||||
if err := c.Init(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.Load(context.TODO()); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !h.f {
|
|
||||||
t.Fatal("hook not works")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDefault(t *testing.T) {
|
func TestDefault(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
conf := &cfg{IntValue: 10}
|
conf := &cfg{IntValue: 10}
|
||||||
@ -100,21 +63,6 @@ func TestDefault(t *testing.T) {
|
|||||||
if conf.StringValue != "after_load" {
|
if conf.StringValue != "after_load" {
|
||||||
t.Fatal("AfterLoad option not working")
|
t.Fatal("AfterLoad option not working")
|
||||||
}
|
}
|
||||||
if len(conf.MapValue) != 2 {
|
|
||||||
t.Fatalf("map value invalid: %#+v\n", conf.MapValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
if conf.UUIDValue == "" {
|
|
||||||
t.Fatalf("uuid value empty")
|
|
||||||
} else if len(conf.UUIDValue) != 36 {
|
|
||||||
t.Fatalf("uuid value invalid: %s", conf.UUIDValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
if conf.IDValue == "" {
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"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/meter"
|
"go.unistack.org/micro/v3/meter"
|
||||||
"go.unistack.org/micro/v3/options"
|
|
||||||
"go.unistack.org/micro/v3/tracer"
|
"go.unistack.org/micro/v3/tracer"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -29,26 +28,16 @@ type Options struct {
|
|||||||
Name string
|
Name string
|
||||||
// StructTag name
|
// StructTag name
|
||||||
StructTag string
|
StructTag string
|
||||||
// BeforeSave contains slice of funcs that runs before Save
|
// BeforeSave contains slice of funcs that runs before save
|
||||||
BeforeSave []func(context.Context, Config) error
|
BeforeSave []func(context.Context, Config) error
|
||||||
// AfterSave contains slice of funcs that runs after Save
|
// AfterLoad contains slice of funcs that runs after load
|
||||||
AfterSave []func(context.Context, Config) error
|
|
||||||
// BeforeLoad contains slice of funcs that runs before Load
|
|
||||||
BeforeLoad []func(context.Context, Config) error
|
|
||||||
// AfterLoad contains slice of funcs that runs after Load
|
|
||||||
AfterLoad []func(context.Context, Config) error
|
AfterLoad []func(context.Context, Config) error
|
||||||
// BeforeInit contains slice of funcs that runs before Init
|
// BeforeLoad contains slice of funcs that runs before load
|
||||||
BeforeInit []func(context.Context, Config) error
|
BeforeLoad []func(context.Context, Config) error
|
||||||
// AfterInit contains slice of funcs that runs after Init
|
// AfterSave contains slice of funcs that runs after save
|
||||||
AfterInit []func(context.Context, Config) error
|
AfterSave []func(context.Context, Config) error
|
||||||
// AllowFail flag to allow fail in config source
|
// AllowFail flag to allow fail in config source
|
||||||
AllowFail bool
|
AllowFail bool
|
||||||
// SkipLoad runs only if condition returns true
|
|
||||||
SkipLoad func(context.Context, Config) bool
|
|
||||||
// SkipSave runs only if condition returns true
|
|
||||||
SkipSave func(context.Context, Config) bool
|
|
||||||
// Hooks can be run before/after config Save/Load
|
|
||||||
Hooks options.Hooks
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Option function signature
|
// Option function signature
|
||||||
@ -75,9 +64,9 @@ type LoadOption func(o *LoadOptions)
|
|||||||
// LoadOptions struct
|
// LoadOptions struct
|
||||||
type LoadOptions struct {
|
type LoadOptions struct {
|
||||||
Struct interface{}
|
Struct interface{}
|
||||||
Context context.Context
|
|
||||||
Override bool
|
Override bool
|
||||||
Append bool
|
Append bool
|
||||||
|
Context context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLoadOptions create LoadOptions struct with provided opts
|
// NewLoadOptions create LoadOptions struct with provided opts
|
||||||
@ -142,20 +131,6 @@ func AllowFail(b bool) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BeforeInit run funcs before config Init
|
|
||||||
func BeforeInit(fn ...func(context.Context, Config) error) Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.BeforeInit = fn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AfterInit run funcs after config Init
|
|
||||||
func AfterInit(fn ...func(context.Context, Config) error) Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.AfterInit = fn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// BeforeLoad run funcs before config load
|
// BeforeLoad run funcs before config load
|
||||||
func BeforeLoad(fn ...func(context.Context, Config) error) Option {
|
func BeforeLoad(fn ...func(context.Context, Config) error) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
@ -291,10 +266,3 @@ func WatchStruct(src interface{}) WatchOption {
|
|||||||
o.Struct = src
|
o.Struct = src
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hooks sets hook runs before action
|
|
||||||
func Hooks(h ...options.Hook) Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.Hooks = append(o.Hooks, h...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
157
database/dsn.go
157
database/dsn.go
@ -1,157 +0,0 @@
|
|||||||
package database
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrInvalidDSNAddr = errors.New("invalid dsn addr")
|
|
||||||
ErrInvalidDSNUnescaped = errors.New("dsn must be escaped")
|
|
||||||
ErrInvalidDSNNoSlash = errors.New("dsn must contains slash")
|
|
||||||
)
|
|
||||||
|
|
||||||
type Config struct {
|
|
||||||
TLSConfig *tls.Config
|
|
||||||
Username string
|
|
||||||
Password string
|
|
||||||
Scheme string
|
|
||||||
Host string
|
|
||||||
Port string
|
|
||||||
Database string
|
|
||||||
Params []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *Config) FormatDSN() string {
|
|
||||||
var s strings.Builder
|
|
||||||
|
|
||||||
if len(cfg.Scheme) > 0 {
|
|
||||||
s.WriteString(cfg.Scheme + "://")
|
|
||||||
}
|
|
||||||
// [username[:password]@]
|
|
||||||
if len(cfg.Username) > 0 {
|
|
||||||
s.WriteString(cfg.Username)
|
|
||||||
if len(cfg.Password) > 0 {
|
|
||||||
s.WriteByte(':')
|
|
||||||
s.WriteString(url.PathEscape(cfg.Password))
|
|
||||||
}
|
|
||||||
s.WriteByte('@')
|
|
||||||
}
|
|
||||||
|
|
||||||
// [host:port]
|
|
||||||
if len(cfg.Host) > 0 {
|
|
||||||
s.WriteString(cfg.Host)
|
|
||||||
if len(cfg.Port) > 0 {
|
|
||||||
s.WriteByte(':')
|
|
||||||
s.WriteString(cfg.Port)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// /dbname
|
|
||||||
s.WriteByte('/')
|
|
||||||
s.WriteString(url.PathEscape(cfg.Database))
|
|
||||||
|
|
||||||
for i := 0; i < len(cfg.Params); i += 2 {
|
|
||||||
if i == 0 {
|
|
||||||
s.WriteString("?")
|
|
||||||
} else {
|
|
||||||
s.WriteString("&")
|
|
||||||
}
|
|
||||||
s.WriteString(cfg.Params[i])
|
|
||||||
s.WriteString("=")
|
|
||||||
s.WriteString(cfg.Params[i+1])
|
|
||||||
}
|
|
||||||
|
|
||||||
return s.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseDSN(dsn string) (*Config, error) {
|
|
||||||
cfg := &Config{}
|
|
||||||
|
|
||||||
// [user[:password]@][net[(addr)]]/dbname[?param1=value1¶mN=valueN]
|
|
||||||
// Find last '/' that goes before dbname
|
|
||||||
foundSlash := false
|
|
||||||
for i := len(dsn) - 1; i >= 0; i-- {
|
|
||||||
if dsn[i] == '/' {
|
|
||||||
foundSlash = true
|
|
||||||
var j, k int
|
|
||||||
|
|
||||||
// left part is empty if i <= 0
|
|
||||||
if i > 0 {
|
|
||||||
// Find the first ':' in dsn
|
|
||||||
for j = i; j >= 0; j-- {
|
|
||||||
if dsn[j] == ':' {
|
|
||||||
cfg.Scheme = dsn[0:j]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// [username[:password]@][host]
|
|
||||||
// Find the last '@' in dsn[:i]
|
|
||||||
for j = i; j >= 0; j-- {
|
|
||||||
if dsn[j] == '@' {
|
|
||||||
// username[:password]
|
|
||||||
// Find the second ':' in dsn[:j]
|
|
||||||
for k = 0; k < j; k++ {
|
|
||||||
if dsn[k] == ':' {
|
|
||||||
if cfg.Scheme == dsn[:k] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
cfg.Password, err = url.PathUnescape(dsn[k+1 : j])
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cfg.Username = dsn[len(cfg.Scheme)+3 : k]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for k = j + 1; k < i; k++ {
|
|
||||||
if dsn[k] == ':' {
|
|
||||||
cfg.Host = dsn[j+1 : k]
|
|
||||||
cfg.Port = dsn[k+1 : i]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// dbname[?param1=value1&...¶mN=valueN]
|
|
||||||
// Find the first '?' in dsn[i+1:]
|
|
||||||
for j = i + 1; j < len(dsn); j++ {
|
|
||||||
if dsn[j] == '?' {
|
|
||||||
parts := strings.Split(dsn[j+1:], "&")
|
|
||||||
cfg.Params = make([]string, 0, len(parts)*2)
|
|
||||||
for _, p := range parts {
|
|
||||||
k, v, found := strings.Cut(p, "=")
|
|
||||||
if !found {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
cfg.Params = append(cfg.Params, k, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
dbname := dsn[i+1 : j]
|
|
||||||
if cfg.Database, err = url.PathUnescape(dbname); err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid dbname %q: %w", dbname, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !foundSlash && len(dsn) > 0 {
|
|
||||||
return nil, ErrInvalidDSNNoSlash
|
|
||||||
}
|
|
||||||
|
|
||||||
return cfg, nil
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
package database
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/url"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestParseDSN(t *testing.T) {
|
|
||||||
cfg, err := ParseDSN("postgres://username:p@ssword#@host:12345/dbname?key1=val2&key2=val2")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if cfg.Password != "p@ssword#" {
|
|
||||||
t.Fatalf("parsing error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFormatDSN(t *testing.T) {
|
|
||||||
src := "postgres://username:p@ssword#@host:12345/dbname?key1=val2&key2=val2"
|
|
||||||
cfg, err := ParseDSN(src)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
dst, err := url.PathUnescape(cfg.FormatDSN())
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if src != dst {
|
|
||||||
t.Fatalf("\n%s\n%s", src, dst)
|
|
||||||
}
|
|
||||||
}
|
|
158
errors/errors.go
158
errors/errors.go
@ -1,20 +1,14 @@
|
|||||||
// Package errors provides a way to return detailed information
|
// Package errors provides a way to return detailed information
|
||||||
// for an RPC request error. The error is normally JSON encoded.
|
// for an RPC request error. The error is normally JSON encoded.
|
||||||
package errors
|
package errors // import "go.unistack.org/micro/v3/errors"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"google.golang.org/grpc/codes"
|
|
||||||
"google.golang.org/grpc/status"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -44,20 +38,6 @@ var (
|
|||||||
ErrGatewayTimeout = &Error{Code: 504}
|
ErrGatewayTimeout = &Error{Code: 504}
|
||||||
)
|
)
|
||||||
|
|
||||||
const ProblemContentType = "application/problem+json"
|
|
||||||
|
|
||||||
type Problem struct {
|
|
||||||
Type string `json:"type,omitempty"`
|
|
||||||
Title string `json:"title,omitempty"`
|
|
||||||
Detail string `json:"detail,omitempty"`
|
|
||||||
Instance string `json:"instance,omitempty"`
|
|
||||||
Errors []struct {
|
|
||||||
Title string `json:"title,omitempty"`
|
|
||||||
Detail string `json:"detail,omitempty"`
|
|
||||||
} `json:"errors,omitempty"`
|
|
||||||
Status int `json:"status,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error type
|
// Error type
|
||||||
type Error struct {
|
type Error struct {
|
||||||
// ID holds error id or service, usually someting like my_service or id
|
// ID holds error id or service, usually someting like my_service or id
|
||||||
@ -276,10 +256,6 @@ func CodeIn(err interface{}, codes ...int32) bool {
|
|||||||
|
|
||||||
// FromError try to convert go error to *Error
|
// FromError try to convert go error to *Error
|
||||||
func FromError(err error) *Error {
|
func FromError(err error) *Error {
|
||||||
if err == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if verr, ok := err.(*Error); ok && verr != nil {
|
if verr, ok := err.(*Error); ok && verr != nil {
|
||||||
return verr
|
return verr
|
||||||
}
|
}
|
||||||
@ -364,135 +340,3 @@ func addslashes(str string) string {
|
|||||||
}
|
}
|
||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
type retryableError struct {
|
|
||||||
err error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retryable returns error that can be retried later
|
|
||||||
func Retryable(err error) error {
|
|
||||||
return &retryableError{err: err}
|
|
||||||
}
|
|
||||||
|
|
||||||
type IsRetryableFunc func(error) bool
|
|
||||||
|
|
||||||
var (
|
|
||||||
RetrayableOracleErrors = []IsRetryableFunc{
|
|
||||||
func(err error) bool {
|
|
||||||
errmsg := err.Error()
|
|
||||||
switch {
|
|
||||||
case strings.Contains(errmsg, `ORA-`):
|
|
||||||
return true
|
|
||||||
case strings.Contains(errmsg, `can not assign`):
|
|
||||||
return true
|
|
||||||
case strings.Contains(errmsg, `can't assign`):
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
}
|
|
||||||
RetrayablePostgresErrors = []IsRetryableFunc{
|
|
||||||
func(err error) bool {
|
|
||||||
errmsg := err.Error()
|
|
||||||
switch {
|
|
||||||
case strings.Contains(errmsg, `number of field descriptions must equal number of`):
|
|
||||||
return true
|
|
||||||
case strings.Contains(errmsg, `not a pointer`):
|
|
||||||
return true
|
|
||||||
case strings.Contains(errmsg, `values, but dst struct has only`):
|
|
||||||
return true
|
|
||||||
case strings.Contains(errmsg, `struct doesn't have corresponding row field`):
|
|
||||||
return true
|
|
||||||
case strings.Contains(errmsg, `cannot find field`):
|
|
||||||
return true
|
|
||||||
case strings.Contains(errmsg, `cannot scan`) || strings.Contains(errmsg, `cannot convert`):
|
|
||||||
return true
|
|
||||||
case strings.Contains(errmsg, `failed to connect to`):
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
}
|
|
||||||
RetryableMicroErrors = []IsRetryableFunc{
|
|
||||||
func(err error) bool {
|
|
||||||
switch verr := err.(type) {
|
|
||||||
case *Error:
|
|
||||||
switch verr.Code {
|
|
||||||
case 401, 403, 408, 500, 501, 502, 503, 504:
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
case *retryableError:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
}
|
|
||||||
RetryableGoErrors = []IsRetryableFunc{
|
|
||||||
func(err error) bool {
|
|
||||||
switch verr := err.(type) {
|
|
||||||
case interface{ SafeToRetry() bool }:
|
|
||||||
return verr.SafeToRetry()
|
|
||||||
case interface{ Timeout() bool }:
|
|
||||||
return verr.Timeout()
|
|
||||||
}
|
|
||||||
switch {
|
|
||||||
case errors.Is(err, io.EOF), errors.Is(err, io.ErrUnexpectedEOF):
|
|
||||||
return true
|
|
||||||
case errors.Is(err, context.DeadlineExceeded):
|
|
||||||
return true
|
|
||||||
case errors.Is(err, io.ErrClosedPipe), errors.Is(err, io.ErrShortBuffer), errors.Is(err, io.ErrShortWrite):
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
}
|
|
||||||
RetryableGrpcErrors = []IsRetryableFunc{
|
|
||||||
func(err error) bool {
|
|
||||||
st, ok := status.FromError(err)
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
switch st.Code() {
|
|
||||||
case codes.Unavailable, codes.ResourceExhausted:
|
|
||||||
return true
|
|
||||||
case codes.DeadlineExceeded:
|
|
||||||
return true
|
|
||||||
case codes.Internal:
|
|
||||||
switch {
|
|
||||||
case strings.Contains(st.Message(), `transport: received the unexpected content-type "text/html; charset=UTF-8"`):
|
|
||||||
return true
|
|
||||||
case strings.Contains(st.Message(), io.ErrUnexpectedEOF.Error()):
|
|
||||||
return true
|
|
||||||
case strings.Contains(st.Message(), `stream terminated by RST_STREAM with error code: INTERNAL_ERROR`):
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Unwrap provides error wrapping
|
|
||||||
func (e *retryableError) Unwrap() error {
|
|
||||||
return e.err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error returns the error string
|
|
||||||
func (e *retryableError) Error() string {
|
|
||||||
if e.err == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return e.err.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsRetryable checks error for ability to retry later
|
|
||||||
func IsRetryable(err error, fns ...IsRetryableFunc) bool {
|
|
||||||
for _, fn := range fns {
|
|
||||||
if ok := fn(err); ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
@ -8,13 +8,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIsRetrayable(t *testing.T) {
|
|
||||||
err := fmt.Errorf("ORA-")
|
|
||||||
if !IsRetryable(err, RetrayableOracleErrors...) {
|
|
||||||
t.Fatalf("IsRetrayable not works")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMarshalJSON(t *testing.T) {
|
func TestMarshalJSON(t *testing.T) {
|
||||||
e := InternalServerError("id", "err: %v", fmt.Errorf("err: %v", `xxx: "UNIX_TIMESTAMP": invalid identifier`))
|
e := InternalServerError("id", "err: %v", fmt.Errorf("err: %v", `xxx: "UNIX_TIMESTAMP": invalid identifier`))
|
||||||
_, err := json.Marshal(e)
|
_, err := json.Marshal(e)
|
||||||
|
@ -8,7 +8,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestDeps(t *testing.T) {
|
func TestDeps(t *testing.T) {
|
||||||
t.Skip()
|
|
||||||
d := &dag.AcyclicGraph{}
|
d := &dag.AcyclicGraph{}
|
||||||
|
|
||||||
v0 := d.Add(&node{"v0"})
|
v0 := d.Add(&node{"v0"})
|
||||||
|
@ -188,7 +188,7 @@ func (w *microWorkflow) Execute(ctx context.Context, req *Message, opts ...Execu
|
|||||||
steps, err := w.getSteps(options.Start, options.Reverse)
|
steps, err := w.getSteps(options.Start, options.Reverse)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusPending.String())}); werr != nil {
|
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusPending.String())}); werr != nil {
|
||||||
w.opts.Logger.Error(w.opts.Context, "store write error", werr)
|
w.opts.Logger.Errorf(w.opts.Context, "store error: %v", werr)
|
||||||
}
|
}
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -212,7 +212,7 @@ func (w *microWorkflow) Execute(ctx context.Context, req *Message, opts ...Execu
|
|||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
|
|
||||||
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusRunning.String())}); werr != nil {
|
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusRunning.String())}); werr != nil {
|
||||||
w.opts.Logger.Error(w.opts.Context, "store write error", werr)
|
w.opts.Logger.Errorf(w.opts.Context, "store error: %v", werr)
|
||||||
return eid, werr
|
return eid, werr
|
||||||
}
|
}
|
||||||
for idx := range steps {
|
for idx := range steps {
|
||||||
@ -237,7 +237,7 @@ func (w *microWorkflow) Execute(ctx context.Context, req *Message, opts ...Execu
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if w.opts.Logger.V(logger.TraceLevel) {
|
if w.opts.Logger.V(logger.TraceLevel) {
|
||||||
w.opts.Logger.Trace(nctx, fmt.Sprintf("will be executed %v", steps[idx][nidx]))
|
w.opts.Logger.Tracef(nctx, "will be executed %v", steps[idx][nidx])
|
||||||
}
|
}
|
||||||
cstep := steps[idx][nidx]
|
cstep := steps[idx][nidx]
|
||||||
// nolint: nestif
|
// nolint: nestif
|
||||||
@ -257,21 +257,21 @@ func (w *microWorkflow) Execute(ctx context.Context, req *Message, opts ...Execu
|
|||||||
if serr != nil {
|
if serr != nil {
|
||||||
step.SetStatus(StatusFailure)
|
step.SetStatus(StatusFailure)
|
||||||
if werr := stepStore.Write(ctx, step.ID()+w.opts.Store.Options().Separator+"rsp", serr); werr != nil && w.opts.Logger.V(logger.ErrorLevel) {
|
if werr := stepStore.Write(ctx, step.ID()+w.opts.Store.Options().Separator+"rsp", serr); werr != nil && w.opts.Logger.V(logger.ErrorLevel) {
|
||||||
w.opts.Logger.Error(ctx, "store write error", werr)
|
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
|
||||||
}
|
}
|
||||||
if werr := stepStore.Write(ctx, step.ID()+w.opts.Store.Options().Separator+"status", &codec.Frame{Data: []byte(StatusFailure.String())}); werr != nil && w.opts.Logger.V(logger.ErrorLevel) {
|
if werr := stepStore.Write(ctx, step.ID()+w.opts.Store.Options().Separator+"status", &codec.Frame{Data: []byte(StatusFailure.String())}); werr != nil && w.opts.Logger.V(logger.ErrorLevel) {
|
||||||
w.opts.Logger.Error(ctx, "store write error", werr)
|
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
|
||||||
}
|
}
|
||||||
cherr <- serr
|
cherr <- serr
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if werr := stepStore.Write(ctx, step.ID()+w.opts.Store.Options().Separator+"rsp", rsp); werr != nil {
|
if werr := stepStore.Write(ctx, step.ID()+w.opts.Store.Options().Separator+"rsp", rsp); werr != nil {
|
||||||
w.opts.Logger.Error(ctx, "store write error", werr)
|
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
|
||||||
cherr <- werr
|
cherr <- werr
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if werr := stepStore.Write(ctx, step.ID()+w.opts.Store.Options().Separator+"status", &codec.Frame{Data: []byte(StatusSuccess.String())}); werr != nil {
|
if werr := stepStore.Write(ctx, step.ID()+w.opts.Store.Options().Separator+"status", &codec.Frame{Data: []byte(StatusSuccess.String())}); werr != nil {
|
||||||
w.opts.Logger.Error(ctx, "store write error", werr)
|
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
|
||||||
cherr <- werr
|
cherr <- werr
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -290,16 +290,16 @@ func (w *microWorkflow) Execute(ctx context.Context, req *Message, opts ...Execu
|
|||||||
if serr != nil {
|
if serr != nil {
|
||||||
cstep.SetStatus(StatusFailure)
|
cstep.SetStatus(StatusFailure)
|
||||||
if werr := stepStore.Write(ctx, cstep.ID()+w.opts.Store.Options().Separator+"rsp", serr); werr != nil && w.opts.Logger.V(logger.ErrorLevel) {
|
if werr := stepStore.Write(ctx, cstep.ID()+w.opts.Store.Options().Separator+"rsp", serr); werr != nil && w.opts.Logger.V(logger.ErrorLevel) {
|
||||||
w.opts.Logger.Error(ctx, "store write error", werr)
|
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
|
||||||
}
|
}
|
||||||
if werr := stepStore.Write(ctx, cstep.ID()+w.opts.Store.Options().Separator+"status", &codec.Frame{Data: []byte(StatusFailure.String())}); werr != nil && w.opts.Logger.V(logger.ErrorLevel) {
|
if werr := stepStore.Write(ctx, cstep.ID()+w.opts.Store.Options().Separator+"status", &codec.Frame{Data: []byte(StatusFailure.String())}); werr != nil && w.opts.Logger.V(logger.ErrorLevel) {
|
||||||
w.opts.Logger.Error(ctx, "store write error", werr)
|
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
|
||||||
}
|
}
|
||||||
cherr <- serr
|
cherr <- serr
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if werr := stepStore.Write(ctx, cstep.ID()+w.opts.Store.Options().Separator+"rsp", rsp); werr != nil {
|
if werr := stepStore.Write(ctx, cstep.ID()+w.opts.Store.Options().Separator+"rsp", rsp); werr != nil {
|
||||||
w.opts.Logger.Error(ctx, "store write error", werr)
|
w.opts.Logger.Errorf(ctx, "store write error: %v", werr)
|
||||||
cherr <- werr
|
cherr <- werr
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -317,7 +317,7 @@ func (w *microWorkflow) Execute(ctx context.Context, req *Message, opts ...Execu
|
|||||||
return eid, nil
|
return eid, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.DefaultLogger.Trace(ctx, "wait for finish or error")
|
logger.Tracef(ctx, "wait for finish or error")
|
||||||
select {
|
select {
|
||||||
case <-nctx.Done():
|
case <-nctx.Done():
|
||||||
err = nctx.Err()
|
err = nctx.Err()
|
||||||
@ -333,15 +333,15 @@ func (w *microWorkflow) Execute(ctx context.Context, req *Message, opts ...Execu
|
|||||||
switch {
|
switch {
|
||||||
case nctx.Err() != nil:
|
case nctx.Err() != nil:
|
||||||
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusAborted.String())}); werr != nil {
|
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusAborted.String())}); werr != nil {
|
||||||
w.opts.Logger.Error(w.opts.Context, "store write error", werr)
|
w.opts.Logger.Errorf(w.opts.Context, "store error: %v", werr)
|
||||||
}
|
}
|
||||||
case err == nil:
|
case err == nil:
|
||||||
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusSuccess.String())}); werr != nil {
|
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusSuccess.String())}); werr != nil {
|
||||||
w.opts.Logger.Error(w.opts.Context, "store write error", werr)
|
w.opts.Logger.Errorf(w.opts.Context, "store error: %v", werr)
|
||||||
}
|
}
|
||||||
case err != nil:
|
case err != nil:
|
||||||
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusFailure.String())}); werr != nil {
|
if werr := workflowStore.Write(w.opts.Context, "status", &codec.Frame{Data: []byte(StatusFailure.String())}); werr != nil {
|
||||||
w.opts.Logger.Error(w.opts.Context, "store write error", werr)
|
w.opts.Logger.Errorf(w.opts.Context, "store error: %v", werr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Package flow is an interface used for saga pattern microservice workflow
|
// Package flow is an interface used for saga pattern microservice workflow
|
||||||
package flow
|
package flow // import "go.unistack.org/micro/v3/flow"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package fsm
|
package fsm // import "go.unistack.org/micro/v3/fsm"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -17,7 +17,7 @@ func TestFSMStart(t *testing.T) {
|
|||||||
|
|
||||||
wrapper := func(next StateFunc) StateFunc {
|
wrapper := func(next StateFunc) StateFunc {
|
||||||
return func(sctx context.Context, s State, opts ...StateOption) (State, error) {
|
return func(sctx context.Context, s State, opts ...StateOption) (State, error) {
|
||||||
sctx = logger.NewContext(sctx, logger.DefaultLogger.Fields("state", s.Name()))
|
sctx = logger.NewContext(sctx, logger.Fields("state", s.Name()))
|
||||||
return next(sctx, s, opts...)
|
return next(sctx, s, opts...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
33
go.mod
33
go.mod
@ -1,32 +1,13 @@
|
|||||||
module go.unistack.org/micro/v3
|
module go.unistack.org/micro/v3
|
||||||
|
|
||||||
go 1.22
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
dario.cat/mergo v1.0.0
|
github.com/google/go-cmp v0.5.7 // indirect
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.0
|
github.com/imdario/mergo v0.3.13
|
||||||
github.com/KimMachineGun/automemlimit v0.6.1
|
github.com/kr/pretty v0.2.1 // indirect
|
||||||
github.com/google/uuid v1.3.0
|
github.com/kr/text v0.2.0 // indirect
|
||||||
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-20211117232152-9d50aa809f35
|
||||||
go.uber.org/automaxprocs v1.6.0
|
go.unistack.org/micro-proto/v3 v3.3.1
|
||||||
go.unistack.org/micro-proto/v3 v3.4.1
|
|
||||||
golang.org/x/sync v0.3.0
|
|
||||||
google.golang.org/grpc v1.57.0
|
|
||||||
google.golang.org/protobuf v1.33.0
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
|
||||||
github.com/cilium/ebpf v0.9.1 // indirect
|
|
||||||
github.com/containerd/cgroups/v3 v3.0.1 // indirect
|
|
||||||
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
|
||||||
github.com/docker/go-units v0.4.0 // indirect
|
|
||||||
github.com/godbus/dbus/v5 v5.0.4 // indirect
|
|
||||||
github.com/golang/protobuf v1.5.3 // indirect
|
|
||||||
github.com/opencontainers/runtime-spec v1.0.2 // indirect
|
|
||||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
|
||||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
|
||||||
golang.org/x/net v0.14.0 // indirect
|
|
||||||
golang.org/x/sys v0.11.0 // indirect
|
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e // indirect
|
|
||||||
)
|
)
|
||||||
|
214
go.sum
214
go.sum
@ -1,78 +1,160 @@
|
|||||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
github.com/KimMachineGun/automemlimit v0.6.1 h1:ILa9j1onAAMadBsyyUJv5cack8Y1WT26yLj/V+ulKp8=
|
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||||
github.com/KimMachineGun/automemlimit v0.6.1/go.mod h1:T7xYht7B8r6AG/AqFcUdc7fzd2bIdBKmepfP2S1svPY=
|
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||||
github.com/cilium/ebpf v0.9.1 h1:64sn2K3UKw8NbP/blsixRpF3nXuyhz/VjRlRzvlBRu4=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/cilium/ebpf v0.9.1/go.mod h1:+OhNOIXx/Fnu1IE8bJz2dzOA+VSfyTfdNUVdlQnxUFY=
|
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||||
github.com/containerd/cgroups/v3 v3.0.1 h1:4hfGvu8rfGIwVIDd+nLzn/B9ZXx4BcCjzt5ToenJRaE=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/containerd/cgroups/v3 v3.0.1/go.mod h1:/vtwk1VXrtoa5AaZLkypuOJgA/6DyPMZHJPGQNtlHnw=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
|
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
|
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||||
github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA=
|
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
||||||
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
|
github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0=
|
||||||
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
|
github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0=
|
||||||
|
github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E=
|
||||||
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
|
||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
|
||||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
|
||||||
|
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||||
|
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||||
|
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.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/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0=
|
|
||||||
github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0=
|
|
||||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||||
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
|
github.com/silas/dag v0.0.0-20211117232152-9d50aa809f35 h1:4mohWoM/UGg1BvFFiqSPRl5uwJY3rVV0HQX0ETqauqQ=
|
||||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
github.com/silas/dag v0.0.0-20211117232152-9d50aa809f35/go.mod h1:7RTUFBdIRC9nZ7/3RyRNH1bdqIShrDejd1YbLwgPS+I=
|
||||||
github.com/silas/dag v0.0.0-20220518035006-a7e85ada93c5 h1:G/FZtUu7a6NTWl3KUHMV9jkLAh/Rvtf03NWMHaEDl+E=
|
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
github.com/silas/dag v0.0.0-20220518035006-a7e85ada93c5/go.mod h1:7RTUFBdIRC9nZ7/3RyRNH1bdqIShrDejd1YbLwgPS+I=
|
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
|
||||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||||
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
|
go.unistack.org/micro-proto/v3 v3.3.1 h1:nQ0MtWvP2G3QrpOgawVOPhpZZYkq6umTGDqs8FxJYIo=
|
||||||
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
go.unistack.org/micro-proto/v3 v3.3.1/go.mod h1:cwRyv8uInM2I7EbU7O8Fx2Ls3N90Uw9UCCcq4olOdfE=
|
||||||
go.unistack.org/micro-proto/v3 v3.4.1 h1:UTjLSRz2YZuaHk9iSlVqqsA50JQNAEK2ZFboGqtEa9Q=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
go.unistack.org/micro-proto/v3 v3.4.1/go.mod h1:okx/cnOhzuCX0ggl/vToatbCupi0O44diiiLLsZ93Zo=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
|
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e h1:NumxXLPfHSndr3wBBdeKiVHjGVFzi9RX2HwwQke94iY=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
|
google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||||
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
|
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||||
|
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||||
|
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||||
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
|
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||||
|
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
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/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
@ -4,17 +4,6 @@ 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
|
|
||||||
}
|
|
||||||
if l, ok := ctx.Value(loggerKey{}).(Logger); ok && l != nil {
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
return DefaultLogger
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 {
|
||||||
|
230
logger/default.go
Normal file
230
logger/default.go
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type defaultLogger struct {
|
||||||
|
enc *json.Encoder
|
||||||
|
opts Options
|
||||||
|
sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init(opts...) should only overwrite provided options
|
||||||
|
func (l *defaultLogger) Init(opts ...Option) error {
|
||||||
|
l.Lock()
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&l.opts)
|
||||||
|
}
|
||||||
|
l.enc = json.NewEncoder(l.opts.Out)
|
||||||
|
// wrap the Log func
|
||||||
|
l.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *defaultLogger) String() string {
|
||||||
|
return "micro"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *defaultLogger) Clone(opts ...Option) Logger {
|
||||||
|
newopts := NewOptions(opts...)
|
||||||
|
oldopts := l.opts
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&newopts)
|
||||||
|
o(&oldopts)
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Lock()
|
||||||
|
cl := &defaultLogger{opts: oldopts, enc: json.NewEncoder(l.opts.Out)}
|
||||||
|
l.Unlock()
|
||||||
|
|
||||||
|
return cl
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *defaultLogger) V(level Level) bool {
|
||||||
|
l.RLock()
|
||||||
|
ok := l.opts.Level.Enabled(level)
|
||||||
|
l.RUnlock()
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *defaultLogger) Level(level Level) {
|
||||||
|
l.Lock()
|
||||||
|
l.opts.Level = level
|
||||||
|
l.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *defaultLogger) Fields(fields ...interface{}) Logger {
|
||||||
|
l.RLock()
|
||||||
|
nl := &defaultLogger{opts: l.opts, enc: l.enc}
|
||||||
|
if len(fields) == 0 {
|
||||||
|
l.RUnlock()
|
||||||
|
return nl
|
||||||
|
} else if len(fields)%2 != 0 {
|
||||||
|
fields = fields[:len(fields)-1]
|
||||||
|
}
|
||||||
|
nl.opts.Fields = copyFields(l.opts.Fields)
|
||||||
|
nl.opts.Fields = append(nl.opts.Fields, fields...)
|
||||||
|
l.RUnlock()
|
||||||
|
return nl
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyFields(src []interface{}) []interface{} {
|
||||||
|
dst := make([]interface{}, len(src))
|
||||||
|
copy(dst, src)
|
||||||
|
return dst
|
||||||
|
}
|
||||||
|
|
||||||
|
// logCallerfilePath returns a package/file:line description of the caller,
|
||||||
|
// preserving only the leaf directory name and file name.
|
||||||
|
func logCallerfilePath(loggingFilePath string) string {
|
||||||
|
// To make sure we trim the path correctly on Windows too, we
|
||||||
|
// counter-intuitively need to use '/' and *not* os.PathSeparator here,
|
||||||
|
// because the path given originates from Go stdlib, specifically
|
||||||
|
// runtime.Caller() which (as of Mar/17) returns forward slashes even on
|
||||||
|
// Windows.
|
||||||
|
//
|
||||||
|
// See https://github.com/golang/go/issues/3335
|
||||||
|
// and https://github.com/golang/go/issues/18151
|
||||||
|
//
|
||||||
|
// for discussion on the issue on Go side.
|
||||||
|
idx := strings.LastIndexByte(loggingFilePath, '/')
|
||||||
|
if idx == -1 {
|
||||||
|
return loggingFilePath
|
||||||
|
}
|
||||||
|
idx = strings.LastIndexByte(loggingFilePath[:idx], '/')
|
||||||
|
if idx == -1 {
|
||||||
|
return loggingFilePath
|
||||||
|
}
|
||||||
|
return loggingFilePath[idx+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *defaultLogger) Info(ctx context.Context, args ...interface{}) {
|
||||||
|
l.Log(ctx, InfoLevel, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *defaultLogger) Error(ctx context.Context, args ...interface{}) {
|
||||||
|
l.Log(ctx, ErrorLevel, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *defaultLogger) Debug(ctx context.Context, args ...interface{}) {
|
||||||
|
l.Log(ctx, DebugLevel, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *defaultLogger) Warn(ctx context.Context, args ...interface{}) {
|
||||||
|
l.Log(ctx, WarnLevel, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *defaultLogger) Trace(ctx context.Context, args ...interface{}) {
|
||||||
|
l.Log(ctx, TraceLevel, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *defaultLogger) Fatal(ctx context.Context, args ...interface{}) {
|
||||||
|
l.Log(ctx, FatalLevel, args...)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *defaultLogger) Infof(ctx context.Context, msg string, args ...interface{}) {
|
||||||
|
l.Logf(ctx, InfoLevel, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *defaultLogger) Errorf(ctx context.Context, msg string, args ...interface{}) {
|
||||||
|
l.Logf(ctx, ErrorLevel, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *defaultLogger) Debugf(ctx context.Context, msg string, args ...interface{}) {
|
||||||
|
l.Logf(ctx, DebugLevel, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *defaultLogger) Warnf(ctx context.Context, msg string, args ...interface{}) {
|
||||||
|
l.Logf(ctx, WarnLevel, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *defaultLogger) Tracef(ctx context.Context, msg string, args ...interface{}) {
|
||||||
|
l.Logf(ctx, TraceLevel, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *defaultLogger) Fatalf(ctx context.Context, msg string, args ...interface{}) {
|
||||||
|
l.Logf(ctx, FatalLevel, msg, args...)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *defaultLogger) Log(ctx context.Context, level Level, args ...interface{}) {
|
||||||
|
if !l.V(level) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l.RLock()
|
||||||
|
fields := copyFields(l.opts.Fields)
|
||||||
|
l.RUnlock()
|
||||||
|
|
||||||
|
fields = append(fields, "level", level.String())
|
||||||
|
|
||||||
|
if _, file, line, ok := runtime.Caller(l.opts.CallerSkipCount); ok {
|
||||||
|
fields = append(fields, "caller", fmt.Sprintf("%s:%d", logCallerfilePath(file), line))
|
||||||
|
}
|
||||||
|
fields = append(fields, "timestamp", time.Now().Format("2006-01-02 15:04:05"))
|
||||||
|
|
||||||
|
if len(args) > 0 {
|
||||||
|
fields = append(fields, "msg", fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
|
||||||
|
out := make(map[string]interface{}, len(fields)/2)
|
||||||
|
for i := 0; i < len(fields); i += 2 {
|
||||||
|
out[fields[i].(string)] = fields[i+1]
|
||||||
|
}
|
||||||
|
l.RLock()
|
||||||
|
_ = l.enc.Encode(out)
|
||||||
|
l.RUnlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *defaultLogger) Logf(ctx context.Context, level Level, msg string, args ...interface{}) {
|
||||||
|
if !l.V(level) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l.RLock()
|
||||||
|
fields := copyFields(l.opts.Fields)
|
||||||
|
l.RUnlock()
|
||||||
|
|
||||||
|
fields = append(fields, "level", level.String())
|
||||||
|
|
||||||
|
if _, file, line, ok := runtime.Caller(l.opts.CallerSkipCount); ok {
|
||||||
|
fields = append(fields, "caller", fmt.Sprintf("%s:%d", logCallerfilePath(file), line))
|
||||||
|
}
|
||||||
|
|
||||||
|
fields = append(fields, "timestamp", time.Now().Format("2006-01-02 15:04:05"))
|
||||||
|
if len(args) > 0 {
|
||||||
|
fields = append(fields, "msg", fmt.Sprintf(msg, args...))
|
||||||
|
} else if msg != "" {
|
||||||
|
fields = append(fields, "msg", msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
out := make(map[string]interface{}, len(fields)/2)
|
||||||
|
for i := 0; i < len(fields); i += 2 {
|
||||||
|
out[fields[i].(string)] = fields[i+1]
|
||||||
|
}
|
||||||
|
l.RLock()
|
||||||
|
_ = l.enc.Encode(out)
|
||||||
|
l.RUnlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *defaultLogger) Options() Options {
|
||||||
|
return l.opts
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLogger builds a new logger based on options
|
||||||
|
func NewLogger(opts ...Option) Logger {
|
||||||
|
l := &defaultLogger{
|
||||||
|
opts: NewOptions(opts...),
|
||||||
|
}
|
||||||
|
l.enc = json.NewEncoder(l.opts.Out)
|
||||||
|
return l
|
||||||
|
}
|
118
logger/logger.go
118
logger/logger.go
@ -1,19 +1,18 @@
|
|||||||
// Package logger provides a log interface
|
// Package logger provides a log interface
|
||||||
package logger
|
package logger // import "go.unistack.org/micro/v3/logger"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ContextAttrFunc func(ctx context.Context) []interface{}
|
|
||||||
|
|
||||||
var DefaultContextAttrFuncs []ContextAttrFunc
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// DefaultLogger variable
|
// DefaultLogger variable
|
||||||
DefaultLogger Logger = NewLogger()
|
DefaultLogger = NewLogger(WithLevel(ParseLevel(os.Getenv("MICRO_LOG_LEVEL"))))
|
||||||
// DefaultLevel used by logger
|
// DefaultLevel used by logger
|
||||||
DefaultLevel = InfoLevel
|
DefaultLevel = InfoLevel
|
||||||
|
// DefaultCallerSkipCount used by logger
|
||||||
|
DefaultCallerSkipCount = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
// Logger is a generic logging interface
|
// Logger is a generic logging interface
|
||||||
@ -31,24 +30,111 @@ type Logger interface {
|
|||||||
// Fields set fields to always be logged with keyval pairs
|
// Fields set fields to always be logged with keyval pairs
|
||||||
Fields(fields ...interface{}) Logger
|
Fields(fields ...interface{}) Logger
|
||||||
// Info level message
|
// Info level message
|
||||||
Info(ctx context.Context, msg string, args ...interface{})
|
Info(ctx context.Context, args ...interface{})
|
||||||
// Trace level message
|
// Trace level message
|
||||||
Trace(ctx context.Context, msg string, args ...interface{})
|
Trace(ctx context.Context, args ...interface{})
|
||||||
// Debug level message
|
// Debug level message
|
||||||
Debug(ctx context.Context, msg string, args ...interface{})
|
Debug(ctx context.Context, args ...interface{})
|
||||||
// Warn level message
|
// Warn level message
|
||||||
Warn(ctx context.Context, msg string, args ...interface{})
|
Warn(ctx context.Context, args ...interface{})
|
||||||
// Error level message
|
// Error level message
|
||||||
Error(ctx context.Context, msg string, args ...interface{})
|
Error(ctx context.Context, args ...interface{})
|
||||||
// Fatal level message
|
// Fatal level message
|
||||||
Fatal(ctx context.Context, msg string, args ...interface{})
|
Fatal(ctx context.Context, args ...interface{})
|
||||||
|
// Infof level message
|
||||||
|
Infof(ctx context.Context, msg string, args ...interface{})
|
||||||
|
// Tracef level message
|
||||||
|
Tracef(ctx context.Context, msg string, args ...interface{})
|
||||||
|
// Debug level message
|
||||||
|
Debugf(ctx context.Context, msg string, args ...interface{})
|
||||||
|
// Warn level message
|
||||||
|
Warnf(ctx context.Context, msg string, args ...interface{})
|
||||||
|
// Error level message
|
||||||
|
Errorf(ctx context.Context, msg string, args ...interface{})
|
||||||
|
// Fatal level message
|
||||||
|
Fatalf(ctx context.Context, msg string, args ...interface{})
|
||||||
// Log logs message with needed level
|
// Log logs message with needed level
|
||||||
Log(ctx context.Context, level Level, msg string, args ...interface{})
|
Log(ctx context.Context, level Level, args ...interface{})
|
||||||
// Name returns broker instance name
|
// Logf logs message with needed level
|
||||||
Name() string
|
Logf(ctx context.Context, level Level, msg string, args ...interface{})
|
||||||
// String returns the type of logger
|
// String returns the name of logger
|
||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Field contains keyval pair
|
// Field contains keyval pair
|
||||||
type Field interface{}
|
type Field interface{}
|
||||||
|
|
||||||
|
// Info writes msg to default logger on info level
|
||||||
|
func Info(ctx context.Context, args ...interface{}) {
|
||||||
|
DefaultLogger.Info(ctx, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error writes msg to default logger on error level
|
||||||
|
func Error(ctx context.Context, args ...interface{}) {
|
||||||
|
DefaultLogger.Error(ctx, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug writes msg to default logger on debug level
|
||||||
|
func Debug(ctx context.Context, args ...interface{}) {
|
||||||
|
DefaultLogger.Debug(ctx, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn writes msg to default logger on warn level
|
||||||
|
func Warn(ctx context.Context, args ...interface{}) {
|
||||||
|
DefaultLogger.Warn(ctx, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trace writes msg to default logger on trace level
|
||||||
|
func Trace(ctx context.Context, args ...interface{}) {
|
||||||
|
DefaultLogger.Trace(ctx, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatal writes msg to default logger on fatal level
|
||||||
|
func Fatal(ctx context.Context, args ...interface{}) {
|
||||||
|
DefaultLogger.Fatal(ctx, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infof writes formatted msg to default logger on info level
|
||||||
|
func Infof(ctx context.Context, msg string, args ...interface{}) {
|
||||||
|
DefaultLogger.Infof(ctx, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errorf writes formatted msg to default logger on error level
|
||||||
|
func Errorf(ctx context.Context, msg string, args ...interface{}) {
|
||||||
|
DefaultLogger.Errorf(ctx, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debugf writes formatted msg to default logger on debug level
|
||||||
|
func Debugf(ctx context.Context, msg string, args ...interface{}) {
|
||||||
|
DefaultLogger.Debugf(ctx, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warnf writes formatted msg to default logger on warn level
|
||||||
|
func Warnf(ctx context.Context, msg string, args ...interface{}) {
|
||||||
|
DefaultLogger.Warnf(ctx, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tracef writes formatted msg to default logger on trace level
|
||||||
|
func Tracef(ctx context.Context, msg string, args ...interface{}) {
|
||||||
|
DefaultLogger.Tracef(ctx, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatalf writes formatted msg to default logger on fatal level
|
||||||
|
func Fatalf(ctx context.Context, msg string, args ...interface{}) {
|
||||||
|
DefaultLogger.Fatalf(ctx, msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// V returns true if passed level enabled in default logger
|
||||||
|
func V(level Level) bool {
|
||||||
|
return DefaultLogger.V(level)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init initialize logger
|
||||||
|
func Init(opts ...Option) error {
|
||||||
|
return DefaultLogger.Init(opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fields create logger with specific fields
|
||||||
|
func Fields(fields ...interface{}) Logger {
|
||||||
|
return DefaultLogger.Fields(fields...)
|
||||||
|
}
|
||||||
|
138
logger/logger_test.go
Normal file
138
logger/logger_test.go
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestContext(t *testing.T) {
|
||||||
|
ctx := context.TODO()
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
l := NewLogger(WithLevel(TraceLevel), WithOutput(buf))
|
||||||
|
if err := l.Init(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nl, ok := FromContext(NewContext(ctx, l.Fields("key", "val")))
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("context without logger")
|
||||||
|
}
|
||||||
|
nl.Info(ctx, "message")
|
||||||
|
if !bytes.Contains(buf.Bytes(), []byte(`"key":"val"`)) {
|
||||||
|
t.Fatalf("logger fields not works, buf contains: %s", buf.Bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFields(t *testing.T) {
|
||||||
|
ctx := context.TODO()
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
l := NewLogger(WithLevel(TraceLevel), WithOutput(buf))
|
||||||
|
if err := l.Init(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nl := l.Fields("key", "val")
|
||||||
|
|
||||||
|
nl.Info(ctx, "message")
|
||||||
|
if !bytes.Contains(buf.Bytes(), []byte(`"key":"val"`)) {
|
||||||
|
t.Fatalf("logger fields not works, buf contains: %s", buf.Bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromContextWithFields(t *testing.T) {
|
||||||
|
ctx := context.TODO()
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
var ok bool
|
||||||
|
l := NewLogger(WithLevel(TraceLevel), WithOutput(buf))
|
||||||
|
if err := l.Init(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
nl := l.Fields("key", "val")
|
||||||
|
|
||||||
|
ctx = NewContext(ctx, nl)
|
||||||
|
|
||||||
|
l, ok = FromContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("context does not have logger")
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Info(ctx, "message")
|
||||||
|
if !bytes.Contains(buf.Bytes(), []byte(`"key":"val"`)) {
|
||||||
|
t.Fatalf("logger fields not works, buf contains: %s", buf.Bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClone(t *testing.T) {
|
||||||
|
ctx := context.TODO()
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
l := NewLogger(WithLevel(TraceLevel), WithOutput(buf))
|
||||||
|
if err := l.Init(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
nl := l.Clone(WithLevel(ErrorLevel))
|
||||||
|
if err := nl.Init(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
nl.Info(ctx, "info message")
|
||||||
|
if len(buf.Bytes()) != 0 {
|
||||||
|
t.Fatal("message must not be logged")
|
||||||
|
}
|
||||||
|
l.Info(ctx, "info message")
|
||||||
|
if len(buf.Bytes()) == 0 {
|
||||||
|
t.Fatal("message must be logged")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRedirectStdLogger(t *testing.T) {
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
l := NewLogger(WithLevel(TraceLevel), WithOutput(buf))
|
||||||
|
if err := l.Init(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
fn := RedirectStdLogger(l, ErrorLevel)
|
||||||
|
defer fn()
|
||||||
|
log.Print("test")
|
||||||
|
if !bytes.Contains(buf.Bytes(), []byte(`"level":"error","msg":"test","timestamp"`)) {
|
||||||
|
t.Fatalf("logger error, buf %s", buf.Bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStdLogger(t *testing.T) {
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
l := NewLogger(WithLevel(TraceLevel), WithOutput(buf))
|
||||||
|
if err := l.Init(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
lg := NewStdLogger(l, ErrorLevel)
|
||||||
|
lg.Print("test")
|
||||||
|
if !bytes.Contains(buf.Bytes(), []byte(`"level":"error","msg":"test","timestamp"`)) {
|
||||||
|
t.Fatalf("logger error, buf %s", buf.Bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogger(t *testing.T) {
|
||||||
|
ctx := context.TODO()
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
l := NewLogger(WithLevel(TraceLevel), WithOutput(buf))
|
||||||
|
if err := l.Init(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
l.Trace(ctx, "trace_msg1")
|
||||||
|
l.Warn(ctx, "warn_msg1")
|
||||||
|
l.Fields("error", "test").Info(ctx, "error message")
|
||||||
|
l.Warn(ctx, "first", " ", "second")
|
||||||
|
if !bytes.Contains(buf.Bytes(), []byte(`"level":"trace","msg":"trace_msg1"`)) {
|
||||||
|
t.Fatalf("logger error, buf %s", buf.Bytes())
|
||||||
|
}
|
||||||
|
if !bytes.Contains(buf.Bytes(), []byte(`"warn","msg":"warn_msg1"`)) {
|
||||||
|
t.Fatalf("logger error, buf %s", buf.Bytes())
|
||||||
|
}
|
||||||
|
if !bytes.Contains(buf.Bytes(), []byte(`"error":"test","level":"info","msg":"error message"`)) {
|
||||||
|
t.Fatalf("logger error, buf %s", buf.Bytes())
|
||||||
|
}
|
||||||
|
if !bytes.Contains(buf.Bytes(), []byte(`"level":"warn","msg":"first second"`)) {
|
||||||
|
t.Fatalf("logger error, buf %s", buf.Bytes())
|
||||||
|
}
|
||||||
|
}
|
@ -1,78 +0,0 @@
|
|||||||
package logger
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
defaultCallerSkipCount = 2
|
|
||||||
)
|
|
||||||
|
|
||||||
type noopLogger struct {
|
|
||||||
opts Options
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLogger(opts ...Option) Logger {
|
|
||||||
options := NewOptions(opts...)
|
|
||||||
options.CallerSkipCount = defaultCallerSkipCount
|
|
||||||
return &noopLogger{opts: options}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *noopLogger) V(_ Level) bool {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *noopLogger) Level(_ Level) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *noopLogger) Name() string {
|
|
||||||
return l.opts.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *noopLogger) Init(opts ...Option) error {
|
|
||||||
for _, o := range opts {
|
|
||||||
o(&l.opts)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *noopLogger) Clone(opts ...Option) Logger {
|
|
||||||
nl := &noopLogger{opts: l.opts}
|
|
||||||
for _, o := range opts {
|
|
||||||
o(&nl.opts)
|
|
||||||
}
|
|
||||||
return nl
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *noopLogger) Fields(_ ...interface{}) Logger {
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *noopLogger) Options() Options {
|
|
||||||
return l.opts
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *noopLogger) String() string {
|
|
||||||
return "noop"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *noopLogger) Log(ctx context.Context, lvl Level, msg string, attrs ...interface{}) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *noopLogger) Info(ctx context.Context, msg string, attrs ...interface{}) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *noopLogger) Debug(ctx context.Context, msg string, attrs ...interface{}) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *noopLogger) Error(ctx context.Context, msg string, attrs ...interface{}) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *noopLogger) Trace(ctx context.Context, msg string, attrs ...interface{}) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *noopLogger) Warn(ctx context.Context, msg string, attrs ...interface{}) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *noopLogger) Fatal(ctx context.Context, msg string, attrs ...interface{}) {
|
|
||||||
}
|
|
@ -3,14 +3,10 @@ package logger
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
"log/slog"
|
|
||||||
"os"
|
"os"
|
||||||
"time"
|
|
||||||
|
|
||||||
"go.unistack.org/micro/v3/meter"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Option func signature
|
// Option func
|
||||||
type Option func(*Options)
|
type Option func(*Options)
|
||||||
|
|
||||||
// Options holds logger options
|
// Options holds logger options
|
||||||
@ -19,67 +15,31 @@ type Options struct {
|
|||||||
Out io.Writer
|
Out io.Writer
|
||||||
// Context holds exernal options
|
// Context holds exernal options
|
||||||
Context context.Context
|
Context context.Context
|
||||||
// Name holds the logger name
|
|
||||||
Name string
|
|
||||||
// Fields holds additional metadata
|
// Fields holds additional metadata
|
||||||
Fields []interface{}
|
Fields []interface{}
|
||||||
// callerSkipCount number of frmaes to skip
|
// Name holds the logger name
|
||||||
CallerSkipCount int
|
Name string
|
||||||
// ContextAttrFuncs contains funcs that executed before log func on context
|
|
||||||
ContextAttrFuncs []ContextAttrFunc
|
|
||||||
// TimeKey is the key used for the time of the log call
|
|
||||||
TimeKey string
|
|
||||||
// LevelKey is the key used for the level of the log call
|
|
||||||
LevelKey string
|
|
||||||
// ErroreKey is the key used for the error of the log call
|
|
||||||
ErrorKey string
|
|
||||||
// MessageKey is the key used for the message of the log call
|
|
||||||
MessageKey string
|
|
||||||
// SourceKey is the key used for the source file and line of the log call
|
|
||||||
SourceKey string
|
|
||||||
// StacktraceKey is the key used for the stacktrace
|
|
||||||
StacktraceKey string
|
|
||||||
// AddStacktrace controls writing of stacktaces on error
|
|
||||||
AddStacktrace bool
|
|
||||||
// AddSource enabled writing source file and position in log
|
|
||||||
AddSource bool
|
|
||||||
// The logging level the logger should log
|
// The logging level the logger should log
|
||||||
Level Level
|
Level Level
|
||||||
// TimeFunc used to obtain current time
|
// CallerSkipCount number of frmaes to skip
|
||||||
TimeFunc func() time.Time
|
CallerSkipCount int
|
||||||
// Meter used to count logs for specific level
|
|
||||||
Meter meter.Meter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOptions creates new options struct
|
// NewOptions creates new options struct
|
||||||
func NewOptions(opts ...Option) Options {
|
func NewOptions(opts ...Option) Options {
|
||||||
options := Options{
|
options := Options{
|
||||||
Level: DefaultLevel,
|
Level: DefaultLevel,
|
||||||
Fields: make([]interface{}, 0, 6),
|
Fields: make([]interface{}, 0, 6),
|
||||||
Out: os.Stderr,
|
Out: os.Stderr,
|
||||||
Context: context.Background(),
|
CallerSkipCount: DefaultCallerSkipCount,
|
||||||
ContextAttrFuncs: DefaultContextAttrFuncs,
|
Context: context.Background(),
|
||||||
AddSource: true,
|
|
||||||
TimeFunc: time.Now,
|
|
||||||
Meter: meter.DefaultMeter,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WithMicroKeys()(&options)
|
|
||||||
|
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&options)
|
o(&options)
|
||||||
}
|
}
|
||||||
|
|
||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithContextAttrFuncs appends default funcs for the context attrs filler
|
|
||||||
func WithContextAttrFuncs(fncs ...ContextAttrFunc) Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.ContextAttrFuncs = append(o.ContextAttrFuncs, fncs...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithFields set default fields for the logger
|
// WithFields set default fields for the logger
|
||||||
func WithFields(fields ...interface{}) Option {
|
func WithFields(fields ...interface{}) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
@ -101,17 +61,10 @@ func WithOutput(out io.Writer) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithAddStacktrace controls writing stacktrace on error
|
// WithCallerSkipCount set frame count to skip
|
||||||
func WithAddStacktrace(v bool) Option {
|
func WithCallerSkipCount(c int) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.AddStacktrace = v
|
o.CallerSkipCount = c
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithAddSource controls writing source file and pos in log
|
|
||||||
func WithAddSource(v bool) Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.AddSource = v
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,70 +81,3 @@ func WithName(n string) Option {
|
|||||||
o.Name = n
|
o.Name = n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithMeter sets the meter
|
|
||||||
func WithMeter(m meter.Meter) Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.Meter = m
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithTimeFunc sets the func to obtain current time
|
|
||||||
func WithTimeFunc(fn func() time.Time) Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.TimeFunc = fn
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithZapKeys() Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.TimeKey = "@timestamp"
|
|
||||||
o.LevelKey = "level"
|
|
||||||
o.MessageKey = "msg"
|
|
||||||
o.SourceKey = "caller"
|
|
||||||
o.StacktraceKey = "stacktrace"
|
|
||||||
o.ErrorKey = "error"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithZerologKeys() Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.TimeKey = "time"
|
|
||||||
o.LevelKey = "level"
|
|
||||||
o.MessageKey = "message"
|
|
||||||
o.SourceKey = "caller"
|
|
||||||
o.StacktraceKey = "stacktrace"
|
|
||||||
o.ErrorKey = "error"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithSlogKeys() Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.TimeKey = slog.TimeKey
|
|
||||||
o.LevelKey = slog.LevelKey
|
|
||||||
o.MessageKey = slog.MessageKey
|
|
||||||
o.SourceKey = slog.SourceKey
|
|
||||||
o.StacktraceKey = "stacktrace"
|
|
||||||
o.ErrorKey = "error"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func WithMicroKeys() Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
o.TimeKey = "timestamp"
|
|
||||||
o.LevelKey = "level"
|
|
||||||
o.MessageKey = "msg"
|
|
||||||
o.SourceKey = "caller"
|
|
||||||
o.StacktraceKey = "stacktrace"
|
|
||||||
o.ErrorKey = "error"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithAddCallerSkipCount add skip count for copy logger
|
|
||||||
func WithAddCallerSkipCount(n int) Option {
|
|
||||||
return func(o *Options) {
|
|
||||||
if n > 0 {
|
|
||||||
o.CallerSkipCount += n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,332 +0,0 @@
|
|||||||
package slog
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"log/slog"
|
|
||||||
"os"
|
|
||||||
"regexp"
|
|
||||||
"runtime"
|
|
||||||
"strconv"
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
|
|
||||||
"go.unistack.org/micro/v3/logger"
|
|
||||||
"go.unistack.org/micro/v3/semconv"
|
|
||||||
"go.unistack.org/micro/v3/tracer"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
badKey = "!BADKEY"
|
|
||||||
// defaultCallerSkipCount used by logger
|
|
||||||
defaultCallerSkipCount = 3
|
|
||||||
)
|
|
||||||
|
|
||||||
var reTrace = regexp.MustCompile(`.*/slog/logger\.go.*\n`)
|
|
||||||
|
|
||||||
var (
|
|
||||||
traceValue = slog.StringValue("trace")
|
|
||||||
debugValue = slog.StringValue("debug")
|
|
||||||
infoValue = slog.StringValue("info")
|
|
||||||
warnValue = slog.StringValue("warn")
|
|
||||||
errorValue = slog.StringValue("error")
|
|
||||||
fatalValue = slog.StringValue("fatal")
|
|
||||||
)
|
|
||||||
|
|
||||||
type wrapper struct {
|
|
||||||
h slog.Handler
|
|
||||||
level atomic.Int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *wrapper) Enabled(ctx context.Context, level slog.Level) bool {
|
|
||||||
return level >= slog.Level(int(h.level.Load()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *wrapper) Handle(ctx context.Context, rec slog.Record) error {
|
|
||||||
return h.h.Handle(ctx, rec)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *wrapper) WithAttrs(attrs []slog.Attr) slog.Handler {
|
|
||||||
return h.WithAttrs(attrs)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *wrapper) WithGroup(name string) slog.Handler {
|
|
||||||
return h.WithGroup(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *slogLogger) renameAttr(_ []string, a slog.Attr) slog.Attr {
|
|
||||||
switch a.Key {
|
|
||||||
case slog.SourceKey:
|
|
||||||
source := a.Value.Any().(*slog.Source)
|
|
||||||
a.Value = slog.StringValue(source.File + ":" + strconv.Itoa(source.Line))
|
|
||||||
a.Key = s.opts.SourceKey
|
|
||||||
case slog.TimeKey:
|
|
||||||
a.Key = s.opts.TimeKey
|
|
||||||
case slog.MessageKey:
|
|
||||||
a.Key = s.opts.MessageKey
|
|
||||||
case slog.LevelKey:
|
|
||||||
level := a.Value.Any().(slog.Level)
|
|
||||||
lvl := slogToLoggerLevel(level)
|
|
||||||
a.Key = s.opts.LevelKey
|
|
||||||
switch {
|
|
||||||
case lvl < logger.DebugLevel:
|
|
||||||
a.Value = traceValue
|
|
||||||
case lvl < logger.InfoLevel:
|
|
||||||
a.Value = debugValue
|
|
||||||
case lvl < logger.WarnLevel:
|
|
||||||
a.Value = infoValue
|
|
||||||
case lvl < logger.ErrorLevel:
|
|
||||||
a.Value = warnValue
|
|
||||||
case lvl < logger.FatalLevel:
|
|
||||||
a.Value = errorValue
|
|
||||||
case lvl >= logger.FatalLevel:
|
|
||||||
a.Value = fatalValue
|
|
||||||
default:
|
|
||||||
a.Value = infoValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
type slogLogger struct {
|
|
||||||
leveler *slog.LevelVar
|
|
||||||
handler *wrapper
|
|
||||||
opts logger.Options
|
|
||||||
mu sync.RWMutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *slogLogger) Clone(opts ...logger.Option) logger.Logger {
|
|
||||||
s.mu.RLock()
|
|
||||||
options := s.opts
|
|
||||||
s.mu.RUnlock()
|
|
||||||
|
|
||||||
for _, o := range opts {
|
|
||||||
o(&options)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(options.ContextAttrFuncs) == 0 {
|
|
||||||
options.ContextAttrFuncs = logger.DefaultContextAttrFuncs
|
|
||||||
}
|
|
||||||
|
|
||||||
attrs, _ := s.argsAttrs(options.Fields)
|
|
||||||
l := &slogLogger{
|
|
||||||
handler: &wrapper{h: s.handler.h.WithAttrs(attrs)},
|
|
||||||
opts: options,
|
|
||||||
}
|
|
||||||
l.handler.level.Store(int64(loggerToSlogLevel(options.Level)))
|
|
||||||
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *slogLogger) V(level logger.Level) bool {
|
|
||||||
s.mu.Lock()
|
|
||||||
v := s.opts.Level.Enabled(level)
|
|
||||||
s.mu.Unlock()
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *slogLogger) Level(level logger.Level) {
|
|
||||||
s.mu.Lock()
|
|
||||||
s.opts.Level = level
|
|
||||||
s.handler.level.Store(int64(loggerToSlogLevel(level)))
|
|
||||||
s.mu.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *slogLogger) Options() logger.Options {
|
|
||||||
return s.opts
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *slogLogger) Fields(fields ...interface{}) logger.Logger {
|
|
||||||
s.mu.RLock()
|
|
||||||
options := s.opts
|
|
||||||
s.mu.RUnlock()
|
|
||||||
|
|
||||||
l := &slogLogger{opts: options}
|
|
||||||
|
|
||||||
if len(options.ContextAttrFuncs) == 0 {
|
|
||||||
options.ContextAttrFuncs = logger.DefaultContextAttrFuncs
|
|
||||||
}
|
|
||||||
|
|
||||||
attrs, _ := s.argsAttrs(fields)
|
|
||||||
l.handler = &wrapper{h: s.handler.h.WithAttrs(attrs)}
|
|
||||||
l.handler.level.Store(int64(loggerToSlogLevel(l.opts.Level)))
|
|
||||||
|
|
||||||
return l
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *slogLogger) Init(opts ...logger.Option) error {
|
|
||||||
s.mu.Lock()
|
|
||||||
|
|
||||||
for _, o := range opts {
|
|
||||||
o(&s.opts)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(s.opts.ContextAttrFuncs) == 0 {
|
|
||||||
s.opts.ContextAttrFuncs = logger.DefaultContextAttrFuncs
|
|
||||||
}
|
|
||||||
|
|
||||||
handleOpt := &slog.HandlerOptions{
|
|
||||||
ReplaceAttr: s.renameAttr,
|
|
||||||
Level: loggerToSlogLevel(logger.TraceLevel),
|
|
||||||
AddSource: s.opts.AddSource,
|
|
||||||
}
|
|
||||||
|
|
||||||
attrs, _ := s.argsAttrs(s.opts.Fields)
|
|
||||||
s.handler = &wrapper{h: slog.NewJSONHandler(s.opts.Out, handleOpt).WithAttrs(attrs)}
|
|
||||||
s.handler.level.Store(int64(loggerToSlogLevel(s.opts.Level)))
|
|
||||||
s.mu.Unlock()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *slogLogger) Log(ctx context.Context, lvl logger.Level, msg string, attrs ...interface{}) {
|
|
||||||
s.printLog(ctx, lvl, msg, attrs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *slogLogger) Info(ctx context.Context, msg string, attrs ...interface{}) {
|
|
||||||
s.printLog(ctx, logger.InfoLevel, msg, attrs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *slogLogger) Debug(ctx context.Context, msg string, attrs ...interface{}) {
|
|
||||||
s.printLog(ctx, logger.DebugLevel, msg, attrs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *slogLogger) Trace(ctx context.Context, msg string, attrs ...interface{}) {
|
|
||||||
s.printLog(ctx, logger.TraceLevel, msg, attrs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *slogLogger) Error(ctx context.Context, msg string, attrs ...interface{}) {
|
|
||||||
s.printLog(ctx, logger.ErrorLevel, msg, attrs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *slogLogger) Fatal(ctx context.Context, msg string, attrs ...interface{}) {
|
|
||||||
s.printLog(ctx, logger.FatalLevel, msg, attrs...)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *slogLogger) Warn(ctx context.Context, msg string, attrs ...interface{}) {
|
|
||||||
s.printLog(ctx, logger.WarnLevel, msg, attrs...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *slogLogger) Name() string {
|
|
||||||
return s.opts.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *slogLogger) String() string {
|
|
||||||
return "slog"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *slogLogger) printLog(ctx context.Context, lvl logger.Level, msg string, args ...interface{}) {
|
|
||||||
if !s.V(lvl) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var argError error
|
|
||||||
|
|
||||||
s.opts.Meter.Counter(semconv.LoggerMessageTotal, "level", lvl.String()).Inc()
|
|
||||||
|
|
||||||
attrs, err := s.argsAttrs(args)
|
|
||||||
if err != nil {
|
|
||||||
argError = err
|
|
||||||
}
|
|
||||||
if argError != nil {
|
|
||||||
if span, ok := tracer.SpanFromContext(ctx); ok {
|
|
||||||
span.SetStatus(tracer.SpanStatusError, argError.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, fn := range s.opts.ContextAttrFuncs {
|
|
||||||
ctxAttrs, err := s.argsAttrs(fn(ctx))
|
|
||||||
if err != nil {
|
|
||||||
argError = err
|
|
||||||
}
|
|
||||||
attrs = append(attrs, ctxAttrs...)
|
|
||||||
}
|
|
||||||
if argError != nil {
|
|
||||||
if span, ok := tracer.SpanFromContext(ctx); ok {
|
|
||||||
span.SetStatus(tracer.SpanStatusError, argError.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.opts.AddStacktrace && lvl == logger.ErrorLevel {
|
|
||||||
stackInfo := make([]byte, 1024*1024)
|
|
||||||
if stackSize := runtime.Stack(stackInfo, false); stackSize > 0 {
|
|
||||||
traceLines := reTrace.Split(string(stackInfo[:stackSize]), -1)
|
|
||||||
if len(traceLines) != 0 {
|
|
||||||
attrs = append(attrs, slog.String(s.opts.StacktraceKey, traceLines[len(traceLines)-1]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var pcs [1]uintptr
|
|
||||||
runtime.Callers(s.opts.CallerSkipCount, pcs[:]) // skip [Callers, printLog, LogLvlMethod]
|
|
||||||
r := slog.NewRecord(s.opts.TimeFunc(), loggerToSlogLevel(lvl), msg, pcs[0])
|
|
||||||
r.AddAttrs(attrs...)
|
|
||||||
_ = s.handler.Handle(ctx, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewLogger(opts ...logger.Option) logger.Logger {
|
|
||||||
s := &slogLogger{
|
|
||||||
opts: logger.NewOptions(opts...),
|
|
||||||
}
|
|
||||||
s.opts.CallerSkipCount = defaultCallerSkipCount
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func loggerToSlogLevel(level logger.Level) slog.Level {
|
|
||||||
switch level {
|
|
||||||
case logger.DebugLevel:
|
|
||||||
return slog.LevelDebug
|
|
||||||
case logger.WarnLevel:
|
|
||||||
return slog.LevelWarn
|
|
||||||
case logger.ErrorLevel:
|
|
||||||
return slog.LevelError
|
|
||||||
case logger.TraceLevel:
|
|
||||||
return slog.LevelDebug - 1
|
|
||||||
case logger.FatalLevel:
|
|
||||||
return slog.LevelError + 1
|
|
||||||
default:
|
|
||||||
return slog.LevelInfo
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func slogToLoggerLevel(level slog.Level) logger.Level {
|
|
||||||
switch level {
|
|
||||||
case slog.LevelDebug:
|
|
||||||
return logger.DebugLevel
|
|
||||||
case slog.LevelWarn:
|
|
||||||
return logger.WarnLevel
|
|
||||||
case slog.LevelError:
|
|
||||||
return logger.ErrorLevel
|
|
||||||
case slog.LevelDebug - 1:
|
|
||||||
return logger.TraceLevel
|
|
||||||
case slog.LevelError + 1:
|
|
||||||
return logger.FatalLevel
|
|
||||||
default:
|
|
||||||
return logger.InfoLevel
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *slogLogger) argsAttrs(args []interface{}) ([]slog.Attr, error) {
|
|
||||||
attrs := make([]slog.Attr, 0, len(args))
|
|
||||||
var err error
|
|
||||||
|
|
||||||
for idx := 0; idx < len(args); idx++ {
|
|
||||||
switch arg := args[idx].(type) {
|
|
||||||
case slog.Attr:
|
|
||||||
attrs = append(attrs, arg)
|
|
||||||
case string:
|
|
||||||
if idx+1 < len(args) {
|
|
||||||
attrs = append(attrs, slog.Any(arg, args[idx+1]))
|
|
||||||
idx += 1
|
|
||||||
} else {
|
|
||||||
attrs = append(attrs, slog.String(badKey, arg))
|
|
||||||
}
|
|
||||||
case error:
|
|
||||||
attrs = append(attrs, slog.String(s.opts.ErrorKey, arg.Error()))
|
|
||||||
err = arg
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return attrs, err
|
|
||||||
}
|
|
@ -1,294 +0,0 @@
|
|||||||
package slog
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"go.unistack.org/micro/v3/metadata"
|
|
||||||
|
|
||||||
"go.unistack.org/micro/v3/logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestMultipleFieldsWithLevel(t *testing.T) {
|
|
||||||
ctx := context.TODO()
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
l := NewLogger(logger.WithLevel(logger.InfoLevel), logger.WithOutput(buf))
|
|
||||||
if err := l.Init(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l = l.Fields("key", "val")
|
|
||||||
|
|
||||||
l.Info(ctx, "msg1")
|
|
||||||
nl := l.Clone(logger.WithLevel(logger.DebugLevel))
|
|
||||||
nl.Debug(ctx, "msg2")
|
|
||||||
l.Debug(ctx, "msg3")
|
|
||||||
if !bytes.Contains(buf.Bytes(), []byte(`"key":"val"`)) {
|
|
||||||
t.Fatalf("logger error not works, buf contains: %s", buf.Bytes())
|
|
||||||
}
|
|
||||||
if !bytes.Contains(buf.Bytes(), []byte(`"msg1"`)) {
|
|
||||||
t.Fatalf("logger error not works, buf contains: %s", buf.Bytes())
|
|
||||||
}
|
|
||||||
if !bytes.Contains(buf.Bytes(), []byte(`"msg2"`)) {
|
|
||||||
t.Fatalf("logger error not works, buf contains: %s", buf.Bytes())
|
|
||||||
}
|
|
||||||
if bytes.Contains(buf.Bytes(), []byte(`"msg3"`)) {
|
|
||||||
t.Fatalf("logger error not works, buf contains: %s", buf.Bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMultipleFields(t *testing.T) {
|
|
||||||
ctx := context.TODO()
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
l := NewLogger(logger.WithLevel(logger.InfoLevel), logger.WithOutput(buf))
|
|
||||||
if err := l.Init(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l = l.Fields("key", "val")
|
|
||||||
|
|
||||||
l = l.Fields("key1", "val1")
|
|
||||||
|
|
||||||
l.Info(ctx, "msg")
|
|
||||||
|
|
||||||
if !bytes.Contains(buf.Bytes(), []byte(`"key":"val"`)) {
|
|
||||||
t.Fatalf("logger error not works, buf contains: %s", buf.Bytes())
|
|
||||||
}
|
|
||||||
if !bytes.Contains(buf.Bytes(), []byte(`"key1":"val1"`)) {
|
|
||||||
t.Fatalf("logger error not works, buf contains: %s", buf.Bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestError(t *testing.T) {
|
|
||||||
ctx := context.TODO()
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
l := NewLogger(logger.WithLevel(logger.ErrorLevel), logger.WithOutput(buf), logger.WithAddStacktrace(true))
|
|
||||||
if err := l.Init(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Error(ctx, "message", fmt.Errorf("error message"))
|
|
||||||
if !bytes.Contains(buf.Bytes(), []byte(`"stacktrace":"`)) {
|
|
||||||
t.Fatalf("logger stacktrace not works, buf contains: %s", buf.Bytes())
|
|
||||||
}
|
|
||||||
if !bytes.Contains(buf.Bytes(), []byte(`"error":"`)) {
|
|
||||||
t.Fatalf("logger error not works, buf contains: %s", buf.Bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestErrorf(t *testing.T) {
|
|
||||||
ctx := context.TODO()
|
|
||||||
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
l := NewLogger(logger.WithLevel(logger.ErrorLevel), logger.WithOutput(buf), logger.WithAddStacktrace(true))
|
|
||||||
if err := l.Init(logger.WithContextAttrFuncs(func(ctx context.Context) []interface{} {
|
|
||||||
return nil
|
|
||||||
})); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Log(ctx, logger.ErrorLevel, "message", errors.New("error msg"))
|
|
||||||
|
|
||||||
l.Log(ctx, logger.ErrorLevel, "", errors.New("error msg"))
|
|
||||||
if !bytes.Contains(buf.Bytes(), []byte(`"error":"error msg"`)) {
|
|
||||||
t.Fatalf("logger error not works, buf contains: %s", buf.Bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
if !bytes.Contains(buf.Bytes(), []byte(`"stacktrace":"`)) {
|
|
||||||
t.Fatalf("logger stacktrace not works, buf contains: %s", buf.Bytes())
|
|
||||||
}
|
|
||||||
if !bytes.Contains(buf.Bytes(), []byte(`"error":"`)) {
|
|
||||||
t.Fatalf("logger error not works, buf contains: %s", buf.Bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestContext(t *testing.T) {
|
|
||||||
ctx := context.TODO()
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
l := NewLogger(logger.WithLevel(logger.TraceLevel), logger.WithOutput(buf))
|
|
||||||
if err := l.Init(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
nl, ok := logger.FromContext(logger.NewContext(ctx, l.Fields("key", "val")))
|
|
||||||
if !ok {
|
|
||||||
t.Fatal("context without logger")
|
|
||||||
}
|
|
||||||
nl.Info(ctx, "message")
|
|
||||||
if !bytes.Contains(buf.Bytes(), []byte(`"key":"val"`)) {
|
|
||||||
t.Fatalf("logger fields not works, buf contains: %s", buf.Bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFields(t *testing.T) {
|
|
||||||
ctx := context.TODO()
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
l := NewLogger(logger.WithLevel(logger.TraceLevel), logger.WithOutput(buf))
|
|
||||||
if err := l.Init(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
nl := l.Fields("key", "val")
|
|
||||||
|
|
||||||
nl.Info(ctx, "message")
|
|
||||||
if !bytes.Contains(buf.Bytes(), []byte(`"key":"val"`)) {
|
|
||||||
t.Fatalf("logger fields not works, buf contains: %s", buf.Bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFromContextWithFields(t *testing.T) {
|
|
||||||
ctx := context.TODO()
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
var ok bool
|
|
||||||
l := NewLogger(logger.WithLevel(logger.TraceLevel), logger.WithOutput(buf))
|
|
||||||
if err := l.Init(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
nl := l.Fields("key", "val")
|
|
||||||
|
|
||||||
ctx = logger.NewContext(ctx, nl)
|
|
||||||
|
|
||||||
l, ok = logger.FromContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
t.Fatalf("context does not have logger")
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Info(ctx, "message")
|
|
||||||
if !bytes.Contains(buf.Bytes(), []byte(`"key":"val"`)) {
|
|
||||||
t.Fatalf("logger fields not works, buf contains: %s", buf.Bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Info(ctx, "test", "uncorrected number attributes")
|
|
||||||
if !bytes.Contains(buf.Bytes(), []byte(`"!BADKEY":"uncorrected number attributes"`)) {
|
|
||||||
t.Fatalf("logger fields not works, buf contains: %s", buf.Bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestClone(t *testing.T) {
|
|
||||||
ctx := context.TODO()
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
l := NewLogger(logger.WithLevel(logger.TraceLevel), logger.WithOutput(buf))
|
|
||||||
if err := l.Init(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
nl := l.Clone(logger.WithLevel(logger.ErrorLevel))
|
|
||||||
if err := nl.Init(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
nl.Info(ctx, "info message")
|
|
||||||
if len(buf.Bytes()) != 0 {
|
|
||||||
t.Fatal("message must not be logged")
|
|
||||||
}
|
|
||||||
l.Info(ctx, "info message")
|
|
||||||
if len(buf.Bytes()) == 0 {
|
|
||||||
t.Fatal("message must be logged")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRedirectStdLogger(t *testing.T) {
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
l := NewLogger(logger.WithLevel(logger.ErrorLevel), logger.WithOutput(buf))
|
|
||||||
if err := l.Init(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
fn := logger.RedirectStdLogger(l, logger.ErrorLevel)
|
|
||||||
defer fn()
|
|
||||||
log.Print("test")
|
|
||||||
if !(bytes.Contains(buf.Bytes(), []byte(`"level":"error"`)) && bytes.Contains(buf.Bytes(), []byte(`"msg":"test"`))) {
|
|
||||||
t.Fatalf("logger error, buf %s", buf.Bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStdLogger(t *testing.T) {
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
l := NewLogger(logger.WithLevel(logger.TraceLevel), logger.WithOutput(buf))
|
|
||||||
if err := l.Init(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
lg := logger.NewStdLogger(l, logger.ErrorLevel)
|
|
||||||
lg.Print("test")
|
|
||||||
if !(bytes.Contains(buf.Bytes(), []byte(`"level":"error"`)) && bytes.Contains(buf.Bytes(), []byte(`"msg":"test"`))) {
|
|
||||||
t.Fatalf("logger error, buf %s", buf.Bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLogger(t *testing.T) {
|
|
||||||
ctx := context.TODO()
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
l := NewLogger(logger.WithLevel(logger.TraceLevel), logger.WithOutput(buf))
|
|
||||||
if err := l.Init(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
l.Trace(ctx, "trace_msg1")
|
|
||||||
l.Warn(ctx, "warn_msg1")
|
|
||||||
l.Fields("error", "test").Info(ctx, "error message")
|
|
||||||
l.Warn(ctx, "first second")
|
|
||||||
|
|
||||||
if !(bytes.Contains(buf.Bytes(), []byte(`"level":"trace"`)) && bytes.Contains(buf.Bytes(), []byte(`"msg":"trace_msg1"`))) {
|
|
||||||
t.Fatalf("logger tracer, buf %s", buf.Bytes())
|
|
||||||
}
|
|
||||||
if !(bytes.Contains(buf.Bytes(), []byte(`"level":"warn"`)) && bytes.Contains(buf.Bytes(), []byte(`"msg":"warn_msg1"`))) {
|
|
||||||
t.Fatalf("logger warn, buf %s", buf.Bytes())
|
|
||||||
}
|
|
||||||
if !(bytes.Contains(buf.Bytes(), []byte(`"level":"info"`)) && bytes.Contains(buf.Bytes(), []byte(`"msg":"error message","error":"test"`))) {
|
|
||||||
t.Fatalf("logger info, buf %s", buf.Bytes())
|
|
||||||
}
|
|
||||||
if !(bytes.Contains(buf.Bytes(), []byte(`"level":"warn"`)) && bytes.Contains(buf.Bytes(), []byte(`"msg":"first second"`))) {
|
|
||||||
t.Fatalf("logger warn, buf %s", buf.Bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_WithContextAttrFunc(t *testing.T) {
|
|
||||||
loggerContextAttrFuncs := []logger.ContextAttrFunc{
|
|
||||||
func(ctx context.Context) []interface{} {
|
|
||||||
md, ok := metadata.FromIncomingContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
attrs := make([]interface{}, 0, 10)
|
|
||||||
for k, v := range md {
|
|
||||||
switch k {
|
|
||||||
case "X-Request-Id", "Phone", "External-Id", "Source-Service", "X-App-Install-Id", "Client-Id", "Client-Ip":
|
|
||||||
attrs = append(attrs, strings.ToLower(k), v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return attrs
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.DefaultContextAttrFuncs = append(logger.DefaultContextAttrFuncs, loggerContextAttrFuncs...)
|
|
||||||
|
|
||||||
ctx := context.TODO()
|
|
||||||
ctx = metadata.AppendIncomingContext(ctx, "X-Request-Id", uuid.New().String(),
|
|
||||||
"Source-Service", "Test-System")
|
|
||||||
|
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
l := NewLogger(logger.WithLevel(logger.TraceLevel), logger.WithOutput(buf))
|
|
||||||
if err := l.Init(); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Info(ctx, "test message")
|
|
||||||
if !(bytes.Contains(buf.Bytes(), []byte(`"level":"info"`)) && bytes.Contains(buf.Bytes(), []byte(`"msg":"test message"`))) {
|
|
||||||
t.Fatalf("logger info, buf %s", buf.Bytes())
|
|
||||||
}
|
|
||||||
if !(bytes.Contains(buf.Bytes(), []byte(`"x-request-id":"`))) {
|
|
||||||
t.Fatalf("logger info, buf %s", buf.Bytes())
|
|
||||||
}
|
|
||||||
if !(bytes.Contains(buf.Bytes(), []byte(`"source-service":"Test-System"`))) {
|
|
||||||
t.Fatalf("logger info, buf %s", buf.Bytes())
|
|
||||||
}
|
|
||||||
buf.Reset()
|
|
||||||
imd, _ := metadata.FromIncomingContext(ctx)
|
|
||||||
l.Info(ctx, "test message1")
|
|
||||||
imd.Set("Source-Service", "Test-System2")
|
|
||||||
l.Info(ctx, "test message2")
|
|
||||||
|
|
||||||
// t.Logf("xxx %s", buf.Bytes())
|
|
||||||
}
|
|
@ -46,11 +46,6 @@ var (
|
|||||||
closeMapBytes = []byte("}")
|
closeMapBytes = []byte("}")
|
||||||
)
|
)
|
||||||
|
|
||||||
type protoMessage interface {
|
|
||||||
Reset()
|
|
||||||
ProtoMessage()
|
|
||||||
}
|
|
||||||
|
|
||||||
type Wrapper struct {
|
type Wrapper struct {
|
||||||
val interface{}
|
val interface{}
|
||||||
s fmt.State
|
s fmt.State
|
||||||
@ -58,7 +53,7 @@ type Wrapper struct {
|
|||||||
opts *Options
|
opts *Options
|
||||||
depth int
|
depth int
|
||||||
ignoreNextType bool
|
ignoreNextType bool
|
||||||
takeMap map[int]bool
|
takeAll map[int]bool
|
||||||
protoWrapperType bool
|
protoWrapperType bool
|
||||||
sqlWrapperType bool
|
sqlWrapperType bool
|
||||||
}
|
}
|
||||||
@ -116,7 +111,7 @@ func Tagged(b bool) Option {
|
|||||||
|
|
||||||
func Unwrap(val interface{}, opts ...Option) *Wrapper {
|
func Unwrap(val interface{}, opts ...Option) *Wrapper {
|
||||||
options := NewOptions(opts...)
|
options := NewOptions(opts...)
|
||||||
return &Wrapper{val: val, opts: &options, pointers: make(map[uintptr]int), takeMap: make(map[int]bool)}
|
return &Wrapper{val: val, opts: &options, pointers: make(map[uintptr]int), takeAll: make(map[int]bool)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wrapper) unpackValue(v reflect.Value) reflect.Value {
|
func (w *Wrapper) unpackValue(v reflect.Value) reflect.Value {
|
||||||
@ -242,6 +237,9 @@ func (w *Wrapper) format(v reflect.Value) {
|
|||||||
_, _ = w.s.Write(buf)
|
_, _ = w.s.Write(buf)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if w.opts.Tagged {
|
||||||
|
w.checkTakeAll(v, 1)
|
||||||
|
}
|
||||||
|
|
||||||
// Handle invalid reflect values immediately.
|
// Handle invalid reflect values immediately.
|
||||||
kind := v.Kind()
|
kind := v.Kind()
|
||||||
@ -258,10 +256,6 @@ func (w *Wrapper) format(v reflect.Value) {
|
|||||||
w.protoWrapperType = true
|
w.protoWrapperType = true
|
||||||
} else if strings.HasPrefix(reflect.Indirect(v).Type().String(), "sql.Null") {
|
} else if strings.HasPrefix(reflect.Indirect(v).Type().String(), "sql.Null") {
|
||||||
w.sqlWrapperType = true
|
w.sqlWrapperType = true
|
||||||
} else if v.CanInterface() {
|
|
||||||
if _, ok := v.Interface().(protoMessage); ok {
|
|
||||||
w.protoWrapperType = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
w.formatPtr(v)
|
w.formatPtr(v)
|
||||||
@ -384,12 +378,6 @@ func (w *Wrapper) format(v reflect.Value) {
|
|||||||
prevSkip := false
|
prevSkip := false
|
||||||
|
|
||||||
for i := 0; i < numFields; i++ {
|
for i := 0; i < numFields; i++ {
|
||||||
switch vt.Field(i).Type.PkgPath() {
|
|
||||||
case "google.golang.org/protobuf/internal/impl", "google.golang.org/protobuf/internal/pragma":
|
|
||||||
w.protoWrapperType = true
|
|
||||||
prevSkip = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if w.protoWrapperType && !vt.Field(i).IsExported() {
|
if w.protoWrapperType && !vt.Field(i).IsExported() {
|
||||||
prevSkip = true
|
prevSkip = true
|
||||||
continue
|
continue
|
||||||
@ -397,9 +385,6 @@ func (w *Wrapper) format(v reflect.Value) {
|
|||||||
prevSkip = true
|
prevSkip = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if _, ok := vt.Field(i).Tag.Lookup("protobuf"); ok && !w.protoWrapperType {
|
|
||||||
w.protoWrapperType = true
|
|
||||||
}
|
|
||||||
sv, ok := vt.Field(i).Tag.Lookup("logger")
|
sv, ok := vt.Field(i).Tag.Lookup("logger")
|
||||||
switch {
|
switch {
|
||||||
case ok:
|
case ok:
|
||||||
@ -410,16 +395,11 @@ func (w *Wrapper) format(v reflect.Value) {
|
|||||||
case "take":
|
case "take":
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
case w.takeAll[w.depth]:
|
||||||
|
break
|
||||||
case !ok && w.opts.Tagged:
|
case !ok && w.opts.Tagged:
|
||||||
// skip top level untagged
|
prevSkip = true
|
||||||
if w.depth == 1 {
|
continue
|
||||||
prevSkip = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if tv, ok := w.takeMap[w.depth]; ok && !tv {
|
|
||||||
prevSkip = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if prevSkip {
|
if prevSkip {
|
||||||
@ -436,7 +416,9 @@ func (w *Wrapper) format(v reflect.Value) {
|
|||||||
_, _ = w.s.Write([]byte(vt.Name))
|
_, _ = w.s.Write([]byte(vt.Name))
|
||||||
_, _ = w.s.Write(colonBytes)
|
_, _ = w.s.Write(colonBytes)
|
||||||
}
|
}
|
||||||
w.format(w.unpackValue(v.Field(i)))
|
unpackValue := w.unpackValue(v.Field(i))
|
||||||
|
w.checkTakeAll(unpackValue, w.depth)
|
||||||
|
w.format(unpackValue)
|
||||||
numWritten++
|
numWritten++
|
||||||
}
|
}
|
||||||
w.depth--
|
w.depth--
|
||||||
@ -479,10 +461,6 @@ func (w *Wrapper) Format(s fmt.State, verb rune) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if w.opts.Tagged {
|
|
||||||
w.buildTakeMap(reflect.ValueOf(w.val), 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
w.format(reflect.ValueOf(w.val))
|
w.format(reflect.ValueOf(w.val))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -637,28 +615,24 @@ func (w *Wrapper) constructOrigFormat(verb rune) string {
|
|||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *Wrapper) buildTakeMap(v reflect.Value, depth int) {
|
func (w *Wrapper) checkTakeAll(v reflect.Value, depth int) {
|
||||||
|
if _, ok := w.takeAll[depth]; ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
if !v.IsValid() || v.IsZero() {
|
if !v.IsValid() || v.IsZero() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
switch v.Kind() {
|
switch v.Kind() {
|
||||||
case reflect.Slice, reflect.Array:
|
|
||||||
for i := 0; i < v.Len(); i++ {
|
|
||||||
w.buildTakeMap(v.Index(i), depth+1)
|
|
||||||
}
|
|
||||||
w.takeMap[depth] = true
|
|
||||||
return
|
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
break
|
break
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
v = v.Elem()
|
v = v.Elem()
|
||||||
if v.Kind() != reflect.Struct {
|
if v.Kind() != reflect.Struct {
|
||||||
w.takeMap[depth] = true
|
w.takeAll[depth] = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
w.takeMap[depth] = true
|
w.takeAll[depth] = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -667,15 +641,8 @@ func (w *Wrapper) buildTakeMap(v reflect.Value, depth int) {
|
|||||||
for i := 0; i < v.NumField(); i++ {
|
for i := 0; i < v.NumField(); i++ {
|
||||||
sv, ok := vt.Field(i).Tag.Lookup("logger")
|
sv, ok := vt.Field(i).Tag.Lookup("logger")
|
||||||
if ok && sv == "take" {
|
if ok && sv == "take" {
|
||||||
w.takeMap[depth] = false
|
w.takeAll[depth] = false
|
||||||
}
|
}
|
||||||
if v.Kind() == reflect.Struct ||
|
w.checkTakeAll(v.Field(i), depth+1)
|
||||||
(v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct) {
|
|
||||||
w.buildTakeMap(v.Field(i), depth+1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := w.takeMap[depth]; !ok {
|
|
||||||
w.takeMap[depth] = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
399
logger/wrapper/wrapper.go
Normal file
399
logger/wrapper/wrapper.go
Normal file
@ -0,0 +1,399 @@
|
|||||||
|
// Package wrapper provides wrapper for Logger
|
||||||
|
package wrapper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"go.unistack.org/micro/v3/client"
|
||||||
|
"go.unistack.org/micro/v3/logger"
|
||||||
|
"go.unistack.org/micro/v3/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// DefaultClientCallObserver called by wrapper in client Call
|
||||||
|
DefaultClientCallObserver = func(ctx context.Context, req client.Request, rsp interface{}, opts []client.CallOption, err error) []string {
|
||||||
|
labels := []string{"service", req.Service(), "endpoint", req.Endpoint()}
|
||||||
|
if err != nil {
|
||||||
|
labels = append(labels, "error", err.Error())
|
||||||
|
}
|
||||||
|
return labels
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultClientStreamObserver called by wrapper in client Stream
|
||||||
|
DefaultClientStreamObserver = func(ctx context.Context, req client.Request, opts []client.CallOption, stream client.Stream, err error) []string {
|
||||||
|
labels := []string{"service", req.Service(), "endpoint", req.Endpoint()}
|
||||||
|
if err != nil {
|
||||||
|
labels = append(labels, "error", err.Error())
|
||||||
|
}
|
||||||
|
return labels
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultClientPublishObserver called by wrapper in client Publish
|
||||||
|
DefaultClientPublishObserver = func(ctx context.Context, msg client.Message, opts []client.PublishOption, err error) []string {
|
||||||
|
labels := []string{"endpoint", msg.Topic()}
|
||||||
|
if err != nil {
|
||||||
|
labels = append(labels, "error", err.Error())
|
||||||
|
}
|
||||||
|
return labels
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultServerHandlerObserver called by wrapper in server Handler
|
||||||
|
DefaultServerHandlerObserver = func(ctx context.Context, req server.Request, rsp interface{}, err error) []string {
|
||||||
|
labels := []string{"service", req.Service(), "endpoint", req.Endpoint()}
|
||||||
|
if err != nil {
|
||||||
|
labels = append(labels, "error", err.Error())
|
||||||
|
}
|
||||||
|
return labels
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultServerSubscriberObserver called by wrapper in server Subscriber
|
||||||
|
DefaultServerSubscriberObserver = func(ctx context.Context, msg server.Message, err error) []string {
|
||||||
|
labels := []string{"endpoint", msg.Topic()}
|
||||||
|
if err != nil {
|
||||||
|
labels = append(labels, "error", err.Error())
|
||||||
|
}
|
||||||
|
return labels
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultClientCallFuncObserver called by wrapper in client CallFunc
|
||||||
|
DefaultClientCallFuncObserver = func(ctx context.Context, addr string, req client.Request, rsp interface{}, opts client.CallOptions, err error) []string {
|
||||||
|
labels := []string{"service", req.Service(), "endpoint", req.Endpoint()}
|
||||||
|
if err != nil {
|
||||||
|
labels = append(labels, "error", err.Error())
|
||||||
|
}
|
||||||
|
return labels
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultSkipEndpoints wrapper not called for this endpoints
|
||||||
|
DefaultSkipEndpoints = []string{"Meter.Metrics", "Health.Live", "Health.Ready", "Health.Version"}
|
||||||
|
)
|
||||||
|
|
||||||
|
type lWrapper struct {
|
||||||
|
client.Client
|
||||||
|
serverHandler server.HandlerFunc
|
||||||
|
serverSubscriber server.SubscriberFunc
|
||||||
|
clientCallFunc client.CallFunc
|
||||||
|
opts Options
|
||||||
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
// ClientCallObserver func signature
|
||||||
|
ClientCallObserver func(context.Context, client.Request, interface{}, []client.CallOption, error) []string
|
||||||
|
// ClientStreamObserver func signature
|
||||||
|
ClientStreamObserver func(context.Context, client.Request, []client.CallOption, client.Stream, error) []string
|
||||||
|
// ClientPublishObserver func signature
|
||||||
|
ClientPublishObserver func(context.Context, client.Message, []client.PublishOption, error) []string
|
||||||
|
// ClientCallFuncObserver func signature
|
||||||
|
ClientCallFuncObserver func(context.Context, string, client.Request, interface{}, client.CallOptions, error) []string
|
||||||
|
// ServerHandlerObserver func signature
|
||||||
|
ServerHandlerObserver func(context.Context, server.Request, interface{}, error) []string
|
||||||
|
// ServerSubscriberObserver func signature
|
||||||
|
ServerSubscriberObserver func(context.Context, server.Message, error) []string
|
||||||
|
)
|
||||||
|
|
||||||
|
// Options struct for wrapper
|
||||||
|
type Options struct {
|
||||||
|
// Logger that used for log
|
||||||
|
Logger logger.Logger
|
||||||
|
// ServerHandlerObservers funcs
|
||||||
|
ServerHandlerObservers []ServerHandlerObserver
|
||||||
|
// ServerSubscriberObservers funcs
|
||||||
|
ServerSubscriberObservers []ServerSubscriberObserver
|
||||||
|
// ClientCallObservers funcs
|
||||||
|
ClientCallObservers []ClientCallObserver
|
||||||
|
// ClientStreamObservers funcs
|
||||||
|
ClientStreamObservers []ClientStreamObserver
|
||||||
|
// ClientPublishObservers funcs
|
||||||
|
ClientPublishObservers []ClientPublishObserver
|
||||||
|
// ClientCallFuncObservers funcs
|
||||||
|
ClientCallFuncObservers []ClientCallFuncObserver
|
||||||
|
// SkipEndpoints
|
||||||
|
SkipEndpoints []string
|
||||||
|
// Level for logger
|
||||||
|
Level logger.Level
|
||||||
|
// Enabled flag
|
||||||
|
Enabled bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Option func signature
|
||||||
|
type Option func(*Options)
|
||||||
|
|
||||||
|
// NewOptions creates Options from Option slice
|
||||||
|
func NewOptions(opts ...Option) Options {
|
||||||
|
options := Options{
|
||||||
|
Logger: logger.DefaultLogger,
|
||||||
|
Level: logger.TraceLevel,
|
||||||
|
ClientCallObservers: []ClientCallObserver{DefaultClientCallObserver},
|
||||||
|
ClientStreamObservers: []ClientStreamObserver{DefaultClientStreamObserver},
|
||||||
|
ClientPublishObservers: []ClientPublishObserver{DefaultClientPublishObserver},
|
||||||
|
ClientCallFuncObservers: []ClientCallFuncObserver{DefaultClientCallFuncObserver},
|
||||||
|
ServerHandlerObservers: []ServerHandlerObserver{DefaultServerHandlerObserver},
|
||||||
|
ServerSubscriberObservers: []ServerSubscriberObserver{DefaultServerSubscriberObserver},
|
||||||
|
SkipEndpoints: DefaultSkipEndpoints,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithEnabled enable/diable flag
|
||||||
|
func WithEnabled(b bool) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.Enabled = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithLevel log level
|
||||||
|
func WithLevel(l logger.Level) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.Level = l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithLogger logger
|
||||||
|
func WithLogger(l logger.Logger) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.Logger = l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithClientCallObservers funcs
|
||||||
|
func WithClientCallObservers(ob ...ClientCallObserver) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.ClientCallObservers = ob
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithClientStreamObservers funcs
|
||||||
|
func WithClientStreamObservers(ob ...ClientStreamObserver) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.ClientStreamObservers = ob
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithClientPublishObservers funcs
|
||||||
|
func WithClientPublishObservers(ob ...ClientPublishObserver) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.ClientPublishObservers = ob
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithClientCallFuncObservers funcs
|
||||||
|
func WithClientCallFuncObservers(ob ...ClientCallFuncObserver) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.ClientCallFuncObservers = ob
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithServerHandlerObservers funcs
|
||||||
|
func WithServerHandlerObservers(ob ...ServerHandlerObserver) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.ServerHandlerObservers = ob
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithServerSubscriberObservers funcs
|
||||||
|
func WithServerSubscriberObservers(ob ...ServerSubscriberObserver) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.ServerSubscriberObservers = ob
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SkipEndpoins
|
||||||
|
func SkipEndpoints(eps ...string) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.SkipEndpoints = append(o.SkipEndpoints, eps...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {
|
||||||
|
err := l.Client.Call(ctx, req, rsp, opts...)
|
||||||
|
|
||||||
|
endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
|
||||||
|
for _, ep := range l.opts.SkipEndpoints {
|
||||||
|
if ep == endpoint {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !l.opts.Enabled {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var labels []string
|
||||||
|
for _, o := range l.opts.ClientCallObservers {
|
||||||
|
labels = append(labels, o(ctx, req, rsp, opts, err)...)
|
||||||
|
}
|
||||||
|
l.opts.Logger.Fields(labels).Log(ctx, l.opts.Level)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lWrapper) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Stream, error) {
|
||||||
|
stream, err := l.Client.Stream(ctx, req, opts...)
|
||||||
|
|
||||||
|
endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
|
||||||
|
for _, ep := range l.opts.SkipEndpoints {
|
||||||
|
if ep == endpoint {
|
||||||
|
return stream, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !l.opts.Enabled {
|
||||||
|
return stream, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var labels []string
|
||||||
|
for _, o := range l.opts.ClientStreamObservers {
|
||||||
|
labels = append(labels, o(ctx, req, opts, stream, err)...)
|
||||||
|
}
|
||||||
|
l.opts.Logger.Fields(labels).Log(ctx, l.opts.Level)
|
||||||
|
|
||||||
|
return stream, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lWrapper) Publish(ctx context.Context, msg client.Message, opts ...client.PublishOption) error {
|
||||||
|
err := l.Client.Publish(ctx, msg, opts...)
|
||||||
|
|
||||||
|
endpoint := msg.Topic()
|
||||||
|
for _, ep := range l.opts.SkipEndpoints {
|
||||||
|
if ep == endpoint {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !l.opts.Enabled {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var labels []string
|
||||||
|
for _, o := range l.opts.ClientPublishObservers {
|
||||||
|
labels = append(labels, o(ctx, msg, opts, err)...)
|
||||||
|
}
|
||||||
|
l.opts.Logger.Fields(labels).Log(ctx, l.opts.Level)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lWrapper) ServerHandler(ctx context.Context, req server.Request, rsp interface{}) error {
|
||||||
|
err := l.serverHandler(ctx, req, rsp)
|
||||||
|
|
||||||
|
endpoint := req.Endpoint()
|
||||||
|
for _, ep := range l.opts.SkipEndpoints {
|
||||||
|
if ep == endpoint {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !l.opts.Enabled {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var labels []string
|
||||||
|
for _, o := range l.opts.ServerHandlerObservers {
|
||||||
|
labels = append(labels, o(ctx, req, rsp, err)...)
|
||||||
|
}
|
||||||
|
l.opts.Logger.Fields(labels).Log(ctx, l.opts.Level)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lWrapper) ServerSubscriber(ctx context.Context, msg server.Message) error {
|
||||||
|
err := l.serverSubscriber(ctx, msg)
|
||||||
|
|
||||||
|
endpoint := msg.Topic()
|
||||||
|
for _, ep := range l.opts.SkipEndpoints {
|
||||||
|
if ep == endpoint {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !l.opts.Enabled {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var labels []string
|
||||||
|
for _, o := range l.opts.ServerSubscriberObservers {
|
||||||
|
labels = append(labels, o(ctx, msg, err)...)
|
||||||
|
}
|
||||||
|
l.opts.Logger.Fields(labels).Log(ctx, l.opts.Level)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClientWrapper accepts an open options and returns a Client Wrapper
|
||||||
|
func NewClientWrapper(opts ...Option) client.Wrapper {
|
||||||
|
return func(c client.Client) client.Client {
|
||||||
|
options := NewOptions()
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
return &lWrapper{opts: options, Client: c}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClientCallWrapper accepts an options and returns a Call Wrapper
|
||||||
|
func NewClientCallWrapper(opts ...Option) client.CallWrapper {
|
||||||
|
return func(h client.CallFunc) client.CallFunc {
|
||||||
|
options := NewOptions()
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
l := &lWrapper{opts: options, clientCallFunc: h}
|
||||||
|
return l.ClientCallFunc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *lWrapper) ClientCallFunc(ctx context.Context, addr string, req client.Request, rsp interface{}, opts client.CallOptions) error {
|
||||||
|
err := l.clientCallFunc(ctx, addr, req, rsp, opts)
|
||||||
|
|
||||||
|
endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
|
||||||
|
for _, ep := range l.opts.SkipEndpoints {
|
||||||
|
if ep == endpoint {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !l.opts.Enabled {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var labels []string
|
||||||
|
for _, o := range l.opts.ClientCallFuncObservers {
|
||||||
|
labels = append(labels, o(ctx, addr, req, rsp, opts, err)...)
|
||||||
|
}
|
||||||
|
l.opts.Logger.Fields(labels).Log(ctx, l.opts.Level)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewServerHandlerWrapper accepts an options and returns a Handler Wrapper
|
||||||
|
func NewServerHandlerWrapper(opts ...Option) server.HandlerWrapper {
|
||||||
|
return func(h server.HandlerFunc) server.HandlerFunc {
|
||||||
|
options := NewOptions()
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
l := &lWrapper{opts: options, serverHandler: h}
|
||||||
|
return l.ServerHandler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewServerSubscriberWrapper accepts an options and returns a Subscriber Wrapper
|
||||||
|
func NewServerSubscriberWrapper(opts ...Option) server.SubscriberWrapper {
|
||||||
|
return func(h server.SubscriberFunc) server.SubscriberFunc {
|
||||||
|
options := NewOptions()
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
l := &lWrapper{opts: options, serverSubscriber: h}
|
||||||
|
return l.ServerSubscriber
|
||||||
|
}
|
||||||
|
}
|
@ -39,6 +39,8 @@ func FromOutgoingContext(ctx context.Context) (Metadata, bool) {
|
|||||||
|
|
||||||
// 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
|
||||||
|
//
|
||||||
|
// Deprecated: use FromIncomingContext or FromOutgoingContext
|
||||||
func FromContext(ctx context.Context) (Metadata, bool) {
|
func FromContext(ctx context.Context) (Metadata, bool) {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
return nil, false
|
return nil, false
|
||||||
@ -51,6 +53,8 @@ func FromContext(ctx context.Context) (Metadata, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewContext creates a new context with the given metadata
|
// NewContext creates a new context with the given metadata
|
||||||
|
//
|
||||||
|
// Deprecated: use NewIncomingContext or NewOutgoingContext
|
||||||
func NewContext(ctx context.Context, md Metadata) context.Context {
|
func NewContext(ctx context.Context, md Metadata) context.Context {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
ctx = context.Background()
|
ctx = context.Background()
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Package metadata is a way of defining message headers
|
// Package metadata is a way of defining message headers
|
||||||
package metadata
|
package metadata // import "go.unistack.org/micro/v3/metadata"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/textproto"
|
"net/textproto"
|
||||||
@ -19,8 +19,6 @@ var (
|
|||||||
HeaderTimeout = "Micro-Timeout"
|
HeaderTimeout = "Micro-Timeout"
|
||||||
// HeaderAuthorization specifies Authorization header
|
// HeaderAuthorization specifies Authorization header
|
||||||
HeaderAuthorization = "Authorization"
|
HeaderAuthorization = "Authorization"
|
||||||
// HeaderXRequestID specifies request id
|
|
||||||
HeaderXRequestID = "X-Request-Id"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Metadata is our way of representing request headers internally.
|
// Metadata is our way of representing request headers internally.
|
||||||
@ -98,12 +96,11 @@ func (md Metadata) Del(keys ...string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 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) Metadata {
|
||||||
nmd := New(len(md))
|
nmd := New(len(md))
|
||||||
for key, val := range md {
|
for key, val := range md {
|
||||||
nmd.Set(key, val)
|
nmd.Set(key, val)
|
||||||
}
|
}
|
||||||
nmd.Del(exclude...)
|
|
||||||
return nmd
|
return nmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,14 +190,3 @@ func TestMetadataContext(t *testing.T) {
|
|||||||
t.Errorf("Expected metadata length 1 got %d", i)
|
t.Errorf("Expected metadata length 1 got %d", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCopy(t *testing.T) {
|
|
||||||
md := New(2)
|
|
||||||
md.Set("key1", "val1", "key2", "val2")
|
|
||||||
nmd := Copy(md, "key2")
|
|
||||||
if len(nmd) != 1 {
|
|
||||||
t.Fatal("Copy exclude not works")
|
|
||||||
} else if nmd["Key1"] != "val1" {
|
|
||||||
t.Fatal("Copy exclude not works")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
12
meter/generate.go
Normal file
12
meter/generate.go
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package meter
|
||||||
|
|
||||||
|
//go:generate sh -c "protoc -I./handler -I../ -I$(go list -f '{{ .Dir }}' -m go.unistack.org/micro-proto/v3) --go-micro_out='components=micro|http|server',standalone=false,debug=true,paths=source_relative:./handler handler/handler.proto"
|
||||||
|
|
||||||
|
import (
|
||||||
|
|
||||||
|
// import required packages
|
||||||
|
_ "go.unistack.org/micro-proto/v3/api"
|
||||||
|
|
||||||
|
// import required packages
|
||||||
|
_ "go.unistack.org/micro-proto/v3/openapiv3"
|
||||||
|
)
|
67
meter/handler/handler.go
Normal file
67
meter/handler/handler.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package handler // import "go.unistack.org/micro/v3/meter/handler"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"go.unistack.org/micro/v3/codec"
|
||||||
|
"go.unistack.org/micro/v3/errors"
|
||||||
|
"go.unistack.org/micro/v3/meter"
|
||||||
|
)
|
||||||
|
|
||||||
|
// guard to fail early
|
||||||
|
var _ MeterServer = &Handler{}
|
||||||
|
|
||||||
|
type Handler struct {
|
||||||
|
opts Options
|
||||||
|
}
|
||||||
|
|
||||||
|
type Option func(*Options)
|
||||||
|
|
||||||
|
type Options struct {
|
||||||
|
Meter meter.Meter
|
||||||
|
Name string
|
||||||
|
MeterOptions []meter.Option
|
||||||
|
}
|
||||||
|
|
||||||
|
func Meter(m meter.Meter) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.Meter = m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Name(name string) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.Name = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MeterOptions(opts ...meter.Option) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.MeterOptions = append(o.MeterOptions, opts...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOptions(opts ...Option) Options {
|
||||||
|
options := Options{Meter: meter.DefaultMeter}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHandler(opts ...Option) *Handler {
|
||||||
|
options := NewOptions(opts...)
|
||||||
|
return &Handler{opts: options}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) Metrics(ctx context.Context, req *codec.Frame, rsp *codec.Frame) error {
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
if err := h.opts.Meter.Write(buf, h.opts.MeterOptions...); err != nil {
|
||||||
|
return errors.InternalServerError(h.opts.Name, "%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rsp.Data = buf.Bytes()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
24
meter/handler/handler.proto
Normal file
24
meter/handler/handler.proto
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package micro.meter.handler;
|
||||||
|
option go_package = "go.unistack.org/micro/v3/meter/handler;handler";
|
||||||
|
|
||||||
|
import "api/annotations.proto";
|
||||||
|
import "openapiv3/annotations.proto";
|
||||||
|
import "codec/frame.proto";
|
||||||
|
|
||||||
|
service Meter {
|
||||||
|
rpc Metrics(micro.codec.Frame) returns (micro.codec.Frame) {
|
||||||
|
option (micro.openapiv3.openapiv3_operation) = {
|
||||||
|
operation_id: "Metrics";
|
||||||
|
responses: {
|
||||||
|
default: {
|
||||||
|
reference: {
|
||||||
|
_ref: "micro.codec.Frame";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
option (micro.api.http) = { get: "/metrics"; };
|
||||||
|
};
|
||||||
|
};
|
32
meter/handler/handler_micro.pb.go
Normal file
32
meter/handler/handler_micro.pb.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// Code generated by protoc-gen-go-micro. DO NOT EDIT.
|
||||||
|
// protoc-gen-go-micro version: v3.5.3
|
||||||
|
// source: handler.proto
|
||||||
|
|
||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
api "go.unistack.org/micro/v3/api"
|
||||||
|
codec "go.unistack.org/micro/v3/codec"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
MeterName = "Meter"
|
||||||
|
|
||||||
|
MeterEndpoints = []api.Endpoint{
|
||||||
|
{
|
||||||
|
Name: "Meter.Metrics",
|
||||||
|
Path: []string{"/metrics"},
|
||||||
|
Method: []string{"GET"},
|
||||||
|
Handler: "rpc",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewMeterEndpoints() []api.Endpoint {
|
||||||
|
return MeterEndpoints
|
||||||
|
}
|
||||||
|
|
||||||
|
type MeterServer interface {
|
||||||
|
Metrics(ctx context.Context, req *codec.Frame, rsp *codec.Frame) error
|
||||||
|
}
|
35
meter/handler/handler_micro_http.pb.go
Normal file
35
meter/handler/handler_micro_http.pb.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Code generated by protoc-gen-go-micro. DO NOT EDIT.
|
||||||
|
// protoc-gen-go-micro version: v3.5.3
|
||||||
|
// source: handler.proto
|
||||||
|
|
||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
api "go.unistack.org/micro/v3/api"
|
||||||
|
codec "go.unistack.org/micro/v3/codec"
|
||||||
|
server "go.unistack.org/micro/v3/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
type meterServer struct {
|
||||||
|
MeterServer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *meterServer) Metrics(ctx context.Context, req *codec.Frame, rsp *codec.Frame) error {
|
||||||
|
return h.MeterServer.Metrics(ctx, req, rsp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterMeterServer(s server.Server, sh MeterServer, opts ...server.HandlerOption) error {
|
||||||
|
type meter interface {
|
||||||
|
Metrics(ctx context.Context, req *codec.Frame, rsp *codec.Frame) error
|
||||||
|
}
|
||||||
|
type Meter struct {
|
||||||
|
meter
|
||||||
|
}
|
||||||
|
h := &meterServer{sh}
|
||||||
|
var nopts []server.HandlerOption
|
||||||
|
for _, endpoint := range MeterEndpoints {
|
||||||
|
nopts = append(nopts, api.WithEndpoint(&endpoint))
|
||||||
|
}
|
||||||
|
return s.Handle(s.NewHandler(&Meter{h}, append(nopts, opts...)...))
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
// Package meter is for instrumentation
|
// Package meter is for instrumentation
|
||||||
package meter
|
package meter // import "go.unistack.org/micro/v3/meter"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
@ -11,24 +11,19 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
// DefaultMeter is the default meter
|
// DefaultMeter is the default meter
|
||||||
DefaultMeter Meter = NewMeter()
|
DefaultMeter = NewMeter()
|
||||||
// DefaultAddress data will be made available on this host:port
|
// DefaultAddress data will be made available on this host:port
|
||||||
DefaultAddress = ":9090"
|
DefaultAddress = ":9090"
|
||||||
// DefaultPath the meter endpoint where the Meter data will be made available
|
// DefaultPath the meter endpoint where the Meter data will be made available
|
||||||
DefaultPath = "/metrics"
|
DefaultPath = "/metrics"
|
||||||
// DefaultMeterStatsInterval specifies interval for meter updating
|
// DefaultMetricPrefix holds the string that prepends to all metrics
|
||||||
DefaultMeterStatsInterval = 5 * time.Second
|
DefaultMetricPrefix = "micro_"
|
||||||
|
// DefaultLabelPrefix holds the string that prepends to all labels
|
||||||
|
DefaultLabelPrefix = "micro_"
|
||||||
// DefaultSummaryQuantiles is the default spread of stats for summary
|
// DefaultSummaryQuantiles is the default spread of stats for summary
|
||||||
DefaultSummaryQuantiles = []float64{0.5, 0.9, 0.97, 0.99, 1}
|
DefaultSummaryQuantiles = []float64{0.5, 0.9, 0.97, 0.99, 1}
|
||||||
// DefaultSummaryWindow is the default window for summary
|
// DefaultSummaryWindow is the default window for summary
|
||||||
DefaultSummaryWindow = 5 * time.Minute
|
DefaultSummaryWindow = 5 * time.Minute
|
||||||
// DefaultSkipEndpoints is the slice of endpoint that must not be metered
|
|
||||||
DefaultSkipEndpoints = []string{
|
|
||||||
"MeterService.Metrics",
|
|
||||||
"HealthService.Live",
|
|
||||||
"HealthService.Ready",
|
|
||||||
"HealthService.Version",
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Meter is an interface for collecting and instrumenting metrics
|
// Meter is an interface for collecting and instrumenting metrics
|
||||||
|
@ -2,6 +2,8 @@ package meter
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"go.unistack.org/micro/v3/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Option powers the configuration for metrics implementations:
|
// Option powers the configuration for metrics implementations:
|
||||||
@ -9,6 +11,8 @@ type Option func(*Options)
|
|||||||
|
|
||||||
// Options for metrics implementations
|
// Options for metrics implementations
|
||||||
type Options struct {
|
type Options struct {
|
||||||
|
// Logger used for logging
|
||||||
|
Logger logger.Logger
|
||||||
// Context holds external options
|
// Context holds external options
|
||||||
Context context.Context
|
Context context.Context
|
||||||
// Name holds the meter name
|
// Name holds the meter name
|
||||||
@ -17,6 +21,10 @@ type Options struct {
|
|||||||
Address string
|
Address string
|
||||||
// Path holds the path for metrics
|
// Path holds the path for metrics
|
||||||
Path string
|
Path string
|
||||||
|
// MetricPrefix holds the prefix for all metrics
|
||||||
|
MetricPrefix string
|
||||||
|
// LabelPrefix holds the prefix for all labels
|
||||||
|
LabelPrefix string
|
||||||
// Labels holds the default labels
|
// Labels holds the default labels
|
||||||
Labels []string
|
Labels []string
|
||||||
// WriteProcessMetrics flag to write process metrics
|
// WriteProcessMetrics flag to write process metrics
|
||||||
@ -28,9 +36,12 @@ type Options struct {
|
|||||||
// NewOptions prepares a set of options:
|
// NewOptions prepares a set of options:
|
||||||
func NewOptions(opt ...Option) Options {
|
func NewOptions(opt ...Option) Options {
|
||||||
opts := Options{
|
opts := Options{
|
||||||
Address: DefaultAddress,
|
Address: DefaultAddress,
|
||||||
Path: DefaultPath,
|
Path: DefaultPath,
|
||||||
Context: context.Background(),
|
Context: context.Background(),
|
||||||
|
Logger: logger.DefaultLogger,
|
||||||
|
MetricPrefix: DefaultMetricPrefix,
|
||||||
|
LabelPrefix: DefaultLabelPrefix,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, o := range opt {
|
for _, o := range opt {
|
||||||
@ -40,6 +51,20 @@ func NewOptions(opt ...Option) Options {
|
|||||||
return opts
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LabelPrefix sets the labels prefix
|
||||||
|
func LabelPrefix(pref string) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.LabelPrefix = pref
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MetricPrefix sets the metric prefix
|
||||||
|
func MetricPrefix(pref string) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.MetricPrefix = pref
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Context sets the metrics context
|
// Context sets the metrics context
|
||||||
func Context(ctx context.Context) Option {
|
func Context(ctx context.Context) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
@ -70,7 +95,14 @@ func TimingObjectives(value map[float64]float64) Option {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Labels add the meter labels
|
// Logger sets the logger
|
||||||
|
func Logger(l logger.Logger) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.Logger = l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Labels sets the meter labels
|
||||||
func Labels(ls ...string) Option {
|
func Labels(ls ...string) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
o.Labels = append(o.Labels, ls...)
|
o.Labels = append(o.Labels, ls...)
|
||||||
|
347
meter/wrapper/wrapper.go
Normal file
347
meter/wrapper/wrapper.go
Normal file
@ -0,0 +1,347 @@
|
|||||||
|
package wrapper // import "go.unistack.org/micro/v3/meter/wrapper"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.unistack.org/micro/v3/client"
|
||||||
|
"go.unistack.org/micro/v3/meter"
|
||||||
|
"go.unistack.org/micro/v3/server"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ClientRequestDurationSeconds specifies meter metric name
|
||||||
|
ClientRequestDurationSeconds = "client_request_duration_seconds"
|
||||||
|
// ClientRequestLatencyMicroseconds specifies meter metric name
|
||||||
|
ClientRequestLatencyMicroseconds = "client_request_latency_microseconds"
|
||||||
|
// ClientRequestTotal specifies meter metric name
|
||||||
|
ClientRequestTotal = "client_request_total"
|
||||||
|
// ClientRequestInflight specifies meter metric name
|
||||||
|
ClientRequestInflight = "client_request_inflight"
|
||||||
|
// ServerRequestDurationSeconds specifies meter metric name
|
||||||
|
ServerRequestDurationSeconds = "server_request_duration_seconds"
|
||||||
|
// ServerRequestLatencyMicroseconds specifies meter metric name
|
||||||
|
ServerRequestLatencyMicroseconds = "server_request_latency_microseconds"
|
||||||
|
// ServerRequestTotal specifies meter metric name
|
||||||
|
ServerRequestTotal = "server_request_total"
|
||||||
|
// ServerRequestInflight specifies meter metric name
|
||||||
|
ServerRequestInflight = "server_request_inflight"
|
||||||
|
// PublishMessageDurationSeconds specifies meter metric name
|
||||||
|
PublishMessageDurationSeconds = "publish_message_duration_seconds"
|
||||||
|
// PublishMessageLatencyMicroseconds specifies meter metric name
|
||||||
|
PublishMessageLatencyMicroseconds = "publish_message_latency_microseconds"
|
||||||
|
// PublishMessageTotal specifies meter metric name
|
||||||
|
PublishMessageTotal = "publish_message_total"
|
||||||
|
// PublishMessageInflight specifies meter metric name
|
||||||
|
PublishMessageInflight = "publish_message_inflight"
|
||||||
|
// SubscribeMessageDurationSeconds specifies meter metric name
|
||||||
|
SubscribeMessageDurationSeconds = "subscribe_message_duration_seconds"
|
||||||
|
// SubscribeMessageLatencyMicroseconds specifies meter metric name
|
||||||
|
SubscribeMessageLatencyMicroseconds = "subscribe_message_latency_microseconds"
|
||||||
|
// SubscribeMessageTotal specifies meter metric name
|
||||||
|
SubscribeMessageTotal = "subscribe_message_total"
|
||||||
|
// SubscribeMessageInflight specifies meter metric name
|
||||||
|
SubscribeMessageInflight = "subscribe_message_inflight"
|
||||||
|
|
||||||
|
labelSuccess = "success"
|
||||||
|
labelFailure = "failure"
|
||||||
|
labelStatus = "status"
|
||||||
|
labelEndpoint = "endpoint"
|
||||||
|
|
||||||
|
// DefaultSkipEndpoints contains list of endpoints that not evaluted by wrapper
|
||||||
|
DefaultSkipEndpoints = []string{"Meter.Metrics", "Health.Live", "Health.Ready", "Health.Version"}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Options struct
|
||||||
|
type Options struct {
|
||||||
|
Meter meter.Meter
|
||||||
|
lopts []meter.Option
|
||||||
|
SkipEndpoints []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Option func signature
|
||||||
|
type Option func(*Options)
|
||||||
|
|
||||||
|
// NewOptions creates new Options struct
|
||||||
|
func NewOptions(opts ...Option) Options {
|
||||||
|
options := Options{
|
||||||
|
Meter: meter.DefaultMeter,
|
||||||
|
lopts: make([]meter.Option, 0, 5),
|
||||||
|
SkipEndpoints: DefaultSkipEndpoints,
|
||||||
|
}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceName passes service name to meter label
|
||||||
|
func ServiceName(name string) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.lopts = append(o.lopts, meter.Labels("name", name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceVersion passes service version to meter label
|
||||||
|
func ServiceVersion(version string) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.lopts = append(o.lopts, meter.Labels("version", version))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceID passes service id to meter label
|
||||||
|
func ServiceID(id string) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.lopts = append(o.lopts, meter.Labels("id", id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Meter passes meter
|
||||||
|
func Meter(m meter.Meter) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.Meter = m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SkipEndoints add endpoint to skip
|
||||||
|
func SkipEndoints(eps ...string) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.SkipEndpoints = append(o.SkipEndpoints, eps...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type wrapper struct {
|
||||||
|
client.Client
|
||||||
|
callFunc client.CallFunc
|
||||||
|
opts Options
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClientWrapper create new client wrapper
|
||||||
|
func NewClientWrapper(opts ...Option) client.Wrapper {
|
||||||
|
return func(c client.Client) client.Client {
|
||||||
|
handler := &wrapper{
|
||||||
|
opts: NewOptions(opts...),
|
||||||
|
Client: c,
|
||||||
|
}
|
||||||
|
return handler
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCallWrapper create new call wrapper
|
||||||
|
func NewCallWrapper(opts ...Option) client.CallWrapper {
|
||||||
|
return func(fn client.CallFunc) client.CallFunc {
|
||||||
|
handler := &wrapper{
|
||||||
|
opts: NewOptions(opts...),
|
||||||
|
callFunc: fn,
|
||||||
|
}
|
||||||
|
return handler.CallFunc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapper) CallFunc(ctx context.Context, addr string, req client.Request, rsp interface{}, opts client.CallOptions) error {
|
||||||
|
endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
|
||||||
|
for _, ep := range w.opts.SkipEndpoints {
|
||||||
|
if ep == endpoint {
|
||||||
|
return w.callFunc(ctx, addr, req, rsp, opts)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
labels := make([]string, 0, 4)
|
||||||
|
labels = append(labels, labelEndpoint, endpoint)
|
||||||
|
|
||||||
|
w.opts.Meter.Counter(ClientRequestInflight, labels...).Inc()
|
||||||
|
ts := time.Now()
|
||||||
|
err := w.callFunc(ctx, addr, req, rsp, opts)
|
||||||
|
te := time.Since(ts)
|
||||||
|
w.opts.Meter.Counter(ClientRequestInflight, labels...).Dec()
|
||||||
|
|
||||||
|
w.opts.Meter.Summary(ClientRequestLatencyMicroseconds, labels...).Update(te.Seconds())
|
||||||
|
w.opts.Meter.Histogram(ClientRequestDurationSeconds, labels...).Update(te.Seconds())
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
labels = append(labels, labelStatus, labelSuccess)
|
||||||
|
} else {
|
||||||
|
labels = append(labels, labelStatus, labelFailure)
|
||||||
|
}
|
||||||
|
w.opts.Meter.Counter(ClientRequestTotal, labels...).Inc()
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {
|
||||||
|
endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
|
||||||
|
for _, ep := range w.opts.SkipEndpoints {
|
||||||
|
if ep == endpoint {
|
||||||
|
return w.Client.Call(ctx, req, rsp, opts...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
labels := make([]string, 0, 4)
|
||||||
|
labels = append(labels, labelEndpoint, endpoint)
|
||||||
|
|
||||||
|
w.opts.Meter.Counter(ClientRequestInflight, labels...).Inc()
|
||||||
|
ts := time.Now()
|
||||||
|
err := w.Client.Call(ctx, req, rsp, opts...)
|
||||||
|
te := time.Since(ts)
|
||||||
|
w.opts.Meter.Counter(ClientRequestInflight, labels...).Dec()
|
||||||
|
|
||||||
|
w.opts.Meter.Summary(ClientRequestLatencyMicroseconds, labels...).Update(te.Seconds())
|
||||||
|
w.opts.Meter.Histogram(ClientRequestDurationSeconds, labels...).Update(te.Seconds())
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
labels = append(labels, labelStatus, labelSuccess)
|
||||||
|
} else {
|
||||||
|
labels = append(labels, labelStatus, labelFailure)
|
||||||
|
}
|
||||||
|
w.opts.Meter.Counter(ClientRequestTotal, labels...).Inc()
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapper) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Stream, error) {
|
||||||
|
endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
|
||||||
|
for _, ep := range w.opts.SkipEndpoints {
|
||||||
|
if ep == endpoint {
|
||||||
|
return w.Client.Stream(ctx, req, opts...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
labels := make([]string, 0, 4)
|
||||||
|
labels = append(labels, labelEndpoint, endpoint)
|
||||||
|
|
||||||
|
w.opts.Meter.Counter(ClientRequestInflight, labels...).Inc()
|
||||||
|
ts := time.Now()
|
||||||
|
stream, err := w.Client.Stream(ctx, req, opts...)
|
||||||
|
te := time.Since(ts)
|
||||||
|
w.opts.Meter.Counter(ClientRequestInflight, labels...).Dec()
|
||||||
|
|
||||||
|
w.opts.Meter.Summary(ClientRequestLatencyMicroseconds, labels...).Update(te.Seconds())
|
||||||
|
w.opts.Meter.Histogram(ClientRequestDurationSeconds, labels...).Update(te.Seconds())
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
labels = append(labels, labelStatus, labelSuccess)
|
||||||
|
} else {
|
||||||
|
labels = append(labels, labelStatus, labelFailure)
|
||||||
|
}
|
||||||
|
w.opts.Meter.Counter(ClientRequestTotal, labels...).Inc()
|
||||||
|
|
||||||
|
return stream, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapper) Publish(ctx context.Context, p client.Message, opts ...client.PublishOption) error {
|
||||||
|
endpoint := p.Topic()
|
||||||
|
|
||||||
|
labels := make([]string, 0, 4)
|
||||||
|
labels = append(labels, labelEndpoint, endpoint)
|
||||||
|
|
||||||
|
w.opts.Meter.Counter(PublishMessageInflight, labels...).Inc()
|
||||||
|
ts := time.Now()
|
||||||
|
err := w.Client.Publish(ctx, p, opts...)
|
||||||
|
te := time.Since(ts)
|
||||||
|
w.opts.Meter.Counter(PublishMessageInflight, labels...).Dec()
|
||||||
|
|
||||||
|
w.opts.Meter.Summary(PublishMessageLatencyMicroseconds, labels...).Update(te.Seconds())
|
||||||
|
w.opts.Meter.Histogram(PublishMessageDurationSeconds, labels...).Update(te.Seconds())
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
labels = append(labels, labelStatus, labelSuccess)
|
||||||
|
} else {
|
||||||
|
labels = append(labels, labelStatus, labelFailure)
|
||||||
|
}
|
||||||
|
w.opts.Meter.Counter(PublishMessageTotal, labels...).Inc()
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewHandlerWrapper create new server handler wrapper
|
||||||
|
// deprecated
|
||||||
|
func NewHandlerWrapper(opts ...Option) server.HandlerWrapper {
|
||||||
|
handler := &wrapper{
|
||||||
|
opts: NewOptions(opts...),
|
||||||
|
}
|
||||||
|
return handler.HandlerFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewServerHandlerWrapper create new server handler wrapper
|
||||||
|
func NewServerHandlerWrapper(opts ...Option) server.HandlerWrapper {
|
||||||
|
handler := &wrapper{
|
||||||
|
opts: NewOptions(opts...),
|
||||||
|
}
|
||||||
|
return handler.HandlerFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapper) HandlerFunc(fn server.HandlerFunc) server.HandlerFunc {
|
||||||
|
return func(ctx context.Context, req server.Request, rsp interface{}) error {
|
||||||
|
endpoint := req.Service() + "." + req.Endpoint()
|
||||||
|
for _, ep := range w.opts.SkipEndpoints {
|
||||||
|
if ep == endpoint {
|
||||||
|
return fn(ctx, req, rsp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
labels := make([]string, 0, 4)
|
||||||
|
labels = append(labels, labelEndpoint, endpoint)
|
||||||
|
|
||||||
|
w.opts.Meter.Counter(ServerRequestInflight, labels...).Inc()
|
||||||
|
ts := time.Now()
|
||||||
|
err := fn(ctx, req, rsp)
|
||||||
|
te := time.Since(ts)
|
||||||
|
w.opts.Meter.Counter(ServerRequestInflight, labels...).Dec()
|
||||||
|
|
||||||
|
w.opts.Meter.Summary(ServerRequestLatencyMicroseconds, labels...).Update(te.Seconds())
|
||||||
|
w.opts.Meter.Histogram(ServerRequestDurationSeconds, labels...).Update(te.Seconds())
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
labels = append(labels, labelStatus, labelSuccess)
|
||||||
|
} else {
|
||||||
|
labels = append(labels, labelStatus, labelFailure)
|
||||||
|
}
|
||||||
|
w.opts.Meter.Counter(ServerRequestTotal, labels...).Inc()
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSubscriberWrapper create server subscribe wrapper
|
||||||
|
// deprecated
|
||||||
|
func NewSubscriberWrapper(opts ...Option) server.SubscriberWrapper {
|
||||||
|
handler := &wrapper{
|
||||||
|
opts: NewOptions(opts...),
|
||||||
|
}
|
||||||
|
return handler.SubscriberFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewServerSubscriberWrapper(opts ...Option) server.SubscriberWrapper {
|
||||||
|
handler := &wrapper{
|
||||||
|
opts: NewOptions(opts...),
|
||||||
|
}
|
||||||
|
return handler.SubscriberFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *wrapper) SubscriberFunc(fn server.SubscriberFunc) server.SubscriberFunc {
|
||||||
|
return func(ctx context.Context, msg server.Message) error {
|
||||||
|
endpoint := msg.Topic()
|
||||||
|
|
||||||
|
labels := make([]string, 0, 4)
|
||||||
|
labels = append(labels, labelEndpoint, endpoint)
|
||||||
|
|
||||||
|
w.opts.Meter.Counter(SubscribeMessageInflight, labels...).Inc()
|
||||||
|
ts := time.Now()
|
||||||
|
err := fn(ctx, msg)
|
||||||
|
te := time.Since(ts)
|
||||||
|
w.opts.Meter.Counter(SubscribeMessageInflight, labels...).Dec()
|
||||||
|
|
||||||
|
w.opts.Meter.Summary(SubscribeMessageLatencyMicroseconds, labels...).Update(te.Seconds())
|
||||||
|
w.opts.Meter.Histogram(SubscribeMessageDurationSeconds, labels...).Update(te.Seconds())
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
labels = append(labels, labelStatus, labelSuccess)
|
||||||
|
} else {
|
||||||
|
labels = append(labels, labelStatus, labelFailure)
|
||||||
|
}
|
||||||
|
w.opts.Meter.Counter(SubscribeMessageTotal, labels...).Inc()
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
94
micro.go
94
micro.go
@ -1,94 +0,0 @@
|
|||||||
package micro
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
|
|
||||||
"go.unistack.org/micro/v3/broker"
|
|
||||||
"go.unistack.org/micro/v3/client"
|
|
||||||
"go.unistack.org/micro/v3/codec"
|
|
||||||
"go.unistack.org/micro/v3/flow"
|
|
||||||
"go.unistack.org/micro/v3/fsm"
|
|
||||||
"go.unistack.org/micro/v3/logger"
|
|
||||||
"go.unistack.org/micro/v3/meter"
|
|
||||||
"go.unistack.org/micro/v3/register"
|
|
||||||
"go.unistack.org/micro/v3/resolver"
|
|
||||||
"go.unistack.org/micro/v3/router"
|
|
||||||
"go.unistack.org/micro/v3/selector"
|
|
||||||
"go.unistack.org/micro/v3/server"
|
|
||||||
"go.unistack.org/micro/v3/store"
|
|
||||||
"go.unistack.org/micro/v3/sync"
|
|
||||||
"go.unistack.org/micro/v3/tracer"
|
|
||||||
)
|
|
||||||
|
|
||||||
func As(b any, target any) bool {
|
|
||||||
if b == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if target == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
val := reflect.ValueOf(target)
|
|
||||||
typ := val.Type()
|
|
||||||
if typ.Kind() != reflect.Ptr || val.IsNil() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
targetType := typ.Elem()
|
|
||||||
if targetType.Kind() != reflect.Interface {
|
|
||||||
switch {
|
|
||||||
case targetType.Implements(brokerType):
|
|
||||||
break
|
|
||||||
case targetType.Implements(loggerType):
|
|
||||||
break
|
|
||||||
case targetType.Implements(clientType):
|
|
||||||
break
|
|
||||||
case targetType.Implements(serverType):
|
|
||||||
break
|
|
||||||
case targetType.Implements(codecType):
|
|
||||||
break
|
|
||||||
case targetType.Implements(flowType):
|
|
||||||
break
|
|
||||||
case targetType.Implements(fsmType):
|
|
||||||
break
|
|
||||||
case targetType.Implements(meterType):
|
|
||||||
break
|
|
||||||
case targetType.Implements(registerType):
|
|
||||||
break
|
|
||||||
case targetType.Implements(resolverType):
|
|
||||||
break
|
|
||||||
case targetType.Implements(selectorType):
|
|
||||||
break
|
|
||||||
case targetType.Implements(storeType):
|
|
||||||
break
|
|
||||||
case targetType.Implements(syncType):
|
|
||||||
break
|
|
||||||
case targetType.Implements(serviceType):
|
|
||||||
break
|
|
||||||
case targetType.Implements(routerType):
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if reflect.TypeOf(b).AssignableTo(targetType) {
|
|
||||||
val.Elem().Set(reflect.ValueOf(b))
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
var brokerType = reflect.TypeOf((*broker.Broker)(nil)).Elem()
|
|
||||||
var loggerType = reflect.TypeOf((*logger.Logger)(nil)).Elem()
|
|
||||||
var clientType = reflect.TypeOf((*client.Client)(nil)).Elem()
|
|
||||||
var serverType = reflect.TypeOf((*server.Server)(nil)).Elem()
|
|
||||||
var codecType = reflect.TypeOf((*codec.Codec)(nil)).Elem()
|
|
||||||
var flowType = reflect.TypeOf((*flow.Flow)(nil)).Elem()
|
|
||||||
var fsmType = reflect.TypeOf((*fsm.FSM)(nil)).Elem()
|
|
||||||
var meterType = reflect.TypeOf((*meter.Meter)(nil)).Elem()
|
|
||||||
var registerType = reflect.TypeOf((*register.Register)(nil)).Elem()
|
|
||||||
var resolverType = reflect.TypeOf((*resolver.Resolver)(nil)).Elem()
|
|
||||||
var routerType = reflect.TypeOf((*router.Router)(nil)).Elem()
|
|
||||||
var selectorType = reflect.TypeOf((*selector.Selector)(nil)).Elem()
|
|
||||||
var storeType = reflect.TypeOf((*store.Store)(nil)).Elem()
|
|
||||||
var syncType = reflect.TypeOf((*sync.Sync)(nil)).Elem()
|
|
||||||
var tracerType = reflect.TypeOf((*tracer.Tracer)(nil)).Elem()
|
|
||||||
var serviceType = reflect.TypeOf((*Service)(nil)).Elem()
|
|
115
micro_test.go
115
micro_test.go
@ -1,115 +0,0 @@
|
|||||||
package micro
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"go.unistack.org/micro/v3/broker"
|
|
||||||
"go.unistack.org/micro/v3/fsm"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestAs(t *testing.T) {
|
|
||||||
var b *bro
|
|
||||||
broTarget := &bro{name: "kafka"}
|
|
||||||
fsmTarget := &fsmT{name: "fsm"}
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
b any
|
|
||||||
target any
|
|
||||||
match bool
|
|
||||||
want any
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
broTarget,
|
|
||||||
&b,
|
|
||||||
true,
|
|
||||||
broTarget,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
nil,
|
|
||||||
&b,
|
|
||||||
false,
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fsmTarget,
|
|
||||||
&b,
|
|
||||||
false,
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for i, tc := range testCases {
|
|
||||||
name := fmt.Sprintf("%d:As(Errorf(..., %v), %v)", i, tc.b, tc.target)
|
|
||||||
// Clear the target pointer, in case it was set in a previous test.
|
|
||||||
rtarget := reflect.ValueOf(tc.target)
|
|
||||||
rtarget.Elem().Set(reflect.Zero(reflect.TypeOf(tc.target).Elem()))
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
match := As(tc.b, tc.target)
|
|
||||||
if match != tc.match {
|
|
||||||
t.Fatalf("match: got %v; want %v", match, tc.match)
|
|
||||||
}
|
|
||||||
if !match {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if got := rtarget.Elem().Interface(); got != tc.want {
|
|
||||||
t.Fatalf("got %#v, want %#v", got, tc.want)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type bro struct {
|
|
||||||
name string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *bro) Name() string { return p.name }
|
|
||||||
|
|
||||||
func (p *bro) Init(opts ...broker.Option) error { return nil }
|
|
||||||
|
|
||||||
// Options returns broker options
|
|
||||||
func (p *bro) Options() broker.Options { return broker.Options{} }
|
|
||||||
|
|
||||||
// Address return configured address
|
|
||||||
func (p *bro) Address() string { return "" }
|
|
||||||
|
|
||||||
// Connect connects to broker
|
|
||||||
func (p *bro) Connect(ctx context.Context) error { return nil }
|
|
||||||
|
|
||||||
// Disconnect disconnect from broker
|
|
||||||
func (p *bro) Disconnect(ctx context.Context) error { return nil }
|
|
||||||
|
|
||||||
// Publish message, msg can be single broker.Message or []broker.Message
|
|
||||||
func (p *bro) Publish(ctx context.Context, topic string, msg *broker.Message, opts ...broker.PublishOption) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// BatchPublish messages to broker with multiple topics
|
|
||||||
func (p *bro) BatchPublish(ctx context.Context, msgs []*broker.Message, opts ...broker.PublishOption) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// BatchSubscribe subscribes to topic messages via handler
|
|
||||||
func (p *bro) BatchSubscribe(ctx context.Context, topic string, h broker.BatchHandler, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subscribe subscribes to topic message via handler
|
|
||||||
func (p *bro) Subscribe(ctx context.Context, topic string, handler broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// String type of broker
|
|
||||||
func (p *bro) String() string { return p.name }
|
|
||||||
|
|
||||||
type fsmT struct {
|
|
||||||
name string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *fsmT) Start(ctx context.Context, a interface{}, o ...Option) (interface{}, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
func (f *fsmT) Current() string { return f.name }
|
|
||||||
func (f *fsmT) Reset() {}
|
|
||||||
func (f *fsmT) State(s string, sf fsm.StateFunc) {}
|
|
@ -1,4 +1,4 @@
|
|||||||
package mtls
|
package mtls // import "go.unistack.org/micro/v3/mtls"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Package network is for creating internetworks
|
// Package network is for creating internetworks
|
||||||
package network
|
package network // import "go.unistack.org/micro/v3/network"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"go.unistack.org/micro/v3/client"
|
"go.unistack.org/micro/v3/client"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Package transport is an interface for synchronous connection based communication
|
// Package transport is an interface for synchronous connection based communication
|
||||||
package transport
|
package transport // import "go.unistack.org/micro/v3/network/transport"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Package broker is a tunnel broker
|
// Package broker is a tunnel broker
|
||||||
package broker
|
package broker // import "go.unistack.org/micro/v3/network/tunnel/broker"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@ -177,12 +177,12 @@ func (t *tunBatchSubscriber) run() {
|
|||||||
// receive message
|
// receive message
|
||||||
m := new(transport.Message)
|
m := new(transport.Message)
|
||||||
if err := c.Recv(m); err != nil {
|
if err := c.Recv(m); err != nil {
|
||||||
if logger.DefaultLogger.V(logger.ErrorLevel) {
|
if logger.V(logger.ErrorLevel) {
|
||||||
logger.DefaultLogger.Error(t.opts.Context, err.Error(), err)
|
logger.Error(t.opts.Context, err.Error())
|
||||||
}
|
}
|
||||||
if err = c.Close(); err != nil {
|
if err = c.Close(); err != nil {
|
||||||
if logger.DefaultLogger.V(logger.ErrorLevel) {
|
if logger.V(logger.ErrorLevel) {
|
||||||
logger.DefaultLogger.Error(t.opts.Context, err.Error(), err)
|
logger.Error(t.opts.Context, err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
@ -222,12 +222,12 @@ func (t *tunSubscriber) run() {
|
|||||||
// receive message
|
// receive message
|
||||||
m := new(transport.Message)
|
m := new(transport.Message)
|
||||||
if err := c.Recv(m); err != nil {
|
if err := c.Recv(m); err != nil {
|
||||||
if logger.DefaultLogger.V(logger.ErrorLevel) {
|
if logger.V(logger.ErrorLevel) {
|
||||||
logger.DefaultLogger.Error(t.opts.Context, err.Error(), err)
|
logger.Error(t.opts.Context, err.Error())
|
||||||
}
|
}
|
||||||
if err = c.Close(); err != nil {
|
if err = c.Close(); err != nil {
|
||||||
if logger.DefaultLogger.V(logger.ErrorLevel) {
|
if logger.V(logger.ErrorLevel) {
|
||||||
logger.DefaultLogger.Error(t.opts.Context, err.Error(), err)
|
logger.Error(t.opts.Context, err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
@ -305,10 +305,6 @@ func (t *tunEvent) SetError(err error) {
|
|||||||
t.err = err
|
t.err = err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tunEvent) Context() context.Context {
|
|
||||||
return context.TODO()
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBroker returns new tunnel broker
|
// NewBroker returns new tunnel broker
|
||||||
func NewBroker(opts ...broker.Option) (broker.Broker, error) {
|
func NewBroker(opts ...broker.Option) (broker.Broker, error) {
|
||||||
options := broker.NewOptions(opts...)
|
options := broker.NewOptions(opts...)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Package transport provides a tunnel transport
|
// Package transport provides a tunnel transport
|
||||||
package transport
|
package transport // import "go.unistack.org/micro/v3/network/tunnel/transport"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Package tunnel provides gre network tunnelling
|
// Package tunnel provides gre network tunnelling
|
||||||
package tunnel
|
package tunnel // import "go.unistack.org/micro/v3/network/transport/tunnel"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
13
options.go
13
options.go
@ -211,7 +211,6 @@ func Stores(s ...store.Store) Option {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Logger set the logger to use
|
// Logger set the logger to use
|
||||||
//
|
|
||||||
//nolint:gocyclo
|
//nolint:gocyclo
|
||||||
func Logger(l logger.Logger, opts ...LoggerOption) Option {
|
func Logger(l logger.Logger, opts ...LoggerOption) Option {
|
||||||
return func(o *Options) error {
|
return func(o *Options) error {
|
||||||
@ -269,7 +268,15 @@ func Logger(l logger.Logger, opts ...LoggerOption) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for _, mtr := range o.Meters {
|
||||||
|
for _, or := range lopts.meters {
|
||||||
|
if mtr.Name() == or || all {
|
||||||
|
if err = mtr.Init(meter.Logger(l)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
for _, trc := range o.Tracers {
|
for _, trc := range o.Tracers {
|
||||||
for _, ot := range lopts.tracers {
|
for _, ot := range lopts.tracers {
|
||||||
if trc.Name() == ot || all {
|
if trc.Name() == ot || all {
|
||||||
@ -322,7 +329,6 @@ func Meters(m ...meter.Meter) Option {
|
|||||||
|
|
||||||
// Register sets the register for the service
|
// Register sets the register for the service
|
||||||
// and the underlying components
|
// and the underlying components
|
||||||
//
|
|
||||||
//nolint:gocyclo
|
//nolint:gocyclo
|
||||||
func Register(r register.Register, opts ...RegisterOption) Option {
|
func Register(r register.Register, opts ...RegisterOption) Option {
|
||||||
return func(o *Options) error {
|
return func(o *Options) error {
|
||||||
@ -397,7 +403,6 @@ func RegisterBroker(n string) RegisterOption {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Tracer sets the tracer
|
// Tracer sets the tracer
|
||||||
//
|
|
||||||
//nolint:gocyclo
|
//nolint:gocyclo
|
||||||
func Tracer(t tracer.Tracer, opts ...TracerOption) Option {
|
func Tracer(t tracer.Tracer, opts ...TracerOption) Option {
|
||||||
return func(o *Options) error {
|
return func(o *Options) error {
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
package options
|
|
||||||
|
|
||||||
// Hook func interface
|
|
||||||
type Hook interface{}
|
|
||||||
|
|
||||||
// Hooks func slice
|
|
||||||
type Hooks []Hook
|
|
||||||
|
|
||||||
// Append is used to add hooks
|
|
||||||
func (hs *Hooks) Append(h ...Hook) {
|
|
||||||
*hs = append(*hs, h...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace is used to set hooks
|
|
||||||
func (hs *Hooks) Replace(h ...Hook) {
|
|
||||||
*hs = h
|
|
||||||
}
|
|
||||||
|
|
||||||
// EachNext is used to itearate over hooks forward
|
|
||||||
func (hs *Hooks) EachNext(fn func(Hook)) {
|
|
||||||
for idx := 0; idx < len(*hs); idx++ {
|
|
||||||
fn((*hs)[idx])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// EachPrev is used to iterate over hooks backward
|
|
||||||
func (hs *Hooks) EachPrev(fn func(Hook)) {
|
|
||||||
for idx := len(*hs) - 1; idx >= 0; idx-- {
|
|
||||||
fn((*hs)[idx])
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
package options
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestHooks_Append(t *testing.T) {
|
|
||||||
fn1 := func() {}
|
|
||||||
fn2 := func() {}
|
|
||||||
hs := &Hooks{}
|
|
||||||
hs.Append(fn1, fn2)
|
|
||||||
if len(*hs) != 2 {
|
|
||||||
t.Fatalf("unexpected Append error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHooks_Replace(t *testing.T) {
|
|
||||||
fn1 := func() {}
|
|
||||||
fn2 := func() {}
|
|
||||||
hs := &Hooks{}
|
|
||||||
hs.Append(fn1, fn2, fn1)
|
|
||||||
if len(*hs) != 3 {
|
|
||||||
t.Fatalf("unexpected Append error")
|
|
||||||
}
|
|
||||||
hs.Replace(fn1, fn2)
|
|
||||||
if len(*hs) != 2 {
|
|
||||||
t.Fatalf("unexpected Replace error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHooks_EachNext(t *testing.T) {
|
|
||||||
n := 5
|
|
||||||
fn1 := func() {
|
|
||||||
n *= 2
|
|
||||||
}
|
|
||||||
fn2 := func() {
|
|
||||||
n -= 10
|
|
||||||
}
|
|
||||||
hs := &Hooks{}
|
|
||||||
hs.Append(fn1, fn2)
|
|
||||||
|
|
||||||
hs.EachNext(func(h Hook) {
|
|
||||||
h.(func())()
|
|
||||||
})
|
|
||||||
if n != 0 {
|
|
||||||
t.Fatalf("unexpected EachNext")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHooks_EachPrev(t *testing.T) {
|
|
||||||
n := 5
|
|
||||||
fn1 := func() {
|
|
||||||
n *= 2
|
|
||||||
}
|
|
||||||
fn2 := func() {
|
|
||||||
n -= 10
|
|
||||||
}
|
|
||||||
hs := &Hooks{}
|
|
||||||
hs.Append(fn2, fn1)
|
|
||||||
|
|
||||||
hs.EachPrev(func(h Hook) {
|
|
||||||
h.(func())()
|
|
||||||
})
|
|
||||||
if n != 0 {
|
|
||||||
t.Fatalf("unexpected EachPrev")
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,5 @@
|
|||||||
// Package http enables the http profiler
|
// Package http enables the http profiler
|
||||||
package http
|
package http // import "go.unistack.org/micro/v3/profiler/http"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Package pprof provides a pprof profiler which writes output to /tmp/[name].{cpu,mem}.pprof
|
// Package pprof provides a pprof profiler which writes output to /tmp/[name].{cpu,mem}.pprof
|
||||||
package pprof
|
package pprof // import "go.unistack.org/micro/v3/profiler/pprof"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Package profiler is for profilers
|
// Package profiler is for profilers
|
||||||
package profiler
|
package profiler // import "go.unistack.org/micro/v3/profiler"
|
||||||
|
|
||||||
// Profiler interface
|
// Profiler interface
|
||||||
type Profiler interface {
|
type Profiler interface {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Package proxy is a transparent proxy built on the micro/server
|
// Package proxy is a transparent proxy built on the micro/server
|
||||||
package proxy
|
package proxy // import "go.unistack.org/micro/v3/proxy"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -2,12 +2,10 @@ package register
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.unistack.org/micro/v3/logger"
|
"go.unistack.org/micro/v3/logger"
|
||||||
"go.unistack.org/micro/v3/register"
|
|
||||||
"go.unistack.org/micro/v3/util/id"
|
"go.unistack.org/micro/v3/util/id"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -18,7 +16,7 @@ var (
|
|||||||
|
|
||||||
type node struct {
|
type node struct {
|
||||||
LastSeen time.Time
|
LastSeen time.Time
|
||||||
*register.Node
|
*Node
|
||||||
TTL time.Duration
|
TTL time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,23 +25,23 @@ type record struct {
|
|||||||
Version string
|
Version string
|
||||||
Metadata map[string]string
|
Metadata map[string]string
|
||||||
Nodes map[string]*node
|
Nodes map[string]*node
|
||||||
Endpoints []*register.Endpoint
|
Endpoints []*Endpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
type memory struct {
|
type memory struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
records map[string]services
|
records map[string]services
|
||||||
watchers map[string]*watcher
|
watchers map[string]*watcher
|
||||||
opts register.Options
|
opts Options
|
||||||
}
|
}
|
||||||
|
|
||||||
// services is a KV map with service name as the key and a map of records as the value
|
// services is a KV map with service name as the key and a map of records as the value
|
||||||
type services map[string]map[string]*record
|
type services map[string]map[string]*record
|
||||||
|
|
||||||
// NewRegister returns an initialized in-memory register
|
// NewRegister returns an initialized in-memory register
|
||||||
func NewRegister(opts ...register.Option) register.Register {
|
func NewRegister(opts ...Option) Register {
|
||||||
r := &memory{
|
r := &memory{
|
||||||
opts: register.NewOptions(opts...),
|
opts: NewOptions(opts...),
|
||||||
records: make(map[string]services),
|
records: make(map[string]services),
|
||||||
watchers: make(map[string]*watcher),
|
watchers: make(map[string]*watcher),
|
||||||
}
|
}
|
||||||
@ -65,7 +63,7 @@ func (m *memory) ttlPrune() {
|
|||||||
for id, n := range record.Nodes {
|
for id, n := range record.Nodes {
|
||||||
if n.TTL != 0 && time.Since(n.LastSeen) > n.TTL {
|
if n.TTL != 0 && time.Since(n.LastSeen) > n.TTL {
|
||||||
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.Debugf(m.opts.Context, "Register TTL expired for node %s of service %s", n.ID, service)
|
||||||
}
|
}
|
||||||
delete(m.records[domain][service][version].Nodes, id)
|
delete(m.records[domain][service][version].Nodes, id)
|
||||||
}
|
}
|
||||||
@ -77,7 +75,7 @@ func (m *memory) ttlPrune() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memory) sendEvent(r *register.Result) {
|
func (m *memory) sendEvent(r *Result) {
|
||||||
m.RLock()
|
m.RLock()
|
||||||
watchers := make([]*watcher, 0, len(m.watchers))
|
watchers := make([]*watcher, 0, len(m.watchers))
|
||||||
for _, w := range m.watchers {
|
for _, w := range m.watchers {
|
||||||
@ -108,7 +106,7 @@ func (m *memory) Disconnect(ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memory) Init(opts ...register.Option) error {
|
func (m *memory) Init(opts ...Option) error {
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&m.opts)
|
o(&m.opts)
|
||||||
}
|
}
|
||||||
@ -120,15 +118,15 @@ func (m *memory) Init(opts ...register.Option) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memory) Options() register.Options {
|
func (m *memory) Options() Options {
|
||||||
return m.opts
|
return m.opts
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memory) Register(ctx context.Context, s *register.Service, opts ...register.RegisterOption) error {
|
func (m *memory) Register(ctx context.Context, s *Service, opts ...RegisterOption) error {
|
||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
|
|
||||||
options := register.NewRegisterOptions(opts...)
|
options := NewRegisterOptions(opts...)
|
||||||
|
|
||||||
// get the services for this domain from the register
|
// get the services for this domain from the register
|
||||||
srvs, ok := m.records[options.Domain]
|
srvs, ok := m.records[options.Domain]
|
||||||
@ -152,10 +150,10 @@ func (m *memory) Register(ctx context.Context, s *register.Service, opts ...regi
|
|||||||
if _, ok := srvs[s.Name][s.Version]; !ok {
|
if _, ok := srvs[s.Name][s.Version]; !ok {
|
||||||
srvs[s.Name][s.Version] = r
|
srvs[s.Name][s.Version] = r
|
||||||
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.Debugf(m.opts.Context, "Register added new service: %s, version: %s", s.Name, s.Version)
|
||||||
}
|
}
|
||||||
m.records[options.Domain] = srvs
|
m.records[options.Domain] = srvs
|
||||||
go m.sendEvent(®ister.Result{Action: "create", Service: s})
|
go m.sendEvent(&Result{Action: "create", Service: s})
|
||||||
}
|
}
|
||||||
|
|
||||||
var addedNodes bool
|
var addedNodes bool
|
||||||
@ -178,7 +176,7 @@ func (m *memory) Register(ctx context.Context, s *register.Service, opts ...regi
|
|||||||
|
|
||||||
// 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: &Node{
|
||||||
ID: n.ID,
|
ID: n.ID,
|
||||||
Address: n.Address,
|
Address: n.Address,
|
||||||
Metadata: metadata,
|
Metadata: metadata,
|
||||||
@ -192,14 +190,14 @@ func (m *memory) Register(ctx context.Context, s *register.Service, opts ...regi
|
|||||||
|
|
||||||
if addedNodes {
|
if addedNodes {
|
||||||
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.Debugf(m.opts.Context, "Register added new node to service: %s, version: %s", s.Name, s.Version)
|
||||||
}
|
}
|
||||||
go m.sendEvent(®ister.Result{Action: "update", Service: s})
|
go m.sendEvent(&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 {
|
||||||
if m.opts.Logger.V(logger.DebugLevel) {
|
if m.opts.Logger.V(logger.DebugLevel) {
|
||||||
m.opts.Logger.Debug(m.opts.Context, fmt.Sprintf("Updated registration for service: %s, version: %s", s.Name, s.Version))
|
m.opts.Logger.Debugf(m.opts.Context, "Updated registration for service: %s, version: %s", s.Name, s.Version)
|
||||||
}
|
}
|
||||||
srvs[s.Name][s.Version].Nodes[n.ID].TTL = options.TTL
|
srvs[s.Name][s.Version].Nodes[n.ID].TTL = options.TTL
|
||||||
srvs[s.Name][s.Version].Nodes[n.ID].LastSeen = time.Now()
|
srvs[s.Name][s.Version].Nodes[n.ID].LastSeen = time.Now()
|
||||||
@ -210,11 +208,11 @@ func (m *memory) Register(ctx context.Context, s *register.Service, opts ...regi
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memory) Deregister(ctx context.Context, s *register.Service, opts ...register.DeregisterOption) error {
|
func (m *memory) Deregister(ctx context.Context, s *Service, opts ...DeregisterOption) error {
|
||||||
m.Lock()
|
m.Lock()
|
||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
|
|
||||||
options := register.NewDeregisterOptions(opts...)
|
options := NewDeregisterOptions(opts...)
|
||||||
|
|
||||||
// domain is set in metadata so it can be passed to watchers
|
// domain is set in metadata so it can be passed to watchers
|
||||||
if s.Metadata == nil {
|
if s.Metadata == nil {
|
||||||
@ -244,7 +242,7 @@ func (m *memory) Deregister(ctx context.Context, s *register.Service, opts ...re
|
|||||||
for _, n := range s.Nodes {
|
for _, n := range s.Nodes {
|
||||||
if _, ok := version.Nodes[n.ID]; ok {
|
if _, ok := version.Nodes[n.ID]; ok {
|
||||||
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 node from service: %s, version: %s", s.Name, s.Version))
|
m.opts.Logger.Debugf(m.opts.Context, "Register removed node from service: %s, version: %s", s.Name, s.Version)
|
||||||
}
|
}
|
||||||
delete(version.Nodes, n.ID)
|
delete(version.Nodes, n.ID)
|
||||||
}
|
}
|
||||||
@ -254,7 +252,7 @@ func (m *memory) Deregister(ctx context.Context, s *register.Service, opts ...re
|
|||||||
// is cleanup
|
// is cleanup
|
||||||
if len(version.Nodes) > 0 {
|
if len(version.Nodes) > 0 {
|
||||||
m.records[options.Domain][s.Name][s.Version] = version
|
m.records[options.Domain][s.Name][s.Version] = version
|
||||||
go m.sendEvent(®ister.Result{Action: "update", Service: s})
|
go m.sendEvent(&Result{Action: "update", Service: s})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,38 +260,38 @@ func (m *memory) Deregister(ctx context.Context, s *register.Service, opts ...re
|
|||||||
// register and exit
|
// register and exit
|
||||||
if len(versions) == 1 {
|
if len(versions) == 1 {
|
||||||
delete(m.records[options.Domain], s.Name)
|
delete(m.records[options.Domain], s.Name)
|
||||||
go m.sendEvent(®ister.Result{Action: "delete", Service: s})
|
go m.sendEvent(&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.Debugf(m.opts.Context, "Register removed service: %s", s.Name)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// there are other versions of the service running, so only remove this version of it
|
// there are other versions of the service running, so only remove this version of it
|
||||||
delete(m.records[options.Domain][s.Name], s.Version)
|
delete(m.records[options.Domain][s.Name], s.Version)
|
||||||
go m.sendEvent(®ister.Result{Action: "delete", Service: s})
|
go m.sendEvent(&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.Debugf(m.opts.Context, "Register removed service: %s, version: %s", s.Name, s.Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memory) LookupService(ctx context.Context, name string, opts ...register.LookupOption) ([]*register.Service, error) {
|
func (m *memory) LookupService(ctx context.Context, name string, opts ...LookupOption) ([]*Service, error) {
|
||||||
options := register.NewLookupOptions(opts...)
|
options := NewLookupOptions(opts...)
|
||||||
|
|
||||||
// if it's a wildcard domain, return from all domains
|
// if it's a wildcard domain, return from all domains
|
||||||
if options.Domain == register.WildcardDomain {
|
if options.Domain == WildcardDomain {
|
||||||
m.RLock()
|
m.RLock()
|
||||||
recs := m.records
|
recs := m.records
|
||||||
m.RUnlock()
|
m.RUnlock()
|
||||||
|
|
||||||
var services []*register.Service
|
var services []*Service
|
||||||
|
|
||||||
for domain := range recs {
|
for domain := range recs {
|
||||||
srvs, err := m.LookupService(ctx, name, append(opts, register.LookupDomain(domain))...)
|
srvs, err := m.LookupService(ctx, name, append(opts, LookupDomain(domain))...)
|
||||||
if err == register.ErrNotFound {
|
if err == ErrNotFound {
|
||||||
continue
|
continue
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -302,7 +300,7 @@ func (m *memory) LookupService(ctx context.Context, name string, opts ...registe
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(services) == 0 {
|
if len(services) == 0 {
|
||||||
return nil, register.ErrNotFound
|
return nil, ErrNotFound
|
||||||
}
|
}
|
||||||
return services, nil
|
return services, nil
|
||||||
}
|
}
|
||||||
@ -313,17 +311,17 @@ func (m *memory) LookupService(ctx context.Context, name string, opts ...registe
|
|||||||
// check the domain exists
|
// check the domain exists
|
||||||
services, ok := m.records[options.Domain]
|
services, ok := m.records[options.Domain]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, register.ErrNotFound
|
return nil, ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
// check the service exists
|
// check the service exists
|
||||||
versions, ok := services[name]
|
versions, ok := services[name]
|
||||||
if !ok || len(versions) == 0 {
|
if !ok || len(versions) == 0 {
|
||||||
return nil, register.ErrNotFound
|
return nil, ErrNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
// serialize the response
|
// serialize the response
|
||||||
result := make([]*register.Service, len(versions))
|
result := make([]*Service, len(versions))
|
||||||
|
|
||||||
var i int
|
var i int
|
||||||
|
|
||||||
@ -335,19 +333,19 @@ func (m *memory) LookupService(ctx context.Context, name string, opts ...registe
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memory) ListServices(ctx context.Context, opts ...register.ListOption) ([]*register.Service, error) {
|
func (m *memory) ListServices(ctx context.Context, opts ...ListOption) ([]*Service, error) {
|
||||||
options := register.NewListOptions(opts...)
|
options := NewListOptions(opts...)
|
||||||
|
|
||||||
// if it's a wildcard domain, list from all domains
|
// if it's a wildcard domain, list from all domains
|
||||||
if options.Domain == register.WildcardDomain {
|
if options.Domain == WildcardDomain {
|
||||||
m.RLock()
|
m.RLock()
|
||||||
recs := m.records
|
recs := m.records
|
||||||
m.RUnlock()
|
m.RUnlock()
|
||||||
|
|
||||||
var services []*register.Service
|
var services []*Service
|
||||||
|
|
||||||
for domain := range recs {
|
for domain := range recs {
|
||||||
srvs, err := m.ListServices(ctx, append(opts, register.ListDomain(domain))...)
|
srvs, err := m.ListServices(ctx, append(opts, ListDomain(domain))...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -363,11 +361,11 @@ func (m *memory) ListServices(ctx context.Context, opts ...register.ListOption)
|
|||||||
// ensure the domain exists
|
// ensure the domain exists
|
||||||
services, ok := m.records[options.Domain]
|
services, ok := m.records[options.Domain]
|
||||||
if !ok {
|
if !ok {
|
||||||
return make([]*register.Service, 0), nil
|
return make([]*Service, 0), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// serialize the result, each version counts as an individual service
|
// serialize the result, each version counts as an individual service
|
||||||
var result []*register.Service
|
var result []*Service
|
||||||
|
|
||||||
for _, service := range services {
|
for _, service := range services {
|
||||||
for _, version := range service {
|
for _, version := range service {
|
||||||
@ -378,16 +376,16 @@ func (m *memory) ListServices(ctx context.Context, opts ...register.ListOption)
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memory) Watch(ctx context.Context, opts ...register.WatchOption) (register.Watcher, error) {
|
func (m *memory) Watch(ctx context.Context, opts ...WatchOption) (Watcher, error) {
|
||||||
id, err := id.New()
|
id, err := id.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
wo := register.NewWatchOptions(opts...)
|
wo := NewWatchOptions(opts...)
|
||||||
// construct the watcher
|
// construct the watcher
|
||||||
w := &watcher{
|
w := &watcher{
|
||||||
exit: make(chan bool),
|
exit: make(chan bool),
|
||||||
res: make(chan *register.Result),
|
res: make(chan *Result),
|
||||||
id: id,
|
id: id,
|
||||||
wo: wo,
|
wo: wo,
|
||||||
}
|
}
|
||||||
@ -408,13 +406,13 @@ func (m *memory) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type watcher struct {
|
type watcher struct {
|
||||||
res chan *register.Result
|
res chan *Result
|
||||||
exit chan bool
|
exit chan bool
|
||||||
wo register.WatchOptions
|
wo WatchOptions
|
||||||
id string
|
id string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *watcher) Next() (*register.Result, error) {
|
func (m *watcher) Next() (*Result, error) {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case r := <-m.res:
|
case r := <-m.res:
|
||||||
@ -431,15 +429,15 @@ func (m *watcher) Next() (*register.Result, error) {
|
|||||||
if r.Service.Metadata != nil && len(r.Service.Metadata["domain"]) > 0 {
|
if r.Service.Metadata != nil && len(r.Service.Metadata["domain"]) > 0 {
|
||||||
domain = r.Service.Metadata["domain"]
|
domain = r.Service.Metadata["domain"]
|
||||||
} else {
|
} else {
|
||||||
domain = register.DefaultDomain
|
domain = DefaultDomain
|
||||||
}
|
}
|
||||||
|
|
||||||
// only send the event if watching the wildcard or this specific domain
|
// only send the event if watching the wildcard or this specific domain
|
||||||
if m.wo.Domain == register.WildcardDomain || m.wo.Domain == domain {
|
if m.wo.Domain == WildcardDomain || m.wo.Domain == domain {
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
case <-m.exit:
|
case <-m.exit:
|
||||||
return nil, register.ErrWatcherStopped
|
return nil, ErrWatcherStopped
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -453,7 +451,7 @@ func (m *watcher) Stop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func serviceToRecord(s *register.Service, ttl time.Duration) *record {
|
func serviceToRecord(s *Service, ttl time.Duration) *record {
|
||||||
metadata := make(map[string]string, len(s.Metadata))
|
metadata := make(map[string]string, len(s.Metadata))
|
||||||
for k, v := range s.Metadata {
|
for k, v := range s.Metadata {
|
||||||
metadata[k] = v
|
metadata[k] = v
|
||||||
@ -468,7 +466,7 @@ func serviceToRecord(s *register.Service, ttl time.Duration) *record {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
endpoints := make([]*register.Endpoint, len(s.Endpoints))
|
endpoints := make([]*Endpoint, len(s.Endpoints))
|
||||||
for i, e := range s.Endpoints {
|
for i, e := range s.Endpoints {
|
||||||
endpoints[i] = e
|
endpoints[i] = e
|
||||||
}
|
}
|
||||||
@ -482,7 +480,7 @@ func serviceToRecord(s *register.Service, ttl time.Duration) *record {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func recordToService(r *record, domain string) *register.Service {
|
func recordToService(r *record, domain string) *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
|
||||||
@ -491,14 +489,14 @@ func recordToService(r *record, domain string) *register.Service {
|
|||||||
// set the domain in metadata so it can be determined when a wildcard query is performed
|
// set the domain in metadata so it can be determined when a wildcard query is performed
|
||||||
metadata["domain"] = domain
|
metadata["domain"] = domain
|
||||||
|
|
||||||
endpoints := make([]*register.Endpoint, len(r.Endpoints))
|
endpoints := make([]*Endpoint, len(r.Endpoints))
|
||||||
for i, e := range r.Endpoints {
|
for i, e := range r.Endpoints {
|
||||||
md := make(map[string]string, len(e.Metadata))
|
md := make(map[string]string, len(e.Metadata))
|
||||||
for k, v := range e.Metadata {
|
for k, v := range e.Metadata {
|
||||||
md[k] = v
|
md[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
endpoints[i] = ®ister.Endpoint{
|
endpoints[i] = &Endpoint{
|
||||||
Name: e.Name,
|
Name: e.Name,
|
||||||
Request: e.Request,
|
Request: e.Request,
|
||||||
Response: e.Response,
|
Response: e.Response,
|
||||||
@ -506,7 +504,7 @@ func recordToService(r *record, domain string) *register.Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes := make([]*register.Node, len(r.Nodes))
|
nodes := make([]*Node, len(r.Nodes))
|
||||||
i := 0
|
i := 0
|
||||||
for _, n := range r.Nodes {
|
for _, n := range r.Nodes {
|
||||||
md := make(map[string]string, len(n.Metadata))
|
md := make(map[string]string, len(n.Metadata))
|
||||||
@ -514,7 +512,7 @@ func recordToService(r *record, domain string) *register.Service {
|
|||||||
md[k] = v
|
md[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes[i] = ®ister.Node{
|
nodes[i] = &Node{
|
||||||
ID: n.ID,
|
ID: n.ID,
|
||||||
Address: n.Address,
|
Address: n.Address,
|
||||||
Metadata: md,
|
Metadata: md,
|
||||||
@ -522,7 +520,7 @@ func recordToService(r *record, domain string) *register.Service {
|
|||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
return ®ister.Service{
|
return &Service{
|
||||||
Name: r.Name,
|
Name: r.Name,
|
||||||
Version: r.Version,
|
Version: r.Version,
|
||||||
Metadata: metadata,
|
Metadata: metadata,
|
@ -3,19 +3,18 @@ package register
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.unistack.org/micro/v3/register"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var testData = map[string][]*register.Service{
|
var testData = map[string][]*Service{
|
||||||
"foo": {
|
"foo": {
|
||||||
{
|
{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
Version: "1.0.0",
|
Version: "1.0.0",
|
||||||
Nodes: []*register.Node{
|
Nodes: []*Node{
|
||||||
{
|
{
|
||||||
ID: "foo-1.0.0-123",
|
ID: "foo-1.0.0-123",
|
||||||
Address: "localhost:9999",
|
Address: "localhost:9999",
|
||||||
@ -29,7 +28,7 @@ var testData = map[string][]*register.Service{
|
|||||||
{
|
{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
Version: "1.0.1",
|
Version: "1.0.1",
|
||||||
Nodes: []*register.Node{
|
Nodes: []*Node{
|
||||||
{
|
{
|
||||||
ID: "foo-1.0.1-321",
|
ID: "foo-1.0.1-321",
|
||||||
Address: "localhost:6666",
|
Address: "localhost:6666",
|
||||||
@ -39,7 +38,7 @@ var testData = map[string][]*register.Service{
|
|||||||
{
|
{
|
||||||
Name: "foo",
|
Name: "foo",
|
||||||
Version: "1.0.3",
|
Version: "1.0.3",
|
||||||
Nodes: []*register.Node{
|
Nodes: []*Node{
|
||||||
{
|
{
|
||||||
ID: "foo-1.0.3-345",
|
ID: "foo-1.0.3-345",
|
||||||
Address: "localhost:8888",
|
Address: "localhost:8888",
|
||||||
@ -51,7 +50,7 @@ var testData = map[string][]*register.Service{
|
|||||||
{
|
{
|
||||||
Name: "bar",
|
Name: "bar",
|
||||||
Version: "default",
|
Version: "default",
|
||||||
Nodes: []*register.Node{
|
Nodes: []*Node{
|
||||||
{
|
{
|
||||||
ID: "bar-1.0.0-123",
|
ID: "bar-1.0.0-123",
|
||||||
Address: "localhost:9999",
|
Address: "localhost:9999",
|
||||||
@ -65,7 +64,7 @@ var testData = map[string][]*register.Service{
|
|||||||
{
|
{
|
||||||
Name: "bar",
|
Name: "bar",
|
||||||
Version: "latest",
|
Version: "latest",
|
||||||
Nodes: []*register.Node{
|
Nodes: []*Node{
|
||||||
{
|
{
|
||||||
ID: "bar-1.0.1-321",
|
ID: "bar-1.0.1-321",
|
||||||
Address: "localhost:6666",
|
Address: "localhost:6666",
|
||||||
@ -80,7 +79,7 @@ func TestMemoryRegistry(t *testing.T) {
|
|||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
m := NewRegister()
|
m := NewRegister()
|
||||||
|
|
||||||
fn := func(k string, v []*register.Service) {
|
fn := func(k string, v []*Service) {
|
||||||
services, err := m.LookupService(ctx, k)
|
services, err := m.LookupService(ctx, k)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Unexpected error getting service %s: %v", k, err)
|
t.Errorf("Unexpected error getting service %s: %v", k, err)
|
||||||
@ -157,8 +156,8 @@ func TestMemoryRegistry(t *testing.T) {
|
|||||||
for _, v := range testData {
|
for _, v := range testData {
|
||||||
for _, service := range v {
|
for _, service := range v {
|
||||||
services, err := m.LookupService(ctx, service.Name)
|
services, err := m.LookupService(ctx, service.Name)
|
||||||
if err != register.ErrNotFound {
|
if err != ErrNotFound {
|
||||||
t.Errorf("Expected error: %v, got: %v", register.ErrNotFound, err)
|
t.Errorf("Expected error: %v, got: %v", ErrNotFound, err)
|
||||||
}
|
}
|
||||||
if len(services) != 0 {
|
if len(services) != 0 {
|
||||||
t.Errorf("Expected %d services for %s, got %d", 0, service.Name, len(services))
|
t.Errorf("Expected %d services for %s, got %d", 0, service.Name, len(services))
|
||||||
@ -173,7 +172,7 @@ func TestMemoryRegistryTTL(t *testing.T) {
|
|||||||
|
|
||||||
for _, v := range testData {
|
for _, v := range testData {
|
||||||
for _, service := range v {
|
for _, service := range v {
|
||||||
if err := m.Register(ctx, service, register.RegisterTTL(time.Millisecond)); err != nil {
|
if err := m.Register(ctx, service, RegisterTTL(time.Millisecond)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -202,15 +201,15 @@ func TestMemoryRegistryTTLConcurrent(t *testing.T) {
|
|||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
for _, v := range testData {
|
for _, v := range testData {
|
||||||
for _, service := range v {
|
for _, service := range v {
|
||||||
if err := m.Register(ctx, service, register.RegisterTTL(waitTime/2)); err != nil {
|
if err := m.Register(ctx, service, RegisterTTL(waitTime/2)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//if len(os.Getenv("IN_TRAVIS_CI")) == 0 {
|
if len(os.Getenv("IN_TRAVIS_CI")) == 0 {
|
||||||
// t.Logf("test will wait %v, then check TTL timeouts", waitTime)
|
t.Logf("test will wait %v, then check TTL timeouts", waitTime)
|
||||||
//}
|
}
|
||||||
|
|
||||||
errChan := make(chan error, concurrency)
|
errChan := make(chan error, concurrency)
|
||||||
syncChan := make(chan struct{})
|
syncChan := make(chan struct{})
|
||||||
@ -251,34 +250,34 @@ func TestMemoryWildcard(t *testing.T) {
|
|||||||
m := NewRegister()
|
m := NewRegister()
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
|
|
||||||
testSrv := ®ister.Service{Name: "foo", Version: "1.0.0"}
|
testSrv := &Service{Name: "foo", Version: "1.0.0"}
|
||||||
|
|
||||||
if err := m.Register(ctx, testSrv, register.RegisterDomain("one")); err != nil {
|
if err := m.Register(ctx, testSrv, RegisterDomain("one")); err != nil {
|
||||||
t.Fatalf("Register err: %v", err)
|
t.Fatalf("Register err: %v", err)
|
||||||
}
|
}
|
||||||
if err := m.Register(ctx, testSrv, register.RegisterDomain("two")); err != nil {
|
if err := m.Register(ctx, testSrv, RegisterDomain("two")); err != nil {
|
||||||
t.Fatalf("Register err: %v", err)
|
t.Fatalf("Register err: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if recs, err := m.ListServices(ctx, register.ListDomain("one")); err != nil {
|
if recs, err := m.ListServices(ctx, 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.ListDomain("*")); err != nil {
|
if recs, err := m.ListServices(ctx, 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.LookupDomain("one")); err != nil {
|
if recs, err := m.LookupService(ctx, testSrv.Name, 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.LookupDomain("*")); err != nil {
|
if recs, err := m.LookupService(ctx, testSrv.Name, 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))
|
||||||
@ -286,7 +285,7 @@ func TestMemoryWildcard(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestWatcher(t *testing.T) {
|
func TestWatcher(t *testing.T) {
|
||||||
testSrv := ®ister.Service{Name: "foo", Version: "1.0.0"}
|
testSrv := &Service{Name: "foo", Version: "1.0.0"}
|
||||||
|
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
m := NewRegister()
|
m := NewRegister()
|
@ -1,72 +0,0 @@
|
|||||||
package register
|
|
||||||
|
|
||||||
import "context"
|
|
||||||
|
|
||||||
type noop struct {
|
|
||||||
opts Options
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewRegister(opts ...Option) Register {
|
|
||||||
return &noop{
|
|
||||||
opts: NewOptions(opts...),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noop) Name() string {
|
|
||||||
return n.opts.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noop) Init(opts ...Option) error {
|
|
||||||
for _, o := range opts {
|
|
||||||
o(&n.opts)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noop) Options() Options {
|
|
||||||
return n.opts
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noop) Connect(ctx context.Context) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noop) Disconnect(ctx context.Context) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noop) Register(ctx context.Context, service *Service, option ...RegisterOption) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noop) Deregister(ctx context.Context, service *Service, option ...DeregisterOption) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noop) LookupService(ctx context.Context, s string, option ...LookupOption) ([]*Service, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noop) ListServices(ctx context.Context, option ...ListOption) ([]*Service, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noop) Watch(ctx context.Context, opts ...WatchOption) (Watcher, error) {
|
|
||||||
wOpts := NewWatchOptions(opts...)
|
|
||||||
|
|
||||||
return &watcher{wo: wOpts}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noop) String() string {
|
|
||||||
return "noop"
|
|
||||||
}
|
|
||||||
|
|
||||||
type watcher struct {
|
|
||||||
wo WatchOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *watcher) Next() (*Result, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *watcher) Stop() {}
|
|
@ -1,5 +1,5 @@
|
|||||||
// Package register is an interface for service discovery
|
// Package register is an interface for service discovery
|
||||||
package register
|
package register // import "go.unistack.org/micro/v3/register"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@ -18,7 +18,7 @@ var DefaultDomain = "micro"
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
// DefaultRegister is the global default register
|
// DefaultRegister is the global default register
|
||||||
DefaultRegister Register = NewRegister()
|
DefaultRegister = NewRegister()
|
||||||
// ErrNotFound returned when LookupService is called and no services found
|
// ErrNotFound returned when LookupService is called and no services found
|
||||||
ErrNotFound = errors.New("service not found")
|
ErrNotFound = errors.New("service not found")
|
||||||
// ErrWatcherStopped returned when when watcher is stopped
|
// ErrWatcherStopped returned when when watcher is stopped
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Package dns resolves names to dns records
|
// Package dns resolves names to dns records
|
||||||
package dns
|
package dns // import "go.unistack.org/micro/v3/resolver/dns"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Package dnssrv resolves names to dns srv records
|
// Package dnssrv resolves names to dns srv records
|
||||||
package dnssrv
|
package dnssrv // import "go.unistack.org/micro/v3/resolver/dnssrv"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Package http resolves names to network addresses using a http request
|
// Package http resolves names to network addresses using a http request
|
||||||
package http
|
package http // import "go.unistack.org/micro/v3/resolver/http"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Package noop is a noop resolver
|
// Package noop is a noop resolver
|
||||||
package noop
|
package noop // import "go.unistack.org/micro/v3/resolver/noop"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"go.unistack.org/micro/v3/resolver"
|
"go.unistack.org/micro/v3/resolver"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Package register resolves names using the micro register
|
// Package register resolves names using the micro register
|
||||||
package register
|
package register // import "go.unistack.org/micro/v3/resolver/registry"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Package resolver resolves network names to addresses
|
// Package resolver resolves network names to addresses
|
||||||
package resolver
|
package resolver // import "go.unistack.org/micro/v3/resolver"
|
||||||
|
|
||||||
// Resolver is network resolver. It's used to find network nodes
|
// Resolver is network resolver. It's used to find network nodes
|
||||||
// via the name to connect to. This is done based on Network.Name().
|
// via the name to connect to. This is done based on Network.Name().
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Package static is a static resolver
|
// Package static is a static resolver
|
||||||
package static
|
package static // import "go.unistack.org/micro/v3/resolver/static"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"go.unistack.org/micro/v3/resolver"
|
"go.unistack.org/micro/v3/resolver"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Package router provides a network routing control plane
|
// Package router provides a network routing control plane
|
||||||
package router
|
package router // import "go.unistack.org/micro/v3/router"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
@ -7,7 +7,7 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
// DefaultRouter is the global default router
|
// DefaultRouter is the global default router
|
||||||
DefaultRouter Router = NewRouter()
|
DefaultRouter = NewRouter()
|
||||||
// DefaultNetwork is default micro network
|
// DefaultNetwork is default micro network
|
||||||
DefaultNetwork = "micro"
|
DefaultNetwork = "micro"
|
||||||
// ErrRouteNotFound is returned when no route was found in the routing table
|
// ErrRouteNotFound is returned when no route was found in the routing table
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user