Skip to content

Commit

Permalink
otelgrpc: Set attribute with gRPC status code (#453)
Browse files Browse the repository at this point in the history
* Add gRPC status code attribute

* Adjust interceptor and gRPC tests

* Update CHANGELOG

Co-authored-by: Tyler Yahn <MrAlias@users.noreply.github.com>
  • Loading branch information
matej-g and MrAlias committed Nov 19, 2020
1 parent 1010e02 commit 1d3290d
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 46 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm

- Add semantic version to `Tracer` / `Meter` created by instrumentation packages `otelsaram`, `otelrestful`, `otelmongo`, `otelhttp` and `otelhttptrace`. (#412)
- Update instrumentation guidelines about tracer / meter semantic version. (#412)
- gRPC instrumentation sets span attribute `rpc.grpc.status_code`. (#453)

## Fixed

Expand Down
71 changes: 41 additions & 30 deletions instrumentation/google.golang.org/grpc/otelgrpc/grpc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/interop"
pb "google.golang.org/grpc/interop/grpc_testing"
"google.golang.org/grpc/test/bufconn"
Expand Down Expand Up @@ -136,9 +137,10 @@ func checkUnaryClientSpans(t *testing.T, spans []*tracetest.Span) {
},
}, noTimestamp(emptySpan.Events()))
assert.Equal(t, map[label.Key]label.Value{
semconv.RPCMethodKey: label.StringValue("EmptyCall"),
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
semconv.RPCMethodKey: label.StringValue("EmptyCall"),
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
otelgrpc.GRPCStatusCodeKey: label.Uint32Value(uint32(codes.OK)),
}, emptySpan.Attributes())

largeSpan := spans[1]
Expand All @@ -165,9 +167,10 @@ func checkUnaryClientSpans(t *testing.T, spans []*tracetest.Span) {
},
}, noTimestamp(largeSpan.Events()))
assert.Equal(t, map[label.Key]label.Value{
semconv.RPCMethodKey: label.StringValue("UnaryCall"),
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
semconv.RPCMethodKey: label.StringValue("UnaryCall"),
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
otelgrpc.GRPCStatusCodeKey: label.Uint32Value(uint32(codes.OK)),
}, largeSpan.Attributes())
}

Expand Down Expand Up @@ -214,9 +217,10 @@ func checkStreamClientSpans(t *testing.T, spans []*tracetest.Span) {
// client does not record an event for the server response.
}, noTimestamp(streamInput.Events()))
assert.Equal(t, map[label.Key]label.Value{
semconv.RPCMethodKey: label.StringValue("StreamingInputCall"),
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
semconv.RPCMethodKey: label.StringValue("StreamingInputCall"),
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
otelgrpc.GRPCStatusCodeKey: label.Uint32Value(uint32(codes.OK)),
}, streamInput.Attributes())

streamOutput := spans[1]
Expand Down Expand Up @@ -266,9 +270,10 @@ func checkStreamClientSpans(t *testing.T, spans []*tracetest.Span) {
},
}, noTimestamp(streamOutput.Events()))
assert.Equal(t, map[label.Key]label.Value{
semconv.RPCMethodKey: label.StringValue("StreamingOutputCall"),
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
semconv.RPCMethodKey: label.StringValue("StreamingOutputCall"),
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
otelgrpc.GRPCStatusCodeKey: label.Uint32Value(uint32(codes.OK)),
}, streamOutput.Attributes())

pingPong := spans[2]
Expand Down Expand Up @@ -341,9 +346,10 @@ func checkStreamClientSpans(t *testing.T, spans []*tracetest.Span) {
},
}, noTimestamp(pingPong.Events()))
assert.Equal(t, map[label.Key]label.Value{
semconv.RPCMethodKey: label.StringValue("FullDuplexCall"),
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
semconv.RPCMethodKey: label.StringValue("FullDuplexCall"),
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
otelgrpc.GRPCStatusCodeKey: label.Uint32Value(uint32(codes.OK)),
}, pingPong.Attributes())
}

Expand Down Expand Up @@ -397,9 +403,10 @@ func checkStreamServerSpans(t *testing.T, spans []*tracetest.Span) {
},
}, noTimestamp(streamInput.Events()))
assert.Equal(t, map[label.Key]label.Value{
semconv.RPCMethodKey: label.StringValue("StreamingInputCall"),
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
semconv.RPCMethodKey: label.StringValue("StreamingInputCall"),
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
otelgrpc.GRPCStatusCodeKey: label.Uint32Value(uint32(codes.OK)),
}, streamInput.Attributes())

streamOutput := spans[1]
Expand Down Expand Up @@ -449,9 +456,10 @@ func checkStreamServerSpans(t *testing.T, spans []*tracetest.Span) {
},
}, noTimestamp(streamOutput.Events()))
assert.Equal(t, map[label.Key]label.Value{
semconv.RPCMethodKey: label.StringValue("StreamingOutputCall"),
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
semconv.RPCMethodKey: label.StringValue("StreamingOutputCall"),
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
otelgrpc.GRPCStatusCodeKey: label.Uint32Value(uint32(codes.OK)),
}, streamOutput.Attributes())

