diff --git a/api/user_http.pb.go b/api/user_http.pb.go index 4aa8306..e8946e6 100644 --- a/api/user_http.pb.go +++ b/api/user_http.pb.go @@ -2,8 +2,8 @@ package user import ( "context" - "git.diulo.com/mogfee/kit/middleware/jwt" "git.diulo.com/mogfee/kit/transport/http" + "git.diulo.com/mogfee/kit/middleware/jwt" ) type UserHTTPServer interface { diff --git a/cmd/mysql-kit/core/template.go b/cmd/mysql-kit/core/template.go index ae8f5fc..5e99be0 100644 --- a/cmd/mysql-kit/core/template.go +++ b/cmd/mysql-kit/core/template.go @@ -2,8 +2,8 @@ package core import ( "fmt" + "git.diulo.com/mogfee/kit/core/stringx" "git.diulo.com/mogfee/kit/mysql/ddl" - "git.diulo.com/mogfee/kit/stringx" "html/template" "io" "strings" diff --git a/constants/middleware.go b/constants/middleware.go deleted file mode 100644 index 1255fc8..0000000 --- a/constants/middleware.go +++ /dev/null @@ -1 +0,0 @@ -package constants diff --git a/mapping/mapping.go b/core/mapping/mapping.go similarity index 100% rename from mapping/mapping.go rename to core/mapping/mapping.go diff --git a/core/mathx/entropy.go b/core/mathx/entropy.go new file mode 100644 index 0000000..50663b0 --- /dev/null +++ b/core/mathx/entropy.go @@ -0,0 +1,24 @@ +package mathx + +import "math" + +const epsilon = 1e-6 + +func CalcEntropy(m map[any]int) float64 { + if len(m) == 0 || len(m) == 1 { + return 1 + } + var entropy float64 + var total int + for _, v := range m { + total += v + } + for _, v := range m { + probe := float64(v) / float64(total) + if probe < epsilon { + probe = epsilon + } + entropy -= probe * math.Log2(probe) + } + return entropy / math.Log2(float64(len(m))) +} diff --git a/core/mathx/int.go b/core/mathx/int.go new file mode 100644 index 0000000..e923c34 --- /dev/null +++ b/core/mathx/int.go @@ -0,0 +1,15 @@ +package mathx + +func MaxInt(a, b int) int { + if a > b { + return a + } + return b +} + +func MinInt(a, b int) int { + if a < b { + return a + } + return b +} diff --git a/core/mathx/proba.go b/core/mathx/proba.go new file mode 100644 index 0000000..57c1594 --- /dev/null +++ b/core/mathx/proba.go @@ -0,0 +1,25 @@ +package mathx + +import ( + "math/rand" + "sync" + "time" +) + +type Probe struct { + r *rand.Rand + lock sync.Mutex +} + +func NewProbe() *Probe { + return &Probe{ + r: rand.New(rand.NewSource(time.Now().UnixNano())), + } +} + +func (p *Probe) TrueOnProbe(probe float64) bool { + p.lock.Lock() + truth := p.r.Float64() < probe + p.lock.Unlock() + return truth +} diff --git a/core/mathx/unstable.go b/core/mathx/unstable.go new file mode 100644 index 0000000..51e25a2 --- /dev/null +++ b/core/mathx/unstable.go @@ -0,0 +1,39 @@ +package mathx + +import ( + "math/rand" + "sync" + "time" +) + +type Unstable struct { + deviation float64 + r *rand.Rand + lock *sync.Mutex +} + +func NewUnstable(deviation float64) Unstable { + if deviation < 0 { + deviation = 0 + } + if deviation > 1 { + deviation = 1 + } + return Unstable{ + deviation: deviation, + r: rand.New(rand.NewSource(time.Now().UnixNano())), + lock: new(sync.Mutex), + } +} +func (u Unstable) AroundDuration(base time.Duration) time.Duration { + u.lock.Lock() + val := time.Duration((1 + u.deviation - 2*u.deviation*u.r.Float64()) * float64(base)) + u.lock.Unlock() + return val +} +func (u Unstable) AroundInt(base int64) int64 { + u.lock.Lock() + val := int64((1 + u.deviation - 2*u.deviation*u.r.Float64()) * float64(base)) + u.lock.Unlock() + return val +} diff --git a/core/rescue/recover.go b/core/rescue/recover.go new file mode 100644 index 0000000..1a0ab06 --- /dev/null +++ b/core/rescue/recover.go @@ -0,0 +1,12 @@ +package rescue + +import "git.diulo.com/mogfee/kit/core/logx" + +func Recover(cleanups ...func()) { + for _, cleanup := range cleanups { + cleanup() + } + if p := recover(); p != nil { + logx.ErrorStack(p) + } +} diff --git a/stringx/stringx.go b/core/stringx/stringx.go similarity index 100% rename from stringx/stringx.go rename to core/stringx/stringx.go diff --git a/stringx/stringx_test.go b/core/stringx/stringx_test.go similarity index 100% rename from stringx/stringx_test.go rename to core/stringx/stringx_test.go diff --git a/core/syncx/atomicbool.go b/core/syncx/atomicbool.go new file mode 100644 index 0000000..3a58850 --- /dev/null +++ b/core/syncx/atomicbool.go @@ -0,0 +1,34 @@ +package syncx + +import "sync/atomic" + +type AtomicBool uint32 + +func NewAtomicBool() *AtomicBool { + return new(AtomicBool) +} +func ForAtomicBool(val bool) *AtomicBool { + b := NewAtomicBool() + b.Set(val) + return b +} +func (b *AtomicBool) CompareAndSwap(old, val bool) bool { + var ov, nv uint32 + if old { + ov = 1 + } + if val { + nv = 1 + } + return atomic.CompareAndSwapUint32((*uint32)(b), ov, nv) +} +func (b *AtomicBool) Set(v bool) { + if v { + atomic.StoreUint32((*uint32)(b), 1) + } else { + atomic.StoreUint32((*uint32)(b), 0) + } +} +func (b *AtomicBool) True() bool { + return atomic.LoadUint32((*uint32)(b)) == 1 +} diff --git a/syncx/atomicduration.go b/core/syncx/atomicduration.go similarity index 100% rename from syncx/atomicduration.go rename to core/syncx/atomicduration.go diff --git a/syncx/resourcemanager.go b/core/syncx/resourcemanager.go similarity index 100% rename from syncx/resourcemanager.go rename to core/syncx/resourcemanager.go diff --git a/syncx/singleflight.go b/core/syncx/singleflight.go similarity index 100% rename from syncx/singleflight.go rename to core/syncx/singleflight.go diff --git a/core/sysx/automaxprocs.go b/core/sysx/automaxprocs.go new file mode 100644 index 0000000..820822a --- /dev/null +++ b/core/sysx/automaxprocs.go @@ -0,0 +1,8 @@ +package sysx + +import "go.uber.org/automaxprocs/maxprocs" + +// Automatically set GOMAXPROCS to match Linux container CPU quota. +func init() { + maxprocs.Set(maxprocs.Logger(nil)) +} diff --git a/core/sysx/host.go b/core/sysx/host.go new file mode 100644 index 0000000..a0899a8 --- /dev/null +++ b/core/sysx/host.go @@ -0,0 +1,22 @@ +package sysx + +import ( + "os" + + "github.com/zeromicro/go-zero/core/stringx" +) + +var hostname string + +func init() { + var err error + hostname, err = os.Hostname() + if err != nil { + hostname = stringx.RandId() + } +} + +// Hostname returns the name of the host, if no hostname, a random id is returned. +func Hostname() string { + return hostname +} diff --git a/core/sysx/host_test.go b/core/sysx/host_test.go new file mode 100644 index 0000000..ae426b8 --- /dev/null +++ b/core/sysx/host_test.go @@ -0,0 +1,11 @@ +package sysx + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestHostname(t *testing.T) { + assert.True(t, len(Hostname()) > 0) +} diff --git a/core/threading/routinegroup.go b/core/threading/routinegroup.go new file mode 100644 index 0000000..d32b5df --- /dev/null +++ b/core/threading/routinegroup.go @@ -0,0 +1,29 @@ +package threading + +import "sync" + +type RoutineGroup struct { + waitGroup sync.WaitGroup +} + +func NewRoutineGroup() *RoutineGroup { + return new(RoutineGroup) +} +func (g *RoutineGroup) Run(fn func()) { + g.waitGroup.Add(1) + go func() { + defer g.waitGroup.Done() + fn() + }() +} + +func (g *RoutineGroup) RunSafe(fn func()) { + g.waitGroup.Add(1) + GoSafe(func() { + defer g.waitGroup.Done() + fn() + }) +} +func (g *RoutineGroup) Wait() { + g.waitGroup.Wait() +} diff --git a/core/threading/routines.go b/core/threading/routines.go new file mode 100644 index 0000000..b451119 --- /dev/null +++ b/core/threading/routines.go @@ -0,0 +1,24 @@ +package threading + +import ( + "bytes" + "git.diulo.com/mogfee/kit/core/rescue" + "runtime" + "strconv" +) + +func GoSafe(fn func()) { + go RunSafe(fn) +} +func RoutineId() uint64 { + b := make([]byte, 64) + b = b[:runtime.Stack(b, false)] + b = bytes.TrimPrefix(b, []byte("goroutine ")) + b = b[:bytes.IndexByte(b, ' ')] + n, _ := strconv.ParseUint(string(b), 10, 64) + return n +} +func RunSafe(fn func()) { + defer rescue.Recover() + fn() +} diff --git a/core/threading/taskrunner.go b/core/threading/taskrunner.go new file mode 100644 index 0000000..6b20bb7 --- /dev/null +++ b/core/threading/taskrunner.go @@ -0,0 +1,26 @@ +package threading + +import ( + "git.diulo.com/mogfee/kit/core/rescue" + "git.diulo.com/mogfee/kit/lang" +) + +type TaskRunner struct { + limitChan chan lang.PlaceholderType +} + +func NewTaskRunner(concurrency int) *TaskRunner { + return &TaskRunner{ + limitChan: make(chan lang.PlaceholderType, concurrency), + } +} + +func (r *TaskRunner) Schedule(task func()) { + r.limitChan <- lang.Placeholder + go func() { + defer rescue.Recover(func() { + <-r.limitChan + }) + task() + }() +} diff --git a/core/threading/workergroup.go b/core/threading/workergroup.go new file mode 100644 index 0000000..7821812 --- /dev/null +++ b/core/threading/workergroup.go @@ -0,0 +1,20 @@ +package threading + +type WorkerGroup struct { + job func() + workers int +} + +func NewWorkerGroup(job func(), works int) *WorkerGroup { + return &WorkerGroup{ + job: job, + workers: works, + } +} +func (wg WorkerGroup) Start() { + group := NewRoutineGroup() + for i := 0; i < wg.workers; i++ { + group.RunSafe(wg.job) + } + group.Wait() +} diff --git a/timex/relativetime.go b/core/timex/relativetime.go similarity index 100% rename from timex/relativetime.go rename to core/timex/relativetime.go diff --git a/core/timex/repr.go b/core/timex/repr.go new file mode 100644 index 0000000..856eb96 --- /dev/null +++ b/core/timex/repr.go @@ -0,0 +1,11 @@ +package timex + +import ( + "fmt" + "time" +) + +// ReprOfDuration returns the string representation of given duration in ms. +func ReprOfDuration(duration time.Duration) string { + return fmt.Sprintf("%.1fms", float32(duration)/float32(time.Millisecond)) +} diff --git a/core/timex/ticker.go b/core/timex/ticker.go new file mode 100644 index 0000000..44b126a --- /dev/null +++ b/core/timex/ticker.go @@ -0,0 +1,73 @@ +package timex + +import ( + "errors" + "git.diulo.com/mogfee/kit/lang" + "time" +) + +var errTimeout = errors.New("timeout") + +type ( + Ticker interface { + Chan() <-chan time.Time + Stop() + } + + FakeTicker interface { + Ticker + Done() + Tick() + Wait(d time.Duration) error + } + + fakeTicker struct { + c chan time.Time + done chan lang.PlaceholderType + } + + realTicker struct { + *time.Ticker + } +) + +func NewTicker(d time.Duration) Ticker { + return &realTicker{ + Ticker: time.NewTicker(d), + } +} + +func (r *realTicker) Chan() <-chan time.Time { + return r.C +} +func NewFakeTicker() FakeTicker { + return &fakeTicker{ + c: make(chan time.Time, 1), + done: make(chan lang.PlaceholderType, 1), + } +} + +func (f *fakeTicker) Chan() <-chan time.Time { + return f.c +} + +func (f *fakeTicker) Stop() { + close(f.c) +} + +func (f *fakeTicker) Done() { + f.done <- lang.Placeholder +} + +func (f *fakeTicker) Tick() { + f.c <- time.Now() +} + +func (f *fakeTicker) Wait(d time.Duration) error { + select { + case <-time.After(d): + return errTimeout + case <-f.done: + return nil + } +} diff --git a/core/trace/agent.go b/core/trace/agent.go new file mode 100644 index 0000000..939c1d9 --- /dev/null +++ b/core/trace/agent.go @@ -0,0 +1,117 @@ +package trace + +import ( + "context" + "fmt" + "net/url" + "sync" + + "github.com/zeromicro/go-zero/core/lang" + "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/otlp/otlptrace/otlptracehttp" + "go.opentelemetry.io/otel/exporters/zipkin" + "go.opentelemetry.io/otel/sdk/resource" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.4.0" +) + +const ( + kindJaeger = "jaeger" + kindZipkin = "zipkin" + kindOtlpGrpc = "otlpgrpc" + kindOtlpHttp = "otlphttp" +) + +var ( + agents = make(map[string]lang.PlaceholderType) + lock sync.Mutex + tp *sdktrace.TracerProvider +) + +// StartAgent starts an opentelemetry agent. +func StartAgent(c Config) { + lock.Lock() + defer lock.Unlock() + + _, ok := agents[c.Endpoint] + if ok { + return + } + + // if error happens, let later calls run. + if err := startAgent(c); err != nil { + return + } + + 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 { + case kindJaeger: + u, _ := url.Parse(c.Endpoint) + if u.Scheme == "udp" { + return jaeger.New(jaeger.WithAgentEndpoint(jaeger.WithAgentHost(u.Hostname()), jaeger.WithAgentPort(u.Port()))) + } + return jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(c.Endpoint))) + case kindZipkin: + return zipkin.New(c.Endpoint) + case kindOtlpGrpc: + // Always treat trace exporter as optional component, so we use nonblock here, + // otherwise this would slow down app start up even set a dial timeout here when + // endpoint can not reach. + // If the connection not dial success, the global otel ErrorHandler will catch error + // when reporting data like other exporters. + return otlptracegrpc.New( + context.Background(), + otlptracegrpc.WithInsecure(), + otlptracegrpc.WithEndpoint(c.Endpoint), + ) + case kindOtlpHttp: + // Not support flexible configuration now. + return otlptracehttp.New( + context.Background(), + otlptracehttp.WithInsecure(), + otlptracehttp.WithEndpoint(c.Endpoint), + ) + default: + return nil, fmt.Errorf("unknown exporter: %s", c.Batcher) + } +} + +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 a Resource. + sdktrace.WithResource(resource.NewSchemaless(semconv.ServiceNameKey.String(c.Name))), + } + + if len(c.Endpoint) > 0 { + exp, err := createExporter(c) + if err != nil { + logx.Error(err) + return err + } + + // Always be sure to batch in production. + opts = append(opts, sdktrace.WithBatcher(exp)) + } + + tp = sdktrace.NewTracerProvider(opts...) + otel.SetTracerProvider(tp) + otel.SetErrorHandler(otel.ErrorHandlerFunc(func(err error) { + logx.Errorf("[otel] error: %v", err) + })) + + return nil +} diff --git a/core/trace/agent_test.go b/core/trace/agent_test.go new file mode 100644 index 0000000..853fa6b --- /dev/null +++ b/core/trace/agent_test.go @@ -0,0 +1,76 @@ +package trace + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/zeromicro/go-zero/core/logx" +) + +func TestStartAgent(t *testing.T) { + logx.Disable() + + const ( + endpoint1 = "localhost:1234" + endpoint2 = "remotehost:1234" + endpoint3 = "localhost:1235" + endpoint4 = "localhost:1236" + endpoint5 = "udp://localhost:6831" + ) + c1 := Config{ + Name: "foo", + } + c2 := Config{ + Name: "bar", + Endpoint: endpoint1, + Batcher: kindJaeger, + } + c3 := Config{ + Name: "any", + Endpoint: endpoint2, + Batcher: kindZipkin, + } + c4 := Config{ + Name: "bla", + Endpoint: endpoint3, + Batcher: "otlp", + } + c5 := Config{ + Name: "grpc", + Endpoint: endpoint3, + Batcher: kindOtlpGrpc, + } + c6 := Config{ + Name: "otlphttp", + Endpoint: endpoint4, + Batcher: kindOtlpHttp, + } + c7 := Config{ + Name: "UDP", + Endpoint: endpoint5, + Batcher: kindJaeger, + } + + StartAgent(c1) + StartAgent(c1) + StartAgent(c2) + StartAgent(c3) + StartAgent(c4) + StartAgent(c5) + StartAgent(c6) + StartAgent(c7) + + lock.Lock() + defer lock.Unlock() + + // because remotehost cannot be resolved + assert.Equal(t, 5, len(agents)) + _, ok := agents[""] + assert.True(t, ok) + _, ok = agents[endpoint1] + assert.True(t, ok) + _, ok = agents[endpoint2] + assert.False(t, ok) + _, ok = agents[endpoint5] + assert.True(t, ok) +} diff --git a/core/trace/attributes.go b/core/trace/attributes.go new file mode 100644 index 0000000..6da2e68 --- /dev/null +++ b/core/trace/attributes.go @@ -0,0 +1,40 @@ +package trace + +import ( + "go.opentelemetry.io/otel/attribute" + semconv "go.opentelemetry.io/otel/semconv/v1.4.0" + gcodes "google.golang.org/grpc/codes" +) + +const ( + // GRPCStatusCodeKey is convention for numeric status code of a gRPC request. + GRPCStatusCodeKey = attribute.Key("rpc.grpc.status_code") + // RPCNameKey is the name of message transmitted or received. + RPCNameKey = attribute.Key("name") + // RPCMessageTypeKey is the type of message transmitted or received. + RPCMessageTypeKey = attribute.Key("message.type") + // RPCMessageIDKey is the identifier of message transmitted or received. + RPCMessageIDKey = attribute.Key("message.id") + // RPCMessageCompressedSizeKey is the compressed size of the message transmitted or received in bytes. + RPCMessageCompressedSizeKey = attribute.Key("message.compressed_size") + // RPCMessageUncompressedSizeKey is the uncompressed size of the message + // transmitted or received in bytes. + RPCMessageUncompressedSizeKey = attribute.Key("message.uncompressed_size") +) + +// Semantic conventions for common RPC attributes. +var ( + // RPCSystemGRPC is the semantic convention for gRPC as the remoting system. + RPCSystemGRPC = semconv.RPCSystemKey.String("grpc") + // RPCNameMessage is the semantic convention for a message named message. + RPCNameMessage = RPCNameKey.String("message") + // RPCMessageTypeSent is the semantic conventions for sent RPC message types. + RPCMessageTypeSent = RPCMessageTypeKey.String("SENT") + // RPCMessageTypeReceived is the semantic conventions for the received RPC message types. + RPCMessageTypeReceived = RPCMessageTypeKey.String("RECEIVED") +) + +// StatusCodeAttr returns an attribute.KeyValue that represents the give c. +func StatusCodeAttr(c gcodes.Code) attribute.KeyValue { + return GRPCStatusCodeKey.Int64(int64(c)) +} diff --git a/core/trace/attributes_test.go b/core/trace/attributes_test.go new file mode 100644 index 0000000..67c0097 --- /dev/null +++ b/core/trace/attributes_test.go @@ -0,0 +1,12 @@ +package trace + +import ( + "testing" + + "github.com/stretchr/testify/assert" + gcodes "google.golang.org/grpc/codes" +) + +func TestStatusCodeAttr(t *testing.T) { + assert.Equal(t, GRPCStatusCodeKey.Int(int(gcodes.DataLoss)), StatusCodeAttr(gcodes.DataLoss)) +} diff --git a/core/trace/config.go b/core/trace/config.go new file mode 100644 index 0000000..83c62c3 --- /dev/null +++ b/core/trace/config.go @@ -0,0 +1,12 @@ +package trace + +// TraceName represents the tracing name. +const TraceName = "go-zero" + +// A Config is an opentelemetry config. +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|otlpgrpc|otlphttp"` +} diff --git a/core/trace/message.go b/core/trace/message.go new file mode 100644 index 0000000..93c52c7 --- /dev/null +++ b/core/trace/message.go @@ -0,0 +1,38 @@ +package trace + +import ( + "context" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" + "google.golang.org/protobuf/proto" +) + +const messageEvent = "message" + +var ( + // MessageSent is the type of sent messages. + MessageSent = messageType(RPCMessageTypeSent) + // MessageReceived is the type of received messages. + MessageReceived = messageType(RPCMessageTypeReceived) +) + +type messageType attribute.KeyValue + +// Event adds an event of the messageType to the span associated with the +// passed context with id and size (if message is a proto message). +func (m messageType) Event(ctx context.Context, id int, message any) { + span := trace.SpanFromContext(ctx) + if p, ok := message.(proto.Message); ok { + span.AddEvent(messageEvent, trace.WithAttributes( + attribute.KeyValue(m), + RPCMessageIDKey.Int(id), + RPCMessageUncompressedSizeKey.Int(proto.Size(p)), + )) + } else { + span.AddEvent(messageEvent, trace.WithAttributes( + attribute.KeyValue(m), + RPCMessageIDKey.Int(id), + )) + } +} diff --git a/core/trace/message_test.go b/core/trace/message_test.go new file mode 100644 index 0000000..039a5e7 --- /dev/null +++ b/core/trace/message_test.go @@ -0,0 +1,73 @@ +package trace + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/types/dynamicpb" +) + +func TestMessageType_Event(t *testing.T) { + var span mockSpan + ctx := trace.ContextWithSpan(context.Background(), &span) + MessageReceived.Event(ctx, 1, "foo") + assert.Equal(t, messageEvent, span.name) + assert.NotEmpty(t, span.options) +} + +func TestMessageType_EventProtoMessage(t *testing.T) { + var span mockSpan + var message mockMessage + ctx := trace.ContextWithSpan(context.Background(), &span) + MessageReceived.Event(ctx, 1, message) + assert.Equal(t, messageEvent, span.name) + assert.NotEmpty(t, span.options) +} + +type mockSpan struct { + name string + options []trace.EventOption +} + +func (m *mockSpan) End(options ...trace.SpanEndOption) { +} + +func (m *mockSpan) AddEvent(name string, options ...trace.EventOption) { + m.name = name + m.options = options +} + +func (m *mockSpan) IsRecording() bool { + return false +} + +func (m *mockSpan) RecordError(err error, options ...trace.EventOption) { +} + +func (m *mockSpan) SpanContext() trace.SpanContext { + panic("implement me") +} + +func (m *mockSpan) SetStatus(code codes.Code, description string) { +} + +func (m *mockSpan) SetName(name string) { +} + +func (m *mockSpan) SetAttributes(kv ...attribute.KeyValue) { +} + +func (m *mockSpan) TracerProvider() trace.TracerProvider { + return nil +} + +type mockMessage struct{} + +func (m mockMessage) ProtoReflect() protoreflect.Message { + return new(dynamicpb.Message) +} diff --git a/core/trace/propagation.go b/core/trace/propagation.go new file mode 100644 index 0000000..e092003 --- /dev/null +++ b/core/trace/propagation.go @@ -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{})) +} diff --git a/core/trace/tracer.go b/core/trace/tracer.go new file mode 100644 index 0000000..1e3ce0f --- /dev/null +++ b/core/trace/tracer.go @@ -0,0 +1,56 @@ +package trace + +import ( + "context" + + "go.opentelemetry.io/otel/baggage" + "go.opentelemetry.io/otel/propagation" + sdktrace "go.opentelemetry.io/otel/trace" + "google.golang.org/grpc/metadata" +) + +// assert that metadataSupplier implements the TextMapCarrier interface +var _ propagation.TextMapCarrier = (*metadataSupplier)(nil) + +type metadataSupplier struct { + metadata *metadata.MD +} + +func (s *metadataSupplier) Get(key string) string { + values := s.metadata.Get(key) + if len(values) == 0 { + return "" + } + + return values[0] +} + +func (s *metadataSupplier) Set(key, value string) { + s.metadata.Set(key, value) +} + +func (s *metadataSupplier) Keys() []string { + out := make([]string, 0, len(*s.metadata)) + for key := range *s.metadata { + out = append(out, key) + } + + return out +} + +// 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, + }) +} + +// Extract extracts the metadata from ctx. +func Extract(ctx context.Context, p propagation.TextMapPropagator, metadata *metadata.MD) ( + baggage.Baggage, sdktrace.SpanContext) { + ctx = p.Extract(ctx, &metadataSupplier{ + metadata: metadata, + }) + + return baggage.FromContext(ctx), sdktrace.SpanContextFromContext(ctx) +} diff --git a/core/trace/tracer_test.go b/core/trace/tracer_test.go new file mode 100644 index 0000000..bb77d38 --- /dev/null +++ b/core/trace/tracer_test.go @@ -0,0 +1,356 @@ +package trace + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/trace" + "google.golang.org/grpc/metadata" +) + +const ( + traceIDStr = "4bf92f3577b34da6a3ce929d0e0e4736" + spanIDStr = "00f067aa0ba902b7" +) + +var ( + traceID = mustTraceIDFromHex(traceIDStr) + spanID = mustSpanIDFromHex(spanIDStr) +) + +func mustTraceIDFromHex(s string) (t trace.TraceID) { + var err error + t, err = trace.TraceIDFromHex(s) + if err != nil { + panic(err) + } + return +} + +func mustSpanIDFromHex(s string) (t trace.SpanID) { + var err error + t, err = trace.SpanIDFromHex(s) + if err != nil { + panic(err) + } + return +} + +func TestExtractValidTraceContext(t *testing.T) { + stateStr := "key1=value1,key2=value2" + state, err := trace.ParseTraceState(stateStr) + require.NoError(t, err) + + tests := []struct { + name string + traceparent string + tracestate string + sc trace.SpanContext + }{ + { + name: "not sampled", + traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00", + sc: trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: traceID, + SpanID: spanID, + Remote: true, + }), + }, + { + name: "sampled", + traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01", + sc: trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: traceID, + SpanID: spanID, + TraceFlags: trace.FlagsSampled, + Remote: true, + }), + }, + { + name: "valid tracestate", + traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00", + tracestate: stateStr, + sc: trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: traceID, + SpanID: spanID, + TraceState: state, + Remote: true, + }), + }, + { + name: "invalid tracestate perserves traceparent", + traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00", + tracestate: "invalid$@#=invalid", + sc: trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: traceID, + SpanID: spanID, + Remote: true, + }), + }, + { + name: "future version not sampled", + traceparent: "02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00", + sc: trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: traceID, + SpanID: spanID, + Remote: true, + }), + }, + { + name: "future version sampled", + traceparent: "02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01", + sc: trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: traceID, + SpanID: spanID, + TraceFlags: trace.FlagsSampled, + Remote: true, + }), + }, + { + name: "future version sample bit set", + traceparent: "02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-09", + sc: trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: traceID, + SpanID: spanID, + TraceFlags: trace.FlagsSampled, + Remote: true, + }), + }, + { + name: "future version sample bit not set", + traceparent: "02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-08", + sc: trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: traceID, + SpanID: spanID, + Remote: true, + }), + }, + { + name: "future version additional data", + traceparent: "02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00-XYZxsf09", + sc: trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: traceID, + SpanID: spanID, + Remote: true, + }), + }, + { + name: "B3 format ending in dash", + traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00-", + sc: trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: traceID, + SpanID: spanID, + Remote: true, + }), + }, + { + name: "future version B3 format ending in dash", + traceparent: "03-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00-", + sc: trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: traceID, + SpanID: spanID, + Remote: true, + }), + }, + } + otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator( + propagation.TraceContext{}, propagation.Baggage{})) + propagator := otel.GetTextMapPropagator() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() + md := metadata.MD{} + md.Set("traceparent", tt.traceparent) + md.Set("tracestate", tt.tracestate) + _, spanCtx := Extract(ctx, propagator, &md) + assert.Equal(t, tt.sc, spanCtx) + }) + } +} + +func TestExtractInvalidTraceContext(t *testing.T) { + tests := []struct { + name string + header string + }{ + { + name: "wrong version length", + header: "0000-00000000000000000000000000000000-0000000000000000-01", + }, + { + name: "wrong trace ID length", + header: "00-ab00000000000000000000000000000000-cd00000000000000-01", + }, + { + name: "wrong span ID length", + header: "00-ab000000000000000000000000000000-cd0000000000000000-01", + }, + { + name: "wrong trace flag length", + header: "00-ab000000000000000000000000000000-cd00000000000000-0100", + }, + { + name: "bogus version", + header: "qw-00000000000000000000000000000000-0000000000000000-01", + }, + { + name: "bogus trace ID", + header: "00-qw000000000000000000000000000000-cd00000000000000-01", + }, + { + name: "bogus span ID", + header: "00-ab000000000000000000000000000000-qw00000000000000-01", + }, + { + name: "bogus trace flag", + header: "00-ab000000000000000000000000000000-cd00000000000000-qw", + }, + { + name: "upper case version", + header: "A0-00000000000000000000000000000000-0000000000000000-01", + }, + { + name: "upper case trace ID", + header: "00-AB000000000000000000000000000000-cd00000000000000-01", + }, + { + name: "upper case span ID", + header: "00-ab000000000000000000000000000000-CD00000000000000-01", + }, + { + name: "upper case trace flag", + header: "00-ab000000000000000000000000000000-cd00000000000000-A1", + }, + { + name: "zero trace ID and span ID", + header: "00-00000000000000000000000000000000-0000000000000000-01", + }, + { + name: "trace-flag unused bits set", + header: "00-ab000000000000000000000000000000-cd00000000000000-09", + }, + { + name: "missing options", + header: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7", + }, + { + name: "empty options", + header: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-", + }, + } + otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator( + propagation.TraceContext{}, propagation.Baggage{})) + propagator := otel.GetTextMapPropagator() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() + md := metadata.MD{} + md.Set("traceparent", tt.header) + _, spanCtx := Extract(ctx, propagator, &md) + assert.Equal(t, trace.SpanContext{}, spanCtx) + }) + } +} + +func TestInjectValidTraceContext(t *testing.T) { + stateStr := "key1=value1,key2=value2" + state, err := trace.ParseTraceState(stateStr) + require.NoError(t, err) + + tests := []struct { + name string + traceparent string + tracestate string + sc trace.SpanContext + }{ + { + name: "not sampled", + traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00", + sc: trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: traceID, + SpanID: spanID, + Remote: true, + }), + }, + { + name: "sampled", + traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01", + sc: trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: traceID, + SpanID: spanID, + TraceFlags: trace.FlagsSampled, + Remote: true, + }), + }, + { + name: "unsupported trace flag bits dropped", + traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01", + sc: trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: traceID, + SpanID: spanID, + TraceFlags: 0xff, + Remote: true, + }), + }, + { + name: "with tracestate", + traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00", + tracestate: stateStr, + sc: trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: traceID, + SpanID: spanID, + TraceState: state, + Remote: true, + }), + }, + } + otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator( + propagation.TraceContext{}, propagation.Baggage{})) + propagator := otel.GetTextMapPropagator() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() + ctx = trace.ContextWithRemoteSpanContext(ctx, tt.sc) + + want := metadata.MD{} + want.Set("traceparent", tt.traceparent) + if len(tt.tracestate) > 0 { + want.Set("tracestate", tt.tracestate) + } + + md := metadata.MD{} + Inject(ctx, propagator, &md) + assert.Equal(t, want, md) + + mm := &metadataSupplier{ + metadata: &md, + } + assert.NotEmpty(t, mm.Keys()) + }) + } +} + +func TestInvalidSpanContextDropped(t *testing.T) { + invalidSC := trace.SpanContext{} + require.False(t, invalidSC.IsValid()) + ctx := trace.ContextWithRemoteSpanContext(context.Background(), invalidSC) + + otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator( + propagation.TraceContext{}, propagation.Baggage{})) + propagator := otel.GetTextMapPropagator() + + md := metadata.MD{} + Inject(ctx, propagator, &md) + mm := &metadataSupplier{ + metadata: &md, + } + assert.Empty(t, mm.Keys()) + assert.Equal(t, "", mm.Get("traceparent"), "injected invalid SpanContext") +} diff --git a/core/trace/utils.go b/core/trace/utils.go new file mode 100644 index 0000000..c665b89 --- /dev/null +++ b/core/trace/utils.go @@ -0,0 +1,90 @@ +package trace + +import ( + "context" + "net" + "strings" + + ztrace "github.com/zeromicro/go-zero/internal/trace" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + semconv "go.opentelemetry.io/otel/semconv/v1.4.0" + "go.opentelemetry.io/otel/trace" + "google.golang.org/grpc/peer" +) + +const localhost = "127.0.0.1" + +var ( + // SpanIDFromContext returns the span id from ctx. + SpanIDFromContext = ztrace.SpanIDFromContext + // TraceIDFromContext returns the trace id from ctx. + TraceIDFromContext = ztrace.TraceIDFromContext +) + +// ParseFullMethod returns the method name and attributes. +func ParseFullMethod(fullMethod string) (string, []attribute.KeyValue) { + name := strings.TrimLeft(fullMethod, "/") + parts := strings.SplitN(name, "/", 2) + if len(parts) != 2 { + // Invalid format, does not follow `/package.service/method`. + return name, []attribute.KeyValue(nil) + } + + var attrs []attribute.KeyValue + if service := parts[0]; service != "" { + attrs = append(attrs, semconv.RPCServiceKey.String(service)) + } + if method := parts[1]; method != "" { + attrs = append(attrs, semconv.RPCMethodKey.String(method)) + } + + return name, attrs +} + +// PeerAttr returns the peer attributes. +func PeerAttr(addr string) []attribute.KeyValue { + host, port, err := net.SplitHostPort(addr) + if err != nil { + return nil + } + + if len(host) == 0 { + host = localhost + } + + return []attribute.KeyValue{ + semconv.NetPeerIPKey.String(host), + semconv.NetPeerPortKey.String(port), + } +} + +// PeerFromCtx returns the peer from ctx. +func PeerFromCtx(ctx context.Context) string { + p, ok := peer.FromContext(ctx) + if !ok || p == nil { + return "" + } + + return p.Addr.String() +} + +// SpanInfo returns the span info. +func SpanInfo(fullMethod, peerAddress string) (string, []attribute.KeyValue) { + attrs := []attribute.KeyValue{RPCSystemGRPC} + name, mAttrs := ParseFullMethod(fullMethod) + attrs = append(attrs, mAttrs...) + attrs = append(attrs, PeerAttr(peerAddress)...) + return name, attrs +} + +// TracerFromContext returns a tracer in ctx, otherwise returns a global tracer. +func TracerFromContext(ctx context.Context) (tracer trace.Tracer) { + if span := trace.SpanFromContext(ctx); span.SpanContext().IsValid() { + tracer = span.TracerProvider().Tracer(TraceName) + } else { + tracer = otel.Tracer(TraceName) + } + + return +} diff --git a/core/trace/utils_test.go b/core/trace/utils_test.go new file mode 100644 index 0000000..089479e --- /dev/null +++ b/core/trace/utils_test.go @@ -0,0 +1,204 @@ +package trace + +import ( + "context" + "net" + "testing" + + "github.com/stretchr/testify/assert" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/sdk/resource" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.4.0" + "go.opentelemetry.io/otel/trace" + "google.golang.org/grpc/peer" +) + +func TestPeerFromContext(t *testing.T) { + addrs, err := net.InterfaceAddrs() + assert.Nil(t, err) + assert.NotEmpty(t, addrs) + tests := []struct { + name string + ctx context.Context + empty bool + }{ + { + name: "empty", + ctx: context.Background(), + empty: true, + }, + { + name: "nil", + ctx: peer.NewContext(context.Background(), nil), + empty: true, + }, + { + name: "with value", + ctx: peer.NewContext(context.Background(), &peer.Peer{ + Addr: addrs[0], + }), + }, + } + + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + t.Parallel() + addr := PeerFromCtx(test.ctx) + assert.Equal(t, test.empty, len(addr) == 0) + }) + } +} + +func TestParseFullMethod(t *testing.T) { + tests := []struct { + fullMethod string + name string + attr []attribute.KeyValue + }{ + { + fullMethod: "/grpc.test.EchoService/Echo", + name: "grpc.test.EchoService/Echo", + attr: []attribute.KeyValue{ + semconv.RPCServiceKey.String("grpc.test.EchoService"), + semconv.RPCMethodKey.String("Echo"), + }, + }, { + fullMethod: "/com.example.ExampleRmiService/exampleMethod", + name: "com.example.ExampleRmiService/exampleMethod", + attr: []attribute.KeyValue{ + semconv.RPCServiceKey.String("com.example.ExampleRmiService"), + semconv.RPCMethodKey.String("exampleMethod"), + }, + }, { + fullMethod: "/MyCalcService.Calculator/Add", + name: "MyCalcService.Calculator/Add", + attr: []attribute.KeyValue{ + semconv.RPCServiceKey.String("MyCalcService.Calculator"), + semconv.RPCMethodKey.String("Add"), + }, + }, { + fullMethod: "/MyServiceReference.ICalculator/Add", + name: "MyServiceReference.ICalculator/Add", + attr: []attribute.KeyValue{ + semconv.RPCServiceKey.String("MyServiceReference.ICalculator"), + semconv.RPCMethodKey.String("Add"), + }, + }, { + fullMethod: "/MyServiceWithNoPackage/theMethod", + name: "MyServiceWithNoPackage/theMethod", + attr: []attribute.KeyValue{ + semconv.RPCServiceKey.String("MyServiceWithNoPackage"), + semconv.RPCMethodKey.String("theMethod"), + }, + }, { + fullMethod: "/pkg.svr", + name: "pkg.svr", + attr: []attribute.KeyValue(nil), + }, { + fullMethod: "/pkg.svr/", + name: "pkg.svr/", + attr: []attribute.KeyValue{ + semconv.RPCServiceKey.String("pkg.svr"), + }, + }, + } + + for _, test := range tests { + n, a := ParseFullMethod(test.fullMethod) + assert.Equal(t, test.name, n) + assert.Equal(t, test.attr, a) + } +} + +func TestSpanInfo(t *testing.T) { + val, kvs := SpanInfo("/fullMethod", "remote") + assert.Equal(t, "fullMethod", val) + assert.NotEmpty(t, kvs) +} + +func TestPeerAttr(t *testing.T) { + tests := []struct { + name string + addr string + expect []attribute.KeyValue + }{ + { + name: "empty", + }, + { + name: "port only", + addr: ":8080", + expect: []attribute.KeyValue{ + semconv.NetPeerIPKey.String(localhost), + semconv.NetPeerPortKey.String("8080"), + }, + }, + { + name: "port only", + addr: "192.168.0.2:8080", + expect: []attribute.KeyValue{ + semconv.NetPeerIPKey.String("192.168.0.2"), + semconv.NetPeerPortKey.String("8080"), + }, + }, + } + + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + t.Parallel() + kvs := PeerAttr(test.addr) + assert.EqualValues(t, test.expect, kvs) + }) + } +} + +func TestTracerFromContext(t *testing.T) { + traceFn := func(ctx context.Context, hasTraceId bool) { + spanContext := trace.SpanContextFromContext(ctx) + assert.Equal(t, spanContext.IsValid(), hasTraceId) + parentTraceId := spanContext.TraceID().String() + + tracer := TracerFromContext(ctx) + _, span := tracer.Start(ctx, "b") + defer span.End() + + spanContext = span.SpanContext() + assert.True(t, spanContext.IsValid()) + if hasTraceId { + assert.Equal(t, parentTraceId, spanContext.TraceID().String()) + } + + } + + t.Run("context", func(t *testing.T) { + opts := []sdktrace.TracerProviderOption{ + // Set the sampling rate based on the parent span to 100% + sdktrace.WithSampler(sdktrace.ParentBased(sdktrace.TraceIDRatioBased(1))), + // Record information about this application in a Resource. + sdktrace.WithResource(resource.NewSchemaless(semconv.ServiceNameKey.String("test"))), + } + tp = sdktrace.NewTracerProvider(opts...) + otel.SetTracerProvider(tp) + ctx, span := tp.Tracer(TraceName).Start(context.Background(), "a") + + defer span.End() + traceFn(ctx, true) + }) + + t.Run("global", func(t *testing.T) { + opts := []sdktrace.TracerProviderOption{ + // Set the sampling rate based on the parent span to 100% + sdktrace.WithSampler(sdktrace.ParentBased(sdktrace.TraceIDRatioBased(1))), + // Record information about this application in a Resource. + sdktrace.WithResource(resource.NewSchemaless(semconv.ServiceNameKey.String("test"))), + } + tp = sdktrace.NewTracerProvider(opts...) + otel.SetTracerProvider(tp) + + traceFn(context.Background(), false) + }) +} diff --git a/core/trace/vars.go b/core/trace/vars.go new file mode 100644 index 0000000..d8cee29 --- /dev/null +++ b/core/trace/vars.go @@ -0,0 +1,8 @@ +package trace + +import "net/http" + +// TraceIdKey is the trace id header. +// https://www.w3.org/TR/trace-context/#trace-id +// May change it to trace-id afterwards. +var TraceIdKey = http.CanonicalHeaderKey("x-trace-id") diff --git a/example/main.go b/example/main.go index 0f0bb38..ac55d62 100644 --- a/example/main.go +++ b/example/main.go @@ -1,25 +1,39 @@ package main import ( + "context" "flag" "fmt" "git.diulo.com/mogfee/kit" - user "git.diulo.com/mogfee/kit/api" - "git.diulo.com/mogfee/kit/example/service" - "git.diulo.com/mogfee/kit/middleware/jwt" + "git.diulo.com/mogfee/kit/middleware" "git.diulo.com/mogfee/kit/transport/http" ) func main() { flag.Parse() - runApp("localhost:8080") + runApp("localhost:8998") } func runApp(host string) { hs := http.NewServer( http.Address(host), - http.Middleware(jwt.JWT(jwt.WithJwtKey("Jt6Zv!KTopXZ6S4C"))), + http.Middleware(func(handler middleware.Handler) middleware.Handler { + return func(ctx context.Context, a any) (any, error) { + fmt.Println("middleare 1") + return handler(ctx, a) + } + }), ) - user.RegisterUserHTTPServer(hs, service.NewUserService("1234567890123456")) + hs.Use("/api/v2/user/me", func(handler middleware.Handler) middleware.Handler { + return func(ctx context.Context, a any) (any, error) { + fmt.Println("use midd") + return handler(ctx, a) + } + }) + r := hs.Route("") + r.GET("/api/v2/user/me", func(ctx http.Context) error { + fmt.Println("/api/v2/user/me") + return nil + }) app := kit.New(kit.Server(hs)) fmt.Println(app.Run()) fmt.Println(app.Stop()) diff --git a/go.mod b/go.mod index db8dd9d..e1b53d4 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,8 @@ go 1.20 require ( github.com/antlr4-go/antlr/v4 v4.13.0 + github.com/fatih/color v1.15.0 + github.com/felixge/fgprof v0.9.3 github.com/gin-gonic/gin v1.9.0 github.com/go-playground/form v3.1.4+incompatible github.com/go-sql-driver/mysql v1.7.1 @@ -12,17 +14,27 @@ require ( github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 github.com/nsqio/go-nsq v1.1.0 + github.com/pelletier/go-toml/v2 v2.0.9 + github.com/pkg/errors v0.9.1 + github.com/prometheus/client_golang v1.16.0 github.com/spf13/cobra v1.7.0 - go.etcd.io/etcd/client/v3 v3.5.7 - go.opentelemetry.io/otel v1.17.0 + github.com/stretchr/testify v1.8.4 + github.com/zeromicro/go-zero v1.5.5 + go.etcd.io/etcd/client/v3 v3.5.9 + go.opentelemetry.io/otel v1.18.0 go.opentelemetry.io/otel/exporters/jaeger v1.17.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.18.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.18.0 go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.17.0 - go.opentelemetry.io/otel/sdk v1.17.0 - go.opentelemetry.io/otel/trace v1.17.0 - golang.org/x/net v0.10.0 + go.opentelemetry.io/otel/exporters/zipkin v1.18.0 + go.opentelemetry.io/otel/sdk v1.18.0 + go.opentelemetry.io/otel/trace v1.18.0 + go.uber.org/automaxprocs v1.5.3 + golang.org/x/net v0.14.0 golang.org/x/sync v0.3.0 - google.golang.org/genproto v0.0.0-20230301171018-9ab4bdc49ad5 - google.golang.org/grpc v1.53.0 + google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 + google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 + google.golang.org/grpc v1.58.0 google.golang.org/protobuf v1.31.0 gopkg.in/go-playground/assert.v1 v1.2.1 gopkg.in/yaml.v3 v3.0.1 @@ -31,10 +43,12 @@ require ( require ( github.com/beorn7/perks v1.0.1 // indirect github.com/bytedance/sonic v1.8.3 // indirect + github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect - github.com/coreos/go-semver v0.3.0 // indirect - github.com/coreos/go-systemd/v22 v22.3.2 // indirect + github.com/coreos/go-semver v0.3.1 // indirect + github.com/coreos/go-systemd/v22 v22.5.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -44,32 +58,38 @@ require ( github.com/goccy/go-json v0.10.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/snappy v0.0.4 // indirect + github.com/google/pprof v0.0.0-20211214055906-6f57359322fd // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/leodido/go-urn v1.2.2 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.17 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.0.7 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_golang v1.16.0 // indirect + github.com/openzipkin/zipkin-go v0.4.2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/procfs v0.11.1 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.10 // indirect - go.etcd.io/etcd/api/v3 v3.5.7 // indirect - go.etcd.io/etcd/client/pkg/v3 v3.5.7 // indirect - go.opentelemetry.io/otel/metric v1.17.0 // indirect - go.uber.org/atomic v1.7.0 // indirect - go.uber.org/multierr v1.6.0 // indirect - go.uber.org/zap v1.17.0 // indirect + go.etcd.io/etcd/api/v3 v3.5.9 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.5.9 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0 // indirect + go.opentelemetry.io/otel/metric v1.18.0 // indirect + go.opentelemetry.io/proto/otlp v1.0.0 // indirect + go.uber.org/atomic v1.10.0 // indirect + go.uber.org/multierr v1.9.0 // indirect + go.uber.org/zap v1.24.0 // indirect golang.org/x/arch v0.2.0 // indirect - golang.org/x/crypto v0.6.0 // indirect + golang.org/x/crypto v0.12.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect golang.org/x/sys v0.12.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/text v0.12.0 // indirect + google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 // indirect ) diff --git a/go.sum b/go.sum index 04e755a..889d213 100644 --- a/go.sum +++ b/go.sum @@ -1,23 +1,33 @@ github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= 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/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.8.3 h1:pf6fGl5eqWYKkx1RcD4qpuX+BIUaduv/wTm5ekWJ80M= github.com/bytedance/sonic v1.8.3/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= -github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= +github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g= +github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8= @@ -45,10 +55,9 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v5 v5.0.0-rc.1 h1:tDQ1LjKga657layZ4JLsRdxgvupebc0xuPwRNuTfUgs= github.com/golang-jwt/jwt/v5 v5.0.0-rc.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -57,10 +66,15 @@ github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y= +github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= +github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -70,10 +84,13 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/leodido/go-urn v1.2.2 h1:7z68G0FCGvDk646jz1AelTYNYWrTNm0bEcFAo147wt4= github.com/leodido/go-urn v1.2.2/go.mod h1:kUaIbLZWttglzwNuG0pgsh5vuV6u2YcGBYz1hIPjtOQ= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= @@ -85,13 +102,15 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/nsqio/go-nsq v1.1.0 h1:PQg+xxiUjA7V+TLdXw7nVrJ5Jbl3sN86EhGCQj4+FYE= github.com/nsqio/go-nsq v1.1.0/go.mod h1:vKq36oyeVXgsS5Q8YEO7WghqidAVXQlcFxzQbQTuDEY= -github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us= -github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/openzipkin/zipkin-go v0.4.2 h1:zjqfqHjUpPmB3c1GlCvvgsM1G4LkvqQbBDueDOCg/jA= +github.com/openzipkin/zipkin-go v0.4.2/go.mod h1:ZeVkFjuuBiSy13y8vpSDCjMi9GoI3hPpCJSBx/EYFhY= +github.com/pelletier/go-toml/v2 v2.0.9 h1:uH2qQXheeefCCkuBBSLi7jCiSmj3VRh2+Goq2N7Xxu0= +github.com/pelletier/go-toml/v2 v2.0.9/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= @@ -100,9 +119,11 @@ github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdO github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= -github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/rwtodd/Go.Sed v0.0.0-20210816025313-55464686f9ef/go.mod h1:8AEUvGVi2uQ5b24BIhcr0GCcpd/RNAFWaN2CJFrWIIQ= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -118,44 +139,60 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.10 h1:eimT6Lsr+2lzmSZxPhLFoOWFmQqwk0fllJJ5hEbTXtQ= github.com/ugorji/go/codec v1.2.10/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.etcd.io/etcd/api/v3 v3.5.7 h1:sbcmosSVesNrWOJ58ZQFitHMdncusIifYcrBfwrlJSY= -go.etcd.io/etcd/api/v3 v3.5.7/go.mod h1:9qew1gCdDDLu+VwmeG+iFpL+QlpHTo7iubavdVDgCAA= -go.etcd.io/etcd/client/pkg/v3 v3.5.7 h1:y3kf5Gbp4e4q7egZdn5T7W9TSHUvkClN6u+Rq9mEOmg= -go.etcd.io/etcd/client/pkg/v3 v3.5.7/go.mod h1:o0Abi1MK86iad3YrWhgUsbGx1pmTS+hrORWc2CamuhY= -go.etcd.io/etcd/client/v3 v3.5.7 h1:u/OhpiuCgYY8awOHlhIhmGIGpxfBU/GZBUP3m/3/Iz4= -go.etcd.io/etcd/client/v3 v3.5.7/go.mod h1:sOWmj9DZUMyAngS7QQwCyAXXAL6WhgTOPLNS/NabQgw= -go.opentelemetry.io/otel v1.17.0 h1:MW+phZ6WZ5/uk2nd93ANk/6yJ+dVrvNWUjGhnnFU5jM= -go.opentelemetry.io/otel v1.17.0/go.mod h1:I2vmBGtFaODIVMBSTPVDlJSzBDNf93k60E6Ft0nyjo0= +github.com/zeromicro/go-zero v1.5.5 h1:qEHnDuCBu/gDBmfWEZXYow6ZmWmzsrJTjtjSMVm4SiY= +github.com/zeromicro/go-zero v1.5.5/go.mod h1:AGCspTFitHzYjl5ddAmYWLfdt341+BrhefqlwO45UbU= +go.etcd.io/etcd/api/v3 v3.5.9 h1:4wSsluwyTbGGmyjJktOf3wFQoTBIURXHnq9n/G/JQHs= +go.etcd.io/etcd/api/v3 v3.5.9/go.mod h1:uyAal843mC8uUVSLWz6eHa/d971iDGnCRpmKd2Z+X8k= +go.etcd.io/etcd/client/pkg/v3 v3.5.9 h1:oidDC4+YEuSIQbsR94rY9gur91UPL6DnxDCIYd2IGsE= +go.etcd.io/etcd/client/pkg/v3 v3.5.9/go.mod h1:y+CzeSmkMpWN2Jyu1npecjB9BBnABxGM4pN8cGuJeL4= +go.etcd.io/etcd/client/v3 v3.5.9 h1:r5xghnU7CwbUxD/fbUtRyJGaYNfDun8sp/gTr1hew6E= +go.etcd.io/etcd/client/v3 v3.5.9/go.mod h1:i/Eo5LrZ5IKqpbtpPDuaUnDOUv471oDg8cjQaUr2MbA= +go.opentelemetry.io/otel v1.18.0 h1:TgVozPGZ01nHyDZxK5WGPFB9QexeTMXEH7+tIClWfzs= +go.opentelemetry.io/otel v1.18.0/go.mod h1:9lWqYO0Db579XzVuCKFNPDl4s73Voa+zEck3wHaAYQI= go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4= go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0 h1:IAtl+7gua134xcV3NieDhJHjjOVeJhXAnYf/0hswjUY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0/go.mod h1:w+pXobnBzh95MNIkeIuAKcHe/Uu/CX2PKIvBP6ipKRA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.18.0 h1:yE32ay7mJG2leczfREEhoW3VfSZIvHaB+gvVo1o8DQ8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.18.0/go.mod h1:G17FHPDLt74bCI7tJ4CMitEk4BXTYG4FW6XUpkPBXa4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.18.0 h1:6pu8ttx76BxHf+xz/H77AUZkPF3cwWzXqAUsXhVKI18= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.18.0/go.mod h1:IOmXxPrxoxFMXdNy7lfDmE8MzE61YPcurbUm0SMjerI= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.17.0 h1:Ut6hgtYcASHwCzRHkXEtSsM251cXJPW+Z9DyLwEn6iI= go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.17.0/go.mod h1:TYeE+8d5CjrgBa0ZuRaDeMpIC1xZ7atg4g+nInjuSjc= -go.opentelemetry.io/otel/metric v1.17.0 h1:iG6LGVz5Gh+IuO0jmgvpTB6YVrCGngi8QGm+pMd8Pdc= -go.opentelemetry.io/otel/metric v1.17.0/go.mod h1:h4skoxdZI17AxwITdmdZjjYJQH5nzijUUjm+wtPph5o= -go.opentelemetry.io/otel/sdk v1.17.0 h1:FLN2X66Ke/k5Sg3V623Q7h7nt3cHXaW1FOvKKrW0IpE= -go.opentelemetry.io/otel/sdk v1.17.0/go.mod h1:U87sE0f5vQB7hwUoW98pW5Rz4ZDuCFBZFNUBlSgmDFQ= -go.opentelemetry.io/otel/trace v1.17.0 h1:/SWhSRHmDPOImIAetP1QAeMnZYiQXrTy4fMMYOdSKWQ= -go.opentelemetry.io/otel/trace v1.17.0/go.mod h1:I/4vKTgFclIsXRVucpH25X0mpFSczM7aHeaz0ZBLWjY= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.opentelemetry.io/otel/exporters/zipkin v1.18.0 h1:ZqrHgvega5NIiScTiVrtpZSpEmjUdwzkhuuCEIMAp+s= +go.opentelemetry.io/otel/exporters/zipkin v1.18.0/go.mod h1:C80yIYcSceQipAZb4Ah11EE/yERlyc1MtqJG2xP7p+s= +go.opentelemetry.io/otel/metric v1.18.0 h1:JwVzw94UYmbx3ej++CwLUQZxEODDj/pOuTCvzhtRrSQ= +go.opentelemetry.io/otel/metric v1.18.0/go.mod h1:nNSpsVDjWGfb7chbRLUNW+PBNdcSTHD4Uu5pfFMOI0k= +go.opentelemetry.io/otel/sdk v1.18.0 h1:e3bAB0wB3MljH38sHzpV/qWrOTCFrdZF2ct9F8rBkcY= +go.opentelemetry.io/otel/sdk v1.18.0/go.mod h1:1RCygWV7plY2KmdskZEDDBs4tJeHG92MdHZIluiYs/M= +go.opentelemetry.io/otel/trace v1.18.0 h1:NY+czwbHbmndxojTEKiSMHkG2ClNH2PwmcHrdo0JY10= +go.opentelemetry.io/otel/trace v1.18.0/go.mod h1:T2+SGJGuYZY3bjj5rgh/hN7KIrlpWC5nS8Mjvzckz+0= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= +go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.2.0 h1:W1sUEHXiJTfjaFJ5SLo0N6lZn+0eO5gWD1MFeTGqQEY= golang.org/x/arch v0.2.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -164,28 +201,26 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/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.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -194,24 +229,23 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto v0.0.0-20230301171018-9ab4bdc49ad5 h1:/cadn7taPtPlCgiWNetEPsle7jgnlad2R7gR5MXB6dM= -google.golang.org/genproto v0.0.0-20230301171018-9ab4bdc49ad5/go.mod h1:TvhZT5f700eVlTNwND1xoEZQeWTB2RY/65kplwl/bFA= -google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= +google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 h1:Z0hjGZePRE0ZBWotvtrwxFNrNE9CUAGtplaDK5NNI/g= +google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98/go.mod h1:S7mY02OqCJTD0E1OiQy1F72PWFB4bZJ87cAtLPYgDR0= +google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 h1:FmF5cCW94Ij59cfpoLiwTgodWmm60eEV0CjlsVg2fuw= +google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= +google.golang.org/grpc v1.58.0 h1:32JY8YpPMSR45K+c3o6b8VL73V+rR8k+DeMIr4vRH8o= +google.golang.org/grpc v1.58.0/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= 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.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/internal/devserver/config.go b/internal/devserver/config.go new file mode 100644 index 0000000..a289536 --- /dev/null +++ b/internal/devserver/config.go @@ -0,0 +1,11 @@ +package devserver + +type Config struct { + Enabled bool `json:",default=true"` + Host string `json:",optional"` + Port int `json:",default=6470"` + MetricsPath string `json:",default=/metrics"` + HealthPath string `json:",default=/healthz"` + EnableMetrics bool `json:",default=true"` + EnablePprof bool `json:",default=true"` +} diff --git a/internal/devserver/server.go b/internal/devserver/server.go new file mode 100644 index 0000000..54e8704 --- /dev/null +++ b/internal/devserver/server.go @@ -0,0 +1,73 @@ +package devserver + +import ( + "encoding/json" + "fmt" + "git.diulo.com/mogfee/kit/core/logx" + "git.diulo.com/mogfee/kit/internal/health" + "github.com/felixge/fgprof" + "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/zeromicro/go-zero/core/threading" + "net/http" + "net/http/pprof" + "sync" +) + +var once sync.Once + +type Server struct { + config Config + server *http.ServeMux + routes []string +} + +func NewServer(cfg Config) *Server { + return &Server{ + config: cfg, + server: http.NewServeMux(), + } +} +func (s *Server) addRoutes() { + s.handleFunc("/", func(writer http.ResponseWriter, r *http.Request) { + _ = json.NewEncoder(writer).Encode(s.routes) + }) + //health + s.handleFunc(s.config.HealthPath, health.CreateHttpHandler()) + //metrics + if s.config.EnableMetrics { + s.handleFunc(s.config.MetricsPath, promhttp.Handler().ServeHTTP) + } + + if s.config.EnablePprof { + + s.handleFunc("/debug/fgprof", fgprof.Handler().(http.HandlerFunc)) + s.handleFunc("/debug/pprof/", pprof.Index) + s.handleFunc("/debug/pprof/cmdline", pprof.Cmdline) + s.handleFunc("/debug/pprof/profile", pprof.Profile) + s.handleFunc("/debug/pprof/symbol", pprof.Symbol) + s.handleFunc("/debug/pprof/trace", pprof.Trace) + } +} +func (s *Server) handleFunc(pattern string, handler http.HandlerFunc) { + s.server.HandleFunc(pattern, handler) + s.routes = append(s.routes, pattern) +} + +func (s *Server) StartASync() { + s.addRoutes() + threading.GoSafe(func() { + addr := fmt.Sprintf("%s:%d", s.config.Host, s.config.Port) + logx.Infof("Starting dev http server at %s", addr) + if err := http.ListenAndServe(addr, s.server); err != nil { + logx.Error(err) + } + }) +} +func StartAgent(c Config) { + once.Do(func() { + if c.Enabled { + s := NewServer(c) + s.StartASync() + } + }) +} diff --git a/internal/encoding/encoding.go b/internal/encoding/encoding.go new file mode 100644 index 0000000..0ffbace --- /dev/null +++ b/internal/encoding/encoding.go @@ -0,0 +1,65 @@ +package encoding + +import ( + "bytes" + "encoding/json" + "git.diulo.com/mogfee/kit/lang" + "github.com/pelletier/go-toml/v2" + "gopkg.in/yaml.v3" +) + +func TomlToJson(data []byte) ([]byte, error) { + var val any + if err := toml.NewDecoder(bytes.NewReader(data)).Decode(&val); err != nil { + return nil, err + } + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(val); err != nil { + return nil, err + } + return buf.Bytes(), nil +} +func YamlToJson(data []byte) ([]byte, error) { + var val any + if err := yaml.Unmarshal(data, &val); err != nil { + return nil, err + } + val = toStringKeyMap(val) + var buf bytes.Buffer + if err := json.NewEncoder(&buf).Encode(val); err != nil { + return nil, err + } + return buf.Bytes(), nil +} +func convertKeyToString(in map[any]any) map[string]any { + res := make(map[string]any) + for k, v := range in { + res[lang.Repr(k)] = toStringKeyMap(v) + } + return res +} + +func convertNumberToJsonNumber(in any) json.Number { + return json.Number(lang.Repr(in)) +} +func covertSlice(in []any) []any { + res := make([]any, len(in)) + for i, v := range in { + res[i] = toStringKeyMap(v) + } + return res +} +func toStringKeyMap(v any) any { + switch v := v.(type) { + case []any: + return covertSlice(v) + case map[any]any: + return convertKeyToString(v) + case bool, string: + return v + case int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64, float32, float64: + return convertNumberToJsonNumber(v) + default: + return lang.Repr(v) + } +} diff --git a/internal/health/health.go b/internal/health/health.go new file mode 100644 index 0000000..aa1a72a --- /dev/null +++ b/internal/health/health.go @@ -0,0 +1,109 @@ +package health + +import ( + "fmt" + "github.com/zeromicro/go-zero/core/syncx" + "net/http" + "strings" + "sync" +) + +var defaultHealthManager = newComboHealthManager() + +type ( + Probe interface { + MarkReady() + MarkNotReady() + IsReady() bool + Name() string + } + healthManager struct { + ready syncx.AtomicBool + name string + } + comboHealthManager struct { + mu sync.Mutex + probes []Probe + } +) + +func AddProbe(probe Probe) { + defaultHealthManager.addProbe(probe) +} +func CreateHttpHandler() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + if defaultHealthManager.IsReady() { + _, _ = w.Write([]byte("OK")) + } else { + http.Error(w, "Service Unavailable\n"+defaultHealthManager.verboseInfo(), + http.StatusServiceUnavailable) + } + } +} +func NewHealthManager(name string) Probe { + return &healthManager{ + name: name, + } +} + +func (h *healthManager) MarkReady() { + h.ready.Set(true) +} + +func (h *healthManager) MarkNotReady() { + h.ready.Set(false) +} + +func (h *healthManager) IsReady() bool { + return h.ready.True() +} + +func (h *healthManager) Name() string { + return h.name +} +func newComboHealthManager() *comboHealthManager { + return &comboHealthManager{} +} + +func (p *comboHealthManager) MarkReady() { + p.mu.Lock() + defer p.mu.Unlock() + for _, probe := range p.probes { + probe.MarkReady() + } +} +func (p *comboHealthManager) MarkNotReady() { + p.mu.Lock() + defer p.mu.Unlock() + for _, probe := range p.probes { + probe.MarkNotReady() + } +} +func (p *comboHealthManager) IsReady() bool { + p.mu.Lock() + defer p.mu.Unlock() + for _, probe := range p.probes { + if !probe.IsReady() { + return false + } + } + return true +} +func (p *comboHealthManager) verboseInfo() string { + p.mu.Lock() + defer p.mu.Unlock() + var info strings.Builder + for _, probe := range p.probes { + if probe.IsReady() { + info.WriteString(fmt.Sprintf("%s is ready\n", probe.Name())) + } else { + info.WriteString(fmt.Sprintf("%s is not ready\n", probe.Name())) + } + } + return info.String() +} +func (p *comboHealthManager) addProbe(probe Probe) { + p.mu.Lock() + defer p.mu.Unlock() + p.probes = append(p.probes, probe) +} diff --git a/internal/matcher/matcher.go b/internal/matcher/matcher.go index 48e1dfb..9af8041 100644 --- a/internal/matcher/matcher.go +++ b/internal/matcher/matcher.go @@ -6,12 +6,14 @@ import ( "strings" ) +// Matcher is a middleware matcher. type Matcher interface { Use(ms ...middleware.Middleware) Add(selector string, ms ...middleware.Middleware) Match(operation string) []middleware.Middleware } +// New new a middleware matcher. func New() Matcher { return &matcher{ matchs: make(map[string][]middleware.Middleware), @@ -32,6 +34,9 @@ func (m *matcher) Add(selector string, ms ...middleware.Middleware) { if strings.HasSuffix(selector, "*") { selector = strings.TrimSuffix(selector, "*") m.prefix = append(m.prefix, selector) + // sort the prefix: + // - /foo/bar + // - /foo sort.Slice(m.prefix, func(i, j int) bool { return m.prefix[i] > m.prefix[j] }) @@ -45,7 +50,7 @@ func (m *matcher) Match(operation string) []middleware.Middleware { ms = append(ms, m.defaults...) } if next, ok := m.matchs[operation]; ok { - ms = append(ms, next...) + return append(ms, next...) } for _, prefix := range m.prefix { if strings.HasPrefix(operation, prefix) { diff --git a/internal/trace/trace.go b/internal/trace/trace.go new file mode 100644 index 0000000..2025432 --- /dev/null +++ b/internal/trace/trace.go @@ -0,0 +1,21 @@ +package trace + +import ( + "context" + "go.opentelemetry.io/otel/trace" +) + +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 "" +} diff --git a/middleware/cors/cors.go b/middleware/cors/cors.go index b556ea6..9d81893 100644 --- a/middleware/cors/cors.go +++ b/middleware/cors/cors.go @@ -2,6 +2,7 @@ package cors import ( "context" + "fmt" "git.diulo.com/mogfee/kit/middleware" "git.diulo.com/mogfee/kit/transport" "net/http" @@ -68,6 +69,7 @@ type cors struct { } func (ch *cors) ServeHTTP(w http.ResponseWriter, r *http.Request) { - + fmt.Println("cors start") ch.h.ServeHTTP(w, r) + fmt.Println("cors end") } diff --git a/mysql/builder/builder.go b/mysql/builder/builder.go index 37beeb4..718b22c 100644 --- a/mysql/builder/builder.go +++ b/mysql/builder/builder.go @@ -2,7 +2,7 @@ package builder import ( "fmt" - "git.diulo.com/mogfee/kit/stringx" + "git.diulo.com/mogfee/kit/core/stringx" "reflect" "strings" ) diff --git a/mysql/sqlx/orm.go b/mysql/sqlx/orm.go index 68b7d79..98f5153 100644 --- a/mysql/sqlx/orm.go +++ b/mysql/sqlx/orm.go @@ -2,7 +2,7 @@ package sqlx import ( "errors" - "git.diulo.com/mogfee/kit/mapping" + "git.diulo.com/mogfee/kit/core/mapping" "reflect" "strings" ) diff --git a/mysql/sqlx/sqlmanager.go b/mysql/sqlx/sqlmanager.go index f749768..c042491 100644 --- a/mysql/sqlx/sqlmanager.go +++ b/mysql/sqlx/sqlmanager.go @@ -2,7 +2,7 @@ package sqlx import ( "database/sql" - "git.diulo.com/mogfee/kit/syncx" + "git.diulo.com/mogfee/kit/core/syncx" "io" "time" ) diff --git a/mysql/sqlx/utils.go b/mysql/sqlx/utils.go index 3f666c5..037c383 100644 --- a/mysql/sqlx/utils.go +++ b/mysql/sqlx/utils.go @@ -4,7 +4,7 @@ import ( "context" "errors" "fmt" - "git.diulo.com/mogfee/kit/mapping" + "git.diulo.com/mogfee/kit/core/mapping" "strconv" "strings" "time" diff --git a/transport/http/router.go b/transport/http/router.go index e19c5c4..26691fd 100644 --- a/transport/http/router.go +++ b/transport/http/router.go @@ -1,6 +1,7 @@ package http import ( + "fmt" "net/http" "path" "sync" @@ -16,7 +17,7 @@ type RouteInfo struct { } // HandlerFunc defines a function to serve HTTP requests. -type HandlerFunc func(ctx Context) error +type HandlerFunc func(Context) error // Router is an HTTP router. type Router struct { @@ -54,6 +55,7 @@ func (r *Router) Handle(method, relativePath string, h HandlerFunc, filters ...F if err := h(ctx); err != nil { r.srv.ene(res, req, err) } + fmt.Println("=resp") ctx.Reset(nil, nil) r.pool.Put(ctx) }))