Skip to content

Commit 1d3290d

Browse files
matej-gMrAlias
andauthored
otelgrpc: Set attribute with gRPC status code (#453)
* Add gRPC status code attribute * Adjust interceptor and gRPC tests * Update CHANGELOG Co-authored-by: Tyler Yahn <[email protected]>
1 parent 1010e02 commit 1d3290d

File tree

5 files changed

+125
-46
lines changed

5 files changed

+125
-46
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
1616

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

2021
## Fixed
2122

instrumentation/google.golang.org/grpc/otelgrpc/grpc_test.go

Lines changed: 41 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"github.com/stretchr/testify/assert"
2323
"github.com/stretchr/testify/require"
2424
"google.golang.org/grpc"
25+
"google.golang.org/grpc/codes"
2526
"google.golang.org/grpc/interop"
2627
pb "google.golang.org/grpc/interop/grpc_testing"
2728
"google.golang.org/grpc/test/bufconn"
@@ -136,9 +137,10 @@ func checkUnaryClientSpans(t *testing.T, spans []*tracetest.Span) {
136137
},
137138
}, noTimestamp(emptySpan.Events()))
138139
assert.Equal(t, map[label.Key]label.Value{
139-
semconv.RPCMethodKey: label.StringValue("EmptyCall"),
140-
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
141-
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
140+
semconv.RPCMethodKey: label.StringValue("EmptyCall"),
141+
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
142+
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
143+
otelgrpc.GRPCStatusCodeKey: label.Uint32Value(uint32(codes.OK)),
142144
}, emptySpan.Attributes())
143145

144146
largeSpan := spans[1]
@@ -165,9 +167,10 @@ func checkUnaryClientSpans(t *testing.T, spans []*tracetest.Span) {
165167
},
166168
}, noTimestamp(largeSpan.Events()))
167169
assert.Equal(t, map[label.Key]label.Value{
168-
semconv.RPCMethodKey: label.StringValue("UnaryCall"),
169-
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
170-
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
170+
semconv.RPCMethodKey: label.StringValue("UnaryCall"),
171+
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
172+
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
173+
otelgrpc.GRPCStatusCodeKey: label.Uint32Value(uint32(codes.OK)),
171174
}, largeSpan.Attributes())
172175
}
173176

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

222226
streamOutput := spans[1]
@@ -266,9 +270,10 @@ func checkStreamClientSpans(t *testing.T, spans []*tracetest.Span) {
266270
},
267271
}, noTimestamp(streamOutput.Events()))
268272
assert.Equal(t, map[label.Key]label.Value{
269-
semconv.RPCMethodKey: label.StringValue("StreamingOutputCall"),
270-
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
271-
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
273+
semconv.RPCMethodKey: label.StringValue("StreamingOutputCall"),
274+
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
275+
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
276+
otelgrpc.GRPCStatusCodeKey: label.Uint32Value(uint32(codes.OK)),
272277
}, streamOutput.Attributes())
273278

274279
pingPong := spans[2]
@@ -341,9 +346,10 @@ func checkStreamClientSpans(t *testing.T, spans []*tracetest.Span) {
341346
},
342347
}, noTimestamp(pingPong.Events()))
343348
assert.Equal(t, map[label.Key]label.Value{
344-
semconv.RPCMethodKey: label.StringValue("FullDuplexCall"),
345-
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
346-
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
349+
semconv.RPCMethodKey: label.StringValue("FullDuplexCall"),
350+
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
351+
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
352+
otelgrpc.GRPCStatusCodeKey: label.Uint32Value(uint32(codes.OK)),
347353
}, pingPong.Attributes())
348354
}
349355

@@ -397,9 +403,10 @@ func checkStreamServerSpans(t *testing.T, spans []*tracetest.Span) {
397403
},
398404
}, noTimestamp(streamInput.Events()))
399405
assert.Equal(t, map[label.Key]label.Value{
400-
semconv.RPCMethodKey: label.StringValue("StreamingInputCall"),
401-
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
402-
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
406+
semconv.RPCMethodKey: label.StringValue("StreamingInputCall"),
407+
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
408+
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
409+
otelgrpc.GRPCStatusCodeKey: label.Uint32Value(uint32(codes.OK)),
403410
}, streamInput.Attributes())
404411

405412
streamOutput := spans[1]
@@ -449,9 +456,10 @@ func checkStreamServerSpans(t *testing.T, spans []*tracetest.Span) {
449456
},
450457
}, noTimestamp(streamOutput.Events()))
451458
assert.Equal(t, map[label.Key]label.Value{
452-
semconv.RPCMethodKey: label.StringValue("StreamingOutputCall"),
453-
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
454-
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
459+
semconv.RPCMethodKey: label.StringValue("StreamingOutputCall"),
460+
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
461+
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
462+
otelgrpc.GRPCStatusCodeKey: label.Uint32Value(uint32(codes.OK)),
455463
}, streamOutput.Attributes())
456464

