diff --git a/components/client.go b/components/client.go deleted file mode 100644 index 93383a8..0000000 --- a/components/client.go +++ /dev/null @@ -1,234 +0,0 @@ -package components - -import ( - "context" - "crypto/tls" - "encoding/json" - "fmt" - "io" - "net/http" - "net/url" - "strconv" - "sync" - "time" - - "github.com/dustin/go-humanize" - "github.com/go-resty/resty/v2" - "github.com/pkg/errors" - "google.golang.org/grpc/metadata" - "k8s.io/klog/v2" -) - -type ctxKey int - -const ( - timeout = time.Second * 30 - // BKAPIRequestIDHeader 蓝鲸网关的请求ID - BKAPIRequestIDHeader = "X-Bkapi-Request-Id" - userAgent = "prime/v1.0" - requestIDCtxKey = ctxKey(1) - // RequestIDHeaderKey - RequestIDHeaderKey = "X-Request-Id" -) - -var ( - maskKeys = map[string]struct{}{ - "bk_app_secret": {}, - } - clientOnce sync.Once - globalClient *resty.Client -) - -// WithLabelMatchValue 设置 RequestId 值 -func WithRequestIDValue(ctx context.Context, id string) context.Context { - newCtx := context.WithValue(ctx, requestIDCtxKey, id) - return metadata.AppendToOutgoingContext(newCtx, RequestIDHeaderKey, id) -} - -// RequestIDValue 获取 RequestId 值 -func RequestIDValue(ctx context.Context) string { - v, ok := ctx.Value(requestIDCtxKey).(string) - if !ok || v == "" { - return grpcRequestIDValue(ctx) - } - - return v -} - -// grpcRequestIDValue grpc 需要单独处理 -func grpcRequestIDValue(ctx context.Context) string { - md, ok := metadata.FromIncomingContext(ctx) - if !ok { - return "" - } - values := md.Get(RequestIDHeaderKey) - if len(values) == 0 { - return "" - } - return values[0] -} - -// SetRequestIDHeaderValue 设置 RequestId 值到头部 -func SetRequestIDHeaderValue(req *http.Request, id string) { - req.Header.Set(RequestIDHeaderKey, id) -} - -// restyReqToCurl curl 格式的请求日志 -func restyReqToCurl(r *resty.Request) string { - headers := "" - for key, values := range r.Header { - for _, value := range values { - headers += fmt.Sprintf(" -H %q", fmt.Sprintf("%s: %s", key, value)) - } - } - - // 过滤掉敏感信息 - rawURL := *r.RawRequest.URL - queryValue := rawURL.Query() - for key := range queryValue { - if _, ok := maskKeys[key]; ok { - queryValue.Set(key, "") - } - } - rawURL.RawQuery = queryValue.Encode() - - reqMsg := fmt.Sprintf("curl -X %s '%s'%s", r.Method, rawURL.String(), headers) - if r.Body != nil { - switch body := r.Body.(type) { - case []byte: - reqMsg += fmt.Sprintf(" -d %q", body) - case string: - reqMsg += fmt.Sprintf(" -d %q", body) - case io.Reader: - reqMsg += " -d (io.Reader)" - default: - prtBodyBytes, err := json.Marshal(body) - if err != nil { - reqMsg += fmt.Sprintf(" -d %q (MarshalErr %s)", body, err) - } else { - reqMsg += fmt.Sprintf(" -d '%s'", prtBodyBytes) - } - } - } - if r.FormData.Encode() != "" { - encodeStr := r.FormData.Encode() - reqMsg += fmt.Sprintf(" -d %q", encodeStr) - rawStr, _ := url.QueryUnescape(encodeStr) - reqMsg += fmt.Sprintf(" -raw `%s`", rawStr) - } - - return reqMsg -} - -// restyResponseToCurl 返回日志 -func restyResponseToCurl(resp *resty.Response) string { - // 最大打印 1024 个字符 - body := string(resp.Body()) - if len(body) > 1024 { - body = fmt.Sprintf("%s...(Total %s)", body[:1024], humanize.Bytes(uint64(len(body)))) - } - - respMsg := fmt.Sprintf("[%s] %s %s", resp.Status(), resp.Time(), body) - - // 请求蓝鲸网关记录RequestID - bkAPIRequestID := resp.RawResponse.Header.Get(BKAPIRequestIDHeader) - if bkAPIRequestID != "" { - respMsg = fmt.Sprintf("[%s] %s bkapi_request_id=%s %s", resp.Status(), resp.Time(), bkAPIRequestID, body) - } - - return respMsg -} - -func restyErrHook(r *resty.Request, err error) { - klog.Infof("[%s] REQ: %s", RequestIDValue(r.RawRequest.Context()), restyReqToCurl(r)) - klog.Infof("[%s] RESP: [err] %s", RequestIDValue(r.RawRequest.Context()), err) -} - -func restyAfterResponseHook(c *resty.Client, r *resty.Response) error { - klog.Infof("[%s] REQ: %s", RequestIDValue(r.Request.Context()), restyReqToCurl(r.Request)) - klog.Infof("[%s] RESP: %s", RequestIDValue(r.Request.Context()), restyResponseToCurl(r)) - return nil -} - -func restyBeforeRequestHook(c *resty.Client, r *http.Request) error { - SetRequestIDHeaderValue(r, RequestIDValue(r.Context())) - return nil -} - -// GetClient : 新建Client, 设置公共参数,每次新建,cookies不复用 -func GetClient() *resty.Client { - if globalClient == nil { - clientOnce.Do(func() { - globalClient = resty.New(). - SetTimeout(timeout). - SetDebug(false). // 更多详情, 可以开启为 true - SetCookieJar(nil). // 后台API去掉 cookie 记录 - SetDebugBodyLimit(1024). - OnAfterResponse(restyAfterResponseHook). - SetPreRequestHook(restyBeforeRequestHook). - OnError(restyErrHook). - SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}). - SetHeader("User-Agent", userAgent) - }) - } - return globalClient -} - -// BKResult 蓝鲸返回规范的结构体 -type BKResult struct { - Code interface{} `json:"code"` - Message string `json:"message"` - Data interface{} `json:"data"` -} - -// UnmarshalBKResult 反序列化为蓝鲸返回规范 -func UnmarshalBKResult(resp *resty.Response, data interface{}) error { - if resp.StatusCode() != http.StatusOK { - return errors.Errorf("http code %d != 200", resp.StatusCode()) - } - - // 部分接口,如 usermanager 返回的content-type不是json, 需要手动Unmarshal - bkResult := &BKResult{Data: data} - if err := json.Unmarshal(resp.Body(), bkResult); err != nil { - return err - } - - if err := bkResult.ValidateCode(); err != nil { - return err - } - - return nil -} - -// ValidateCode 返回结果是否OK -func (r *BKResult) ValidateCode() error { - code, err := refineCode(r.Code) - if err != nil { - return err - } - if code != 0 { - return errors.Errorf("resp code %d != 0, %s", code, r.Message) - } - return nil -} - -// refineCode 多种返回Code统一处理 -// 支持 "00", 0, "0" -func refineCode(code interface{}) (int, error) { - var resultCode int - switch code := code.(type) { - case int: - resultCode = code - case float64: - resultCode = int(code) - case string: - c, err := strconv.Atoi(code) - if err != nil { - return -1, err - } - resultCode = c - default: - return -1, errors.Errorf("conversion to int from %T not supported", code) - } - return resultCode, nil -} diff --git a/go.mod b/go.mod index 46a9e53..5a284d6 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,6 @@ require ( github.com/go-resty/resty/v2 v2.7.0 github.com/google/uuid v1.6.0 github.com/patrickmn/go-cache v2.1.0+incompatible - github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.14.0 github.com/redis/go-redis/v9 v9.0.3 github.com/samber/lo v1.47.0 @@ -24,8 +23,6 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 go.opentelemetry.io/otel/sdk v1.27.0 - google.golang.org/grpc v1.64.0 - k8s.io/klog/v2 v2.90.1 ) require ( @@ -63,6 +60,7 @@ require ( golang.org/x/sys v0.20.0 // indirect golang.org/x/text v0.16.0 // indirect google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd // indirect + google.golang.org/grpc v1.64.0 // indirect google.golang.org/protobuf v1.34.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 15ee35a..bb5c189 100644 --- a/go.sum +++ b/go.sum @@ -34,7 +34,6 @@ github.com/go-chi/chi/v5 v5.0.11 h1:BnpYbFZ3T3S1WMpD79r7R5ThWX40TaFB7L31Y8xqSwA= github.com/go-chi/chi/v5 v5.0.11/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg= github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -86,8 +85,6 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zk github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/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/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= @@ -162,5 +159,3 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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= -k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= -k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= diff --git a/http/rest/response.go b/http/rest/response.go index 1707519..1af1b75 100644 --- a/http/rest/response.go +++ b/http/rest/response.go @@ -1,10 +1,10 @@ package rest import ( + "errors" "net/http" "github.com/go-chi/render" - "github.com/pkg/errors" ) var ( @@ -14,12 +14,12 @@ var ( // APIResponse 返回的标准结构 type APIResponse struct { - Err error `json:"-"` // low-level runtime error - HTTPStatusCode int `json:"-"` // http response status code - Code int `json:"code"` - Message string `json:"message"` - RequestId string `json:"request_id"` - Data interface{} `json:"data"` + Err error `json:"-"` // low-level runtime error + HTTPStatusCode int `json:"-"` // http response status code + Code int `json:"code"` + Message string `json:"message"` + RequestId string `json:"request_id"` + Data any `json:"data"` } // Render chi Render 实现