diff --git a/internal/gengapic/client_init_test.go b/internal/gengapic/client_init_test.go index f37efbaa79..751310de95 100644 --- a/internal/gengapic/client_init_test.go +++ b/internal/gengapic/client_init_test.go @@ -415,7 +415,29 @@ func TestClientInit(t *testing.T) { imports map[pbinfo.ImportSpec]bool wantNumSnps int setExt func() (protoreflect.ExtensionType, interface{}) + features []featureID }{ + { + tstName: "foo_client_init_default", + mixins: mixins{ + "google.cloud.location.Locations": locationMethods(), + "google.iam.v1.IAMPolicy": iamPolicyMethods(), + }, + servName: "Foo", + serv: servPlain, + parameter: proto.String("go-gapic-package=path;mypackage"), + imports: map[pbinfo.ImportSpec]bool{ + {Path: "context"}: true, + {Path: "google.golang.org/grpc"}: true, + {Path: "google.golang.org/api/option"}: true, + {Name: "gtransport", Path: "google.golang.org/api/transport/grpc"}: true, + {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, + }, { tstName: "foo_client_init", mixins: mixins{ @@ -436,6 +458,73 @@ func TestClientInit(t *testing.T) { {Path: "log/slog"}: true, }, wantNumSnps: 6, + features: []featureID{OpenTelemetryTracingFeature}, + }, + { + tstName: "foo_client_init_logging", + mixins: mixins{ + "google.cloud.location.Locations": locationMethods(), + "google.iam.v1.IAMPolicy": iamPolicyMethods(), + }, + servName: "Foo", + serv: servPlain, + parameter: proto.String("go-gapic-package=path;mypackage"), + imports: map[pbinfo.ImportSpec]bool{ + {Path: "context"}: true, + {Path: "google.golang.org/grpc"}: true, + {Path: "google.golang.org/api/option"}: true, + {Name: "gtransport", Path: "google.golang.org/api/transport/grpc"}: true, + {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, + features: []featureID{OpenTelemetryLoggingFeature}, + }, + { + tstName: "foo_client_init_metrics", + mixins: mixins{ + "google.cloud.location.Locations": locationMethods(), + "google.iam.v1.IAMPolicy": iamPolicyMethods(), + }, + servName: "Foo", + serv: servPlain, + parameter: proto.String("go-gapic-package=path;mypackage"), + imports: map[pbinfo.ImportSpec]bool{ + {Path: "context"}: true, + {Path: "google.golang.org/grpc"}: true, + {Path: "google.golang.org/api/option"}: true, + {Name: "gtransport", Path: "google.golang.org/api/transport/grpc"}: true, + {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, + features: []featureID{OpenTelemetryMetricsFeature}, + }, + { + tstName: "foo_client_init_all_telemetry", + mixins: mixins{ + "google.cloud.location.Locations": locationMethods(), + "google.iam.v1.IAMPolicy": iamPolicyMethods(), + }, + servName: "Foo", + serv: servPlain, + parameter: proto.String("go-gapic-package=path;mypackage"), + imports: map[pbinfo.ImportSpec]bool{ + {Path: "context"}: true, + {Path: "google.golang.org/grpc"}: true, + {Path: "google.golang.org/api/option"}: true, + {Name: "gtransport", Path: "google.golang.org/api/transport/grpc"}: true, + {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, + features: []featureID{OpenTelemetryTracingFeature, OpenTelemetryLoggingFeature, OpenTelemetryMetricsFeature}, }, { tstName: "foo_rest_client_init", @@ -460,6 +549,54 @@ func TestClientInit(t *testing.T) { }, wantNumSnps: 6, }, + { + tstName: "foo_rest_client_init_default", + mixins: mixins{ + "google.cloud.location.Locations": locationMethods(), + "google.iam.v1.IAMPolicy": iamPolicyMethods(), + }, + servName: "Foo", + serv: servPlain, + parameter: proto.String("go-gapic-package=path;mypackage,transport=rest"), + imports: map[pbinfo.ImportSpec]bool{ + {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, + {Name: "httptransport", Path: "google.golang.org/api/transport/http"}: true, + {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, + features: []featureID{""}, + }, + { + tstName: "foo_rest_client_init_metrics", + mixins: mixins{ + "google.cloud.location.Locations": locationMethods(), + "google.iam.v1.IAMPolicy": iamPolicyMethods(), + }, + servName: "Foo", + serv: servPlain, + parameter: proto.String("go-gapic-package=path;mypackage,transport=rest"), + imports: map[pbinfo.ImportSpec]bool{ + {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, + {Name: "httptransport", Path: "google.golang.org/api/transport/http"}: true, + {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, + features: []featureID{OpenTelemetryTracingFeature, OpenTelemetryMetricsFeature}, + }, { tstName: "empty_client_init", servName: "", @@ -537,6 +674,28 @@ func TestClientInit(t *testing.T) { return extendedops.E_OperationService, opS.GetName() }, }, + { + tstName: "custom_op_init_metrics", + servName: "", + serv: servCustomOp, + customOpServ: opS, + parameter: proto.String("go-gapic-package=path;mypackage,transport=rest,diregapic"), + imports: map[pbinfo.ImportSpec]bool{ + {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, + {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{}) { + return extendedops.E_OperationService, opS.GetName() + }, + features: []featureID{OpenTelemetryTracingFeature, OpenTelemetryMetricsFeature}, + }, } { t.Run(tst.tstName, func(t *testing.T) { setExt := tst.setExt @@ -602,7 +761,17 @@ func TestClientInit(t *testing.T) { } g.reset() - g.cfg.featureEnablement = map[featureID]struct{}{OpenTelemetryTracingFeature: {}} + g.cfg.featureEnablement = make(map[featureID]struct{}) + if len(tst.features) == 0 { + g.cfg.featureEnablement[OpenTelemetryTracingFeature] = struct{}{} + } + for _, f := range tst.features { + if f == "" { + g.cfg.featureEnablement = make(map[featureID]struct{}) + } else { + g.cfg.featureEnablement[f] = struct{}{} + } + } sm := snippets.NewMetadata("mypackage", "github.com/googleapis/mypackage", "mypackagego") sm.AddService(tst.serv.GetName(), "mypackage.googleapis.com") for _, m := range tst.serv.GetMethod() { diff --git a/internal/gengapic/feature.go b/internal/gengapic/feature.go index 8eb1dbc526..353750d11b 100644 --- a/internal/gengapic/feature.go +++ b/internal/gengapic/feature.go @@ -34,6 +34,7 @@ const ( MTLSHardBoundTokensFeature featureID = "mtls_hard_bound_tokens" OpenTelemetryTracingFeature featureID = "open_telemetry_tracing" OpenTelemetryLoggingFeature featureID = "open_telemetry_logging" + OpenTelemetryMetricsFeature featureID = "open_telemetry_metrics" ) // featureRegistry contains the registry of defined features. @@ -50,6 +51,9 @@ var featureRegistry = map[featureID]*featureInfo{ Description: "Enable OpenTelemetry logging support (Service Identity, Resource Names, URL Templates).", TrackingID: "b/476980971", }, + OpenTelemetryMetricsFeature: { + Description: "Enable OpenTelemetry M1 metrics support (Duration, RPC Method, URL Template, Error Mapping).", + }, MTLSHardBoundTokensFeature: { Description: "support MTLS hard bound tokens", TrackingID: "b/327916505", diff --git a/internal/gengapic/gengapic.go b/internal/gengapic/gengapic.go index bb9203d1d0..e91a234c41 100644 --- a/internal/gengapic/gengapic.go +++ b/internal/gengapic/gengapic.go @@ -681,6 +681,24 @@ func (g *generator) appendCallOpts(m *descriptorpb.MethodDescriptorProto) { g.printf("opts = append(%[1]s[0:len(%[1]s):len(%[1]s)], opts...)", "(*c.CallOptions)."+*m.Name) } +func (g *generator) injectTelemetryContext(m *descriptorpb.MethodDescriptorProto, info *httpInfo) { + if m == nil { + return + } + if g.featureEnabled(OpenTelemetryMetricsFeature) || g.featureEnabled(OpenTelemetryTracingFeature) { + g.imports[pbinfo.ImportSpec{Path: "github.com/googleapis/gax-go/v2/callctx"}] = true + g.imports[pbinfo.ImportSpec{Name: "gax", Path: "github.com/googleapis/gax-go/v2"}] = true + serv := g.descInfo.ParentElement[m].(*descriptorpb.ServiceDescriptorProto) + fqn := fmt.Sprintf("%s.%s/%s", g.descInfo.ParentFile[serv].GetPackage(), serv.GetName(), m.GetName()) + g.printf("if gax.IsFeatureEnabled(\"METRICS\") || gax.IsFeatureEnabled(\"TRACING\") {") + g.printf(" ctx = callctx.WithTelemetryContext(ctx, \"rpc_method\", %q)", fqn) + if info != nil && info.url != "" { + g.printf(" ctx = callctx.WithTelemetryContext(ctx, \"url_template\", %q)", info.url) + } + g.printf("}") + } +} + // This is a helper function that checks whether a description contains a Deprecated header. func containsDeprecated(com string) bool { for _, s := range strings.Split(com, "\n") { diff --git a/internal/gengapic/gengapic_test.go b/internal/gengapic/gengapic_test.go index ab55bec01b..b35d76fd11 100644 --- a/internal/gengapic/gengapic_test.go +++ b/internal/gengapic/gengapic_test.go @@ -419,11 +419,13 @@ func TestGenGRPCMethods(t *testing.T) { Options: opts, }, imports: map[pbinfo.ImportSpec]bool{ - {Path: "fmt"}: true, - {Path: "github.com/google/uuid"}: true, - {Path: "google.golang.org/grpc/metadata"}: true, - {Path: "net/url"}: true, - {Name: "mypackagepb", Path: "mypackage"}: true, + {Path: "fmt"}: true, + {Path: "github.com/google/uuid"}: true, + {Path: "google.golang.org/grpc/metadata"}: true, + {Path: "net/url"}: true, + {Name: "mypackagepb", Path: "mypackage"}: true, + {Path: "github.com/googleapis/gax-go/v2/callctx"}: true, + {Name: "gax", Path: "github.com/googleapis/gax-go/v2"}: true, }, }, { m: &descriptorpb.MethodDescriptorProto{ @@ -433,11 +435,13 @@ func TestGenGRPCMethods(t *testing.T) { Options: opts, }, imports: map[pbinfo.ImportSpec]bool{ - {Path: "fmt"}: true, - {Path: "github.com/google/uuid"}: true, - {Path: "google.golang.org/grpc/metadata"}: true, - {Path: "net/url"}: true, - {Name: "mypackagepb", Path: "mypackage"}: true, + {Path: "fmt"}: true, + {Path: "github.com/google/uuid"}: true, + {Path: "google.golang.org/grpc/metadata"}: true, + {Path: "net/url"}: true, + {Name: "mypackagepb", Path: "mypackage"}: true, + {Path: "github.com/googleapis/gax-go/v2/callctx"}: true, + {Name: "gax", Path: "github.com/googleapis/gax-go/v2"}: true, }, }, { @@ -448,12 +452,14 @@ func TestGenGRPCMethods(t *testing.T) { Options: opts, }, imports: map[pbinfo.ImportSpec]bool{ - {Path: "fmt"}: true, - {Path: "google.golang.org/api/iterator"}: true, - {Path: "google.golang.org/grpc/metadata"}: true, - {Path: "google.golang.org/protobuf/proto"}: true, - {Path: "net/url"}: true, - {Name: "mypackagepb", Path: "mypackage"}: true, + {Path: "fmt"}: true, + {Path: "google.golang.org/api/iterator"}: true, + {Path: "google.golang.org/grpc/metadata"}: true, + {Path: "google.golang.org/protobuf/proto"}: true, + {Path: "net/url"}: true, + {Name: "mypackagepb", Path: "mypackage"}: true, + {Path: "github.com/googleapis/gax-go/v2/callctx"}: true, + {Name: "gax", Path: "github.com/googleapis/gax-go/v2"}: true, }, }, { m: &descriptorpb.MethodDescriptorProto{ @@ -463,12 +469,14 @@ func TestGenGRPCMethods(t *testing.T) { Options: opts, }, imports: map[pbinfo.ImportSpec]bool{ - {Path: "fmt"}: true, - {Path: "google.golang.org/api/iterator"}: true, - {Path: "google.golang.org/grpc/metadata"}: true, - {Path: "google.golang.org/protobuf/proto"}: true, - {Path: "net/url"}: true, - {Name: "mypackagepb", Path: "mypackage"}: true, + {Path: "fmt"}: true, + {Path: "google.golang.org/api/iterator"}: true, + {Path: "google.golang.org/grpc/metadata"}: true, + {Path: "google.golang.org/protobuf/proto"}: true, + {Path: "net/url"}: true, + {Name: "mypackagepb", Path: "mypackage"}: true, + {Path: "github.com/googleapis/gax-go/v2/callctx"}: true, + {Name: "gax", Path: "github.com/googleapis/gax-go/v2"}: true, }, }, { @@ -481,9 +489,11 @@ func TestGenGRPCMethods(t *testing.T) { }, imports: map[pbinfo.ImportSpec]bool{ {Path: "fmt"}: true, - {Path: "google.golang.org/grpc/metadata"}: true, - {Path: "net/url"}: true, - {Name: "mypackagepb", Path: "mypackage"}: true, + {Path: "google.golang.org/grpc/metadata"}: true, + {Path: "net/url"}: true, + {Name: "mypackagepb", Path: "mypackage"}: true, + {Path: "github.com/googleapis/gax-go/v2/callctx"}: true, + {Name: "gax", Path: "github.com/googleapis/gax-go/v2"}: true, }, }, { @@ -495,7 +505,9 @@ func TestGenGRPCMethods(t *testing.T) { Options: opts, }, imports: map[pbinfo.ImportSpec]bool{ - {Name: "mypackagepb", Path: "mypackage"}: true, + {Name: "mypackagepb", Path: "mypackage"}: true, + {Path: "github.com/googleapis/gax-go/v2/callctx"}: true, + {Name: "gax", Path: "github.com/googleapis/gax-go/v2"}: true, }, }, { @@ -508,7 +520,9 @@ func TestGenGRPCMethods(t *testing.T) { Options: opts, }, imports: map[pbinfo.ImportSpec]bool{ - {Name: "mypackagepb", Path: "mypackage"}: true, + {Name: "mypackagepb", Path: "mypackage"}: true, + {Path: "github.com/googleapis/gax-go/v2/callctx"}: true, + {Name: "gax", Path: "github.com/googleapis/gax-go/v2"}: true, }, }, // Test for dynamic routing header annotations. @@ -521,11 +535,13 @@ func TestGenGRPCMethods(t *testing.T) { }, imports: map[pbinfo.ImportSpec]bool{ {Path: "fmt"}: true, - {Path: "google.golang.org/grpc/metadata"}: true, - {Path: "net/url"}: true, - {Path: "regexp"}: true, - {Path: "strings"}: true, - {Name: "mypackagepb", Path: "mypackage"}: true, + {Path: "google.golang.org/grpc/metadata"}: true, + {Path: "net/url"}: true, + {Path: "regexp"}: true, + {Path: "strings"}: true, + {Name: "mypackagepb", Path: "mypackage"}: true, + {Path: "github.com/googleapis/gax-go/v2/callctx"}: true, + {Name: "gax", Path: "github.com/googleapis/gax-go/v2"}: true, }, }, // Test for empty dynamic routing annotation, so no headers should be sent. @@ -537,9 +553,11 @@ func TestGenGRPCMethods(t *testing.T) { Options: optsGetManyOtherThings, }, imports: map[pbinfo.ImportSpec]bool{ - {Path: "google.golang.org/api/iterator"}: true, - {Path: "google.golang.org/protobuf/proto"}: true, - {Name: "mypackagepb", Path: "mypackage"}: true, + {Path: "google.golang.org/api/iterator"}: true, + {Path: "google.golang.org/protobuf/proto"}: true, + {Name: "mypackagepb", Path: "mypackage"}: true, + {Path: "github.com/googleapis/gax-go/v2/callctx"}: true, + {Name: "gax", Path: "github.com/googleapis/gax-go/v2"}: true, }, }, } { diff --git a/internal/gengapic/gengrpc.go b/internal/gengapic/gengrpc.go index 46dc8d7966..a9cabc05ce 100644 --- a/internal/gengapic/gengrpc.go +++ b/internal/gengapic/gengrpc.go @@ -104,6 +104,7 @@ func (g *generator) unaryGRPCCall(servName string, m *descriptorpb.MethodDescrip lowcaseServName, m.GetName(), inSpec.Name, inType.GetName(), retTyp) g.insertRequestHeaders(m, grpc) + g.injectTelemetryContext(m, nil) g.initializeAutoPopulatedFields(servName, m) g.appendCallOpts(m) @@ -143,6 +144,7 @@ func (g *generator) emptyUnaryGRPCCall(servName string, m *descriptorpb.MethodDe lowcaseServName, m.GetName(), inSpec.Name, inType.GetName()) g.insertRequestHeaders(m, grpc) + g.injectTelemetryContext(m, nil) g.initializeAutoPopulatedFields(servName, m) g.appendCallOpts(m) p("err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {") @@ -301,7 +303,6 @@ func (g *generator) grpcClientInit(serv *descriptorpb.ServiceDescriptorProto, se p("xGoogHeaders []string") p("") p("logger *slog.Logger") - p("}") p("") @@ -365,6 +366,25 @@ func (g *generator) grpcClientUtilities(serv *descriptorpb.ServiceDescriptorProt p("") p(" }") p(" c.setGoogleClientInfo()") + if g.featureEnabled(OpenTelemetryMetricsFeature) { + p(" if gax.IsFeatureEnabled(\"METRICS\") {") + p(" metrics := gax.NewClientMetrics(") + p(" gax.WithTelemetryLogger(c.logger),") + p(" gax.WithTelemetryAttributes(map[string]string{") + p(" gax.ClientService: %q,", strings.Split(g.cfg.APIServiceConfig.GetName(), ".")[0]) + p(" gax.ClientVersion: getVersionClient(),") + p(" gax.ClientArtifact: %q,", g.cfg.pkgPath) + p(" gax.RPCSystem: \"grpc\",") + p(" gax.URLDomain: %q,", g.cfg.APIServiceConfig.GetName()) + p(" }),") + p(" )") + p("") + methods := append(serv.GetMethod(), g.getMixinMethods()...) + for _, m := range methods { + p(" client.CallOptions.%s = append(client.CallOptions.%s, gax.WithClientMetrics(metrics))", m.GetName(), m.GetName()) + } + p(" }") + } p("") p(" client.internalClient = c") p("") diff --git a/internal/gengapic/gengrpc_test.go b/internal/gengapic/gengrpc_test.go index bc54420d44..03b6a2599f 100644 --- a/internal/gengapic/gengrpc_test.go +++ b/internal/gengapic/gengrpc_test.go @@ -126,3 +126,54 @@ func TestServiceRenaming(t *testing.T) { txtdiff.Diff(t, g.pt.String(), filepath.Join("testdata", "service_rename.want")) } + +func TestMetricsInstrumentation(t *testing.T) { + file := &descriptorpb.FileDescriptorProto{ + Package: proto.String("my.pkg"), + Options: &descriptorpb.FileOptions{ + GoPackage: proto.String("mypackage"), + }, + } + serv := &descriptorpb.ServiceDescriptorProto{ + Name: proto.String("Foo"), + } + m := &descriptorpb.MethodDescriptorProto{ + Name: proto.String("Baz"), + InputType: proto.String(".my.pkg.InputType"), + OutputType: proto.String(emptyType), + } + serv.Method = []*descriptorpb.MethodDescriptorProto{m} + + var g generator + g.imports = map[pbinfo.ImportSpec]bool{} + g.cfg = &generatorConfig{ + pkgName: "pkg", + transports: []transport{grpc}, + featureEnablement: map[featureID]struct{}{OpenTelemetryMetricsFeature: {}}, + APIServiceConfig: &serviceconfig.Service{ + Name: "foo.googleapis.com", + }, + } + + commonTypes(&g) + + inputType := &descriptorpb.DescriptorProto{ + Name: proto.String("InputType"), + } + // empty is already created in commonTypes, but let's make sure we have the right one or look it up. + // Actually commonTypes creates its own 'empty' and adds it to descInfo. + // Let's just use the one from descInfo if possible, or just overwrite it. + + g.descInfo.ParentFile[serv] = file + g.descInfo.ParentFile[m] = file + g.descInfo.ParentFile[inputType] = file + + g.descInfo.Type[".my.pkg.InputType"] = inputType + g.descInfo.ParentElement[m] = serv + + if err := g.genGRPCMethod("Foo", serv, m); err != nil { + t.Fatal(err) + } + + txtdiff.Diff(t, g.pt.String(), filepath.Join("testdata", "metrics_instrumentation.want")) +} diff --git a/internal/gengapic/genrest.go b/internal/gengapic/genrest.go index 89a575eb72..6624d486c5 100644 --- a/internal/gengapic/genrest.go +++ b/internal/gengapic/genrest.go @@ -188,6 +188,26 @@ func (g *generator) restClientUtilities(serv *descriptorpb.ServiceDescriptorProt p(" }") p(" c.setGoogleClientInfo()") p("") + if g.featureEnabled(OpenTelemetryMetricsFeature) { + p(" if gax.IsFeatureEnabled(\"METRICS\") {") + p(" metrics := gax.NewClientMetrics(") + p(" gax.WithTelemetryLogger(c.logger),") + p(" gax.WithTelemetryAttributes(map[string]string{") + p(" gax.ClientService: %q,", strings.Split(g.cfg.APIServiceConfig.GetName(), ".")[0]) + p(" gax.ClientVersion: getVersionClient(),") + p(" gax.ClientArtifact: %q,", g.cfg.pkgPath) + p(" gax.RPCSystem: \"http\",") + p(" gax.URLDomain: %q,", g.cfg.APIServiceConfig.GetName()) + p(" }),") + p(" )") + p("") + methods := append(serv.GetMethod(), g.getMixinMethods()...) + for _, m := range methods { + p(" callOpts.%s = append(callOpts.%s, gax.WithClientMetrics(metrics))", m.GetName(), m.GetName()) + } + p(" }") + p("") + } if hasRPCForLRO { p("lroOpts := []option.ClientOption{") p(" option.WithHTTPClient(httpClient),") @@ -669,6 +689,7 @@ func (g *generator) serverStreamRESTCall(servName string, s *descriptorpb.Servic g.generateQueryString(m) p("// Build HTTP headers from client and context metadata.") g.insertRequestHeaders(m, rest) + g.injectTelemetryContext(m, info) if info != nil && (g.featureEnabled(OpenTelemetryTracingFeature) || g.featureEnabled(OpenTelemetryLoggingFeature)) { p("if gax.IsFeatureEnabled(\"TRACING\") || gax.IsFeatureEnabled(\"LOGGING\") {") p(" ctx = metadata.AppendToOutgoingContext(ctx, \"url.template\", %q)", info.url) @@ -957,6 +978,7 @@ func (g *generator) lroRESTCall(servName string, m *descriptorpb.MethodDescripto g.generateQueryString(m) p("// Build HTTP headers from client and context metadata.") g.insertRequestHeaders(m, rest) + g.injectTelemetryContext(m, info) if info != nil && (g.featureEnabled(OpenTelemetryTracingFeature) || g.featureEnabled(OpenTelemetryLoggingFeature)) { p("if gax.IsFeatureEnabled(\"TRACING\") || gax.IsFeatureEnabled(\"LOGGING\") {") p(" ctx = metadata.AppendToOutgoingContext(ctx, \"url.template\", %q)", info.url) @@ -1059,6 +1081,7 @@ func (g *generator) emptyUnaryRESTCall(servName string, m *descriptorpb.MethodDe g.generateQueryString(m) p("// Build HTTP headers from client and context metadata.") g.insertRequestHeaders(m, rest) + g.injectTelemetryContext(m, info) if info != nil && (g.featureEnabled(OpenTelemetryTracingFeature) || g.featureEnabled(OpenTelemetryLoggingFeature)) { p("if gax.IsFeatureEnabled(\"TRACING\") || gax.IsFeatureEnabled(\"LOGGING\") {") p(" ctx = metadata.AppendToOutgoingContext(ctx, \"url.template\", %q)", info.url) @@ -1154,6 +1177,7 @@ func (g *generator) unaryRESTCall(servName string, m *descriptorpb.MethodDescrip g.generateQueryString(m) p("// Build HTTP headers from client and context metadata.") g.insertRequestHeaders(m, rest) + g.injectTelemetryContext(m, info) if info != nil && (g.featureEnabled(OpenTelemetryTracingFeature) || g.featureEnabled(OpenTelemetryLoggingFeature)) { p("if gax.IsFeatureEnabled(\"TRACING\") || gax.IsFeatureEnabled(\"LOGGING\") {") p(" ctx = metadata.AppendToOutgoingContext(ctx, \"url.template\", %q)", info.url) diff --git a/internal/gengapic/genrest_test.go b/internal/gengapic/genrest_test.go index 0fa597a9d2..4ff35d7a1b 100644 --- a/internal/gengapic/genrest_test.go +++ b/internal/gengapic/genrest_test.go @@ -767,6 +767,8 @@ func TestGenRestMethod(t *testing.T) { {Path: "net/url"}: true, {Path: "fmt"}: true, {Name: "foopb", Path: "google.golang.org/genproto/cloud/foo/v1"}: true, + {Path: "github.com/googleapis/gax-go/v2/callctx"}: true, + {Name: "gax", Path: "github.com/googleapis/gax-go/v2"}: true, }, }, { @@ -779,6 +781,8 @@ func TestGenRestMethod(t *testing.T) { {Path: "google.golang.org/grpc/metadata"}: true, {Path: "net/url"}: true, {Name: "foopb", Path: "google.golang.org/genproto/cloud/foo/v1"}: true, + {Path: "github.com/googleapis/gax-go/v2/callctx"}: true, + {Name: "gax", Path: "github.com/googleapis/gax-go/v2"}: true, }, }, { @@ -795,6 +799,8 @@ func TestGenRestMethod(t *testing.T) { {Path: "regexp"}: true, {Path: "strings"}: true, {Name: "foopb", Path: "google.golang.org/genproto/cloud/foo/v1"}: true, + {Path: "github.com/googleapis/gax-go/v2/callctx"}: true, + {Name: "gax", Path: "github.com/googleapis/gax-go/v2"}: true, }, }, { @@ -826,6 +832,8 @@ func TestGenRestMethod(t *testing.T) { {Path: "google.golang.org/grpc/metadata"}: true, {Path: "google.golang.org/protobuf/encoding/protojson"}: true, {Name: "foopb", Path: "google.golang.org/genproto/cloud/foo/v1"}: true, + {Path: "github.com/googleapis/gax-go/v2/callctx"}: true, + {Name: "gax", Path: "github.com/googleapis/gax-go/v2"}: true, }, }, { @@ -851,6 +859,8 @@ func TestGenRestMethod(t *testing.T) { {Path: "google.golang.org/protobuf/encoding/protojson"}: true, {Path: "net/url"}: true, {Name: "longrunningpb", Path: "cloud.google.com/go/longrunning/autogen/longrunningpb"}: true, + {Path: "github.com/googleapis/gax-go/v2/callctx"}: true, + {Name: "gax", Path: "github.com/googleapis/gax-go/v2"}: true, }, }, { @@ -867,6 +877,8 @@ func TestGenRestMethod(t *testing.T) { {Path: "strings"}: true, {Name: "foopb", Path: "google.golang.org/genproto/cloud/foo/v1"}: true, {Name: "httpbodypb", Path: "google.golang.org/genproto/googleapis/api/httpbody"}: true, + {Path: "github.com/googleapis/gax-go/v2/callctx"}: true, + {Name: "gax", Path: "github.com/googleapis/gax-go/v2"}: true, }, }, { @@ -880,6 +892,8 @@ func TestGenRestMethod(t *testing.T) { {Path: "google.golang.org/protobuf/encoding/protojson"}: true, {Path: "net/url"}: true, {Name: "foopb", Path: "google.golang.org/genproto/cloud/foo/v1"}: true, + {Path: "github.com/googleapis/gax-go/v2/callctx"}: true, + {Name: "gax", Path: "github.com/googleapis/gax-go/v2"}: true, }, }, } { diff --git a/internal/gengapic/lro.go b/internal/gengapic/lro.go index 1c93d56bcf..7b226b958c 100644 --- a/internal/gengapic/lro.go +++ b/internal/gengapic/lro.go @@ -44,6 +44,7 @@ func (g *generator) lroCall(servName string, m *descriptorpb.MethodDescriptorPro lowcaseServName, m.GetName(), inSpec.Name, inType.GetName(), lroType) g.insertRequestHeaders(m, grpc) + g.injectTelemetryContext(m, nil) g.initializeAutoPopulatedFields(servName, m) g.appendCallOpts(m) diff --git a/internal/gengapic/paging.go b/internal/gengapic/paging.go index ad0dda1b07..1a6d93fe97 100644 --- a/internal/gengapic/paging.go +++ b/internal/gengapic/paging.go @@ -377,6 +377,7 @@ func (g *generator) pagingCall(servName string, m *descriptorpb.MethodDescriptor lowcaseServName, *m.Name, inSpec.Name, inType.GetName(), pt.iterTypeName) g.insertRequestHeaders(m, grpc) + g.injectTelemetryContext(m, nil) g.appendCallOpts(m) p("it := &%s{}", pt.iterTypeName) p("req = proto.Clone(req).(*%s.%s)", inSpec.Name, inType.GetName()) diff --git a/internal/gengapic/stream.go b/internal/gengapic/stream.go index cba474a084..3e9c24f891 100644 --- a/internal/gengapic/stream.go +++ b/internal/gengapic/stream.go @@ -37,6 +37,7 @@ func (g *generator) noRequestStreamCall(servName string, s *descriptorpb.Service p("func (c *%s) %s(ctx context.Context, opts ...gax.CallOption) (%s, error) {", lowcaseServName, m.GetName(), retTyp) g.insertRequestHeaders(nil, grpc) + g.injectTelemetryContext(m, nil) p(" var resp %s", retTyp) g.appendCallOpts(m) @@ -80,6 +81,7 @@ func (g *generator) serverStreamCall(servName string, s *descriptorpb.ServiceDes lowcaseServName, m.GetName(), inSpec.Name, inType.GetName(), retTyp) g.insertRequestHeaders(m, grpc) + g.injectTelemetryContext(m, nil) g.appendCallOpts(m) p(" var resp %s", retTyp) diff --git a/internal/gengapic/testdata/custom_op_init_metrics.want b/internal/gengapic/testdata/custom_op_init_metrics.want new file mode 100644 index 0000000000..00669d8162 --- /dev/null +++ b/internal/gengapic/testdata/custom_op_init_metrics.want @@ -0,0 +1,154 @@ +// internalClient is an interface that defines the methods available from Awesome Foo API. +type internalClient interface { + Close() error + setGoogleClientInfo(...string) + Connection() *grpc.ClientConn + Zip(context.Context, *mypackagepb.Bar, ...gax.CallOption) (*Operation, error) +} + +// Client is a client for interacting with Awesome Foo API. +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +// +// Foo service does stuff. +type Client struct { + // The internal transport-dependent client. + internalClient internalClient + + // The call options for this service. + CallOptions *CallOptions + +} + +// Wrapper methods routed to the internal client. + +// Close closes the connection to the API service. The user should invoke this when +// the client is no longer required. +func (c *Client) Close() error { + return c.internalClient.Close() +} + +// setGoogleClientInfo sets the name and version of the application in +// the `x-goog-api-client` header passed on each request. Intended for +// use by Google-written clients. +func (c *Client) setGoogleClientInfo(keyval ...string) { + c.internalClient.setGoogleClientInfo(keyval...) +} + +// Connection returns a connection to the API service. +// +// Deprecated: Connections are now pooled so this method does not always +// return the same resource. +func (c *Client) Connection() *grpc.ClientConn { + return c.internalClient.Connection() +} + +// Zip does some stuff. +func (c *Client) Zip(ctx context.Context, req *mypackagepb.Bar, opts ...gax.CallOption) (*Operation, error) { + return c.internalClient.Zip(ctx, req, opts...) +} + +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type restClient struct { + // The http endpoint to connect to. + endpoint string + + // The http client. + httpClient *http.Client + + // operationClient is used to call the operation-specific management service. + operationClient *FooOperationClient + + // The x-goog-* headers to be sent with each request. + xGoogHeaders []string + + // Points back to the CallOptions field of the containing Client + CallOptions **CallOptions + + logger *slog.Logger +} + +// NewRESTClient creates a new foo rest client. +// +// Foo service does stuff. +func NewRESTClient(ctx context.Context, opts ...option.ClientOption) (*Client, error) { + clientOpts := append(defaultRESTClientOptions(), opts...) + if gax.IsFeatureEnabled("TRACING") || gax.IsFeatureEnabled("LOGGING") { + clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{ + "gcp.client.service": "foo", + "gcp.client.version": getVersionClient(), + "gcp.client.repo": "googleapis/google-cloud-go", + "gcp.client.artifact": "path", + "gcp.client.language": "go", + "url.domain": "foo.googleapis.com", + })) + } + httpClient, endpoint, err := httptransport.NewClient(ctx, clientOpts...) + if err != nil { + return nil, err + } + + callOpts := defaultRESTCallOptions() + c := &restClient{ + endpoint: endpoint, + httpClient: httpClient, + CallOptions: &callOpts, + logger: internaloption.GetLogger(opts), + } + c.setGoogleClientInfo() + + if gax.IsFeatureEnabled("METRICS") { + metrics := gax.NewClientMetrics( + gax.WithTelemetryLogger(c.logger), + gax.WithTelemetryAttributes(map[string]string{ + gax.ClientService: "foo", + gax.ClientVersion: getVersionClient(), + gax.ClientArtifact: "path", + gax.RPCSystem: "http", + gax.URLDomain: "foo.googleapis.com", + }), + ) + + callOpts.Zip = append(callOpts.Zip, gax.WithClientMetrics(metrics)) + } + + o := []option.ClientOption{ + option.WithHTTPClient(httpClient), + option.WithEndpoint(endpoint), + } + opC, err := NewFooOperationRESTClient(ctx, o...) + if err != nil { + return nil, err + } + c.operationClient = opC + + return &Client{internalClient: c, CallOptions: callOpts}, nil +} + +// setGoogleClientInfo sets the name and version of the application in +// the `x-goog-api-client` header passed on each request. Intended for +// use by Google-written clients. +func (c *restClient) setGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", gax.GoVersion}, keyval...) + kv = append(kv, "gapic", getVersionClient(), "gax", gax.Version, "rest", "UNKNOWN", "pb", protoVersion) + c.xGoogHeaders = []string{ + "x-goog-api-client", gax.XGoogHeader(kv...), + } +} + +// Close closes the connection to the API service. The user should invoke this when +// the client is no longer required. +func (c *restClient) Close() error { + // Replace httpClient with nil to force cleanup. + c.httpClient = nil + if err := c.operationClient.Close(); err != nil { + return err + } + return nil +} + +// Connection returns a connection to the API service. +// +// Deprecated: This method always returns nil. +func (c *restClient) Connection() *grpc.ClientConn { + return nil +} diff --git a/internal/gengapic/testdata/foo_client_init_all_telemetry.want b/internal/gengapic/testdata/foo_client_init_all_telemetry.want new file mode 100644 index 0000000000..fff8175cea --- /dev/null +++ b/internal/gengapic/testdata/foo_client_init_all_telemetry.want @@ -0,0 +1,190 @@ +// internalFooClient is an interface that defines the methods available from Awesome Foo API. +type internalFooClient interface { + Close() error + setGoogleClientInfo(...string) + Connection() *grpc.ClientConn + Zip(context.Context, *mypackagepb.Bar, ...gax.CallOption) (*mypackagepb.Foo, error) + ListLocations(context.Context, *locationpb.ListLocationsRequest, ...gax.CallOption) *LocationIterator + GetLocation(context.Context, *locationpb.GetLocationRequest, ...gax.CallOption) (*locationpb.Location, error) + SetIamPolicy(context.Context, *iampb.SetIamPolicyRequest, ...gax.CallOption) (*iampb.Policy, error) + GetIamPolicy(context.Context, *iampb.GetIamPolicyRequest, ...gax.CallOption) (*iampb.Policy, error) + TestIamPermissions(context.Context, *iampb.TestIamPermissionsRequest, ...gax.CallOption) (*iampb.TestIamPermissionsResponse, error) +} + +// FooClient is a client for interacting with Awesome Foo API. +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +// +// Foo service does stuff. +// +// This client uses Foo version v1_20240425. +type FooClient struct { + // The internal transport-dependent client. + internalClient internalFooClient + + // The call options for this service. + CallOptions *FooCallOptions + +} + +// Wrapper methods routed to the internal client. + +// Close closes the connection to the API service. The user should invoke this when +// the client is no longer required. +func (c *FooClient) Close() error { + return c.internalClient.Close() +} + +// setGoogleClientInfo sets the name and version of the application in +// the `x-goog-api-client` header passed on each request. Intended for +// use by Google-written clients. +func (c *FooClient) setGoogleClientInfo(keyval ...string) { + c.internalClient.setGoogleClientInfo(keyval...) +} + +// Connection returns a connection to the API service. +// +// Deprecated: Connections are now pooled so this method does not always +// return the same resource. +func (c *FooClient) Connection() *grpc.ClientConn { + return c.internalClient.Connection() +} + +// Zip does some stuff. +func (c *FooClient) Zip(ctx context.Context, req *mypackagepb.Bar, opts ...gax.CallOption) (*mypackagepb.Foo, error) { + return c.internalClient.Zip(ctx, req, opts...) +} + +func (c *FooClient) ListLocations(ctx context.Context, req *locationpb.ListLocationsRequest, opts ...gax.CallOption) *LocationIterator { + return c.internalClient.ListLocations(ctx, req, opts...) +} + +func (c *FooClient) GetLocation(ctx context.Context, req *locationpb.GetLocationRequest, opts ...gax.CallOption) (*locationpb.Location, error) { + return c.internalClient.GetLocation(ctx, req, opts...) +} + +func (c *FooClient) SetIamPolicy(ctx context.Context, req *iampb.SetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) { + return c.internalClient.SetIamPolicy(ctx, req, opts...) +} + +func (c *FooClient) GetIamPolicy(ctx context.Context, req *iampb.GetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) { + return c.internalClient.GetIamPolicy(ctx, req, opts...) +} + +func (c *FooClient) TestIamPermissions(ctx context.Context, req *iampb.TestIamPermissionsRequest, opts ...gax.CallOption) (*iampb.TestIamPermissionsResponse, error) { + return c.internalClient.TestIamPermissions(ctx, req, opts...) +} + +// fooGRPCClient is a client for interacting with Awesome Foo API over gRPC transport. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type fooGRPCClient struct { + // Connection pool of gRPC connections to the service. + connPool gtransport.ConnPool + + // Points back to the CallOptions field of the containing FooClient + CallOptions **FooCallOptions + + // The gRPC API client. + fooClient mypackagepb.FooClient + + iamPolicyClient iampb.IAMPolicyClient + + locationsClient locationpb.LocationsClient + + // The x-goog-* metadata to be sent with each request. + xGoogHeaders []string + + logger *slog.Logger +} + +// NewFooClient creates a new foo client based on gRPC. +// The returned client must be Closed when it is done being used to clean up its underlying connections. +// +// Foo service does stuff. +func NewFooClient(ctx context.Context, opts ...option.ClientOption) (*FooClient, error) { + clientOpts := defaultFooGRPCClientOptions() + if gax.IsFeatureEnabled("TRACING") || gax.IsFeatureEnabled("LOGGING") { + clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{ + "gcp.client.service": "foo", + "gcp.client.version": getVersionClient(), + "gcp.client.repo": "googleapis/google-cloud-go", + "gcp.client.artifact": "path", + "gcp.client.language": "go", + "url.domain": "foo.googleapis.com", + })) + } + if newFooClientHook != nil { + hookOpts, err := newFooClientHook(ctx, clientHookParams{}) + if err != nil { + return nil, err + } + clientOpts = append(clientOpts, hookOpts...) + } + + connPool, err := gtransport.DialPool(ctx, append(clientOpts, opts...)...) + if err != nil { + return nil, err + } + client := FooClient{CallOptions: defaultFooCallOptions()} + + c := &fooGRPCClient{ + connPool: connPool, + fooClient: mypackagepb.NewFooClient(connPool), + CallOptions: &client.CallOptions, + logger: internaloption.GetLogger(opts), + iamPolicyClient: iampb.NewIAMPolicyClient(connPool), + locationsClient: locationpb.NewLocationsClient(connPool), + + } + c.setGoogleClientInfo() + if gax.IsFeatureEnabled("METRICS") { + metrics := gax.NewClientMetrics( + gax.WithTelemetryLogger(c.logger), + gax.WithTelemetryAttributes(map[string]string{ + gax.ClientService: "foo", + gax.ClientVersion: getVersionClient(), + gax.ClientArtifact: "path", + gax.RPCSystem: "grpc", + gax.URLDomain: "foo.googleapis.com", + }), + ) + + client.CallOptions.Zip = append(client.CallOptions.Zip, gax.WithClientMetrics(metrics)) + client.CallOptions.ListLocations = append(client.CallOptions.ListLocations, gax.WithClientMetrics(metrics)) + client.CallOptions.GetLocation = append(client.CallOptions.GetLocation, gax.WithClientMetrics(metrics)) + client.CallOptions.SetIamPolicy = append(client.CallOptions.SetIamPolicy, gax.WithClientMetrics(metrics)) + client.CallOptions.GetIamPolicy = append(client.CallOptions.GetIamPolicy, gax.WithClientMetrics(metrics)) + client.CallOptions.TestIamPermissions = append(client.CallOptions.TestIamPermissions, gax.WithClientMetrics(metrics)) + } + + client.internalClient = c + + return &client, nil +} + +// Connection returns a connection to the API service. +// +// Deprecated: Connections are now pooled so this method does not always +// return the same resource. +func (c *fooGRPCClient) Connection() *grpc.ClientConn { + return c.connPool.Conn() +} + +// setGoogleClientInfo sets the name and version of the application in +// the `x-goog-api-client` header passed on each request. Intended for +// use by Google-written clients. +func (c *fooGRPCClient) setGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", gax.GoVersion}, keyval...) + kv = append(kv, "gapic", getVersionClient(), "gax", gax.Version, "grpc", grpc.Version, "pb", protoVersion) + c.xGoogHeaders = []string{ + "x-goog-api-client", gax.XGoogHeader(kv...), + "x-goog-api-version", "v1_20240425", + } +} + +// Close closes the connection to the API service. The user should invoke this when +// the client is no longer required. +func (c *fooGRPCClient) Close() error { + return c.connPool.Close() +} + diff --git a/internal/gengapic/testdata/foo_client_init_default.want b/internal/gengapic/testdata/foo_client_init_default.want new file mode 100644 index 0000000000..ce38d2a276 --- /dev/null +++ b/internal/gengapic/testdata/foo_client_init_default.want @@ -0,0 +1,171 @@ +// internalFooClient is an interface that defines the methods available from Awesome Foo API. +type internalFooClient interface { + Close() error + setGoogleClientInfo(...string) + Connection() *grpc.ClientConn + Zip(context.Context, *mypackagepb.Bar, ...gax.CallOption) (*mypackagepb.Foo, error) + ListLocations(context.Context, *locationpb.ListLocationsRequest, ...gax.CallOption) *LocationIterator + GetLocation(context.Context, *locationpb.GetLocationRequest, ...gax.CallOption) (*locationpb.Location, error) + SetIamPolicy(context.Context, *iampb.SetIamPolicyRequest, ...gax.CallOption) (*iampb.Policy, error) + GetIamPolicy(context.Context, *iampb.GetIamPolicyRequest, ...gax.CallOption) (*iampb.Policy, error) + TestIamPermissions(context.Context, *iampb.TestIamPermissionsRequest, ...gax.CallOption) (*iampb.TestIamPermissionsResponse, error) +} + +// FooClient is a client for interacting with Awesome Foo API. +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +// +// Foo service does stuff. +// +// This client uses Foo version v1_20240425. +type FooClient struct { + // The internal transport-dependent client. + internalClient internalFooClient + + // The call options for this service. + CallOptions *FooCallOptions + +} + +// Wrapper methods routed to the internal client. + +// Close closes the connection to the API service. The user should invoke this when +// the client is no longer required. +func (c *FooClient) Close() error { + return c.internalClient.Close() +} + +// setGoogleClientInfo sets the name and version of the application in +// the `x-goog-api-client` header passed on each request. Intended for +// use by Google-written clients. +func (c *FooClient) setGoogleClientInfo(keyval ...string) { + c.internalClient.setGoogleClientInfo(keyval...) +} + +// Connection returns a connection to the API service. +// +// Deprecated: Connections are now pooled so this method does not always +// return the same resource. +func (c *FooClient) Connection() *grpc.ClientConn { + return c.internalClient.Connection() +} + +// Zip does some stuff. +func (c *FooClient) Zip(ctx context.Context, req *mypackagepb.Bar, opts ...gax.CallOption) (*mypackagepb.Foo, error) { + return c.internalClient.Zip(ctx, req, opts...) +} + +func (c *FooClient) ListLocations(ctx context.Context, req *locationpb.ListLocationsRequest, opts ...gax.CallOption) *LocationIterator { + return c.internalClient.ListLocations(ctx, req, opts...) +} + +func (c *FooClient) GetLocation(ctx context.Context, req *locationpb.GetLocationRequest, opts ...gax.CallOption) (*locationpb.Location, error) { + return c.internalClient.GetLocation(ctx, req, opts...) +} + +func (c *FooClient) SetIamPolicy(ctx context.Context, req *iampb.SetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) { + return c.internalClient.SetIamPolicy(ctx, req, opts...) +} + +func (c *FooClient) GetIamPolicy(ctx context.Context, req *iampb.GetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) { + return c.internalClient.GetIamPolicy(ctx, req, opts...) +} + +func (c *FooClient) TestIamPermissions(ctx context.Context, req *iampb.TestIamPermissionsRequest, opts ...gax.CallOption) (*iampb.TestIamPermissionsResponse, error) { + return c.internalClient.TestIamPermissions(ctx, req, opts...) +} + +// fooGRPCClient is a client for interacting with Awesome Foo API over gRPC transport. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type fooGRPCClient struct { + // Connection pool of gRPC connections to the service. + connPool gtransport.ConnPool + + // Points back to the CallOptions field of the containing FooClient + CallOptions **FooCallOptions + + // The gRPC API client. + fooClient mypackagepb.FooClient + + iamPolicyClient iampb.IAMPolicyClient + + locationsClient locationpb.LocationsClient + + // The x-goog-* metadata to be sent with each request. + xGoogHeaders []string + + logger *slog.Logger +} + +// NewFooClient creates a new foo client based on gRPC. +// The returned client must be Closed when it is done being used to clean up its underlying connections. +// +// Foo service does stuff. +func NewFooClient(ctx context.Context, opts ...option.ClientOption) (*FooClient, error) { + clientOpts := defaultFooGRPCClientOptions() + if gax.IsFeatureEnabled("TRACING") || gax.IsFeatureEnabled("LOGGING") { + clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{ + "gcp.client.service": "foo", + "gcp.client.version": getVersionClient(), + "gcp.client.repo": "googleapis/google-cloud-go", + "gcp.client.artifact": "path", + "gcp.client.language": "go", + "url.domain": "foo.googleapis.com", + })) + } + if newFooClientHook != nil { + hookOpts, err := newFooClientHook(ctx, clientHookParams{}) + if err != nil { + return nil, err + } + clientOpts = append(clientOpts, hookOpts...) + } + + connPool, err := gtransport.DialPool(ctx, append(clientOpts, opts...)...) + if err != nil { + return nil, err + } + client := FooClient{CallOptions: defaultFooCallOptions()} + + c := &fooGRPCClient{ + connPool: connPool, + fooClient: mypackagepb.NewFooClient(connPool), + CallOptions: &client.CallOptions, + logger: internaloption.GetLogger(opts), + iamPolicyClient: iampb.NewIAMPolicyClient(connPool), + locationsClient: locationpb.NewLocationsClient(connPool), + + } + c.setGoogleClientInfo() + + client.internalClient = c + + return &client, nil +} + +// Connection returns a connection to the API service. +// +// Deprecated: Connections are now pooled so this method does not always +// return the same resource. +func (c *fooGRPCClient) Connection() *grpc.ClientConn { + return c.connPool.Conn() +} + +// setGoogleClientInfo sets the name and version of the application in +// the `x-goog-api-client` header passed on each request. Intended for +// use by Google-written clients. +func (c *fooGRPCClient) setGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", gax.GoVersion}, keyval...) + kv = append(kv, "gapic", getVersionClient(), "gax", gax.Version, "grpc", grpc.Version, "pb", protoVersion) + c.xGoogHeaders = []string{ + "x-goog-api-client", gax.XGoogHeader(kv...), + "x-goog-api-version", "v1_20240425", + } +} + +// Close closes the connection to the API service. The user should invoke this when +// the client is no longer required. +func (c *fooGRPCClient) Close() error { + return c.connPool.Close() +} + diff --git a/internal/gengapic/testdata/foo_client_init_logging.want b/internal/gengapic/testdata/foo_client_init_logging.want new file mode 100644 index 0000000000..ce38d2a276 --- /dev/null +++ b/internal/gengapic/testdata/foo_client_init_logging.want @@ -0,0 +1,171 @@ +// internalFooClient is an interface that defines the methods available from Awesome Foo API. +type internalFooClient interface { + Close() error + setGoogleClientInfo(...string) + Connection() *grpc.ClientConn + Zip(context.Context, *mypackagepb.Bar, ...gax.CallOption) (*mypackagepb.Foo, error) + ListLocations(context.Context, *locationpb.ListLocationsRequest, ...gax.CallOption) *LocationIterator + GetLocation(context.Context, *locationpb.GetLocationRequest, ...gax.CallOption) (*locationpb.Location, error) + SetIamPolicy(context.Context, *iampb.SetIamPolicyRequest, ...gax.CallOption) (*iampb.Policy, error) + GetIamPolicy(context.Context, *iampb.GetIamPolicyRequest, ...gax.CallOption) (*iampb.Policy, error) + TestIamPermissions(context.Context, *iampb.TestIamPermissionsRequest, ...gax.CallOption) (*iampb.TestIamPermissionsResponse, error) +} + +// FooClient is a client for interacting with Awesome Foo API. +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +// +// Foo service does stuff. +// +// This client uses Foo version v1_20240425. +type FooClient struct { + // The internal transport-dependent client. + internalClient internalFooClient + + // The call options for this service. + CallOptions *FooCallOptions + +} + +// Wrapper methods routed to the internal client. + +// Close closes the connection to the API service. The user should invoke this when +// the client is no longer required. +func (c *FooClient) Close() error { + return c.internalClient.Close() +} + +// setGoogleClientInfo sets the name and version of the application in +// the `x-goog-api-client` header passed on each request. Intended for +// use by Google-written clients. +func (c *FooClient) setGoogleClientInfo(keyval ...string) { + c.internalClient.setGoogleClientInfo(keyval...) +} + +// Connection returns a connection to the API service. +// +// Deprecated: Connections are now pooled so this method does not always +// return the same resource. +func (c *FooClient) Connection() *grpc.ClientConn { + return c.internalClient.Connection() +} + +// Zip does some stuff. +func (c *FooClient) Zip(ctx context.Context, req *mypackagepb.Bar, opts ...gax.CallOption) (*mypackagepb.Foo, error) { + return c.internalClient.Zip(ctx, req, opts...) +} + +func (c *FooClient) ListLocations(ctx context.Context, req *locationpb.ListLocationsRequest, opts ...gax.CallOption) *LocationIterator { + return c.internalClient.ListLocations(ctx, req, opts...) +} + +func (c *FooClient) GetLocation(ctx context.Context, req *locationpb.GetLocationRequest, opts ...gax.CallOption) (*locationpb.Location, error) { + return c.internalClient.GetLocation(ctx, req, opts...) +} + +func (c *FooClient) SetIamPolicy(ctx context.Context, req *iampb.SetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) { + return c.internalClient.SetIamPolicy(ctx, req, opts...) +} + +func (c *FooClient) GetIamPolicy(ctx context.Context, req *iampb.GetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) { + return c.internalClient.GetIamPolicy(ctx, req, opts...) +} + +func (c *FooClient) TestIamPermissions(ctx context.Context, req *iampb.TestIamPermissionsRequest, opts ...gax.CallOption) (*iampb.TestIamPermissionsResponse, error) { + return c.internalClient.TestIamPermissions(ctx, req, opts...) +} + +// fooGRPCClient is a client for interacting with Awesome Foo API over gRPC transport. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type fooGRPCClient struct { + // Connection pool of gRPC connections to the service. + connPool gtransport.ConnPool + + // Points back to the CallOptions field of the containing FooClient + CallOptions **FooCallOptions + + // The gRPC API client. + fooClient mypackagepb.FooClient + + iamPolicyClient iampb.IAMPolicyClient + + locationsClient locationpb.LocationsClient + + // The x-goog-* metadata to be sent with each request. + xGoogHeaders []string + + logger *slog.Logger +} + +// NewFooClient creates a new foo client based on gRPC. +// The returned client must be Closed when it is done being used to clean up its underlying connections. +// +// Foo service does stuff. +func NewFooClient(ctx context.Context, opts ...option.ClientOption) (*FooClient, error) { + clientOpts := defaultFooGRPCClientOptions() + if gax.IsFeatureEnabled("TRACING") || gax.IsFeatureEnabled("LOGGING") { + clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{ + "gcp.client.service": "foo", + "gcp.client.version": getVersionClient(), + "gcp.client.repo": "googleapis/google-cloud-go", + "gcp.client.artifact": "path", + "gcp.client.language": "go", + "url.domain": "foo.googleapis.com", + })) + } + if newFooClientHook != nil { + hookOpts, err := newFooClientHook(ctx, clientHookParams{}) + if err != nil { + return nil, err + } + clientOpts = append(clientOpts, hookOpts...) + } + + connPool, err := gtransport.DialPool(ctx, append(clientOpts, opts...)...) + if err != nil { + return nil, err + } + client := FooClient{CallOptions: defaultFooCallOptions()} + + c := &fooGRPCClient{ + connPool: connPool, + fooClient: mypackagepb.NewFooClient(connPool), + CallOptions: &client.CallOptions, + logger: internaloption.GetLogger(opts), + iamPolicyClient: iampb.NewIAMPolicyClient(connPool), + locationsClient: locationpb.NewLocationsClient(connPool), + + } + c.setGoogleClientInfo() + + client.internalClient = c + + return &client, nil +} + +// Connection returns a connection to the API service. +// +// Deprecated: Connections are now pooled so this method does not always +// return the same resource. +func (c *fooGRPCClient) Connection() *grpc.ClientConn { + return c.connPool.Conn() +} + +// setGoogleClientInfo sets the name and version of the application in +// the `x-goog-api-client` header passed on each request. Intended for +// use by Google-written clients. +func (c *fooGRPCClient) setGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", gax.GoVersion}, keyval...) + kv = append(kv, "gapic", getVersionClient(), "gax", gax.Version, "grpc", grpc.Version, "pb", protoVersion) + c.xGoogHeaders = []string{ + "x-goog-api-client", gax.XGoogHeader(kv...), + "x-goog-api-version", "v1_20240425", + } +} + +// Close closes the connection to the API service. The user should invoke this when +// the client is no longer required. +func (c *fooGRPCClient) Close() error { + return c.connPool.Close() +} + diff --git a/internal/gengapic/testdata/foo_client_init_metrics.want b/internal/gengapic/testdata/foo_client_init_metrics.want new file mode 100644 index 0000000000..5710fabb39 --- /dev/null +++ b/internal/gengapic/testdata/foo_client_init_metrics.want @@ -0,0 +1,180 @@ +// internalFooClient is an interface that defines the methods available from Awesome Foo API. +type internalFooClient interface { + Close() error + setGoogleClientInfo(...string) + Connection() *grpc.ClientConn + Zip(context.Context, *mypackagepb.Bar, ...gax.CallOption) (*mypackagepb.Foo, error) + ListLocations(context.Context, *locationpb.ListLocationsRequest, ...gax.CallOption) *LocationIterator + GetLocation(context.Context, *locationpb.GetLocationRequest, ...gax.CallOption) (*locationpb.Location, error) + SetIamPolicy(context.Context, *iampb.SetIamPolicyRequest, ...gax.CallOption) (*iampb.Policy, error) + GetIamPolicy(context.Context, *iampb.GetIamPolicyRequest, ...gax.CallOption) (*iampb.Policy, error) + TestIamPermissions(context.Context, *iampb.TestIamPermissionsRequest, ...gax.CallOption) (*iampb.TestIamPermissionsResponse, error) +} + +// FooClient is a client for interacting with Awesome Foo API. +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +// +// Foo service does stuff. +// +// This client uses Foo version v1_20240425. +type FooClient struct { + // The internal transport-dependent client. + internalClient internalFooClient + + // The call options for this service. + CallOptions *FooCallOptions + +} + +// Wrapper methods routed to the internal client. + +// Close closes the connection to the API service. The user should invoke this when +// the client is no longer required. +func (c *FooClient) Close() error { + return c.internalClient.Close() +} + +// setGoogleClientInfo sets the name and version of the application in +// the `x-goog-api-client` header passed on each request. Intended for +// use by Google-written clients. +func (c *FooClient) setGoogleClientInfo(keyval ...string) { + c.internalClient.setGoogleClientInfo(keyval...) +} + +// Connection returns a connection to the API service. +// +// Deprecated: Connections are now pooled so this method does not always +// return the same resource. +func (c *FooClient) Connection() *grpc.ClientConn { + return c.internalClient.Connection() +} + +// Zip does some stuff. +func (c *FooClient) Zip(ctx context.Context, req *mypackagepb.Bar, opts ...gax.CallOption) (*mypackagepb.Foo, error) { + return c.internalClient.Zip(ctx, req, opts...) +} + +func (c *FooClient) ListLocations(ctx context.Context, req *locationpb.ListLocationsRequest, opts ...gax.CallOption) *LocationIterator { + return c.internalClient.ListLocations(ctx, req, opts...) +} + +func (c *FooClient) GetLocation(ctx context.Context, req *locationpb.GetLocationRequest, opts ...gax.CallOption) (*locationpb.Location, error) { + return c.internalClient.GetLocation(ctx, req, opts...) +} + +func (c *FooClient) SetIamPolicy(ctx context.Context, req *iampb.SetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) { + return c.internalClient.SetIamPolicy(ctx, req, opts...) +} + +func (c *FooClient) GetIamPolicy(ctx context.Context, req *iampb.GetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) { + return c.internalClient.GetIamPolicy(ctx, req, opts...) +} + +func (c *FooClient) TestIamPermissions(ctx context.Context, req *iampb.TestIamPermissionsRequest, opts ...gax.CallOption) (*iampb.TestIamPermissionsResponse, error) { + return c.internalClient.TestIamPermissions(ctx, req, opts...) +} + +// fooGRPCClient is a client for interacting with Awesome Foo API over gRPC transport. +// +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type fooGRPCClient struct { + // Connection pool of gRPC connections to the service. + connPool gtransport.ConnPool + + // Points back to the CallOptions field of the containing FooClient + CallOptions **FooCallOptions + + // The gRPC API client. + fooClient mypackagepb.FooClient + + iamPolicyClient iampb.IAMPolicyClient + + locationsClient locationpb.LocationsClient + + // The x-goog-* metadata to be sent with each request. + xGoogHeaders []string + + logger *slog.Logger +} + +// NewFooClient creates a new foo client based on gRPC. +// The returned client must be Closed when it is done being used to clean up its underlying connections. +// +// Foo service does stuff. +func NewFooClient(ctx context.Context, opts ...option.ClientOption) (*FooClient, error) { + clientOpts := defaultFooGRPCClientOptions() + if newFooClientHook != nil { + hookOpts, err := newFooClientHook(ctx, clientHookParams{}) + if err != nil { + return nil, err + } + clientOpts = append(clientOpts, hookOpts...) + } + + connPool, err := gtransport.DialPool(ctx, append(clientOpts, opts...)...) + if err != nil { + return nil, err + } + client := FooClient{CallOptions: defaultFooCallOptions()} + + c := &fooGRPCClient{ + connPool: connPool, + fooClient: mypackagepb.NewFooClient(connPool), + CallOptions: &client.CallOptions, + logger: internaloption.GetLogger(opts), + iamPolicyClient: iampb.NewIAMPolicyClient(connPool), + locationsClient: locationpb.NewLocationsClient(connPool), + + } + c.setGoogleClientInfo() + if gax.IsFeatureEnabled("METRICS") { + metrics := gax.NewClientMetrics( + gax.WithTelemetryLogger(c.logger), + gax.WithTelemetryAttributes(map[string]string{ + gax.ClientService: "foo", + gax.ClientVersion: getVersionClient(), + gax.ClientArtifact: "path", + gax.RPCSystem: "grpc", + gax.URLDomain: "foo.googleapis.com", + }), + ) + + client.CallOptions.Zip = append(client.CallOptions.Zip, gax.WithClientMetrics(metrics)) + client.CallOptions.ListLocations = append(client.CallOptions.ListLocations, gax.WithClientMetrics(metrics)) + client.CallOptions.GetLocation = append(client.CallOptions.GetLocation, gax.WithClientMetrics(metrics)) + client.CallOptions.SetIamPolicy = append(client.CallOptions.SetIamPolicy, gax.WithClientMetrics(metrics)) + client.CallOptions.GetIamPolicy = append(client.CallOptions.GetIamPolicy, gax.WithClientMetrics(metrics)) + client.CallOptions.TestIamPermissions = append(client.CallOptions.TestIamPermissions, gax.WithClientMetrics(metrics)) + } + + client.internalClient = c + + return &client, nil +} + +// Connection returns a connection to the API service. +// +// Deprecated: Connections are now pooled so this method does not always +// return the same resource. +func (c *fooGRPCClient) Connection() *grpc.ClientConn { + return c.connPool.Conn() +} + +// setGoogleClientInfo sets the name and version of the application in +// the `x-goog-api-client` header passed on each request. Intended for +// use by Google-written clients. +func (c *fooGRPCClient) setGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", gax.GoVersion}, keyval...) + kv = append(kv, "gapic", getVersionClient(), "gax", gax.Version, "grpc", grpc.Version, "pb", protoVersion) + c.xGoogHeaders = []string{ + "x-goog-api-client", gax.XGoogHeader(kv...), + "x-goog-api-version", "v1_20240425", + } +} + +// Close closes the connection to the API service. The user should invoke this when +// the client is no longer required. +func (c *fooGRPCClient) Close() error { + return c.connPool.Close() +} + diff --git a/internal/gengapic/testdata/foo_rest_client_init_default.want b/internal/gengapic/testdata/foo_rest_client_init_default.want new file mode 100644 index 0000000000..a50a98942f --- /dev/null +++ b/internal/gengapic/testdata/foo_rest_client_init_default.want @@ -0,0 +1,141 @@ +// internalFooClient is an interface that defines the methods available from Awesome Foo API. +type internalFooClient interface { + Close() error + setGoogleClientInfo(...string) + Connection() *grpc.ClientConn + Zip(context.Context, *mypackagepb.Bar, ...gax.CallOption) (*mypackagepb.Foo, error) + ListLocations(context.Context, *locationpb.ListLocationsRequest, ...gax.CallOption) *LocationIterator + GetLocation(context.Context, *locationpb.GetLocationRequest, ...gax.CallOption) (*locationpb.Location, error) + SetIamPolicy(context.Context, *iampb.SetIamPolicyRequest, ...gax.CallOption) (*iampb.Policy, error) + GetIamPolicy(context.Context, *iampb.GetIamPolicyRequest, ...gax.CallOption) (*iampb.Policy, error) + TestIamPermissions(context.Context, *iampb.TestIamPermissionsRequest, ...gax.CallOption) (*iampb.TestIamPermissionsResponse, error) +} + +// FooClient is a client for interacting with Awesome Foo API. +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +// +// Foo service does stuff. +// +// This client uses Foo version v1_20240425. +type FooClient struct { + // The internal transport-dependent client. + internalClient internalFooClient + + // The call options for this service. + CallOptions *FooCallOptions + +} + +// Wrapper methods routed to the internal client. + +// Close closes the connection to the API service. The user should invoke this when +// the client is no longer required. +func (c *FooClient) Close() error { + return c.internalClient.Close() +} + +// setGoogleClientInfo sets the name and version of the application in +// the `x-goog-api-client` header passed on each request. Intended for +// use by Google-written clients. +func (c *FooClient) setGoogleClientInfo(keyval ...string) { + c.internalClient.setGoogleClientInfo(keyval...) +} + +// Connection returns a connection to the API service. +// +// Deprecated: Connections are now pooled so this method does not always +// return the same resource. +func (c *FooClient) Connection() *grpc.ClientConn { + return c.internalClient.Connection() +} + +// Zip does some stuff. +func (c *FooClient) Zip(ctx context.Context, req *mypackagepb.Bar, opts ...gax.CallOption) (*mypackagepb.Foo, error) { + return c.internalClient.Zip(ctx, req, opts...) +} + +func (c *FooClient) ListLocations(ctx context.Context, req *locationpb.ListLocationsRequest, opts ...gax.CallOption) *LocationIterator { + return c.internalClient.ListLocations(ctx, req, opts...) +} + +func (c *FooClient) GetLocation(ctx context.Context, req *locationpb.GetLocationRequest, opts ...gax.CallOption) (*locationpb.Location, error) { + return c.internalClient.GetLocation(ctx, req, opts...) +} + +func (c *FooClient) SetIamPolicy(ctx context.Context, req *iampb.SetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) { + return c.internalClient.SetIamPolicy(ctx, req, opts...) +} + +func (c *FooClient) GetIamPolicy(ctx context.Context, req *iampb.GetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) { + return c.internalClient.GetIamPolicy(ctx, req, opts...) +} + +func (c *FooClient) TestIamPermissions(ctx context.Context, req *iampb.TestIamPermissionsRequest, opts ...gax.CallOption) (*iampb.TestIamPermissionsResponse, error) { + return c.internalClient.TestIamPermissions(ctx, req, opts...) +} + +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type fooRESTClient struct { + // The http endpoint to connect to. + endpoint string + + // The http client. + httpClient *http.Client + + // The x-goog-* headers to be sent with each request. + xGoogHeaders []string + + // Points back to the CallOptions field of the containing FooClient + CallOptions **FooCallOptions + + logger *slog.Logger +} + +// NewFooRESTClient creates a new foo rest client. +// +// Foo service does stuff. +func NewFooRESTClient(ctx context.Context, opts ...option.ClientOption) (*FooClient, error) { + clientOpts := append(defaultFooRESTClientOptions(), opts...) + httpClient, endpoint, err := httptransport.NewClient(ctx, clientOpts...) + if err != nil { + return nil, err + } + + callOpts := defaultFooRESTCallOptions() + c := &fooRESTClient{ + endpoint: endpoint, + httpClient: httpClient, + CallOptions: &callOpts, + logger: internaloption.GetLogger(opts), + } + c.setGoogleClientInfo() + + return &FooClient{internalClient: c, CallOptions: callOpts}, nil +} + +// setGoogleClientInfo sets the name and version of the application in +// the `x-goog-api-client` header passed on each request. Intended for +// use by Google-written clients. +func (c *fooRESTClient) setGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", gax.GoVersion}, keyval...) + kv = append(kv, "gapic", getVersionClient(), "gax", gax.Version, "rest", "UNKNOWN", "pb", protoVersion) + c.xGoogHeaders = []string{ + "x-goog-api-client", gax.XGoogHeader(kv...), + "x-goog-api-version", "v1_20240425", + } +} + +// Close closes the connection to the API service. The user should invoke this when +// the client is no longer required. +func (c *fooRESTClient) Close() error { + // Replace httpClient with nil to force cleanup. + c.httpClient = nil + return nil +} + +// Connection returns a connection to the API service. +// +// Deprecated: This method always returns nil. +func (c *fooRESTClient) Connection() *grpc.ClientConn { + return nil +} diff --git a/internal/gengapic/testdata/foo_rest_client_init_metrics.want b/internal/gengapic/testdata/foo_rest_client_init_metrics.want new file mode 100644 index 0000000000..a0c57e4527 --- /dev/null +++ b/internal/gengapic/testdata/foo_rest_client_init_metrics.want @@ -0,0 +1,171 @@ +// internalFooClient is an interface that defines the methods available from Awesome Foo API. +type internalFooClient interface { + Close() error + setGoogleClientInfo(...string) + Connection() *grpc.ClientConn + Zip(context.Context, *mypackagepb.Bar, ...gax.CallOption) (*mypackagepb.Foo, error) + ListLocations(context.Context, *locationpb.ListLocationsRequest, ...gax.CallOption) *LocationIterator + GetLocation(context.Context, *locationpb.GetLocationRequest, ...gax.CallOption) (*locationpb.Location, error) + SetIamPolicy(context.Context, *iampb.SetIamPolicyRequest, ...gax.CallOption) (*iampb.Policy, error) + GetIamPolicy(context.Context, *iampb.GetIamPolicyRequest, ...gax.CallOption) (*iampb.Policy, error) + TestIamPermissions(context.Context, *iampb.TestIamPermissionsRequest, ...gax.CallOption) (*iampb.TestIamPermissionsResponse, error) +} + +// FooClient is a client for interacting with Awesome Foo API. +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +// +// Foo service does stuff. +// +// This client uses Foo version v1_20240425. +type FooClient struct { + // The internal transport-dependent client. + internalClient internalFooClient + + // The call options for this service. + CallOptions *FooCallOptions + +} + +// Wrapper methods routed to the internal client. + +// Close closes the connection to the API service. The user should invoke this when +// the client is no longer required. +func (c *FooClient) Close() error { + return c.internalClient.Close() +} + +// setGoogleClientInfo sets the name and version of the application in +// the `x-goog-api-client` header passed on each request. Intended for +// use by Google-written clients. +func (c *FooClient) setGoogleClientInfo(keyval ...string) { + c.internalClient.setGoogleClientInfo(keyval...) +} + +// Connection returns a connection to the API service. +// +// Deprecated: Connections are now pooled so this method does not always +// return the same resource. +func (c *FooClient) Connection() *grpc.ClientConn { + return c.internalClient.Connection() +} + +// Zip does some stuff. +func (c *FooClient) Zip(ctx context.Context, req *mypackagepb.Bar, opts ...gax.CallOption) (*mypackagepb.Foo, error) { + return c.internalClient.Zip(ctx, req, opts...) +} + +func (c *FooClient) ListLocations(ctx context.Context, req *locationpb.ListLocationsRequest, opts ...gax.CallOption) *LocationIterator { + return c.internalClient.ListLocations(ctx, req, opts...) +} + +func (c *FooClient) GetLocation(ctx context.Context, req *locationpb.GetLocationRequest, opts ...gax.CallOption) (*locationpb.Location, error) { + return c.internalClient.GetLocation(ctx, req, opts...) +} + +func (c *FooClient) SetIamPolicy(ctx context.Context, req *iampb.SetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) { + return c.internalClient.SetIamPolicy(ctx, req, opts...) +} + +func (c *FooClient) GetIamPolicy(ctx context.Context, req *iampb.GetIamPolicyRequest, opts ...gax.CallOption) (*iampb.Policy, error) { + return c.internalClient.GetIamPolicy(ctx, req, opts...) +} + +func (c *FooClient) TestIamPermissions(ctx context.Context, req *iampb.TestIamPermissionsRequest, opts ...gax.CallOption) (*iampb.TestIamPermissionsResponse, error) { + return c.internalClient.TestIamPermissions(ctx, req, opts...) +} + +// Methods, except Close, may be called concurrently. However, fields must not be modified concurrently with method calls. +type fooRESTClient struct { + // The http endpoint to connect to. + endpoint string + + // The http client. + httpClient *http.Client + + // The x-goog-* headers to be sent with each request. + xGoogHeaders []string + + // Points back to the CallOptions field of the containing FooClient + CallOptions **FooCallOptions + + logger *slog.Logger +} + +// NewFooRESTClient creates a new foo rest client. +// +// Foo service does stuff. +func NewFooRESTClient(ctx context.Context, opts ...option.ClientOption) (*FooClient, error) { + clientOpts := append(defaultFooRESTClientOptions(), opts...) + if gax.IsFeatureEnabled("TRACING") || gax.IsFeatureEnabled("LOGGING") { + clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{ + "gcp.client.service": "foo", + "gcp.client.version": getVersionClient(), + "gcp.client.repo": "googleapis/google-cloud-go", + "gcp.client.artifact": "path", + "gcp.client.language": "go", + "url.domain": "foo.googleapis.com", + })) + } + httpClient, endpoint, err := httptransport.NewClient(ctx, clientOpts...) + if err != nil { + return nil, err + } + + callOpts := defaultFooRESTCallOptions() + c := &fooRESTClient{ + endpoint: endpoint, + httpClient: httpClient, + CallOptions: &callOpts, + logger: internaloption.GetLogger(opts), + } + c.setGoogleClientInfo() + + if gax.IsFeatureEnabled("METRICS") { + metrics := gax.NewClientMetrics( + gax.WithTelemetryLogger(c.logger), + gax.WithTelemetryAttributes(map[string]string{ + gax.ClientService: "foo", + gax.ClientVersion: getVersionClient(), + gax.ClientArtifact: "path", + gax.RPCSystem: "http", + gax.URLDomain: "foo.googleapis.com", + }), + ) + + callOpts.Zip = append(callOpts.Zip, gax.WithClientMetrics(metrics)) + callOpts.ListLocations = append(callOpts.ListLocations, gax.WithClientMetrics(metrics)) + callOpts.GetLocation = append(callOpts.GetLocation, gax.WithClientMetrics(metrics)) + callOpts.SetIamPolicy = append(callOpts.SetIamPolicy, gax.WithClientMetrics(metrics)) + callOpts.GetIamPolicy = append(callOpts.GetIamPolicy, gax.WithClientMetrics(metrics)) + callOpts.TestIamPermissions = append(callOpts.TestIamPermissions, gax.WithClientMetrics(metrics)) + } + + return &FooClient{internalClient: c, CallOptions: callOpts}, nil +} + +// setGoogleClientInfo sets the name and version of the application in +// the `x-goog-api-client` header passed on each request. Intended for +// use by Google-written clients. +func (c *fooRESTClient) setGoogleClientInfo(keyval ...string) { + kv := append([]string{"gl-go", gax.GoVersion}, keyval...) + kv = append(kv, "gapic", getVersionClient(), "gax", gax.Version, "rest", "UNKNOWN", "pb", protoVersion) + c.xGoogHeaders = []string{ + "x-goog-api-client", gax.XGoogHeader(kv...), + "x-goog-api-version", "v1_20240425", + } +} + +// Close closes the connection to the API service. The user should invoke this when +// the client is no longer required. +func (c *fooRESTClient) Close() error { + // Replace httpClient with nil to force cleanup. + c.httpClient = nil + return nil +} + +// Connection returns a connection to the API service. +// +// Deprecated: This method always returns nil. +func (c *fooRESTClient) Connection() *grpc.ClientConn { + return nil +} diff --git a/internal/gengapic/testdata/method_BidiThings.want b/internal/gengapic/testdata/method_BidiThings.want index de93bbef02..50e42c439e 100644 --- a/internal/gengapic/testdata/method_BidiThings.want +++ b/internal/gengapic/testdata/method_BidiThings.want @@ -1,5 +1,8 @@ func (c *fooGRPCClient) BidiThings(ctx context.Context, opts ...gax.CallOption) (mypackagepb.Foo_BidiThingsClient, error) { ctx = gax.InsertMetadataIntoOutgoingContext(ctx, c.xGoogHeaders...) + if gax.IsFeatureEnabled("METRICS") || gax.IsFeatureEnabled("TRACING") { + ctx = callctx.WithTelemetryContext(ctx, "rpc_method", "my.pkg.Foo/BidiThings") + } var resp mypackagepb.Foo_BidiThingsClient 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 { diff --git a/internal/gengapic/testdata/method_ClientThings.want b/internal/gengapic/testdata/method_ClientThings.want index ac016d4cd3..cf9c4af2c8 100644 --- a/internal/gengapic/testdata/method_ClientThings.want +++ b/internal/gengapic/testdata/method_ClientThings.want @@ -1,5 +1,8 @@ func (c *fooGRPCClient) ClientThings(ctx context.Context, opts ...gax.CallOption) (mypackagepb.Foo_ClientThingsClient, error) { ctx = gax.InsertMetadataIntoOutgoingContext(ctx, c.xGoogHeaders...) + if gax.IsFeatureEnabled("METRICS") || gax.IsFeatureEnabled("TRACING") { + ctx = callctx.WithTelemetryContext(ctx, "rpc_method", "my.pkg.Foo/ClientThings") + } var resp mypackagepb.Foo_ClientThingsClient 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 { diff --git a/internal/gengapic/testdata/method_GetAnotherThing.want b/internal/gengapic/testdata/method_GetAnotherThing.want index e39a017bf4..ac45d36831 100644 --- a/internal/gengapic/testdata/method_GetAnotherThing.want +++ b/internal/gengapic/testdata/method_GetAnotherThing.want @@ -33,6 +33,9 @@ func (c *fooGRPCClient) GetAnotherThing(ctx context.Context, req *mypackagepb.In if gax.IsFeatureEnabled("TRACING") || gax.IsFeatureEnabled("LOGGING") { ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", fmt.Sprintf("//foo.googleapis.com/%v", req.GetOther())) } + if gax.IsFeatureEnabled("METRICS") || gax.IsFeatureEnabled("TRACING") { + ctx = callctx.WithTelemetryContext(ctx, "rpc_method", "my.pkg.Foo/GetAnotherThing") + } opts = append((*c.CallOptions).GetAnotherThing[0:len((*c.CallOptions).GetAnotherThing):len((*c.CallOptions).GetAnotherThing)], opts...) var resp *mypackagepb.OutputType err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { diff --git a/internal/gengapic/testdata/method_GetEmptyThing.want b/internal/gengapic/testdata/method_GetEmptyThing.want index 0014ac7ffc..e63c07d331 100644 --- a/internal/gengapic/testdata/method_GetEmptyThing.want +++ b/internal/gengapic/testdata/method_GetEmptyThing.want @@ -6,6 +6,9 @@ func (c *fooGRPCClient) GetEmptyThing(ctx context.Context, req *mypackagepb.Inpu if gax.IsFeatureEnabled("TRACING") || gax.IsFeatureEnabled("LOGGING") { ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", fmt.Sprintf("//foo.googleapis.com/%v", req.GetOther())) } + if gax.IsFeatureEnabled("METRICS") || gax.IsFeatureEnabled("TRACING") { + ctx = callctx.WithTelemetryContext(ctx, "rpc_method", "my.pkg.Foo/GetEmptyThing") + } if req != nil && req.RequestId == nil { req.RequestId = proto.String(uuid.NewString()) } diff --git a/internal/gengapic/testdata/method_GetManyOtherThings.want b/internal/gengapic/testdata/method_GetManyOtherThings.want index fd80fe5f80..ba1b0d5b9e 100644 --- a/internal/gengapic/testdata/method_GetManyOtherThings.want +++ b/internal/gengapic/testdata/method_GetManyOtherThings.want @@ -1,5 +1,8 @@ func (c *fooGRPCClient) GetManyOtherThings(ctx context.Context, req *mypackagepb.PageInputType, opts ...gax.CallOption) *StringIterator { ctx = gax.InsertMetadataIntoOutgoingContext(ctx, c.xGoogHeaders...) + if gax.IsFeatureEnabled("METRICS") || gax.IsFeatureEnabled("TRACING") { + ctx = callctx.WithTelemetryContext(ctx, "rpc_method", "my.pkg.Foo/GetManyOtherThings") + } opts = append((*c.CallOptions).GetManyOtherThings[0:len((*c.CallOptions).GetManyOtherThings):len((*c.CallOptions).GetManyOtherThings)], opts...) it := &StringIterator{} req = proto.Clone(req).(*mypackagepb.PageInputType) diff --git a/internal/gengapic/testdata/method_GetManyThings.want b/internal/gengapic/testdata/method_GetManyThings.want index a0b27029f0..ffaa4b47c2 100644 --- a/internal/gengapic/testdata/method_GetManyThings.want +++ b/internal/gengapic/testdata/method_GetManyThings.want @@ -6,6 +6,9 @@ func (c *fooGRPCClient) GetManyThings(ctx context.Context, req *mypackagepb.Page if gax.IsFeatureEnabled("TRACING") || gax.IsFeatureEnabled("LOGGING") { ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", fmt.Sprintf("//foo.googleapis.com/%v", req.GetOther())) } + if gax.IsFeatureEnabled("METRICS") || gax.IsFeatureEnabled("TRACING") { + ctx = callctx.WithTelemetryContext(ctx, "rpc_method", "my.pkg.Foo/GetManyThings") + } opts = append((*c.CallOptions).GetManyThings[0:len((*c.CallOptions).GetManyThings):len((*c.CallOptions).GetManyThings)], opts...) it := &StringIterator{} req = proto.Clone(req).(*mypackagepb.PageInputType) diff --git a/internal/gengapic/testdata/method_GetManyThingsOptional.want b/internal/gengapic/testdata/method_GetManyThingsOptional.want index d77d7199ca..3bcbd51e36 100644 --- a/internal/gengapic/testdata/method_GetManyThingsOptional.want +++ b/internal/gengapic/testdata/method_GetManyThingsOptional.want @@ -6,6 +6,9 @@ func (c *fooGRPCClient) GetManyThingsOptional(ctx context.Context, req *mypackag if gax.IsFeatureEnabled("TRACING") || gax.IsFeatureEnabled("LOGGING") { ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", fmt.Sprintf("//foo.googleapis.com/%v", req.GetOther())) } + if gax.IsFeatureEnabled("METRICS") || gax.IsFeatureEnabled("TRACING") { + ctx = callctx.WithTelemetryContext(ctx, "rpc_method", "my.pkg.Foo/GetManyThingsOptional") + } opts = append((*c.CallOptions).GetManyThingsOptional[0:len((*c.CallOptions).GetManyThingsOptional):len((*c.CallOptions).GetManyThingsOptional)], opts...) it := &StringIterator{} req = proto.Clone(req).(*mypackagepb.PageInputTypeOptional) diff --git a/internal/gengapic/testdata/method_GetOneThing.want b/internal/gengapic/testdata/method_GetOneThing.want index df8b4891d4..72fae0c689 100644 --- a/internal/gengapic/testdata/method_GetOneThing.want +++ b/internal/gengapic/testdata/method_GetOneThing.want @@ -6,6 +6,9 @@ func (c *fooGRPCClient) GetOneThing(ctx context.Context, req *mypackagepb.InputT if gax.IsFeatureEnabled("TRACING") || gax.IsFeatureEnabled("LOGGING") { ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", fmt.Sprintf("//foo.googleapis.com/%v", req.GetOther())) } + if gax.IsFeatureEnabled("METRICS") || gax.IsFeatureEnabled("TRACING") { + ctx = callctx.WithTelemetryContext(ctx, "rpc_method", "my.pkg.Foo/GetOneThing") + } if req != nil && req.RequestId == nil { req.RequestId = proto.String(uuid.NewString()) } diff --git a/internal/gengapic/testdata/method_ServerThings.want b/internal/gengapic/testdata/method_ServerThings.want index fe2bc382cb..6614925971 100644 --- a/internal/gengapic/testdata/method_ServerThings.want +++ b/internal/gengapic/testdata/method_ServerThings.want @@ -6,6 +6,9 @@ func (c *fooGRPCClient) ServerThings(ctx context.Context, req *mypackagepb.Input if gax.IsFeatureEnabled("TRACING") || gax.IsFeatureEnabled("LOGGING") { ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", fmt.Sprintf("//foo.googleapis.com/%v", req.GetOther())) } + if gax.IsFeatureEnabled("METRICS") || gax.IsFeatureEnabled("TRACING") { + ctx = callctx.WithTelemetryContext(ctx, "rpc_method", "my.pkg.Foo/ServerThings") + } opts = append((*c.CallOptions).ServerThings[0:len((*c.CallOptions).ServerThings):len((*c.CallOptions).ServerThings)], opts...) var resp mypackagepb.Foo_ServerThingsClient err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { diff --git a/internal/gengapic/testdata/metrics_instrumentation.want b/internal/gengapic/testdata/metrics_instrumentation.want new file mode 100644 index 0000000000..fd1614c943 --- /dev/null +++ b/internal/gengapic/testdata/metrics_instrumentation.want @@ -0,0 +1,14 @@ +func (c *fooGRPCClient) Baz(ctx context.Context, req *mypackagepb.InputType, opts ...gax.CallOption) error { + ctx = gax.InsertMetadataIntoOutgoingContext(ctx, c.xGoogHeaders...) + if gax.IsFeatureEnabled("METRICS") || gax.IsFeatureEnabled("TRACING") { + ctx = callctx.WithTelemetryContext(ctx, "rpc_method", "my.pkg.Foo/Baz") + } + opts = append((*c.CallOptions).Baz[0:len((*c.CallOptions).Baz):len((*c.CallOptions).Baz)], opts...) + err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { + var err error + _, err = executeRPC(ctx, c.fooClient.Baz, req, settings.GRPC, c.logger, "Baz") + return err + }, opts...) + return err +} + diff --git a/internal/gengapic/testdata/rest_CustomOp.want b/internal/gengapic/testdata/rest_CustomOp.want index e6889bb495..3149a121e4 100644 --- a/internal/gengapic/testdata/rest_CustomOp.want +++ b/internal/gengapic/testdata/rest_CustomOp.want @@ -19,6 +19,10 @@ func (c *fooRESTClient) CustomOp(ctx context.Context, req *foopb.Foo, opts ...ga // Build HTTP headers from client and context metadata. hds := append(c.xGoogHeaders, "Content-Type", "application/json") headers := gax.BuildHeaders(ctx, hds...) + if gax.IsFeatureEnabled("METRICS") || gax.IsFeatureEnabled("TRACING") { + ctx = callctx.WithTelemetryContext(ctx, "rpc_method", "google.cloud.foo.v1.FooService/CustomOp") + ctx = callctx.WithTelemetryContext(ctx, "url_template", "/v1/foo") + } if gax.IsFeatureEnabled("TRACING") || gax.IsFeatureEnabled("LOGGING") { ctx = metadata.AppendToOutgoingContext(ctx, "url.template", "/v1/foo") } diff --git a/internal/gengapic/testdata/rest_EmptyRPC.want b/internal/gengapic/testdata/rest_EmptyRPC.want index 30080a0020..0a4169299f 100644 --- a/internal/gengapic/testdata/rest_EmptyRPC.want +++ b/internal/gengapic/testdata/rest_EmptyRPC.want @@ -25,6 +25,10 @@ func (c *fooRESTClient) EmptyRPC(ctx context.Context, req *foopb.Foo, opts ...ga if gax.IsFeatureEnabled("TRACING") || gax.IsFeatureEnabled("LOGGING") { ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", fmt.Sprintf("//foo.googleapis.com/%v", req.GetOther())) } + if gax.IsFeatureEnabled("METRICS") || gax.IsFeatureEnabled("TRACING") { + ctx = callctx.WithTelemetryContext(ctx, "rpc_method", "google.cloud.foo.v1.FooService/EmptyRPC") + ctx = callctx.WithTelemetryContext(ctx, "url_template", "/v1/foo/{other=*}") + } if gax.IsFeatureEnabled("TRACING") || gax.IsFeatureEnabled("LOGGING") { ctx = metadata.AppendToOutgoingContext(ctx, "url.template", "/v1/foo/{other=*}") } diff --git a/internal/gengapic/testdata/rest_HttpBodyRPC.want b/internal/gengapic/testdata/rest_HttpBodyRPC.want index c94b140f63..6da8c0df42 100644 --- a/internal/gengapic/testdata/rest_HttpBodyRPC.want +++ b/internal/gengapic/testdata/rest_HttpBodyRPC.want @@ -29,6 +29,10 @@ func (c *fooRESTClient) HttpBodyRPC(ctx context.Context, req *foopb.Foo, opts .. if gax.IsFeatureEnabled("TRACING") || gax.IsFeatureEnabled("LOGGING") { ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", fmt.Sprintf("//foo.googleapis.com/%v", req.GetOther())) } + if gax.IsFeatureEnabled("METRICS") || gax.IsFeatureEnabled("TRACING") { + ctx = callctx.WithTelemetryContext(ctx, "rpc_method", "google.cloud.foo.v1.FooService/HttpBodyRPC") + ctx = callctx.WithTelemetryContext(ctx, "url_template", "/v1/foo") + } if gax.IsFeatureEnabled("TRACING") || gax.IsFeatureEnabled("LOGGING") { ctx = metadata.AppendToOutgoingContext(ctx, "url.template", "/v1/foo") } diff --git a/internal/gengapic/testdata/rest_LongrunningRPC.want b/internal/gengapic/testdata/rest_LongrunningRPC.want index b30b310c55..14ef6fd709 100644 --- a/internal/gengapic/testdata/rest_LongrunningRPC.want +++ b/internal/gengapic/testdata/rest_LongrunningRPC.want @@ -17,6 +17,10 @@ func (c *fooRESTClient) LongrunningRPC(ctx context.Context, req *foopb.Foo, opts // Build HTTP headers from client and context metadata. hds := append(c.xGoogHeaders, "Content-Type", "application/json") headers := gax.BuildHeaders(ctx, hds...) + if gax.IsFeatureEnabled("METRICS") || gax.IsFeatureEnabled("TRACING") { + ctx = callctx.WithTelemetryContext(ctx, "rpc_method", "google.cloud.foo.v1.FooService/LongrunningRPC") + ctx = callctx.WithTelemetryContext(ctx, "url_template", "/v1/foo") + } if gax.IsFeatureEnabled("TRACING") || gax.IsFeatureEnabled("LOGGING") { ctx = metadata.AppendToOutgoingContext(ctx, "url.template", "/v1/foo") } diff --git a/internal/gengapic/testdata/rest_ServerStreamRPC.want b/internal/gengapic/testdata/rest_ServerStreamRPC.want index 0ba41ed17b..a5e22dce20 100644 --- a/internal/gengapic/testdata/rest_ServerStreamRPC.want +++ b/internal/gengapic/testdata/rest_ServerStreamRPC.want @@ -29,6 +29,10 @@ func (c *fooRESTClient) ServerStreamRPC(ctx context.Context, req *foopb.Foo, opt if gax.IsFeatureEnabled("TRACING") || gax.IsFeatureEnabled("LOGGING") { ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", fmt.Sprintf("//foo.googleapis.com/%v", req.GetOther())) } + if gax.IsFeatureEnabled("METRICS") || gax.IsFeatureEnabled("TRACING") { + ctx = callctx.WithTelemetryContext(ctx, "rpc_method", "google.cloud.foo.v1.FooService/ServerStreamRPC") + ctx = callctx.WithTelemetryContext(ctx, "url_template", "/v1/foo") + } if gax.IsFeatureEnabled("TRACING") || gax.IsFeatureEnabled("LOGGING") { ctx = metadata.AppendToOutgoingContext(ctx, "url.template", "/v1/foo") } diff --git a/internal/gengapic/testdata/rest_UnaryRPC.want b/internal/gengapic/testdata/rest_UnaryRPC.want index fe493b9d1f..4704ff2bad 100644 --- a/internal/gengapic/testdata/rest_UnaryRPC.want +++ b/internal/gengapic/testdata/rest_UnaryRPC.want @@ -37,6 +37,10 @@ func (c *fooRESTClient) UnaryRPC(ctx context.Context, req *foopb.Foo, opts ...ga if gax.IsFeatureEnabled("TRACING") || gax.IsFeatureEnabled("LOGGING") { ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", fmt.Sprintf("//foo.googleapis.com/%v", req.GetOther())) } + if gax.IsFeatureEnabled("METRICS") || gax.IsFeatureEnabled("TRACING") { + ctx = callctx.WithTelemetryContext(ctx, "rpc_method", "google.cloud.foo.v1.FooService/UnaryRPC") + ctx = callctx.WithTelemetryContext(ctx, "url_template", "/v1/foo") + } if gax.IsFeatureEnabled("TRACING") || gax.IsFeatureEnabled("LOGGING") { ctx = metadata.AppendToOutgoingContext(ctx, "url.template", "/v1/foo") } diff --git a/internal/gengapic/testdata/rest_UpdateRPC.want b/internal/gengapic/testdata/rest_UpdateRPC.want index d9ad2fde21..f0e1deedb4 100644 --- a/internal/gengapic/testdata/rest_UpdateRPC.want +++ b/internal/gengapic/testdata/rest_UpdateRPC.want @@ -39,6 +39,10 @@ func (c *fooRESTClient) UpdateRPC(ctx context.Context, req *foopb.UpdateRequest, // Build HTTP headers from client and context metadata. hds := append(c.xGoogHeaders, "Content-Type", "application/json") headers := gax.BuildHeaders(ctx, hds...) + if gax.IsFeatureEnabled("METRICS") || gax.IsFeatureEnabled("TRACING") { + ctx = callctx.WithTelemetryContext(ctx, "rpc_method", "google.cloud.foo.v1.FooService/UpdateRPC") + ctx = callctx.WithTelemetryContext(ctx, "url_template", "/v1/foo") + } if gax.IsFeatureEnabled("TRACING") || gax.IsFeatureEnabled("LOGGING") { ctx = metadata.AppendToOutgoingContext(ctx, "url.template", "/v1/foo") } diff --git a/internal/gengapic/testdata/service_rename.want b/internal/gengapic/testdata/service_rename.want index 98803b0f58..82685d7d0a 100644 --- a/internal/gengapic/testdata/service_rename.want +++ b/internal/gengapic/testdata/service_rename.want @@ -86,6 +86,9 @@ func (c *barGRPCClient) Close() error { func (c *barGRPCClient) Baz(ctx context.Context, req *mypackagepb.InputType, opts ...gax.CallOption) error { ctx = gax.InsertMetadataIntoOutgoingContext(ctx, c.xGoogHeaders...) + if gax.IsFeatureEnabled("METRICS") || gax.IsFeatureEnabled("TRACING") { + ctx = callctx.WithTelemetryContext(ctx, "rpc_method", "my.pkg.Foo/Baz") + } opts = append((*c.CallOptions).Baz[0:len((*c.CallOptions).Baz):len((*c.CallOptions).Baz)], opts...) err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { var err error