Skip to content
Merged
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

## [Unreleased]

### Added

- `WithSpanKind` option in `go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc` to override the default span kind. (#8506)

### Fixed

- Enforce that `client_certificate_file` and `client_key_file` are provided together in `go.opentelemetry.io/contrib/otelconf`. (#8450)
Expand Down
13 changes: 13 additions & 0 deletions instrumentation/google.golang.org/grpc/otelgrpc/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type config struct {
Propagators propagation.TextMapPropagator
TracerProvider trace.TracerProvider
MeterProvider metric.MeterProvider
SpanKind trace.SpanKind
SpanStartOptions []trace.SpanStartOption
SpanAttributes []attribute.KeyValue
MetricAttributes []attribute.KeyValue
Expand Down Expand Up @@ -181,6 +182,18 @@ func WithSpanOptions(opts ...trace.SpanStartOption) Option {
})
}

// WithSpanKind returns an Option to set the span kind for spans created by
// the handler.
//
// By default, [NewServerHandler] creates spans with
// [trace.SpanKindServer] and [NewClientHandler] creates spans with
// [trace.SpanKindClient].
func WithSpanKind(sk trace.SpanKind) Option {
return optionFunc(func(c *config) {
c.SpanKind = sk
})
}

// WithSpanAttributes returns an Option to add custom attributes to the spans.
func WithSpanAttributes(a ...attribute.KeyValue) Option {
return optionFunc(func(c *config) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ type serverHandler struct {
// NewServerHandler creates a stats.Handler for a gRPC server.
func NewServerHandler(opts ...Option) stats.Handler {
c := newConfig(opts)
if c.SpanKind == trace.SpanKindUnspecified {
c.SpanKind = trace.SpanKindServer
}

h := &serverHandler{config: c}

h.tracer = c.TracerProvider.Tracer(
Expand Down Expand Up @@ -104,7 +108,7 @@ func (h *serverHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) cont
spanAttributes := make([]attribute.KeyValue, 0, len(attrs)+len(h.SpanAttributes))
spanAttributes = append(append(spanAttributes, attrs...), h.SpanAttributes...)
opts := []trace.SpanStartOption{
trace.WithSpanKind(trace.SpanKindServer),
trace.WithSpanKind(h.SpanKind),
trace.WithAttributes(spanAttributes...),
}
if h.PublicEndpoint || (h.PublicEndpointFn != nil && h.PublicEndpointFn(ctx, info)) {
Expand Down Expand Up @@ -161,6 +165,10 @@ type clientHandler struct {
// NewClientHandler creates a stats.Handler for a gRPC client.
func NewClientHandler(opts ...Option) stats.Handler {
c := newConfig(opts)
if c.SpanKind == trace.SpanKindUnspecified {
c.SpanKind = trace.SpanKindClient
}

h := &clientHandler{config: c}

h.tracer = c.TracerProvider.Tracer(
Expand Down Expand Up @@ -210,7 +218,7 @@ func (h *clientHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) cont
ctx, _ = h.tracer.Start(
ctx,
name,
trace.WithSpanKind(trace.SpanKindClient),
trace.WithSpanKind(h.SpanKind),
trace.WithAttributes(spanAttributes...),
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,92 @@ func TestWithPublicEndpointFn(t *testing.T) {
}
}

func TestWithSpanKind(t *testing.T) {
tests := []struct {
name string
handler func(...Option) stats.Handler
opt Option
wantSpanKind trace.SpanKind
defaultKind trace.SpanKind
defaultKindStr string
}{
{
name: "ServerHandler with default kind",
handler: NewServerHandler,
opt: nil,
wantSpanKind: trace.SpanKindServer,
defaultKind: trace.SpanKindServer,
defaultKindStr: "server",
},
{
name: "ServerHandler with Internal kind",
handler: NewServerHandler,
opt: WithSpanKind(trace.SpanKindInternal),
wantSpanKind: trace.SpanKindInternal,
},
{
name: "ServerHandler with Consumer kind",
handler: NewServerHandler,
opt: WithSpanKind(trace.SpanKindConsumer),
wantSpanKind: trace.SpanKindConsumer,
},
{
name: "ClientHandler with default kind",
handler: NewClientHandler,
opt: nil,
wantSpanKind: trace.SpanKindClient,
defaultKind: trace.SpanKindClient,
defaultKindStr: "client",
},
{
name: "ClientHandler with Internal kind",
handler: NewClientHandler,
opt: WithSpanKind(trace.SpanKindInternal),
wantSpanKind: trace.SpanKindInternal,
},
{
name: "ClientHandler with Producer kind",
handler: NewClientHandler,
opt: WithSpanKind(trace.SpanKindProducer),
wantSpanKind: trace.SpanKindProducer,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
spanRecorder := tracetest.NewSpanRecorder()
provider := sdktrace.NewTracerProvider(
sdktrace.WithSpanProcessor(spanRecorder),
)

opts := []Option{WithTracerProvider(provider)}
if tt.opt != nil {
opts = append(opts, tt.opt)
}

h := tt.handler(opts...)

ctx := h.TagRPC(t.Context(), &stats.RPCTagInfo{
FullMethodName: "some.package/Method",
FailFast: true,
})

h.HandleRPC(ctx, &stats.End{
Client: false,
BeginTime: time.Time{},
EndTime: time.Time{},
Trailer: metadata.MD{},
Error: nil,
})

require.NoError(t, spanRecorder.ForceFlush(ctx))
spans := spanRecorder.Ended()
require.Len(t, spans, 1)
assert.Equal(t, tt.wantSpanKind, spans[0].SpanKind())
})
}
}

func TestNilProviderOption(t *testing.T) {
// Passing a nil TracerProvider or MeterProvider should not panic and
// should use the global provider instead.
Expand Down