Skip to content

Commit ef8063b

Browse files
authored
otelgrpc: Add peer attributes to spans generated by stats handlers (#4873)
1 parent bfae9a3 commit ef8063b

File tree

3 files changed

+44
-2
lines changed

3 files changed

+44
-2
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
1111
### Added
1212

1313
- Add the new `go.opentelemetry.io/contrib/instrgen` package to provide auto-generated source code instrumentation. (#3068, #3108)
14+
- Add peer attributes to spans recorded by `NewClientHandler`, `NewServerHandler` in `go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc`. (#4873)
1415

1516
## [1.22.0/0.47.0/0.16.0/0.2.0] - 2024-01-18
1617

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

+5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"time"
2121

2222
grpc_codes "google.golang.org/grpc/codes"
23+
"google.golang.org/grpc/peer"
2324
"google.golang.org/grpc/stats"
2425
"google.golang.org/grpc/status"
2526

@@ -179,6 +180,10 @@ func (c *config) handleRPC(ctx context.Context, rs stats.RPCStats, isServer bool
179180
)
180181
}
181182
case *stats.OutTrailer:
183+
case *stats.OutHeader:
184+
if p, ok := peer.FromContext(ctx); ok {
185+
span.SetAttributes(peerAttr(p.Addr.String())...)
186+
}
182187
case *stats.End:
183188
var rpcStatusAttr attribute.KeyValue
184189

instrumentation/google.golang.org/grpc/otelgrpc/test/grpc_stats_handler_test.go

+38-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"context"
1919
"io"
2020
"net"
21+
"strconv"
2122
"sync"
2223
"testing"
2324

@@ -74,7 +75,7 @@ func TestStatsHandler(t *testing.T) {
7475
doCalls(client)
7576

7677
t.Run("ClientSpans", func(t *testing.T) {
77-
checkClientSpans(t, clientSR.Ended())
78+
checkClientSpans(t, clientSR.Ended(), listener.Addr().String())
7879
})
7980

8081
t.Run("ClientMetrics", func(t *testing.T) {
@@ -90,9 +91,14 @@ func TestStatsHandler(t *testing.T) {
9091
})
9192
}
9293

93-
func checkClientSpans(t *testing.T, spans []trace.ReadOnlySpan) {
94+
func checkClientSpans(t *testing.T, spans []trace.ReadOnlySpan, addr string) {
9495
require.Len(t, spans, 5)
9596

97+
host, p, err := net.SplitHostPort(addr)
98+
require.NoError(t, err)
99+
port, err := strconv.Atoi(p)
100+
require.NoError(t, err)
101+
96102
emptySpan := spans[0]
97103
assert.False(t, emptySpan.EndTime().IsZero())
98104
assert.Equal(t, "grpc.testing.TestService/EmptyCall", emptySpan.Name())
@@ -121,6 +127,8 @@ func checkClientSpans(t *testing.T, spans []trace.ReadOnlySpan) {
121127
semconv.RPCServiceKey.String("grpc.testing.TestService"),
122128
otelgrpc.RPCSystemGRPC,
123129
otelgrpc.GRPCStatusCodeKey.Int64(int64(codes.OK)),
130+
semconv.NetSockPeerAddr(host),
131+
semconv.NetSockPeerPort(port),
124132
}, emptySpan.Attributes())
125133

126134
largeSpan := spans[1]
@@ -151,6 +159,8 @@ func checkClientSpans(t *testing.T, spans []trace.ReadOnlySpan) {
151159
semconv.RPCServiceKey.String("grpc.testing.TestService"),
152160
otelgrpc.RPCSystemGRPC,
153161
otelgrpc.GRPCStatusCodeKey.Int64(int64(codes.OK)),
162+
semconv.NetSockPeerAddr(host),
163+
semconv.NetSockPeerPort(port),
154164
}, largeSpan.Attributes())
155165

156166
streamInput := spans[2]
@@ -209,6 +219,8 @@ func checkClientSpans(t *testing.T, spans []trace.ReadOnlySpan) {
209219
semconv.RPCServiceKey.String("grpc.testing.TestService"),
210220
otelgrpc.RPCSystemGRPC,
211221
otelgrpc.GRPCStatusCodeKey.Int64(int64(codes.OK)),
222+
semconv.NetSockPeerAddr(host),
223+
semconv.NetSockPeerPort(port),
212224
}, streamInput.Attributes())
213225

214226
streamOutput := spans[3]
@@ -266,6 +278,8 @@ func checkClientSpans(t *testing.T, spans []trace.ReadOnlySpan) {
266278
semconv.RPCServiceKey.String("grpc.testing.TestService"),
267279
otelgrpc.RPCSystemGRPC,
268280
otelgrpc.GRPCStatusCodeKey.Int64(int64(codes.OK)),
281+
semconv.NetSockPeerAddr(host),
282+
semconv.NetSockPeerPort(port),
269283
}, streamOutput.Attributes())
270284

271285
pingPong := spans[4]
@@ -350,6 +364,8 @@ func checkClientSpans(t *testing.T, spans []trace.ReadOnlySpan) {
350364
semconv.RPCServiceKey.String("grpc.testing.TestService"),
351365
otelgrpc.RPCSystemGRPC,
352366
otelgrpc.GRPCStatusCodeKey.Int64(int64(codes.OK)),
367+
semconv.NetSockPeerAddr(host),
368+
semconv.NetSockPeerPort(port),
353369
}, pingPong.Attributes())
354370
}
355371

