diff --git a/cmd/servicechecker/main.go b/cmd/servicechecker/main.go index 13ad43c..4cd349b 100644 --- a/cmd/servicechecker/main.go +++ b/cmd/servicechecker/main.go @@ -8,6 +8,7 @@ import ( "time" openapi_v3 "github.com/google/gnostic/openapiv3" + "github.com/google/uuid" grpccli "go.unistack.org/micro-client-grpc/v3" httpcli "go.unistack.org/micro-client-http/v3" jsoncodec "go.unistack.org/micro-codec-json/v3" @@ -156,7 +157,7 @@ func main() { l.Info(ctx, "exiting") } -func newHTTPTask(ctx context.Context, l logger.Logger, m meter.Meter, check string, task *config.Task) (any, []any, error) { +func newHTTPTask(ctx context.Context, l logger.Logger, m meter.Meter, check string, task *config.TaskConfig) (any, []any, error) { var err error c, ok := clients["http"] @@ -193,6 +194,7 @@ func newHTTPTask(ctx context.Context, l logger.Logger, m meter.Meter, check stri httpcli.ErrorMap(errmap), httpcli.Method(task.HTTP.Method), httpcli.Path(task.HTTP.Endpoint), + // client.WithContentType("application/json"), } @@ -205,18 +207,39 @@ func newHTTPTask(ctx context.Context, l logger.Logger, m meter.Meter, check stri } fn := func() { + var cerr error + + metadata := make(map[string]string, len(task.HTTP.Metadata)) + var rquid string + for k, v := range task.HTTP.Metadata { + if k == "x-request-id" && v == "generate" { + uid, err := uuid.NewV7() + if err != nil { + l.Error(ctx, "failed to generate x-request-id", err) + uid = uuid.Nil + } else { + v = uid.String() + } + } + metadata[k] = v + rquid = v + } + + l.Info(ctx, fmt.Sprintf("call %s.%s endpoint %s", treq.Service(), treq.Method(), treq.Endpoint()), "x-request-id", rquid) m.Counter(semconv.ClientRequestInflight, labels...).Inc() ts := time.Now() - // l.Info(ctx, fmt.Sprintf("try to call %s.%s via %s", task.Name, task.Name, task.HTTP.Addr)) - err = httpconn.Call(ctx, l, c, task.HTTP.Addr, time.Duration(task.Timeout), + cerr = httpconn.Call(ctx, rquid, l, c, task.HTTP.Addr, time.Duration(task.Timeout), treq, rsp, - opts...) + append(opts, client.WithRequestMetadata(metadata))..., + ) te := time.Since(ts) + m.Counter(semconv.ClientRequestInflight, labels...).Dec() + m.Summary(semconv.ClientRequestLatencyMicroseconds, labels...).Update(te.Seconds()) m.Histogram(semconv.ClientRequestDurationSeconds, labels...).Update(te.Seconds()) - m.Counter(semconv.ClientRequestInflight, labels...).Dec() - if err != nil { + + if cerr != nil { m.Counter(semconv.ClientRequestTotal, append(labels, "status", "failure")...).Inc() } else { m.Counter(semconv.ClientRequestTotal, append(labels, "status", "success")...).Inc() @@ -226,7 +249,7 @@ func newHTTPTask(ctx context.Context, l logger.Logger, m meter.Meter, check stri return fn, nil, nil } -func newGRPCTask(ctx context.Context, l logger.Logger, m meter.Meter, check string, task *config.Task) (any, []any, error) { +func newGRPCTask(ctx context.Context, l logger.Logger, m meter.Meter, check string, task *config.TaskConfig) (any, []any, error) { var err error c, ok := clients["grpc"] @@ -296,17 +319,38 @@ func newGRPCTask(ctx context.Context, l logger.Logger, m meter.Meter, check stri } fn := func() { + var cerr error + var rquid string + metadata := make(map[string]string, len(task.HTTP.Metadata)) + for k, v := range task.GRPC.Metadata { + if k == "x-request-id" && v == "generate" { + uid, err := uuid.NewV7() + if err != nil { + l.Error(ctx, "failed to generate x-request-id", err) + uid = uuid.Nil + } else { + v = uid.String() + } + } + metadata[k] = v + rquid = v + } + + l.Info(ctx, fmt.Sprintf("call %s.%s endpoint %s", treq.Service(), treq.Method(), treq.Endpoint()), "x-request-id", rquid) m.Counter(semconv.ClientRequestInflight, labels...).Inc() ts := time.Now() - - err = grpcconn.Call(ctx, l, c, task.GRPC.Addr, time.Duration(task.Timeout), + cerr = grpcconn.Call(ctx, rquid, l, c, task.GRPC.Addr, time.Duration(task.Timeout), treq, - rsp) + rsp, + client.WithRequestMetadata(metadata), + ) te := time.Since(ts) + m.Counter(semconv.ClientRequestInflight, labels...).Dec() + m.Summary(semconv.ClientRequestLatencyMicroseconds, labels...).Update(te.Seconds()) m.Histogram(semconv.ClientRequestDurationSeconds, labels...).Update(te.Seconds()) - m.Counter(semconv.ClientRequestInflight, labels...).Dec() - if err != nil { + + if cerr != nil { m.Counter(semconv.ClientRequestTotal, append(labels, "status", "failure")...).Inc() } else { m.Counter(semconv.ClientRequestTotal, append(labels, "status", "success")...).Inc() diff --git a/pkg/config/config.go b/pkg/config/config.go index d42d9cf..7acb673 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -7,46 +7,53 @@ import ( mtime "go.unistack.org/micro/v3/util/time" ) -type Meter struct { +type AppConfig struct { + MultiUser bool `json:"multi_user,omitempty" yaml:"multi_user,omitempty"` +} + +type MeterConfig struct { Addr string `json:"addr,omitempty" yaml:"addr,omitempty"` Path string `json:"path,omitempty" yaml:"path,omitempty"` } type Config struct { - Meter *Meter `json:"meter,omitempty" yaml:"meter,omitempty"` - Checks []*Check `json:"checks,omitempty" yaml:"checks,omitempty"` + Meter *MeterConfig `json:"meter,omitempty" yaml:"meter,omitempty"` + Checks []*CheckConfig `json:"checks,omitempty" yaml:"checks,omitempty"` } -type Check struct { +type CheckConfig struct { Name string `json:"name,omitempty" yaml:"name,omitempty"` - Tasks []*Task `json:"tasks,omitempty" yaml:"tasks,omitempty"` + Tasks []*TaskConfig `json:"tasks,omitempty" yaml:"tasks,omitempty"` Timeout mtime.Duration `json:"timeout,omitempty" yaml:"timeout,omitempty"` Interval mtime.Duration `json:"interval,omitempty" yaml:"interval,omitempty"` Active bool `json:"active,omitempty" yaml:"active,omitempty"` } type HTTPConfig struct { - Endpoint string `json:"endpoint,omitempty" yaml:"endpoint,omitempty"` - Data string `json:"data,omitempty" yaml:"data,omitempty"` - Addr string `json:"addr,omitempty" yaml:"addr,omitempty"` - OpenAPI string `json:"openapi,omitempty" yaml:"openapi,omitempty"` - Method string `json:"method,omitempty" yaml:"method,omitempty"` + Metadata map[string]string `json:"metadata,omitempty" yaml:"metadata,omitempty"` + Endpoint string `json:"endpoint,omitempty" yaml:"endpoint,omitempty"` + Data string `json:"data,omitempty" yaml:"data,omitempty"` + Addr string `json:"addr,omitempty" yaml:"addr,omitempty"` + OpenAPI string `json:"openapi,omitempty" yaml:"openapi,omitempty"` + Method string `json:"method,omitempty" yaml:"method,omitempty"` } type GRPCConfig struct { - Endpoint string `json:"endpoint,omitempty" yaml:"endpoint,omitempty"` - Data string `json:"data,omitempty" yaml:"data,omitempty"` - Addr string `json:"addr,omitempty" yaml:"addr,omitempty"` - Protoset string `json:"protoset,omitempty" yaml:"protoset,omitempty"` + Metadata map[string]string `json:"metadata,omitempty" yaml:"metadata,omitempty"` + Endpoint string `json:"endpoint,omitempty" yaml:"endpoint,omitempty"` + Data string `json:"data,omitempty" yaml:"data,omitempty"` + Addr string `json:"addr,omitempty" yaml:"addr,omitempty"` + Protoset string `json:"protoset,omitempty" yaml:"protoset,omitempty"` } type GraphQLConfig struct { - Endpoint string `json:"endpoint,omitempty" yaml:"endpoint,omitempty"` - Data string `json:"data,omitempty" yaml:"data,omitempty"` - Addr string `json:"addr,omitempty" yaml:"addr,omitempty"` + Metadata map[string]string `json:"metadata,omitempty" yaml:"metadata,omitempty"` + Endpoint string `json:"endpoint,omitempty" yaml:"endpoint,omitempty"` + Data string `json:"data,omitempty" yaml:"data,omitempty"` + Addr string `json:"addr,omitempty" yaml:"addr,omitempty"` } -type Task struct { +type TaskConfig struct { HTTP *HTTPConfig `json:"http,omitempty" yaml:"http,omitempty"` GRPC *GRPCConfig `json:"grpc,omitempty" yaml:"grpc,omitempty"` GraphQL *GraphQLConfig `json:"graphql,omitempty" yaml:"graphql"` diff --git a/pkg/grpcconn/grpcconn.go b/pkg/grpcconn/grpcconn.go index 58b4eed..48f85b5 100644 --- a/pkg/grpcconn/grpcconn.go +++ b/pkg/grpcconn/grpcconn.go @@ -5,18 +5,12 @@ import ( "strings" "time" - "github.com/google/uuid" "go.unistack.org/micro/v3/client" "go.unistack.org/micro/v3/logger" ) -func Call(ctx context.Context, l logger.Logger, c client.Client, addr string, td time.Duration, req client.Request, rsp interface{}, opts ...client.CallOption) error { +func Call(ctx context.Context, rquid string, l logger.Logger, c client.Client, addr string, td time.Duration, req client.Request, rsp interface{}, opts ...client.CallOption) error { var err error - uid, err := uuid.NewRandom() - if err != nil { - l.Error(ctx, "failed to generate x-request-id", err) - return err - } err = c.Call(ctx, req, rsp, client.WithAddress(addr), @@ -24,7 +18,7 @@ func Call(ctx context.Context, l logger.Logger, c client.Client, addr string, td // client.WithContentType("application/json"), ) if err != nil { - l.Error(ctx, "call failed", "x-request-id", uid.String(), err) + l.Error(ctx, "call failed", "x-request-id", rquid, err) return err } return nil diff --git a/pkg/httpconn/httpconn.go b/pkg/httpconn/httpconn.go index a53e9ce..122cb48 100644 --- a/pkg/httpconn/httpconn.go +++ b/pkg/httpconn/httpconn.go @@ -4,18 +4,12 @@ import ( "context" "time" - "github.com/google/uuid" "go.unistack.org/micro/v3/client" "go.unistack.org/micro/v3/logger" ) -func Call(ctx context.Context, l logger.Logger, c client.Client, addr string, td time.Duration, req client.Request, rsp interface{}, opts ...client.CallOption) error { +func Call(ctx context.Context, rquid string, l logger.Logger, c client.Client, addr string, td time.Duration, req client.Request, rsp interface{}, opts ...client.CallOption) error { var err error - uid, err := uuid.NewRandom() - if err != nil { - l.Error(ctx, "failed to generate x-request-id", err) - return err - } err = c.Call(ctx, req, rsp, client.WithAddress(addr), @@ -23,7 +17,8 @@ func Call(ctx context.Context, l logger.Logger, c client.Client, addr string, td // client.WithContentType("application/json"), ) if err != nil { - l.Error(ctx, "call failed", "x-request-id", uid.String(), err) + // httpcli.GetError(err) + l.Error(ctx, "call failed", "x-request-id", rquid, err) return err } return nil