457465
pingPong := spans[2]
@@ -524,9 +532,10 @@ func checkStreamServerSpans(t *testing.T, spans []*tracetest.Span) {
524532
},
525533
}, noTimestamp(pingPong.Events()))
526534
assert.Equal(t, map[label.Key]label.Value{
527-
semconv.RPCMethodKey: label.StringValue("FullDuplexCall"),
528-
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
529-
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
535+
semconv.RPCMethodKey: label.StringValue("FullDuplexCall"),
536+
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
537+
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
538+
otelgrpc.GRPCStatusCodeKey: label.Uint32Value(uint32(codes.OK)),
530539
}, pingPong.Attributes())
531540
}
532541

@@ -555,9 +564,10 @@ func checkUnaryServerSpans(t *testing.T, spans []*tracetest.Span) {
555564
},
556565
}, noTimestamp(emptySpan.Events()))
557566
assert.Equal(t, map[label.Key]label.Value{
558-
semconv.RPCMethodKey: label.StringValue("EmptyCall"),
559-
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
560-
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
567+
semconv.RPCMethodKey: label.StringValue("EmptyCall"),
568+
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
569+
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
570+
otelgrpc.GRPCStatusCodeKey: label.Uint32Value(uint32(codes.OK)),
561571
}, emptySpan.Attributes())
562572

563573
largeSpan := spans[1]
@@ -584,9 +594,10 @@ func checkUnaryServerSpans(t *testing.T, spans []*tracetest.Span) {
584594
},
585595
}, noTimestamp(largeSpan.Events()))
586596
assert.Equal(t, map[label.Key]label.Value{
587-
semconv.RPCMethodKey: label.StringValue("UnaryCall"),
588-
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
589-
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
597+
semconv.RPCMethodKey: label.StringValue("UnaryCall"),
598+
semconv.RPCServiceKey: label.StringValue("grpc.testing.TestService"),
599+
semconv.RPCSystemGRPC.Key: semconv.RPCSystemGRPC.Value,
600+
otelgrpc.GRPCStatusCodeKey: label.Uint32Value(uint32(codes.OK)),
590601
}, largeSpan.Attributes())
591602
}
592603

instrumentation/google.golang.org/grpc/otelgrpc/grpctrace.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,12 @@ import (
2525
"go.opentelemetry.io/otel/label"
2626
)
2727

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

3135
// config is a group of options for this instrumentation.
3236
type config struct {

instrumentation/google.golang.org/grpc/otelgrpc/interceptor.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"github.com/golang/protobuf/proto" //nolint:staticcheck
2626

2727
"google.golang.org/grpc"
28+
grpc_codes "google.golang.org/grpc/codes"
2829
"google.golang.org/grpc/metadata"
2930
"google.golang.org/grpc/peer"
3031
"google.golang.org/grpc/status"
@@ -104,6 +105,9 @@ func UnaryClientInterceptor(opts ...Option) grpc.UnaryClientInterceptor {
104105
if err != nil {
105106
s, _ := status.FromError(err)
106107
span.SetStatus(codes.Error, s.Message())
108+
span.SetAttributes(statusCodeAttr(s.Code()))
109+
} else {
110+
span.SetAttributes(statusCodeAttr(grpc_codes.OK))
107111
}
108112

109113
return err
@@ -283,6 +287,9 @@ func StreamClientInterceptor(opts ...Option) grpc.StreamClientInterceptor {
283287
if err != nil {
284288
s, _ := status.FromError(err)
285289
span.SetStatus(codes.Error, s.Message())
290+
span.SetAttributes(statusCodeAttr(s.Code()))
291+
} else {
292+
span.SetAttributes(statusCodeAttr(grpc_codes.OK))
286293
}
287294

288295
span.End()
@@ -327,8 +334,10 @@ func UnaryServerInterceptor(opts ...Option) grpc.UnaryServerInterceptor {
327334
if err != nil {
328335
s, _ := status.FromError(err)
329336
span.SetStatus(codes.Error, s.Message())
337+
span.SetAttributes(statusCodeAttr(s.Code()))
330338
messageSent.Event(ctx, 1, s.Proto())
331339
} else {
340+
span.SetAttributes(statusCodeAttr(grpc_codes.OK))
332341
messageSent.Event(ctx, 1, resp)
333342
}
334343

@@ -413,6 +422,9 @@ func StreamServerInterceptor(opts ...Option) grpc.StreamServerInterceptor {
413422
if err != nil {
414423
s, _ := status.FromError(err)
415424
span.SetStatus(codes.Error, s.Message())
425+
span.SetAttributes(statusCodeAttr(s.Code()))
426+
} else {
427+
span.SetAttributes(statusCodeAttr(grpc_codes.OK))
416428
}
417429

418430
return err
@@ -475,3 +487,8 @@ func parseFullMethod(fullMethod string) (string, []label.KeyValue) {
475487
}
476488
return name, attrs
477489
}
490+
491+
// statusCodeAttr returns status code attribute based on given gRPC code
492+
func statusCodeAttr(c grpc_codes.Code) label.KeyValue {
493+
return GRPCStatusCodeKey.Uint32(uint32(c))
494+
}

0 commit comments

Comments
 (0)