diff --git a/internal/gengapic/client_init_test.go b/internal/gengapic/client_init_test.go index e370139415..f37efbaa79 100644 --- a/internal/gengapic/client_init_test.go +++ b/internal/gengapic/client_init_test.go @@ -137,7 +137,8 @@ func TestClientOpt(t *testing.T) { gRPCServiceConfig: grpcConf, // Showcase would enable MTLS if we went through legacy enablements, so add it explicitly here. featureEnablement: map[featureID]struct{}{ - MTLSHardBoundTokensFeature: struct{}{}, + MTLSHardBoundTokensFeature: struct{}{}, + OpenTelemetryTracingFeature: struct{}{}, }, }, } @@ -582,6 +583,7 @@ func TestClientInit(t *testing.T) { } g.mixins = tst.mixins g.cfg.APIServiceConfig = &serviceconfig.Service{ + Name: "foo.googleapis.com", Apis: []*apipb.Api{ {Name: "foo.bar.Baz"}, {Name: "google.iam.v1.IAMPolicy"}, @@ -600,6 +602,7 @@ func TestClientInit(t *testing.T) { } g.reset() + g.cfg.featureEnablement = map[featureID]struct{}{OpenTelemetryTracingFeature: {}} 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 59a3d765db..e45afe39e2 100644 --- a/internal/gengapic/feature.go +++ b/internal/gengapic/feature.go @@ -34,6 +34,7 @@ const ( WrapperTypesForPageSizeFeature featureID = "wrapper_types_for_page_size" OrderedRoutingHeadersFeature featureID = "ordered_routing_headers" MTLSHardBoundTokensFeature featureID = "mtls_hard_bound_tokens" + OpenTelemetryTracingFeature featureID = "open_telemetry_tracing" ) // featureRegistry contains the registry of defined features. @@ -41,6 +42,11 @@ const ( // must be registered to be enabled. This should not be modified at runtime. Those // who attempt to do so will be given a stern talking to. var featureRegistry = map[featureID]*featureInfo{ + + OpenTelemetryTracingFeature: { + Description: "Enable OpenTelemetry tracing support (Service Identity, Resource Names, URL Templates).", + TrackingID: "b/467342602,b/467403185", + }, MTLSHardBoundTokensFeature: { Description: "support MTLS hard bound tokens", TrackingID: "b/327916505", diff --git a/internal/gengapic/generator_test.go b/internal/gengapic/generator_test.go index 4a0c49196d..cd3eb54851 100644 --- a/internal/gengapic/generator_test.go +++ b/internal/gengapic/generator_test.go @@ -98,6 +98,7 @@ func TestAutoPopulatedFields(t *testing.T) { g.imports = map[pbinfo.ImportSpec]bool{} g.cfg.APIServiceConfig = &serviceconfig.Service{ + Name: "foo.googleapis.com", Publishing: &annotations.Publishing{ MethodSettings: []*annotations.MethodSettings{ { diff --git a/internal/gengapic/gengapic.go b/internal/gengapic/gengapic.go index 36fe3ee5fe..e124f36ed2 100644 --- a/internal/gengapic/gengapic.go +++ b/internal/gengapic/gengapic.go @@ -498,10 +498,62 @@ func (g *generator) insertRequestHeaders(m *descriptorpb.MethodDescriptorProto, case grpc: p("hds = append(c.xGoogHeaders, hds...)") p("ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)") + if g.featureEnabled(OpenTelemetryTracingFeature) { + resField := g.resourceNameField(m) + if resField != "" { + p("if gax.IsFeatureEnabled(\"TRACING\") {") + // For Standard APIs (AIP-122 compliant), for both gRPC and HTTP transports, + // the expression fieldGetter(resField) returns an accessor for the full + // canonical resource name (e.g., "projects/p/secrets/s"). For non-compliant + // APIs (missing the resource_reference annotation), an empty string is returned. + + // Prepend the service host if available + serv := g.descInfo.ParentElement[m].(*descriptorpb.ServiceDescriptorProto) + host := "" + if proto.HasExtension(serv.Options, annotations.E_DefaultHost) { + host = proto.GetExtension(serv.Options, annotations.E_DefaultHost).(string) + } + + if host != "" { + p(` ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", fmt.Sprintf("//%s/%%v", req%s))`, host, fieldGetter(resField)) + } else { + p(` ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", fmt.Sprintf("%%v", req%s))`, fieldGetter(resField)) + } + p("}") + g.imports[pbinfo.ImportSpec{Path: "google.golang.org/grpc/metadata"}] = true + g.imports[pbinfo.ImportSpec{Path: "fmt"}] = true + } + } case rest: p(`hds = append(c.xGoogHeaders, hds...)`) p(`hds = append(hds, "Content-Type", "application/json")`) p(`headers := gax.BuildHeaders(ctx, hds...)`) + if g.featureEnabled(OpenTelemetryTracingFeature) { + resField := g.resourceNameField(m) + if resField != "" { + p("if gax.IsFeatureEnabled(\"TRACING\") {") + // For Standard APIs (AIP-122 compliant), for both gRPC and HTTP transports, + // the expression fieldGetter(resField) returns an accessor for the full + // canonical resource name (e.g., "projects/p/secrets/s"). For non-compliant + // APIs (missing the resource_reference annotation), an empty string is returned. + + // Prepend the service host if available + serv := g.descInfo.ParentElement[m].(*descriptorpb.ServiceDescriptorProto) + host := "" + if proto.HasExtension(serv.Options, annotations.E_DefaultHost) { + host = proto.GetExtension(serv.Options, annotations.E_DefaultHost).(string) + } + + if host != "" { + p(` ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", fmt.Sprintf("//%s/%%v", req%s))`, host, fieldGetter(resField)) + } else { + p(` ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", fmt.Sprintf("%%v", req%s))`, fieldGetter(resField)) + } + p("}") + g.imports[pbinfo.ImportSpec{Path: "google.golang.org/grpc/metadata"}] = true + g.imports[pbinfo.ImportSpec{Path: "fmt"}] = true + } + } } g.imports[pbinfo.ImportSpec{Path: "fmt"}] = true g.imports[pbinfo.ImportSpec{Path: "net/url"}] = true @@ -816,3 +868,55 @@ func parseDynamicRequestHeaders(m *descriptorpb.MethodDescriptorProto) [][]strin return matches } + +// resourceNameField returns the name of the field in the input message +// that carries a google.api.resource_reference annotation. +// If multiple fields match, it prioritizes the one that also appears in the HTTP path. +// If no input type or associated attributes are found, it returns an empty string. +func (g *generator) resourceNameField(m *descriptorpb.MethodDescriptorProto) string { + if m.GetInputType() == "" { + return "" + } + inType := g.descInfo.Type[m.GetInputType()] + if inType == nil { + return "" + } + msg, ok := inType.(*descriptorpb.DescriptorProto) + if !ok { + return "" + } + + var candidates []string + for _, f := range msg.GetField() { + if proto.HasExtension(f.GetOptions(), annotations.E_ResourceReference) { + candidates = append(candidates, f.GetName()) + } + } + + if len(candidates) == 0 { + return "" + } + if len(candidates) == 1 { + return candidates[0] + } + + // Tie-breaking: check against HTTP path variables. + // parseImplicitRequestHeaders uses headerParamRegexp (which has one capturing + // group) to find variables in the path. h[1] corresponds to that capturing group + // and contains the variable's string name (e.g., "name", "parent" or "project"). + pathParams := make(map[string]bool) + headers := parseImplicitRequestHeaders(m) + for _, h := range headers { + if len(h) > 1 { + pathParams[h[1]] = true + } + } + + for _, c := range candidates { + if pathParams[c] { + return c + } + } + + return candidates[0] +} diff --git a/internal/gengapic/gengapic_test.go b/internal/gengapic/gengapic_test.go index 607dcf0b5e..ab55bec01b 100644 --- a/internal/gengapic/gengapic_test.go +++ b/internal/gengapic/gengapic_test.go @@ -153,13 +153,20 @@ func TestGenGRPCMethods(t *testing.T) { optsUUID4 := &descriptorpb.FieldOptions{} proto.SetExtension(optsUUID4, annotations.E_FieldInfo, &annotations.FieldInfo{Format: annotations.FieldInfo_UUID4}) + optsResRef := &descriptorpb.FieldOptions{} + proto.SetExtension(optsResRef, annotations.E_ResourceReference, &annotations.ResourceReference{ + // "{service}.googleapis.com/{Resource}" is the AIP-122 format for the google.api.resource_reference annotation. + Type: "foo.googleapis.com/Bar", + }) + inputType := &descriptorpb.DescriptorProto{ Name: proto.String("InputType"), Field: []*descriptorpb.FieldDescriptorProto{ { - Name: proto.String("other"), - Type: typep(descriptorpb.FieldDescriptorProto_TYPE_STRING), - Label: labelp(descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL), + Name: proto.String("other"), + Type: typep(descriptorpb.FieldDescriptorProto_TYPE_STRING), + Label: labelp(descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL), + Options: optsResRef, }, { Name: proto.String("another"), @@ -322,12 +329,17 @@ func TestGenGRPCMethods(t *testing.T) { }, } serv := &descriptorpb.ServiceDescriptorProto{ - Name: proto.String("Foo"), + Name: proto.String("Foo"), + Options: &descriptorpb.ServiceOptions{}, } + proto.SetExtension(serv.Options, annotations.E_DefaultHost, "foo.googleapis.com") var g generator g.cfg = &generatorConfig{ pkgName: "pkg", + featureEnablement: map[featureID]struct{}{ + OpenTelemetryTracingFeature: {}, + }, APIServiceConfig: &serviceconfig.Service{ Publishing: &annotations.Publishing{ MethodSettings: []*annotations.MethodSettings{ @@ -407,13 +419,13 @@ func TestGenGRPCMethods(t *testing.T) { Options: opts, }, imports: map[pbinfo.ImportSpec]bool{ - {Path: "fmt"}: true, - {Path: "github.com/google/uuid"}: 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, }, - }, - { + }, { m: &descriptorpb.MethodDescriptorProto{ Name: proto.String("GetOneThing"), InputType: proto.String(".my.pkg.InputType"), @@ -421,10 +433,11 @@ func TestGenGRPCMethods(t *testing.T) { Options: opts, }, imports: map[pbinfo.ImportSpec]bool{ - {Path: "fmt"}: true, - {Path: "github.com/google/uuid"}: 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, }, }, { @@ -437,12 +450,12 @@ func TestGenGRPCMethods(t *testing.T) { 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, }, - }, - { + }, { m: &descriptorpb.MethodDescriptorProto{ Name: proto.String("GetManyThingsOptional"), InputType: proto.String(".my.pkg.PageInputTypeOptional"), @@ -452,6 +465,7 @@ func TestGenGRPCMethods(t *testing.T) { 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, @@ -466,9 +480,10 @@ func TestGenGRPCMethods(t *testing.T) { Options: opts, }, imports: map[pbinfo.ImportSpec]bool{ - {Path: "fmt"}: true, - {Path: "net/url"}: true, - {Name: "mypackagepb", Path: "mypackage"}: true, + {Path: "fmt"}: true, + {Path: "google.golang.org/grpc/metadata"}: true, + {Path: "net/url"}: true, + {Name: "mypackagepb", Path: "mypackage"}: true, }, }, { @@ -505,11 +520,12 @@ func TestGenGRPCMethods(t *testing.T) { Options: optsGetAnotherThing, }, imports: map[pbinfo.ImportSpec]bool{ - {Path: "fmt"}: true, - {Path: "net/url"}: true, - {Path: "regexp"}: true, - {Path: "strings"}: true, - {Name: "mypackagepb", Path: "mypackage"}: true, + {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, }, }, // Test for empty dynamic routing annotation, so no headers should be sent. @@ -712,8 +728,10 @@ func TestGenOperationBuilders(t *testing.T) { }, } serv := &descriptorpb.ServiceDescriptorProto{ - Name: proto.String("Foo"), + Name: proto.String("Foo"), + Options: &descriptorpb.ServiceOptions{}, } + proto.SetExtension(serv.Options, annotations.E_DefaultHost, "foo.googleapis.com") var g generator g.imports = map[pbinfo.ImportSpec]bool{} diff --git a/internal/gengapic/gengrpc.go b/internal/gengapic/gengrpc.go index d0c35da4d7..d35d6a6592 100644 --- a/internal/gengapic/gengrpc.go +++ b/internal/gengapic/gengrpc.go @@ -329,6 +329,15 @@ func (g *generator) grpcClientUtilities(serv *descriptorpb.ServiceDescriptorProt g.serviceDoc(serv, false) // exclude API version docs p("func New%[1]sClient(ctx context.Context, opts ...option.ClientOption) (*%[1]sClient, error) {", servName) p(" clientOpts := default%[1]sGRPCClientOptions()", servName) + if g.featureEnabled(OpenTelemetryTracingFeature) { + p(" if gax.IsFeatureEnabled(\"TRACING\") {") + p(" clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{") + p(" \"gcp.client.service\": %q,", strings.Split(g.cfg.APIServiceConfig.GetName(), ".")[0]) + p(" \"gcp.client.version\": getVersionClient(),") + p(" \"gcp.client.repo\": \"googleapis/google-cloud-go\",") + p(" }))") + p(" }") + } p(" if new%sClientHook != nil {", servName) p(" hookOpts, err := new%sClientHook(ctx, clientHookParams{})", servName) diff --git a/internal/gengapic/gengrpc_test.go b/internal/gengapic/gengrpc_test.go index 419912f2da..bc54420d44 100644 --- a/internal/gengapic/gengrpc_test.go +++ b/internal/gengapic/gengrpc_test.go @@ -54,9 +54,11 @@ func TestServiceRenaming(t *testing.T) { var g generator g.imports = map[pbinfo.ImportSpec]bool{} g.cfg = &generatorConfig{ - pkgName: "pkg", - transports: []transport{grpc}, + pkgName: "pkg", + transports: []transport{grpc}, + featureEnablement: map[featureID]struct{}{OpenTelemetryTracingFeature: {}}, APIServiceConfig: &serviceconfig.Service{ + Name: "foo.googleapis.com", Publishing: &annotations.Publishing{ LibrarySettings: []*annotations.ClientLibrarySettings{ { diff --git a/internal/gengapic/genrest.go b/internal/gengapic/genrest.go index 393d5a1b6d..2bf67d1d8e 100644 --- a/internal/gengapic/genrest.go +++ b/internal/gengapic/genrest.go @@ -162,6 +162,15 @@ func (g *generator) restClientUtilities(serv *descriptorpb.ServiceDescriptorProt g.serviceDoc(serv, false) // exclude API version docs p("func New%[1]sRESTClient(ctx context.Context, opts ...option.ClientOption) (*%[1]sClient, error) {", servName) p(" clientOpts := append(default%sRESTClientOptions(), opts...)", servName) + if g.featureEnabled(OpenTelemetryTracingFeature) { + p(" if gax.IsFeatureEnabled(\"TRACING\") {") + p(" clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{") + p(" \"gcp.client.service\": %q,", strings.Split(g.cfg.APIServiceConfig.GetName(), ".")[0]) + p(" \"gcp.client.version\": getVersionClient(),") + p(" \"gcp.client.repo\": \"googleapis/google-cloud-go\",") + p(" }))") + p(" }") + } p(" httpClient, endpoint, err := httptransport.NewClient(ctx, clientOpts...)") p(" if err != nil {") p(" return nil, err") @@ -657,6 +666,12 @@ 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) + if info != nil && g.featureEnabled(OpenTelemetryTracingFeature) { + p("if gax.IsFeatureEnabled(\"TRACING\") {") + p(" ctx = metadata.AppendToOutgoingContext(ctx, \"url.template\", %q)", info.url) + p("}") + g.imports[pbinfo.ImportSpec{Path: "google.golang.org/grpc/metadata"}] = true + } p("var streamClient *%s", streamClient) p("e := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {") p(` if settings.Path != "" {`) @@ -939,6 +954,12 @@ 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) + if info != nil && g.featureEnabled(OpenTelemetryTracingFeature) { + p("if gax.IsFeatureEnabled(\"TRACING\") {") + p(" ctx = metadata.AppendToOutgoingContext(ctx, \"url.template\", %q)", info.url) + p("}") + g.imports[pbinfo.ImportSpec{Path: "google.golang.org/grpc/metadata"}] = true + } p("unm := protojson.UnmarshalOptions{AllowPartial: true, DiscardUnknown: true}") p("resp := &%s.%s{}", outSpec.Name, outType.GetName()) p("e := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {") @@ -1035,6 +1056,12 @@ 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) + if info != nil && g.featureEnabled(OpenTelemetryTracingFeature) { + p("if gax.IsFeatureEnabled(\"TRACING\") {") + p(" ctx = metadata.AppendToOutgoingContext(ctx, \"url.template\", %q)", info.url) + p("}") + g.imports[pbinfo.ImportSpec{Path: "google.golang.org/grpc/metadata"}] = true + } p("return gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {") p(` if settings.Path != "" {`) p(" baseUrl.Path = settings.Path") @@ -1124,6 +1151,12 @@ 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) + if info != nil && g.featureEnabled(OpenTelemetryTracingFeature) { + p("if gax.IsFeatureEnabled(\"TRACING\") {") + p(" ctx = metadata.AppendToOutgoingContext(ctx, \"url.template\", %q)", info.url) + p("}") + g.imports[pbinfo.ImportSpec{Path: "google.golang.org/grpc/metadata"}] = true + } g.appendCallOpts(m) if !isHTTPBodyMessage { p("unm := protojson.UnmarshalOptions{AllowPartial: true, DiscardUnknown: true}") diff --git a/internal/gengapic/genrest_test.go b/internal/gengapic/genrest_test.go index 38ed49c4cc..0fa597a9d2 100644 --- a/internal/gengapic/genrest_test.go +++ b/internal/gengapic/genrest_test.go @@ -440,10 +440,15 @@ func TestGenRestMethod(t *testing.T) { Type: descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(), Options: sizeOpts, } + resRefOpts := &descriptorpb.FieldOptions{} + proto.SetExtension(resRefOpts, annotations.E_ResourceReference, &annotations.ResourceReference{ + Type: "foo.googleapis.com/Bar", + }) otherField := &descriptorpb.FieldDescriptorProto{ Name: proto.String("other"), Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(), Proto3Optional: proto.Bool(true), + Options: resRefOpts, } infoOpts := &descriptorpb.FieldOptions{} proto.SetExtension(infoOpts, annotations.E_FieldInfo, &annotations.FieldInfo{Format: annotations.FieldInfo_UUID4}) @@ -673,8 +678,10 @@ func TestGenRestMethod(t *testing.T) { } s := &descriptorpb.ServiceDescriptorProto{ - Name: proto.String("FooService"), + Name: proto.String("FooService"), + Options: &descriptorpb.ServiceOptions{}, } + proto.SetExtension(s.Options, annotations.E_DefaultHost, "foo.googleapis.com") opS := &descriptorpb.ServiceDescriptorProto{ Name: proto.String("FooOperationService"), } @@ -696,7 +703,7 @@ func TestGenRestMethod(t *testing.T) { methodToWrapper: map[*descriptorpb.MethodDescriptorProto]operationWrapper{}, opWrappers: map[string]operationWrapper{}, }, - cfg: &generatorConfig{}, + cfg: &generatorConfig{featureEnablement: map[featureID]struct{}{OpenTelemetryTracingFeature: {}}}, customOpServices: map[*descriptorpb.ServiceDescriptorProto]*descriptorpb.ServiceDescriptorProto{ s: opS, }, @@ -753,8 +760,9 @@ func TestGenRestMethod(t *testing.T) { { name: "custom_op", method: opRPC, - cfg: &generatorConfig{generateAsDIREGAPIC: true}, + cfg: &generatorConfig{generateAsDIREGAPIC: true, featureEnablement: map[featureID]struct{}{OpenTelemetryTracingFeature: {}}}, imports: map[pbinfo.ImportSpec]bool{ + {Path: "google.golang.org/grpc/metadata"}: true, {Path: "google.golang.org/protobuf/encoding/protojson"}: true, {Path: "net/url"}: true, {Path: "fmt"}: true, @@ -764,33 +772,35 @@ func TestGenRestMethod(t *testing.T) { { name: "empty_rpc", method: emptyRPC, - cfg: &generatorConfig{}, + cfg: &generatorConfig{featureEnablement: map[featureID]struct{}{OpenTelemetryTracingFeature: {}}}, imports: map[pbinfo.ImportSpec]bool{ - {Path: "fmt"}: true, - {Path: "github.com/google/uuid"}: true, - {Path: "net/url"}: true, + {Path: "fmt"}: true, + {Path: "github.com/google/uuid"}: true, + {Path: "google.golang.org/grpc/metadata"}: true, + {Path: "net/url"}: true, {Name: "foopb", Path: "google.golang.org/genproto/cloud/foo/v1"}: true, }, }, { name: "unary_rpc", method: unaryRPC, - cfg: &generatorConfig{restNumericEnum: true}, + cfg: &generatorConfig{restNumericEnum: true, featureEnablement: map[featureID]struct{}{OpenTelemetryTracingFeature: {}}}, imports: map[pbinfo.ImportSpec]bool{ - {Path: "bytes"}: true, - {Path: "fmt"}: true, - {Path: "github.com/google/uuid"}: true, + {Path: "bytes"}: true, + {Path: "fmt"}: true, + {Path: "github.com/google/uuid"}: true, + {Path: "google.golang.org/grpc/metadata"}: true, {Path: "google.golang.org/protobuf/encoding/protojson"}: true, - {Path: "net/url"}: true, - {Path: "regexp"}: true, - {Path: "strings"}: true, + {Path: "net/url"}: true, + {Path: "regexp"}: true, + {Path: "strings"}: true, {Name: "foopb", Path: "google.golang.org/genproto/cloud/foo/v1"}: true, }, }, { name: "paging_rpc", method: pagingRPC, - cfg: &generatorConfig{}, + cfg: &generatorConfig{featureEnablement: map[featureID]struct{}{OpenTelemetryTracingFeature: {}}}, imports: map[pbinfo.ImportSpec]bool{ {Path: "math"}: true, {Path: "net/url"}: true, @@ -804,7 +814,7 @@ func TestGenRestMethod(t *testing.T) { { name: "server_stream_rpc", method: serverStreamRPC, - cfg: &generatorConfig{}, + cfg: &generatorConfig{featureEnablement: map[featureID]struct{}{OpenTelemetryTracingFeature: {}}}, imports: map[pbinfo.ImportSpec]bool{ {Path: "bytes"}: true, {Path: "context"}: true, @@ -821,7 +831,7 @@ func TestGenRestMethod(t *testing.T) { { name: "no_request_stream_rpc", method: clientStreamRPC, - cfg: &generatorConfig{}, + cfg: &generatorConfig{featureEnablement: map[featureID]struct{}{OpenTelemetryTracingFeature: {}}}, imports: map[pbinfo.ImportSpec]bool{ {Path: "context"}: true, {Path: "errors"}: true, @@ -831,8 +841,9 @@ func TestGenRestMethod(t *testing.T) { { name: "lro_rpc", method: lroRPC, - cfg: &generatorConfig{transports: []transport{rest}}, + cfg: &generatorConfig{transports: []transport{rest}, featureEnablement: map[featureID]struct{}{OpenTelemetryTracingFeature: {}}}, imports: map[pbinfo.ImportSpec]bool{ + {Path: "google.golang.org/grpc/metadata"}: true, {Path: "bytes"}: true, {Path: "cloud.google.com/go/longrunning"}: true, {Path: "fmt"}: true, @@ -845,10 +856,11 @@ func TestGenRestMethod(t *testing.T) { { name: "http_body_rpc", method: httpBodyRPC, - cfg: &generatorConfig{}, + cfg: &generatorConfig{featureEnablement: map[featureID]struct{}{OpenTelemetryTracingFeature: {}}}, imports: map[pbinfo.ImportSpec]bool{ {Path: "bytes"}: true, {Path: "fmt"}: true, + {Path: "google.golang.org/grpc/metadata"}: true, {Path: "google.golang.org/protobuf/encoding/protojson"}: true, {Path: "net/url"}: true, {Path: "regexp"}: true, @@ -860,8 +872,9 @@ func TestGenRestMethod(t *testing.T) { { name: "update_rpc", method: updateRPC, - cfg: &generatorConfig{restNumericEnum: true}, + cfg: &generatorConfig{restNumericEnum: true, featureEnablement: map[featureID]struct{}{OpenTelemetryTracingFeature: {}}}, imports: map[pbinfo.ImportSpec]bool{ + {Path: "google.golang.org/grpc/metadata"}: true, {Path: "bytes"}: true, {Path: "fmt"}: true, {Path: "google.golang.org/protobuf/encoding/protojson"}: true, diff --git a/internal/gengapic/mixins_test.go b/internal/gengapic/mixins_test.go index 37cb176fa5..0f1c28af3b 100644 --- a/internal/gengapic/mixins_test.go +++ b/internal/gengapic/mixins_test.go @@ -51,6 +51,7 @@ func TestCollectMixins(t *testing.T) { mixins: make(mixins), cfg: &generatorConfig{ APIServiceConfig: &serviceconfig.Service{ + Name: "foo.googleapis.com", Apis: []*apipb.Api{ {Name: "google.example.library.v1.Library"}, {Name: "google.longrunning.Operations"}, @@ -136,6 +137,7 @@ func TestHasIAMPolicyMixin(t *testing.T) { }, cfg: &generatorConfig{ APIServiceConfig: &serviceconfig.Service{ + Name: "foo.googleapis.com", Apis: []*apipb.Api{ {Name: "foo.bar.Baz"}, {Name: "google.iam.v1.IAMPolicy"}, @@ -204,6 +206,7 @@ func TestHasLocationMixin(t *testing.T) { }, cfg: &generatorConfig{ APIServiceConfig: &serviceconfig.Service{ + Name: "foo.googleapis.com", Apis: []*apipb.Api{ {Name: "foo.bar.Baz"}, {Name: "google.cloud.location.Locations"}, @@ -238,6 +241,7 @@ func TestHasLROMixin(t *testing.T) { }, cfg: &generatorConfig{ APIServiceConfig: &serviceconfig.Service{ + Name: "foo.googleapis.com", Apis: []*apipb.Api{ {Name: "foo.bar.Baz"}, {Name: "google.iam.v1.IAMPolicy"}, @@ -303,6 +307,7 @@ func TestGetOperationPathOverride(t *testing.T) { mixins: make(mixins), cfg: &generatorConfig{ APIServiceConfig: &serviceconfig.Service{ + Name: "foo.googleapis.com", Http: tc.http, }, }, diff --git a/internal/gengapic/testdata/custom_op_init.want b/internal/gengapic/testdata/custom_op_init.want index 2267094186..c6fa701020 100644 --- a/internal/gengapic/testdata/custom_op_init.want +++ b/internal/gengapic/testdata/custom_op_init.want @@ -72,6 +72,13 @@ type restClient struct { // Foo service does stuff. func NewRESTClient(ctx context.Context, opts ...option.ClientOption) (*Client, error) { clientOpts := append(defaultRESTClientOptions(), opts...) + if gax.IsFeatureEnabled("TRACING") { + clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{ + "gcp.client.service": "foo", + "gcp.client.version": getVersionClient(), + "gcp.client.repo": "googleapis/google-cloud-go", + })) + } httpClient, endpoint, err := httptransport.NewClient(ctx, clientOpts...) if err != nil { return nil, err diff --git a/internal/gengapic/testdata/deprecated_client_init.want b/internal/gengapic/testdata/deprecated_client_init.want index ae2a2464f1..8e2ff8cc58 100644 --- a/internal/gengapic/testdata/deprecated_client_init.want +++ b/internal/gengapic/testdata/deprecated_client_init.want @@ -76,6 +76,13 @@ type gRPCClient struct { // Deprecated: Foo may be removed in a future version. func NewClient(ctx context.Context, opts ...option.ClientOption) (*Client, error) { clientOpts := defaultGRPCClientOptions() + if gax.IsFeatureEnabled("TRACING") { + clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{ + "gcp.client.service": "foo", + "gcp.client.version": getVersionClient(), + "gcp.client.repo": "googleapis/google-cloud-go", + })) + } if newClientHook != nil { hookOpts, err := newClientHook(ctx, clientHookParams{}) if err != nil { @@ -153,6 +160,13 @@ type restClient struct { // Deprecated: Foo may be removed in a future version. func NewRESTClient(ctx context.Context, opts ...option.ClientOption) (*Client, error) { clientOpts := append(defaultRESTClientOptions(), opts...) + if gax.IsFeatureEnabled("TRACING") { + clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{ + "gcp.client.service": "foo", + "gcp.client.version": getVersionClient(), + "gcp.client.repo": "googleapis/google-cloud-go", + })) + } httpClient, endpoint, err := httptransport.NewClient(ctx, clientOpts...) if err != nil { return nil, err diff --git a/internal/gengapic/testdata/empty_client_init.want b/internal/gengapic/testdata/empty_client_init.want index cbf05d3afb..9f88320f94 100644 --- a/internal/gengapic/testdata/empty_client_init.want +++ b/internal/gengapic/testdata/empty_client_init.want @@ -74,6 +74,13 @@ type gRPCClient struct { // Foo service does stuff. func NewClient(ctx context.Context, opts ...option.ClientOption) (*Client, error) { clientOpts := defaultGRPCClientOptions() + if gax.IsFeatureEnabled("TRACING") { + clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{ + "gcp.client.service": "foo", + "gcp.client.version": getVersionClient(), + "gcp.client.repo": "googleapis/google-cloud-go", + })) + } if newClientHook != nil { hookOpts, err := newClientHook(ctx, clientHookParams{}) if err != nil { @@ -150,6 +157,13 @@ type restClient struct { // Foo service does stuff. func NewRESTClient(ctx context.Context, opts ...option.ClientOption) (*Client, error) { clientOpts := append(defaultRESTClientOptions(), opts...) + if gax.IsFeatureEnabled("TRACING") { + clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{ + "gcp.client.service": "foo", + "gcp.client.version": getVersionClient(), + "gcp.client.repo": "googleapis/google-cloud-go", + })) + } httpClient, endpoint, err := httptransport.NewClient(ctx, clientOpts...) if err != nil { return nil, err diff --git a/internal/gengapic/testdata/foo_client_init.want b/internal/gengapic/testdata/foo_client_init.want index dd0006ef3c..852f7de901 100644 --- a/internal/gengapic/testdata/foo_client_init.want +++ b/internal/gengapic/testdata/foo_client_init.want @@ -103,6 +103,13 @@ type fooGRPCClient struct { // Foo service does stuff. func NewFooClient(ctx context.Context, opts ...option.ClientOption) (*FooClient, error) { clientOpts := defaultFooGRPCClientOptions() + if gax.IsFeatureEnabled("TRACING") { + clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{ + "gcp.client.service": "foo", + "gcp.client.version": getVersionClient(), + "gcp.client.repo": "googleapis/google-cloud-go", + })) + } if newFooClientHook != nil { hookOpts, err := newFooClientHook(ctx, clientHookParams{}) if err != nil { diff --git a/internal/gengapic/testdata/foo_rest_client_init.want b/internal/gengapic/testdata/foo_rest_client_init.want index a50a98942f..a74120bd57 100644 --- a/internal/gengapic/testdata/foo_rest_client_init.want +++ b/internal/gengapic/testdata/foo_rest_client_init.want @@ -96,6 +96,13 @@ type fooRESTClient struct { // Foo service does stuff. func NewFooRESTClient(ctx context.Context, opts ...option.ClientOption) (*FooClient, error) { clientOpts := append(defaultFooRESTClientOptions(), opts...) + if gax.IsFeatureEnabled("TRACING") { + clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{ + "gcp.client.service": "foo", + "gcp.client.version": getVersionClient(), + "gcp.client.repo": "googleapis/google-cloud-go", + })) + } httpClient, endpoint, err := httptransport.NewClient(ctx, clientOpts...) if err != nil { return nil, err diff --git a/internal/gengapic/testdata/lro_client_init.want b/internal/gengapic/testdata/lro_client_init.want index 93689df967..b54d2c9237 100644 --- a/internal/gengapic/testdata/lro_client_init.want +++ b/internal/gengapic/testdata/lro_client_init.want @@ -118,6 +118,13 @@ type fooGRPCClient struct { // Foo service does stuff. func NewFooClient(ctx context.Context, opts ...option.ClientOption) (*FooClient, error) { clientOpts := defaultFooGRPCClientOptions() + if gax.IsFeatureEnabled("TRACING") { + clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{ + "gcp.client.service": "foo", + "gcp.client.version": getVersionClient(), + "gcp.client.repo": "googleapis/google-cloud-go", + })) + } if newFooClientHook != nil { hookOpts, err := newFooClientHook(ctx, clientHookParams{}) if err != nil { diff --git a/internal/gengapic/testdata/method_GetAnotherThing.want b/internal/gengapic/testdata/method_GetAnotherThing.want index 1849b02789..0c6153adf7 100644 --- a/internal/gengapic/testdata/method_GetAnotherThing.want +++ b/internal/gengapic/testdata/method_GetAnotherThing.want @@ -30,6 +30,9 @@ func (c *fooGRPCClient) GetAnotherThing(ctx context.Context, req *mypackagepb.In hds = append(c.xGoogHeaders, hds...) ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...) + if gax.IsFeatureEnabled("TRACING") { + ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", fmt.Sprintf("//foo.googleapis.com/%v", req.GetOther())) + } 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 ee58f16f8e..532f01e1d7 100644 --- a/internal/gengapic/testdata/method_GetEmptyThing.want +++ b/internal/gengapic/testdata/method_GetEmptyThing.want @@ -3,6 +3,9 @@ func (c *fooGRPCClient) GetEmptyThing(ctx context.Context, req *mypackagepb.Inpu hds = append(c.xGoogHeaders, hds...) ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...) + if gax.IsFeatureEnabled("TRACING") { + ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", fmt.Sprintf("//foo.googleapis.com/%v", req.GetOther())) + } if req != nil && req.RequestId == nil { req.RequestId = proto.String(uuid.NewString()) } diff --git a/internal/gengapic/testdata/method_GetManyThings.want b/internal/gengapic/testdata/method_GetManyThings.want index 1e8581ef42..0c621bfac9 100644 --- a/internal/gengapic/testdata/method_GetManyThings.want +++ b/internal/gengapic/testdata/method_GetManyThings.want @@ -3,6 +3,9 @@ func (c *fooGRPCClient) GetManyThings(ctx context.Context, req *mypackagepb.Page hds = append(c.xGoogHeaders, hds...) ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...) + if gax.IsFeatureEnabled("TRACING") { + ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", fmt.Sprintf("//foo.googleapis.com/%v", req.GetOther())) + } 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 5400460de2..00fca00884 100644 --- a/internal/gengapic/testdata/method_GetManyThingsOptional.want +++ b/internal/gengapic/testdata/method_GetManyThingsOptional.want @@ -3,6 +3,9 @@ func (c *fooGRPCClient) GetManyThingsOptional(ctx context.Context, req *mypackag hds = append(c.xGoogHeaders, hds...) ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...) + if gax.IsFeatureEnabled("TRACING") { + ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", fmt.Sprintf("//foo.googleapis.com/%v", req.GetOther())) + } 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 2bf6a039e2..8fc8f2d6e9 100644 --- a/internal/gengapic/testdata/method_GetOneThing.want +++ b/internal/gengapic/testdata/method_GetOneThing.want @@ -3,6 +3,9 @@ func (c *fooGRPCClient) GetOneThing(ctx context.Context, req *mypackagepb.InputT hds = append(c.xGoogHeaders, hds...) ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...) + if gax.IsFeatureEnabled("TRACING") { + ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", fmt.Sprintf("//foo.googleapis.com/%v", req.GetOther())) + } 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 c177fcc3f7..f2694b8cd5 100644 --- a/internal/gengapic/testdata/method_ServerThings.want +++ b/internal/gengapic/testdata/method_ServerThings.want @@ -3,6 +3,9 @@ func (c *fooGRPCClient) ServerThings(ctx context.Context, req *mypackagepb.Input hds = append(c.xGoogHeaders, hds...) ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...) + if gax.IsFeatureEnabled("TRACING") { + ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", fmt.Sprintf("//foo.googleapis.com/%v", req.GetOther())) + } 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/rest_CustomOp.want b/internal/gengapic/testdata/rest_CustomOp.want index 47e7439bbb..d7fcffedc2 100644 --- a/internal/gengapic/testdata/rest_CustomOp.want +++ b/internal/gengapic/testdata/rest_CustomOp.want @@ -19,6 +19,9 @@ 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("TRACING") { + ctx = metadata.AppendToOutgoingContext(ctx, "url.template", "/v1/foo") + } opts = append((*c.CallOptions).CustomOp[0:len((*c.CallOptions).CustomOp):len((*c.CallOptions).CustomOp)], opts...) unm := protojson.UnmarshalOptions{AllowPartial: true, DiscardUnknown: true} resp := &foopb.Operation{} diff --git a/internal/gengapic/testdata/rest_EmptyRPC.want b/internal/gengapic/testdata/rest_EmptyRPC.want index 1ca911d4d3..f053dff964 100644 --- a/internal/gengapic/testdata/rest_EmptyRPC.want +++ b/internal/gengapic/testdata/rest_EmptyRPC.want @@ -22,6 +22,12 @@ func (c *fooRESTClient) EmptyRPC(ctx context.Context, req *foopb.Foo, opts ...ga hds = append(c.xGoogHeaders, hds...) hds = append(hds, "Content-Type", "application/json") headers := gax.BuildHeaders(ctx, hds...) + if gax.IsFeatureEnabled("TRACING") { + ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", fmt.Sprintf("//foo.googleapis.com/%v", req.GetOther())) + } + if gax.IsFeatureEnabled("TRACING") { + ctx = metadata.AppendToOutgoingContext(ctx, "url.template", "/v1/foo/{other=*}") + } return gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { if settings.Path != "" { baseUrl.Path = settings.Path diff --git a/internal/gengapic/testdata/rest_HttpBodyRPC.want b/internal/gengapic/testdata/rest_HttpBodyRPC.want index 691d0a492f..e8c2fdf645 100644 --- a/internal/gengapic/testdata/rest_HttpBodyRPC.want +++ b/internal/gengapic/testdata/rest_HttpBodyRPC.want @@ -26,6 +26,12 @@ func (c *fooRESTClient) HttpBodyRPC(ctx context.Context, req *foopb.Foo, opts .. hds = append(c.xGoogHeaders, hds...) hds = append(hds, "Content-Type", "application/json") headers := gax.BuildHeaders(ctx, hds...) + if gax.IsFeatureEnabled("TRACING") { + ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", fmt.Sprintf("//foo.googleapis.com/%v", req.GetOther())) + } + if gax.IsFeatureEnabled("TRACING") { + ctx = metadata.AppendToOutgoingContext(ctx, "url.template", "/v1/foo") + } opts = append((*c.CallOptions).HttpBodyRPC[0:len((*c.CallOptions).HttpBodyRPC):len((*c.CallOptions).HttpBodyRPC)], opts...) resp := &httpbodypb.HttpBody{} e := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { diff --git a/internal/gengapic/testdata/rest_LongrunningRPC.want b/internal/gengapic/testdata/rest_LongrunningRPC.want index ac7003ac70..f824cc77d0 100644 --- a/internal/gengapic/testdata/rest_LongrunningRPC.want +++ b/internal/gengapic/testdata/rest_LongrunningRPC.want @@ -17,6 +17,9 @@ 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("TRACING") { + ctx = metadata.AppendToOutgoingContext(ctx, "url.template", "/v1/foo") + } unm := protojson.UnmarshalOptions{AllowPartial: true, DiscardUnknown: true} resp := &longrunningpb.Operation{} e := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { diff --git a/internal/gengapic/testdata/rest_ServerStreamRPC.want b/internal/gengapic/testdata/rest_ServerStreamRPC.want index 1cc8d1d668..35a29601d4 100644 --- a/internal/gengapic/testdata/rest_ServerStreamRPC.want +++ b/internal/gengapic/testdata/rest_ServerStreamRPC.want @@ -26,6 +26,12 @@ func (c *fooRESTClient) ServerStreamRPC(ctx context.Context, req *foopb.Foo, opt hds = append(c.xGoogHeaders, hds...) hds = append(hds, "Content-Type", "application/json") headers := gax.BuildHeaders(ctx, hds...) + if gax.IsFeatureEnabled("TRACING") { + ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", fmt.Sprintf("//foo.googleapis.com/%v", req.GetOther())) + } + if gax.IsFeatureEnabled("TRACING") { + ctx = metadata.AppendToOutgoingContext(ctx, "url.template", "/v1/foo") + } var streamClient *serverStreamRPCRESTStreamClient e := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { if settings.Path != "" { diff --git a/internal/gengapic/testdata/rest_UnaryRPC.want b/internal/gengapic/testdata/rest_UnaryRPC.want index dbf6297b6b..965fca2648 100644 --- a/internal/gengapic/testdata/rest_UnaryRPC.want +++ b/internal/gengapic/testdata/rest_UnaryRPC.want @@ -34,6 +34,12 @@ func (c *fooRESTClient) UnaryRPC(ctx context.Context, req *foopb.Foo, opts ...ga hds = append(c.xGoogHeaders, hds...) hds = append(hds, "Content-Type", "application/json") headers := gax.BuildHeaders(ctx, hds...) + if gax.IsFeatureEnabled("TRACING") { + ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", fmt.Sprintf("//foo.googleapis.com/%v", req.GetOther())) + } + if gax.IsFeatureEnabled("TRACING") { + ctx = metadata.AppendToOutgoingContext(ctx, "url.template", "/v1/foo") + } opts = append((*c.CallOptions).UnaryRPC[0:len((*c.CallOptions).UnaryRPC):len((*c.CallOptions).UnaryRPC)], opts...) unm := protojson.UnmarshalOptions{AllowPartial: true, DiscardUnknown: true} resp := &foopb.Foo{} diff --git a/internal/gengapic/testdata/rest_UpdateRPC.want b/internal/gengapic/testdata/rest_UpdateRPC.want index 694ec10b78..73e43aa115 100644 --- a/internal/gengapic/testdata/rest_UpdateRPC.want +++ b/internal/gengapic/testdata/rest_UpdateRPC.want @@ -39,6 +39,9 @@ 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("TRACING") { + ctx = metadata.AppendToOutgoingContext(ctx, "url.template", "/v1/foo") + } opts = append((*c.CallOptions).UpdateRPC[0:len((*c.CallOptions).UpdateRPC):len((*c.CallOptions).UpdateRPC)], opts...) unm := protojson.UnmarshalOptions{AllowPartial: true, DiscardUnknown: true} resp := &foopb.Foo{} diff --git a/internal/gengapic/testdata/service_rename.want b/internal/gengapic/testdata/service_rename.want index cc505cd2ac..393fc58dc7 100644 --- a/internal/gengapic/testdata/service_rename.want +++ b/internal/gengapic/testdata/service_rename.want @@ -21,6 +21,13 @@ type barGRPCClient struct { // The returned client must be Closed when it is done being used to clean up its underlying connections. func NewBarClient(ctx context.Context, opts ...option.ClientOption) (*BarClient, error) { clientOpts := defaultBarGRPCClientOptions() + if gax.IsFeatureEnabled("TRACING") { + clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{ + "gcp.client.service": "foo", + "gcp.client.version": getVersionClient(), + "gcp.client.repo": "googleapis/google-cloud-go", + })) + } if newBarClientHook != nil { hookOpts, err := newBarClientHook(ctx, clientHookParams{}) if err != nil {