@@ -379,11 +395,15 @@ func checkServerSpans(t *testing.T, spans []trace.ReadOnlySpan) {
379395
},
380396
},
381397
}, emptySpan.Events())
398+
port, ok := findAttribute(emptySpan.Attributes(), semconv.NetSockPeerPortKey)
399+
assert.True(t, ok)
382400
assert.ElementsMatch(t, []attribute.KeyValue{
383401
semconv.RPCMethodKey.String("EmptyCall"),
384402
semconv.RPCServiceKey.String("grpc.testing.TestService"),
385403
otelgrpc.RPCSystemGRPC,
386404
otelgrpc.GRPCStatusCodeKey.Int64(int64(codes.OK)),
405+
semconv.NetSockPeerAddr("127.0.0.1"),
406+
port,
387407
}, emptySpan.Attributes())
388408

389409
largeSpan := spans[1]
@@ -409,11 +429,15 @@ func checkServerSpans(t *testing.T, spans []trace.ReadOnlySpan) {
409429
},
410430
},
411431
}, largeSpan.Events())
432+
port, ok = findAttribute(largeSpan.Attributes(), semconv.NetSockPeerPortKey)
433+
assert.True(t, ok)
412434
assert.ElementsMatch(t, []attribute.KeyValue{
413435
semconv.RPCMethodKey.String("UnaryCall"),
414436
semconv.RPCServiceKey.String("grpc.testing.TestService"),
415437
otelgrpc.RPCSystemGRPC,
416438
otelgrpc.GRPCStatusCodeKey.Int64(int64(codes.OK)),
439+
semconv.NetSockPeerAddr("127.0.0.1"),
440+
port,
417441
}, largeSpan.Attributes())
418442

419443
streamInput := spans[2]
@@ -467,11 +491,15 @@ func checkServerSpans(t *testing.T, spans []trace.ReadOnlySpan) {
467491
},
468492
// client does not record an event for the server response.
469493
}, streamInput.Events())
494+
port, ok = findAttribute(streamInput.Attributes(), semconv.NetSockPeerPortKey)
495+
assert.True(t, ok)
470496
assert.ElementsMatch(t, []attribute.KeyValue{
471497
semconv.RPCMethodKey.String("StreamingInputCall"),
472498
semconv.RPCServiceKey.String("grpc.testing.TestService"),
473499
otelgrpc.RPCSystemGRPC,
474500
otelgrpc.GRPCStatusCodeKey.Int64(int64(codes.OK)),
501+
semconv.NetSockPeerAddr("127.0.0.1"),
502+
port,
475503
}, streamInput.Attributes())
476504

477505
streamOutput := spans[3]
@@ -524,11 +552,15 @@ func checkServerSpans(t *testing.T, spans []trace.ReadOnlySpan) {
524552
},
525553
},
526554
}, streamOutput.Events())
555+
port, ok = findAttribute(streamOutput.Attributes(), semconv.NetSockPeerPortKey)
556+
assert.True(t, ok)
527557
assert.ElementsMatch(t, []attribute.KeyValue{
528558
semconv.RPCMethodKey.String("StreamingOutputCall"),
529559
semconv.RPCServiceKey.String("grpc.testing.TestService"),
530560
otelgrpc.RPCSystemGRPC,
531561
otelgrpc.GRPCStatusCodeKey.Int64(int64(codes.OK)),
562+
semconv.NetSockPeerAddr("127.0.0.1"),
563+
port,
532564
}, streamOutput.Attributes())
533565

534566
pingPong := spans[4]
@@ -608,11 +640,15 @@ func checkServerSpans(t *testing.T, spans []trace.ReadOnlySpan) {
608640
},
609641
},
610642
}, pingPong.Events())
643+
port, ok = findAttribute(pingPong.Attributes(), semconv.NetSockPeerPortKey)
644+
assert.True(t, ok)
611645
assert.ElementsMatch(t, []attribute.KeyValue{
612646
semconv.RPCMethodKey.String("FullDuplexCall"),
613647
semconv.RPCServiceKey.String("grpc.testing.TestService"),
614648
otelgrpc.RPCSystemGRPC,
615649
otelgrpc.GRPCStatusCodeKey.Int64(int64(codes.OK)),
650+
semconv.NetSockPeerAddr("127.0.0.1"),
651+
port,
616652
}, pingPong.Attributes())
617653
}
618654

0 commit comments

Comments
 (0)