Google Cloud Go SDK启用HTTP日志遇gRPC兼容错误的解决方法
解决Google Cloud Go SDK Service Usage客户端的"WithHTTPClient incompatible"错误及日志方案
一、错误原因分析
Service Usage客户端默认采用gRPC传输协议,而WithHTTPClient选项仅适用于使用REST/HTTP1.1协议的客户端。两者底层传输机制不同,混用会直接触发"WithHTTPClient is incompatible with gRPC dial options"错误——即使你未手动设置gRPC选项,SDK也会默认启用gRPC相关配置。
二、错误解决办法
方案1:切换到REST版客户端使用自定义HTTP客户端
如果需要通过自定义HTTP客户端捕获日志,直接使用Service Usage的REST版本客户端即可,它天然支持WithHTTPClient选项:
import ( "context" "net/http" "net/http/httputil" "google.golang.org/api/option" serviceusage "google.golang.org/api/serviceusage/v1" ) // 自定义带请求响应日志的Transport type loggingTransport struct { http.RoundTripper } func (t *loggingTransport) RoundTrip(req *http.Request) (*http.Response, error) { // 打印请求详情 reqDump, _ := httputil.DumpRequestOut(req, true) println("=== HTTP Request ===\n", string(reqDump)) // 发送请求并获取响应 resp, err := t.RoundTripper.RoundTrip(req) if err != nil { return resp, err } // 打印响应详情 respDump, _ := httputil.DumpResponse(resp, true) println("=== HTTP Response ===\n", string(respDump)) return resp, nil } func main() { ctx := context.Background() // 构建带日志Transport的HTTP客户端 customHTTPClient := &http.Client{ Transport: &loggingTransport{RoundTripper: http.DefaultTransport}, } // 创建REST版Service Usage客户端,传入自定义HTTP客户端 svc, err := serviceusage.NewService(ctx, option.WithHTTPClient(customHTTPClient)) if err != nil { panic(err) } // 调用API列出已启用服务 result, err := svc.Services.List("projects/你的项目ID").Filter("state:ENABLED").Do() if err != nil { panic(err) } // 处理返回结果... }
方案2:保留gRPC客户端,改用gRPC拦截器日志
如果要继续使用默认的gRPC客户端,不要用WithHTTPClient,而是通过gRPC的一元拦截器来捕获请求响应日志:
import ( "context" "fmt" "google.golang.org/grpc" serviceusagepb "google.golang.org/genproto/googleapis/api/serviceusage/v1" serviceusage "google.golang.org/api/serviceusage/v1" "google.golang.org/api/option" ) // gRPC一元拦截器:打印请求响应详情 func grpcLoggingInterceptor( ctx context.Context, method string, req interface{}, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption, ) error { fmt.Printf("=== gRPC Request ===\nMethod: %s\nBody: %+v\n", method, req) err := invoker(ctx, method, req, reply, cc, opts...) if err != nil { fmt.Printf("=== gRPC Error ===\nMethod: %s\nError: %v\n", method, err) return err } fmt.Printf("=== gRPC Response ===\nMethod: %s\nBody: %+v\n", method, reply) return nil } func main() { ctx := context.Background() // 创建带日志拦截器的gRPC客户端 client, err := serviceusage.NewClient(ctx, option.WithGRPCDialOption(grpc.WithUnaryInterceptor(grpcLoggingInterceptor)), // 生产环境无需此配置,默认会使用安全凭证 // option.WithGRPCDialOption(grpc.WithTransportCredentials(insecure.NewCredentials())), ) if err != nil { panic(err) } defer client.Close() // 调用API列出已启用服务 resp, err := client.ListServices(ctx, &serviceusagepb.ListServicesRequest{ Parent: "projects/你的项目ID", Filter: "state:ENABLED", }) if err != nil { panic(err) } // 处理返回结果... }
三、无需自定义Transport的日志启用方案
Google Cloud Go SDK内置了日志能力,无需自定义Transport或拦截器,直接通过环境变量或内置配置即可开启:
方法1:通过环境变量快速启用日志
- REST/HTTP客户端:设置
GOOGLE_API_GO_EXPERIMENTAL_LOGGING=1,SDK会自动打印HTTP请求响应的完整细节(包括URL、Headers、Body):export GOOGLE_API_GO_EXPERIMENTAL_LOGGING=1 go run 你的程序.go - gRPC客户端:设置以下两个环境变量,开启gRPC级别的请求日志:
export GRPC_GO_LOG_SEVERITY_LEVEL=info export GRPC_GO_LOG_VERBOSITY_LEVEL=2 go run 你的程序.go
方法2:集成自定义日志库
如果需要将日志接入自己的日志系统(如Zap、Glog),可以通过option.WithLogger将SDK日志定向到自定义日志器:
import ( "context" "go.uber.org/zap" serviceusage "google.golang.org/api/serviceusage/v1" "google.golang.org/api/option" "google.golang.org/api/support/logging" ) func main() { ctx := context.Background() zapLogger, _ := zap.NewProduction() defer zapLogger.Sync() // 将SDK日志定向到Zap日志器 client, err := serviceusage.NewClient(ctx, option.WithLogger(logging.NewGoogleLogger(zapLogger.Sugar())), ) if err != nil { panic(err) } defer client.Close() // 调用API... }
内容的提问来源于stack exchange,提问作者Radek Simko
相关产品推荐
相关产品推荐

