From 69b6543265e89b4613365da2804086d3fbbf6c21 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Tue, 30 Dec 2025 15:43:55 -0700 Subject: [PATCH 1/5] feat(gengapic): expose resource names and static identity for OpenTelemetry tracing Modify grpcClientOptions and restClientOptions to populate internaloption.WithTelemetryAttributes. Modify insertRequestHeaders to expose resource names via metadata.AppendToOutgoingContext. --- internal/gengapic/gengapic.go | 8 ++ internal/gengapic/gengapic_test.go | 88 ++++++++++--------- internal/gengapic/gengrpc.go | 5 ++ internal/gengapic/genrest.go | 5 ++ internal/gengapic/genrest_test.go | 21 +++-- .../gengapic/testdata/custom_op_init.want | 5 ++ .../testdata/deprecated_client_init.want | 10 +++ .../gengapic/testdata/empty_client_init.want | 10 +++ .../gengapic/testdata/foo_client_init.want | 5 ++ .../testdata/foo_rest_client_init.want | 5 ++ .../gengapic/testdata/lro_client_init.want | 5 ++ .../testdata/method_GetAnotherThing.want | 3 + .../testdata/method_GetEmptyThing.want | 3 + .../testdata/method_GetManyThings.want | 3 + .../method_GetManyThingsOptional.want | 3 + .../gengapic/testdata/method_GetOneThing.want | 3 + .../testdata/method_ServerThings.want | 3 + internal/gengapic/testdata/rest_EmptyRPC.want | 3 + .../gengapic/testdata/rest_HttpBodyRPC.want | 3 + .../testdata/rest_ServerStreamRPC.want | 3 + internal/gengapic/testdata/rest_UnaryRPC.want | 3 + .../gengapic/testdata/service_rename.want | 5 ++ 22 files changed, 151 insertions(+), 51 deletions(-) diff --git a/internal/gengapic/gengapic.go b/internal/gengapic/gengapic.go index fc36fd4407b..623471fe09d 100644 --- a/internal/gengapic/gengapic.go +++ b/internal/gengapic/gengapic.go @@ -505,10 +505,18 @@ func (g *generator) insertRequestHeaders(m *descriptorpb.MethodDescriptorProto, case grpc: p("hds = append(c.xGoogHeaders, hds...)") p("ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)") + p("if gax.IsFeatureEnabled(\"TRACING\") {") + p(` ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", req%s)`, fieldGetter(headers[0][1])) + p("}") + g.imports[pbinfo.ImportSpec{Path: "google.golang.org/grpc/metadata"}] = true case rest: p(`hds = append(c.xGoogHeaders, hds...)`) p(`hds = append(hds, "Content-Type", "application/json")`) p(`headers := gax.BuildHeaders(ctx, hds...)`) + p("if gax.IsFeatureEnabled(\"TRACING\") {") + p(` ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", req%s)`, fieldGetter(headers[0][1])) + p("}") + g.imports[pbinfo.ImportSpec{Path: "google.golang.org/grpc/metadata"}] = true } g.imports[pbinfo.ImportSpec{Path: "fmt"}] = true g.imports[pbinfo.ImportSpec{Path: "net/url"}] = true diff --git a/internal/gengapic/gengapic_test.go b/internal/gengapic/gengapic_test.go index 0bc148a1061..753f626035d 100644 --- a/internal/gengapic/gengapic_test.go +++ b/internal/gengapic/gengapic_test.go @@ -400,20 +400,20 @@ func TestGenGRPCMethods(t *testing.T) { imports map[pbinfo.ImportSpec]bool }{ { - m: &descriptorpb.MethodDescriptorProto{ - Name: proto.String("GetEmptyThing"), - InputType: proto.String(".my.pkg.InputType"), - OutputType: proto.String(emptyType), - 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, - }, - }, - { + m: &descriptorpb.MethodDescriptorProto{ + Name: proto.String("GetEmptyThing"), + InputType: proto.String(".my.pkg.InputType"), + OutputType: proto.String(emptyType), + 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, + }, + }, { m: &descriptorpb.MethodDescriptorProto{ Name: proto.String("GetOneThing"), InputType: proto.String(".my.pkg.InputType"), @@ -421,28 +421,29 @@ 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("GetManyThings"), - InputType: proto.String(".my.pkg.PageInputType"), - OutputType: proto.String(".my.pkg.PageOutputType"), - Options: opts, - }, - imports: map[pbinfo.ImportSpec]bool{ - {Path: "fmt"}: true, - {Path: "google.golang.org/api/iterator"}: true, - {Path: "google.golang.org/protobuf/proto"}: true, - {Path: "net/url"}: true, - {Name: "mypackagepb", Path: "mypackage"}: true, - }, - }, - { + { + m: &descriptorpb.MethodDescriptorProto{ + Name: proto.String("GetManyThings"), + InputType: proto.String(".my.pkg.PageInputType"), + OutputType: proto.String(".my.pkg.PageOutputType"), + 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, + }, + }, { m: &descriptorpb.MethodDescriptorProto{ Name: proto.String("GetManyThingsOptional"), InputType: proto.String(".my.pkg.PageInputTypeOptional"), @@ -452,6 +453,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 +468,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 +508,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. diff --git a/internal/gengapic/gengrpc.go b/internal/gengapic/gengrpc.go index f09fbcad781..b906670ed43 100644 --- a/internal/gengapic/gengrpc.go +++ b/internal/gengapic/gengrpc.go @@ -329,6 +329,11 @@ 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) + p(" if gax.IsFeatureEnabled(\"TRACING\") {") + p(" clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{") + p(" \"gcp.client.service\": %q,", strings.Split(g.serviceConfig.GetName(), ".")[0]) + p(" }))") + p(" }") p(" if new%sClientHook != nil {", servName) p(" hookOpts, err := new%sClientHook(ctx, clientHookParams{})", servName) diff --git a/internal/gengapic/genrest.go b/internal/gengapic/genrest.go index 393d5a1b6d2..5e945499c0c 100644 --- a/internal/gengapic/genrest.go +++ b/internal/gengapic/genrest.go @@ -162,6 +162,11 @@ 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) + p(" if gax.IsFeatureEnabled(\"TRACING\") {") + p(" clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{") + p(" \"gcp.client.service\": %q,", strings.Split(g.serviceConfig.GetName(), ".")[0]) + p(" }))") + p(" }") p(" httpClient, endpoint, err := httptransport.NewClient(ctx, clientOpts...)") p(" if err != nil {") p(" return nil, err") diff --git a/internal/gengapic/genrest_test.go b/internal/gengapic/genrest_test.go index 38ed49c4ccb..5dc3081de72 100644 --- a/internal/gengapic/genrest_test.go +++ b/internal/gengapic/genrest_test.go @@ -766,9 +766,10 @@ func TestGenRestMethod(t *testing.T) { method: emptyRPC, cfg: &generatorConfig{}, 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, }, }, @@ -777,13 +778,14 @@ func TestGenRestMethod(t *testing.T) { method: unaryRPC, cfg: &generatorConfig{restNumericEnum: true}, 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, }, }, @@ -849,6 +851,7 @@ func TestGenRestMethod(t *testing.T) { 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, diff --git a/internal/gengapic/testdata/custom_op_init.want b/internal/gengapic/testdata/custom_op_init.want index 2267094186b..27398bcaa19 100644 --- a/internal/gengapic/testdata/custom_op_init.want +++ b/internal/gengapic/testdata/custom_op_init.want @@ -72,6 +72,11 @@ 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": "", + })) + } 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 ae2a2464f14..87cd8e9c9fc 100644 --- a/internal/gengapic/testdata/deprecated_client_init.want +++ b/internal/gengapic/testdata/deprecated_client_init.want @@ -76,6 +76,11 @@ 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": "", + })) + } if newClientHook != nil { hookOpts, err := newClientHook(ctx, clientHookParams{}) if err != nil { @@ -153,6 +158,11 @@ 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": "", + })) + } 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 cbf05d3afb6..85a8c2d98d8 100644 --- a/internal/gengapic/testdata/empty_client_init.want +++ b/internal/gengapic/testdata/empty_client_init.want @@ -74,6 +74,11 @@ 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": "", + })) + } if newClientHook != nil { hookOpts, err := newClientHook(ctx, clientHookParams{}) if err != nil { @@ -150,6 +155,11 @@ 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": "", + })) + } 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 dd0006ef3cb..55a66ff8192 100644 --- a/internal/gengapic/testdata/foo_client_init.want +++ b/internal/gengapic/testdata/foo_client_init.want @@ -103,6 +103,11 @@ 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": "", + })) + } 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 a50a98942fe..90c519fb460 100644 --- a/internal/gengapic/testdata/foo_rest_client_init.want +++ b/internal/gengapic/testdata/foo_rest_client_init.want @@ -96,6 +96,11 @@ 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": "", + })) + } 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 93689df9674..8d3579b9bae 100644 --- a/internal/gengapic/testdata/lro_client_init.want +++ b/internal/gengapic/testdata/lro_client_init.want @@ -118,6 +118,11 @@ 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": "", + })) + } 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 1849b02789a..26855fdbaa6 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", 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 ee58f16f8ea..35e1b8390e7 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", req.GetFieldName().GetNested()) + } 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 1e8581ef427..e028dbf8032 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", req.GetFieldName().GetNested()) + } 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 5400460de27..599de5b158e 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", req.GetFieldName().GetNested()) + } 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 2bf6a039e20..f9eaa0a7efd 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", req.GetFieldName().GetNested()) + } 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 c177fcc3f7b..83029b6f00c 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", req.GetFieldName().GetNested()) + } 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_EmptyRPC.want b/internal/gengapic/testdata/rest_EmptyRPC.want index 1ca911d4d32..2163f66971a 100644 --- a/internal/gengapic/testdata/rest_EmptyRPC.want +++ b/internal/gengapic/testdata/rest_EmptyRPC.want @@ -22,6 +22,9 @@ 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", req.GetOther()) + } 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 691d0a492fc..d6e3479c7d4 100644 --- a/internal/gengapic/testdata/rest_HttpBodyRPC.want +++ b/internal/gengapic/testdata/rest_HttpBodyRPC.want @@ -26,6 +26,9 @@ 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", req.GetOther()) + } 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_ServerStreamRPC.want b/internal/gengapic/testdata/rest_ServerStreamRPC.want index 1cc8d1d668c..fec6c551659 100644 --- a/internal/gengapic/testdata/rest_ServerStreamRPC.want +++ b/internal/gengapic/testdata/rest_ServerStreamRPC.want @@ -26,6 +26,9 @@ 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", req.GetOther()) + } 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 dbf6297b6b9..63002071e13 100644 --- a/internal/gengapic/testdata/rest_UnaryRPC.want +++ b/internal/gengapic/testdata/rest_UnaryRPC.want @@ -34,6 +34,9 @@ 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", req.GetOther()) + } 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/service_rename.want b/internal/gengapic/testdata/service_rename.want index cc505cd2ac8..f95833b9eb2 100644 --- a/internal/gengapic/testdata/service_rename.want +++ b/internal/gengapic/testdata/service_rename.want @@ -21,6 +21,11 @@ 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": "", + })) + } if newBarClientHook != nil { hookOpts, err := newBarClientHook(ctx, clientHookParams{}) if err != nil { From 6a0f02ba63102892690335cbb93d5e0ce184d7c2 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Mon, 26 Jan 2026 17:05:10 -0700 Subject: [PATCH 2/5] add gcp.client.version and gcp.client.repo to static data Add url.template to RPC data --- internal/gengapic/gengapic.go | 5 ++++ internal/gengapic/gengrpc.go | 4 ++- internal/gengapic/genrest.go | 28 ++++++++++++++++++- internal/gengapic/genrest_test.go | 3 ++ .../gengapic/testdata/custom_op_init.want | 2 ++ .../testdata/deprecated_client_init.want | 4 +++ .../gengapic/testdata/empty_client_init.want | 4 +++ .../gengapic/testdata/foo_client_init.want | 2 ++ .../testdata/foo_rest_client_init.want | 2 ++ .../gengapic/testdata/lro_client_init.want | 2 ++ internal/gengapic/testdata/rest_CustomOp.want | 3 ++ internal/gengapic/testdata/rest_EmptyRPC.want | 3 ++ .../gengapic/testdata/rest_HttpBodyRPC.want | 3 ++ .../testdata/rest_LongrunningRPC.want | 3 ++ .../testdata/rest_ServerStreamRPC.want | 3 ++ internal/gengapic/testdata/rest_UnaryRPC.want | 3 ++ .../gengapic/testdata/rest_UpdateRPC.want | 3 ++ .../gengapic/testdata/service_rename.want | 2 ++ 18 files changed, 77 insertions(+), 2 deletions(-) diff --git a/internal/gengapic/gengapic.go b/internal/gengapic/gengapic.go index 623471fe09d..8ecc03717cb 100644 --- a/internal/gengapic/gengapic.go +++ b/internal/gengapic/gengapic.go @@ -506,6 +506,11 @@ func (g *generator) insertRequestHeaders(m *descriptorpb.MethodDescriptorProto, p("hds = append(c.xGoogHeaders, hds...)") p("ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)") p("if gax.IsFeatureEnabled(\"TRACING\") {") + // For Standard APIs (AIP-122 compliant), for both gRPC and HTTP transports, + // the expression fieldGetter(headers[0][1]) returns an accessor for the full + // canonical resource name (e.g., "projects/p/secrets/s"). For non-compliant + // APIs, this logic will most likely only capture a single path component, + // such as the project ID. p(` ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", req%s)`, fieldGetter(headers[0][1])) p("}") g.imports[pbinfo.ImportSpec{Path: "google.golang.org/grpc/metadata"}] = true diff --git a/internal/gengapic/gengrpc.go b/internal/gengapic/gengrpc.go index b906670ed43..5acda58120e 100644 --- a/internal/gengapic/gengrpc.go +++ b/internal/gengapic/gengrpc.go @@ -331,7 +331,9 @@ func (g *generator) grpcClientUtilities(serv *descriptorpb.ServiceDescriptorProt p(" clientOpts := default%[1]sGRPCClientOptions()", servName) p(" if gax.IsFeatureEnabled(\"TRACING\") {") p(" clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{") - p(" \"gcp.client.service\": %q,", strings.Split(g.serviceConfig.GetName(), ".")[0]) + 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(" }") diff --git a/internal/gengapic/genrest.go b/internal/gengapic/genrest.go index 5e945499c0c..a22d2efd6ab 100644 --- a/internal/gengapic/genrest.go +++ b/internal/gengapic/genrest.go @@ -164,7 +164,9 @@ func (g *generator) restClientUtilities(serv *descriptorpb.ServiceDescriptorProt p(" clientOpts := append(default%sRESTClientOptions(), opts...)", servName) p(" if gax.IsFeatureEnabled(\"TRACING\") {") p(" clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{") - p(" \"gcp.client.service\": %q,", strings.Split(g.serviceConfig.GetName(), ".")[0]) + 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...)") @@ -662,6 +664,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 { + 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 != "" {`) @@ -944,6 +952,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 { + 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 {") @@ -1040,6 +1054,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 { + 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") @@ -1129,6 +1149,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 { + 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 5dc3081de72..564f30d34ae 100644 --- a/internal/gengapic/genrest_test.go +++ b/internal/gengapic/genrest_test.go @@ -755,6 +755,7 @@ func TestGenRestMethod(t *testing.T) { method: opRPC, cfg: &generatorConfig{generateAsDIREGAPIC: true}, 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, @@ -835,6 +836,7 @@ func TestGenRestMethod(t *testing.T) { method: lroRPC, cfg: &generatorConfig{transports: []transport{rest}}, 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, @@ -865,6 +867,7 @@ func TestGenRestMethod(t *testing.T) { method: updateRPC, cfg: &generatorConfig{restNumericEnum: true}, 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/testdata/custom_op_init.want b/internal/gengapic/testdata/custom_op_init.want index 27398bcaa19..236b3efe91e 100644 --- a/internal/gengapic/testdata/custom_op_init.want +++ b/internal/gengapic/testdata/custom_op_init.want @@ -75,6 +75,8 @@ func NewRESTClient(ctx context.Context, opts ...option.ClientOption) (*Client, e if gax.IsFeatureEnabled("TRACING") { clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{ "gcp.client.service": "", + "gcp.client.version": getVersionClient(), + "gcp.client.repo": "googleapis/google-cloud-go", })) } httpClient, endpoint, err := httptransport.NewClient(ctx, clientOpts...) diff --git a/internal/gengapic/testdata/deprecated_client_init.want b/internal/gengapic/testdata/deprecated_client_init.want index 87cd8e9c9fc..521a46b5e51 100644 --- a/internal/gengapic/testdata/deprecated_client_init.want +++ b/internal/gengapic/testdata/deprecated_client_init.want @@ -79,6 +79,8 @@ func NewClient(ctx context.Context, opts ...option.ClientOption) (*Client, error if gax.IsFeatureEnabled("TRACING") { clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{ "gcp.client.service": "", + "gcp.client.version": getVersionClient(), + "gcp.client.repo": "googleapis/google-cloud-go", })) } if newClientHook != nil { @@ -161,6 +163,8 @@ func NewRESTClient(ctx context.Context, opts ...option.ClientOption) (*Client, e if gax.IsFeatureEnabled("TRACING") { clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{ "gcp.client.service": "", + "gcp.client.version": getVersionClient(), + "gcp.client.repo": "googleapis/google-cloud-go", })) } httpClient, endpoint, err := httptransport.NewClient(ctx, clientOpts...) diff --git a/internal/gengapic/testdata/empty_client_init.want b/internal/gengapic/testdata/empty_client_init.want index 85a8c2d98d8..6c2c164e771 100644 --- a/internal/gengapic/testdata/empty_client_init.want +++ b/internal/gengapic/testdata/empty_client_init.want @@ -77,6 +77,8 @@ func NewClient(ctx context.Context, opts ...option.ClientOption) (*Client, error if gax.IsFeatureEnabled("TRACING") { clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{ "gcp.client.service": "", + "gcp.client.version": getVersionClient(), + "gcp.client.repo": "googleapis/google-cloud-go", })) } if newClientHook != nil { @@ -158,6 +160,8 @@ func NewRESTClient(ctx context.Context, opts ...option.ClientOption) (*Client, e if gax.IsFeatureEnabled("TRACING") { clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{ "gcp.client.service": "", + "gcp.client.version": getVersionClient(), + "gcp.client.repo": "googleapis/google-cloud-go", })) } httpClient, endpoint, err := httptransport.NewClient(ctx, clientOpts...) diff --git a/internal/gengapic/testdata/foo_client_init.want b/internal/gengapic/testdata/foo_client_init.want index 55a66ff8192..7823689737e 100644 --- a/internal/gengapic/testdata/foo_client_init.want +++ b/internal/gengapic/testdata/foo_client_init.want @@ -106,6 +106,8 @@ func NewFooClient(ctx context.Context, opts ...option.ClientOption) (*FooClient, if gax.IsFeatureEnabled("TRACING") { clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{ "gcp.client.service": "", + "gcp.client.version": getVersionClient(), + "gcp.client.repo": "googleapis/google-cloud-go", })) } if newFooClientHook != nil { diff --git a/internal/gengapic/testdata/foo_rest_client_init.want b/internal/gengapic/testdata/foo_rest_client_init.want index 90c519fb460..80a786a9e24 100644 --- a/internal/gengapic/testdata/foo_rest_client_init.want +++ b/internal/gengapic/testdata/foo_rest_client_init.want @@ -99,6 +99,8 @@ func NewFooRESTClient(ctx context.Context, opts ...option.ClientOption) (*FooCli if gax.IsFeatureEnabled("TRACING") { clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{ "gcp.client.service": "", + "gcp.client.version": getVersionClient(), + "gcp.client.repo": "googleapis/google-cloud-go", })) } httpClient, endpoint, err := httptransport.NewClient(ctx, clientOpts...) diff --git a/internal/gengapic/testdata/lro_client_init.want b/internal/gengapic/testdata/lro_client_init.want index 8d3579b9bae..c3efe4441f8 100644 --- a/internal/gengapic/testdata/lro_client_init.want +++ b/internal/gengapic/testdata/lro_client_init.want @@ -121,6 +121,8 @@ func NewFooClient(ctx context.Context, opts ...option.ClientOption) (*FooClient, if gax.IsFeatureEnabled("TRACING") { clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{ "gcp.client.service": "", + "gcp.client.version": getVersionClient(), + "gcp.client.repo": "googleapis/google-cloud-go", })) } if newFooClientHook != nil { diff --git a/internal/gengapic/testdata/rest_CustomOp.want b/internal/gengapic/testdata/rest_CustomOp.want index 47e7439bbb7..d7fcffedc27 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 2163f66971a..1d429d06a1d 100644 --- a/internal/gengapic/testdata/rest_EmptyRPC.want +++ b/internal/gengapic/testdata/rest_EmptyRPC.want @@ -25,6 +25,9 @@ func (c *fooRESTClient) EmptyRPC(ctx context.Context, req *foopb.Foo, opts ...ga if gax.IsFeatureEnabled("TRACING") { ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", 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 d6e3479c7d4..081483f3ded 100644 --- a/internal/gengapic/testdata/rest_HttpBodyRPC.want +++ b/internal/gengapic/testdata/rest_HttpBodyRPC.want @@ -29,6 +29,9 @@ func (c *fooRESTClient) HttpBodyRPC(ctx context.Context, req *foopb.Foo, opts .. if gax.IsFeatureEnabled("TRACING") { ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", 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 ac7003ac70f..f824cc77d0f 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 fec6c551659..c9a17a55fa5 100644 --- a/internal/gengapic/testdata/rest_ServerStreamRPC.want +++ b/internal/gengapic/testdata/rest_ServerStreamRPC.want @@ -29,6 +29,9 @@ func (c *fooRESTClient) ServerStreamRPC(ctx context.Context, req *foopb.Foo, opt if gax.IsFeatureEnabled("TRACING") { ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", 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 63002071e13..a54679dc529 100644 --- a/internal/gengapic/testdata/rest_UnaryRPC.want +++ b/internal/gengapic/testdata/rest_UnaryRPC.want @@ -37,6 +37,9 @@ func (c *fooRESTClient) UnaryRPC(ctx context.Context, req *foopb.Foo, opts ...ga if gax.IsFeatureEnabled("TRACING") { ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", 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 694ec10b783..73e43aa1150 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 f95833b9eb2..10dfeef0326 100644 --- a/internal/gengapic/testdata/service_rename.want +++ b/internal/gengapic/testdata/service_rename.want @@ -24,6 +24,8 @@ func NewBarClient(ctx context.Context, opts ...option.ClientOption) (*BarClient, if gax.IsFeatureEnabled("TRACING") { clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{ "gcp.client.service": "", + "gcp.client.version": getVersionClient(), + "gcp.client.repo": "googleapis/google-cloud-go", })) } if newBarClientHook != nil { From f5f0947e6683c4a80198862dd8ca7493448b06fa Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Wed, 28 Jan 2026 17:32:21 -0700 Subject: [PATCH 3/5] add resourceNameField to scan request fields for the google.api.resource_reference annotation --- internal/gengapic/gengapic.go | 113 ++++++++++++++++-- internal/gengapic/gengapic_test.go | 85 +++++++------ internal/gengapic/genrest_test.go | 15 ++- .../testdata/method_GetAnotherThing.want | 2 +- .../testdata/method_GetEmptyThing.want | 2 +- .../testdata/method_GetManyThings.want | 2 +- .../method_GetManyThingsOptional.want | 2 +- .../gengapic/testdata/method_GetOneThing.want | 2 +- .../testdata/method_ServerThings.want | 2 +- internal/gengapic/testdata/rest_EmptyRPC.want | 2 +- .../gengapic/testdata/rest_HttpBodyRPC.want | 2 +- .../testdata/rest_ServerStreamRPC.want | 2 +- internal/gengapic/testdata/rest_UnaryRPC.want | 2 +- 13 files changed, 169 insertions(+), 64 deletions(-) diff --git a/internal/gengapic/gengapic.go b/internal/gengapic/gengapic.go index 8ecc03717cb..6b80ddabd90 100644 --- a/internal/gengapic/gengapic.go +++ b/internal/gengapic/gengapic.go @@ -505,23 +505,58 @@ func (g *generator) insertRequestHeaders(m *descriptorpb.MethodDescriptorProto, case grpc: p("hds = append(c.xGoogHeaders, hds...)") p("ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)") - p("if gax.IsFeatureEnabled(\"TRACING\") {") - // For Standard APIs (AIP-122 compliant), for both gRPC and HTTP transports, - // the expression fieldGetter(headers[0][1]) returns an accessor for the full - // canonical resource name (e.g., "projects/p/secrets/s"). For non-compliant - // APIs, this logic will most likely only capture a single path component, - // such as the project ID. - p(` ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", req%s)`, fieldGetter(headers[0][1])) - p("}") - g.imports[pbinfo.ImportSpec{Path: "google.golang.org/grpc/metadata"}] = true + 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...)`) - p("if gax.IsFeatureEnabled(\"TRACING\") {") - p(` ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", req%s)`, fieldGetter(headers[0][1])) - p("}") - g.imports[pbinfo.ImportSpec{Path: "google.golang.org/grpc/metadata"}] = true + 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 @@ -836,3 +871,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 753f626035d..36149eb648c 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,8 +329,10 @@ 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{ @@ -400,20 +409,20 @@ func TestGenGRPCMethods(t *testing.T) { imports map[pbinfo.ImportSpec]bool }{ { - m: &descriptorpb.MethodDescriptorProto{ - Name: proto.String("GetEmptyThing"), - InputType: proto.String(".my.pkg.InputType"), - OutputType: proto.String(emptyType), - 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, - }, - }, { + m: &descriptorpb.MethodDescriptorProto{ + Name: proto.String("GetEmptyThing"), + InputType: proto.String(".my.pkg.InputType"), + OutputType: proto.String(emptyType), + 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, + }, + }, { m: &descriptorpb.MethodDescriptorProto{ Name: proto.String("GetOneThing"), InputType: proto.String(".my.pkg.InputType"), @@ -428,22 +437,22 @@ func TestGenGRPCMethods(t *testing.T) { {Name: "mypackagepb", Path: "mypackage"}: true, }, }, - { - m: &descriptorpb.MethodDescriptorProto{ - Name: proto.String("GetManyThings"), - InputType: proto.String(".my.pkg.PageInputType"), - OutputType: proto.String(".my.pkg.PageOutputType"), - 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, - }, - }, { + { + m: &descriptorpb.MethodDescriptorProto{ + Name: proto.String("GetManyThings"), + InputType: proto.String(".my.pkg.PageInputType"), + OutputType: proto.String(".my.pkg.PageOutputType"), + 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, + }, + }, { m: &descriptorpb.MethodDescriptorProto{ Name: proto.String("GetManyThingsOptional"), InputType: proto.String(".my.pkg.PageInputTypeOptional"), @@ -468,7 +477,7 @@ func TestGenGRPCMethods(t *testing.T) { Options: opts, }, imports: map[pbinfo.ImportSpec]bool{ - {Path: "fmt"}: true, + {Path: "fmt"}: true, {Path: "google.golang.org/grpc/metadata"}: true, {Path: "net/url"}: true, {Name: "mypackagepb", Path: "mypackage"}: true, @@ -508,7 +517,7 @@ func TestGenGRPCMethods(t *testing.T) { Options: optsGetAnotherThing, }, imports: map[pbinfo.ImportSpec]bool{ - {Path: "fmt"}: true, + {Path: "fmt"}: true, {Path: "google.golang.org/grpc/metadata"}: true, {Path: "net/url"}: true, {Path: "regexp"}: true, @@ -716,8 +725,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/genrest_test.go b/internal/gengapic/genrest_test.go index 564f30d34ae..1ff99ff5660 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"), } @@ -755,7 +762,7 @@ func TestGenRestMethod(t *testing.T) { method: opRPC, cfg: &generatorConfig{generateAsDIREGAPIC: true}, imports: map[pbinfo.ImportSpec]bool{ - {Path: "google.golang.org/grpc/metadata"}: true, + {Path: "google.golang.org/grpc/metadata"}: true, {Path: "google.golang.org/protobuf/encoding/protojson"}: true, {Path: "net/url"}: true, {Path: "fmt"}: true, @@ -836,7 +843,7 @@ func TestGenRestMethod(t *testing.T) { method: lroRPC, cfg: &generatorConfig{transports: []transport{rest}}, imports: map[pbinfo.ImportSpec]bool{ - {Path: "google.golang.org/grpc/metadata"}: true, + {Path: "google.golang.org/grpc/metadata"}: true, {Path: "bytes"}: true, {Path: "cloud.google.com/go/longrunning"}: true, {Path: "fmt"}: true, @@ -867,7 +874,7 @@ func TestGenRestMethod(t *testing.T) { method: updateRPC, cfg: &generatorConfig{restNumericEnum: true}, imports: map[pbinfo.ImportSpec]bool{ - {Path: "google.golang.org/grpc/metadata"}: true, + {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/testdata/method_GetAnotherThing.want b/internal/gengapic/testdata/method_GetAnotherThing.want index 26855fdbaa6..0c6153adf72 100644 --- a/internal/gengapic/testdata/method_GetAnotherThing.want +++ b/internal/gengapic/testdata/method_GetAnotherThing.want @@ -31,7 +31,7 @@ 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", req.GetOther()) + 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 diff --git a/internal/gengapic/testdata/method_GetEmptyThing.want b/internal/gengapic/testdata/method_GetEmptyThing.want index 35e1b8390e7..532f01e1d72 100644 --- a/internal/gengapic/testdata/method_GetEmptyThing.want +++ b/internal/gengapic/testdata/method_GetEmptyThing.want @@ -4,7 +4,7 @@ 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", req.GetFieldName().GetNested()) + 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 e028dbf8032..0c621bfac9e 100644 --- a/internal/gengapic/testdata/method_GetManyThings.want +++ b/internal/gengapic/testdata/method_GetManyThings.want @@ -4,7 +4,7 @@ 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", req.GetFieldName().GetNested()) + 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{} diff --git a/internal/gengapic/testdata/method_GetManyThingsOptional.want b/internal/gengapic/testdata/method_GetManyThingsOptional.want index 599de5b158e..00fca008847 100644 --- a/internal/gengapic/testdata/method_GetManyThingsOptional.want +++ b/internal/gengapic/testdata/method_GetManyThingsOptional.want @@ -4,7 +4,7 @@ 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", req.GetFieldName().GetNested()) + 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{} diff --git a/internal/gengapic/testdata/method_GetOneThing.want b/internal/gengapic/testdata/method_GetOneThing.want index f9eaa0a7efd..8fc8f2d6e9d 100644 --- a/internal/gengapic/testdata/method_GetOneThing.want +++ b/internal/gengapic/testdata/method_GetOneThing.want @@ -4,7 +4,7 @@ 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", req.GetFieldName().GetNested()) + 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 83029b6f00c..f2694b8cd57 100644 --- a/internal/gengapic/testdata/method_ServerThings.want +++ b/internal/gengapic/testdata/method_ServerThings.want @@ -4,7 +4,7 @@ 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", req.GetFieldName().GetNested()) + 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 diff --git a/internal/gengapic/testdata/rest_EmptyRPC.want b/internal/gengapic/testdata/rest_EmptyRPC.want index 1d429d06a1d..f053dff9646 100644 --- a/internal/gengapic/testdata/rest_EmptyRPC.want +++ b/internal/gengapic/testdata/rest_EmptyRPC.want @@ -23,7 +23,7 @@ func (c *fooRESTClient) EmptyRPC(ctx context.Context, req *foopb.Foo, opts ...ga hds = append(hds, "Content-Type", "application/json") headers := gax.BuildHeaders(ctx, hds...) if gax.IsFeatureEnabled("TRACING") { - ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", req.GetOther()) + 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=*}") diff --git a/internal/gengapic/testdata/rest_HttpBodyRPC.want b/internal/gengapic/testdata/rest_HttpBodyRPC.want index 081483f3ded..e8c2fdf6453 100644 --- a/internal/gengapic/testdata/rest_HttpBodyRPC.want +++ b/internal/gengapic/testdata/rest_HttpBodyRPC.want @@ -27,7 +27,7 @@ func (c *fooRESTClient) HttpBodyRPC(ctx context.Context, req *foopb.Foo, opts .. hds = append(hds, "Content-Type", "application/json") headers := gax.BuildHeaders(ctx, hds...) if gax.IsFeatureEnabled("TRACING") { - ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", req.GetOther()) + 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") diff --git a/internal/gengapic/testdata/rest_ServerStreamRPC.want b/internal/gengapic/testdata/rest_ServerStreamRPC.want index c9a17a55fa5..35a29601d44 100644 --- a/internal/gengapic/testdata/rest_ServerStreamRPC.want +++ b/internal/gengapic/testdata/rest_ServerStreamRPC.want @@ -27,7 +27,7 @@ func (c *fooRESTClient) ServerStreamRPC(ctx context.Context, req *foopb.Foo, opt hds = append(hds, "Content-Type", "application/json") headers := gax.BuildHeaders(ctx, hds...) if gax.IsFeatureEnabled("TRACING") { - ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", req.GetOther()) + 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") diff --git a/internal/gengapic/testdata/rest_UnaryRPC.want b/internal/gengapic/testdata/rest_UnaryRPC.want index a54679dc529..965fca26484 100644 --- a/internal/gengapic/testdata/rest_UnaryRPC.want +++ b/internal/gengapic/testdata/rest_UnaryRPC.want @@ -35,7 +35,7 @@ func (c *fooRESTClient) UnaryRPC(ctx context.Context, req *foopb.Foo, opts ...ga hds = append(hds, "Content-Type", "application/json") headers := gax.BuildHeaders(ctx, hds...) if gax.IsFeatureEnabled("TRACING") { - ctx = metadata.AppendToOutgoingContext(ctx, "gcp.resource.name", req.GetOther()) + 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") From 6bcd92bbe1ce76be44997cb8f29417b70ca90f78 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Fri, 30 Jan 2026 13:18:52 -0700 Subject: [PATCH 4/5] add OpenTelemetryTracingFeature --- internal/gengapic/client_init_test.go | 4 +- internal/gengapic/feature.go | 6 ++ internal/gengapic/gengapic.go | 88 ++++++++++++++------------- internal/gengapic/gengapic_test.go | 3 + internal/gengapic/gengrpc.go | 16 ++--- internal/gengapic/gengrpc_test.go | 5 +- internal/gengapic/genrest.go | 24 ++++---- internal/gengapic/genrest_test.go | 20 +++--- 8 files changed, 93 insertions(+), 73 deletions(-) diff --git a/internal/gengapic/client_init_test.go b/internal/gengapic/client_init_test.go index e370139415d..6079a20e1fc 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{}{}, }, }, } @@ -600,6 +601,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 59a3d765dbb..e45afe39e2b 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/gengapic.go b/internal/gengapic/gengapic.go index d04e67e58c6..e124f36ed2e 100644 --- a/internal/gengapic/gengapic.go +++ b/internal/gengapic/gengapic.go @@ -498,57 +498,61 @@ func (g *generator) insertRequestHeaders(m *descriptorpb.MethodDescriptorProto, case grpc: p("hds = append(c.xGoogHeaders, hds...)") p("ctx = gax.InsertMetadataIntoOutgoingContext(ctx, hds...)") - 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 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)) + 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 } - 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...)`) - 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 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)) + 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 } - 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 diff --git a/internal/gengapic/gengapic_test.go b/internal/gengapic/gengapic_test.go index 7688ce36088..ab55bec01be 100644 --- a/internal/gengapic/gengapic_test.go +++ b/internal/gengapic/gengapic_test.go @@ -337,6 +337,9 @@ func TestGenGRPCMethods(t *testing.T) { var g generator g.cfg = &generatorConfig{ pkgName: "pkg", + featureEnablement: map[featureID]struct{}{ + OpenTelemetryTracingFeature: {}, + }, APIServiceConfig: &serviceconfig.Service{ Publishing: &annotations.Publishing{ MethodSettings: []*annotations.MethodSettings{ diff --git a/internal/gengapic/gengrpc.go b/internal/gengapic/gengrpc.go index 9579672f176..d35d6a65925 100644 --- a/internal/gengapic/gengrpc.go +++ b/internal/gengapic/gengrpc.go @@ -329,13 +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) - 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(" }") + 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 419912f2dac..07187e72a7a 100644 --- a/internal/gengapic/gengrpc_test.go +++ b/internal/gengapic/gengrpc_test.go @@ -54,8 +54,9 @@ 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{ Publishing: &annotations.Publishing{ LibrarySettings: []*annotations.ClientLibrarySettings{ diff --git a/internal/gengapic/genrest.go b/internal/gengapic/genrest.go index a22d2efd6ab..2bf67d1d8ef 100644 --- a/internal/gengapic/genrest.go +++ b/internal/gengapic/genrest.go @@ -162,13 +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) - 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(" }") + 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") @@ -664,7 +666,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) - if info != nil { + if info != nil && g.featureEnabled(OpenTelemetryTracingFeature) { p("if gax.IsFeatureEnabled(\"TRACING\") {") p(" ctx = metadata.AppendToOutgoingContext(ctx, \"url.template\", %q)", info.url) p("}") @@ -952,7 +954,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) - if info != nil { + if info != nil && g.featureEnabled(OpenTelemetryTracingFeature) { p("if gax.IsFeatureEnabled(\"TRACING\") {") p(" ctx = metadata.AppendToOutgoingContext(ctx, \"url.template\", %q)", info.url) p("}") @@ -1054,7 +1056,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) - if info != nil { + if info != nil && g.featureEnabled(OpenTelemetryTracingFeature) { p("if gax.IsFeatureEnabled(\"TRACING\") {") p(" ctx = metadata.AppendToOutgoingContext(ctx, \"url.template\", %q)", info.url) p("}") @@ -1149,7 +1151,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) - if info != nil { + if info != nil && g.featureEnabled(OpenTelemetryTracingFeature) { p("if gax.IsFeatureEnabled(\"TRACING\") {") p(" ctx = metadata.AppendToOutgoingContext(ctx, \"url.template\", %q)", info.url) p("}") diff --git a/internal/gengapic/genrest_test.go b/internal/gengapic/genrest_test.go index 1ff99ff5660..0fa597a9d28 100644 --- a/internal/gengapic/genrest_test.go +++ b/internal/gengapic/genrest_test.go @@ -703,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, }, @@ -760,7 +760,7 @@ 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, @@ -772,7 +772,7 @@ 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, @@ -784,7 +784,7 @@ func TestGenRestMethod(t *testing.T) { { 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, @@ -800,7 +800,7 @@ func TestGenRestMethod(t *testing.T) { { 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, @@ -814,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, @@ -831,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, @@ -841,7 +841,7 @@ 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, @@ -856,7 +856,7 @@ 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, @@ -872,7 +872,7 @@ 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, From 1dd4a39562bc75c22264fb644f0bb2c80486f5c7 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Fri, 30 Jan 2026 14:37:48 -0700 Subject: [PATCH 5/5] add serviceconfig.Service.Name to test fixtures --- internal/gengapic/client_init_test.go | 1 + internal/gengapic/generator_test.go | 1 + internal/gengapic/gengrpc_test.go | 1 + internal/gengapic/mixins_test.go | 5 +++++ internal/gengapic/testdata/custom_op_init.want | 2 +- internal/gengapic/testdata/deprecated_client_init.want | 4 ++-- internal/gengapic/testdata/empty_client_init.want | 4 ++-- internal/gengapic/testdata/foo_client_init.want | 2 +- internal/gengapic/testdata/foo_rest_client_init.want | 2 +- internal/gengapic/testdata/lro_client_init.want | 2 +- internal/gengapic/testdata/service_rename.want | 2 +- 11 files changed, 17 insertions(+), 9 deletions(-) diff --git a/internal/gengapic/client_init_test.go b/internal/gengapic/client_init_test.go index 6079a20e1fc..f37efbaa792 100644 --- a/internal/gengapic/client_init_test.go +++ b/internal/gengapic/client_init_test.go @@ -583,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"}, diff --git a/internal/gengapic/generator_test.go b/internal/gengapic/generator_test.go index 4a0c49196d9..cd3eb548515 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/gengrpc_test.go b/internal/gengapic/gengrpc_test.go index 07187e72a7a..bc54420d441 100644 --- a/internal/gengapic/gengrpc_test.go +++ b/internal/gengapic/gengrpc_test.go @@ -58,6 +58,7 @@ func TestServiceRenaming(t *testing.T) { 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/mixins_test.go b/internal/gengapic/mixins_test.go index 37cb176fa5e..0f1c28af3bf 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 236b3efe91e..c6fa7010203 100644 --- a/internal/gengapic/testdata/custom_op_init.want +++ b/internal/gengapic/testdata/custom_op_init.want @@ -74,7 +74,7 @@ func NewRESTClient(ctx context.Context, opts ...option.ClientOption) (*Client, e clientOpts := append(defaultRESTClientOptions(), opts...) if gax.IsFeatureEnabled("TRACING") { clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{ - "gcp.client.service": "", + "gcp.client.service": "foo", "gcp.client.version": getVersionClient(), "gcp.client.repo": "googleapis/google-cloud-go", })) diff --git a/internal/gengapic/testdata/deprecated_client_init.want b/internal/gengapic/testdata/deprecated_client_init.want index 521a46b5e51..8e2ff8cc583 100644 --- a/internal/gengapic/testdata/deprecated_client_init.want +++ b/internal/gengapic/testdata/deprecated_client_init.want @@ -78,7 +78,7 @@ 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": "", + "gcp.client.service": "foo", "gcp.client.version": getVersionClient(), "gcp.client.repo": "googleapis/google-cloud-go", })) @@ -162,7 +162,7 @@ func NewRESTClient(ctx context.Context, opts ...option.ClientOption) (*Client, e clientOpts := append(defaultRESTClientOptions(), opts...) if gax.IsFeatureEnabled("TRACING") { clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{ - "gcp.client.service": "", + "gcp.client.service": "foo", "gcp.client.version": getVersionClient(), "gcp.client.repo": "googleapis/google-cloud-go", })) diff --git a/internal/gengapic/testdata/empty_client_init.want b/internal/gengapic/testdata/empty_client_init.want index 6c2c164e771..9f88320f949 100644 --- a/internal/gengapic/testdata/empty_client_init.want +++ b/internal/gengapic/testdata/empty_client_init.want @@ -76,7 +76,7 @@ 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": "", + "gcp.client.service": "foo", "gcp.client.version": getVersionClient(), "gcp.client.repo": "googleapis/google-cloud-go", })) @@ -159,7 +159,7 @@ func NewRESTClient(ctx context.Context, opts ...option.ClientOption) (*Client, e clientOpts := append(defaultRESTClientOptions(), opts...) if gax.IsFeatureEnabled("TRACING") { clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{ - "gcp.client.service": "", + "gcp.client.service": "foo", "gcp.client.version": getVersionClient(), "gcp.client.repo": "googleapis/google-cloud-go", })) diff --git a/internal/gengapic/testdata/foo_client_init.want b/internal/gengapic/testdata/foo_client_init.want index 7823689737e..852f7de9015 100644 --- a/internal/gengapic/testdata/foo_client_init.want +++ b/internal/gengapic/testdata/foo_client_init.want @@ -105,7 +105,7 @@ func NewFooClient(ctx context.Context, opts ...option.ClientOption) (*FooClient, clientOpts := defaultFooGRPCClientOptions() if gax.IsFeatureEnabled("TRACING") { clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{ - "gcp.client.service": "", + "gcp.client.service": "foo", "gcp.client.version": getVersionClient(), "gcp.client.repo": "googleapis/google-cloud-go", })) diff --git a/internal/gengapic/testdata/foo_rest_client_init.want b/internal/gengapic/testdata/foo_rest_client_init.want index 80a786a9e24..a74120bd577 100644 --- a/internal/gengapic/testdata/foo_rest_client_init.want +++ b/internal/gengapic/testdata/foo_rest_client_init.want @@ -98,7 +98,7 @@ func NewFooRESTClient(ctx context.Context, opts ...option.ClientOption) (*FooCli clientOpts := append(defaultFooRESTClientOptions(), opts...) if gax.IsFeatureEnabled("TRACING") { clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{ - "gcp.client.service": "", + "gcp.client.service": "foo", "gcp.client.version": getVersionClient(), "gcp.client.repo": "googleapis/google-cloud-go", })) diff --git a/internal/gengapic/testdata/lro_client_init.want b/internal/gengapic/testdata/lro_client_init.want index c3efe4441f8..b54d2c92374 100644 --- a/internal/gengapic/testdata/lro_client_init.want +++ b/internal/gengapic/testdata/lro_client_init.want @@ -120,7 +120,7 @@ func NewFooClient(ctx context.Context, opts ...option.ClientOption) (*FooClient, clientOpts := defaultFooGRPCClientOptions() if gax.IsFeatureEnabled("TRACING") { clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{ - "gcp.client.service": "", + "gcp.client.service": "foo", "gcp.client.version": getVersionClient(), "gcp.client.repo": "googleapis/google-cloud-go", })) diff --git a/internal/gengapic/testdata/service_rename.want b/internal/gengapic/testdata/service_rename.want index 10dfeef0326..393fc58dc7f 100644 --- a/internal/gengapic/testdata/service_rename.want +++ b/internal/gengapic/testdata/service_rename.want @@ -23,7 +23,7 @@ func NewBarClient(ctx context.Context, opts ...option.ClientOption) (*BarClient, clientOpts := defaultBarGRPCClientOptions() if gax.IsFeatureEnabled("TRACING") { clientOpts = append(clientOpts, internaloption.WithTelemetryAttributes(map[string]string{ - "gcp.client.service": "", + "gcp.client.service": "foo", "gcp.client.version": getVersionClient(), "gcp.client.repo": "googleapis/google-cloud-go", }))