diff --git a/internal/gengapic/client_init_test.go b/internal/gengapic/client_init_test.go index 74209f2ab..28332118e 100644 --- a/internal/gengapic/client_init_test.go +++ b/internal/gengapic/client_init_test.go @@ -414,6 +414,7 @@ func TestClientInit(t *testing.T) { {Name: "iampb", Path: "cloud.google.com/go/iam/apiv1/iampb"}: true, {Name: "locationpb", Path: "google.golang.org/genproto/googleapis/cloud/location"}: true, {Name: "mypackagepb", Path: "github.com/googleapis/mypackage"}: true, + {Path: "log/slog"}: true, }, wantNumSnps: 6, }, @@ -436,6 +437,7 @@ func TestClientInit(t *testing.T) { {Name: "iampb", Path: "cloud.google.com/go/iam/apiv1/iampb"}: true, {Name: "locationpb", Path: "google.golang.org/genproto/googleapis/cloud/location"}: true, {Name: "mypackagepb", Path: "github.com/googleapis/mypackage"}: true, + {Path: "log/slog"}: true, }, wantNumSnps: 6, }, @@ -453,6 +455,7 @@ func TestClientInit(t *testing.T) { {Name: "gtransport", Path: "google.golang.org/api/transport/grpc"}: true, {Name: "mypackagepb", Path: "github.com/googleapis/mypackage"}: true, {Name: "httptransport", Path: "google.golang.org/api/transport/http"}: true, + {Path: "log/slog"}: true, }, wantNumSnps: 1, }, @@ -469,9 +472,10 @@ func TestClientInit(t *testing.T) { {Name: "longrunningpb", Path: "cloud.google.com/go/longrunning/autogen/longrunningpb"}: true, {Name: "lroauto", Path: "cloud.google.com/go/longrunning/autogen"}: true, {Name: "mypackagepb", Path: "github.com/googleapis/mypackage"}: true, - {Path: "context"}: true, - {Path: "google.golang.org/api/option"}: true, - {Path: "google.golang.org/grpc"}: true, + {Path: "context"}: true, + {Path: "google.golang.org/api/option"}: true, + {Path: "google.golang.org/grpc"}: true, + {Path: "log/slog"}: true, }, wantNumSnps: 6, }, @@ -484,11 +488,12 @@ func TestClientInit(t *testing.T) { {Name: "gtransport", Path: "google.golang.org/api/transport/grpc"}: true, {Name: "httptransport", Path: "google.golang.org/api/transport/http"}: true, {Name: "mypackagepb", Path: "github.com/googleapis/mypackage"}: true, - {Path: "context"}: true, - {Path: "google.golang.org/api/option"}: true, - {Path: "google.golang.org/api/option/internaloption"}: true, - {Path: "google.golang.org/grpc"}: true, - {Path: "net/http"}: true, + {Path: "context"}: true, + {Path: "google.golang.org/api/option"}: true, + {Path: "google.golang.org/api/option/internaloption"}: true, + {Path: "google.golang.org/grpc"}: true, + {Path: "net/http"}: true, + {Path: "log/slog"}: true, }, wantNumSnps: 1, }, @@ -506,6 +511,7 @@ func TestClientInit(t *testing.T) { {Path: "net/http"}: true, {Name: "httptransport", Path: "google.golang.org/api/transport/http"}: true, {Name: "mypackagepb", Path: "github.com/googleapis/mypackage"}: true, + {Path: "log/slog"}: true, }, wantNumSnps: 1, setExt: func() (protoreflect.ExtensionType, interface{}) { diff --git a/internal/gengapic/doc_file.go b/internal/gengapic/doc_file.go index 0801e10c5..9a59b4f62 100644 --- a/internal/gengapic/doc_file.go +++ b/internal/gengapic/doc_file.go @@ -128,10 +128,19 @@ func (g *generator) genDocFile(year int, scopes []string, serv *descriptorpb.Ser p("import (") p("%s%q", "\t", "context") + p("%s%q", "\t", "io") + p("%s%q", "\t", "log/slog") + p("%s%q", "\t", "net/http") p("") + p("%s%q", "\t", "github.com/googleapis/gax-go/v2/internallog") + p("%s%q", "\t", "github.com/googleapis/gax-go/v2/internallog/grpclog") + p("%s%q", "\t", "google.golang.org/api/googleapi") p("%s%q", "\t", "google.golang.org/api/option") + p("%s%q", "\t", "google.golang.org/grpc") + p("%s%q", "\t", "google.golang.org/protobuf/proto") p(")") p("") + p("const serviceName = %q", g.serviceConfig.GetName()) p("// For more information on implementing a client constructor hook, see") p("// https://github.com/googleapis/google-cloud-go/wiki/Customizing-constructors.") @@ -157,6 +166,52 @@ func (g *generator) genDocFile(year int, scopes []string, serv *descriptorpb.Ser } p(" }") p("}") + p("") + + p("func executeHTTPRequest(ctx context.Context, client *http.Client, req *http.Request, logger *slog.Logger, body []byte, rpc string) ([]byte, error) {") + p(` logger.DebugContext(ctx, "api request", "serviceName", serviceName, "rpcName", rpc, "request", internallog.HTTPRequest(req, body))`) + p(" resp, err := client.Do(req)") + p(" if err != nil{") + p(" return nil, err") + p(" }") + p(" defer resp.Body.Close()") + p(" buf, err := io.ReadAll(resp.Body)") + p(" if err != nil {") + p(" return nil, err") + p(" }") + p(` logger.DebugContext(ctx, "api response", "serviceName", serviceName, "rpcName", rpc, "response", internallog.HTTPResponse(resp, buf))`) + p(" if err = googleapi.CheckResponse(resp); err != nil {") + p(" return nil, err") + p(" }") + p(" return buf, nil") + p("}") + p("") + + p("func executeStreamingHTTPRequest(ctx context.Context, client *http.Client, req *http.Request, logger *slog.Logger, body []byte, rpc string) (*http.Response, error) {") + p(` logger.DebugContext(ctx, "api request", "serviceName", serviceName, "rpcName", rpc, "request", internallog.HTTPRequest(req, body))`) + p(" resp, err := client.Do(req)") + p(" if err != nil{") + p(" return nil, err") + p(" }") + p(` logger.DebugContext(ctx, "api response", "serviceName", serviceName, "rpcName", rpc, "response", internallog.HTTPResponse(resp, nil))`) + p(" if err = googleapi.CheckResponse(resp); err != nil {") + p(" return nil, err") + p(" }") + p(" return resp, nil") + p("}") + p("") + + p("func executeRPC[I proto.Message, O proto.Message](ctx context.Context, fn func(context.Context, I, ...grpc.CallOption) (O, error), req I, opts []grpc.CallOption, logger *slog.Logger, rpc string) (O, error) {") + p(" var zero O") + p(` logger.DebugContext(ctx, "api request", "serviceName", serviceName, "rpcName", rpc, "request", grpclog.ProtoMessageRequest(ctx, req))`) + p(" resp, err := fn(ctx, req, opts...)") + p(" if err != nil {") + p(" return zero, err") + p(" }") + p(` logger.DebugContext(ctx, "api response", "serviceName", serviceName, "rpcName", rpc, "response", grpclog.ProtoMessageResponse(resp))`) + p(" return resp, err") + p("}") + p("") } func collectScopes(servs []*descriptorpb.ServiceDescriptorProto) []string { diff --git a/internal/gengapic/gengapic_test.go b/internal/gengapic/gengapic_test.go index 421a4ecb5..b765947b0 100644 --- a/internal/gengapic/gengapic_test.go +++ b/internal/gengapic/gengapic_test.go @@ -1127,12 +1127,12 @@ func TestGRPCStubCall(t *testing.T) { }{ { name: "foo.FooService.GetFoo", - want: "c.client.GetFoo(ctx, req, settings.GRPC...)", + want: `executeRPC(ctx, c.client.GetFoo, req, settings.GRPC, c.logger, "GetFoo")`, in: getFoo, }, { name: "foo.BarService.GetBar", - want: "c.barClient.GetBar(ctx, req, settings.GRPC...)", + want: `executeRPC(ctx, c.barClient.GetBar, req, settings.GRPC, c.logger, "GetBar")`, in: getBar, }, } { diff --git a/internal/gengapic/gengrpc.go b/internal/gengapic/gengrpc.go index 9857e11c2..51f167154 100644 --- a/internal/gengapic/gengrpc.go +++ b/internal/gengapic/gengrpc.go @@ -162,7 +162,7 @@ func (g *generator) emptyUnaryGRPCCall(servName string, m *descriptorpb.MethodDe func (g *generator) grpcStubCall(method *descriptorpb.MethodDescriptorProto) string { service := g.descInfo.ParentElement[method] stub := pbinfo.ReduceServName(service.GetName(), g.opts.pkgName) - return fmt.Sprintf("c.%s.%s(ctx, req, settings.GRPC...)", grpcClientField(stub), method.GetName()) + return fmt.Sprintf("executeRPC(ctx, c.%s.%s, req, settings.GRPC, c.logger, %q)", grpcClientField(stub), method.GetName(), method.GetName()) } func (g *generator) grpcClientOptions(serv *descriptorpb.ServiceDescriptorProto, servName string) error { @@ -295,6 +295,8 @@ func (g *generator) grpcClientInit(serv *descriptorpb.ServiceDescriptorProto, se p("// The x-goog-* metadata to be sent with each request.") p("xGoogHeaders []string") + p("") + p("logger *slog.Logger") p("}") p("") @@ -303,6 +305,7 @@ func (g *generator) grpcClientInit(serv *descriptorpb.ServiceDescriptorProto, se g.imports[imp] = true g.grpcClientUtilities(serv, servName, imp, hasRPCForLRO) + g.imports[pbinfo.ImportSpec{Path: "log/slog"}] = true } func (g *generator) grpcClientUtilities(serv *descriptorpb.ServiceDescriptorProto, servName string, imp pbinfo.ImportSpec, hasRPCForLRO bool) { @@ -337,6 +340,7 @@ func (g *generator) grpcClientUtilities(serv *descriptorpb.ServiceDescriptorProt p(" connPool: connPool,") p(" %s: %s.New%sClient(connPool),", grpcClientField(servName), imp.Name, serv.GetName()) p(" CallOptions: &client.CallOptions,") + p(" logger: internaloption.GetLogger(opts),") g.mixinStubsInit() p("") p(" }") diff --git a/internal/gengapic/genrest.go b/internal/gengapic/genrest.go index b9bf409b5..523092505 100644 --- a/internal/gengapic/genrest.go +++ b/internal/gengapic/genrest.go @@ -91,6 +91,8 @@ func (g *generator) restClientInit(serv *descriptorpb.ServiceDescriptorProto, se p("") p(" // Points back to the CallOptions field of the containing %sClient", servName) p(" CallOptions **%sCallOptions", servName) + p("") + p(" logger *slog.Logger") p("}") p("") g.restClientUtilities(serv, servName, hasRPCForLRO) @@ -98,6 +100,7 @@ func (g *generator) restClientInit(serv *descriptorpb.ServiceDescriptorProto, se g.imports[pbinfo.ImportSpec{Path: "net/http"}] = true g.imports[pbinfo.ImportSpec{Name: "httptransport", Path: "google.golang.org/api/transport/http"}] = true g.imports[pbinfo.ImportSpec{Path: "google.golang.org/api/option/internaloption"}] = true + g.imports[pbinfo.ImportSpec{Path: "log/slog"}] = true } func (g *generator) genRESTMethods(serv *descriptorpb.ServiceDescriptorProto, servName string) error { @@ -163,6 +166,7 @@ func (g *generator) restClientUtilities(serv *descriptorpb.ServiceDescriptorProt p(" endpoint: endpoint,") p(" httpClient: httpClient,") p(" CallOptions: &callOpts,") + p(" logger: internaloption.GetLogger(opts),") p(" }") p(" c.setGoogleClientInfo()") p("") @@ -617,7 +621,7 @@ func (g *generator) serverStreamRESTCall(servName string, s *descriptorpb.Servic // rest-client method p("func (c *%s) %s(ctx context.Context, req *%s.%s, opts ...gax.CallOption) (%s.%s_%sClient, error) {", lowcaseServName, m.GetName(), inSpec.Name, inType.GetName(), servSpec.Name, s.GetName(), m.GetName()) - body := "nil" + body, logBody := "nil", "nil" verb := strings.ToUpper(info.verb) // Marshal body for HTTP methods that take a body. @@ -638,6 +642,7 @@ func (g *generator) serverStreamRESTCall(servName string, s *descriptorpb.Servic p("") body = "bytes.NewReader(jsonReq)" + logBody = "jsonReq" g.imports[pbinfo.ImportSpec{Path: "bytes"}] = true g.imports[pbinfo.ImportSpec{Path: "google.golang.org/protobuf/encoding/protojson"}] = true } @@ -658,15 +663,11 @@ func (g *generator) serverStreamRESTCall(servName string, s *descriptorpb.Servic p(" httpReq = httpReq.WithContext(ctx)") p(" httpReq.Header = headers") p("") - p(" httpRsp, err := c.httpClient.Do(httpReq)") + p(" httpRsp, err := executeStreamingHTTPRequest(ctx, c.httpClient, httpReq, c.logger, %s, %q)", logBody, m.GetName()) p(" if err != nil{") p(" return err") p(" }") p("") - p(" if err = googleapi.CheckResponse(httpRsp); err != nil {") - p(" return err") - p(" }") - p("") p(" streamClient = &%s{", streamClient) p(" ctx: ctx,") p(" md: metadata.MD(httpRsp.Header),") @@ -679,8 +680,6 @@ func (g *generator) serverStreamRESTCall(servName string, s *descriptorpb.Servic p("}") p("") - g.imports[pbinfo.ImportSpec{Path: "google.golang.org/api/googleapi"}] = true - // server-stream wrapper client p("// %s is the stream client used to consume the server stream created by", streamClient) p("// the REST implementation of %s.", m.GetName()) @@ -800,10 +799,11 @@ func (g *generator) pagingRESTCall(servName string, m *descriptorpb.MethodDescri p("it := &%s{}", pt.iterTypeName) p("req = proto.Clone(req).(*%s.%s)", inSpec.Name, inType.GetName()) - maybeReqBytes := "nil" + maybeReqBytes, logBody := "nil", "nil" if info.body != "" { g.protoJSONMarshaler() maybeReqBytes = "bytes.NewReader(jsonReq)" + logBody = "jsonReq" g.imports[pbinfo.ImportSpec{Path: "bytes"}] = true } @@ -835,21 +835,10 @@ func (g *generator) pagingRESTCall(servName string, m *descriptorpb.MethodDescri // TODO: Should this http.Request use WithContext? p(" httpReq.Header = headers") p("") - p(" httpRsp, err := c.httpClient.Do(httpReq)") + p(" buf, err := executeHTTPRequest(ctx, c.httpClient, httpReq, c.logger, %s, %q)", logBody, m.GetName()) p(" if err != nil{") p(` return err`) p(" }") - p(" defer httpRsp.Body.Close()") - p("") - p(" if err = googleapi.CheckResponse(httpRsp); err != nil {") - p(` return err`) - p(" }") - p("") - p(" buf, err := io.ReadAll(httpRsp.Body)") - p(" if err != nil {") - p(` return err`) - p(" }") - p("") p(" if err := unm.Unmarshal(buf, resp); err != nil {") p(" return err") p(" }") @@ -868,10 +857,8 @@ func (g *generator) pagingRESTCall(servName string, m *descriptorpb.MethodDescri p("}") g.imports[pbinfo.ImportSpec{Path: "google.golang.org/api/iterator"}] = true - g.imports[pbinfo.ImportSpec{Path: "io"}] = true g.imports[pbinfo.ImportSpec{Path: "google.golang.org/protobuf/proto"}] = true g.imports[pbinfo.ImportSpec{Path: "google.golang.org/protobuf/encoding/protojson"}] = true - g.imports[pbinfo.ImportSpec{Path: "google.golang.org/api/googleapi"}] = true g.imports[inSpec] = true g.imports[outSpec] = true @@ -916,7 +903,7 @@ func (g *generator) lroRESTCall(servName string, m *descriptorpb.MethodDescripto // TODO(noahdietz): handle deadlines? // TODO(noahdietz): handle calloptions - body := "nil" + body, logBody := "nil", "nil" verb := strings.ToUpper(info.verb) // Marshal body for HTTP methods that take a body. @@ -937,6 +924,7 @@ func (g *generator) lroRESTCall(servName string, m *descriptorpb.MethodDescripto p("") body = "bytes.NewReader(jsonReq)" + logBody = "jsonReq" g.imports[pbinfo.ImportSpec{Path: "bytes"}] = true } @@ -957,21 +945,10 @@ func (g *generator) lroRESTCall(servName string, m *descriptorpb.MethodDescripto p(" httpReq = httpReq.WithContext(ctx)") p(" httpReq.Header = headers") p("") - p(" httpRsp, err := c.httpClient.Do(httpReq)") + p(" buf, err := executeHTTPRequest(ctx, c.httpClient, httpReq, c.logger, %s, %q)", logBody, m.GetName()) p(" if err != nil{") p(" return err") p(" }") - p(" defer httpRsp.Body.Close()") - p("") - p(" if err = googleapi.CheckResponse(httpRsp); err != nil {") - p(" return err") - p(" }") - p("") - p(" buf, err := io.ReadAll(httpRsp.Body)") - p(" if err != nil {") - p(" return err") - p(" }") - p("") p(" if err := unm.Unmarshal(buf, resp); err != nil {") p(" return err") p(" }") @@ -992,9 +969,7 @@ func (g *generator) lroRESTCall(servName string, m *descriptorpb.MethodDescripto p("") g.imports[pbinfo.ImportSpec{Path: "fmt"}] = true - g.imports[pbinfo.ImportSpec{Path: "io"}] = true g.imports[pbinfo.ImportSpec{Path: "cloud.google.com/go/longrunning"}] = true - g.imports[pbinfo.ImportSpec{Path: "google.golang.org/api/googleapi"}] = true g.imports[pbinfo.ImportSpec{Path: "google.golang.org/protobuf/encoding/protojson"}] = true return nil @@ -1023,7 +998,7 @@ func (g *generator) emptyUnaryRESTCall(servName string, m *descriptorpb.MethodDe // TODO(dovs): handle deadlines // TODO(dovs): handle call options - body := "nil" + body, logBody := "nil", "nil" verb := strings.ToUpper(info.verb) // Marshal body for HTTP methods that take a body. @@ -1044,6 +1019,7 @@ func (g *generator) emptyUnaryRESTCall(servName string, m *descriptorpb.MethodDe p("}") p("") body = "bytes.NewReader(jsonReq)" + logBody = "jsonReq" g.imports[pbinfo.ImportSpec{Path: "bytes"}] = true g.imports[pbinfo.ImportSpec{Path: "google.golang.org/protobuf/encoding/protojson"}] = true } @@ -1063,19 +1039,11 @@ func (g *generator) emptyUnaryRESTCall(servName string, m *descriptorpb.MethodDe p(" httpReq = httpReq.WithContext(ctx)") p(" httpReq.Header = headers") p("") - p(" httpRsp, err := c.httpClient.Do(httpReq)") - p(" if err != nil{") - p(" return err") - p(" }") - p(" defer httpRsp.Body.Close()") - p("") - p(" // Returns nil if there is no error, otherwise wraps") - p(" // the response code and body into a non-nil error") - p(" return googleapi.CheckResponse(httpRsp)") + p(" _, err = executeHTTPRequest(ctx, c.httpClient, httpReq, c.logger, %s, %q)", logBody, m.GetName()) + p(" return err") p(" }, opts...)") p("}") - g.imports[pbinfo.ImportSpec{Path: "google.golang.org/api/googleapi"}] = true g.imports[inSpec] = true return nil } @@ -1117,7 +1085,7 @@ func (g *generator) unaryRESTCall(servName string, m *descriptorpb.MethodDescrip // TODO(dovs): handle deadlines? // TODO(dovs): handle calloptions - body := "nil" + body, logBody := "nil", "nil" verb := strings.ToUpper(info.verb) // Marshal body for HTTP methods that take a body. @@ -1139,6 +1107,7 @@ func (g *generator) unaryRESTCall(servName string, m *descriptorpb.MethodDescrip p("") body = "bytes.NewReader(jsonReq)" + logBody = "jsonReq" g.imports[pbinfo.ImportSpec{Path: "bytes"}] = true g.imports[pbinfo.ImportSpec{Path: "google.golang.org/protobuf/encoding/protojson"}] = true @@ -1166,20 +1135,10 @@ func (g *generator) unaryRESTCall(servName string, m *descriptorpb.MethodDescrip p(" httpReq = httpReq.WithContext(ctx)") p(" httpReq.Header = headers") p("") - p(" httpRsp, err := c.httpClient.Do(httpReq)") + p(" buf, err := executeHTTPRequest(ctx, c.httpClient, httpReq, c.logger, %s, %q)", logBody, m.GetName()) p(" if err != nil{") p(" return err") p(" }") - p(" defer httpRsp.Body.Close()") - p("") - p(" if err = googleapi.CheckResponse(httpRsp); err != nil {") - p(" return err") - p(" }") - p("") - p(" buf, err := io.ReadAll(httpRsp.Body)") - p(" if err != nil {") - p(" return err") - p(" }") p("") if isHTTPBodyMessage { p("resp.Data = buf") @@ -1207,8 +1166,6 @@ func (g *generator) unaryRESTCall(servName string, m *descriptorpb.MethodDescrip p(ret) p("}") - g.imports[pbinfo.ImportSpec{Path: "io"}] = true - g.imports[pbinfo.ImportSpec{Path: "google.golang.org/api/googleapi"}] = true g.imports[inSpec] = true g.imports[outSpec] = true return nil diff --git a/internal/gengapic/genrest_test.go b/internal/gengapic/genrest_test.go index 08756941b..a86c3d952 100644 --- a/internal/gengapic/genrest_test.go +++ b/internal/gengapic/genrest_test.go @@ -756,10 +756,8 @@ func TestGenRestMethod(t *testing.T) { options: &options{diregapic: true}, imports: map[pbinfo.ImportSpec]bool{ {Path: "google.golang.org/protobuf/encoding/protojson"}: true, - {Path: "io"}: true, {Path: "net/url"}: true, {Path: "fmt"}: true, - {Path: "google.golang.org/api/googleapi"}: true, {Name: "foopb", Path: "google.golang.org/genproto/cloud/foo/v1"}: true, }, }, @@ -770,7 +768,6 @@ func TestGenRestMethod(t *testing.T) { imports: map[pbinfo.ImportSpec]bool{ {Path: "fmt"}: true, {Path: "github.com/google/uuid"}: true, - {Path: "google.golang.org/api/googleapi"}: true, {Path: "net/url"}: true, {Name: "foopb", Path: "google.golang.org/genproto/cloud/foo/v1"}: true, }, @@ -784,8 +781,6 @@ func TestGenRestMethod(t *testing.T) { {Path: "fmt"}: true, {Path: "github.com/google/uuid"}: true, {Path: "google.golang.org/protobuf/encoding/protojson"}: true, - {Path: "io"}: true, - {Path: "google.golang.org/api/googleapi"}: true, {Path: "net/url"}: true, {Path: "regexp"}: true, {Path: "strings"}: true, @@ -801,10 +796,8 @@ func TestGenRestMethod(t *testing.T) { {Path: "net/url"}: true, {Path: "google.golang.org/protobuf/encoding/protojson"}: true, {Path: "fmt"}: true, - {Path: "google.golang.org/api/googleapi"}: true, {Path: "google.golang.org/api/iterator"}: true, {Path: "google.golang.org/protobuf/proto"}: true, - {Path: "io"}: true, {Name: "foopb", Path: "google.golang.org/genproto/cloud/foo/v1"}: true, }, }, @@ -817,7 +810,6 @@ func TestGenRestMethod(t *testing.T) { {Path: "context"}: true, {Path: "errors"}: true, {Path: "fmt"}: true, - {Path: "google.golang.org/api/googleapi"}: true, {Path: "net/url"}: true, {Path: "regexp"}: true, {Path: "strings"}: true, @@ -844,9 +836,7 @@ func TestGenRestMethod(t *testing.T) { {Path: "bytes"}: true, {Path: "cloud.google.com/go/longrunning"}: true, {Path: "fmt"}: true, - {Path: "google.golang.org/api/googleapi"}: true, {Path: "google.golang.org/protobuf/encoding/protojson"}: true, - {Path: "io"}: true, {Path: "net/url"}: true, {Name: "longrunningpb", Path: "cloud.google.com/go/longrunning/autogen/longrunningpb"}: true, }, @@ -859,8 +849,6 @@ func TestGenRestMethod(t *testing.T) { {Path: "bytes"}: true, {Path: "fmt"}: true, {Path: "google.golang.org/protobuf/encoding/protojson"}: true, - {Path: "io"}: true, - {Path: "google.golang.org/api/googleapi"}: true, {Path: "net/url"}: true, {Path: "regexp"}: true, {Path: "strings"}: true, @@ -876,8 +864,6 @@ func TestGenRestMethod(t *testing.T) { {Path: "bytes"}: true, {Path: "fmt"}: true, {Path: "google.golang.org/protobuf/encoding/protojson"}: true, - {Path: "io"}: true, - {Path: "google.golang.org/api/googleapi"}: true, {Path: "net/url"}: true, {Name: "foopb", Path: "google.golang.org/genproto/cloud/foo/v1"}: true, }, diff --git a/internal/gengapic/stream.go b/internal/gengapic/stream.go index d8c710748..ad6e89775 100644 --- a/internal/gengapic/stream.go +++ b/internal/gengapic/stream.go @@ -43,7 +43,9 @@ func (g *generator) noRequestStreamCall(servName string, s *descriptorpb.Service p(" err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {") p(" var err error") + p(` c.logger.DebugContext(ctx, "api streaming client request", "serviceName", serviceName, "rpcName", %q)`, m.GetName()) p(" resp, err = c.%s.%s(ctx, settings.GRPC...)", grpcClientField(servName), m.GetName()) + p(` c.logger.DebugContext(ctx, "api streaming client response", "serviceName", serviceName, "rpcName", %q)`, m.GetName()) p(" return err") p(" }, opts...)") p(" if err != nil {") @@ -83,7 +85,9 @@ func (g *generator) serverStreamCall(servName string, s *descriptorpb.ServiceDes p(" var resp %s", retTyp) p("err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {") p(" var err error") + p(` c.logger.DebugContext(ctx, "api streaming client request", "serviceName", serviceName, "rpcName", %q)`, m.GetName()) p(" resp, err = %s", g.grpcStubCall(m)) + p(` c.logger.DebugContext(ctx, "api streaming client response", "serviceName", serviceName, "rpcName", %q)`, m.GetName()) p(" return err") p("}, opts...)") p("if err != nil {") diff --git a/internal/gengapic/testdata/custom_op_init.want b/internal/gengapic/testdata/custom_op_init.want index 5ab35d283..1a190c4d7 100644 --- a/internal/gengapic/testdata/custom_op_init.want +++ b/internal/gengapic/testdata/custom_op_init.want @@ -63,6 +63,8 @@ type restClient struct { // Points back to the CallOptions field of the containing Client CallOptions **CallOptions + + logger *slog.Logger } // NewRESTClient creates a new foo rest client. @@ -80,6 +82,7 @@ func NewRESTClient(ctx context.Context, opts ...option.ClientOption) (*Client, e endpoint: endpoint, httpClient: httpClient, CallOptions: &callOpts, + logger: internaloption.GetLogger(opts), } c.setGoogleClientInfo() diff --git a/internal/gengapic/testdata/deprecated_client_init.want b/internal/gengapic/testdata/deprecated_client_init.want index ff8f94001..c45be8d16 100644 --- a/internal/gengapic/testdata/deprecated_client_init.want +++ b/internal/gengapic/testdata/deprecated_client_init.want @@ -64,6 +64,8 @@ type gRPCClient struct { // The x-goog-* metadata to be sent with each request. xGoogHeaders []string + + logger *slog.Logger } // NewClient creates a new foo client based on gRPC. @@ -92,6 +94,7 @@ func NewClient(ctx context.Context, opts ...option.ClientOption) (*Client, error connPool: connPool, client: mypackagepb.NewFooClient(connPool), CallOptions: &client.CallOptions, + logger: internaloption.GetLogger(opts), } c.setGoogleClientInfo() @@ -139,6 +142,8 @@ type restClient struct { // Points back to the CallOptions field of the containing Client CallOptions **CallOptions + + logger *slog.Logger } // NewRESTClient creates a new foo rest client. @@ -158,6 +163,7 @@ func NewRESTClient(ctx context.Context, opts ...option.ClientOption) (*Client, e endpoint: endpoint, httpClient: httpClient, CallOptions: &callOpts, + logger: internaloption.GetLogger(opts), } c.setGoogleClientInfo() diff --git a/internal/gengapic/testdata/doc_file.want b/internal/gengapic/testdata/doc_file.want index 97fb47311..c5d237c2d 100644 --- a/internal/gengapic/testdata/doc_file.want +++ b/internal/gengapic/testdata/doc_file.want @@ -85,10 +85,19 @@ package secretmanager // import "cloud.google.com/go/secretmanager/apiv1" import ( "context" + "io" + "log/slog" + "net/http" + "github.com/googleapis/gax-go/v2/internallog" + "github.com/googleapis/gax-go/v2/internallog/grpclog" + "google.golang.org/api/googleapi" "google.golang.org/api/option" + "google.golang.org/grpc" + "google.golang.org/protobuf/proto" ) +const serviceName = "secretmanager.googleapis.com" // For more information on implementing a client constructor hook, see // https://github.com/googleapis/google-cloud-go/wiki/Customizing-constructors. type clientHookParams struct{} @@ -109,3 +118,46 @@ func DefaultAuthScopes() []string { "https://www.googleapis.com/auth/cloud-platform", } } + +func executeHTTPRequest(ctx context.Context, client *http.Client, req *http.Request, logger *slog.Logger, body []byte, rpc string) ([]byte, error) { + logger.DebugContext(ctx, "api request", "serviceName", serviceName, "rpcName", rpc, "request", internallog.HTTPRequest(req, body)) + resp, err := client.Do(req) + if err != nil{ + return nil, err + } + defer resp.Body.Close() + buf, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + logger.DebugContext(ctx, "api response", "serviceName", serviceName, "rpcName", rpc, "response", internallog.HTTPResponse(resp, buf)) + if err = googleapi.CheckResponse(resp); err != nil { + return nil, err + } + return buf, nil +} + +func executeStreamingHTTPRequest(ctx context.Context, client *http.Client, req *http.Request, logger *slog.Logger, body []byte, rpc string) (*http.Response, error) { + logger.DebugContext(ctx, "api request", "serviceName", serviceName, "rpcName", rpc, "request", internallog.HTTPRequest(req, body)) + resp, err := client.Do(req) + if err != nil{ + return nil, err + } + logger.DebugContext(ctx, "api response", "serviceName", serviceName, "rpcName", rpc, "response", internallog.HTTPResponse(resp, nil)) + if err = googleapi.CheckResponse(resp); err != nil { + return nil, err + } + return resp, nil +} + +func executeRPC[I proto.Message, O proto.Message](ctx context.Context, fn func(context.Context, I, ...grpc.CallOption) (O, error), req I, opts []grpc.CallOption, logger *slog.Logger, rpc string) (O, error) { + var zero O + logger.DebugContext(ctx, "api request", "serviceName", serviceName, "rpcName", rpc, "request", grpclog.ProtoMessageRequest(ctx, req)) + resp, err := fn(ctx, req, opts...) + if err != nil { + return zero, err + } + logger.DebugContext(ctx, "api response", "serviceName", serviceName, "rpcName", rpc, "response", grpclog.ProtoMessageResponse(resp)) + return resp, err +} + diff --git a/internal/gengapic/testdata/doc_file_alpha.want b/internal/gengapic/testdata/doc_file_alpha.want index 299de1d5a..3f896294a 100644 --- a/internal/gengapic/testdata/doc_file_alpha.want +++ b/internal/gengapic/testdata/doc_file_alpha.want @@ -87,10 +87,19 @@ package secretmanager // import "cloud.google.com/go/secretmanager/apiv1" import ( "context" + "io" + "log/slog" + "net/http" + "github.com/googleapis/gax-go/v2/internallog" + "github.com/googleapis/gax-go/v2/internallog/grpclog" + "google.golang.org/api/googleapi" "google.golang.org/api/option" + "google.golang.org/grpc" + "google.golang.org/protobuf/proto" ) +const serviceName = "secretmanager.googleapis.com" // For more information on implementing a client constructor hook, see // https://github.com/googleapis/google-cloud-go/wiki/Customizing-constructors. type clientHookParams struct{} @@ -111,3 +120,46 @@ func DefaultAuthScopes() []string { "https://www.googleapis.com/auth/cloud-platform", } } + +func executeHTTPRequest(ctx context.Context, client *http.Client, req *http.Request, logger *slog.Logger, body []byte, rpc string) ([]byte, error) { + logger.DebugContext(ctx, "api request", "serviceName", serviceName, "rpcName", rpc, "request", internallog.HTTPRequest(req, body)) + resp, err := client.Do(req) + if err != nil{ + return nil, err + } + defer resp.Body.Close() + buf, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + logger.DebugContext(ctx, "api response", "serviceName", serviceName, "rpcName", rpc, "response", internallog.HTTPResponse(resp, buf)) + if err = googleapi.CheckResponse(resp); err != nil { + return nil, err + } + return buf, nil +} + +func executeStreamingHTTPRequest(ctx context.Context, client *http.Client, req *http.Request, logger *slog.Logger, body []byte, rpc string) (*http.Response, error) { + logger.DebugContext(ctx, "api request", "serviceName", serviceName, "rpcName", rpc, "request", internallog.HTTPRequest(req, body)) + resp, err := client.Do(req) + if err != nil{ + return nil, err + } + logger.DebugContext(ctx, "api response", "serviceName", serviceName, "rpcName", rpc, "response", internallog.HTTPResponse(resp, nil)) + if err = googleapi.CheckResponse(resp); err != nil { + return nil, err + } + return resp, nil +} + +func executeRPC[I proto.Message, O proto.Message](ctx context.Context, fn func(context.Context, I, ...grpc.CallOption) (O, error), req I, opts []grpc.CallOption, logger *slog.Logger, rpc string) (O, error) { + var zero O + logger.DebugContext(ctx, "api request", "serviceName", serviceName, "rpcName", rpc, "request", grpclog.ProtoMessageRequest(ctx, req)) + resp, err := fn(ctx, req, opts...) + if err != nil { + return zero, err + } + logger.DebugContext(ctx, "api response", "serviceName", serviceName, "rpcName", rpc, "response", grpclog.ProtoMessageResponse(resp)) + return resp, err +} + diff --git a/internal/gengapic/testdata/doc_file_alpha_emptyservice.want b/internal/gengapic/testdata/doc_file_alpha_emptyservice.want index b8dcc676f..c1a44e679 100644 --- a/internal/gengapic/testdata/doc_file_alpha_emptyservice.want +++ b/internal/gengapic/testdata/doc_file_alpha_emptyservice.want @@ -71,10 +71,19 @@ package secretmanager // import "cloud.google.com/go/secretmanager/apiv1" import ( "context" + "io" + "log/slog" + "net/http" + "github.com/googleapis/gax-go/v2/internallog" + "github.com/googleapis/gax-go/v2/internallog/grpclog" + "google.golang.org/api/googleapi" "google.golang.org/api/option" + "google.golang.org/grpc" + "google.golang.org/protobuf/proto" ) +const serviceName = "secretmanager.googleapis.com" // For more information on implementing a client constructor hook, see // https://github.com/googleapis/google-cloud-go/wiki/Customizing-constructors. type clientHookParams struct{} @@ -95,3 +104,46 @@ func DefaultAuthScopes() []string { "https://www.googleapis.com/auth/cloud-platform", } } + +func executeHTTPRequest(ctx context.Context, client *http.Client, req *http.Request, logger *slog.Logger, body []byte, rpc string) ([]byte, error) { + logger.DebugContext(ctx, "api request", "serviceName", serviceName, "rpcName", rpc, "request", internallog.HTTPRequest(req, body)) + resp, err := client.Do(req) + if err != nil{ + return nil, err + } + defer resp.Body.Close() + buf, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + logger.DebugContext(ctx, "api response", "serviceName", serviceName, "rpcName", rpc, "response", internallog.HTTPResponse(resp, buf)) + if err = googleapi.CheckResponse(resp); err != nil { + return nil, err + } + return buf, nil +} + +func executeStreamingHTTPRequest(ctx context.Context, client *http.Client, req *http.Request, logger *slog.Logger, body []byte, rpc string) (*http.Response, error) { + logger.DebugContext(ctx, "api request", "serviceName", serviceName, "rpcName", rpc, "request", internallog.HTTPRequest(req, body)) + resp, err := client.Do(req) + if err != nil{ + return nil, err + } + logger.DebugContext(ctx, "api response", "serviceName", serviceName, "rpcName", rpc, "response", internallog.HTTPResponse(resp, nil)) + if err = googleapi.CheckResponse(resp); err != nil { + return nil, err + } + return resp, nil +} + +func executeRPC[I proto.Message, O proto.Message](ctx context.Context, fn func(context.Context, I, ...grpc.CallOption) (O, error), req I, opts []grpc.CallOption, logger *slog.Logger, rpc string) (O, error) { + var zero O + logger.DebugContext(ctx, "api request", "serviceName", serviceName, "rpcName", rpc, "request", grpclog.ProtoMessageRequest(ctx, req)) + resp, err := fn(ctx, req, opts...) + if err != nil { + return zero, err + } + logger.DebugContext(ctx, "api response", "serviceName", serviceName, "rpcName", rpc, "response", grpclog.ProtoMessageResponse(resp)) + return resp, err +} + diff --git a/internal/gengapic/testdata/doc_file_beta.want b/internal/gengapic/testdata/doc_file_beta.want index 018e88bec..ac0f7a72e 100644 --- a/internal/gengapic/testdata/doc_file_beta.want +++ b/internal/gengapic/testdata/doc_file_beta.want @@ -87,10 +87,19 @@ package secretmanager // import "cloud.google.com/go/secretmanager/apiv1" import ( "context" + "io" + "log/slog" + "net/http" + "github.com/googleapis/gax-go/v2/internallog" + "github.com/googleapis/gax-go/v2/internallog/grpclog" + "google.golang.org/api/googleapi" "google.golang.org/api/option" + "google.golang.org/grpc" + "google.golang.org/protobuf/proto" ) +const serviceName = "secretmanager.googleapis.com" // For more information on implementing a client constructor hook, see // https://github.com/googleapis/google-cloud-go/wiki/Customizing-constructors. type clientHookParams struct{} @@ -111,3 +120,46 @@ func DefaultAuthScopes() []string { "https://www.googleapis.com/auth/cloud-platform", } } + +func executeHTTPRequest(ctx context.Context, client *http.Client, req *http.Request, logger *slog.Logger, body []byte, rpc string) ([]byte, error) { + logger.DebugContext(ctx, "api request", "serviceName", serviceName, "rpcName", rpc, "request", internallog.HTTPRequest(req, body)) + resp, err := client.Do(req) + if err != nil{ + return nil, err + } + defer resp.Body.Close() + buf, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + logger.DebugContext(ctx, "api response", "serviceName", serviceName, "rpcName", rpc, "response", internallog.HTTPResponse(resp, buf)) + if err = googleapi.CheckResponse(resp); err != nil { + return nil, err + } + return buf, nil +} + +func executeStreamingHTTPRequest(ctx context.Context, client *http.Client, req *http.Request, logger *slog.Logger, body []byte, rpc string) (*http.Response, error) { + logger.DebugContext(ctx, "api request", "serviceName", serviceName, "rpcName", rpc, "request", internallog.HTTPRequest(req, body)) + resp, err := client.Do(req) + if err != nil{ + return nil, err + } + logger.DebugContext(ctx, "api response", "serviceName", serviceName, "rpcName", rpc, "response", internallog.HTTPResponse(resp, nil)) + if err = googleapi.CheckResponse(resp); err != nil { + return nil, err + } + return resp, nil +} + +func executeRPC[I proto.Message, O proto.Message](ctx context.Context, fn func(context.Context, I, ...grpc.CallOption) (O, error), req I, opts []grpc.CallOption, logger *slog.Logger, rpc string) (O, error) { + var zero O + logger.DebugContext(ctx, "api request", "serviceName", serviceName, "rpcName", rpc, "request", grpclog.ProtoMessageRequest(ctx, req)) + resp, err := fn(ctx, req, opts...) + if err != nil { + return zero, err + } + logger.DebugContext(ctx, "api response", "serviceName", serviceName, "rpcName", rpc, "response", grpclog.ProtoMessageResponse(resp)) + return resp, err +} + diff --git a/internal/gengapic/testdata/doc_file_beta_emptyservice.want b/internal/gengapic/testdata/doc_file_beta_emptyservice.want index 6ea0ad6a8..ff637b652 100644 --- a/internal/gengapic/testdata/doc_file_beta_emptyservice.want +++ b/internal/gengapic/testdata/doc_file_beta_emptyservice.want @@ -71,10 +71,19 @@ package secretmanager // import "cloud.google.com/go/secretmanager/apiv1" import ( "context" + "io" + "log/slog" + "net/http" + "github.com/googleapis/gax-go/v2/internallog" + "github.com/googleapis/gax-go/v2/internallog/grpclog" + "google.golang.org/api/googleapi" "google.golang.org/api/option" + "google.golang.org/grpc" + "google.golang.org/protobuf/proto" ) +const serviceName = "secretmanager.googleapis.com" // For more information on implementing a client constructor hook, see // https://github.com/googleapis/google-cloud-go/wiki/Customizing-constructors. type clientHookParams struct{} @@ -95,3 +104,46 @@ func DefaultAuthScopes() []string { "https://www.googleapis.com/auth/cloud-platform", } } + +func executeHTTPRequest(ctx context.Context, client *http.Client, req *http.Request, logger *slog.Logger, body []byte, rpc string) ([]byte, error) { + logger.DebugContext(ctx, "api request", "serviceName", serviceName, "rpcName", rpc, "request", internallog.HTTPRequest(req, body)) + resp, err := client.Do(req) + if err != nil{ + return nil, err + } + defer resp.Body.Close() + buf, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + logger.DebugContext(ctx, "api response", "serviceName", serviceName, "rpcName", rpc, "response", internallog.HTTPResponse(resp, buf)) + if err = googleapi.CheckResponse(resp); err != nil { + return nil, err + } + return buf, nil +} + +func executeStreamingHTTPRequest(ctx context.Context, client *http.Client, req *http.Request, logger *slog.Logger, body []byte, rpc string) (*http.Response, error) { + logger.DebugContext(ctx, "api request", "serviceName", serviceName, "rpcName", rpc, "request", internallog.HTTPRequest(req, body)) + resp, err := client.Do(req) + if err != nil{ + return nil, err + } + logger.DebugContext(ctx, "api response", "serviceName", serviceName, "rpcName", rpc, "response", internallog.HTTPResponse(resp, nil)) + if err = googleapi.CheckResponse(resp); err != nil { + return nil, err + } + return resp, nil +} + +func executeRPC[I proto.Message, O proto.Message](ctx context.Context, fn func(context.Context, I, ...grpc.CallOption) (O, error), req I, opts []grpc.CallOption, logger *slog.Logger, rpc string) (O, error) { + var zero O + logger.DebugContext(ctx, "api request", "serviceName", serviceName, "rpcName", rpc, "request", grpclog.ProtoMessageRequest(ctx, req)) + resp, err := fn(ctx, req, opts...) + if err != nil { + return zero, err + } + logger.DebugContext(ctx, "api response", "serviceName", serviceName, "rpcName", rpc, "response", grpclog.ProtoMessageResponse(resp)) + return resp, err +} + diff --git a/internal/gengapic/testdata/doc_file_deprecated_emptyservice.want b/internal/gengapic/testdata/doc_file_deprecated_emptyservice.want index 4ab01384e..f99f2f02e 100644 --- a/internal/gengapic/testdata/doc_file_deprecated_emptyservice.want +++ b/internal/gengapic/testdata/doc_file_deprecated_emptyservice.want @@ -71,10 +71,19 @@ package secretmanager // import "cloud.google.com/go/secretmanager/apiv1" import ( "context" + "io" + "log/slog" + "net/http" + "github.com/googleapis/gax-go/v2/internallog" + "github.com/googleapis/gax-go/v2/internallog/grpclog" + "google.golang.org/api/googleapi" "google.golang.org/api/option" + "google.golang.org/grpc" + "google.golang.org/protobuf/proto" ) +const serviceName = "secretmanager.googleapis.com" // For more information on implementing a client constructor hook, see // https://github.com/googleapis/google-cloud-go/wiki/Customizing-constructors. type clientHookParams struct{} @@ -95,3 +104,46 @@ func DefaultAuthScopes() []string { "https://www.googleapis.com/auth/cloud-platform", } } + +func executeHTTPRequest(ctx context.Context, client *http.Client, req *http.Request, logger *slog.Logger, body []byte, rpc string) ([]byte, error) { + logger.DebugContext(ctx, "api request", "serviceName", serviceName, "rpcName", rpc, "request", internallog.HTTPRequest(req, body)) + resp, err := client.Do(req) + if err != nil{ + return nil, err + } + defer resp.Body.Close() + buf, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + logger.DebugContext(ctx, "api response", "serviceName", serviceName, "rpcName", rpc, "response", internallog.HTTPResponse(resp, buf)) + if err = googleapi.CheckResponse(resp); err != nil { + return nil, err + } + return buf, nil +} + +func executeStreamingHTTPRequest(ctx context.Context, client *http.Client, req *http.Request, logger *slog.Logger, body []byte, rpc string) (*http.Response, error) { + logger.DebugContext(ctx, "api request", "serviceName", serviceName, "rpcName", rpc, "request", internallog.HTTPRequest(req, body)) + resp, err := client.Do(req) + if err != nil{ + return nil, err + } + logger.DebugContext(ctx, "api response", "serviceName", serviceName, "rpcName", rpc, "response", internallog.HTTPResponse(resp, nil)) + if err = googleapi.CheckResponse(resp); err != nil { + return nil, err + } + return resp, nil +} + +func executeRPC[I proto.Message, O proto.Message](ctx context.Context, fn func(context.Context, I, ...grpc.CallOption) (O, error), req I, opts []grpc.CallOption, logger *slog.Logger, rpc string) (O, error) { + var zero O + logger.DebugContext(ctx, "api request", "serviceName", serviceName, "rpcName", rpc, "request", grpclog.ProtoMessageRequest(ctx, req)) + resp, err := fn(ctx, req, opts...) + if err != nil { + return zero, err + } + logger.DebugContext(ctx, "api response", "serviceName", serviceName, "rpcName", rpc, "response", grpclog.ProtoMessageResponse(resp)) + return resp, err +} + diff --git a/internal/gengapic/testdata/doc_file_emptyservice.want b/internal/gengapic/testdata/doc_file_emptyservice.want index 9a68f0aa9..30497f188 100644 --- a/internal/gengapic/testdata/doc_file_emptyservice.want +++ b/internal/gengapic/testdata/doc_file_emptyservice.want @@ -69,10 +69,19 @@ package secretmanager // import "cloud.google.com/go/secretmanager/apiv1" import ( "context" + "io" + "log/slog" + "net/http" + "github.com/googleapis/gax-go/v2/internallog" + "github.com/googleapis/gax-go/v2/internallog/grpclog" + "google.golang.org/api/googleapi" "google.golang.org/api/option" + "google.golang.org/grpc" + "google.golang.org/protobuf/proto" ) +const serviceName = "secretmanager.googleapis.com" // For more information on implementing a client constructor hook, see // https://github.com/googleapis/google-cloud-go/wiki/Customizing-constructors. type clientHookParams struct{} @@ -93,3 +102,46 @@ func DefaultAuthScopes() []string { "https://www.googleapis.com/auth/cloud-platform", } } + +func executeHTTPRequest(ctx context.Context, client *http.Client, req *http.Request, logger *slog.Logger, body []byte, rpc string) ([]byte, error) { + logger.DebugContext(ctx, "api request", "serviceName", serviceName, "rpcName", rpc, "request", internallog.HTTPRequest(req, body)) + resp, err := client.Do(req) + if err != nil{ + return nil, err + } + defer resp.Body.Close() + buf, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + logger.DebugContext(ctx, "api response", "serviceName", serviceName, "rpcName", rpc, "response", internallog.HTTPResponse(resp, buf)) + if err = googleapi.CheckResponse(resp); err != nil { + return nil, err + } + return buf, nil +} + +func executeStreamingHTTPRequest(ctx context.Context, client *http.Client, req *http.Request, logger *slog.Logger, body []byte, rpc string) (*http.Response, error) { + logger.DebugContext(ctx, "api request", "serviceName", serviceName, "rpcName", rpc, "request", internallog.HTTPRequest(req, body)) + resp, err := client.Do(req) + if err != nil{ + return nil, err + } + logger.DebugContext(ctx, "api response", "serviceName", serviceName, "rpcName", rpc, "response", internallog.HTTPResponse(resp, nil)) + if err = googleapi.CheckResponse(resp); err != nil { + return nil, err + } + return resp, nil +} + +func executeRPC[I proto.Message, O proto.Message](ctx context.Context, fn func(context.Context, I, ...grpc.CallOption) (O, error), req I, opts []grpc.CallOption, logger *slog.Logger, rpc string) (O, error) { + var zero O + logger.DebugContext(ctx, "api request", "serviceName", serviceName, "rpcName", rpc, "request", grpclog.ProtoMessageRequest(ctx, req)) + resp, err := fn(ctx, req, opts...) + if err != nil { + return zero, err + } + logger.DebugContext(ctx, "api response", "serviceName", serviceName, "rpcName", rpc, "response", grpclog.ProtoMessageResponse(resp)) + return resp, err +} + diff --git a/internal/gengapic/testdata/empty_client_init.want b/internal/gengapic/testdata/empty_client_init.want index 1bafa2553..1fec834fc 100644 --- a/internal/gengapic/testdata/empty_client_init.want +++ b/internal/gengapic/testdata/empty_client_init.want @@ -62,6 +62,8 @@ type gRPCClient struct { // The x-goog-* metadata to be sent with each request. xGoogHeaders []string + + logger *slog.Logger } // NewClient creates a new foo client based on gRPC. @@ -88,6 +90,7 @@ func NewClient(ctx context.Context, opts ...option.ClientOption) (*Client, error connPool: connPool, client: mypackagepb.NewFooClient(connPool), CallOptions: &client.CallOptions, + logger: internaloption.GetLogger(opts), } c.setGoogleClientInfo() @@ -136,6 +139,8 @@ type restClient struct { // Points back to the CallOptions field of the containing Client CallOptions **CallOptions + + logger *slog.Logger } // NewRESTClient creates a new foo rest client. @@ -153,6 +158,7 @@ func NewRESTClient(ctx context.Context, opts ...option.ClientOption) (*Client, e endpoint: endpoint, httpClient: httpClient, CallOptions: &callOpts, + logger: internaloption.GetLogger(opts), } c.setGoogleClientInfo() diff --git a/internal/gengapic/testdata/foo_client_init.want b/internal/gengapic/testdata/foo_client_init.want index e32c9eaa0..9a5f5e778 100644 --- a/internal/gengapic/testdata/foo_client_init.want +++ b/internal/gengapic/testdata/foo_client_init.want @@ -91,6 +91,8 @@ type fooGRPCClient struct { // The x-goog-* metadata to be sent with each request. xGoogHeaders []string + + logger *slog.Logger } // NewFooClient creates a new foo client based on gRPC. @@ -117,6 +119,7 @@ func NewFooClient(ctx context.Context, opts ...option.ClientOption) (*FooClient, connPool: connPool, fooClient: mypackagepb.NewFooClient(connPool), CallOptions: &client.CallOptions, + logger: internaloption.GetLogger(opts), iamPolicyClient: iampb.NewIAMPolicyClient(connPool), locationsClient: locationpb.NewLocationsClient(connPool), diff --git a/internal/gengapic/testdata/foo_rest_client_init.want b/internal/gengapic/testdata/foo_rest_client_init.want index c24c9ef7a..e69085c90 100644 --- a/internal/gengapic/testdata/foo_rest_client_init.want +++ b/internal/gengapic/testdata/foo_rest_client_init.want @@ -85,6 +85,8 @@ type fooRESTClient struct { // Points back to the CallOptions field of the containing FooClient CallOptions **FooCallOptions + + logger *slog.Logger } // NewFooRESTClient creates a new foo rest client. @@ -102,6 +104,7 @@ func NewFooRESTClient(ctx context.Context, opts ...option.ClientOption) (*FooCli endpoint: endpoint, httpClient: httpClient, CallOptions: &callOpts, + logger: internaloption.GetLogger(opts), } c.setGoogleClientInfo() diff --git a/internal/gengapic/testdata/lro_client_init.want b/internal/gengapic/testdata/lro_client_init.want index feb4dc2c9..619eae0e9 100644 --- a/internal/gengapic/testdata/lro_client_init.want +++ b/internal/gengapic/testdata/lro_client_init.want @@ -106,6 +106,8 @@ type fooGRPCClient struct { // The x-goog-* metadata to be sent with each request. xGoogHeaders []string + + logger *slog.Logger } // NewFooClient creates a new foo client based on gRPC. @@ -132,6 +134,7 @@ func NewFooClient(ctx context.Context, opts ...option.ClientOption) (*FooClient, connPool: connPool, fooClient: mypackagepb.NewFooClient(connPool), CallOptions: &client.CallOptions, + logger: internaloption.GetLogger(opts), operationsClient: longrunningpb.NewOperationsClient(connPool), } diff --git a/internal/gengapic/testdata/method_BidiThings.want b/internal/gengapic/testdata/method_BidiThings.want index 7335ad111..de93bbef0 100644 --- a/internal/gengapic/testdata/method_BidiThings.want +++ b/internal/gengapic/testdata/method_BidiThings.want @@ -4,7 +4,9 @@ func (c *fooGRPCClient) BidiThings(ctx context.Context, opts ...gax.CallOption) opts = append((*c.CallOptions).BidiThings[0:len((*c.CallOptions).BidiThings):len((*c.CallOptions).BidiThings)], opts...) err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { var err error + c.logger.DebugContext(ctx, "api streaming client request", "serviceName", serviceName, "rpcName", "BidiThings") resp, err = c.fooClient.BidiThings(ctx, settings.GRPC...) + c.logger.DebugContext(ctx, "api streaming client response", "serviceName", serviceName, "rpcName", "BidiThings") return err }, opts...) if err != nil { diff --git a/internal/gengapic/testdata/method_ClientThings.want b/internal/gengapic/testdata/method_ClientThings.want index 51a3d2a9d..ac016d4cd 100644 --- a/internal/gengapic/testdata/method_ClientThings.want +++ b/internal/gengapic/testdata/method_ClientThings.want @@ -4,7 +4,9 @@ func (c *fooGRPCClient) ClientThings(ctx context.Context, opts ...gax.CallOption opts = append((*c.CallOptions).ClientThings[0:len((*c.CallOptions).ClientThings):len((*c.CallOptions).ClientThings)], opts...) err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { var err error + c.logger.DebugContext(ctx, "api streaming client request", "serviceName", serviceName, "rpcName", "ClientThings") resp, err = c.fooClient.ClientThings(ctx, settings.GRPC...) + c.logger.DebugContext(ctx, "api streaming client response", "serviceName", serviceName, "rpcName", "ClientThings") return err }, opts...) if err != nil { diff --git a/internal/gengapic/testdata/method_EmptyLRO.want b/internal/gengapic/testdata/method_EmptyLRO.want index e07753269..bf16102a6 100644 --- a/internal/gengapic/testdata/method_EmptyLRO.want +++ b/internal/gengapic/testdata/method_EmptyLRO.want @@ -4,7 +4,7 @@ func (c *fooGRPCClient) EmptyLRO(ctx context.Context, req *mypackagepb.InputType var resp *longrunningpb.Operation err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { var err error - resp, err = c.fooClient.EmptyLRO(ctx, req, settings.GRPC...) + resp, err = executeRPC(ctx, c.fooClient.EmptyLRO, req, settings.GRPC, c.logger, "EmptyLRO") return err }, opts...) if err != nil { diff --git a/internal/gengapic/testdata/method_GetAnotherThing.want b/internal/gengapic/testdata/method_GetAnotherThing.want index c51f14b55..1849b0278 100644 --- a/internal/gengapic/testdata/method_GetAnotherThing.want +++ b/internal/gengapic/testdata/method_GetAnotherThing.want @@ -34,7 +34,7 @@ func (c *fooGRPCClient) GetAnotherThing(ctx context.Context, req *mypackagepb.In var resp *mypackagepb.OutputType err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { var err error - resp, err = c.fooClient.GetAnotherThing(ctx, req, settings.GRPC...) + resp, err = executeRPC(ctx, c.fooClient.GetAnotherThing, req, settings.GRPC, c.logger, "GetAnotherThing") return err }, opts...) if err != nil { diff --git a/internal/gengapic/testdata/method_GetEmptyThing.want b/internal/gengapic/testdata/method_GetEmptyThing.want index eaf6c423f..ee58f16f8 100644 --- a/internal/gengapic/testdata/method_GetEmptyThing.want +++ b/internal/gengapic/testdata/method_GetEmptyThing.want @@ -9,7 +9,7 @@ func (c *fooGRPCClient) GetEmptyThing(ctx context.Context, req *mypackagepb.Inpu opts = append((*c.CallOptions).GetEmptyThing[0:len((*c.CallOptions).GetEmptyThing):len((*c.CallOptions).GetEmptyThing)], opts...) err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { var err error - _, err = c.fooClient.GetEmptyThing(ctx, req, settings.GRPC...) + _, err = executeRPC(ctx, c.fooClient.GetEmptyThing, req, settings.GRPC, c.logger, "GetEmptyThing") return err }, opts...) return err diff --git a/internal/gengapic/testdata/method_GetManyOtherThings.want b/internal/gengapic/testdata/method_GetManyOtherThings.want index 0c402bd86..fd80fe5f8 100644 --- a/internal/gengapic/testdata/method_GetManyOtherThings.want +++ b/internal/gengapic/testdata/method_GetManyOtherThings.want @@ -15,7 +15,7 @@ func (c *fooGRPCClient) GetManyOtherThings(ctx context.Context, req *mypackagepb } err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { var err error - resp, err = c.fooClient.GetManyOtherThings(ctx, req, settings.GRPC...) + resp, err = executeRPC(ctx, c.fooClient.GetManyOtherThings, req, settings.GRPC, c.logger, "GetManyOtherThings") return err }, opts...) if err != nil { diff --git a/internal/gengapic/testdata/method_GetManyThings.want b/internal/gengapic/testdata/method_GetManyThings.want index 776da673b..1e8581ef4 100644 --- a/internal/gengapic/testdata/method_GetManyThings.want +++ b/internal/gengapic/testdata/method_GetManyThings.want @@ -18,7 +18,7 @@ func (c *fooGRPCClient) GetManyThings(ctx context.Context, req *mypackagepb.Page } err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { var err error - resp, err = c.fooClient.GetManyThings(ctx, req, settings.GRPC...) + resp, err = executeRPC(ctx, c.fooClient.GetManyThings, req, settings.GRPC, c.logger, "GetManyThings") return err }, opts...) if err != nil { diff --git a/internal/gengapic/testdata/method_GetManyThingsOptional.want b/internal/gengapic/testdata/method_GetManyThingsOptional.want index 7d2761f76..5400460de 100644 --- a/internal/gengapic/testdata/method_GetManyThingsOptional.want +++ b/internal/gengapic/testdata/method_GetManyThingsOptional.want @@ -18,7 +18,7 @@ func (c *fooGRPCClient) GetManyThingsOptional(ctx context.Context, req *mypackag } err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { var err error - resp, err = c.fooClient.GetManyThingsOptional(ctx, req, settings.GRPC...) + resp, err = executeRPC(ctx, c.fooClient.GetManyThingsOptional, req, settings.GRPC, c.logger, "GetManyThingsOptional") return err }, opts...) if err != nil { diff --git a/internal/gengapic/testdata/method_GetOneThing.want b/internal/gengapic/testdata/method_GetOneThing.want index 54ba5d7ea..2bf6a039e 100644 --- a/internal/gengapic/testdata/method_GetOneThing.want +++ b/internal/gengapic/testdata/method_GetOneThing.want @@ -13,7 +13,7 @@ func (c *fooGRPCClient) GetOneThing(ctx context.Context, req *mypackagepb.InputT var resp *mypackagepb.OutputType err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { var err error - resp, err = c.fooClient.GetOneThing(ctx, req, settings.GRPC...) + resp, err = executeRPC(ctx, c.fooClient.GetOneThing, req, settings.GRPC, c.logger, "GetOneThing") return err }, opts...) if err != nil { diff --git a/internal/gengapic/testdata/method_RespLRO.want b/internal/gengapic/testdata/method_RespLRO.want index 558c52386..8ca199efe 100644 --- a/internal/gengapic/testdata/method_RespLRO.want +++ b/internal/gengapic/testdata/method_RespLRO.want @@ -4,7 +4,7 @@ func (c *fooGRPCClient) RespLRO(ctx context.Context, req *mypackagepb.InputType, var resp *longrunningpb.Operation err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { var err error - resp, err = c.fooClient.RespLRO(ctx, req, settings.GRPC...) + resp, err = executeRPC(ctx, c.fooClient.RespLRO, req, settings.GRPC, c.logger, "RespLRO") return err }, opts...) if err != nil { diff --git a/internal/gengapic/testdata/method_ServerThings.want b/internal/gengapic/testdata/method_ServerThings.want index 75514616b..4a50bf9bb 100644 --- a/internal/gengapic/testdata/method_ServerThings.want +++ b/internal/gengapic/testdata/method_ServerThings.want @@ -7,7 +7,9 @@ func (c *fooGRPCClient) ServerThings(ctx context.Context, req *mypackagepb.Input var resp mypackagepb.Foo_ServerThingsClient err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { var err error - resp, err = c.fooClient.ServerThings(ctx, req, settings.GRPC...) + c.logger.DebugContext(ctx, "api streaming client request", "serviceName", serviceName, "rpcName", "ServerThings") + resp, err = executeRPC(ctx, c.fooClient.ServerThings, req, settings.GRPC, c.logger, "ServerThings") + c.logger.DebugContext(ctx, "api streaming client response", "serviceName", serviceName, "rpcName", "ServerThings") return err }, opts...) if err != nil { diff --git a/internal/gengapic/testdata/rest_CustomOp.want b/internal/gengapic/testdata/rest_CustomOp.want index faa858f72..47e7439bb 100644 --- a/internal/gengapic/testdata/rest_CustomOp.want +++ b/internal/gengapic/testdata/rest_CustomOp.want @@ -33,20 +33,10 @@ func (c *fooRESTClient) CustomOp(ctx context.Context, req *foopb.Foo, opts ...ga httpReq = httpReq.WithContext(ctx) httpReq.Header = headers - httpRsp, err := c.httpClient.Do(httpReq) + buf, err := executeHTTPRequest(ctx, c.httpClient, httpReq, c.logger, nil, "CustomOp") if err != nil{ return err } - defer httpRsp.Body.Close() - - if err = googleapi.CheckResponse(httpRsp); err != nil { - return err - } - - buf, err := io.ReadAll(httpRsp.Body) - if err != nil { - return err - } if err := unm.Unmarshal(buf, resp); err != nil { return err diff --git a/internal/gengapic/testdata/rest_EmptyRPC.want b/internal/gengapic/testdata/rest_EmptyRPC.want index 1cdcc023b..1ca911d4d 100644 --- a/internal/gengapic/testdata/rest_EmptyRPC.want +++ b/internal/gengapic/testdata/rest_EmptyRPC.want @@ -33,14 +33,7 @@ func (c *fooRESTClient) EmptyRPC(ctx context.Context, req *foopb.Foo, opts ...ga httpReq = httpReq.WithContext(ctx) httpReq.Header = headers - httpRsp, err := c.httpClient.Do(httpReq) - if err != nil{ - return err - } - defer httpRsp.Body.Close() - - // Returns nil if there is no error, otherwise wraps - // the response code and body into a non-nil error - return googleapi.CheckResponse(httpRsp) + _, err = executeHTTPRequest(ctx, c.httpClient, httpReq, c.logger, nil, "EmptyRPC") + return err }, opts...) } diff --git a/internal/gengapic/testdata/rest_HttpBodyRPC.want b/internal/gengapic/testdata/rest_HttpBodyRPC.want index 1e9e696fc..1d7d5d5ee 100644 --- a/internal/gengapic/testdata/rest_HttpBodyRPC.want +++ b/internal/gengapic/testdata/rest_HttpBodyRPC.want @@ -39,20 +39,10 @@ func (c *fooRESTClient) HttpBodyRPC(ctx context.Context, req *foopb.Foo, opts .. httpReq = httpReq.WithContext(ctx) httpReq.Header = headers - httpRsp, err := c.httpClient.Do(httpReq) + buf, err := executeHTTPRequest(ctx, c.httpClient, httpReq, c.logger, jsonReq, "HttpBodyRPC") if err != nil{ return err } - defer httpRsp.Body.Close() - - if err = googleapi.CheckResponse(httpRsp); err != nil { - return err - } - - buf, err := io.ReadAll(httpRsp.Body) - if err != nil { - return err - } resp.Data = buf if headers := httpRsp.Header; len(headers["Content-Type"]) > 0 { diff --git a/internal/gengapic/testdata/rest_LongrunningRPC.want b/internal/gengapic/testdata/rest_LongrunningRPC.want index 504a89a79..3cd9638b2 100644 --- a/internal/gengapic/testdata/rest_LongrunningRPC.want +++ b/internal/gengapic/testdata/rest_LongrunningRPC.want @@ -27,21 +27,10 @@ func (c *fooRESTClient) LongrunningRPC(ctx context.Context, req *foopb.Foo, opts httpReq = httpReq.WithContext(ctx) httpReq.Header = headers - httpRsp, err := c.httpClient.Do(httpReq) + buf, err := executeHTTPRequest(ctx, c.httpClient, httpReq, c.logger, jsonReq, "LongrunningRPC") if err != nil{ return err } - defer httpRsp.Body.Close() - - if err = googleapi.CheckResponse(httpRsp); err != nil { - return err - } - - buf, err := io.ReadAll(httpRsp.Body) - if err != nil { - return err - } - if err := unm.Unmarshal(buf, resp); err != nil { return err } diff --git a/internal/gengapic/testdata/rest_PagingRPC.want b/internal/gengapic/testdata/rest_PagingRPC.want index f1d4e3beb..c60231f0f 100644 --- a/internal/gengapic/testdata/rest_PagingRPC.want +++ b/internal/gengapic/testdata/rest_PagingRPC.want @@ -41,21 +41,10 @@ func (c *fooRESTClient) PagingRPC(ctx context.Context, req *foopb.PagedFooReques } httpReq.Header = headers - httpRsp, err := c.httpClient.Do(httpReq) + buf, err := executeHTTPRequest(ctx, c.httpClient, httpReq, c.logger, nil, "PagingRPC") if err != nil{ return err } - defer httpRsp.Body.Close() - - if err = googleapi.CheckResponse(httpRsp); err != nil { - return err - } - - buf, err := io.ReadAll(httpRsp.Body) - if err != nil { - return err - } - if err := unm.Unmarshal(buf, resp); err != nil { return err } diff --git a/internal/gengapic/testdata/rest_ServerStreamRPC.want b/internal/gengapic/testdata/rest_ServerStreamRPC.want index 0511d21fb..a303fd8da 100644 --- a/internal/gengapic/testdata/rest_ServerStreamRPC.want +++ b/internal/gengapic/testdata/rest_ServerStreamRPC.want @@ -38,15 +38,11 @@ func (c *fooRESTClient) ServerStreamRPC(ctx context.Context, req *foopb.Foo, opt httpReq = httpReq.WithContext(ctx) httpReq.Header = headers - httpRsp, err := c.httpClient.Do(httpReq) + httpRsp, err := executeStreamingHTTPRequest(ctx, c.httpClient, httpReq, c.logger, jsonReq, "ServerStreamRPC") if err != nil{ return err } - if err = googleapi.CheckResponse(httpRsp); err != nil { - return err - } - streamClient = &serverStreamRPCRESTClient{ ctx: ctx, md: metadata.MD(httpRsp.Header), diff --git a/internal/gengapic/testdata/rest_UnaryRPC.want b/internal/gengapic/testdata/rest_UnaryRPC.want index 473af58df..dbf6297b6 100644 --- a/internal/gengapic/testdata/rest_UnaryRPC.want +++ b/internal/gengapic/testdata/rest_UnaryRPC.want @@ -48,20 +48,10 @@ func (c *fooRESTClient) UnaryRPC(ctx context.Context, req *foopb.Foo, opts ...ga httpReq = httpReq.WithContext(ctx) httpReq.Header = headers - httpRsp, err := c.httpClient.Do(httpReq) + buf, err := executeHTTPRequest(ctx, c.httpClient, httpReq, c.logger, jsonReq, "UnaryRPC") if err != nil{ return err } - defer httpRsp.Body.Close() - - if err = googleapi.CheckResponse(httpRsp); err != nil { - return err - } - - buf, err := io.ReadAll(httpRsp.Body) - if err != nil { - return err - } if err := unm.Unmarshal(buf, resp); err != nil { return err diff --git a/internal/gengapic/testdata/rest_UpdateRPC.want b/internal/gengapic/testdata/rest_UpdateRPC.want index 2c156d773..694ec10b7 100644 --- a/internal/gengapic/testdata/rest_UpdateRPC.want +++ b/internal/gengapic/testdata/rest_UpdateRPC.want @@ -53,20 +53,10 @@ func (c *fooRESTClient) UpdateRPC(ctx context.Context, req *foopb.UpdateRequest, httpReq = httpReq.WithContext(ctx) httpReq.Header = headers - httpRsp, err := c.httpClient.Do(httpReq) + buf, err := executeHTTPRequest(ctx, c.httpClient, httpReq, c.logger, jsonReq, "UpdateRPC") if err != nil{ return err } - defer httpRsp.Body.Close() - - if err = googleapi.CheckResponse(httpRsp); err != nil { - return err - } - - buf, err := io.ReadAll(httpRsp.Body) - if err != nil { - return err - } if err := unm.Unmarshal(buf, resp); err != nil { return err