Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions internal/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import (
"time"

"cloud.google.com/go/auth"
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"google.golang.org/api/internal/impersonate"
Expand Down Expand Up @@ -76,6 +78,11 @@ type DialSettings struct {

// TODO(b/372244283): Remove after b/358175516 has been fixed
EnableAsyncRefreshDryRun func()

// otelhttp/otelgrpc options
OpenTelemetryOpts []any
OpenTelemetryOptsGRPC []otelgrpc.Option
OpenTelemetryOptsHTTP []otelhttp.Option
}

// GetScopes returns the user-provided scopes, if set, or else falls back to the
Expand Down Expand Up @@ -181,6 +188,18 @@ func (ds *DialSettings) Validate() error {
if ds.ImpersonationConfig != nil && len(ds.ImpersonationConfig.Scopes) == 0 && len(ds.Scopes) == 0 {
return errors.New("WithImpersonatedCredentials requires scopes being provided")
}
for _, opt := range ds.OpenTelemetryOpts {
switch o := opt.(type) {
case otelhttp.Option:
ds.OpenTelemetryOptsHTTP = append(ds.OpenTelemetryOptsHTTP, o)
case otelgrpc.Option:
ds.OpenTelemetryOptsGRPC = append(ds.OpenTelemetryOptsGRPC, o)
default:
return errors.New("WithOpenTelemetryOpts options must be of type " +
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp.Option " +
"or go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc.Option")
}
}
return nil
}

Expand Down
11 changes: 11 additions & 0 deletions internal/settings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ import (

"google.golang.org/api/internal/impersonate"
"google.golang.org/grpc"
"google.golang.org/grpc/stats"

"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
)
Expand All @@ -38,6 +41,13 @@ func TestSettingsValidate(t *testing.T) {
{ClientCertSource: dummyGetClientCertificate},
{ImpersonationConfig: &impersonate.Config{Scopes: []string{"x"}}},
{ImpersonationConfig: &impersonate.Config{}, Scopes: []string{"x"}},
{OpenTelemetryOpts: []any{
otelgrpc.WithFilter(func(ri *stats.RPCTagInfo) bool {
return true
}),
otelhttp.WithFilter(func(ri *http.Request) bool {
return true
})}},
} {
err := ds.Validate()
if err != nil {
Expand Down Expand Up @@ -67,6 +77,7 @@ func TestSettingsValidate(t *testing.T) {
{ClientCertSource: dummyGetClientCertificate, GRPCDialOpts: []grpc.DialOption{grpc.WithInsecure()}},
{ClientCertSource: dummyGetClientCertificate, GRPCConnPoolSize: 1},
{ImpersonationConfig: &impersonate.Config{}},
{OpenTelemetryOpts: []any{"string"}},
} {
err := ds.Validate()
if err == nil {
Expand Down
17 changes: 17 additions & 0 deletions option/option.go
Original file line number Diff line number Diff line change
Expand Up @@ -414,3 +414,20 @@ type withLogger struct{ l *slog.Logger }
func (w withLogger) Apply(o *internal.DialSettings) {
o.Logger = w.l
}

// WithOpenTelemetryOpts returns a ClientOption that sets the options for
// the OpenTelemetry HTTP/gRPC client. This option is used to configure
// the OpenTelemetry HTTP/gRPC client instrumentation.
// It can accept any number of options, which can be
// go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp.Option or
// go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc.Option
func WithOpenTelemetryOpts(opts ...any) ClientOption {
return withOpenTelemetryOpts{opts}
}

type withOpenTelemetryOpts struct{ opts []any }

func (w withOpenTelemetryOpts) Apply(o *internal.DialSettings) {
o.OpenTelemetryOpts = make([]any, len(w.opts))
copy(o.OpenTelemetryOpts, w.opts)
}
20 changes: 20 additions & 0 deletions option/option_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,23 @@ func TestApplyClientCertSource(t *testing.T) {
t.Error(cmp.Diff(certGot, certWant, cmpopts.IgnoreUnexported(big.Int{}), cmpopts.IgnoreFields(tls.Certificate{}, "Leaf")))
}
}

func TestOpenTelemetryOpts(t *testing.T) {
otelOpts := []any{
"non-otelhttp-otelgrpc-option",
}
opts := []ClientOption{
WithOpenTelemetryOpts(otelOpts...),
}
var got internal.DialSettings
for _, opt := range opts {
opt.Apply(&got)
}
want := internal.DialSettings{
OpenTelemetryOpts: []any{},
}

if cmp.Equal(got, want) {
t.Error(cmp.Diff(got, want))
}
}
7 changes: 4 additions & 3 deletions transport/grpc/dial.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,9 @@ var (

// otelGRPCStatsHandler returns singleton otelStatsHandler for reuse across all
// dial connections.
func otelGRPCStatsHandler() stats.Handler {
func otelGRPCStatsHandler(opts []otelgrpc.Option) stats.Handler {
initOtelStatsHandlerOnce.Do(func() {
otelStatsHandler = otelgrpc.NewClientHandler()
otelStatsHandler = otelgrpc.NewClientHandler(opts...)
})
return otelStatsHandler
}
Expand Down Expand Up @@ -400,7 +400,8 @@ func addOpenTelemetryStatsHandler(opts []grpc.DialOption, settings *internal.Dia
if settings.TelemetryDisabled {
return opts
}
return append(opts, grpc.WithStatsHandler(otelGRPCStatsHandler()))
otelOpts := settings.OpenTelemetryOptsGRPC
return append(opts, grpc.WithStatsHandler(otelGRPCStatsHandler(otelOpts)))
}

// grpcTokenSource supplies PerRPCCredentials from an oauth.TokenSource.
Expand Down
4 changes: 3 additions & 1 deletion transport/http/dial.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ func newClientNewAuth(ctx context.Context, base http.RoundTripper, ds *internal.
if ds.UserAgent != "" {
headers.Set("User-Agent", ds.UserAgent)
}
// TODO: propagate settings.OpenTelemetryOptsHTTP
// see https://github.com/googleapis/google-api-go-client/pull/3130#discussion_r2091318522
client, err := httptransport.NewClient(&httptransport.Options{
DisableTelemetry: ds.TelemetryDisabled,
DisableAuthentication: ds.NoAuth,
Expand Down Expand Up @@ -306,7 +308,7 @@ func addOpenTelemetryTransport(trans http.RoundTripper, settings *internal.DialS
if settings.TelemetryDisabled {
return trans
}
return otelhttp.NewTransport(trans)
return otelhttp.NewTransport(trans, settings.OpenTelemetryOptsHTTP...)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For both this and the gRPC package this change is fine to make, but it won't actually enable the feature for most users -- that is because these packages, by default, delegate to our new auth library which has its own transport packages: https://pkg.go.dev/cloud.google.com/go/auth

At the very least I think we should add some TODOs and keep the issue open until this is plumbed all the way through to the new auth packages. See the newClientNewAuth function in this file as an example of how we delegate calls to this new library.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @codyoss! I added a todo.

}

// clonedTransport returns the given RoundTripper as a cloned *http.Transport.
Expand Down
Loading