Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 2 additions & 1 deletion auth/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ require (
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel/metric v1.40.0 // indirect
golang.org/x/crypto v0.47.0 // indirect
golang.org/x/oauth2 v0.32.0 // indirect
golang.org/x/oauth2 v0.34.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sys v0.40.0 // indirect
golang.org/x/text v0.33.0 // indirect
google.golang.org/api v0.264.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect
)
10 changes: 8 additions & 2 deletions auth/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY=
golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
Expand All @@ -70,6 +70,12 @@ golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/api v0.264.0 h1:+Fo3DQXBK8gLdf8rFZ3uLu39JpOnhvzJrLMQSoSYZJM=
google.golang.org/api v0.264.0/go.mod h1:fAU1xtNNisHgOF5JooAs8rRaTkl2rT3uaoNGo9NS3R8=
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 h1:VQZ/yAbAtjkHgH80teYd2em3xtIkkHd7ZhqfH2N9CsM=
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409/go.mod h1:rxKD3IEILWEu3P44seeNOAwZN4SaoKaQ/2eTg4mM6EM=
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M=
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
Expand Down
82 changes: 80 additions & 2 deletions auth/grpctransport/grpctransport.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,26 @@ import (
"log/slog"
"net/http"
"os"
"strconv"
"strings"

"cloud.google.com/go/auth"
"cloud.google.com/go/auth/credentials"
"cloud.google.com/go/auth/internal"
"cloud.google.com/go/auth/internal/transport"
"cloud.google.com/go/auth/internal/transport/headers"
"github.com/googleapis/gax-go/v2"
"github.com/googleapis/gax-go/v2/callctx"
"github.com/googleapis/gax-go/v2/internallog"
"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/trace"
"google.golang.org/grpc"
grpccreds "google.golang.org/grpc/credentials"
grpcinsecure "google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/stats"
"google.golang.org/grpc/status"
)

const (
Expand Down Expand Up @@ -198,7 +207,7 @@ type InternalOptions struct {
// service.
DefaultScopes []string
// SkipValidation bypasses validation on Options. It should only be used
// internally for clients that needs more control over their transport.
// internally for clients that need more control over their transport.
SkipValidation bool
// TelemetryAttributes specifies a map of telemetry attributes to be added
// to all OpenTelemetry signals, such as tracing and metrics, for purposes
Expand Down Expand Up @@ -430,5 +439,74 @@ func addOpenTelemetryStatsHandler(dialOpts []grpc.DialOption, opts *Options) []g
if opts.DisableTelemetry {
return dialOpts
}
return append(dialOpts, grpc.WithStatsHandler(otelgrpc.NewClientHandler()))
if !gax.IsFeatureEnabled("TRACING") {
return append(dialOpts, grpc.WithStatsHandler(otelgrpc.NewClientHandler()))
}
var attrs []attribute.KeyValue
if opts.InternalOptions != nil {
for k, v := range opts.InternalOptions.TelemetryAttributes {
Comment thread
quartzmo marked this conversation as resolved.
Outdated
attrs = append(attrs, attribute.String(k, v))
}
}
otelOpts := []otelgrpc.Option{
otelgrpc.WithSpanAttributes(attrs...),
}
return append(dialOpts, grpc.WithStatsHandler(&otelHandler{
Handler: otelgrpc.NewClientHandler(otelOpts...),
}))
}

// otelHandler is a wrapper around the OpenTelemetry gRPC client handler that
// adds custom Google Cloud-specific attributes to spans and metrics.
type otelHandler struct {
stats.Handler
}

// TagRPC intercepts the RPC start to extract dynamic attributes like resource
// name and retry count from the outgoing context metadata and attach them to
// the current span.
func (h *otelHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context {
ctx = h.Handler.TagRPC(ctx, info)
span := trace.SpanFromContext(ctx)
if !span.IsRecording() {
return ctx
}
if resName, ok := callctx.TelemetryFromContext(ctx, "resource_name"); ok {
span.SetAttributes(attribute.String("gcp.resource.name", resName))
Comment thread
quartzmo marked this conversation as resolved.
Outdated
}
if resendCountStr, ok := callctx.TelemetryFromContext(ctx, "resend_count"); ok {
if count, err := strconv.Atoi(resendCountStr); err == nil {
span.SetAttributes(attribute.Int("gcp.grpc.resend_count", count))
}
}
Comment thread
quartzmo marked this conversation as resolved.
return ctx
}

// HandleRPC intercepts the RPC completion to capture and format error-related
// attributes ensuring they conform to Google Cloud observability standards.
func (h *otelHandler) HandleRPC(ctx context.Context, s stats.RPCStats) {
end, ok := s.(*stats.End)
if !ok || end.Error == nil {
Comment thread
quartzmo marked this conversation as resolved.
Outdated
h.Handler.HandleRPC(ctx, s)
return
}
span := trace.SpanFromContext(ctx)
if !span.IsRecording() {
h.Handler.HandleRPC(ctx, s)
return
}
// Extract status code and message
st, _ := status.FromError(end.Error)
span.SetAttributes(
attribute.String("error.type", strings.ToUpper(st.Code().String())),
attribute.String("status.message", st.Message()),
attribute.String("grpc.status", strings.ToUpper(st.Code().String())),
Comment thread
quartzmo marked this conversation as resolved.
Outdated
attribute.String("exception.type", fmt.Sprintf("%T", end.Error)),
)
if md, ok := metadata.FromOutgoingContext(ctx); ok {
if v := md[":authority"]; len(v) > 0 {
span.SetAttributes(attribute.String("url.domain", v[0]))
Comment thread
quartzmo marked this conversation as resolved.
Outdated
}
}
Comment thread
quartzmo marked this conversation as resolved.
Outdated
h.Handler.HandleRPC(ctx, s)
}
Loading
Loading