pingPong := spans[2]
Expand Down Expand Up @@ -524,9 +532,10 @@ func checkStreamServerSpans(t *testing.T, spans []*tracetest.Span) {
},
}, noTimestamp(pingPong.Events()))
assert.Equal(t, map[label.Key]label.Value{
semconv.RPCMethodKey: label.StringValue("FullDuplexCall"),
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
semconv.RPCMethodKey: label.StringValue("FullDuplexCall"),
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
otelgrpc.GRPCStatusCodeKey: label.Uint32Value(uint32(codes.OK)),
}, pingPong.Attributes())
}

Expand Down Expand Up @@ -555,9 +564,10 @@ func checkUnaryServerSpans(t *testing.T, spans []*tracetest.Span) {
},
}, noTimestamp(emptySpan.Events()))
assert.Equal(t, map[label.Key]label.Value{
semconv.RPCMethodKey: label.StringValue("EmptyCall"),
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
semconv.RPCMethodKey: label.StringValue("EmptyCall"),
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
otelgrpc.GRPCStatusCodeKey: label.Uint32Value(uint32(codes.OK)),
}, emptySpan.Attributes())

largeSpan := spans[1]
Expand All @@ -584,9 +594,10 @@ func checkUnaryServerSpans(t *testing.T, spans []*tracetest.Span) {
},
}, noTimestamp(largeSpan.Events()))
assert.Equal(t, map[label.Key]label.Value{
semconv.RPCMethodKey: label.StringValue("UnaryCall"),
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
semconv.RPCMethodKey: label.StringValue("UnaryCall"),
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
otelgrpc.GRPCStatusCodeKey: label.Uint32Value(uint32(codes.OK)),
}, largeSpan.Attributes())
}

Expand Down
8 changes: 6 additions & 2 deletions instrumentation/google.golang.org/grpc/otelgrpc/grpctrace.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,12 @@ import (
"go.opentelemetry.io/otel/label"
)

// instrumentationName is the name of this instrumentation package.
const instrumentationName = "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
const (
// instrumentationName is the name of this instrumentation package.
instrumentationName = "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
// GRPCStatusCodeKey is convention for numeric status code of a gRPC request.
GRPCStatusCodeKey = label.Key("rpc.grpc.status_code")
)

// config is a group of options for this instrumentation.
type config struct {
Expand Down
17 changes: 17 additions & 0 deletions instrumentation/google.golang.org/grpc/otelgrpc/interceptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/golang/protobuf/proto" //nolint:staticcheck

"google.golang.org/grpc"
grpc_codes "google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/peer"
"google.golang.org/grpc/status"
Expand Down Expand Up @@ -104,6 +105,9 @@ func UnaryClientInterceptor(opts ...Option) grpc.UnaryClientInterceptor {
if err != nil {
s, _ := status.FromError(err)
span.SetStatus(codes.Error, s.Message())
span.SetAttributes(statusCodeAttr(s.Code()))
} else {
span.SetAttributes(statusCodeAttr(grpc_codes.OK))
}

return err
Expand Down Expand Up @@ -283,6 +287,9 @@ func StreamClientInterceptor(opts ...Option) grpc.StreamClientInterceptor {
if err != nil {
s, _ := status.FromError(err)
span.SetStatus(codes.Error, s.Message())
span.SetAttributes(statusCodeAttr(s.Code()))
} else {
span.SetAttributes(statusCodeAttr(grpc_codes.OK))
}

span.End()
Expand Down Expand Up @@ -327,8 +334,10 @@ func UnaryServerInterceptor(opts ...Option) grpc.UnaryServerInterceptor {
if err != nil {
s, _ := status.FromError(err)
span.SetStatus(codes.Error, s.Message())
span.SetAttributes(statusCodeAttr(s.Code()))
messageSent.Event(ctx, 1, s.Proto())
} else {
span.SetAttributes(statusCodeAttr(grpc_codes.OK))
messageSent.Event(ctx, 1, resp)
}

Expand Down Expand Up @@ -413,6 +422,9 @@ func StreamServerInterceptor(opts ...Option) grpc.StreamServerInterceptor {
if err != nil {
s, _ := status.FromError(err)
span.SetStatus(codes.Error, s.Message())
span.SetAttributes(statusCodeAttr(s.Code()))
} else {
span.SetAttributes(statusCodeAttr(grpc_codes.OK))
}

return err
Expand Down Expand Up @@ -475,3 +487,8 @@ func parseFullMethod(fullMethod string) (string, []label.KeyValue) {
}
return name, attrs
}

// statusCodeAttr returns status code attribute based on given gRPC code
func statusCodeAttr(c grpc_codes.Code) label.KeyValue {
return GRPCStatusCodeKey.Uint32(uint32(c))
}
Loading

0 comments on commit 1d3290d

Please sign in to comment.