mirror of
https://github.com/zeromicro/go-zero.git
synced 2026-05-12 01:10:00 +08:00
Compare commits
122 Commits
tools/goct
...
v1.4.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
06e4914e41 | ||
|
|
9cadab2684 | ||
|
|
7fe2492009 | ||
|
|
22bdf0bbd5 | ||
|
|
c92a2d1b77 | ||
|
|
b21162d638 | ||
|
|
7c9ef3ca67 | ||
|
|
bbadbe0175 | ||
|
|
f9beab1095 | ||
|
|
de5c59aad3 | ||
|
|
36d3765c5c | ||
|
|
d326e6f813 | ||
|
|
ea52fe2e0d | ||
|
|
05a5de7c6d | ||
|
|
d4c9fd2aff | ||
|
|
776673d57d | ||
|
|
1b87f5e30d | ||
|
|
bc47959384 | ||
|
|
9f6d926455 | ||
|
|
f7a4e3a19e | ||
|
|
a515a3c735 | ||
|
|
6f6f1ae21f | ||
|
|
10f94ffcc2 | ||
|
|
f068062b13 | ||
|
|
799c118d95 | ||
|
|
74cc6b55e8 | ||
|
|
fc59aec2e7 | ||
|
|
7868667b4f | ||
|
|
773b59106b | ||
|
|
97f8667b71 | ||
|
|
b51339b69b | ||
|
|
38a73d7fbe | ||
|
|
e50689beed | ||
|
|
1bc138bd34 | ||
|
|
4b9066eda6 | ||
|
|
0c66e041b5 | ||
|
|
aa2be0163a | ||
|
|
ada2941e87 | ||
|
|
59c0013cd1 | ||
|
|
05737f6519 | ||
|
|
4f6a900fd4 | ||
|
|
63cfe60f1a | ||
|
|
e7acadb15d | ||
|
|
111e626a73 | ||
|
|
1a6d7b3ef6 | ||
|
|
2e1e4f3574 | ||
|
|
22d0a2120a | ||
|
|
68e15360c2 | ||
|
|
1b344a8851 | ||
|
|
d640544a40 | ||
|
|
e6aa6fc361 | ||
|
|
4c927624b0 | ||
|
|
0ea92b7280 | ||
|
|
2cde970c9e | ||
|
|
5061158bd6 | ||
|
|
9138056c01 | ||
|
|
0b1884b6bd | ||
|
|
1f6688e5c1 | ||
|
|
ae7f1aabdd | ||
|
|
b8664be2bb | ||
|
|
6e16a9647e | ||
|
|
bb0e76be47 | ||
|
|
27a20e1ed3 | ||
|
|
cbbbee0ace | ||
|
|
e9650d547b | ||
|
|
60160f56b8 | ||
|
|
05c2f313c7 | ||
|
|
f2a0f78288 | ||
|
|
3e96994b7b | ||
|
|
66c2a28e66 | ||
|
|
9672071b5d | ||
|
|
9581e8445a | ||
|
|
6ec8bc6655 | ||
|
|
d935c83a54 | ||
|
|
590d784800 | ||
|
|
784276b360 | ||
|
|
da80662b0f | ||
|
|
cfda972d50 | ||
|
|
6078bf1a04 | ||
|
|
ce638d26d9 | ||
|
|
422f401153 | ||
|
|
dfeef5e497 | ||
|
|
8c72136631 | ||
|
|
9d6c8f67f5 | ||
|
|
f70805ee60 | ||
|
|
a1466e1707 | ||
|
|
1b477bbef9 | ||
|
|
813625d995 | ||
|
|
15a2802f12 | ||
|
|
5d00dfb962 | ||
|
|
d9620bb072 | ||
|
|
d978563523 | ||
|
|
fb6d7e2fd2 | ||
|
|
2d60f0c65a | ||
|
|
5d4ae201d0 | ||
|
|
05007c86bb | ||
|
|
93584c6ca6 | ||
|
|
22bb7e95fd | ||
|
|
bebf6322ff | ||
|
|
36678f9023 | ||
|
|
90cdd61efc | ||
|
|
28166dedd6 | ||
|
|
0316b6e10e | ||
|
|
4cb68a034a | ||
|
|
847a396f1c | ||
|
|
c1babdf8b2 | ||
|
|
040c9e0954 | ||
|
|
1c85d39add | ||
|
|
4cd065f4f4 | ||
|
|
b9c97678bc | ||
|
|
5208def65a | ||
|
|
3b96dc1598 | ||
|
|
fa3f1bc19c | ||
|
|
8ed22eafdd | ||
|
|
05dd6bd743 | ||
|
|
9af1a42386 | ||
|
|
f3645e420e | ||
|
|
62abac0b7e | ||
|
|
6357e27418 | ||
|
|
1568c3be0e | ||
|
|
27e773fa1f | ||
|
|
d8e17be33e |
10
.github/workflows/go.yml
vendored
10
.github/workflows/go.yml
vendored
@@ -12,9 +12,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ^1.15
|
||||
go-version: ^1.16
|
||||
id: go
|
||||
|
||||
- name: Check out code into the Go module directory
|
||||
@@ -34,16 +34,16 @@ jobs:
|
||||
run: go test -race -coverprofile=coverage.txt -covermode=atomic ./...
|
||||
|
||||
- name: Codecov
|
||||
uses: codecov/codecov-action@v2
|
||||
uses: codecov/codecov-action@v3
|
||||
|
||||
test-win:
|
||||
name: Windows
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- name: Set up Go 1.x
|
||||
uses: actions/setup-go@v2
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ^1.15
|
||||
go-version: ^1.16
|
||||
|
||||
- name: Checkout codebase
|
||||
uses: actions/checkout@v3
|
||||
|
||||
2
.github/workflows/issue-translator.yml
vendored
2
.github/workflows/issue-translator.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: tomsun28/issues-translate-action@v2.6
|
||||
- uses: usthe/issues-translate-action@v2.7
|
||||
with:
|
||||
IS_MODIFY_TITLE: true
|
||||
# not require, default false, . Decide whether to modify the issue title
|
||||
|
||||
6
.github/workflows/issues.yml
vendored
6
.github/workflows/issues.yml
vendored
@@ -7,10 +7,10 @@ jobs:
|
||||
close-issues:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v3
|
||||
- uses: actions/stale@v6
|
||||
with:
|
||||
days-before-issue-stale: 30
|
||||
days-before-issue-close: 14
|
||||
days-before-issue-stale: 365
|
||||
days-before-issue-close: 90
|
||||
stale-issue-label: "stale"
|
||||
stale-issue-message: "This issue is stale because it has been open for 30 days with no activity."
|
||||
close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -22,6 +22,7 @@ go.work.sum
|
||||
|
||||
# gitlab ci
|
||||
.cache
|
||||
.golangci.yml
|
||||
|
||||
# vim auto backup file
|
||||
*~
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -48,7 +48,7 @@ type (
|
||||
|
||||
// NewRsaDecrypter returns a RsaDecrypter with the given file.
|
||||
func NewRsaDecrypter(file string) (RsaDecrypter, error) {
|
||||
content, err := ioutil.ReadFile(file)
|
||||
content, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -68,6 +68,24 @@ func (m *SafeMap) Get(key interface{}) (interface{}, bool) {
|
||||
return val, ok
|
||||
}
|
||||
|
||||
// Range calls f sequentially for each key and value present in the map.
|
||||
// If f returns false, range stops the iteration.
|
||||
func (m *SafeMap) Range(f func(key, val interface{}) bool) {
|
||||
m.lock.RLock()
|
||||
defer m.lock.RUnlock()
|
||||
|
||||
for k, v := range m.dirtyOld {
|
||||
if !f(k, v) {
|
||||
return
|
||||
}
|
||||
}
|
||||
for k, v := range m.dirtyNew {
|
||||
if !f(k, v) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set sets the value into m with the given key.
|
||||
func (m *SafeMap) Set(key, value interface{}) {
|
||||
m.lock.Lock()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package collection
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -107,3 +108,42 @@ func testSafeMapWithParameters(t *testing.T, size, exception int) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSafeMap_Range(t *testing.T) {
|
||||
const (
|
||||
size = 100000
|
||||
exception1 = 5
|
||||
exception2 = 500
|
||||
)
|
||||
|
||||
m := NewSafeMap()
|
||||
newMap := NewSafeMap()
|
||||
|
||||
for i := 0; i < size; i++ {
|
||||
m.Set(i, i)
|
||||
}
|
||||
for i := 0; i < size; i++ {
|
||||
if i%exception1 == 0 {
|
||||
m.Del(i)
|
||||
}
|
||||
}
|
||||
|
||||
for i := size; i < size<<1; i++ {
|
||||
m.Set(i, i)
|
||||
}
|
||||
for i := size; i < size<<1; i++ {
|
||||
if i%exception2 != 0 {
|
||||
m.Del(i)
|
||||
}
|
||||
}
|
||||
|
||||
var count int32
|
||||
m.Range(func(k, v interface{}) bool {
|
||||
atomic.AddInt32(&count, 1)
|
||||
newMap.Set(k, v)
|
||||
return true
|
||||
})
|
||||
assert.Equal(t, int(atomic.LoadInt32(&count)), m.Size())
|
||||
assert.Equal(t, m.dirtyNew, newMap.dirtyNew)
|
||||
assert.Equal(t, m.dirtyOld, newMap.dirtyOld)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package conf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
@@ -20,7 +19,7 @@ var loaders = map[string]func([]byte, interface{}) error{
|
||||
|
||||
// Load loads config into v from file, .json, .yaml and .yml are acceptable.
|
||||
func Load(file string, v interface{}, opts ...Option) error {
|
||||
content, err := ioutil.ReadFile(file)
|
||||
content, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
@@ -106,7 +105,6 @@ d = "abcd!@#112"
|
||||
assert.Equal(t, 1, val.B)
|
||||
assert.Equal(t, "2", val.C)
|
||||
assert.Equal(t, "abcd!@#112", val.D)
|
||||
|
||||
}
|
||||
|
||||
func TestConfigJsonEnv(t *testing.T) {
|
||||
@@ -146,12 +144,12 @@ func TestConfigJsonEnv(t *testing.T) {
|
||||
}
|
||||
|
||||
func createTempFile(ext, text string) (string, error) {
|
||||
tmpfile, err := ioutil.TempFile(os.TempDir(), hash.Md5Hex([]byte(text))+"*"+ext)
|
||||
tmpfile, err := os.CreateTemp(os.TempDir(), hash.Md5Hex([]byte(text))+"*"+ext)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(tmpfile.Name(), []byte(text), os.ModeTemporary); err != nil {
|
||||
if err := os.WriteFile(tmpfile.Name(), []byte(text), os.ModeTemporary); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package internal
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sync"
|
||||
)
|
||||
|
||||
@@ -37,7 +37,7 @@ func AddTLS(endpoints []string, certFile, certKeyFile, caFile string, insecureSk
|
||||
return err
|
||||
}
|
||||
|
||||
caData, err := ioutil.ReadFile(caFile)
|
||||
caData, err := os.ReadFile(caFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ func (c *cluster) handleWatchEvents(key string, events []*clientv3.Event) {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *cluster) load(cli EtcdClient, key string) {
|
||||
func (c *cluster) load(cli EtcdClient, key string) int64 {
|
||||
var resp *clientv3.GetResponse
|
||||
for {
|
||||
var err error
|
||||
@@ -232,6 +232,8 @@ func (c *cluster) load(cli EtcdClient, key string) {
|
||||
}
|
||||
|
||||
c.handleChanges(key, kvs)
|
||||
|
||||
return resp.Header.Revision
|
||||
}
|
||||
|
||||
func (c *cluster) monitor(key string, l UpdateListener) error {
|
||||
@@ -244,9 +246,9 @@ func (c *cluster) monitor(key string, l UpdateListener) error {
|
||||
return err
|
||||
}
|
||||
|
||||
c.load(cli, key)
|
||||
rev := c.load(cli, key)
|
||||
c.watchGroup.Run(func() {
|
||||
c.watch(cli, key)
|
||||
c.watch(cli, key, rev)
|
||||
})
|
||||
|
||||
return nil
|
||||
@@ -278,22 +280,29 @@ func (c *cluster) reload(cli EtcdClient) {
|
||||
for _, key := range keys {
|
||||
k := key
|
||||
c.watchGroup.Run(func() {
|
||||
c.load(cli, k)
|
||||
c.watch(cli, k)
|
||||
rev := c.load(cli, k)
|
||||
c.watch(cli, k, rev)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (c *cluster) watch(cli EtcdClient, key string) {
|
||||
func (c *cluster) watch(cli EtcdClient, key string, rev int64) {
|
||||
for {
|
||||
if c.watchStream(cli, key) {
|
||||
if c.watchStream(cli, key, rev) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *cluster) watchStream(cli EtcdClient, key string) bool {
|
||||
rch := cli.Watch(clientv3.WithRequireLeader(c.context(cli)), makeKeyPrefix(key), clientv3.WithPrefix())
|
||||
func (c *cluster) watchStream(cli EtcdClient, key string, rev int64) bool {
|
||||
var rch clientv3.WatchChan
|
||||
if rev != 0 {
|
||||
rch = cli.Watch(clientv3.WithRequireLeader(c.context(cli)), makeKeyPrefix(key), clientv3.WithPrefix(),
|
||||
clientv3.WithRev(rev+1))
|
||||
} else {
|
||||
rch = cli.Watch(clientv3.WithRequireLeader(c.context(cli)), makeKeyPrefix(key), clientv3.WithPrefix())
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case wresp, ok := <-rch:
|
||||
@@ -334,6 +343,7 @@ func DialClient(endpoints []string) (EtcdClient, error) {
|
||||
DialKeepAliveTime: dialKeepAliveTime,
|
||||
DialKeepAliveTimeout: DialTimeout,
|
||||
RejectOldCluster: true,
|
||||
PermitWithoutStream: true,
|
||||
}
|
||||
if account, ok := GetAccount(endpoints); ok {
|
||||
cfg.Username = account.User
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/zeromicro/go-zero/core/lang"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/stringx"
|
||||
"go.etcd.io/etcd/api/v3/etcdserverpb"
|
||||
"go.etcd.io/etcd/api/v3/mvccpb"
|
||||
clientv3 "go.etcd.io/etcd/client/v3"
|
||||
)
|
||||
@@ -112,6 +113,7 @@ func TestCluster_Load(t *testing.T) {
|
||||
restore := setMockClient(cli)
|
||||
defer restore()
|
||||
cli.EXPECT().Get(gomock.Any(), "any/", gomock.Any()).Return(&clientv3.GetResponse{
|
||||
Header: &etcdserverpb.ResponseHeader{},
|
||||
Kvs: []*mvccpb.KeyValue{
|
||||
{
|
||||
Key: []byte("hello"),
|
||||
@@ -168,7 +170,7 @@ func TestCluster_Watch(t *testing.T) {
|
||||
listener.EXPECT().OnDelete(gomock.Any()).Do(func(_ interface{}) {
|
||||
wg.Done()
|
||||
}).MaxTimes(1)
|
||||
go c.watch(cli, "any")
|
||||
go c.watch(cli, "any", 0)
|
||||
ch <- clientv3.WatchResponse{
|
||||
Events: []*clientv3.Event{
|
||||
{
|
||||
@@ -212,7 +214,7 @@ func TestClusterWatch_RespFailures(t *testing.T) {
|
||||
ch <- resp
|
||||
close(c.done)
|
||||
}()
|
||||
c.watch(cli, "any")
|
||||
c.watch(cli, "any", 0)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -232,7 +234,7 @@ func TestClusterWatch_CloseChan(t *testing.T) {
|
||||
close(ch)
|
||||
close(c.done)
|
||||
}()
|
||||
c.watch(cli, "any")
|
||||
c.watch(cli, "any", 0)
|
||||
}
|
||||
|
||||
func TestValueOnlyContext(t *testing.T) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/hash"
|
||||
@@ -12,12 +11,12 @@ import (
|
||||
// The file is kept as open, the caller should close the file handle,
|
||||
// and remove the file by name.
|
||||
func TempFileWithText(text string) (*os.File, error) {
|
||||
tmpfile, err := ioutil.TempFile(os.TempDir(), hash.Md5Hex([]byte(text)))
|
||||
tmpfile, err := os.CreateTemp(os.TempDir(), hash.Md5Hex([]byte(text)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(tmpfile.Name(), []byte(text), os.ModeTemporary); err != nil {
|
||||
if err := os.WriteFile(tmpfile.Name(), []byte(text), os.ModeTemporary); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
@@ -21,7 +21,7 @@ func TestTempFileWithText(t *testing.T) {
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
|
||||
bs, err := ioutil.ReadAll(f)
|
||||
bs, err := io.ReadAll(f)
|
||||
assert.Nil(t, err)
|
||||
if len(bs) != 4 {
|
||||
t.Error("TempFileWithText returned wrong file size")
|
||||
@@ -41,7 +41,7 @@ func TestTempFilenameWithText(t *testing.T) {
|
||||
}
|
||||
defer os.Remove(f)
|
||||
|
||||
bs, err := ioutil.ReadFile(f)
|
||||
bs, err := os.ReadFile(f)
|
||||
assert.Nil(t, err)
|
||||
if len(bs) != 4 {
|
||||
t.Error("TempFilenameWithText returned wrong file size")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package fx
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
@@ -238,7 +238,7 @@ func TestLast(t *testing.T) {
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
runCheckedTest(t, func(t *testing.T) {
|
||||
log.SetOutput(ioutil.Discard)
|
||||
log.SetOutput(io.Discard)
|
||||
|
||||
tests := []struct {
|
||||
mapper MapFunc
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
@@ -26,7 +25,7 @@ type (
|
||||
func DupReadCloser(reader io.ReadCloser) (io.ReadCloser, io.ReadCloser) {
|
||||
var buf bytes.Buffer
|
||||
tee := io.TeeReader(reader, &buf)
|
||||
return ioutil.NopCloser(tee), ioutil.NopCloser(&buf)
|
||||
return io.NopCloser(tee), io.NopCloser(&buf)
|
||||
}
|
||||
|
||||
// KeepSpace customizes the reading functions to keep leading and tailing spaces.
|
||||
@@ -54,7 +53,7 @@ func ReadBytes(reader io.Reader, buf []byte) error {
|
||||
|
||||
// ReadText reads content from the given file with leading and tailing spaces trimmed.
|
||||
func ReadText(filename string) (string, error) {
|
||||
content, err := ioutil.ReadFile(filename)
|
||||
content, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package iox
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -97,10 +96,10 @@ func TestReadTextLines(t *testing.T) {
|
||||
|
||||
func TestDupReadCloser(t *testing.T) {
|
||||
input := "hello"
|
||||
reader := ioutil.NopCloser(bytes.NewBufferString(input))
|
||||
reader := io.NopCloser(bytes.NewBufferString(input))
|
||||
r1, r2 := DupReadCloser(reader)
|
||||
verify := func(r io.Reader) {
|
||||
output, err := ioutil.ReadAll(r)
|
||||
output, err := io.ReadAll(r)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, input, string(output))
|
||||
}
|
||||
@@ -110,7 +109,7 @@ func TestDupReadCloser(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestReadBytes(t *testing.T) {
|
||||
reader := ioutil.NopCloser(bytes.NewBufferString("helloworld"))
|
||||
reader := io.NopCloser(bytes.NewBufferString("helloworld"))
|
||||
buf := make([]byte, 5)
|
||||
err := ReadBytes(reader, buf)
|
||||
assert.Nil(t, err)
|
||||
@@ -118,7 +117,7 @@ func TestReadBytes(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestReadBytesNotEnough(t *testing.T) {
|
||||
reader := ioutil.NopCloser(bytes.NewBufferString("hell"))
|
||||
reader := io.NopCloser(bytes.NewBufferString("hell"))
|
||||
buf := make([]byte, 5)
|
||||
err := ReadBytes(reader, buf)
|
||||
assert.Equal(t, io.EOF, err)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package iox
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
@@ -13,7 +12,7 @@ func TestCountLines(t *testing.T) {
|
||||
2
|
||||
3
|
||||
4`
|
||||
file, err := ioutil.TempFile(os.TempDir(), "test-")
|
||||
file, err := os.CreateTemp(os.TempDir(), "test-")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package limit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"sync"
|
||||
@@ -58,8 +60,8 @@ type TokenLimiter struct {
|
||||
timestampKey string
|
||||
rescueLock sync.Mutex
|
||||
redisAlive uint32
|
||||
rescueLimiter *xrate.Limiter
|
||||
monitorStarted bool
|
||||
rescueLimiter *xrate.Limiter
|
||||
}
|
||||
|
||||
// NewTokenLimiter returns a new TokenLimiter that allows events up to rate and permits
|
||||
@@ -84,19 +86,31 @@ func (lim *TokenLimiter) Allow() bool {
|
||||
return lim.AllowN(time.Now(), 1)
|
||||
}
|
||||
|
||||
// AllowCtx is shorthand for AllowNCtx(ctx,time.Now(), 1) with incoming context.
|
||||
func (lim *TokenLimiter) AllowCtx(ctx context.Context) bool {
|
||||
return lim.AllowNCtx(ctx, time.Now(), 1)
|
||||
}
|
||||
|
||||
// AllowN reports whether n events may happen at time now.
|
||||
// Use this method if you intend to drop / skip events that exceed the rate.
|
||||
// Otherwise, use Reserve or Wait.
|
||||
func (lim *TokenLimiter) AllowN(now time.Time, n int) bool {
|
||||
return lim.reserveN(now, n)
|
||||
return lim.reserveN(context.Background(), now, n)
|
||||
}
|
||||
|
||||
func (lim *TokenLimiter) reserveN(now time.Time, n int) bool {
|
||||
// AllowNCtx reports whether n events may happen at time now with incoming context.
|
||||
// Use this method if you intend to drop / skip events that exceed the rate.
|
||||
// Otherwise, use Reserve or Wait.
|
||||
func (lim *TokenLimiter) AllowNCtx(ctx context.Context, now time.Time, n int) bool {
|
||||
return lim.reserveN(ctx, now, n)
|
||||
}
|
||||
|
||||
func (lim *TokenLimiter) reserveN(ctx context.Context, now time.Time, n int) bool {
|
||||
if atomic.LoadUint32(&lim.redisAlive) == 0 {
|
||||
return lim.rescueLimiter.AllowN(now, n)
|
||||
}
|
||||
|
||||
resp, err := lim.store.Eval(
|
||||
resp, err := lim.store.EvalCtx(ctx,
|
||||
script,
|
||||
[]string{
|
||||
lim.tokenKey,
|
||||
@@ -113,6 +127,10 @@ func (lim *TokenLimiter) reserveN(now time.Time, n int) bool {
|
||||
if err == redis.Nil {
|
||||
return false
|
||||
}
|
||||
if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {
|
||||
logx.Errorf("fail to use rate limiter: %s", err)
|
||||
return false
|
||||
}
|
||||
if err != nil {
|
||||
logx.Errorf("fail to use rate limiter: %s, use in-process limiter for rescue", err)
|
||||
lim.startMonitor()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package limit
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -15,6 +16,30 @@ func init() {
|
||||
logx.Disable()
|
||||
}
|
||||
|
||||
func TestTokenLimit_WithCtx(t *testing.T) {
|
||||
s, err := miniredis.Run()
|
||||
assert.Nil(t, err)
|
||||
|
||||
const (
|
||||
total = 100
|
||||
rate = 5
|
||||
burst = 10
|
||||
)
|
||||
l := NewTokenLimiter(rate, burst, redis.New(s.Addr()), "tokenlimit")
|
||||
defer s.Close()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ok := l.AllowCtx(ctx)
|
||||
assert.True(t, ok)
|
||||
|
||||
cancel()
|
||||
for i := 0; i < total; i++ {
|
||||
ok := l.AllowCtx(ctx)
|
||||
assert.False(t, ok)
|
||||
assert.False(t, l.monitorStarted)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenLimit_Rescue(t *testing.T) {
|
||||
s, err := miniredis.Run()
|
||||
assert.Nil(t, err)
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
const (
|
||||
defaultBuckets = 50
|
||||
defaultWindow = time.Second * 5
|
||||
// using 1000m notation, 900m is like 80%, keep it as var for unit test
|
||||
// using 1000m notation, 900m is like 90%, keep it as var for unit test
|
||||
defaultCpuThreshold = 900
|
||||
defaultMinRt = float64(time.Second / time.Millisecond)
|
||||
// moving average hyperparameter beta for calculating requests on the fly
|
||||
@@ -70,7 +70,7 @@ type (
|
||||
flying int64
|
||||
avgFlying float64
|
||||
avgFlyingLock syncx.SpinLock
|
||||
dropTime *syncx.AtomicDuration
|
||||
overloadTime *syncx.AtomicDuration
|
||||
droppedRecently *syncx.AtomicBool
|
||||
passCounter *collection.RollingWindow
|
||||
rtCounter *collection.RollingWindow
|
||||
@@ -106,7 +106,7 @@ func NewAdaptiveShedder(opts ...ShedderOption) Shedder {
|
||||
return &adaptiveShedder{
|
||||
cpuThreshold: options.cpuThreshold,
|
||||
windows: int64(time.Second / bucketDuration),
|
||||
dropTime: syncx.NewAtomicDuration(),
|
||||
overloadTime: syncx.NewAtomicDuration(),
|
||||
droppedRecently: syncx.NewAtomicBool(),
|
||||
passCounter: collection.NewRollingWindow(options.buckets, bucketDuration,
|
||||
collection.IgnoreCurrentBucket()),
|
||||
@@ -118,7 +118,6 @@ func NewAdaptiveShedder(opts ...ShedderOption) Shedder {
|
||||
// Allow implements Shedder.Allow.
|
||||
func (as *adaptiveShedder) Allow() (Promise, error) {
|
||||
if as.shouldDrop() {
|
||||
as.dropTime.Set(timex.Now())
|
||||
as.droppedRecently.Set(true)
|
||||
|
||||
return nil, ErrServiceOverloaded
|
||||
@@ -215,21 +214,26 @@ func (as *adaptiveShedder) stillHot() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
dropTime := as.dropTime.Load()
|
||||
if dropTime == 0 {
|
||||
overloadTime := as.overloadTime.Load()
|
||||
if overloadTime == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
hot := timex.Since(dropTime) < coolOffDuration
|
||||
if !hot {
|
||||
as.droppedRecently.Set(false)
|
||||
if timex.Since(overloadTime) < coolOffDuration {
|
||||
return true
|
||||
}
|
||||
|
||||
return hot
|
||||
as.droppedRecently.Set(false)
|
||||
return false
|
||||
}
|
||||
|
||||
func (as *adaptiveShedder) systemOverloaded() bool {
|
||||
return systemOverloadChecker(as.cpuThreshold)
|
||||
if !systemOverloadChecker(as.cpuThreshold) {
|
||||
return false
|
||||
}
|
||||
|
||||
as.overloadTime.Set(timex.Now())
|
||||
return true
|
||||
}
|
||||
|
||||
// WithBuckets customizes the Shedder with given number of buckets.
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/zeromicro/go-zero/core/mathx"
|
||||
"github.com/zeromicro/go-zero/core/stat"
|
||||
"github.com/zeromicro/go-zero/core/syncx"
|
||||
"github.com/zeromicro/go-zero/core/timex"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -136,7 +137,7 @@ func TestAdaptiveShedderShouldDrop(t *testing.T) {
|
||||
passCounter: passCounter,
|
||||
rtCounter: rtCounter,
|
||||
windows: buckets,
|
||||
dropTime: syncx.NewAtomicDuration(),
|
||||
overloadTime: syncx.NewAtomicDuration(),
|
||||
droppedRecently: syncx.NewAtomicBool(),
|
||||
}
|
||||
// cpu >= 800, inflight < maxPass
|
||||
@@ -190,12 +191,15 @@ func TestAdaptiveShedderStillHot(t *testing.T) {
|
||||
passCounter: passCounter,
|
||||
rtCounter: rtCounter,
|
||||
windows: buckets,
|
||||
dropTime: syncx.NewAtomicDuration(),
|
||||
overloadTime: syncx.NewAtomicDuration(),
|
||||
droppedRecently: syncx.ForAtomicBool(true),
|
||||
}
|
||||
assert.False(t, shedder.stillHot())
|
||||
shedder.dropTime.Set(-coolOffDuration * 2)
|
||||
shedder.overloadTime.Set(-coolOffDuration * 2)
|
||||
assert.False(t, shedder.stillHot())
|
||||
shedder.droppedRecently.Set(true)
|
||||
shedder.overloadTime.Set(timex.Now())
|
||||
assert.True(t, shedder.stillHot())
|
||||
}
|
||||
|
||||
func BenchmarkAdaptiveShedder_Allow(b *testing.B) {
|
||||
|
||||
122
core/logc/logs.go
Normal file
122
core/logc/logs.go
Normal file
@@ -0,0 +1,122 @@
|
||||
package logc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type (
|
||||
LogConf = logx.LogConf
|
||||
LogField = logx.LogField
|
||||
)
|
||||
|
||||
// AddGlobalFields adds global fields.
|
||||
func AddGlobalFields(fields ...LogField) {
|
||||
logx.AddGlobalFields(fields...)
|
||||
}
|
||||
|
||||
// Alert alerts v in alert level, and the message is written to error log.
|
||||
func Alert(_ context.Context, v string) {
|
||||
logx.Alert(v)
|
||||
}
|
||||
|
||||
// Close closes the logging.
|
||||
func Close() error {
|
||||
return logx.Close()
|
||||
}
|
||||
|
||||
// Error writes v into error log.
|
||||
func Error(ctx context.Context, v ...interface{}) {
|
||||
getLogger(ctx).Error(v...)
|
||||
}
|
||||
|
||||
// Errorf writes v with format into error log.
|
||||
func Errorf(ctx context.Context, format string, v ...interface{}) {
|
||||
getLogger(ctx).Errorf(fmt.Errorf(format, v...).Error())
|
||||
}
|
||||
|
||||
// Errorv writes v into error log with json content.
|
||||
// No call stack attached, because not elegant to pack the messages.
|
||||
func Errorv(ctx context.Context, v interface{}) {
|
||||
getLogger(ctx).Errorv(v)
|
||||
}
|
||||
|
||||
// Errorw writes msg along with fields into error log.
|
||||
func Errorw(ctx context.Context, msg string, fields ...LogField) {
|
||||
getLogger(ctx).Errorw(msg, fields...)
|
||||
}
|
||||
|
||||
// Field returns a LogField for the given key and value.
|
||||
func Field(key string, value interface{}) LogField {
|
||||
return logx.Field(key, value)
|
||||
}
|
||||
|
||||
// Info writes v into access log.
|
||||
func Info(ctx context.Context, v ...interface{}) {
|
||||
getLogger(ctx).Info(v...)
|
||||
}
|
||||
|
||||
// Infof writes v with format into access log.
|
||||
func Infof(ctx context.Context, format string, v ...interface{}) {
|
||||
getLogger(ctx).Infof(format, v...)
|
||||
}
|
||||
|
||||
// Infov writes v into access log with json content.
|
||||
func Infov(ctx context.Context, v interface{}) {
|
||||
getLogger(ctx).Infov(v)
|
||||
}
|
||||
|
||||
// Infow writes msg along with fields into access log.
|
||||
func Infow(ctx context.Context, msg string, fields ...LogField) {
|
||||
getLogger(ctx).Infow(msg, fields...)
|
||||
}
|
||||
|
||||
// Must checks if err is nil, otherwise logs the error and exits.
|
||||
func Must(err error) {
|
||||
logx.Must(err)
|
||||
}
|
||||
|
||||
// MustSetup sets up logging with given config c. It exits on error.
|
||||
func MustSetup(c logx.LogConf) {
|
||||
logx.MustSetup(c)
|
||||
}
|
||||
|
||||
// SetLevel sets the logging level. It can be used to suppress some logs.
|
||||
func SetLevel(level uint32) {
|
||||
logx.SetLevel(level)
|
||||
}
|
||||
|
||||
// SetUp sets up the logx. If already set up, just return nil.
|
||||
// we allow SetUp to be called multiple times, because for example
|
||||
// we need to allow different service frameworks to initialize logx respectively.
|
||||
// the same logic for SetUp
|
||||
func SetUp(c LogConf) error {
|
||||
return logx.SetUp(c)
|
||||
}
|
||||
|
||||
// Slow writes v into slow log.
|
||||
func Slow(ctx context.Context, v ...interface{}) {
|
||||
getLogger(ctx).Slow(v...)
|
||||
}
|
||||
|
||||
// Slowf writes v with format into slow log.
|
||||
func Slowf(ctx context.Context, format string, v ...interface{}) {
|
||||
getLogger(ctx).Slowf(format, v...)
|
||||
}
|
||||
|
||||
// Slowv writes v into slow log with json content.
|
||||
func Slowv(ctx context.Context, v interface{}) {
|
||||
getLogger(ctx).Slowv(v)
|
||||
}
|
||||
|
||||
// Sloww writes msg along with fields into slow log.
|
||||
func Sloww(ctx context.Context, msg string, fields ...LogField) {
|
||||
getLogger(ctx).Sloww(msg, fields...)
|
||||
}
|
||||
|
||||
// getLogger returns the logx.Logger with the given ctx and correct caller.
|
||||
func getLogger(ctx context.Context) logx.Logger {
|
||||
return logx.WithContext(ctx).WithCallerSkip(1)
|
||||
}
|
||||
218
core/logc/logs_test.go
Normal file
218
core/logc/logs_test.go
Normal file
@@ -0,0 +1,218 @@
|
||||
package logc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
func TestAddGlobalFields(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
writer := logx.NewWriter(&buf)
|
||||
old := logx.Reset()
|
||||
logx.SetWriter(writer)
|
||||
defer logx.SetWriter(old)
|
||||
|
||||
Info(context.Background(), "hello")
|
||||
buf.Reset()
|
||||
|
||||
AddGlobalFields(Field("a", "1"), Field("b", "2"))
|
||||
AddGlobalFields(Field("c", "3"))
|
||||
Info(context.Background(), "world")
|
||||
var m map[string]interface{}
|
||||
assert.NoError(t, json.Unmarshal(buf.Bytes(), &m))
|
||||
assert.Equal(t, "1", m["a"])
|
||||
assert.Equal(t, "2", m["b"])
|
||||
assert.Equal(t, "3", m["c"])
|
||||
}
|
||||
|
||||
func TestAlert(t *testing.T) {
|
||||
var buf strings.Builder
|
||||
writer := logx.NewWriter(&buf)
|
||||
old := logx.Reset()
|
||||
logx.SetWriter(writer)
|
||||
defer logx.SetWriter(old)
|
||||
|
||||
Alert(context.Background(), "foo")
|
||||
assert.True(t, strings.Contains(buf.String(), "foo"), buf.String())
|
||||
}
|
||||
|
||||
func TestError(t *testing.T) {
|
||||
var buf strings.Builder
|
||||
writer := logx.NewWriter(&buf)
|
||||
old := logx.Reset()
|
||||
logx.SetWriter(writer)
|
||||
defer logx.SetWriter(old)
|
||||
|
||||
file, line := getFileLine()
|
||||
Error(context.Background(), "foo")
|
||||
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)))
|
||||
}
|
||||
|
||||
func TestErrorf(t *testing.T) {
|
||||
var buf strings.Builder
|
||||
writer := logx.NewWriter(&buf)
|
||||
old := logx.Reset()
|
||||
logx.SetWriter(writer)
|
||||
defer logx.SetWriter(old)
|
||||
|
||||
file, line := getFileLine()
|
||||
Errorf(context.Background(), "foo %s", "bar")
|
||||
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)))
|
||||
}
|
||||
|
||||
func TestErrorv(t *testing.T) {
|
||||
var buf strings.Builder
|
||||
writer := logx.NewWriter(&buf)
|
||||
old := logx.Reset()
|
||||
logx.SetWriter(writer)
|
||||
defer logx.SetWriter(old)
|
||||
|
||||
file, line := getFileLine()
|
||||
Errorv(context.Background(), "foo")
|
||||
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)))
|
||||
}
|
||||
|
||||
func TestErrorw(t *testing.T) {
|
||||
var buf strings.Builder
|
||||
writer := logx.NewWriter(&buf)
|
||||
old := logx.Reset()
|
||||
logx.SetWriter(writer)
|
||||
defer logx.SetWriter(old)
|
||||
|
||||
file, line := getFileLine()
|
||||
Errorw(context.Background(), "foo", Field("a", "b"))
|
||||
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)))
|
||||
}
|
||||
|
||||
func TestInfo(t *testing.T) {
|
||||
var buf strings.Builder
|
||||
writer := logx.NewWriter(&buf)
|
||||
old := logx.Reset()
|
||||
logx.SetWriter(writer)
|
||||
defer logx.SetWriter(old)
|
||||
|
||||
file, line := getFileLine()
|
||||
Info(context.Background(), "foo")
|
||||
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)))
|
||||
}
|
||||
|
||||
func TestInfof(t *testing.T) {
|
||||
var buf strings.Builder
|
||||
writer := logx.NewWriter(&buf)
|
||||
old := logx.Reset()
|
||||
logx.SetWriter(writer)
|
||||
defer logx.SetWriter(old)
|
||||
|
||||
file, line := getFileLine()
|
||||
Infof(context.Background(), "foo %s", "bar")
|
||||
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)))
|
||||
}
|
||||
|
||||
func TestInfov(t *testing.T) {
|
||||
var buf strings.Builder
|
||||
writer := logx.NewWriter(&buf)
|
||||
old := logx.Reset()
|
||||
logx.SetWriter(writer)
|
||||
defer logx.SetWriter(old)
|
||||
|
||||
file, line := getFileLine()
|
||||
Infov(context.Background(), "foo")
|
||||
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)))
|
||||
}
|
||||
|
||||
func TestInfow(t *testing.T) {
|
||||
var buf strings.Builder
|
||||
writer := logx.NewWriter(&buf)
|
||||
old := logx.Reset()
|
||||
logx.SetWriter(writer)
|
||||
defer logx.SetWriter(old)
|
||||
|
||||
file, line := getFileLine()
|
||||
Infow(context.Background(), "foo", Field("a", "b"))
|
||||
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)))
|
||||
}
|
||||
|
||||
func TestMust(t *testing.T) {
|
||||
assert.NotPanics(t, func() {
|
||||
Must(nil)
|
||||
})
|
||||
assert.NotPanics(t, func() {
|
||||
MustSetup(LogConf{})
|
||||
})
|
||||
}
|
||||
|
||||
func TestMisc(t *testing.T) {
|
||||
SetLevel(logx.DebugLevel)
|
||||
assert.NoError(t, SetUp(LogConf{}))
|
||||
assert.NoError(t, Close())
|
||||
}
|
||||
|
||||
func TestSlow(t *testing.T) {
|
||||
var buf strings.Builder
|
||||
writer := logx.NewWriter(&buf)
|
||||
old := logx.Reset()
|
||||
logx.SetWriter(writer)
|
||||
defer logx.SetWriter(old)
|
||||
|
||||
file, line := getFileLine()
|
||||
Slow(context.Background(), "foo")
|
||||
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)), buf.String())
|
||||
}
|
||||
|
||||
func TestSlowf(t *testing.T) {
|
||||
var buf strings.Builder
|
||||
writer := logx.NewWriter(&buf)
|
||||
old := logx.Reset()
|
||||
logx.SetWriter(writer)
|
||||
defer logx.SetWriter(old)
|
||||
|
||||
file, line := getFileLine()
|
||||
Slowf(context.Background(), "foo %s", "bar")
|
||||
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)), buf.String())
|
||||
}
|
||||
|
||||
func TestSlowv(t *testing.T) {
|
||||
var buf strings.Builder
|
||||
writer := logx.NewWriter(&buf)
|
||||
old := logx.Reset()
|
||||
logx.SetWriter(writer)
|
||||
defer logx.SetWriter(old)
|
||||
|
||||
file, line := getFileLine()
|
||||
Slowv(context.Background(), "foo")
|
||||
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)), buf.String())
|
||||
}
|
||||
|
||||
func TestSloww(t *testing.T) {
|
||||
var buf strings.Builder
|
||||
writer := logx.NewWriter(&buf)
|
||||
old := logx.Reset()
|
||||
logx.SetWriter(writer)
|
||||
defer logx.SetWriter(old)
|
||||
|
||||
file, line := getFileLine()
|
||||
Sloww(context.Background(), "foo", Field("a", "b"))
|
||||
assert.True(t, strings.Contains(buf.String(), fmt.Sprintf("%s:%d", file, line+1)), buf.String())
|
||||
}
|
||||
|
||||
func getFileLine() (string, int) {
|
||||
_, file, line, _ := runtime.Caller(1)
|
||||
short := file
|
||||
|
||||
for i := len(file) - 1; i > 0; i-- {
|
||||
if file[i] == '/' {
|
||||
short = file[i+1:]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return short, line
|
||||
}
|
||||
@@ -7,7 +7,7 @@ type LogConf struct {
|
||||
Encoding string `json:",default=json,options=[json,plain]"`
|
||||
TimeFormat string `json:",optional"`
|
||||
Path string `json:",default=logs"`
|
||||
Level string `json:",default=info,options=[info,error,severe]"`
|
||||
Level string `json:",default=info,options=[debug,info,error,severe]"`
|
||||
Compress bool `json:",optional"`
|
||||
KeepDays int `json:",optional"`
|
||||
StackCooldownMillis int `json:",default=100"`
|
||||
|
||||
@@ -1,145 +0,0 @@
|
||||
package logx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/timex"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
// WithContext sets ctx to log, for keeping tracing information.
|
||||
func WithContext(ctx context.Context) Logger {
|
||||
return &contextLogger{
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
type contextLogger struct {
|
||||
logEntry
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func (l *contextLogger) Error(v ...interface{}) {
|
||||
l.err(fmt.Sprint(v...))
|
||||
}
|
||||
|
||||
func (l *contextLogger) Errorf(format string, v ...interface{}) {
|
||||
l.err(fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
func (l *contextLogger) Errorv(v interface{}) {
|
||||
l.err(fmt.Sprint(v))
|
||||
}
|
||||
|
||||
func (l *contextLogger) Errorw(msg string, fields ...LogField) {
|
||||
l.err(msg, fields...)
|
||||
}
|
||||
|
||||
func (l *contextLogger) Info(v ...interface{}) {
|
||||
l.info(fmt.Sprint(v...))
|
||||
}
|
||||
|
||||
func (l *contextLogger) Infof(format string, v ...interface{}) {
|
||||
l.info(fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
func (l *contextLogger) Infov(v interface{}) {
|
||||
l.info(v)
|
||||
}
|
||||
|
||||
func (l *contextLogger) Infow(msg string, fields ...LogField) {
|
||||
l.info(msg, fields...)
|
||||
}
|
||||
|
||||
func (l *contextLogger) Slow(v ...interface{}) {
|
||||
l.slow(fmt.Sprint(v...))
|
||||
}
|
||||
|
||||
func (l *contextLogger) Slowf(format string, v ...interface{}) {
|
||||
l.slow(fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
func (l *contextLogger) Slowv(v interface{}) {
|
||||
l.slow(v)
|
||||
}
|
||||
|
||||
func (l *contextLogger) Sloww(msg string, fields ...LogField) {
|
||||
l.slow(msg, fields...)
|
||||
}
|
||||
|
||||
func (l *contextLogger) WithContext(ctx context.Context) Logger {
|
||||
if ctx == nil {
|
||||
return l
|
||||
}
|
||||
|
||||
l.ctx = ctx
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *contextLogger) WithDuration(duration time.Duration) Logger {
|
||||
l.Duration = timex.ReprOfDuration(duration)
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *contextLogger) buildFields(fields ...LogField) []LogField {
|
||||
if len(l.Duration) > 0 {
|
||||
fields = append(fields, Field(durationKey, l.Duration))
|
||||
}
|
||||
|
||||
traceID := traceIdFromContext(l.ctx)
|
||||
if len(traceID) > 0 {
|
||||
fields = append(fields, Field(traceKey, traceID))
|
||||
}
|
||||
|
||||
spanID := spanIdFromContext(l.ctx)
|
||||
if len(spanID) > 0 {
|
||||
fields = append(fields, Field(spanKey, spanID))
|
||||
}
|
||||
|
||||
val := l.ctx.Value(fieldsContextKey)
|
||||
if val != nil {
|
||||
if arr, ok := val.([]LogField); ok {
|
||||
fields = append(fields, arr...)
|
||||
}
|
||||
}
|
||||
|
||||
return fields
|
||||
}
|
||||
|
||||
func (l *contextLogger) err(v interface{}, fields ...LogField) {
|
||||
if shallLog(ErrorLevel) {
|
||||
getWriter().Error(v, l.buildFields(fields...)...)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *contextLogger) info(v interface{}, fields ...LogField) {
|
||||
if shallLog(InfoLevel) {
|
||||
getWriter().Info(v, l.buildFields(fields...)...)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *contextLogger) slow(v interface{}, fields ...LogField) {
|
||||
if shallLog(ErrorLevel) {
|
||||
getWriter().Slow(v, l.buildFields(fields...)...)
|
||||
}
|
||||
}
|
||||
|
||||
func spanIdFromContext(ctx context.Context) string {
|
||||
spanCtx := trace.SpanContextFromContext(ctx)
|
||||
if spanCtx.HasSpanID() {
|
||||
return spanCtx.SpanID().String()
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func traceIdFromContext(ctx context.Context) string {
|
||||
spanCtx := trace.SpanContextFromContext(ctx)
|
||||
if spanCtx.HasTraceID() {
|
||||
return spanCtx.TraceID().String()
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
package logx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/timex"
|
||||
)
|
||||
|
||||
// WithDuration returns a Logger which logs the given duration.
|
||||
func WithDuration(d time.Duration) Logger {
|
||||
return &durationLogger{
|
||||
Duration: timex.ReprOfDuration(d),
|
||||
}
|
||||
}
|
||||
|
||||
type durationLogger logEntry
|
||||
|
||||
func (l *durationLogger) Error(v ...interface{}) {
|
||||
l.err(fmt.Sprint(v...))
|
||||
}
|
||||
|
||||
func (l *durationLogger) Errorf(format string, v ...interface{}) {
|
||||
l.err(fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
func (l *durationLogger) Errorv(v interface{}) {
|
||||
l.err(v)
|
||||
}
|
||||
|
||||
func (l *durationLogger) Errorw(msg string, fields ...LogField) {
|
||||
l.err(msg, fields...)
|
||||
}
|
||||
|
||||
func (l *durationLogger) Info(v ...interface{}) {
|
||||
l.info(fmt.Sprint(v...))
|
||||
}
|
||||
|
||||
func (l *durationLogger) Infof(format string, v ...interface{}) {
|
||||
l.info(fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
func (l *durationLogger) Infov(v interface{}) {
|
||||
l.info(v)
|
||||
}
|
||||
|
||||
func (l *durationLogger) Infow(msg string, fields ...LogField) {
|
||||
l.info(msg, fields...)
|
||||
}
|
||||
|
||||
func (l *durationLogger) Slow(v ...interface{}) {
|
||||
l.slow(fmt.Sprint(v...))
|
||||
}
|
||||
|
||||
func (l *durationLogger) Slowf(format string, v ...interface{}) {
|
||||
l.slow(fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
func (l *durationLogger) Slowv(v interface{}) {
|
||||
l.slow(v)
|
||||
}
|
||||
|
||||
func (l *durationLogger) Sloww(msg string, fields ...LogField) {
|
||||
l.slow(msg, fields...)
|
||||
}
|
||||
|
||||
func (l *durationLogger) WithContext(ctx context.Context) Logger {
|
||||
return &contextLogger{
|
||||
ctx: ctx,
|
||||
logEntry: logEntry{
|
||||
Duration: l.Duration,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (l *durationLogger) WithDuration(duration time.Duration) Logger {
|
||||
l.Duration = timex.ReprOfDuration(duration)
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *durationLogger) err(v interface{}, fields ...LogField) {
|
||||
if shallLog(ErrorLevel) {
|
||||
fields = append(fields, Field(durationKey, l.Duration))
|
||||
getWriter().Error(v, fields...)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *durationLogger) info(v interface{}, fields ...LogField) {
|
||||
if shallLog(InfoLevel) {
|
||||
fields = append(fields, Field(durationKey, l.Duration))
|
||||
getWriter().Info(v, fields...)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *durationLogger) slow(v interface{}, fields ...LogField) {
|
||||
if shallLog(ErrorLevel) {
|
||||
fields = append(fields, Field(durationKey, l.Duration))
|
||||
getWriter().Slow(v, fields...)
|
||||
}
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
package logx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"go.opentelemetry.io/otel"
|
||||
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||
)
|
||||
|
||||
func TestWithDurationError(t *testing.T) {
|
||||
w := new(mockWriter)
|
||||
old := writer.Swap(w)
|
||||
defer writer.Store(old)
|
||||
|
||||
WithDuration(time.Second).Error("foo")
|
||||
assert.True(t, strings.Contains(w.String(), "duration"), w.String())
|
||||
}
|
||||
|
||||
func TestWithDurationErrorf(t *testing.T) {
|
||||
w := new(mockWriter)
|
||||
old := writer.Swap(w)
|
||||
defer writer.Store(old)
|
||||
|
||||
WithDuration(time.Second).Errorf("foo")
|
||||
assert.True(t, strings.Contains(w.String(), "duration"), w.String())
|
||||
}
|
||||
|
||||
func TestWithDurationErrorv(t *testing.T) {
|
||||
w := new(mockWriter)
|
||||
old := writer.Swap(w)
|
||||
defer writer.Store(old)
|
||||
|
||||
WithDuration(time.Second).Errorv("foo")
|
||||
assert.True(t, strings.Contains(w.String(), "duration"), w.String())
|
||||
}
|
||||
|
||||
func TestWithDurationErrorw(t *testing.T) {
|
||||
w := new(mockWriter)
|
||||
old := writer.Swap(w)
|
||||
defer writer.Store(old)
|
||||
|
||||
WithDuration(time.Second).Errorw("foo", Field("foo", "bar"))
|
||||
assert.True(t, strings.Contains(w.String(), "duration"), w.String())
|
||||
assert.True(t, strings.Contains(w.String(), "foo"), w.String())
|
||||
assert.True(t, strings.Contains(w.String(), "bar"), w.String())
|
||||
}
|
||||
|
||||
func TestWithDurationInfo(t *testing.T) {
|
||||
w := new(mockWriter)
|
||||
old := writer.Swap(w)
|
||||
defer writer.Store(old)
|
||||
|
||||
WithDuration(time.Second).Info("foo")
|
||||
assert.True(t, strings.Contains(w.String(), "duration"), w.String())
|
||||
}
|
||||
|
||||
func TestWithDurationInfoConsole(t *testing.T) {
|
||||
old := atomic.LoadUint32(&encoding)
|
||||
atomic.StoreUint32(&encoding, plainEncodingType)
|
||||
defer func() {
|
||||
atomic.StoreUint32(&encoding, old)
|
||||
}()
|
||||
|
||||
w := new(mockWriter)
|
||||
o := writer.Swap(w)
|
||||
defer writer.Store(o)
|
||||
|
||||
WithDuration(time.Second).Info("foo")
|
||||
assert.True(t, strings.Contains(w.String(), "ms"), w.String())
|
||||
}
|
||||
|
||||
func TestWithDurationInfof(t *testing.T) {
|
||||
w := new(mockWriter)
|
||||
old := writer.Swap(w)
|
||||
defer writer.Store(old)
|
||||
|
||||
WithDuration(time.Second).Infof("foo")
|
||||
assert.True(t, strings.Contains(w.String(), "duration"), w.String())
|
||||
}
|
||||
|
||||
func TestWithDurationInfov(t *testing.T) {
|
||||
w := new(mockWriter)
|
||||
old := writer.Swap(w)
|
||||
defer writer.Store(old)
|
||||
|
||||
WithDuration(time.Second).Infov("foo")
|
||||
assert.True(t, strings.Contains(w.String(), "duration"), w.String())
|
||||
}
|
||||
|
||||
func TestWithDurationInfow(t *testing.T) {
|
||||
w := new(mockWriter)
|
||||
old := writer.Swap(w)
|
||||
defer writer.Store(old)
|
||||
|
||||
WithDuration(time.Second).Infow("foo", Field("foo", "bar"))
|
||||
assert.True(t, strings.Contains(w.String(), "duration"), w.String())
|
||||
assert.True(t, strings.Contains(w.String(), "foo"), w.String())
|
||||
assert.True(t, strings.Contains(w.String(), "bar"), w.String())
|
||||
}
|
||||
|
||||
func TestWithDurationWithContextInfow(t *testing.T) {
|
||||
w := new(mockWriter)
|
||||
old := writer.Swap(w)
|
||||
defer writer.Store(old)
|
||||
|
||||
otp := otel.GetTracerProvider()
|
||||
tp := sdktrace.NewTracerProvider(sdktrace.WithSampler(sdktrace.AlwaysSample()))
|
||||
otel.SetTracerProvider(tp)
|
||||
defer otel.SetTracerProvider(otp)
|
||||
|
||||
ctx, _ := tp.Tracer("foo").Start(context.Background(), "bar")
|
||||
WithDuration(time.Second).WithContext(ctx).Infow("foo", Field("foo", "bar"))
|
||||
assert.True(t, strings.Contains(w.String(), "duration"), w.String())
|
||||
assert.True(t, strings.Contains(w.String(), "foo"), w.String())
|
||||
assert.True(t, strings.Contains(w.String(), "bar"), w.String())
|
||||
assert.True(t, strings.Contains(w.String(), "trace"), w.String())
|
||||
assert.True(t, strings.Contains(w.String(), "span"), w.String())
|
||||
}
|
||||
|
||||
func TestWithDurationSlow(t *testing.T) {
|
||||
w := new(mockWriter)
|
||||
old := writer.Swap(w)
|
||||
defer writer.Store(old)
|
||||
|
||||
WithDuration(time.Second).Slow("foo")
|
||||
assert.True(t, strings.Contains(w.String(), "duration"), w.String())
|
||||
}
|
||||
|
||||
func TestWithDurationSlowf(t *testing.T) {
|
||||
w := new(mockWriter)
|
||||
old := writer.Swap(w)
|
||||
defer writer.Store(old)
|
||||
|
||||
WithDuration(time.Second).WithDuration(time.Hour).Slowf("foo")
|
||||
assert.True(t, strings.Contains(w.String(), "duration"), w.String())
|
||||
}
|
||||
|
||||
func TestWithDurationSlowv(t *testing.T) {
|
||||
w := new(mockWriter)
|
||||
old := writer.Swap(w)
|
||||
defer writer.Store(old)
|
||||
|
||||
WithDuration(time.Second).WithDuration(time.Hour).Slowv("foo")
|
||||
assert.True(t, strings.Contains(w.String(), "duration"), w.String())
|
||||
}
|
||||
|
||||
func TestWithDurationSloww(t *testing.T) {
|
||||
w := new(mockWriter)
|
||||
old := writer.Swap(w)
|
||||
defer writer.Store(old)
|
||||
|
||||
WithDuration(time.Second).WithDuration(time.Hour).Sloww("foo", Field("foo", "bar"))
|
||||
assert.True(t, strings.Contains(w.String(), "duration"), w.String())
|
||||
assert.True(t, strings.Contains(w.String(), "foo"), w.String())
|
||||
assert.True(t, strings.Contains(w.String(), "bar"), w.String())
|
||||
}
|
||||
@@ -1,13 +1,34 @@
|
||||
package logx
|
||||
|
||||
import "context"
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
var fieldsContextKey contextKey
|
||||
var (
|
||||
fieldsContextKey contextKey
|
||||
globalFields atomic.Value
|
||||
globalFieldsLock sync.Mutex
|
||||
)
|
||||
|
||||
type contextKey struct{}
|
||||
|
||||
// WithFields returns a new context with the given fields.
|
||||
func WithFields(ctx context.Context, fields ...LogField) context.Context {
|
||||
// AddGlobalFields adds global fields.
|
||||
func AddGlobalFields(fields ...LogField) {
|
||||
globalFieldsLock.Lock()
|
||||
defer globalFieldsLock.Unlock()
|
||||
|
||||
old := globalFields.Load()
|
||||
if old == nil {
|
||||
globalFields.Store(append([]LogField(nil), fields...))
|
||||
} else {
|
||||
globalFields.Store(append(old.([]LogField), fields...))
|
||||
}
|
||||
}
|
||||
|
||||
// ContextWithFields returns a new context with the given fields.
|
||||
func ContextWithFields(ctx context.Context, fields ...LogField) context.Context {
|
||||
if val := ctx.Value(fieldsContextKey); val != nil {
|
||||
if arr, ok := val.([]LogField); ok {
|
||||
return context.WithValue(ctx, fieldsContextKey, append(arr, fields...))
|
||||
@@ -16,3 +37,9 @@ func WithFields(ctx context.Context, fields ...LogField) context.Context {
|
||||
|
||||
return context.WithValue(ctx, fieldsContextKey, fields)
|
||||
}
|
||||
|
||||
// WithFields returns a new logger with the given fields.
|
||||
// deprecated: use ContextWithFields instead.
|
||||
func WithFields(ctx context.Context, fields ...LogField) context.Context {
|
||||
return ContextWithFields(ctx, fields...)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,45 @@
|
||||
package logx
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAddGlobalFields(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
writer := NewWriter(&buf)
|
||||
old := Reset()
|
||||
SetWriter(writer)
|
||||
defer SetWriter(old)
|
||||
|
||||
Info("hello")
|
||||
buf.Reset()
|
||||
|
||||
AddGlobalFields(Field("a", "1"), Field("b", "2"))
|
||||
AddGlobalFields(Field("c", "3"))
|
||||
Info("world")
|
||||
var m map[string]interface{}
|
||||
assert.NoError(t, json.Unmarshal(buf.Bytes(), &m))
|
||||
assert.Equal(t, "1", m["a"])
|
||||
assert.Equal(t, "2", m["b"])
|
||||
assert.Equal(t, "3", m["c"])
|
||||
}
|
||||
|
||||
func TestContextWithFields(t *testing.T) {
|
||||
ctx := ContextWithFields(context.Background(), Field("a", 1), Field("b", 2))
|
||||
vals := ctx.Value(fieldsContextKey)
|
||||
assert.NotNil(t, vals)
|
||||
fields, ok := vals.([]LogField)
|
||||
assert.True(t, ok)
|
||||
assert.EqualValues(t, []LogField{Field("a", 1), Field("b", 2)}, fields)
|
||||
}
|
||||
|
||||
func TestWithFields(t *testing.T) {
|
||||
ctx := WithFields(context.Background(), Field("a", 1), Field("b", 2))
|
||||
vals := ctx.Value(fieldsContextKey)
|
||||
@@ -19,8 +52,8 @@ func TestWithFields(t *testing.T) {
|
||||
func TestWithFieldsAppend(t *testing.T) {
|
||||
var dummyKey struct{}
|
||||
ctx := context.WithValue(context.Background(), dummyKey, "dummy")
|
||||
ctx = WithFields(ctx, Field("a", 1), Field("b", 2))
|
||||
ctx = WithFields(ctx, Field("c", 3), Field("d", 4))
|
||||
ctx = ContextWithFields(ctx, Field("a", 1), Field("b", 2))
|
||||
ctx = ContextWithFields(ctx, Field("c", 3), Field("d", 4))
|
||||
vals := ctx.Value(fieldsContextKey)
|
||||
assert.NotNil(t, vals)
|
||||
fields, ok := vals.([]LogField)
|
||||
@@ -33,3 +66,39 @@ func TestWithFieldsAppend(t *testing.T) {
|
||||
Field("d", 4),
|
||||
}, fields)
|
||||
}
|
||||
|
||||
func BenchmarkAtomicValue(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
|
||||
var container atomic.Value
|
||||
vals := []LogField{
|
||||
Field("a", "b"),
|
||||
Field("c", "d"),
|
||||
Field("e", "f"),
|
||||
}
|
||||
container.Store(&vals)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
val := container.Load()
|
||||
if val != nil {
|
||||
_ = *val.(*[]LogField)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRWMutex(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
|
||||
var lock sync.RWMutex
|
||||
vals := []LogField{
|
||||
Field("a", "b"),
|
||||
Field("c", "d"),
|
||||
Field("e", "f"),
|
||||
}
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
lock.RLock()
|
||||
_ = vals
|
||||
lock.RUnlock()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,14 @@ import (
|
||||
|
||||
// A Logger represents a logger.
|
||||
type Logger interface {
|
||||
// Debug logs a message at info level.
|
||||
Debug(...interface{})
|
||||
// Debugf logs a message at info level.
|
||||
Debugf(string, ...interface{})
|
||||
// Debugv logs a message at info level.
|
||||
Debugv(interface{})
|
||||
// Debugw logs a message at info level.
|
||||
Debugw(string, ...LogField)
|
||||
// Error logs a message at error level.
|
||||
Error(...interface{})
|
||||
// Errorf logs a message at error level.
|
||||
@@ -31,8 +39,12 @@ type Logger interface {
|
||||
Slowv(interface{})
|
||||
// Sloww logs a message at slow level.
|
||||
Sloww(string, ...LogField)
|
||||
// WithCallerSkip returns a new logger with the given caller skip.
|
||||
WithCallerSkip(skip int) Logger
|
||||
// WithContext returns a new logger with the given context.
|
||||
WithContext(context.Context) Logger
|
||||
WithContext(ctx context.Context) Logger
|
||||
// WithDuration returns a new logger with the given duration.
|
||||
WithDuration(time.Duration) Logger
|
||||
WithDuration(d time.Duration) Logger
|
||||
// WithFields returns a new logger with the given fields.
|
||||
WithFields(fields ...LogField) Logger
|
||||
}
|
||||
|
||||
@@ -14,13 +14,14 @@ import (
|
||||
"github.com/zeromicro/go-zero/core/sysx"
|
||||
)
|
||||
|
||||
const callerDepth = 5
|
||||
const callerDepth = 4
|
||||
|
||||
var (
|
||||
timeFormat = "2006-01-02T15:04:05.000Z07:00"
|
||||
logLevel uint32
|
||||
encoding uint32 = jsonEncodingType
|
||||
// use uint32 for atomic operations
|
||||
disableLog uint32
|
||||
disableStat uint32
|
||||
options logOptions
|
||||
writer = new(atomicWriter)
|
||||
@@ -28,15 +29,16 @@ var (
|
||||
)
|
||||
|
||||
type (
|
||||
logEntry struct {
|
||||
Timestamp string `json:"@timestamp"`
|
||||
Level string `json:"level"`
|
||||
Duration string `json:"duration,omitempty"`
|
||||
Caller string `json:"caller,omitempty"`
|
||||
Content interface{} `json:"content"`
|
||||
// LogField is a key-value pair that will be added to the log entry.
|
||||
LogField struct {
|
||||
Key string
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
logEntryWithFields map[string]interface{}
|
||||
// LogOption defines the method to customize the logging.
|
||||
LogOption func(options *logOptions)
|
||||
|
||||
logEntry map[string]interface{}
|
||||
|
||||
logOptions struct {
|
||||
gzipEnabled bool
|
||||
@@ -46,15 +48,6 @@ type (
|
||||
maxSize int
|
||||
rotationRule string
|
||||
}
|
||||
|
||||
// LogField is a key-value pair that will be added to the log entry.
|
||||
LogField struct {
|
||||
Key string
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// LogOption defines the method to customize the logging.
|
||||
LogOption func(options *logOptions)
|
||||
)
|
||||
|
||||
// Alert alerts v in alert level, and the message is written to error log.
|
||||
@@ -71,8 +64,29 @@ func Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Debug writes v into access log.
|
||||
func Debug(v ...interface{}) {
|
||||
writeDebug(fmt.Sprint(v...))
|
||||
}
|
||||
|
||||
// Debugf writes v with format into access log.
|
||||
func Debugf(format string, v ...interface{}) {
|
||||
writeDebug(fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
// Debugv writes v into access log with json content.
|
||||
func Debugv(v interface{}) {
|
||||
writeDebug(v)
|
||||
}
|
||||
|
||||
// Debugw writes msg along with fields into access log.
|
||||
func Debugw(msg string, fields ...LogField) {
|
||||
writeDebug(msg, fields...)
|
||||
}
|
||||
|
||||
// Disable disables the logging.
|
||||
func Disable() {
|
||||
atomic.StoreUint32(&disableLog, 1)
|
||||
writer.Store(nopWriter{})
|
||||
}
|
||||
|
||||
@@ -83,35 +97,35 @@ func DisableStat() {
|
||||
|
||||
// Error writes v into error log.
|
||||
func Error(v ...interface{}) {
|
||||
errorTextSync(fmt.Sprint(v...))
|
||||
writeError(fmt.Sprint(v...))
|
||||
}
|
||||
|
||||
// Errorf writes v with format into error log.
|
||||
func Errorf(format string, v ...interface{}) {
|
||||
errorTextSync(fmt.Errorf(format, v...).Error())
|
||||
writeError(fmt.Errorf(format, v...).Error())
|
||||
}
|
||||
|
||||
// ErrorStack writes v along with call stack into error log.
|
||||
func ErrorStack(v ...interface{}) {
|
||||
// there is newline in stack string
|
||||
stackSync(fmt.Sprint(v...))
|
||||
writeStack(fmt.Sprint(v...))
|
||||
}
|
||||
|
||||
// ErrorStackf writes v along with call stack in format into error log.
|
||||
func ErrorStackf(format string, v ...interface{}) {
|
||||
// there is newline in stack string
|
||||
stackSync(fmt.Sprintf(format, v...))
|
||||
writeStack(fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
// Errorv writes v into error log with json content.
|
||||
// No call stack attached, because not elegant to pack the messages.
|
||||
func Errorv(v interface{}) {
|
||||
errorAnySync(v)
|
||||
writeError(v)
|
||||
}
|
||||
|
||||
// Errorw writes msg along with fields into error log.
|
||||
func Errorw(msg string, fields ...LogField) {
|
||||
errorFieldsSync(msg, fields...)
|
||||
writeError(msg, fields...)
|
||||
}
|
||||
|
||||
// Field returns a LogField for the given key and value.
|
||||
@@ -154,22 +168,22 @@ func Field(key string, value interface{}) LogField {
|
||||
|
||||
// Info writes v into access log.
|
||||
func Info(v ...interface{}) {
|
||||
infoTextSync(fmt.Sprint(v...))
|
||||
writeInfo(fmt.Sprint(v...))
|
||||
}
|
||||
|
||||
// Infof writes v with format into access log.
|
||||
func Infof(format string, v ...interface{}) {
|
||||
infoTextSync(fmt.Sprintf(format, v...))
|
||||
writeInfo(fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
// Infov writes v into access log with json content.
|
||||
func Infov(v interface{}) {
|
||||
infoAnySync(v)
|
||||
writeInfo(v)
|
||||
}
|
||||
|
||||
// Infow writes msg along with fields into access log.
|
||||
func Infow(msg string, fields ...LogField) {
|
||||
infoFieldsSync(msg, fields...)
|
||||
writeInfo(msg, fields...)
|
||||
}
|
||||
|
||||
// Must checks if err is nil, otherwise logs the error and exits.
|
||||
@@ -201,7 +215,9 @@ func SetLevel(level uint32) {
|
||||
|
||||
// SetWriter sets the logging writer. It can be used to customize the logging.
|
||||
func SetWriter(w Writer) {
|
||||
writer.Store(w)
|
||||
if atomic.LoadUint32(&disableLog) == 0 {
|
||||
writer.Store(w)
|
||||
}
|
||||
}
|
||||
|
||||
// SetUp sets up the logx. If already set up, just return nil.
|
||||
@@ -240,42 +256,42 @@ func SetUp(c LogConf) (err error) {
|
||||
|
||||
// Severe writes v into severe log.
|
||||
func Severe(v ...interface{}) {
|
||||
severeSync(fmt.Sprint(v...))
|
||||
writeSevere(fmt.Sprint(v...))
|
||||
}
|
||||
|
||||
// Severef writes v with format into severe log.
|
||||
func Severef(format string, v ...interface{}) {
|
||||
severeSync(fmt.Sprintf(format, v...))
|
||||
writeSevere(fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
// Slow writes v into slow log.
|
||||
func Slow(v ...interface{}) {
|
||||
slowTextSync(fmt.Sprint(v...))
|
||||
writeSlow(fmt.Sprint(v...))
|
||||
}
|
||||
|
||||
// Slowf writes v with format into slow log.
|
||||
func Slowf(format string, v ...interface{}) {
|
||||
slowTextSync(fmt.Sprintf(format, v...))
|
||||
writeSlow(fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
// Slowv writes v into slow log with json content.
|
||||
func Slowv(v interface{}) {
|
||||
slowAnySync(v)
|
||||
writeSlow(v)
|
||||
}
|
||||
|
||||
// Sloww writes msg along with fields into slow log.
|
||||
func Sloww(msg string, fields ...LogField) {
|
||||
slowFieldsSync(msg, fields...)
|
||||
writeSlow(msg, fields...)
|
||||
}
|
||||
|
||||
// Stat writes v into stat log.
|
||||
func Stat(v ...interface{}) {
|
||||
statSync(fmt.Sprint(v...))
|
||||
writeStat(fmt.Sprint(v...))
|
||||
}
|
||||
|
||||
// Statf writes v with format into stat log.
|
||||
func Statf(format string, v ...interface{}) {
|
||||
statSync(fmt.Sprintf(format, v...))
|
||||
writeStat(fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
// WithCooldownMillis customizes logging on writing call stack interval.
|
||||
@@ -320,6 +336,10 @@ func WithRotation(r string) LogOption {
|
||||
}
|
||||
}
|
||||
|
||||
func addCaller(fields ...LogField) []LogField {
|
||||
return append(fields, Field(callerKey, getCaller(callerDepth)))
|
||||
}
|
||||
|
||||
func createOutput(path string) (io.WriteCloser, error) {
|
||||
if len(path) == 0 {
|
||||
return nil, ErrLogPathNotSet
|
||||
@@ -335,29 +355,10 @@ func createOutput(path string) (io.WriteCloser, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func errorAnySync(v interface{}) {
|
||||
if shallLog(ErrorLevel) {
|
||||
getWriter().Error(v)
|
||||
}
|
||||
}
|
||||
|
||||
func errorFieldsSync(content string, fields ...LogField) {
|
||||
if shallLog(ErrorLevel) {
|
||||
getWriter().Error(content, fields...)
|
||||
}
|
||||
}
|
||||
|
||||
func errorTextSync(msg string) {
|
||||
if shallLog(ErrorLevel) {
|
||||
getWriter().Error(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func getWriter() Writer {
|
||||
w := writer.Load()
|
||||
if w == nil {
|
||||
w = newConsoleWriter()
|
||||
writer.Store(w)
|
||||
w = writer.StoreIfNil(newConsoleWriter())
|
||||
}
|
||||
|
||||
return w
|
||||
@@ -369,26 +370,10 @@ func handleOptions(opts []LogOption) {
|
||||
}
|
||||
}
|
||||
|
||||
func infoAnySync(val interface{}) {
|
||||
if shallLog(InfoLevel) {
|
||||
getWriter().Info(val)
|
||||
}
|
||||
}
|
||||
|
||||
func infoFieldsSync(content string, fields ...LogField) {
|
||||
if shallLog(InfoLevel) {
|
||||
getWriter().Info(content, fields...)
|
||||
}
|
||||
}
|
||||
|
||||
func infoTextSync(msg string) {
|
||||
if shallLog(InfoLevel) {
|
||||
getWriter().Info(msg)
|
||||
}
|
||||
}
|
||||
|
||||
func setupLogLevel(c LogConf) {
|
||||
switch c.Level {
|
||||
case levelDebug:
|
||||
SetLevel(DebugLevel)
|
||||
case levelInfo:
|
||||
SetLevel(InfoLevel)
|
||||
case levelError:
|
||||
@@ -421,12 +406,6 @@ func setupWithVolume(c LogConf) error {
|
||||
return setupWithFiles(c)
|
||||
}
|
||||
|
||||
func severeSync(msg string) {
|
||||
if shallLog(SevereLevel) {
|
||||
getWriter().Severe(fmt.Sprintf("%s\n%s", msg, string(debug.Stack())))
|
||||
}
|
||||
}
|
||||
|
||||
func shallLog(level uint32) bool {
|
||||
return atomic.LoadUint32(&logLevel) <= level
|
||||
}
|
||||
@@ -435,32 +414,44 @@ func shallLogStat() bool {
|
||||
return atomic.LoadUint32(&disableStat) == 0
|
||||
}
|
||||
|
||||
func slowAnySync(v interface{}) {
|
||||
if shallLog(ErrorLevel) {
|
||||
getWriter().Slow(v)
|
||||
func writeDebug(val interface{}, fields ...LogField) {
|
||||
if shallLog(DebugLevel) {
|
||||
getWriter().Debug(val, addCaller(fields...)...)
|
||||
}
|
||||
}
|
||||
|
||||
func slowFieldsSync(content string, fields ...LogField) {
|
||||
func writeError(val interface{}, fields ...LogField) {
|
||||
if shallLog(ErrorLevel) {
|
||||
getWriter().Slow(content, fields...)
|
||||
getWriter().Error(val, addCaller(fields...)...)
|
||||
}
|
||||
}
|
||||
|
||||
func slowTextSync(msg string) {
|
||||
if shallLog(ErrorLevel) {
|
||||
getWriter().Slow(msg)
|
||||
func writeInfo(val interface{}, fields ...LogField) {
|
||||
if shallLog(InfoLevel) {
|
||||
getWriter().Info(val, addCaller(fields...)...)
|
||||
}
|
||||
}
|
||||
|
||||
func stackSync(msg string) {
|
||||
func writeSevere(msg string) {
|
||||
if shallLog(SevereLevel) {
|
||||
getWriter().Severe(fmt.Sprintf("%s\n%s", msg, string(debug.Stack())))
|
||||
}
|
||||
}
|
||||
|
||||
func writeSlow(val interface{}, fields ...LogField) {
|
||||
if shallLog(ErrorLevel) {
|
||||
getWriter().Slow(val, addCaller(fields...)...)
|
||||
}
|
||||
}
|
||||
|
||||
func writeStack(msg string) {
|
||||
if shallLog(ErrorLevel) {
|
||||
getWriter().Stack(fmt.Sprintf("%s\n%s", msg, string(debug.Stack())))
|
||||
}
|
||||
}
|
||||
|
||||
func statSync(msg string) {
|
||||
func writeStat(msg string) {
|
||||
if shallLogStat() && shallLog(InfoLevel) {
|
||||
getWriter().Stat(msg)
|
||||
getWriter().Stat(msg, addCaller()...)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
@@ -35,6 +35,12 @@ func (mw *mockWriter) Alert(v interface{}) {
|
||||
output(&mw.builder, levelAlert, v)
|
||||
}
|
||||
|
||||
func (mw *mockWriter) Debug(v interface{}, fields ...LogField) {
|
||||
mw.lock.Lock()
|
||||
defer mw.lock.Unlock()
|
||||
output(&mw.builder, levelDebug, v, fields...)
|
||||
}
|
||||
|
||||
func (mw *mockWriter) Error(v interface{}, fields ...LogField) {
|
||||
mw.lock.Lock()
|
||||
defer mw.lock.Unlock()
|
||||
@@ -212,6 +218,46 @@ func TestStructedLogAlert(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestStructedLogDebug(t *testing.T) {
|
||||
w := new(mockWriter)
|
||||
old := writer.Swap(w)
|
||||
defer writer.Store(old)
|
||||
|
||||
doTestStructedLog(t, levelDebug, w, func(v ...interface{}) {
|
||||
Debug(v...)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStructedLogDebugf(t *testing.T) {
|
||||
w := new(mockWriter)
|
||||
old := writer.Swap(w)
|
||||
defer writer.Store(old)
|
||||
|
||||
doTestStructedLog(t, levelDebug, w, func(v ...interface{}) {
|
||||
Debugf(fmt.Sprint(v...))
|
||||
})
|
||||
}
|
||||
|
||||
func TestStructedLogDebugv(t *testing.T) {
|
||||
w := new(mockWriter)
|
||||
old := writer.Swap(w)
|
||||
defer writer.Store(old)
|
||||
|
||||
doTestStructedLog(t, levelDebug, w, func(v ...interface{}) {
|
||||
Debugv(fmt.Sprint(v...))
|
||||
})
|
||||
}
|
||||
|
||||
func TestStructedLogDebugw(t *testing.T) {
|
||||
w := new(mockWriter)
|
||||
old := writer.Swap(w)
|
||||
defer writer.Store(old)
|
||||
|
||||
doTestStructedLog(t, levelDebug, w, func(v ...interface{}) {
|
||||
Debugw(fmt.Sprint(v...), Field("foo", time.Second))
|
||||
})
|
||||
}
|
||||
|
||||
func TestStructedLogError(t *testing.T) {
|
||||
w := new(mockWriter)
|
||||
old := writer.Swap(w)
|
||||
@@ -461,13 +507,13 @@ func TestStructedLogWithDuration(t *testing.T) {
|
||||
defer writer.Store(old)
|
||||
|
||||
WithDuration(time.Second).Info(message)
|
||||
var entry logEntry
|
||||
var entry map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(w.String()), &entry); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
assert.Equal(t, levelInfo, entry.Level)
|
||||
assert.Equal(t, message, entry.Content)
|
||||
assert.Equal(t, "1000.0ms", entry.Duration)
|
||||
assert.Equal(t, levelInfo, entry[levelKey])
|
||||
assert.Equal(t, message, entry[contentKey])
|
||||
assert.Equal(t, "1000.0ms", entry[durationKey])
|
||||
}
|
||||
|
||||
func TestSetLevel(t *testing.T) {
|
||||
@@ -531,6 +577,7 @@ func TestSetup(t *testing.T) {
|
||||
MustSetup(LogConf{
|
||||
ServiceName: "any",
|
||||
Mode: "console",
|
||||
TimeFormat: timeFormat,
|
||||
})
|
||||
MustSetup(LogConf{
|
||||
ServiceName: "any",
|
||||
@@ -553,13 +600,23 @@ func TestSetup(t *testing.T) {
|
||||
Encoding: plainEncoding,
|
||||
})
|
||||
|
||||
defer os.RemoveAll("CD01CB7D-2705-4F3F-889E-86219BF56F10")
|
||||
assert.NotNil(t, setupWithVolume(LogConf{}))
|
||||
assert.Nil(t, setupWithVolume(LogConf{
|
||||
ServiceName: "CD01CB7D-2705-4F3F-889E-86219BF56F10",
|
||||
}))
|
||||
assert.Nil(t, setupWithVolume(LogConf{
|
||||
ServiceName: "CD01CB7D-2705-4F3F-889E-86219BF56F10",
|
||||
Rotation: sizeRotationRule,
|
||||
}))
|
||||
assert.NotNil(t, setupWithFiles(LogConf{}))
|
||||
assert.Nil(t, setupWithFiles(LogConf{
|
||||
ServiceName: "any",
|
||||
Path: os.TempDir(),
|
||||
Compress: true,
|
||||
KeepDays: 1,
|
||||
MaxBackups: 3,
|
||||
MaxSize: 1024 * 1024,
|
||||
}))
|
||||
setupLogLevel(LogConf{
|
||||
Level: levelInfo,
|
||||
@@ -583,6 +640,8 @@ func TestDisable(t *testing.T) {
|
||||
var opt logOptions
|
||||
WithKeepDays(1)(&opt)
|
||||
WithGzip()(&opt)
|
||||
WithMaxBackups(1)(&opt)
|
||||
WithMaxSize(1024)(&opt)
|
||||
assert.Nil(t, Close())
|
||||
assert.Nil(t, Close())
|
||||
}
|
||||
@@ -599,6 +658,7 @@ func TestDisableStat(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetWriter(t *testing.T) {
|
||||
atomic.StoreUint32(&disableLog, 0)
|
||||
Reset()
|
||||
SetWriter(nopWriter{})
|
||||
assert.NotNil(t, writer.Load())
|
||||
@@ -648,7 +708,7 @@ func BenchmarkCopyByteSlice(b *testing.B) {
|
||||
buf = make([]byte, len(s))
|
||||
copy(buf, s)
|
||||
}
|
||||
fmt.Fprint(ioutil.Discard, buf)
|
||||
fmt.Fprint(io.Discard, buf)
|
||||
}
|
||||
|
||||
func BenchmarkCopyOnWriteByteSlice(b *testing.B) {
|
||||
@@ -657,7 +717,7 @@ func BenchmarkCopyOnWriteByteSlice(b *testing.B) {
|
||||
size := len(s)
|
||||
buf = s[:size:size]
|
||||
}
|
||||
fmt.Fprint(ioutil.Discard, buf)
|
||||
fmt.Fprint(io.Discard, buf)
|
||||
}
|
||||
|
||||
func BenchmarkCacheByteSlice(b *testing.B) {
|
||||
@@ -671,7 +731,7 @@ func BenchmarkCacheByteSlice(b *testing.B) {
|
||||
func BenchmarkLogs(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
|
||||
log.SetOutput(ioutil.Discard)
|
||||
log.SetOutput(io.Discard)
|
||||
for i := 0; i < b.N; i++ {
|
||||
Info(i)
|
||||
}
|
||||
@@ -710,14 +770,16 @@ func put(b []byte) {
|
||||
func doTestStructedLog(t *testing.T, level string, w *mockWriter, write func(...interface{})) {
|
||||
const message = "hello there"
|
||||
write(message)
|
||||
var entry logEntry
|
||||
|
||||
var entry map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(w.String()), &entry); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
assert.Equal(t, level, entry.Level)
|
||||
val, ok := entry.Content.(string)
|
||||
|
||||
assert.Equal(t, level, entry[levelKey])
|
||||
val, ok := entry[contentKey]
|
||||
assert.True(t, ok)
|
||||
assert.True(t, strings.Contains(val, message))
|
||||
assert.True(t, strings.Contains(val.(string), message))
|
||||
}
|
||||
|
||||
func doTestStructedLogConsole(t *testing.T, w *mockWriter, write func(...interface{})) {
|
||||
|
||||
199
core/logx/richlogger.go
Normal file
199
core/logx/richlogger.go
Normal file
@@ -0,0 +1,199 @@
|
||||
package logx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/timex"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
// WithCallerSkip returns a Logger with given caller skip.
|
||||
func WithCallerSkip(skip int) Logger {
|
||||
if skip <= 0 {
|
||||
return new(richLogger)
|
||||
}
|
||||
|
||||
return &richLogger{
|
||||
callerSkip: skip,
|
||||
}
|
||||
}
|
||||
|
||||
// WithContext sets ctx to log, for keeping tracing information.
|
||||
func WithContext(ctx context.Context) Logger {
|
||||
return &richLogger{
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
// WithDuration returns a Logger with given duration.
|
||||
func WithDuration(d time.Duration) Logger {
|
||||
return &richLogger{
|
||||
fields: []LogField{Field(durationKey, timex.ReprOfDuration(d))},
|
||||
}
|
||||
}
|
||||
|
||||
type richLogger struct {
|
||||
ctx context.Context
|
||||
callerSkip int
|
||||
fields []LogField
|
||||
}
|
||||
|
||||
func (l *richLogger) Debug(v ...interface{}) {
|
||||
l.debug(fmt.Sprint(v...))
|
||||
}
|
||||
|
||||
func (l *richLogger) Debugf(format string, v ...interface{}) {
|
||||
l.debug(fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
func (l *richLogger) Debugv(v interface{}) {
|
||||
l.debug(v)
|
||||
}
|
||||
|
||||
func (l *richLogger) Debugw(msg string, fields ...LogField) {
|
||||
l.debug(msg, fields...)
|
||||
}
|
||||
|
||||
func (l *richLogger) Error(v ...interface{}) {
|
||||
l.err(fmt.Sprint(v...))
|
||||
}
|
||||
|
||||
func (l *richLogger) Errorf(format string, v ...interface{}) {
|
||||
l.err(fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
func (l *richLogger) Errorv(v interface{}) {
|
||||
l.err(fmt.Sprint(v))
|
||||
}
|
||||
|
||||
func (l *richLogger) Errorw(msg string, fields ...LogField) {
|
||||
l.err(msg, fields...)
|
||||
}
|
||||
|
||||
func (l *richLogger) Info(v ...interface{}) {
|
||||
l.info(fmt.Sprint(v...))
|
||||
}
|
||||
|
||||
func (l *richLogger) Infof(format string, v ...interface{}) {
|
||||
l.info(fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
func (l *richLogger) Infov(v interface{}) {
|
||||
l.info(v)
|
||||
}
|
||||
|
||||
func (l *richLogger) Infow(msg string, fields ...LogField) {
|
||||
l.info(msg, fields...)
|
||||
}
|
||||
|
||||
func (l *richLogger) Slow(v ...interface{}) {
|
||||
l.slow(fmt.Sprint(v...))
|
||||
}
|
||||
|
||||
func (l *richLogger) Slowf(format string, v ...interface{}) {
|
||||
l.slow(fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
func (l *richLogger) Slowv(v interface{}) {
|
||||
l.slow(v)
|
||||
}
|
||||
|
||||
func (l *richLogger) Sloww(msg string, fields ...LogField) {
|
||||
l.slow(msg, fields...)
|
||||
}
|
||||
|
||||
func (l *richLogger) WithCallerSkip(skip int) Logger {
|
||||
if skip <= 0 {
|
||||
return l
|
||||
}
|
||||
|
||||
l.callerSkip = skip
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *richLogger) WithContext(ctx context.Context) Logger {
|
||||
l.ctx = ctx
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *richLogger) WithDuration(duration time.Duration) Logger {
|
||||
l.fields = append(l.fields, Field(durationKey, timex.ReprOfDuration(duration)))
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *richLogger) WithFields(fields ...LogField) Logger {
|
||||
l.fields = append(l.fields, fields...)
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *richLogger) buildFields(fields ...LogField) []LogField {
|
||||
fields = append(l.fields, fields...)
|
||||
fields = append(fields, Field(callerKey, getCaller(callerDepth+l.callerSkip)))
|
||||
|
||||
if l.ctx == nil {
|
||||
return fields
|
||||
}
|
||||
|
||||
traceID := traceIdFromContext(l.ctx)
|
||||
if len(traceID) > 0 {
|
||||
fields = append(fields, Field(traceKey, traceID))
|
||||
}
|
||||
|
||||
spanID := spanIdFromContext(l.ctx)
|
||||
if len(spanID) > 0 {
|
||||
fields = append(fields, Field(spanKey, spanID))
|
||||
}
|
||||
|
||||
val := l.ctx.Value(fieldsContextKey)
|
||||
if val != nil {
|
||||
if arr, ok := val.([]LogField); ok {
|
||||
fields = append(fields, arr...)
|
||||
}
|
||||
}
|
||||
|
||||
return fields
|
||||
}
|
||||
|
||||
func (l *richLogger) debug(v interface{}, fields ...LogField) {
|
||||
if shallLog(DebugLevel) {
|
||||
getWriter().Debug(v, l.buildFields(fields...)...)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *richLogger) err(v interface{}, fields ...LogField) {
|
||||
if shallLog(ErrorLevel) {
|
||||
getWriter().Error(v, l.buildFields(fields...)...)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *richLogger) info(v interface{}, fields ...LogField) {
|
||||
if shallLog(InfoLevel) {
|
||||
getWriter().Info(v, l.buildFields(fields...)...)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *richLogger) slow(v interface{}, fields ...LogField) {
|
||||
if shallLog(ErrorLevel) {
|
||||
getWriter().Slow(v, l.buildFields(fields...)...)
|
||||
}
|
||||
}
|
||||
|
||||
func spanIdFromContext(ctx context.Context) string {
|
||||
spanCtx := trace.SpanContextFromContext(ctx)
|
||||
if spanCtx.HasSpanID() {
|
||||
return spanCtx.SpanID().String()
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func traceIdFromContext(ctx context.Context) string {
|
||||
spanCtx := trace.SpanContextFromContext(ctx)
|
||||
if spanCtx.HasTraceID() {
|
||||
return spanCtx.TraceID().String()
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package logx
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
@@ -36,6 +37,41 @@ func TestTraceLog(t *testing.T) {
|
||||
validate(t, w.String(), true, true)
|
||||
}
|
||||
|
||||
func TestTraceDebug(t *testing.T) {
|
||||
w := new(mockWriter)
|
||||
old := writer.Swap(w)
|
||||
writer.lock.RLock()
|
||||
defer func() {
|
||||
writer.lock.RUnlock()
|
||||
writer.Store(old)
|
||||
}()
|
||||
|
||||
otp := otel.GetTracerProvider()
|
||||
tp := sdktrace.NewTracerProvider(sdktrace.WithSampler(sdktrace.AlwaysSample()))
|
||||
otel.SetTracerProvider(tp)
|
||||
defer otel.SetTracerProvider(otp)
|
||||
|
||||
ctx, span := tp.Tracer("foo").Start(context.Background(), "bar")
|
||||
defer span.End()
|
||||
|
||||
l := WithContext(ctx)
|
||||
SetLevel(DebugLevel)
|
||||
l.WithDuration(time.Second).Debug(testlog)
|
||||
assert.True(t, strings.Contains(w.String(), traceKey))
|
||||
assert.True(t, strings.Contains(w.String(), spanKey))
|
||||
w.Reset()
|
||||
l.WithDuration(time.Second).Debugf(testlog)
|
||||
validate(t, w.String(), true, true)
|
||||
w.Reset()
|
||||
l.WithDuration(time.Second).Debugv(testlog)
|
||||
validate(t, w.String(), true, true)
|
||||
w.Reset()
|
||||
l.WithDuration(time.Second).Debugw(testlog, Field("foo", "bar"))
|
||||
validate(t, w.String(), true, true)
|
||||
assert.True(t, strings.Contains(w.String(), "foo"), w.String())
|
||||
assert.True(t, strings.Contains(w.String(), "bar"), w.String())
|
||||
}
|
||||
|
||||
func TestTraceError(t *testing.T) {
|
||||
w := new(mockWriter)
|
||||
old := writer.Swap(w)
|
||||
@@ -201,7 +237,7 @@ func TestLogWithFields(t *testing.T) {
|
||||
writer.Store(old)
|
||||
}()
|
||||
|
||||
ctx := WithFields(context.Background(), Field("foo", "bar"))
|
||||
ctx := ContextWithFields(context.Background(), Field("foo", "bar"))
|
||||
l := WithContext(ctx)
|
||||
SetLevel(InfoLevel)
|
||||
l.Info(testlog)
|
||||
@@ -211,6 +247,48 @@ func TestLogWithFields(t *testing.T) {
|
||||
assert.Equal(t, "bar", val.Foo)
|
||||
}
|
||||
|
||||
func TestLogWithCallerSkip(t *testing.T) {
|
||||
w := new(mockWriter)
|
||||
old := writer.Swap(w)
|
||||
writer.lock.RLock()
|
||||
defer func() {
|
||||
writer.lock.RUnlock()
|
||||
writer.Store(old)
|
||||
}()
|
||||
|
||||
l := WithCallerSkip(1).WithCallerSkip(0)
|
||||
p := func(v string) {
|
||||
l.Info(v)
|
||||
}
|
||||
|
||||
file, line := getFileLine()
|
||||
p(testlog)
|
||||
assert.True(t, w.Contains(fmt.Sprintf("%s:%d", file, line+1)))
|
||||
|
||||
w.Reset()
|
||||
l = WithCallerSkip(0).WithCallerSkip(1)
|
||||
file, line = getFileLine()
|
||||
p(testlog)
|
||||
assert.True(t, w.Contains(fmt.Sprintf("%s:%d", file, line+1)))
|
||||
}
|
||||
|
||||
func TestLoggerWithFields(t *testing.T) {
|
||||
w := new(mockWriter)
|
||||
old := writer.Swap(w)
|
||||
writer.lock.RLock()
|
||||
defer func() {
|
||||
writer.lock.RUnlock()
|
||||
writer.Store(old)
|
||||
}()
|
||||
|
||||
l := WithContext(context.Background()).WithFields(Field("foo", "bar"))
|
||||
l.Info(testlog)
|
||||
|
||||
var val mockValue
|
||||
assert.Nil(t, json.Unmarshal([]byte(w.String()), &val))
|
||||
assert.Equal(t, "bar", val.Foo)
|
||||
}
|
||||
|
||||
func validate(t *testing.T, body string, expectedTrace, expectedSpan bool) {
|
||||
var val mockValue
|
||||
dec := json.NewDecoder(strings.NewReader(body))
|
||||
@@ -115,7 +115,9 @@ func (r *DailyRotateRule) OutdatedFiles() []string {
|
||||
|
||||
var buf strings.Builder
|
||||
boundary := time.Now().Add(-time.Hour * time.Duration(hoursPerDay*r.days)).Format(dateFormat)
|
||||
fmt.Fprintf(&buf, "%s%s%s", r.filename, r.delimiter, boundary)
|
||||
buf.WriteString(r.filename)
|
||||
buf.WriteString(r.delimiter)
|
||||
buf.WriteString(boundary)
|
||||
if r.gzip {
|
||||
buf.WriteString(gzipExt)
|
||||
}
|
||||
|
||||
@@ -42,11 +42,18 @@ func captureOutput(f func()) string {
|
||||
}
|
||||
|
||||
func getContent(jsonStr string) string {
|
||||
var entry logEntry
|
||||
var entry map[string]interface{}
|
||||
json.Unmarshal([]byte(jsonStr), &entry)
|
||||
val, ok := entry.Content.(string)
|
||||
if ok {
|
||||
return val
|
||||
|
||||
val, ok := entry[contentKey]
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return ""
|
||||
|
||||
str, ok := val.(string)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
@@ -3,8 +3,10 @@ package logx
|
||||
import "errors"
|
||||
|
||||
const (
|
||||
// InfoLevel logs everything
|
||||
InfoLevel uint32 = iota
|
||||
// DebugLevel logs everything
|
||||
DebugLevel uint32 = iota
|
||||
// InfoLevel does not include debugs
|
||||
InfoLevel
|
||||
// ErrorLevel includes errors, slows, stacks
|
||||
ErrorLevel
|
||||
// SevereLevel only log severe messages
|
||||
@@ -37,6 +39,7 @@ const (
|
||||
levelFatal = "fatal"
|
||||
levelSlow = "slow"
|
||||
levelStat = "stat"
|
||||
levelDebug = "debug"
|
||||
|
||||
backupFileDelimiter = "-"
|
||||
flags = 0x0
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
package logx
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
fatihcolor "github.com/fatih/color"
|
||||
"github.com/zeromicro/go-zero/core/color"
|
||||
)
|
||||
|
||||
@@ -18,6 +18,7 @@ type (
|
||||
Writer interface {
|
||||
Alert(v interface{})
|
||||
Close() error
|
||||
Debug(v interface{}, fields ...LogField)
|
||||
Error(v interface{}, fields ...LogField)
|
||||
Info(v interface{}, fields ...LogField)
|
||||
Severe(v interface{})
|
||||
@@ -67,6 +68,17 @@ func (w *atomicWriter) Store(v Writer) {
|
||||
w.writer = v
|
||||
}
|
||||
|
||||
func (w *atomicWriter) StoreIfNil(v Writer) Writer {
|
||||
w.lock.Lock()
|
||||
defer w.lock.Unlock()
|
||||
|
||||
if w.writer == nil {
|
||||
w.writer = v
|
||||
}
|
||||
|
||||
return w.writer
|
||||
}
|
||||
|
||||
func (w *atomicWriter) Swap(v Writer) Writer {
|
||||
w.lock.Lock()
|
||||
defer w.lock.Unlock()
|
||||
@@ -76,8 +88,8 @@ func (w *atomicWriter) Swap(v Writer) Writer {
|
||||
}
|
||||
|
||||
func newConsoleWriter() Writer {
|
||||
outLog := newLogWriter(log.New(os.Stdout, "", flags))
|
||||
errLog := newLogWriter(log.New(os.Stderr, "", flags))
|
||||
outLog := newLogWriter(log.New(fatihcolor.Output, "", flags))
|
||||
errLog := newLogWriter(log.New(fatihcolor.Error, "", flags))
|
||||
return &concreteWriter{
|
||||
infoLog: outLog,
|
||||
errorLog: errLog,
|
||||
@@ -183,6 +195,10 @@ func (w *concreteWriter) Close() error {
|
||||
return w.statLog.Close()
|
||||
}
|
||||
|
||||
func (w *concreteWriter) Debug(v interface{}, fields ...LogField) {
|
||||
output(w.infoLog, levelDebug, v, fields...)
|
||||
}
|
||||
|
||||
func (w *concreteWriter) Error(v interface{}, fields ...LogField) {
|
||||
output(w.errorLog, levelError, v, fields...)
|
||||
}
|
||||
@@ -216,6 +232,9 @@ func (n nopWriter) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n nopWriter) Debug(_ interface{}, _ ...LogField) {
|
||||
}
|
||||
|
||||
func (n nopWriter) Error(_ interface{}, _ ...LogField) {
|
||||
}
|
||||
|
||||
@@ -244,14 +263,23 @@ func buildFields(fields ...LogField) []string {
|
||||
return items
|
||||
}
|
||||
|
||||
func combineGlobalFields(fields []LogField) []LogField {
|
||||
globals := globalFields.Load()
|
||||
if globals == nil {
|
||||
return fields
|
||||
}
|
||||
|
||||
return append(globals.([]LogField), fields...)
|
||||
}
|
||||
|
||||
func output(writer io.Writer, level string, val interface{}, fields ...LogField) {
|
||||
fields = append(fields, Field(callerKey, getCaller(callerDepth)))
|
||||
fields = combineGlobalFields(fields)
|
||||
|
||||
switch atomic.LoadUint32(&encoding) {
|
||||
case plainEncodingType:
|
||||
writePlainAny(writer, level, val, buildFields(fields...)...)
|
||||
default:
|
||||
entry := make(logEntryWithFields)
|
||||
entry := make(logEntry)
|
||||
for _, field := range fields {
|
||||
entry[field.Key] = field.Value
|
||||
}
|
||||
@@ -275,6 +303,8 @@ func wrapLevelWithColor(level string) string {
|
||||
colour = color.FgBlue
|
||||
case levelSlow:
|
||||
colour = color.FgYellow
|
||||
case levelDebug:
|
||||
colour = color.FgYellow
|
||||
case levelStat:
|
||||
colour = color.FgGreen
|
||||
}
|
||||
@@ -307,34 +337,12 @@ func writePlainAny(writer io.Writer, level string, val interface{}, fields ...st
|
||||
case fmt.Stringer:
|
||||
writePlainText(writer, level, v.String(), fields...)
|
||||
default:
|
||||
var buf strings.Builder
|
||||
buf.WriteString(getTimestamp())
|
||||
buf.WriteByte(plainEncodingSep)
|
||||
buf.WriteString(level)
|
||||
buf.WriteByte(plainEncodingSep)
|
||||
if err := json.NewEncoder(&buf).Encode(val); err != nil {
|
||||
log.Println(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
for _, item := range fields {
|
||||
buf.WriteByte(plainEncodingSep)
|
||||
buf.WriteString(item)
|
||||
}
|
||||
buf.WriteByte('\n')
|
||||
if writer == nil {
|
||||
log.Println(buf.String())
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := fmt.Fprint(writer, buf.String()); err != nil {
|
||||
log.Println(err.Error())
|
||||
}
|
||||
writePlainValue(writer, level, v, fields...)
|
||||
}
|
||||
}
|
||||
|
||||
func writePlainText(writer io.Writer, level, msg string, fields ...string) {
|
||||
var buf strings.Builder
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(getTimestamp())
|
||||
buf.WriteByte(plainEncodingSep)
|
||||
buf.WriteString(level)
|
||||
@@ -350,7 +358,33 @@ func writePlainText(writer io.Writer, level, msg string, fields ...string) {
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := fmt.Fprint(writer, buf.String()); err != nil {
|
||||
if _, err := writer.Write(buf.Bytes()); err != nil {
|
||||
log.Println(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func writePlainValue(writer io.Writer, level string, val interface{}, fields ...string) {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString(getTimestamp())
|
||||
buf.WriteByte(plainEncodingSep)
|
||||
buf.WriteString(level)
|
||||
buf.WriteByte(plainEncodingSep)
|
||||
if err := json.NewEncoder(&buf).Encode(val); err != nil {
|
||||
log.Println(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
for _, item := range fields {
|
||||
buf.WriteByte(plainEncodingSep)
|
||||
buf.WriteString(item)
|
||||
}
|
||||
buf.WriteByte('\n')
|
||||
if writer == nil {
|
||||
log.Println(buf.String())
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := writer.Write(buf.Bytes()); err != nil {
|
||||
log.Println(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@ func TestNewWriter(t *testing.T) {
|
||||
w := NewWriter(&buf)
|
||||
w.Info(literal)
|
||||
assert.Contains(t, buf.String(), literal)
|
||||
buf.Reset()
|
||||
w.Debug(literal)
|
||||
assert.Contains(t, buf.String(), literal)
|
||||
}
|
||||
|
||||
func TestConsoleWriter(t *testing.T) {
|
||||
@@ -97,6 +100,7 @@ func TestNopWriter(t *testing.T) {
|
||||
assert.NotPanics(t, func() {
|
||||
var w nopWriter
|
||||
w.Alert("foo")
|
||||
w.Debug("foo")
|
||||
w.Error("foo")
|
||||
w.Info("foo")
|
||||
w.Severe("foo")
|
||||
@@ -123,6 +127,12 @@ func TestWritePlainAny(t *testing.T) {
|
||||
writePlainAny(nil, levelInfo, "foo")
|
||||
assert.Contains(t, buf.String(), "foo")
|
||||
|
||||
buf.Reset()
|
||||
writePlainAny(nil, levelDebug, make(chan int))
|
||||
assert.Contains(t, buf.String(), "unsupported type")
|
||||
writePlainAny(nil, levelDebug, 100)
|
||||
assert.Contains(t, buf.String(), "100")
|
||||
|
||||
buf.Reset()
|
||||
writePlainAny(nil, levelError, make(chan int))
|
||||
assert.Contains(t, buf.String(), "unsupported type")
|
||||
|
||||
@@ -261,6 +261,78 @@ func TestMarshal_RangeOut(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshal_RangeIllegal(t *testing.T) {
|
||||
tests := []interface{}{
|
||||
struct {
|
||||
Int int `json:"int,range=[3:1]"`
|
||||
}{
|
||||
Int: 2,
|
||||
},
|
||||
struct {
|
||||
Int int `json:"int,range=(3:1]"`
|
||||
}{
|
||||
Int: 2,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
_, err := Marshal(test)
|
||||
assert.Equal(t, err, errNumberRange)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshal_RangeLeftEqualsToRight(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
value interface{}
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "left inclusive, right inclusive",
|
||||
value: struct {
|
||||
Int int `json:"int,range=[2:2]"`
|
||||
}{
|
||||
Int: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "left inclusive, right exclusive",
|
||||
value: struct {
|
||||
Int int `json:"int,range=[2:2)"`
|
||||
}{
|
||||
Int: 2,
|
||||
},
|
||||
err: errNumberRange,
|
||||
},
|
||||
{
|
||||
name: "left exclusive, right inclusive",
|
||||
value: struct {
|
||||
Int int `json:"int,range=(2:2]"`
|
||||
}{
|
||||
Int: 2,
|
||||
},
|
||||
err: errNumberRange,
|
||||
},
|
||||
{
|
||||
name: "left exclusive, right exclusive",
|
||||
value: struct {
|
||||
Int int `json:"int,range=(2:2)"`
|
||||
}{
|
||||
Int: 2,
|
||||
},
|
||||
err: errNumberRange,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
_, err := Marshal(test.value)
|
||||
assert.Equal(t, test.err, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshal_FromString(t *testing.T) {
|
||||
v := struct {
|
||||
Age int `json:"age,string"`
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package mapping
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -25,9 +26,9 @@ var (
|
||||
errValueNotStruct = errors.New("value type is not struct")
|
||||
keyUnmarshaler = NewUnmarshaler(defaultKeyName)
|
||||
durationType = reflect.TypeOf(time.Duration(0))
|
||||
cacheKeys map[string][]string
|
||||
cacheKeys = make(map[string][]string)
|
||||
cacheKeysLock sync.Mutex
|
||||
defaultCache map[string]interface{}
|
||||
defaultCache = make(map[string]interface{})
|
||||
defaultCacheLock sync.Mutex
|
||||
emptyMap = map[string]interface{}{}
|
||||
emptyValue = reflect.ValueOf(lang.Placeholder)
|
||||
@@ -49,11 +50,6 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
cacheKeys = make(map[string][]string)
|
||||
defaultCache = make(map[string]interface{})
|
||||
}
|
||||
|
||||
// NewUnmarshaler returns a Unmarshaler.
|
||||
func NewUnmarshaler(key string, opts ...UnmarshalOption) *Unmarshaler {
|
||||
unmarshaler := Unmarshaler{
|
||||
@@ -144,7 +140,6 @@ func (u *Unmarshaler) processAnonymousFieldOptional(field reflect.StructField, v
|
||||
filled = true
|
||||
maybeNewValue(field, value)
|
||||
indirectValue = reflect.Indirect(value)
|
||||
|
||||
}
|
||||
if err = u.processField(subField, indirectValue.Field(i), m, fullName); err != nil {
|
||||
return err
|
||||
@@ -205,6 +200,8 @@ func (u *Unmarshaler) processFieldNotFromString(field reflect.StructField, value
|
||||
return u.processFieldStruct(field, value, mapValue, fullName)
|
||||
case valueKind == reflect.Map && typeKind == reflect.Map:
|
||||
return u.fillMap(field, value, mapValue)
|
||||
case valueKind == reflect.String && typeKind == reflect.Map:
|
||||
return u.fillMapFromString(value, mapValue)
|
||||
case valueKind == reflect.String && typeKind == reflect.Slice:
|
||||
return u.fillSliceFromString(fieldType, value, mapValue)
|
||||
case valueKind == reflect.String && derefedFieldType == durationType:
|
||||
@@ -322,6 +319,28 @@ func (u *Unmarshaler) processFieldStructWithMap(field reflect.StructField, value
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Unmarshaler) processFieldTextUnmarshaler(field reflect.StructField, value reflect.Value,
|
||||
mapValue interface{}) (bool, error) {
|
||||
var tval encoding.TextUnmarshaler
|
||||
var ok bool
|
||||
|
||||
if field.Type.Kind() == reflect.Ptr {
|
||||
tval, ok = value.Interface().(encoding.TextUnmarshaler)
|
||||
} else {
|
||||
tval, ok = value.Addr().Interface().(encoding.TextUnmarshaler)
|
||||
}
|
||||
if ok {
|
||||
switch mv := mapValue.(type) {
|
||||
case string:
|
||||
return true, tval.UnmarshalText([]byte(mv))
|
||||
case []byte:
|
||||
return true, tval.UnmarshalText(mv)
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (u *Unmarshaler) processNamedField(field reflect.StructField, value reflect.Value,
|
||||
m Valuer, fullName string) error {
|
||||
key, opts, err := u.parseOptionsWithContext(field, m, fullName)
|
||||
@@ -352,8 +371,16 @@ func (u *Unmarshaler) processNamedFieldWithValue(field reflect.StructField, valu
|
||||
return fmt.Errorf("field %s mustn't be nil", key)
|
||||
}
|
||||
|
||||
if !value.CanSet() {
|
||||
return fmt.Errorf("field %s is not settable", key)
|
||||
}
|
||||
|
||||
maybeNewValue(field, value)
|
||||
|
||||
if yes, err := u.processFieldTextUnmarshaler(field, value, mapValue); yes {
|
||||
return err
|
||||
}
|
||||
|
||||
fieldKind := Deref(field.Type).Kind()
|
||||
switch fieldKind {
|
||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.Struct:
|
||||
@@ -442,6 +469,27 @@ func (u *Unmarshaler) fillMap(field reflect.StructField, value reflect.Value, ma
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Unmarshaler) fillMapFromString(value reflect.Value, mapValue interface{}) error {
|
||||
if !value.CanSet() {
|
||||
return errValueNotSettable
|
||||
}
|
||||
|
||||
switch v := mapValue.(type) {
|
||||
case fmt.Stringer:
|
||||
if err := jsonx.UnmarshalFromString(v.String(), value.Addr().Interface()); err != nil {
|
||||
return err
|
||||
}
|
||||
case string:
|
||||
if err := jsonx.UnmarshalFromString(v, value.Addr().Interface()); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return errUnsupportedType
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Unmarshaler) fillSlice(fieldType reflect.Type, value reflect.Value, mapValue interface{}) error {
|
||||
if !value.CanSet() {
|
||||
return errValueNotSettable
|
||||
|
||||
@@ -2,11 +2,13 @@ package mapping
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/zeromicro/go-zero/core/stringx"
|
||||
)
|
||||
@@ -467,6 +469,146 @@ func TestUnmarshalIntSliceFromString(t *testing.T) {
|
||||
ast.Equal(2, v.Values[1])
|
||||
}
|
||||
|
||||
func TestUnmarshalIntMapFromString(t *testing.T) {
|
||||
var v struct {
|
||||
Sort map[string]int `key:"sort"`
|
||||
}
|
||||
m := map[string]interface{}{
|
||||
"sort": `{"value":12345,"zeroVal":0,"nullVal":null}`,
|
||||
}
|
||||
|
||||
ast := assert.New(t)
|
||||
ast.Nil(UnmarshalKey(m, &v))
|
||||
ast.Equal(3, len(v.Sort))
|
||||
ast.Equal(12345, v.Sort["value"])
|
||||
ast.Equal(0, v.Sort["zeroVal"])
|
||||
ast.Equal(0, v.Sort["nullVal"])
|
||||
}
|
||||
|
||||
func TestUnmarshalBoolMapFromString(t *testing.T) {
|
||||
var v struct {
|
||||
Sort map[string]bool `key:"sort"`
|
||||
}
|
||||
m := map[string]interface{}{
|
||||
"sort": `{"value":true,"zeroVal":false,"nullVal":null}`,
|
||||
}
|
||||
|
||||
ast := assert.New(t)
|
||||
ast.Nil(UnmarshalKey(m, &v))
|
||||
ast.Equal(3, len(v.Sort))
|
||||
ast.Equal(true, v.Sort["value"])
|
||||
ast.Equal(false, v.Sort["zeroVal"])
|
||||
ast.Equal(false, v.Sort["nullVal"])
|
||||
}
|
||||
|
||||
type CustomStringer string
|
||||
|
||||
type UnsupportedStringer string
|
||||
|
||||
func (c CustomStringer) String() string {
|
||||
return fmt.Sprintf("{%s}", string(c))
|
||||
}
|
||||
|
||||
func TestUnmarshalStringMapFromStringer(t *testing.T) {
|
||||
var v struct {
|
||||
Sort map[string]string `key:"sort"`
|
||||
}
|
||||
m := map[string]interface{}{
|
||||
"sort": CustomStringer(`"value":"ascend","emptyStr":""`),
|
||||
}
|
||||
|
||||
ast := assert.New(t)
|
||||
ast.Nil(UnmarshalKey(m, &v))
|
||||
ast.Equal(2, len(v.Sort))
|
||||
ast.Equal("ascend", v.Sort["value"])
|
||||
ast.Equal("", v.Sort["emptyStr"])
|
||||
}
|
||||
|
||||
func TestUnmarshalStringMapFromUnsupportedType(t *testing.T) {
|
||||
var v struct {
|
||||
Sort map[string]string `key:"sort"`
|
||||
}
|
||||
m := map[string]interface{}{
|
||||
"sort": UnsupportedStringer(`{"value":"ascend","emptyStr":""}`),
|
||||
}
|
||||
|
||||
ast := assert.New(t)
|
||||
ast.NotNil(UnmarshalKey(m, &v))
|
||||
}
|
||||
|
||||
func TestUnmarshalStringMapFromNotSettableValue(t *testing.T) {
|
||||
var v struct {
|
||||
sort map[string]string `key:"sort"`
|
||||
psort *map[string]string `key:"sort"`
|
||||
}
|
||||
m := map[string]interface{}{
|
||||
"sort": `{"value":"ascend","emptyStr":""}`,
|
||||
"psort": `{"value":"ascend","emptyStr":""}`,
|
||||
}
|
||||
|
||||
ast := assert.New(t)
|
||||
ast.NotNil(UnmarshalKey(m, &v))
|
||||
}
|
||||
|
||||
func TestUnmarshalStringMapFromString(t *testing.T) {
|
||||
var v struct {
|
||||
Sort map[string]string `key:"sort"`
|
||||
}
|
||||
m := map[string]interface{}{
|
||||
"sort": `{"value":"ascend","emptyStr":""}`,
|
||||
}
|
||||
|
||||
ast := assert.New(t)
|
||||
ast.Nil(UnmarshalKey(m, &v))
|
||||
ast.Equal(2, len(v.Sort))
|
||||
ast.Equal("ascend", v.Sort["value"])
|
||||
ast.Equal("", v.Sort["emptyStr"])
|
||||
}
|
||||
|
||||
func TestUnmarshalStructMapFromString(t *testing.T) {
|
||||
var v struct {
|
||||
Filter map[string]struct {
|
||||
Field1 bool `json:"field1"`
|
||||
Field2 int64 `json:"field2,string"`
|
||||
Field3 string `json:"field3"`
|
||||
Field4 *string `json:"field4"`
|
||||
Field5 []string `json:"field5"`
|
||||
} `key:"filter"`
|
||||
}
|
||||
m := map[string]interface{}{
|
||||
"filter": `{"obj":{"field1":true,"field2":"1573570455447539712","field3":"this is a string",
|
||||
"field4":"this is a string pointer","field5":["str1","str2"]}}`,
|
||||
}
|
||||
|
||||
ast := assert.New(t)
|
||||
ast.Nil(UnmarshalKey(m, &v))
|
||||
ast.Equal(1, len(v.Filter))
|
||||
ast.NotNil(v.Filter["obj"])
|
||||
ast.Equal(true, v.Filter["obj"].Field1)
|
||||
ast.Equal(int64(1573570455447539712), v.Filter["obj"].Field2)
|
||||
ast.Equal("this is a string", v.Filter["obj"].Field3)
|
||||
ast.Equal("this is a string pointer", *v.Filter["obj"].Field4)
|
||||
ast.ElementsMatch([]string{"str1", "str2"}, v.Filter["obj"].Field5)
|
||||
}
|
||||
|
||||
func TestUnmarshalStringSliceMapFromString(t *testing.T) {
|
||||
var v struct {
|
||||
Filter map[string][]string `key:"filter"`
|
||||
}
|
||||
m := map[string]interface{}{
|
||||
"filter": `{"assignType":null,"status":["process","comment"],"rate":[]}`,
|
||||
}
|
||||
|
||||
ast := assert.New(t)
|
||||
ast.Nil(UnmarshalKey(m, &v))
|
||||
ast.Equal(3, len(v.Filter))
|
||||
ast.Equal([]string(nil), v.Filter["assignType"])
|
||||
ast.Equal(2, len(v.Filter["status"]))
|
||||
ast.Equal("process", v.Filter["status"][0])
|
||||
ast.Equal("comment", v.Filter["status"][1])
|
||||
ast.Equal(0, len(v.Filter["rate"]))
|
||||
}
|
||||
|
||||
func TestUnmarshalStruct(t *testing.T) {
|
||||
type address struct {
|
||||
City string `key:"city"`
|
||||
@@ -2879,6 +3021,24 @@ func TestUnmarshalJsonReaderArrayString(t *testing.T) {
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestGoogleUUID(t *testing.T) {
|
||||
var val struct {
|
||||
Uid uuid.UUID `json:"uid,optional"`
|
||||
Uidp *uuid.UUID `json:"uidp,optional"`
|
||||
}
|
||||
assert.NoError(t, UnmarshalJsonBytes([]byte(`{
|
||||
"uid": "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
|
||||
"uidp": "6ba7b810-9dad-11d1-80b4-00c04fd430c9"}`), &val))
|
||||
assert.Equal(t, "6ba7b810-9dad-11d1-80b4-00c04fd430c8", val.Uid.String())
|
||||
assert.Equal(t, "6ba7b810-9dad-11d1-80b4-00c04fd430c9", val.Uidp.String())
|
||||
assert.NoError(t, UnmarshalJsonMap(map[string]interface{}{
|
||||
"uid": []byte("6ba7b810-9dad-11d1-80b4-00c04fd430c1"),
|
||||
"uidp": []byte("6ba7b810-9dad-11d1-80b4-00c04fd430c2"),
|
||||
}, &val))
|
||||
assert.Equal(t, "6ba7b810-9dad-11d1-80b4-00c04fd430c1", val.Uid.String())
|
||||
assert.Equal(t, "6ba7b810-9dad-11d1-80b4-00c04fd430c2", val.Uidp.String())
|
||||
}
|
||||
|
||||
func BenchmarkDefaultValue(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
var a struct {
|
||||
|
||||
@@ -311,6 +311,20 @@ func parseNumberRange(str string) (*numberRange, error) {
|
||||
right = math.MaxFloat64
|
||||
}
|
||||
|
||||
if left > right {
|
||||
return nil, errNumberRange
|
||||
}
|
||||
|
||||
// [2:2] valid
|
||||
// [2:2) invalid
|
||||
// (2:2] invalid
|
||||
// (2:2) invalid
|
||||
if left == right {
|
||||
if !leftInclude || !rightInclude {
|
||||
return nil, errNumberRange
|
||||
}
|
||||
}
|
||||
|
||||
return &numberRange{
|
||||
left: left,
|
||||
leftInclude: leftInclude,
|
||||
|
||||
@@ -63,7 +63,7 @@ func cleanupMapValue(v interface{}) interface{} {
|
||||
}
|
||||
}
|
||||
|
||||
func unmarshal(unmarshaler *Unmarshaler, o interface{}, v interface{}) error {
|
||||
func unmarshal(unmarshaler *Unmarshaler, o, v interface{}) error {
|
||||
if m, ok := o.(map[string]interface{}); ok {
|
||||
return unmarshaler.Unmarshal(m, v)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package metric
|
||||
import (
|
||||
prom "github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/zeromicro/go-zero/core/proc"
|
||||
"github.com/zeromicro/go-zero/core/prometheus"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -47,10 +48,18 @@ func NewCounterVec(cfg *CounterVecOpts) CounterVec {
|
||||
}
|
||||
|
||||
func (cv *promCounterVec) Inc(labels ...string) {
|
||||
if !prometheus.Enabled() {
|
||||
return
|
||||
}
|
||||
|
||||
cv.counter.WithLabelValues(labels...).Inc()
|
||||
}
|
||||
|
||||
func (cv *promCounterVec) Add(v float64, labels ...string) {
|
||||
if !prometheus.Enabled() {
|
||||
return
|
||||
}
|
||||
|
||||
cv.counter.WithLabelValues(labels...).Add(v)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/zeromicro/go-zero/core/prometheus"
|
||||
)
|
||||
|
||||
func TestNewCounterVec(t *testing.T) {
|
||||
@@ -21,6 +22,7 @@ func TestNewCounterVec(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCounterIncr(t *testing.T) {
|
||||
startAgent()
|
||||
counterVec := NewCounterVec(&CounterVecOpts{
|
||||
Namespace: "http_client",
|
||||
Subsystem: "call",
|
||||
@@ -37,6 +39,7 @@ func TestCounterIncr(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCounterAdd(t *testing.T) {
|
||||
startAgent()
|
||||
counterVec := NewCounterVec(&CounterVecOpts{
|
||||
Namespace: "rpc_server",
|
||||
Subsystem: "requests",
|
||||
@@ -51,3 +54,11 @@ func TestCounterAdd(t *testing.T) {
|
||||
r := testutil.ToFloat64(cv.counter)
|
||||
assert.Equal(t, float64(33), r)
|
||||
}
|
||||
|
||||
func startAgent() {
|
||||
prometheus.StartAgent(prometheus.Config{
|
||||
Host: "127.0.0.1",
|
||||
Port: 9101,
|
||||
Path: "/metrics",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package metric
|
||||
import (
|
||||
prom "github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/zeromicro/go-zero/core/proc"
|
||||
"github.com/zeromicro/go-zero/core/prometheus"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -50,14 +51,26 @@ func NewGaugeVec(cfg *GaugeVecOpts) GaugeVec {
|
||||
}
|
||||
|
||||
func (gv *promGaugeVec) Inc(labels ...string) {
|
||||
if !prometheus.Enabled() {
|
||||
return
|
||||
}
|
||||
|
||||
gv.gauge.WithLabelValues(labels...).Inc()
|
||||
}
|
||||
|
||||
func (gv *promGaugeVec) Add(v float64, labels ...string) {
|
||||
if !prometheus.Enabled() {
|
||||
return
|
||||
}
|
||||
|
||||
gv.gauge.WithLabelValues(labels...).Add(v)
|
||||
}
|
||||
|
||||
func (gv *promGaugeVec) Set(v float64, labels ...string) {
|
||||
if !prometheus.Enabled() {
|
||||
return
|
||||
}
|
||||
|
||||
gv.gauge.WithLabelValues(labels...).Set(v)
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ func TestNewGaugeVec(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGaugeInc(t *testing.T) {
|
||||
startAgent()
|
||||
gaugeVec := NewGaugeVec(&GaugeVecOpts{
|
||||
Namespace: "rpc_client2",
|
||||
Subsystem: "requests",
|
||||
@@ -37,6 +38,7 @@ func TestGaugeInc(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGaugeAdd(t *testing.T) {
|
||||
startAgent()
|
||||
gaugeVec := NewGaugeVec(&GaugeVecOpts{
|
||||
Namespace: "rpc_client",
|
||||
Subsystem: "request",
|
||||
@@ -53,6 +55,7 @@ func TestGaugeAdd(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGaugeSet(t *testing.T) {
|
||||
startAgent()
|
||||
gaugeVec := NewGaugeVec(&GaugeVecOpts{
|
||||
Namespace: "http_client",
|
||||
Subsystem: "request",
|
||||
|
||||
@@ -3,6 +3,7 @@ package metric
|
||||
import (
|
||||
prom "github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/zeromicro/go-zero/core/proc"
|
||||
"github.com/zeromicro/go-zero/core/prometheus"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -53,6 +54,10 @@ func NewHistogramVec(cfg *HistogramVecOpts) HistogramVec {
|
||||
}
|
||||
|
||||
func (hv *promHistogramVec) Observe(v int64, labels ...string) {
|
||||
if !prometheus.Enabled() {
|
||||
return
|
||||
}
|
||||
|
||||
hv.histogram.WithLabelValues(labels...).Observe(float64(v))
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ func TestNewHistogramVec(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHistogramObserve(t *testing.T) {
|
||||
startAgent()
|
||||
histogramVec := NewHistogramVec(&HistogramVecOpts{
|
||||
Name: "counts",
|
||||
Help: "rpc server requests duration(ms).",
|
||||
|
||||
@@ -145,7 +145,7 @@ func MapReduceChan(source <-chan interface{}, mapper MapperFunc, reducer Reducer
|
||||
return mapReduceWithPanicChan(source, panicChan, mapper, reducer, opts...)
|
||||
}
|
||||
|
||||
// MapReduceChan maps all elements from source, and reduce the output elements with given reducer.
|
||||
// mapReduceWithPanicChan maps all elements from source, and reduce the output elements with given reducer.
|
||||
func mapReduceWithPanicChan(source <-chan interface{}, panicChan *onceChan, mapper MapperFunc,
|
||||
reducer ReducerFunc, opts ...Option) (interface{}, error) {
|
||||
options := buildOptions(opts...)
|
||||
@@ -212,6 +212,8 @@ func mapReduceWithPanicChan(source <-chan interface{}, panicChan *onceChan, mapp
|
||||
cancel(context.DeadlineExceeded)
|
||||
return nil, context.DeadlineExceeded
|
||||
case v := <-panicChan.channel:
|
||||
// drain output here, otherwise for loop panic in defer
|
||||
drain(output)
|
||||
panic(v)
|
||||
case v, ok := <-output:
|
||||
if err := retErr.Load(); err != nil {
|
||||
|
||||
@@ -19,7 +19,7 @@ func FuzzMapReduce(f *testing.F) {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
f.Add(uint(10), uint(runtime.NumCPU()))
|
||||
f.Fuzz(func(t *testing.T, num uint, workers uint) {
|
||||
f.Fuzz(func(t *testing.T, num, workers uint) {
|
||||
n := int64(num)%5000 + 5000
|
||||
genPanic := rand.Intn(100) == 0
|
||||
mapperPanic := rand.Intn(100) == 0
|
||||
|
||||
@@ -3,7 +3,7 @@ package mr
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
var errDummy = errors.New("dummy")
|
||||
|
||||
func init() {
|
||||
log.SetOutput(ioutil.Discard)
|
||||
log.SetOutput(io.Discard)
|
||||
}
|
||||
|
||||
func TestFinish(t *testing.T) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/zeromicro/go-zero/core/load"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/proc"
|
||||
"github.com/zeromicro/go-zero/core/prometheus"
|
||||
"github.com/zeromicro/go-zero/core/stat"
|
||||
"github.com/zeromicro/go-zero/core/trace"
|
||||
@@ -56,6 +57,9 @@ func (sc ServiceConf) SetUp() error {
|
||||
sc.Telemetry.Name = sc.Name
|
||||
}
|
||||
trace.StartAgent(sc.Telemetry)
|
||||
proc.AddShutdownListener(func() {
|
||||
trace.StopAgent()
|
||||
})
|
||||
|
||||
if len(sc.MetricsUrl) > 0 {
|
||||
stat.SetReportWriter(stat.NewRemoteWriter(sc.MetricsUrl))
|
||||
|
||||
@@ -46,14 +46,14 @@ func Report(msg string) {
|
||||
if fn != nil {
|
||||
reported := lessExecutor.DoOrDiscard(func() {
|
||||
var builder strings.Builder
|
||||
fmt.Fprintf(&builder, "%s\n", time.Now().Format(timeFormat))
|
||||
builder.WriteString(fmt.Sprintln(time.Now().Format(timeFormat)))
|
||||
if len(clusterName) > 0 {
|
||||
fmt.Fprintf(&builder, "cluster: %s\n", clusterName)
|
||||
builder.WriteString(fmt.Sprintf("cluster: %s\n", clusterName))
|
||||
}
|
||||
fmt.Fprintf(&builder, "host: %s\n", sysx.Hostname())
|
||||
builder.WriteString(fmt.Sprintf("host: %s\n", sysx.Hostname()))
|
||||
dp := atomic.SwapInt32(&dropped, 0)
|
||||
if dp > 0 {
|
||||
fmt.Fprintf(&builder, "dropped: %d\n", dp)
|
||||
builder.WriteString(fmt.Sprintf("dropped: %d\n", dp))
|
||||
}
|
||||
builder.WriteString(strings.TrimSpace(msg))
|
||||
fn(builder.String())
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/iox"
|
||||
@@ -20,10 +21,11 @@ var (
|
||||
preTotal uint64
|
||||
quota float64
|
||||
cores uint64
|
||||
initOnce sync.Once
|
||||
)
|
||||
|
||||
// if /proc not present, ignore the cpu calculation, like wsl linux
|
||||
func init() {
|
||||
func initialize() {
|
||||
cpus, err := cpuSets()
|
||||
if err != nil {
|
||||
logx.Error(err)
|
||||
@@ -69,10 +71,13 @@ func init() {
|
||||
|
||||
// RefreshCpu refreshes cpu usage and returns.
|
||||
func RefreshCpu() uint64 {
|
||||
initOnce.Do(initialize)
|
||||
|
||||
total, err := totalCpuUsage()
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
system, err := systemCpuUsage()
|
||||
if err != nil {
|
||||
return 0
|
||||
|
||||
6
core/stores/cache/cachenode.go
vendored
6
core/stores/cache/cachenode.go
vendored
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -130,7 +131,7 @@ func (c cacheNode) SetWithExpireCtx(ctx context.Context, key string, val interfa
|
||||
return err
|
||||
}
|
||||
|
||||
return c.rds.SetexCtx(ctx, key, string(data), int(expire.Seconds()))
|
||||
return c.rds.SetexCtx(ctx, key, string(data), int(math.Ceil(expire.Seconds())))
|
||||
}
|
||||
|
||||
// String returns a string that represents the cacheNode.
|
||||
@@ -275,5 +276,6 @@ func (c cacheNode) processCache(ctx context.Context, key, data string, v interfa
|
||||
}
|
||||
|
||||
func (c cacheNode) setCacheWithNotFound(ctx context.Context, key string) error {
|
||||
return c.rds.SetexCtx(ctx, key, notFoundPlaceholder, int(c.aroundDuration(c.notFoundExpiry).Seconds()))
|
||||
seconds := int(math.Ceil(c.aroundDuration(c.notFoundExpiry).Seconds()))
|
||||
return c.rds.SetexCtx(ctx, key, notFoundPlaceholder, seconds)
|
||||
}
|
||||
|
||||
@@ -110,7 +110,9 @@ type (
|
||||
Ttl(key string) (int, error)
|
||||
TtlCtx(ctx context.Context, key string) (int, error)
|
||||
Zadd(key string, score int64, value string) (bool, error)
|
||||
ZaddFloat(key string, score float64, value string) (bool, error)
|
||||
ZaddCtx(ctx context.Context, key string, score int64, value string) (bool, error)
|
||||
ZaddFloatCtx(ctx context.Context, key string, score float64, value string) (bool, error)
|
||||
Zadds(key string, ps ...redis.Pair) (int64, error)
|
||||
ZaddsCtx(ctx context.Context, key string, ps ...redis.Pair) (int64, error)
|
||||
Zcard(key string) (int, error)
|
||||
@@ -787,13 +789,21 @@ func (cs clusterStore) Zadd(key string, score int64, value string) (bool, error)
|
||||
return cs.ZaddCtx(context.Background(), key, score, value)
|
||||
}
|
||||
|
||||
func (cs clusterStore) ZaddFloat(key string, score float64, value string) (bool, error) {
|
||||
return cs.ZaddFloatCtx(context.Background(), key, score, value)
|
||||
}
|
||||
|
||||
func (cs clusterStore) ZaddCtx(ctx context.Context, key string, score int64, value string) (bool, error) {
|
||||
return cs.ZaddFloatCtx(ctx, key, float64(score), value)
|
||||
}
|
||||
|
||||
func (cs clusterStore) ZaddFloatCtx(ctx context.Context, key string, score float64, value string) (bool, error) {
|
||||
node, err := cs.getRedis(key)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return node.ZaddCtx(ctx, key, score, value)
|
||||
return node.ZaddFloatCtx(ctx, key, score, value)
|
||||
}
|
||||
|
||||
func (cs clusterStore) Zadds(key string, ps ...redis.Pair) (int64, error) {
|
||||
|
||||
@@ -22,7 +22,7 @@ func TestRedis_Decr(t *testing.T) {
|
||||
_, err := store.Decr("a")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
runOnCluster(func(client Store) {
|
||||
val, err := client.Decr("a")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, int64(-1), val)
|
||||
@@ -37,7 +37,7 @@ func TestRedis_DecrBy(t *testing.T) {
|
||||
_, err := store.Incrby("a", 2)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
runOnCluster(func(client Store) {
|
||||
val, err := client.Decrby("a", 2)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, int64(-2), val)
|
||||
@@ -52,7 +52,7 @@ func TestRedis_Exists(t *testing.T) {
|
||||
_, err := store.Exists("foo")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
runOnCluster(func(client Store) {
|
||||
ok, err := client.Exists("a")
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, ok)
|
||||
@@ -68,7 +68,7 @@ func TestRedis_Eval(t *testing.T) {
|
||||
_, err := store.Eval(`redis.call("EXISTS", KEYS[1])`, "key1")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
runOnCluster(func(client Store) {
|
||||
_, err := client.Eval(`redis.call("EXISTS", KEYS[1])`, "notexist")
|
||||
assert.Equal(t, redis.Nil, err)
|
||||
err = client.Set("key1", "value1")
|
||||
@@ -88,7 +88,7 @@ func TestRedis_Hgetall(t *testing.T) {
|
||||
_, err = store.Hgetall("a")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
runOnCluster(func(client Store) {
|
||||
assert.Nil(t, client.Hset("a", "aa", "aaa"))
|
||||
assert.Nil(t, client.Hset("a", "bb", "bbb"))
|
||||
vals, err := client.Hgetall("a")
|
||||
@@ -105,7 +105,7 @@ func TestRedis_Hvals(t *testing.T) {
|
||||
_, err := store.Hvals("a")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
runOnCluster(func(client Store) {
|
||||
assert.Nil(t, client.Hset("a", "aa", "aaa"))
|
||||
assert.Nil(t, client.Hset("a", "bb", "bbb"))
|
||||
vals, err := client.Hvals("a")
|
||||
@@ -119,7 +119,7 @@ func TestRedis_Hsetnx(t *testing.T) {
|
||||
_, err := store.Hsetnx("a", "dd", "ddd")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
runOnCluster(func(client Store) {
|
||||
assert.Nil(t, client.Hset("a", "aa", "aaa"))
|
||||
assert.Nil(t, client.Hset("a", "bb", "bbb"))
|
||||
ok, err := client.Hsetnx("a", "bb", "ccc")
|
||||
@@ -141,7 +141,7 @@ func TestRedis_HdelHlen(t *testing.T) {
|
||||
_, err = store.Hlen("a")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
runOnCluster(func(client Store) {
|
||||
assert.Nil(t, client.Hset("a", "aa", "aaa"))
|
||||
assert.Nil(t, client.Hset("a", "bb", "bbb"))
|
||||
num, err := client.Hlen("a")
|
||||
@@ -161,7 +161,7 @@ func TestRedis_HIncrBy(t *testing.T) {
|
||||
_, err := store.Hincrby("key", "field", 3)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
runOnCluster(func(client Store) {
|
||||
val, err := client.Hincrby("key", "field", 2)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, val)
|
||||
@@ -176,7 +176,7 @@ func TestRedis_Hkeys(t *testing.T) {
|
||||
_, err := store.Hkeys("a")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
runOnCluster(func(client Store) {
|
||||
assert.Nil(t, client.Hset("a", "aa", "aaa"))
|
||||
assert.Nil(t, client.Hset("a", "bb", "bbb"))
|
||||
vals, err := client.Hkeys("a")
|
||||
@@ -190,7 +190,7 @@ func TestRedis_Hmget(t *testing.T) {
|
||||
_, err := store.Hmget("a", "aa", "bb")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
runOnCluster(func(client Store) {
|
||||
assert.Nil(t, client.Hset("a", "aa", "aaa"))
|
||||
assert.Nil(t, client.Hset("a", "bb", "bbb"))
|
||||
vals, err := client.Hmget("a", "aa", "bb")
|
||||
@@ -209,7 +209,7 @@ func TestRedis_Hmset(t *testing.T) {
|
||||
})
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
runOnCluster(func(client Store) {
|
||||
assert.Nil(t, client.Hmset("a", map[string]string{
|
||||
"aa": "aaa",
|
||||
"bb": "bbb",
|
||||
@@ -225,7 +225,7 @@ func TestRedis_Incr(t *testing.T) {
|
||||
_, err := store.Incr("a")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
runOnCluster(func(client Store) {
|
||||
val, err := client.Incr("a")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, int64(1), val)
|
||||
@@ -240,7 +240,7 @@ func TestRedis_IncrBy(t *testing.T) {
|
||||
_, err := store.Incrby("a", 2)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
runOnCluster(func(client Store) {
|
||||
val, err := client.Incrby("a", 2)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, int64(2), val)
|
||||
@@ -267,7 +267,7 @@ func TestRedis_List(t *testing.T) {
|
||||
_, err = store.Lindex("key", 0)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
runOnCluster(func(client Store) {
|
||||
val, err := client.Lpush("key", "value1", "value2")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 2, val)
|
||||
@@ -316,7 +316,7 @@ func TestRedis_Persist(t *testing.T) {
|
||||
err = store.Expireat("key", time.Now().Unix()+5)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
runOnCluster(func(client Store) {
|
||||
ok, err := client.Persist("key")
|
||||
assert.Nil(t, err)
|
||||
assert.False(t, ok)
|
||||
@@ -348,7 +348,7 @@ func TestRedis_Sscan(t *testing.T) {
|
||||
_, err = store.Del(key)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
runOnCluster(func(client Store) {
|
||||
var list []string
|
||||
for i := 0; i < 1550; i++ {
|
||||
list = append(list, stringx.Randn(i))
|
||||
@@ -390,7 +390,7 @@ func TestRedis_Set(t *testing.T) {
|
||||
_, err = store.Spop("key")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
runOnCluster(func(client Store) {
|
||||
num, err := client.Sadd("key", 1, 2, 3, 4)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 4, num)
|
||||
@@ -434,7 +434,7 @@ func TestRedis_SetGetDel(t *testing.T) {
|
||||
_, err = store.Del("hello")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
runOnCluster(func(client Store) {
|
||||
err := client.Set("hello", "world")
|
||||
assert.Nil(t, err)
|
||||
val, err := client.Get("hello")
|
||||
@@ -457,7 +457,7 @@ func TestRedis_SetExNx(t *testing.T) {
|
||||
_, err = store.SetnxEx("newhello", "newworld", 5)
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
runOnCluster(func(client Store) {
|
||||
err := client.Setex("hello", "world", 5)
|
||||
assert.Nil(t, err)
|
||||
ok, err := client.Setnx("hello", "newworld")
|
||||
@@ -495,7 +495,7 @@ func TestRedis_Getset(t *testing.T) {
|
||||
_, err := store.GetSet("hello", "world")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
runOnCluster(func(client Store) {
|
||||
val, err := client.GetSet("hello", "world")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "", val)
|
||||
@@ -524,7 +524,7 @@ func TestRedis_SetGetDelHashField(t *testing.T) {
|
||||
_, err = store.Hdel("key", "field")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
runOnCluster(func(client Store) {
|
||||
err := client.Hset("key", "field", "value")
|
||||
assert.Nil(t, err)
|
||||
val, err := client.Hget("key", "field")
|
||||
@@ -587,8 +587,8 @@ func TestRedis_SortedSet(t *testing.T) {
|
||||
})
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(client Store) {
|
||||
ok, err := client.Zadd("key", 1, "value1")
|
||||
runOnCluster(func(client Store) {
|
||||
ok, err := client.ZaddFloat("key", 1, "value1")
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, ok)
|
||||
ok, err = client.Zadd("key", 2, "value1")
|
||||
@@ -724,7 +724,7 @@ func TestRedis_HyperLogLog(t *testing.T) {
|
||||
_, err = store.Pfcount("key")
|
||||
assert.NotNil(t, err)
|
||||
|
||||
runOnCluster(t, func(cluster Store) {
|
||||
runOnCluster(func(cluster Store) {
|
||||
ok, err := cluster.Pfadd("key", "value")
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, ok)
|
||||
@@ -734,7 +734,7 @@ func TestRedis_HyperLogLog(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func runOnCluster(t *testing.T, fn func(cluster Store)) {
|
||||
func runOnCluster(fn func(cluster Store)) {
|
||||
s1.FlushAll()
|
||||
s2.FlushAll()
|
||||
|
||||
|
||||
@@ -83,12 +83,12 @@ type (
|
||||
// FindOneAndReplace returns at most one document that matches the filter. If the filter
|
||||
// matches multiple documents, FindOneAndReplace returns the first document in the
|
||||
// collection that matches the filter.
|
||||
FindOneAndReplace(ctx context.Context, filter interface{}, replacement interface{},
|
||||
FindOneAndReplace(ctx context.Context, filter, replacement interface{},
|
||||
opts ...*mopt.FindOneAndReplaceOptions) (*mongo.SingleResult, error)
|
||||
// FindOneAndUpdate returns at most one document that matches the filter. If the filter
|
||||
// matches multiple documents, FindOneAndUpdate returns the first document in the
|
||||
// collection that matches the filter.
|
||||
FindOneAndUpdate(ctx context.Context, filter interface{}, update interface{},
|
||||
FindOneAndUpdate(ctx context.Context, filter, update interface{},
|
||||
opts ...*mopt.FindOneAndUpdateOptions) (*mongo.SingleResult, error)
|
||||
// Indexes returns the index view for this collection.
|
||||
Indexes() mongo.IndexView
|
||||
@@ -99,16 +99,16 @@ type (
|
||||
InsertOne(ctx context.Context, document interface{}, opts ...*mopt.InsertOneOptions) (
|
||||
*mongo.InsertOneResult, error)
|
||||
// ReplaceOne replaces at most one document that matches the filter.
|
||||
ReplaceOne(ctx context.Context, filter interface{}, replacement interface{},
|
||||
ReplaceOne(ctx context.Context, filter, replacement interface{},
|
||||
opts ...*mopt.ReplaceOptions) (*mongo.UpdateResult, error)
|
||||
// UpdateByID updates a single document matching the provided filter.
|
||||
UpdateByID(ctx context.Context, id interface{}, update interface{},
|
||||
UpdateByID(ctx context.Context, id, update interface{},
|
||||
opts ...*mopt.UpdateOptions) (*mongo.UpdateResult, error)
|
||||
// UpdateMany updates the provided documents.
|
||||
UpdateMany(ctx context.Context, filter interface{}, update interface{},
|
||||
UpdateMany(ctx context.Context, filter, update interface{},
|
||||
opts ...*mopt.UpdateOptions) (*mongo.UpdateResult, error)
|
||||
// UpdateOne updates a single document matching the provided filter.
|
||||
UpdateOne(ctx context.Context, filter interface{}, update interface{},
|
||||
UpdateOne(ctx context.Context, filter, update interface{},
|
||||
opts ...*mopt.UpdateOptions) (*mongo.UpdateResult, error)
|
||||
// Watch returns a change stream cursor used to receive notifications of changes to the collection.
|
||||
Watch(ctx context.Context, pipeline interface{}, opts ...*mopt.ChangeStreamOptions) (
|
||||
@@ -359,7 +359,7 @@ func (c *decoratedCollection) FindOneAndReplace(ctx context.Context, filter inte
|
||||
return
|
||||
}
|
||||
|
||||
func (c *decoratedCollection) FindOneAndUpdate(ctx context.Context, filter interface{}, update interface{},
|
||||
func (c *decoratedCollection) FindOneAndUpdate(ctx context.Context, filter, update interface{},
|
||||
opts ...*mopt.FindOneAndUpdateOptions) (res *mongo.SingleResult, err error) {
|
||||
ctx, span := startSpan(ctx, findOneAndUpdate)
|
||||
defer func() {
|
||||
@@ -420,7 +420,7 @@ func (c *decoratedCollection) InsertOne(ctx context.Context, document interface{
|
||||
return
|
||||
}
|
||||
|
||||
func (c *decoratedCollection) ReplaceOne(ctx context.Context, filter interface{}, replacement interface{},
|
||||
func (c *decoratedCollection) ReplaceOne(ctx context.Context, filter, replacement interface{},
|
||||
opts ...*mopt.ReplaceOptions) (res *mongo.UpdateResult, err error) {
|
||||
ctx, span := startSpan(ctx, replaceOne)
|
||||
defer func() {
|
||||
@@ -440,7 +440,7 @@ func (c *decoratedCollection) ReplaceOne(ctx context.Context, filter interface{}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *decoratedCollection) UpdateByID(ctx context.Context, id interface{}, update interface{},
|
||||
func (c *decoratedCollection) UpdateByID(ctx context.Context, id, update interface{},
|
||||
opts ...*mopt.UpdateOptions) (res *mongo.UpdateResult, err error) {
|
||||
ctx, span := startSpan(ctx, updateByID)
|
||||
defer func() {
|
||||
@@ -460,7 +460,7 @@ func (c *decoratedCollection) UpdateByID(ctx context.Context, id interface{}, up
|
||||
return
|
||||
}
|
||||
|
||||
func (c *decoratedCollection) UpdateMany(ctx context.Context, filter interface{}, update interface{},
|
||||
func (c *decoratedCollection) UpdateMany(ctx context.Context, filter, update interface{},
|
||||
opts ...*mopt.UpdateOptions) (res *mongo.UpdateResult, err error) {
|
||||
ctx, span := startSpan(ctx, updateMany)
|
||||
defer func() {
|
||||
@@ -480,7 +480,7 @@ func (c *decoratedCollection) UpdateMany(ctx context.Context, filter interface{}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *decoratedCollection) UpdateOne(ctx context.Context, filter interface{}, update interface{},
|
||||
func (c *decoratedCollection) UpdateOne(ctx context.Context, filter, update interface{},
|
||||
opts ...*mopt.UpdateOptions) (res *mongo.UpdateResult, err error) {
|
||||
ctx, span := startSpan(ctx, updateOne)
|
||||
defer func() {
|
||||
|
||||
@@ -20,10 +20,6 @@ import (
|
||||
|
||||
var errDummy = errors.New("dummy")
|
||||
|
||||
func init() {
|
||||
logx.Disable()
|
||||
}
|
||||
|
||||
func TestKeepPromise_accept(t *testing.T) {
|
||||
p := new(mockPromise)
|
||||
kp := keepablePromise{
|
||||
@@ -110,14 +106,14 @@ func TestCollection_BulkWrite(t *testing.T) {
|
||||
}
|
||||
mt.AddMockResponses(mtest.CreateSuccessResponse(bson.D{{Key: "ok", Value: 1}}...))
|
||||
res, err := c.BulkWrite(context.Background(), []mongo.WriteModel{
|
||||
mongo.NewInsertOneModel().SetDocument(bson.D{{Key: "foo", Value: 1}})},
|
||||
)
|
||||
mongo.NewInsertOneModel().SetDocument(bson.D{{Key: "foo", Value: 1}}),
|
||||
})
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, res)
|
||||
c.brk = new(dropBreaker)
|
||||
_, err = c.BulkWrite(context.Background(), []mongo.WriteModel{
|
||||
mongo.NewInsertOneModel().SetDocument(bson.D{{Key: "foo", Value: 1}})},
|
||||
)
|
||||
mongo.NewInsertOneModel().SetDocument(bson.D{{Key: "foo", Value: 1}}),
|
||||
})
|
||||
assert.Equal(t, errDummy, err)
|
||||
})
|
||||
}
|
||||
@@ -208,7 +204,7 @@ func TestCollection_EstimatedDocumentCount(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestCollectionFind(t *testing.T) {
|
||||
func TestCollection_Find(t *testing.T) {
|
||||
mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock))
|
||||
defer mt.Close()
|
||||
|
||||
@@ -256,7 +252,7 @@ func TestCollectionFind(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestCollectionFindOne(t *testing.T) {
|
||||
func TestCollection_FindOne(t *testing.T) {
|
||||
mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock))
|
||||
defer mt.Close()
|
||||
|
||||
@@ -440,7 +436,7 @@ func TestCollection_InsertMany(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestCollection_Remove(t *testing.T) {
|
||||
func TestCollection_DeleteOne(t *testing.T) {
|
||||
mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock))
|
||||
defer mt.Close()
|
||||
|
||||
@@ -460,7 +456,7 @@ func TestCollection_Remove(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestCollectionRemoveAll(t *testing.T) {
|
||||
func TestCollection_DeleteMany(t *testing.T) {
|
||||
mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock))
|
||||
defer mt.Close()
|
||||
|
||||
@@ -569,7 +565,7 @@ func TestCollection_UpdateMany(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DecoratedCollectionLogDuration(t *testing.T) {
|
||||
func TestDecoratedCollection_LogDuration(t *testing.T) {
|
||||
mt := mtest.New(t, mtest.NewOptions().ClientType(mtest.Mock))
|
||||
defer mt.Close()
|
||||
c := decoratedCollection{
|
||||
|
||||
@@ -159,7 +159,7 @@ func (m *Model) FindOneAndDelete(ctx context.Context, v, filter interface{},
|
||||
}
|
||||
|
||||
// FindOneAndReplace finds a single document and replaces it.
|
||||
func (m *Model) FindOneAndReplace(ctx context.Context, v, filter interface{}, replacement interface{},
|
||||
func (m *Model) FindOneAndReplace(ctx context.Context, v, filter, replacement interface{},
|
||||
opts ...*mopt.FindOneAndReplaceOptions) error {
|
||||
res, err := m.Collection.FindOneAndReplace(ctx, filter, replacement, opts...)
|
||||
if err != nil {
|
||||
@@ -170,7 +170,7 @@ func (m *Model) FindOneAndReplace(ctx context.Context, v, filter interface{}, re
|
||||
}
|
||||
|
||||
// FindOneAndUpdate finds a single document and updates it.
|
||||
func (m *Model) FindOneAndUpdate(ctx context.Context, v, filter interface{}, update interface{},
|
||||
func (m *Model) FindOneAndUpdate(ctx context.Context, v, filter, update interface{},
|
||||
opts ...*mopt.FindOneAndUpdateOptions) error {
|
||||
res, err := m.Collection.FindOneAndUpdate(ctx, filter, update, opts...)
|
||||
if err != nil {
|
||||
|
||||
@@ -192,7 +192,7 @@ func (mm *Model) InsertOneNoCache(ctx context.Context, document interface{},
|
||||
}
|
||||
|
||||
// ReplaceOne replaces a single document in the collection, and remove the cache.
|
||||
func (mm *Model) ReplaceOne(ctx context.Context, key string, filter interface{}, replacement interface{},
|
||||
func (mm *Model) ReplaceOne(ctx context.Context, key string, filter, replacement interface{},
|
||||
opts ...*mopt.ReplaceOptions) (*mongo.UpdateResult, error) {
|
||||
res, err := mm.Model.ReplaceOne(ctx, filter, replacement, opts...)
|
||||
if err != nil {
|
||||
@@ -207,7 +207,7 @@ func (mm *Model) ReplaceOne(ctx context.Context, key string, filter interface{},
|
||||
}
|
||||
|
||||
// ReplaceOneNoCache replaces a single document in the collection.
|
||||
func (mm *Model) ReplaceOneNoCache(ctx context.Context, filter interface{}, replacement interface{},
|
||||
func (mm *Model) ReplaceOneNoCache(ctx context.Context, filter, replacement interface{},
|
||||
opts ...*mopt.ReplaceOptions) (*mongo.UpdateResult, error) {
|
||||
return mm.Model.ReplaceOne(ctx, filter, replacement, opts...)
|
||||
}
|
||||
@@ -218,7 +218,7 @@ func (mm *Model) SetCache(key string, v interface{}) error {
|
||||
}
|
||||
|
||||
// UpdateByID updates the document with given id with update, and remove the cache.
|
||||
func (mm *Model) UpdateByID(ctx context.Context, key string, id interface{}, update interface{},
|
||||
func (mm *Model) UpdateByID(ctx context.Context, key string, id, update interface{},
|
||||
opts ...*mopt.UpdateOptions) (*mongo.UpdateResult, error) {
|
||||
res, err := mm.Model.UpdateByID(ctx, id, update, opts...)
|
||||
if err != nil {
|
||||
@@ -233,13 +233,13 @@ func (mm *Model) UpdateByID(ctx context.Context, key string, id interface{}, upd
|
||||
}
|
||||
|
||||
// UpdateByIDNoCache updates the document with given id with update.
|
||||
func (mm *Model) UpdateByIDNoCache(ctx context.Context, id interface{}, update interface{},
|
||||
func (mm *Model) UpdateByIDNoCache(ctx context.Context, id, update interface{},
|
||||
opts ...*mopt.UpdateOptions) (*mongo.UpdateResult, error) {
|
||||
return mm.Model.UpdateByID(ctx, id, update, opts...)
|
||||
}
|
||||
|
||||
// UpdateMany updates the documents that match filter with update, and remove the cache.
|
||||
func (mm *Model) UpdateMany(ctx context.Context, keys []string, filter interface{}, update interface{},
|
||||
func (mm *Model) UpdateMany(ctx context.Context, keys []string, filter, update interface{},
|
||||
opts ...*mopt.UpdateOptions) (*mongo.UpdateResult, error) {
|
||||
res, err := mm.Model.UpdateMany(ctx, filter, update, opts...)
|
||||
if err != nil {
|
||||
@@ -254,13 +254,13 @@ func (mm *Model) UpdateMany(ctx context.Context, keys []string, filter interface
|
||||
}
|
||||
|
||||
// UpdateManyNoCache updates the documents that match filter with update.
|
||||
func (mm *Model) UpdateManyNoCache(ctx context.Context, filter interface{}, update interface{},
|
||||
func (mm *Model) UpdateManyNoCache(ctx context.Context, filter, update interface{},
|
||||
opts ...*mopt.UpdateOptions) (*mongo.UpdateResult, error) {
|
||||
return mm.Model.UpdateMany(ctx, filter, update, opts...)
|
||||
}
|
||||
|
||||
// UpdateOne updates the first document that matches filter with update, and remove the cache.
|
||||
func (mm *Model) UpdateOne(ctx context.Context, key string, filter interface{}, update interface{},
|
||||
func (mm *Model) UpdateOne(ctx context.Context, key string, filter, update interface{},
|
||||
opts ...*mopt.UpdateOptions) (*mongo.UpdateResult, error) {
|
||||
res, err := mm.Model.UpdateOne(ctx, filter, update, opts...)
|
||||
if err != nil {
|
||||
@@ -275,7 +275,7 @@ func (mm *Model) UpdateOne(ctx context.Context, key string, filter interface{},
|
||||
}
|
||||
|
||||
// UpdateOneNoCache updates the first document that matches filter with update.
|
||||
func (mm *Model) UpdateOneNoCache(ctx context.Context, filter interface{}, update interface{},
|
||||
func (mm *Model) UpdateOneNoCache(ctx context.Context, filter, update interface{},
|
||||
opts ...*mopt.UpdateOptions) (*mongo.UpdateResult, error) {
|
||||
return mm.Model.UpdateOne(ctx, filter, update, opts...)
|
||||
}
|
||||
|
||||
@@ -17,10 +17,6 @@ import (
|
||||
|
||||
var errDummy = errors.New("dummy")
|
||||
|
||||
func init() {
|
||||
logx.Disable()
|
||||
}
|
||||
|
||||
func TestKeepPromise_accept(t *testing.T) {
|
||||
p := new(mockPromise)
|
||||
kp := keepablePromise{
|
||||
|
||||
@@ -3,7 +3,7 @@ package mongoc
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
@@ -117,7 +117,7 @@ func TestStat(t *testing.T) {
|
||||
|
||||
func TestStatCacheFails(t *testing.T) {
|
||||
resetStats()
|
||||
log.SetOutput(ioutil.Discard)
|
||||
log.SetOutput(io.Discard)
|
||||
defer log.SetOutput(os.Stdout)
|
||||
|
||||
r := redis.New("localhost:59999")
|
||||
|
||||
@@ -2,10 +2,13 @@ package redis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
red "github.com/go-redis/redis/v8"
|
||||
"github.com/zeromicro/go-zero/core/breaker"
|
||||
"github.com/zeromicro/go-zero/core/errorx"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/mapping"
|
||||
@@ -53,7 +56,12 @@ func (h hook) AfterProcess(ctx context.Context, cmd red.Cmder) error {
|
||||
|
||||
duration := timex.Since(start)
|
||||
if duration > slowThreshold.Load() {
|
||||
logDuration(ctx, cmd, duration)
|
||||
logDuration(ctx, []red.Cmder{cmd}, duration)
|
||||
}
|
||||
|
||||
metricReqDur.Observe(int64(duration/time.Millisecond), cmd.Name())
|
||||
if msg := formatError(err); len(msg) > 0 {
|
||||
metricReqErr.Inc(cmd.Name(), msg)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -95,19 +103,53 @@ func (h hook) AfterProcessPipeline(ctx context.Context, cmds []red.Cmder) error
|
||||
|
||||
duration := timex.Since(start)
|
||||
if duration > slowThreshold.Load()*time.Duration(len(cmds)) {
|
||||
logDuration(ctx, cmds[0], duration)
|
||||
logDuration(ctx, cmds, duration)
|
||||
}
|
||||
|
||||
metricReqDur.Observe(int64(duration/time.Millisecond), "Pipeline")
|
||||
if msg := formatError(batchError.Err()); len(msg) > 0 {
|
||||
metricReqErr.Inc("Pipeline", msg)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func logDuration(ctx context.Context, cmd red.Cmder, duration time.Duration) {
|
||||
func formatError(err error) string {
|
||||
if err == nil || err == red.Nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
opErr, ok := err.(*net.OpError)
|
||||
if ok && opErr.Timeout() {
|
||||
return "timeout"
|
||||
}
|
||||
|
||||
switch err {
|
||||
case io.EOF:
|
||||
return "eof"
|
||||
case context.DeadlineExceeded:
|
||||
return "context deadline"
|
||||
case breaker.ErrServiceUnavailable:
|
||||
return "breaker"
|
||||
default:
|
||||
return "unexpected error"
|
||||
}
|
||||
}
|
||||
|
||||
func logDuration(ctx context.Context, cmds []red.Cmder, duration time.Duration) {
|
||||
var buf strings.Builder
|
||||
for i, arg := range cmd.Args() {
|
||||
if i > 0 {
|
||||
buf.WriteByte(' ')
|
||||
for k, cmd := range cmds {
|
||||
if k > 0 {
|
||||
buf.WriteByte('\n')
|
||||
}
|
||||
buf.WriteString(mapping.Repr(arg))
|
||||
var build strings.Builder
|
||||
for i, arg := range cmd.Args() {
|
||||
if i > 0 {
|
||||
build.WriteByte(' ')
|
||||
}
|
||||
build.WriteString(mapping.Repr(arg))
|
||||
}
|
||||
buf.WriteString(build.String())
|
||||
}
|
||||
logx.WithContext(ctx).WithDuration(duration).Slowf("[REDIS] slowcall on executing: %s", buf.String())
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ func TestHookProcessCase1(t *testing.T) {
|
||||
Batcher: "jaeger",
|
||||
Sampler: 1.0,
|
||||
})
|
||||
defer ztrace.StopAgent()
|
||||
|
||||
writer := log.Writer()
|
||||
var buf strings.Builder
|
||||
@@ -44,6 +45,7 @@ func TestHookProcessCase2(t *testing.T) {
|
||||
Batcher: "jaeger",
|
||||
Sampler: 1.0,
|
||||
})
|
||||
defer ztrace.StopAgent()
|
||||
|
||||
w, restore := injectLog()
|
||||
defer restore()
|
||||
@@ -89,10 +91,10 @@ func TestHookProcessPipelineCase1(t *testing.T) {
|
||||
log.SetOutput(&buf)
|
||||
defer log.SetOutput(writer)
|
||||
|
||||
ctx, err := durationHook.BeforeProcessPipeline(context.Background(), []red.Cmder{red.NewCmd(context.Background())})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx, err := durationHook.BeforeProcessPipeline(context.Background(), []red.Cmder{
|
||||
red.NewCmd(context.Background()),
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "redis", tracesdk.SpanFromContext(ctx).(interface{ Name() string }).Name())
|
||||
|
||||
assert.Nil(t, durationHook.AfterProcessPipeline(ctx, []red.Cmder{
|
||||
@@ -108,14 +110,15 @@ func TestHookProcessPipelineCase2(t *testing.T) {
|
||||
Batcher: "jaeger",
|
||||
Sampler: 1.0,
|
||||
})
|
||||
defer ztrace.StopAgent()
|
||||
|
||||
w, restore := injectLog()
|
||||
defer restore()
|
||||
|
||||
ctx, err := durationHook.BeforeProcessPipeline(context.Background(), []red.Cmder{red.NewCmd(context.Background())})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx, err := durationHook.BeforeProcessPipeline(context.Background(), []red.Cmder{
|
||||
red.NewCmd(context.Background()),
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "redis", tracesdk.SpanFromContext(ctx).(interface{ Name() string }).Name())
|
||||
|
||||
time.Sleep(slowThreshold.Load() + time.Millisecond)
|
||||
@@ -156,10 +159,28 @@ func TestHookProcessPipelineCase5(t *testing.T) {
|
||||
defer log.SetOutput(writer)
|
||||
|
||||
ctx := context.WithValue(context.Background(), startTimeKey, "foo")
|
||||
assert.Nil(t, durationHook.AfterProcessPipeline(ctx, []red.Cmder{red.NewCmd(context.Background())}))
|
||||
assert.Nil(t, durationHook.AfterProcessPipeline(ctx, []red.Cmder{
|
||||
red.NewCmd(context.Background()),
|
||||
}))
|
||||
assert.True(t, buf.Len() == 0)
|
||||
}
|
||||
|
||||
func TestLogDuration(t *testing.T) {
|
||||
w, restore := injectLog()
|
||||
defer restore()
|
||||
|
||||
logDuration(context.Background(), []red.Cmder{
|
||||
red.NewCmd(context.Background(), "get", "foo"),
|
||||
}, 1*time.Second)
|
||||
assert.True(t, strings.Contains(w.String(), "get foo"))
|
||||
|
||||
logDuration(context.Background(), []red.Cmder{
|
||||
red.NewCmd(context.Background(), "get", "foo"),
|
||||
red.NewCmd(context.Background(), "set", "bar", 0),
|
||||
}, 1*time.Second)
|
||||
assert.True(t, strings.Contains(w.String(), `get foo\nset bar 0`))
|
||||
}
|
||||
|
||||
func injectLog() (r *strings.Builder, restore func()) {
|
||||
var buf strings.Builder
|
||||
w := logx.NewWriter(&buf)
|
||||
|
||||
23
core/stores/redis/metrics.go
Normal file
23
core/stores/redis/metrics.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package redis
|
||||
|
||||
import "github.com/zeromicro/go-zero/core/metric"
|
||||
|
||||
const namespace = "redis_client"
|
||||
|
||||
var (
|
||||
metricReqDur = metric.NewHistogramVec(&metric.HistogramVecOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: "requests",
|
||||
Name: "duration_ms",
|
||||
Help: "redis client requests duration(ms).",
|
||||
Labels: []string{"command"},
|
||||
Buckets: []float64{5, 10, 25, 50, 100, 250, 500, 1000, 2500},
|
||||
})
|
||||
metricReqErr = metric.NewCounterVec(&metric.CounterVecOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: "requests",
|
||||
Name: "error_total",
|
||||
Help: "redis client requests error count.",
|
||||
Labels: []string{"command", "error"},
|
||||
})
|
||||
)
|
||||
@@ -226,20 +226,7 @@ func (s *Redis) Blpop(node RedisNode, key string) (string, error) {
|
||||
// BlpopCtx uses passed in redis connection to execute blocking queries.
|
||||
// Doesn't benefit from pooling redis connections of blocking queries
|
||||
func (s *Redis) BlpopCtx(ctx context.Context, node RedisNode, key string) (string, error) {
|
||||
if node == nil {
|
||||
return "", ErrNilNode
|
||||
}
|
||||
|
||||
vals, err := node.BLPop(ctx, blockingQueryTimeout, key).Result()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(vals) < 2 {
|
||||
return "", fmt.Errorf("no value on key: %s", key)
|
||||
}
|
||||
|
||||
return vals[1], nil
|
||||
return s.BlpopWithTimeoutCtx(ctx, node, blockingQueryTimeout, key)
|
||||
}
|
||||
|
||||
// BlpopEx uses passed in redis connection to execute blpop command.
|
||||
@@ -267,6 +254,32 @@ func (s *Redis) BlpopExCtx(ctx context.Context, node RedisNode, key string) (str
|
||||
return vals[1], true, nil
|
||||
}
|
||||
|
||||
// BlpopWithTimeout uses passed in redis connection to execute blpop command.
|
||||
// Control blocking query timeout
|
||||
func (s *Redis) BlpopWithTimeout(node RedisNode, timeout time.Duration, key string) (string, error) {
|
||||
return s.BlpopWithTimeoutCtx(context.Background(), node, timeout, key)
|
||||
}
|
||||
|
||||
// BlpopWithTimeoutCtx uses passed in redis connection to execute blpop command.
|
||||
// Control blocking query timeout
|
||||
func (s *Redis) BlpopWithTimeoutCtx(ctx context.Context, node RedisNode, timeout time.Duration,
|
||||
key string) (string, error) {
|
||||
if node == nil {
|
||||
return "", ErrNilNode
|
||||
}
|
||||
|
||||
vals, err := node.BLPop(ctx, timeout, key).Result()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(vals) < 2 {
|
||||
return "", fmt.Errorf("no value on key: %s", key)
|
||||
}
|
||||
|
||||
return vals[1], nil
|
||||
}
|
||||
|
||||
// Decr is the implementation of redis decr command.
|
||||
func (s *Redis) Decr(key string) (int64, error) {
|
||||
return s.DecrCtx(context.Background(), key)
|
||||
@@ -1836,8 +1849,19 @@ func (s *Redis) Zadd(key string, score int64, value string) (bool, error) {
|
||||
return s.ZaddCtx(context.Background(), key, score, value)
|
||||
}
|
||||
|
||||
// ZaddFloat is the implementation of redis zadd command.
|
||||
func (s *Redis) ZaddFloat(key string, score float64, value string) (bool, error) {
|
||||
return s.ZaddFloatCtx(context.Background(), key, score, value)
|
||||
}
|
||||
|
||||
// ZaddCtx is the implementation of redis zadd command.
|
||||
func (s *Redis) ZaddCtx(ctx context.Context, key string, score int64, value string) (
|
||||
val bool, err error) {
|
||||
return s.ZaddFloatCtx(ctx, key, float64(score), value)
|
||||
}
|
||||
|
||||
// ZaddFloatCtx is the implementation of redis zadd command.
|
||||
func (s *Redis) ZaddFloatCtx(ctx context.Context, key string, score float64, value string) (
|
||||
val bool, err error) {
|
||||
err = s.brk.DoWithAcceptable(func() error {
|
||||
conn, err := getRedis(s)
|
||||
@@ -1846,7 +1870,7 @@ func (s *Redis) ZaddCtx(ctx context.Context, key string, score int64, value stri
|
||||
}
|
||||
|
||||
v, err := conn.ZAdd(ctx, key, &red.Z{
|
||||
Score: float64(score),
|
||||
Score: score,
|
||||
Member: value,
|
||||
}).Result()
|
||||
if err != nil {
|
||||
|
||||
@@ -810,7 +810,7 @@ func TestRedis_SetGetDelHashField(t *testing.T) {
|
||||
|
||||
func TestRedis_SortedSet(t *testing.T) {
|
||||
runOnRedis(t, func(client *Redis) {
|
||||
ok, err := client.Zadd("key", 1, "value1")
|
||||
ok, err := client.ZaddFloat("key", 1, "value1")
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, ok)
|
||||
ok, err = client.Zadd("key", 2, "value1")
|
||||
@@ -988,8 +988,8 @@ func TestRedis_SortedSet(t *testing.T) {
|
||||
assert.Equal(t, 0, len(pairs))
|
||||
_, err = New(client.Addr, badType()).Zrevrank("key", "value")
|
||||
assert.NotNil(t, err)
|
||||
client.Zadd("second", 2, "aa")
|
||||
client.Zadd("third", 3, "bbb")
|
||||
_, _ = client.Zadd("second", 2, "aa")
|
||||
_, _ = client.Zadd("third", 3, "bbb")
|
||||
val, err = client.Zunionstore("union", &ZStore{
|
||||
Keys: []string{"second", "third"},
|
||||
Weights: []float64{1, 2},
|
||||
@@ -1117,6 +1117,17 @@ func TestRedisBlpopEx(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestRedisBlpopWithTimeout(t *testing.T) {
|
||||
runOnRedis(t, func(client *Redis) {
|
||||
client.Ping()
|
||||
var node mockedNode
|
||||
_, err := client.BlpopWithTimeout(nil, 10*time.Second, "foo")
|
||||
assert.NotNil(t, err)
|
||||
_, err = client.BlpopWithTimeout(node, 10*time.Second, "foo")
|
||||
assert.NotNil(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRedisGeo(t *testing.T) {
|
||||
runOnRedis(t, func(client *Redis) {
|
||||
client.Ping()
|
||||
@@ -1176,7 +1187,7 @@ func runOnRedis(t *testing.T, fn func(client *Redis)) {
|
||||
}
|
||||
|
||||
if client != nil {
|
||||
client.Close()
|
||||
_ = client.Close()
|
||||
}
|
||||
}()
|
||||
fn(New(s.Addr()))
|
||||
@@ -1198,7 +1209,7 @@ func runOnRedisTLS(t *testing.T, fn func(client *Redis)) {
|
||||
t.Error(err)
|
||||
}
|
||||
if client != nil {
|
||||
client.Close()
|
||||
_ = client.Close()
|
||||
}
|
||||
}()
|
||||
fn(New(s.Addr(), WithTLS()))
|
||||
@@ -1214,6 +1225,6 @@ type mockedNode struct {
|
||||
RedisNode
|
||||
}
|
||||
|
||||
func (n mockedNode) BLPop(ctx context.Context, timeout time.Duration, keys ...string) *red.StringSliceCmd {
|
||||
func (n mockedNode) BLPop(_ context.Context, _ time.Duration, _ ...string) *red.StringSliceCmd {
|
||||
return red.NewStringSliceCmd(context.Background(), "foo", "bar")
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
@@ -284,7 +284,7 @@ func TestCachedConn_QueryRowIndex_HasWrongCache(t *testing.T) {
|
||||
|
||||
func TestStatCacheFails(t *testing.T) {
|
||||
resetStats()
|
||||
log.SetOutput(ioutil.Discard)
|
||||
log.SetOutput(io.Discard)
|
||||
defer log.SetOutput(os.Stdout)
|
||||
|
||||
r := redis.New("localhost:59999")
|
||||
|
||||
23
core/stores/sqlx/metrics.go
Normal file
23
core/stores/sqlx/metrics.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package sqlx
|
||||
|
||||
import "github.com/zeromicro/go-zero/core/metric"
|
||||
|
||||
const namespace = "sql_client"
|
||||
|
||||
var (
|
||||
metricReqDur = metric.NewHistogramVec(&metric.HistogramVecOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: "requests",
|
||||
Name: "duration_ms",
|
||||
Help: "mysql client requests duration(ms).",
|
||||
Labels: []string{"command"},
|
||||
Buckets: []float64{5, 10, 25, 50, 100, 250, 500, 1000, 2500},
|
||||
})
|
||||
metricReqErr = metric.NewCounterVec(&metric.CounterVecOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: "requests",
|
||||
Name: "error_total",
|
||||
Help: "mysql client requests error count.",
|
||||
Labels: []string{"command", "error"},
|
||||
})
|
||||
)
|
||||
@@ -153,6 +153,9 @@ func (db *commonSqlConn) ExecCtx(ctx context.Context, q string, args ...interfac
|
||||
result, err = exec(ctx, conn, q, args...)
|
||||
return err
|
||||
}, db.acceptable)
|
||||
if err == breaker.ErrServiceUnavailable {
|
||||
metricReqErr.Inc("Exec", "breaker")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -186,6 +189,9 @@ func (db *commonSqlConn) PrepareCtx(ctx context.Context, query string) (stmt Stm
|
||||
}
|
||||
return nil
|
||||
}, db.acceptable)
|
||||
if err == breaker.ErrServiceUnavailable {
|
||||
metricReqErr.Inc("Prepare", "breaker")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -270,9 +276,14 @@ func (db *commonSqlConn) TransactCtx(ctx context.Context, fn func(context.Contex
|
||||
endSpan(span, err)
|
||||
}()
|
||||
|
||||
return db.brk.DoWithAcceptable(func() error {
|
||||
err = db.brk.DoWithAcceptable(func() error {
|
||||
return transact(ctx, db, db.beginTx, fn)
|
||||
}, db.acceptable)
|
||||
if err == breaker.ErrServiceUnavailable {
|
||||
metricReqErr.Inc("Transact", "breaker")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (db *commonSqlConn) acceptable(err error) bool {
|
||||
@@ -287,7 +298,7 @@ func (db *commonSqlConn) acceptable(err error) bool {
|
||||
func (db *commonSqlConn) queryRows(ctx context.Context, scanner func(*sql.Rows) error,
|
||||
q string, args ...interface{}) (err error) {
|
||||
var qerr error
|
||||
return db.brk.DoWithAcceptable(func() error {
|
||||
err = db.brk.DoWithAcceptable(func() error {
|
||||
conn, err := db.connProv()
|
||||
if err != nil {
|
||||
db.onError(err)
|
||||
@@ -301,6 +312,11 @@ func (db *commonSqlConn) queryRows(ctx context.Context, scanner func(*sql.Rows)
|
||||
}, func(err error) bool {
|
||||
return qerr == err || db.acceptable(err)
|
||||
})
|
||||
if err == breaker.ErrServiceUnavailable {
|
||||
metricReqErr.Inc("queryRows", "breaker")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (s statement) Close() error {
|
||||
|
||||
@@ -17,7 +17,8 @@ func init() {
|
||||
}
|
||||
|
||||
func TestSqlConn(t *testing.T) {
|
||||
mock := buildConn()
|
||||
mock, err := buildConn()
|
||||
assert.Nil(t, err)
|
||||
mock.ExpectExec("any")
|
||||
mock.ExpectQuery("any").WillReturnRows(sqlmock.NewRows([]string{"foo"}))
|
||||
conn := NewMysql(mockedDatasource)
|
||||
@@ -50,8 +51,8 @@ func TestSqlConn(t *testing.T) {
|
||||
}))
|
||||
}
|
||||
|
||||
func buildConn() (mock sqlmock.Sqlmock) {
|
||||
connManager.GetResource(mockedDatasource, func() (io.Closer, error) {
|
||||
func buildConn() (mock sqlmock.Sqlmock, err error) {
|
||||
_, err = connManager.GetResource(mockedDatasource, func() (io.Closer, error) {
|
||||
var db *sql.DB
|
||||
var err error
|
||||
db, mock, err = sqlmock.New()
|
||||
|
||||
@@ -12,7 +12,22 @@ import (
|
||||
|
||||
const defaultSlowThreshold = time.Millisecond * 500
|
||||
|
||||
var slowThreshold = syncx.ForAtomicDuration(defaultSlowThreshold)
|
||||
var (
|
||||
slowThreshold = syncx.ForAtomicDuration(defaultSlowThreshold)
|
||||
logSql = syncx.ForAtomicBool(true)
|
||||
logSlowSql = syncx.ForAtomicBool(true)
|
||||
)
|
||||
|
||||
// DisableLog disables logging of sql statements, includes info and slow logs.
|
||||
func DisableLog() {
|
||||
logSql.Set(false)
|
||||
logSlowSql.Set(false)
|
||||
}
|
||||
|
||||
// DisableStmtLog disables info logging of sql statements, but keeps slow logs.
|
||||
func DisableStmtLog() {
|
||||
logSql.Set(false)
|
||||
}
|
||||
|
||||
// SetSlowThreshold sets the slow threshold.
|
||||
func SetSlowThreshold(threshold time.Duration) {
|
||||
@@ -20,64 +35,39 @@ func SetSlowThreshold(threshold time.Duration) {
|
||||
}
|
||||
|
||||
func exec(ctx context.Context, conn sessionConn, q string, args ...interface{}) (sql.Result, error) {
|
||||
stmt, err := format(q, args...)
|
||||
if err != nil {
|
||||
guard := newGuard("exec")
|
||||
if err := guard.start(q, args...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
startTime := timex.Now()
|
||||
result, err := conn.ExecContext(ctx, q, args...)
|
||||
duration := timex.Since(startTime)
|
||||
if duration > slowThreshold.Load() {
|
||||
logx.WithContext(ctx).WithDuration(duration).Slowf("[SQL] exec: slowcall - %s", stmt)
|
||||
} else {
|
||||
logx.WithContext(ctx).WithDuration(duration).Infof("sql exec: %s", stmt)
|
||||
}
|
||||
if err != nil {
|
||||
logSqlError(ctx, stmt, err)
|
||||
}
|
||||
guard.finish(ctx, err)
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
func execStmt(ctx context.Context, conn stmtConn, q string, args ...interface{}) (sql.Result, error) {
|
||||
stmt, err := format(q, args...)
|
||||
if err != nil {
|
||||
guard := newGuard("execStmt")
|
||||
if err := guard.start(q, args...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
startTime := timex.Now()
|
||||
result, err := conn.ExecContext(ctx, args...)
|
||||
duration := timex.Since(startTime)
|
||||
if duration > slowThreshold.Load() {
|
||||
logx.WithContext(ctx).WithDuration(duration).Slowf("[SQL] execStmt: slowcall - %s", stmt)
|
||||
} else {
|
||||
logx.WithContext(ctx).WithDuration(duration).Infof("sql execStmt: %s", stmt)
|
||||
}
|
||||
if err != nil {
|
||||
logSqlError(ctx, stmt, err)
|
||||
}
|
||||
guard.finish(ctx, err)
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
func query(ctx context.Context, conn sessionConn, scanner func(*sql.Rows) error,
|
||||
q string, args ...interface{}) error {
|
||||
stmt, err := format(q, args...)
|
||||
if err != nil {
|
||||
guard := newGuard("query")
|
||||
if err := guard.start(q, args...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
startTime := timex.Now()
|
||||
rows, err := conn.QueryContext(ctx, q, args...)
|
||||
duration := timex.Since(startTime)
|
||||
if duration > slowThreshold.Load() {
|
||||
logx.WithContext(ctx).WithDuration(duration).Slowf("[SQL] query: slowcall - %s", stmt)
|
||||
} else {
|
||||
logx.WithContext(ctx).WithDuration(duration).Infof("sql query: %s", stmt)
|
||||
}
|
||||
guard.finish(ctx, err)
|
||||
if err != nil {
|
||||
logSqlError(ctx, stmt, err)
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
@@ -87,24 +77,76 @@ func query(ctx context.Context, conn sessionConn, scanner func(*sql.Rows) error,
|
||||
|
||||
func queryStmt(ctx context.Context, conn stmtConn, scanner func(*sql.Rows) error,
|
||||
q string, args ...interface{}) error {
|
||||
stmt, err := format(q, args...)
|
||||
if err != nil {
|
||||
guard := newGuard("queryStmt")
|
||||
if err := guard.start(q, args...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
startTime := timex.Now()
|
||||
rows, err := conn.QueryContext(ctx, args...)
|
||||
duration := timex.Since(startTime)
|
||||
if duration > slowThreshold.Load() {
|
||||
logx.WithContext(ctx).WithDuration(duration).Slowf("[SQL] queryStmt: slowcall - %s", stmt)
|
||||
} else {
|
||||
logx.WithContext(ctx).WithDuration(duration).Infof("sql queryStmt: %s", stmt)
|
||||
}
|
||||
guard.finish(ctx, err)
|
||||
if err != nil {
|
||||
logSqlError(ctx, stmt, err)
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
return scanner(rows)
|
||||
}
|
||||
|
||||
type (
|
||||
sqlGuard interface {
|
||||
start(q string, args ...interface{}) error
|
||||
finish(ctx context.Context, err error)
|
||||
}
|
||||
|
||||
nilGuard struct{}
|
||||
|
||||
realSqlGuard struct {
|
||||
command string
|
||||
stmt string
|
||||
startTime time.Duration
|
||||
}
|
||||
)
|
||||
|
||||
func newGuard(command string) sqlGuard {
|
||||
if logSql.True() || logSlowSql.True() {
|
||||
return &realSqlGuard{
|
||||
command: command,
|
||||
}
|
||||
}
|
||||
|
||||
return nilGuard{}
|
||||
}
|
||||
|
||||
func (n nilGuard) start(_ string, _ ...interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n nilGuard) finish(_ context.Context, _ error) {
|
||||
}
|
||||
|
||||
func (e *realSqlGuard) finish(ctx context.Context, err error) {
|
||||
duration := timex.Since(e.startTime)
|
||||
if duration > slowThreshold.Load() {
|
||||
logx.WithContext(ctx).WithDuration(duration).Slowf("[SQL] %s: slowcall - %s", e.command, e.stmt)
|
||||
} else if logSql.True() {
|
||||
logx.WithContext(ctx).WithDuration(duration).Infof("sql %s: %s", e.command, e.stmt)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logSqlError(ctx, e.stmt, err)
|
||||
}
|
||||
|
||||
metricReqDur.Observe(int64(duration/time.Millisecond), e.command)
|
||||
}
|
||||
|
||||
func (e *realSqlGuard) start(q string, args ...interface{}) error {
|
||||
stmt, err := format(q, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
e.stmt = stmt
|
||||
e.startTime = timex.Now()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -178,6 +178,47 @@ func TestSetSlowThreshold(t *testing.T) {
|
||||
assert.Equal(t, time.Second, slowThreshold.Load())
|
||||
}
|
||||
|
||||
func TestDisableLog(t *testing.T) {
|
||||
assert.True(t, logSql.True())
|
||||
assert.True(t, logSlowSql.True())
|
||||
defer func() {
|
||||
logSql.Set(true)
|
||||
logSlowSql.Set(true)
|
||||
}()
|
||||
|
||||
DisableLog()
|
||||
assert.False(t, logSql.True())
|
||||
assert.False(t, logSlowSql.True())
|
||||
}
|
||||
|
||||
func TestDisableStmtLog(t *testing.T) {
|
||||
assert.True(t, logSql.True())
|
||||
assert.True(t, logSlowSql.True())
|
||||
defer func() {
|
||||
logSql.Set(true)
|
||||
logSlowSql.Set(true)
|
||||
}()
|
||||
|
||||
DisableStmtLog()
|
||||
assert.False(t, logSql.True())
|
||||
assert.True(t, logSlowSql.True())
|
||||
}
|
||||
|
||||
func TestNilGuard(t *testing.T) {
|
||||
assert.True(t, logSql.True())
|
||||
assert.True(t, logSlowSql.True())
|
||||
defer func() {
|
||||
logSql.Set(true)
|
||||
logSlowSql.Set(true)
|
||||
}()
|
||||
|
||||
DisableLog()
|
||||
guard := newGuard("any")
|
||||
assert.Nil(t, guard.start("foo", "bar"))
|
||||
guard.finish(context.Background(), nil)
|
||||
assert.Equal(t, nilGuard{}, guard)
|
||||
}
|
||||
|
||||
type mockedSessionConn struct {
|
||||
lastInsertId int64
|
||||
rowsAffected int64
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/mapping"
|
||||
@@ -152,6 +153,14 @@ func writeValue(buf *strings.Builder, arg interface{}) {
|
||||
buf.WriteByte('\'')
|
||||
buf.WriteString(escape(v))
|
||||
buf.WriteByte('\'')
|
||||
case time.Time:
|
||||
buf.WriteByte('\'')
|
||||
buf.WriteString(v.String())
|
||||
buf.WriteByte('\'')
|
||||
case *time.Time:
|
||||
buf.WriteByte('\'')
|
||||
buf.WriteString(v.String())
|
||||
buf.WriteByte('\'')
|
||||
default:
|
||||
buf.WriteString(mapping.Repr(v))
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package sqlx
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -138,3 +139,14 @@ func TestFormat(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteValue(t *testing.T) {
|
||||
var buf strings.Builder
|
||||
tm := time.Now()
|
||||
writeValue(&buf, &tm)
|
||||
assert.Equal(t, "'"+tm.String()+"'", buf.String())
|
||||
|
||||
buf.Reset()
|
||||
writeValue(&buf, tm)
|
||||
assert.Equal(t, "'"+tm.String()+"'", buf.String())
|
||||
}
|
||||
|
||||
@@ -57,8 +57,8 @@ func (manager *ResourceManager) GetResource(key string, create func() (io.Closer
|
||||
}
|
||||
|
||||
manager.lock.Lock()
|
||||
defer manager.lock.Unlock()
|
||||
manager.resources[key] = resource
|
||||
manager.lock.Unlock()
|
||||
|
||||
return resource, nil
|
||||
})
|
||||
|
||||
@@ -74,6 +74,12 @@ func TestResourceManager_UseAfterClose(t *testing.T) {
|
||||
return nil, errors.New("fail")
|
||||
})
|
||||
assert.NotNil(t, err)
|
||||
|
||||
assert.Panics(t, func() {
|
||||
_, err = manager.GetResource("key", func() (io.Closer, error) {
|
||||
return &dummyResource{age: 123}, nil
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package threading
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
@@ -25,7 +25,7 @@ func TestRoutineGroupRun(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRoutingGroupRunSafe(t *testing.T) {
|
||||
log.SetOutput(ioutil.Discard)
|
||||
log.SetOutput(io.Discard)
|
||||
|
||||
var count int32
|
||||
group := NewRoutineGroup()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package threading
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"testing"
|
||||
|
||||
@@ -14,7 +14,7 @@ func TestRoutineId(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRunSafe(t *testing.T) {
|
||||
log.SetOutput(ioutil.Discard)
|
||||
log.SetOutput(io.Discard)
|
||||
|
||||
i := 0
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package trace
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
@@ -8,21 +9,24 @@ import (
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/exporters/jaeger"
|
||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
|
||||
"go.opentelemetry.io/otel/exporters/zipkin"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
"go.opentelemetry.io/otel/sdk/resource"
|
||||
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
const (
|
||||
kindJaeger = "jaeger"
|
||||
kindZipkin = "zipkin"
|
||||
kindGrpc = "grpc"
|
||||
)
|
||||
|
||||
var (
|
||||
agents = make(map[string]lang.PlaceholderType)
|
||||
lock sync.Mutex
|
||||
tp *sdktrace.TracerProvider
|
||||
)
|
||||
|
||||
// StartAgent starts a opentelemetry agent.
|
||||
@@ -43,6 +47,11 @@ func StartAgent(c Config) {
|
||||
agents[c.Endpoint] = lang.Placeholder
|
||||
}
|
||||
|
||||
// StopAgent shuts down the span processors in the order they were registered.
|
||||
func StopAgent() {
|
||||
_ = tp.Shutdown(context.Background())
|
||||
}
|
||||
|
||||
func createExporter(c Config) (sdktrace.SpanExporter, error) {
|
||||
// Just support jaeger and zipkin now, more for later
|
||||
switch c.Batcher {
|
||||
@@ -50,6 +59,12 @@ func createExporter(c Config) (sdktrace.SpanExporter, error) {
|
||||
return jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(c.Endpoint)))
|
||||
case kindZipkin:
|
||||
return zipkin.New(c.Endpoint)
|
||||
case kindGrpc:
|
||||
return otlptracegrpc.NewUnstarted(
|
||||
otlptracegrpc.WithInsecure(),
|
||||
otlptracegrpc.WithEndpoint(c.Endpoint),
|
||||
otlptracegrpc.WithDialOption(grpc.WithBlock()),
|
||||
), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown exporter: %s", c.Batcher)
|
||||
}
|
||||
@@ -59,7 +74,7 @@ func startAgent(c Config) error {
|
||||
opts := []sdktrace.TracerProviderOption{
|
||||
// Set the sampling rate based on the parent span to 100%
|
||||
sdktrace.WithSampler(sdktrace.ParentBased(sdktrace.TraceIDRatioBased(c.Sampler))),
|
||||
// Record information about this application in an Resource.
|
||||
// Record information about this application in a Resource.
|
||||
sdktrace.WithResource(resource.NewSchemaless(semconv.ServiceNameKey.String(c.Name))),
|
||||
}
|
||||
|
||||
@@ -74,10 +89,8 @@ func startAgent(c Config) error {
|
||||
opts = append(opts, sdktrace.WithBatcher(exp))
|
||||
}
|
||||
|
||||
tp := sdktrace.NewTracerProvider(opts...)
|
||||
tp = sdktrace.NewTracerProvider(opts...)
|
||||
otel.SetTracerProvider(tp)
|
||||
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
|
||||
propagation.TraceContext{}, propagation.Baggage{}))
|
||||
otel.SetErrorHandler(otel.ErrorHandlerFunc(func(err error) {
|
||||
logx.Errorf("[otel] error: %v", err)
|
||||
}))
|
||||
|
||||
@@ -33,18 +33,24 @@ func TestStartAgent(t *testing.T) {
|
||||
Endpoint: endpoint3,
|
||||
Batcher: "otlp",
|
||||
}
|
||||
c5 := Config{
|
||||
Name: "grpc",
|
||||
Endpoint: endpoint3,
|
||||
Batcher: "grpc",
|
||||
}
|
||||
|
||||
StartAgent(c1)
|
||||
StartAgent(c1)
|
||||
StartAgent(c2)
|
||||
StartAgent(c3)
|
||||
StartAgent(c4)
|
||||
StartAgent(c5)
|
||||
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
// because remotehost cannot be resolved
|
||||
assert.Equal(t, 2, len(agents))
|
||||
assert.Equal(t, 3, len(agents))
|
||||
_, ok := agents[""]
|
||||
assert.True(t, ok)
|
||||
_, ok = agents[endpoint1]
|
||||
|
||||
@@ -8,5 +8,5 @@ type Config struct {
|
||||
Name string `json:",optional"`
|
||||
Endpoint string `json:",optional"`
|
||||
Sampler float64 `json:",default=1.0"`
|
||||
Batcher string `json:",default=jaeger,options=jaeger|zipkin"`
|
||||
Batcher string `json:",default=jaeger,options=jaeger|zipkin|grpc"`
|
||||
}
|
||||
|
||||
11
core/trace/propagation.go
Normal file
11
core/trace/propagation.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package trace
|
||||
|
||||
import (
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
)
|
||||
|
||||
func init() {
|
||||
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
|
||||
propagation.TraceContext{}, propagation.Baggage{}))
|
||||
}
|
||||
@@ -38,7 +38,7 @@ func (s *metadataSupplier) Keys() []string {
|
||||
return out
|
||||
}
|
||||
|
||||
// Inject injects the metadata into ctx.
|
||||
// Inject injects cross-cutting concerns from the ctx into the metadata.
|
||||
func Inject(ctx context.Context, p propagation.TextMapPropagator, metadata *metadata.MD) {
|
||||
p.Inject(ctx, &metadataSupplier{
|
||||
metadata: metadata,
|
||||
|
||||
@@ -2,7 +2,6 @@ package internal
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
@@ -18,11 +17,11 @@ const (
|
||||
)
|
||||
|
||||
func TestGetMethods(t *testing.T) {
|
||||
tmpfile, err := ioutil.TempFile(os.TempDir(), hash.Md5Hex([]byte(b64pb)))
|
||||
tmpfile, err := os.CreateTemp(os.TempDir(), hash.Md5Hex([]byte(b64pb)))
|
||||
assert.Nil(t, err)
|
||||
b, err := base64.StdEncoding.DecodeString(b64pb)
|
||||
assert.Nil(t, err)
|
||||
assert.Nil(t, ioutil.WriteFile(tmpfile.Name(), b, os.ModeTemporary))
|
||||
assert.Nil(t, os.WriteFile(tmpfile.Name(), b, os.ModeTemporary))
|
||||
defer os.Remove(tmpfile.Name())
|
||||
|
||||
source, err := grpcurl.DescriptorSourceFromProtoSets(tmpfile.Name())
|
||||
@@ -37,11 +36,11 @@ func TestGetMethods(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetMethodsWithAnnotations(t *testing.T) {
|
||||
tmpfile, err := ioutil.TempFile(os.TempDir(), hash.Md5Hex([]byte(b64pb)))
|
||||
tmpfile, err := os.CreateTemp(os.TempDir(), hash.Md5Hex([]byte(b64pb)))
|
||||
assert.Nil(t, err)
|
||||
b, err := base64.StdEncoding.DecodeString(b64pbWithAnnotations)
|
||||
assert.Nil(t, err)
|
||||
assert.Nil(t, ioutil.WriteFile(tmpfile.Name(), b, os.ModeTemporary))
|
||||
assert.Nil(t, os.WriteFile(tmpfile.Name(), b, os.ModeTemporary))
|
||||
defer os.Remove(tmpfile.Name())
|
||||
|
||||
source, err := grpcurl.DescriptorSourceFromProtoSets(tmpfile.Name())
|
||||
|
||||
47
gateway/internal/eventhandler.go
Normal file
47
gateway/internal/eventhandler.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/golang/protobuf/jsonpb"
|
||||
"github.com/golang/protobuf/proto"
|
||||
"github.com/jhump/protoreflect/desc"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type EventHandler struct {
|
||||
Status *status.Status
|
||||
writer io.Writer
|
||||
marshaler jsonpb.Marshaler
|
||||
}
|
||||
|
||||
func NewEventHandler(writer io.Writer, resolver jsonpb.AnyResolver) *EventHandler {
|
||||
return &EventHandler{
|
||||
writer: writer,
|
||||
marshaler: jsonpb.Marshaler{
|
||||
EmitDefaults: true,
|
||||
AnyResolver: resolver,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (h *EventHandler) OnReceiveResponse(message proto.Message) {
|
||||
if err := h.marshaler.Marshal(h.writer, message); err != nil {
|
||||
logx.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *EventHandler) OnReceiveTrailers(status *status.Status, _ metadata.MD) {
|
||||
h.Status = status
|
||||
}
|
||||
|
||||
func (h *EventHandler) OnResolveMethod(_ *desc.MethodDescriptor) {
|
||||
}
|
||||
|
||||
func (h *EventHandler) OnSendHeaders(_ metadata.MD) {
|
||||
}
|
||||
|
||||
func (h *EventHandler) OnReceiveHeaders(_ metadata.MD) {
|
||||
}
|
||||
20
gateway/internal/eventhandler_test.go
Normal file
20
gateway/internal/eventhandler_test.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
func TestEventHandler(t *testing.T) {
|
||||
h := NewEventHandler(io.Discard, nil)
|
||||
h.OnResolveMethod(nil)
|
||||
h.OnSendHeaders(nil)
|
||||
h.OnReceiveHeaders(nil)
|
||||
h.OnReceiveTrailers(status.New(codes.OK, ""), nil)
|
||||
assert.Equal(t, codes.OK, h.Status.Code())
|
||||
h.OnReceiveResponse(nil)
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
@@ -8,13 +9,13 @@ import (
|
||||
)
|
||||
|
||||
func TestBuildHeadersNoValue(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
req := httptest.NewRequest("GET", "/", http.NoBody)
|
||||
req.Header.Add("a", "b")
|
||||
assert.Nil(t, ProcessHeaders(req.Header))
|
||||
}
|
||||
|
||||
func TestBuildHeadersWithValues(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
req := httptest.NewRequest("GET", "/", http.NoBody)
|
||||
req.Header.Add("grpc-metadata-a", "b")
|
||||
req.Header.Add("grpc-metadata-b", "b")
|
||||
assert.ElementsMatch(t, []string{"gateway-A:b", "gateway-B:b"}, ProcessHeaders(req.Header))
|
||||
|
||||
@@ -3,6 +3,7 @@ package internal
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/fullstorydev/grpcurl"
|
||||
@@ -22,16 +23,18 @@ func NewRequestParser(r *http.Request, resolver jsonpb.AnyResolver) (grpcurl.Req
|
||||
for k, v := range vars {
|
||||
params[k] = v
|
||||
}
|
||||
if len(params) == 0 {
|
||||
return grpcurl.NewJSONRequestParser(r.Body, resolver), nil
|
||||
}
|
||||
|
||||
if r.ContentLength == 0 {
|
||||
body, ok := getBody(r)
|
||||
if !ok {
|
||||
return buildJsonRequestParser(params, resolver)
|
||||
}
|
||||
|
||||
if len(params) == 0 {
|
||||
return grpcurl.NewJSONRequestParser(body, resolver), nil
|
||||
}
|
||||
|
||||
m := make(map[string]interface{})
|
||||
if err := json.NewDecoder(r.Body).Decode(&m); err != nil {
|
||||
if err := json.NewDecoder(body).Decode(&m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -51,3 +54,28 @@ func buildJsonRequestParser(m map[string]interface{}, resolver jsonpb.AnyResolve
|
||||
|
||||
return grpcurl.NewJSONRequestParser(&buf, resolver), nil
|
||||
}
|
||||
|
||||
func getBody(r *http.Request) (io.Reader, bool) {
|
||||
if r.Body == nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if r.ContentLength == 0 {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if r.ContentLength > 0 {
|
||||
return r.Body, true
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if _, err := io.Copy(&buf, r.Body); err != nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if buf.Len() > 0 {
|
||||
return &buf, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -10,14 +12,14 @@ import (
|
||||
)
|
||||
|
||||
func TestNewRequestParserNoVar(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
req := httptest.NewRequest("GET", "/", http.NoBody)
|
||||
parser, err := NewRequestParser(req, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, parser)
|
||||
}
|
||||
|
||||
func TestNewRequestParserWithVars(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
req := httptest.NewRequest("GET", "/", http.NoBody)
|
||||
req = pathvar.WithVars(req, map[string]string{"a": "b"})
|
||||
parser, err := NewRequestParser(req, nil)
|
||||
assert.Nil(t, err)
|
||||
@@ -31,6 +33,14 @@ func TestNewRequestParserNoVarWithBody(t *testing.T) {
|
||||
assert.NotNil(t, parser)
|
||||
}
|
||||
|
||||
func TestNewRequestParserWithNegativeContentLength(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/", strings.NewReader(`{"a": "b"}`))
|
||||
req.ContentLength = -1
|
||||
parser, err := NewRequestParser(req, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, parser)
|
||||
}
|
||||
|
||||
func TestNewRequestParserWithVarsWithBody(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/", strings.NewReader(`{"a": "b"}`))
|
||||
req = pathvar.WithVars(req, map[string]string{"c": "d"})
|
||||
@@ -53,3 +63,37 @@ func TestNewRequestParserWithForm(t *testing.T) {
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, parser)
|
||||
}
|
||||
|
||||
func TestNewRequestParserWithNilBody(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/val?a=b", http.NoBody)
|
||||
req.Body = nil
|
||||
parser, err := NewRequestParser(req, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, parser)
|
||||
}
|
||||
|
||||
func TestNewRequestParserWithBadBody(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/val?a=b", badBody{})
|
||||
req.Body = badBody{}
|
||||
parser, err := NewRequestParser(req, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, parser)
|
||||
}
|
||||
|
||||
func TestNewRequestParserWithBadForm(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/val?a%1=b", http.NoBody)
|
||||
parser, err := NewRequestParser(req, nil)
|
||||
assert.NotNil(t, err)
|
||||
assert.Nil(t, parser)
|
||||
}
|
||||
|
||||
func TestRequestParser_buildJsonRequestParser(t *testing.T) {
|
||||
parser, err := buildJsonRequestParser(map[string]interface{}{"a": make(chan int)}, nil)
|
||||
assert.NotNil(t, err)
|
||||
assert.Nil(t, parser)
|
||||
}
|
||||
|
||||
type badBody struct{}
|
||||
|
||||
func (badBody) Read([]byte) (int, error) { return 0, errors.New("something bad") }
|
||||
func (badBody) Close() error { return nil }
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -9,14 +10,14 @@ import (
|
||||
)
|
||||
|
||||
func TestGetTimeout(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
req := httptest.NewRequest("GET", "/", http.NoBody)
|
||||
req.Header.Set(grpcTimeoutHeader, "1s")
|
||||
timeout := GetTimeout(req.Header, time.Second*5)
|
||||
assert.Equal(t, time.Second, timeout)
|
||||
}
|
||||
|
||||
func TestGetTimeoutDefault(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
req := httptest.NewRequest("GET", "/", http.NoBody)
|
||||
timeout := GetTimeout(req.Header, time.Second*5)
|
||||
assert.Equal(t, time.Second*5, timeout)
|
||||
}
|
||||
|
||||
@@ -120,11 +120,6 @@ func (s *Server) build() error {
|
||||
func (s *Server) buildHandler(source grpcurl.DescriptorSource, resolver jsonpb.AnyResolver,
|
||||
cli zrpc.Client, rpcPath string) func(http.ResponseWriter, *http.Request) {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
handler := &grpcurl.DefaultEventHandler{
|
||||
Out: w,
|
||||
Formatter: grpcurl.NewJSONFormatter(true,
|
||||
grpcurl.AnyResolverFromDescriptorSource(source)),
|
||||
}
|
||||
parser, err := internal.NewRequestParser(r, resolver)
|
||||
if err != nil {
|
||||
httpx.Error(w, err)
|
||||
@@ -136,6 +131,7 @@ func (s *Server) buildHandler(source grpcurl.DescriptorSource, resolver jsonpb.A
|
||||
defer can()
|
||||
|
||||
w.Header().Set(httpx.ContentType, httpx.JsonContentType)
|
||||
handler := internal.NewEventHandler(w, resolver)
|
||||
if err := grpcurl.InvokeRPC(ctx, source, cli.Conn(), rpcPath, s.prepareMetadata(r.Header),
|
||||
handler, parser.Next); err != nil {
|
||||
httpx.Error(w, err)
|
||||
|
||||
38
go.mod
38
go.mod
@@ -3,11 +3,11 @@ module github.com/zeromicro/go-zero
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.2.0
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.0.14
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0
|
||||
github.com/alicebob/miniredis/v2 v2.22.0
|
||||
github.com/alicebob/miniredis/v2 v2.23.0
|
||||
github.com/fatih/color v1.13.0
|
||||
github.com/fullstorydev/grpcurl v1.8.6
|
||||
github.com/fullstorydev/grpcurl v1.8.7
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8
|
||||
github.com/go-redis/redis/v8 v8.11.5
|
||||
github.com/go-sql-driver/mysql v1.6.0
|
||||
@@ -15,27 +15,27 @@ require (
|
||||
github.com/golang/mock v1.6.0
|
||||
github.com/golang/protobuf v1.5.2
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/jhump/protoreflect v1.12.0
|
||||
github.com/justinas/alice v1.2.0
|
||||
github.com/lib/pq v1.10.6
|
||||
github.com/jhump/protoreflect v1.13.0
|
||||
github.com/lib/pq v1.10.7
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/pelletier/go-toml/v2 v2.0.2
|
||||
github.com/prometheus/client_golang v1.12.2
|
||||
github.com/pelletier/go-toml/v2 v2.0.5
|
||||
github.com/prometheus/client_golang v1.13.0
|
||||
github.com/spaolacci/murmur3 v1.1.0
|
||||
github.com/stretchr/testify v1.8.0
|
||||
go.etcd.io/etcd/api/v3 v3.5.4
|
||||
go.etcd.io/etcd/client/v3 v3.5.4
|
||||
go.mongodb.org/mongo-driver v1.10.1
|
||||
go.opentelemetry.io/otel v1.9.0
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.9.0
|
||||
go.opentelemetry.io/otel/exporters/zipkin v1.9.0
|
||||
go.opentelemetry.io/otel/sdk v1.9.0
|
||||
go.opentelemetry.io/otel/trace v1.9.0
|
||||
go.etcd.io/etcd/api/v3 v3.5.5
|
||||
go.etcd.io/etcd/client/v3 v3.5.5
|
||||
go.mongodb.org/mongo-driver v1.10.3
|
||||
go.opentelemetry.io/otel v1.11.0
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.11.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.0
|
||||
go.opentelemetry.io/otel/exporters/zipkin v1.11.0
|
||||
go.opentelemetry.io/otel/sdk v1.11.0
|
||||
go.opentelemetry.io/otel/trace v1.11.0
|
||||
go.uber.org/automaxprocs v1.5.1
|
||||
go.uber.org/goleak v1.1.12
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a
|
||||
go.uber.org/goleak v1.2.0
|
||||
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8
|
||||
golang.org/x/time v0.0.0-20220411224347-583f2d630306
|
||||
google.golang.org/grpc v1.48.0
|
||||
google.golang.org/grpc v1.50.1
|
||||
google.golang.org/protobuf v1.28.1
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.28
|
||||
gopkg.in/h2non/gock.v1 v1.1.2
|
||||
|
||||
137
go.sum
137
go.sum
@@ -42,11 +42,12 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/ClickHouse/clickhouse-go v1.5.4 h1:cKjXeYLNWVJIx2J1K6H2CqyRmfwVJVY1OV1coaaFcI0=
|
||||
github.com/ClickHouse/clickhouse-go v1.5.4/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.2.0 h1:dj00TDKY+xwuTJdbpspCSmTLFyWzRJerTHwaBxut1C0=
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.2.0/go.mod h1:8f2XZUi7XoeU+uPIytSi1cvx8fmJxi7vIgqpvYTF1+o=
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.0.14 h1:7HW+MXPaQfVyCzPGEn/LciMc8K6cG58FZMUc7DXQmro=
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.0.14/go.mod h1:iq2DUGgpA4BBki2CVwrF8x43zqBjdgHtbexkFkh5a6M=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/Shopify/sarama v1.30.0/go.mod h1:zujlQQx1kzHsh4jfV1USnptCQrHAEZ2Hk8fTKCulPVs=
|
||||
@@ -59,8 +60,8 @@ github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRF
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk=
|
||||
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
||||
github.com/alicebob/miniredis/v2 v2.22.0 h1:lIHHiSkEyS1MkKHCHzN+0mWrA4YdbGdimE5iZ2sHSzo=
|
||||
github.com/alicebob/miniredis/v2 v2.22.0/go.mod h1:XNqvJdQJv5mSuVMc0ynneafpnL/zv52acZ6kqeS0t88=
|
||||
github.com/alicebob/miniredis/v2 v2.23.0 h1:+lwAJYjvvdIVg6doFHuotFjueJ/7KY10xo/vm3X3Scw=
|
||||
github.com/alicebob/miniredis/v2 v2.23.0/go.mod h1:XNqvJdQJv5mSuVMc0ynneafpnL/zv52acZ6kqeS0t88=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
@@ -70,7 +71,11 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4=
|
||||
github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4=
|
||||
github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
@@ -82,6 +87,7 @@ github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
|
||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
@@ -109,6 +115,7 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
@@ -122,8 +129,8 @@ github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fullstorydev/grpcurl v1.8.6 h1:WylAwnPauJIofYSHqqMTC1eEfUIzqzevXyogBxnQquo=
|
||||
github.com/fullstorydev/grpcurl v1.8.6/go.mod h1:WhP7fRQdhxz2TkL97u+TCb505sxfH78W1usyoB3tepw=
|
||||
github.com/fullstorydev/grpcurl v1.8.7 h1:xJWosq3BQovQ4QrdPO72OrPiWuGgEsxY8ldYsJbPrqI=
|
||||
github.com/fullstorydev/grpcurl v1.8.7/go.mod h1:pVtM4qe3CMoLaIzYS8uvTuDj2jVYmXqMUkZeijnXp/E=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8 h1:DujepqpGd1hyOd7aW59XpK7Qymp8iy83xq74fLr21is=
|
||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
|
||||
@@ -133,9 +140,11 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||
github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
@@ -145,7 +154,6 @@ github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
@@ -163,6 +171,8 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69
|
||||
github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs=
|
||||
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
|
||||
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@@ -237,7 +247,6 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
|
||||
github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
|
||||
github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw=
|
||||
github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA=
|
||||
github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU=
|
||||
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||
@@ -246,7 +255,10 @@ github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
@@ -265,10 +277,10 @@ github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJk
|
||||
github.com/jhump/gopoet v0.0.0-20190322174617-17282ff210b3/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI=
|
||||
github.com/jhump/gopoet v0.1.0/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI=
|
||||
github.com/jhump/goprotoc v0.5.0/go.mod h1:VrbvcYrQOrTi3i0Vf+m+oqQWk9l72mjkJCYo7UvLHRQ=
|
||||
github.com/jhump/protoreflect v1.10.3/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg=
|
||||
github.com/jhump/protoreflect v1.11.0/go.mod h1:U7aMIjN0NWq9swDP7xDdoMfRHb35uiuTd3Z9nFXJf5E=
|
||||
github.com/jhump/protoreflect v1.12.0 h1:1NQ4FpWMgn3by/n1X0fbeKEUxP1wBt7+Oitpv01HR10=
|
||||
github.com/jhump/protoreflect v1.12.0/go.mod h1:JytZfP5d0r8pVNLZvai7U/MCuTWITgrI4tTg7puQFKI=
|
||||
github.com/jhump/protoreflect v1.13.0 h1:zrrZqa7JAc2YGgPSzZZkmUXJ5G6NRPdxOg/9t7ISImA=
|
||||
github.com/jhump/protoreflect v1.13.0/go.mod h1:JytZfP5d0r8pVNLZvai7U/MCuTWITgrI4tTg7puQFKI=
|
||||
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
@@ -280,8 +292,6 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo=
|
||||
github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
|
||||
@@ -298,8 +308,8 @@ 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/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs=
|
||||
github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
|
||||
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
|
||||
@@ -333,7 +343,6 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nishanths/predeclared v0.0.0-20200524104333-86fad755b4d3/go.mod h1:nt3d53pc1VYcphSCIaYAJtnPYnr3Zyn8fMq2wvPGPso=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
@@ -356,17 +365,17 @@ github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
|
||||
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
|
||||
github.com/openzipkin/zipkin-go v0.4.0 h1:CtfRrOVZtbDj8rt1WXjklw0kqqJQwICrCKmlfUuBUUw=
|
||||
github.com/openzipkin/zipkin-go v0.4.0/go.mod h1:4c3sLeE8xjNqehmF5RpAFLPLJxXscc0R4l6Zg0P1tTQ=
|
||||
github.com/paulmach/orb v0.7.1 h1:Zha++Z5OX/l168sqHK3k4z18LDvr+YAO/VjK0ReQ9rU=
|
||||
github.com/paulmach/orb v0.7.1/go.mod h1:FWRlTgl88VI1RBx/MkrwWDRhQ96ctqMCh8boXhmqB/A=
|
||||
github.com/paulmach/orb v0.5.0 h1:sNhJV5ML+mv1F077ljOck/9inorF4ahDO8iNNpHbKHY=
|
||||
github.com/paulmach/orb v0.5.0/go.mod h1:FWRlTgl88VI1RBx/MkrwWDRhQ96ctqMCh8boXhmqB/A=
|
||||
github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=
|
||||
github.com/pelletier/go-toml/v2 v2.0.2 h1:+jQXlF3scKIcSEKkdHzXhCTDLPFi5r1wnK6yPS+49Gw=
|
||||
github.com/pelletier/go-toml/v2 v2.0.2/go.mod h1:MovirKjgVRESsAvNZlAjtFwV867yGuwRkXbG66OzopI=
|
||||
github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg=
|
||||
github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM=
|
||||
github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=
|
||||
github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
|
||||
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
@@ -380,8 +389,9 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34=
|
||||
github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||
github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU=
|
||||
github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
@@ -390,14 +400,16 @@ github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6T
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||
github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=
|
||||
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
||||
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
|
||||
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
|
||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
|
||||
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
||||
github.com/rabbitmq/amqp091-go v1.1.0/go.mod h1:ogQDLSOACsLPsIq0NpbtiifNZi2YOz0VTJ0kHRghqbM=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
@@ -415,6 +427,7 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
@@ -433,8 +446,6 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
|
||||
@@ -459,41 +470,49 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9 h1:k/gmLsJDWwWqbLCur2yWnJzwQEKRcAHXo6seXGuSwWw=
|
||||
github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA=
|
||||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.etcd.io/etcd/api/v3 v3.5.4 h1:OHVyt3TopwtUQ2GKdd5wu3PmmipR4FTwCqoEjSyRdIc=
|
||||
go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.4 h1:lrneYvz923dvC14R54XcA7FXoZ3mlGZAgmwhfm7HqOg=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
||||
go.etcd.io/etcd/client/v3 v3.5.4 h1:p83BUL3tAYS0OT/r0qglgc3M1JjhM0diV8DSWAhVXv4=
|
||||
go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY=
|
||||
go.mongodb.org/mongo-driver v1.10.1 h1:NujsPveKwHaWuKUer/ceo9DzEe7HIj1SlJ6uvXZG0S4=
|
||||
go.mongodb.org/mongo-driver v1.10.1/go.mod h1:z4XpeoU6w+9Vht+jAFyLgVrD+jGSQQe0+CBWFHNiHt8=
|
||||
go.etcd.io/etcd/api/v3 v3.5.5 h1:BX4JIbQ7hl7+jL+g+2j5UAr0o1bctCm6/Ct+ArBGkf0=
|
||||
go.etcd.io/etcd/api/v3 v3.5.5/go.mod h1:KFtNaxGDw4Yx/BA4iPPwevUTAuqcsPxzyX8PHydchN8=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.5 h1:9S0JUVvmrVl7wCF39iTQthdaaNIiAaQbmK75ogO6GU8=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.5/go.mod h1:ggrwbk069qxpKPq8/FKkQ3Xq9y39kbFR4LnKszpRXeQ=
|
||||
go.etcd.io/etcd/client/v3 v3.5.5 h1:q++2WTJbUgpQu4B6hCuT7VkdwaTP7Qz6Daak3WzbrlI=
|
||||
go.etcd.io/etcd/client/v3 v3.5.5/go.mod h1:aApjR4WGlSumpnJ2kloS75h6aHUmAyaPLjHMxpc7E7c=
|
||||
go.mongodb.org/mongo-driver v1.10.3 h1:XDQEvmh6z1EUsXuIkXE9TaVeqHw6SwS1uf93jFs0HBA=
|
||||
go.mongodb.org/mongo-driver v1.10.3/go.mod h1:z4XpeoU6w+9Vht+jAFyLgVrD+jGSQQe0+CBWFHNiHt8=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk=
|
||||
go.opentelemetry.io/otel v1.9.0 h1:8WZNQFIB2a71LnANS9JeyidJKKGOOremcUtb/OtHISw=
|
||||
go.opentelemetry.io/otel v1.9.0/go.mod h1:np4EoPGzoPs3O67xUVNoPPcmSvsfOxNlNA4F4AC+0Eo=
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.9.0 h1:gAEgEVGDWwFjcis9jJTOJqZNxDzoZfR12WNIxr7g9Ww=
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.9.0/go.mod h1:hquezOLVAybNW6vanIxkdLXTXvzlj2Vn3wevSP15RYs=
|
||||
go.opentelemetry.io/otel/exporters/zipkin v1.9.0 h1:06b/nt6xao6th00aue9WU3ZDTTe+InaMXA/vym6pLuA=
|
||||
go.opentelemetry.io/otel/exporters/zipkin v1.9.0/go.mod h1:HyIvYIu37wV4Wx5azd7e05x9k/dOz9KB4x0plw2QNvs=
|
||||
go.opentelemetry.io/otel/sdk v1.9.0 h1:LNXp1vrr83fNXTHgU8eO89mhzxb/bbWAsHG6fNf3qWo=
|
||||
go.opentelemetry.io/otel/sdk v1.9.0/go.mod h1:AEZc8nt5bd2F7BC24J5R0mrjYnpEgYHyTcM/vrSple4=
|
||||
go.opentelemetry.io/otel v1.11.0 h1:kfToEGMDq6TrVrJ9Vht84Y8y9enykSZzDDZglV0kIEk=
|
||||
go.opentelemetry.io/otel v1.11.0/go.mod h1:H2KtuEphyMvlhZ+F7tg9GRhAOe60moNx61Ex+WmiKkk=
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.11.0 h1:Sv2valcFfMlfu6g8USSS+ZUN5vwbuGj1aY/CFtMG33w=
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.11.0/go.mod h1:nRgyJbgJ0hmaUdHwyDpTTfBYz61cTTeeGhVzfQc+FsI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.0 h1:0dly5et1i/6Th3WHn0M6kYiJfFNzhhxanrJ0bOfnjEo=
|
||||
go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.0/go.mod h1:+Lq4/WkdCkjbGcBMVHHg2apTbv8oMBf29QCnyCCJjNQ=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.0 h1:eyJ6njZmH16h9dOKCi7lMswAnGsSOwgTqWzfxqcuNr8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.11.0/go.mod h1:FnDp7XemjN3oZ3xGunnfOUTVwd2XcvLbtRAuOSU3oc8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.0 h1:j2RFV0Qdt38XQ2Jvi4WIsQ56w8T7eSirYbMw19VXRDg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.11.0/go.mod h1:pILgiTEtrqvZpoiuGdblDgS5dbIaTgDrkIuKfEFkt+A=
|
||||
go.opentelemetry.io/otel/exporters/zipkin v1.11.0 h1:v/Abo5REOWrCj4zcEIUHFZtXpsCVjrwZj28iyX2rHXE=
|
||||
go.opentelemetry.io/otel/exporters/zipkin v1.11.0/go.mod h1:unWnsLCMYfINP8ue0aXVrB/GYHoXNn/lbTnupvLekGQ=
|
||||
go.opentelemetry.io/otel/sdk v1.11.0 h1:ZnKIL9V9Ztaq+ME43IUi/eo22mNsb6a7tGfzaOWB5fo=
|
||||
go.opentelemetry.io/otel/sdk v1.11.0/go.mod h1:REusa8RsyKaq0OlyangWXaw97t2VogoO4SSEeKkSTAk=
|
||||
go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU=
|
||||
go.opentelemetry.io/otel/trace v1.9.0 h1:oZaCNJUjWcg60VXWee8lJKlqhPbXAPB51URuR47pQYc=
|
||||
go.opentelemetry.io/otel/trace v1.9.0/go.mod h1:2737Q0MuG8q1uILYm2YYVkAyLtOofiTNGg6VODnOiPo=
|
||||
go.opentelemetry.io/otel/trace v1.11.0 h1:20U/Vj42SX+mASlXLmSGBg6jpI1jQtv682lZtTAOVFI=
|
||||
go.opentelemetry.io/otel/trace v1.11.0/go.mod h1:nyYjis9jy0gytE9LXGU+/m1sHTKbRY0fX0hulNNDP1U=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw=
|
||||
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/automaxprocs v1.5.1 h1:e1YG66Lrk73dn4qhg8WFSvhF0JuFQF0ERIp4rpuV8Qk=
|
||||
go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66vU6XU=
|
||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
|
||||
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
|
||||
go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
|
||||
go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
|
||||
@@ -584,6 +603,8 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||
golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220531201128-c960675eff93 h1:MYimHLfoXEpOhqd/zgoA/uoXzHB86AEky4LAx5ij9xA=
|
||||
golang.org/x/net v0.0.0-20220531201128-c960675eff93/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@@ -591,8 +612,10 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c h1:pkQiBZBvdos9qq4wBAHqlzuZHEXo07pqV06ef90u1WI=
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg=
|
||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
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=
|
||||
@@ -603,8 +626,9 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8=
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -620,7 +644,6 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -654,7 +677,6 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -664,8 +686,9 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220429233432-b5fbb4746d32/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8 h1:h+EGohizhe9XlX18rfpa8k8RAc5XyaeamM+0VHRd4lc=
|
||||
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
@@ -722,10 +745,8 @@ golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWc
|
||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200717024301-6ddee64345a6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
@@ -794,6 +815,7 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20220602131408-e326c6e8e9c8 h1:qRu95HZ148xXw+XeZ3dvqe85PxH4X8+jIo0iRPKcEnM=
|
||||
google.golang.org/genproto v0.0.0-20220602131408-e326c6e8e9c8/go.mod h1:yKyY4AMRwFiC8yMMNaMi+RkCnjZJt9LoWuvhXjMs+To=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
@@ -811,11 +833,13 @@ google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM
|
||||
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.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
|
||||
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
||||
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
||||
google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w=
|
||||
google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY=
|
||||
google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
|
||||
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=
|
||||
@@ -826,7 +850,6 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.25.1-0.20200805231151-a709e31e5d12/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/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
|
||||
@@ -288,6 +288,11 @@ go-zero 已被许多公司用于生产部署,接入场景如在线教育、电
|
||||
>73. 武汉沃柒科技有限公司(茄椒)
|
||||
>74. 驭势科技
|
||||
>75. 叮当跳动
|
||||
>76. Keep
|
||||
>77. simba innovation
|
||||
>78. ZeroCMF
|
||||
>79. 安徽寻梦投资发展集团
|
||||
>80. 广州腾思信息科技有限公司
|
||||
|
||||
如果贵公司也已使用 go-zero,欢迎在 [登记地址](https://github.com/zeromicro/go-zero/issues/602) 登记,仅仅为了推广,不做其它用途。
|
||||
|
||||
|
||||
71
readme.md
71
readme.md
@@ -1,8 +1,11 @@
|
||||
<img align="right" width="150px" src="https://raw.githubusercontent.com/zeromicro/zero-doc/main/doc/images/go-zero.png">
|
||||
|
||||
# go-zero
|
||||
<p align="center">
|
||||
<img align="center" width="150px" src="https://raw.githubusercontent.com/zeromicro/zero-doc/main/doc/images/go-zero.png">
|
||||
</p>
|
||||
|
||||
English | [简体中文](readme-cn.md)
|
||||
go-zero is a web and rpc framework with lots of builtin engineering practices. It’s born to ensure the stability of the busy services with resilience design and has been serving sites with tens of millions of users for years.
|
||||
|
||||
<div align=center>
|
||||
|
||||
[](https://github.com/zeromicro/go-zero/actions)
|
||||
[](https://codecov.io/gh/zeromicro/go-zero)
|
||||
@@ -13,23 +16,20 @@ English | [简体中文](readme-cn.md)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://discord.gg/4JQvC5A4Fe)
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
## 🤷 What is go-zero?
|
||||
English | [简体中文](readme-cn.md)
|
||||
|
||||
<a href="https://www.producthunt.com/posts/go-zero?utm_source=badge-featured&utm_medium=badge&utm_souce=badge-go-zero" target="_blank"><img src="https://api.producthunt.com/widgets/embed-image/v1/featured.svg?post_id=334030&theme=light" alt="go-zero - A web & rpc framework written in Go. | Product Hunt" style="width: 250px; height: 54px;" width="250" height="54" /></a>
|
||||
|
||||
> ***Important!***
|
||||
>
|
||||
> To upgrade from versions eariler than v1.3.0, run the following commands.
|
||||
>
|
||||
> `go install github.com/zeromicro/go-zero/tools/goctl@latest`
|
||||
>
|
||||
> `goctl migrate —verbose —version v1.4.0`
|
||||
|
||||
## 0. what is go-zero
|
||||
|
||||
go-zero (listed in CNCF Landscape: [https://landscape.cncf.io/?selected=go-zero](https://landscape.cncf.io/?selected=go-zero)) is a web and rpc framework with lots of builtin engineering practices. It’s born to ensure the stability of the busy services with resilience design and has been serving sites with tens of millions of users for years.
|
||||
|
||||
go-zero contains simple API description syntax and code generation tool called `goctl`. You can generate Go, iOS, Android, Kotlin, Dart, TypeScript, JavaScript from .api files with `goctl`.
|
||||
|
||||
Advantages of go-zero:
|
||||
#### Advantages of go-zero:
|
||||
|
||||
* improve the stability of the services with tens of millions of daily active users
|
||||
* builtin chained timeout control, concurrency control, rate limit, adaptive circuit breaker, adaptive load shedding, even no configuration needed
|
||||
@@ -40,7 +40,7 @@ Advantages of go-zero:
|
||||
|
||||
<img src="https://raw.githubusercontent.com/zeromicro/zero-doc/main/doc/images/architecture-en.png" alt="Architecture" width="1500" />
|
||||
|
||||
## 1. Backgrounds of go-zero
|
||||
## Backgrounds of go-zero
|
||||
|
||||
At the beginning of 2018, we decided to re-design our system, from monolithic architecture with Java+MongoDB to microservice architecture. After research and comparison, we chose to:
|
||||
|
||||
@@ -55,7 +55,7 @@ At the beginning of 2018, we decided to re-design our system, from monolithic ar
|
||||
* easy to locate the problems
|
||||
* easy to extend the features
|
||||
|
||||
## 2. Design considerations on go-zero
|
||||
## Design considerations on go-zero
|
||||
|
||||
By designing the microservice architecture, we expected to ensure stability, as well as productivity. And from just the beginning, we have the following design principles:
|
||||
|
||||
@@ -69,7 +69,7 @@ By designing the microservice architecture, we expected to ensure stability, as
|
||||
|
||||
After almost half a year, we finished the transfer from a monolithic system to microservice system and deployed on August 2018. The new system guaranteed business growth and system stability.
|
||||
|
||||
## 3. The implementation and features of go-zero
|
||||
## The implementation and features of go-zero
|
||||
|
||||
go-zero is a web and rpc framework that integrates lots of engineering practices. The features are mainly listed below:
|
||||
|
||||
@@ -91,27 +91,38 @@ As below, go-zero protects the system with a couple of layers and mechanisms:
|
||||
|
||||

|
||||
|
||||
## 4. The simplified architecture that we use with go-zero
|
||||
## The simplified architecture that we use with go-zero
|
||||
|
||||
<img width="1067" alt="image" src="https://user-images.githubusercontent.com/1918356/171880372-5010d846-e8b1-4942-8fe2-e2bbb584f762.png">
|
||||
|
||||
## 5. Installation
|
||||
## Installation
|
||||
|
||||
Run the following command under your project:
|
||||
|
||||
```shell
|
||||
go get -u github.com/zeromicro/go-zero
|
||||
```
|
||||
## Upgrade
|
||||
|
||||
## 6. Quick Start
|
||||
To upgrade from versions eariler than v1.3.0, run the following commands.
|
||||
|
||||
0. full examples can be checked out from below:
|
||||
```shell
|
||||
go install github.com/zeromicro/go-zero/tools/goctl@latest
|
||||
```
|
||||
|
||||
```shell
|
||||
goctl migrate —verbose —version v1.4.0
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. full examples can be checked out from below:
|
||||
|
||||
[Rapid development of microservice systems](https://github.com/zeromicro/zero-doc/blob/main/doc/shorturl-en.md)
|
||||
|
||||
[Rapid development of microservice systems - multiple RPCs](https://github.com/zeromicro/zero-doc/blob/main/docs/zero/bookstore-en.md)
|
||||
|
||||
1. install goctl
|
||||
2. install goctl
|
||||
|
||||
`goctl`can be read as `go control`. `goctl` means not to be controlled by code, instead, we control it. The inside `go` is not `golang`. At the very beginning, I was expecting it to help us improve productivity, and make our lives easier.
|
||||
|
||||
@@ -138,7 +149,7 @@ go get -u github.com/zeromicro/go-zero
|
||||
|
||||
make sure goctl is executable.
|
||||
|
||||
2. create the API file, like greet.api, you can install the plugin of goctl in vs code, api syntax is supported.
|
||||
3. create the API file, like greet.api, you can install the plugin of goctl in vs code, api syntax is supported.
|
||||
|
||||
```go
|
||||
type (
|
||||
@@ -163,7 +174,7 @@ go get -u github.com/zeromicro/go-zero
|
||||
goctl api -o greet.api
|
||||
```
|
||||
|
||||
3. generate the go server-side code
|
||||
4. generate the go server-side code
|
||||
|
||||
```shell
|
||||
goctl api go -api greet.api -dir greet
|
||||
@@ -216,12 +227,12 @@ go get -u github.com/zeromicro/go-zero
|
||||
Content-Length: 0
|
||||
```
|
||||
|
||||
4. Write the business logic code
|
||||
5. Write the business logic code
|
||||
|
||||
* the dependencies can be passed into the logic within servicecontext.go, like mysql, reds, etc.
|
||||
* add the logic code in a logic package according to .api file
|
||||
|
||||
5. Generate code like Java, TypeScript, Dart, JavaScript, etc. just from the api file
|
||||
6. Generate code like Java, TypeScript, Dart, JavaScript, etc. just from the api file
|
||||
|
||||
```shell
|
||||
goctl api java -api greet.api -dir greet
|
||||
@@ -229,24 +240,24 @@ go get -u github.com/zeromicro/go-zero
|
||||
...
|
||||
```
|
||||
|
||||
## 7. Benchmark
|
||||
## Benchmark
|
||||
|
||||

|
||||
|
||||
[Checkout the test code](https://github.com/smallnest/go-web-framework-benchmark)
|
||||
|
||||
## 8. Documents
|
||||
## Documents
|
||||
|
||||
* [Documents](https://go-zero.dev/)
|
||||
* [Rapid development of microservice systems](https://github.com/zeromicro/zero-doc/blob/main/doc/shorturl-en.md)
|
||||
* [Rapid development of microservice systems - multiple RPCs](https://github.com/zeromicro/zero-doc/blob/main/docs/zero/bookstore-en.md)
|
||||
* [Examples](https://github.com/zeromicro/zero-examples)
|
||||
|
||||
## 9. Chat group
|
||||
## Chat group
|
||||
|
||||
Join the chat via https://discord.gg/4JQvC5A4Fe
|
||||
|
||||
## 10. Cloud Native Landscape
|
||||
## Cloud Native Landscape
|
||||
|
||||
<p float="left">
|
||||
<img src="https://landscape.cncf.io/images/left-logo.svg" width="150"/>
|
||||
@@ -259,6 +270,8 @@ go-zero enlisted in the [CNCF Cloud Native Landscape](https://landscape.cncf.io/
|
||||
|
||||
If you like or are using this project to learn or start your solution, please give it a star. Thanks!
|
||||
|
||||
[](#go-zero)
|
||||
|
||||
## Buy me a coffee
|
||||
|
||||
<a href="https://www.buymeacoffee.com/kevwan" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" ></a>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user