Skip to content

Commit 1b7cbb0

Browse files
committed
Merge branch 'main' into Issue_6976
2 parents d78e5cd + a262a05 commit 1b7cbb0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+1238
-366
lines changed

CHANGELOG.md

+1
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
- Add `WithAttributes` option to set instrumentation scope attributes on the created `log.Logger` in `go.opentelemetry.io/contrib/bridges/otellogrus`. (#6966)
1717
- Add `WithAttributes` option to set instrumentation scope attributes on the created `log.Logger` in `go.opentelemetry.io/contrib/bridges/otellogr`. (#6967)
1818
- Add the `WithGinMetricAttributes` option to allow setting dynamic, per-request metric attributes based on `*gin.Context` in `go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin`. (#6932)
19+
- Use Gin's own `ClientIP` method to detect the client's IP, which supports custom proxy headers in `go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin`. (#6095)
1920

2021
### Changed
2122

instrumentation/github.com/emicklei/go-restful/otelrestful/internal/semconv/bench_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,6 @@ func BenchmarkHTTPServerRequest(b *testing.B) {
4343
b.ReportAllocs()
4444
b.ResetTimer()
4545
for i := 0; i < b.N; i++ {
46-
benchHTTPServerRequestResults = serv.RequestTraceAttrs("", req)
46+
benchHTTPServerRequestResults = serv.RequestTraceAttrs("", req, RequestTraceAttrsOpts{})
4747
}
4848
}

instrumentation/github.com/emicklei/go-restful/otelrestful/internal/semconv/env.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,9 @@ type HTTPServer struct {
6161
//
6262
// If the primary server name is not known, server should be an empty string.
6363
// The req Host will be used to determine the server instead.
64-
func (s HTTPServer) RequestTraceAttrs(server string, req *http.Request) []attribute.KeyValue {
64+
func (s HTTPServer) RequestTraceAttrs(server string, req *http.Request, opts RequestTraceAttrsOpts) []attribute.KeyValue {
6565
if s.duplicate {
66-
return append(OldHTTPServer{}.RequestTraceAttrs(server, req), CurrentHTTPServer{}.RequestTraceAttrs(server, req)...)
66+
return append(OldHTTPServer{}.RequestTraceAttrs(server, req), CurrentHTTPServer{}.RequestTraceAttrs(server, req, opts)...)
6767
}
6868
return OldHTTPServer{}.RequestTraceAttrs(server, req)
6969
}

instrumentation/github.com/emicklei/go-restful/otelrestful/internal/semconv/env_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ func TestHTTPServerDoesNotPanic(t *testing.T) {
4242
req, err := http.NewRequest("GET", "http://example.com", nil)
4343
require.NoError(t, err)
4444

45-
_ = tt.server.RequestTraceAttrs("stuff", req)
45+
_ = tt.server.RequestTraceAttrs("stuff", req, RequestTraceAttrsOpts{})
4646
_ = tt.server.ResponseTraceAttrs(ResponseTelemetry{StatusCode: 200})
4747
tt.server.RecordMetrics(context.Background(), ServerMetricData{
4848
ServerName: "stuff",
@@ -250,7 +250,7 @@ func BenchmarkRecordMetrics(b *testing.B) {
250250
for _, bm := range benchmarks {
251251
b.Run(bm.name, func(b *testing.B) {
252252
req, _ := http.NewRequest("GET", "http://example.com", nil)
253-
_ = bm.server.RequestTraceAttrs("stuff", req)
253+
_ = bm.server.RequestTraceAttrs("stuff", req, RequestTraceAttrsOpts{})
254254
_ = bm.server.ResponseTraceAttrs(ResponseTelemetry{StatusCode: 200})
255255
ctx := context.Background()
256256
b.ReportAllocs()

instrumentation/github.com/emicklei/go-restful/otelrestful/internal/semconv/httpconv.go

+19-3
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ import (
2020
semconvNew "go.opentelemetry.io/otel/semconv/v1.26.0"
2121
)
2222

23+
type RequestTraceAttrsOpts struct {
24+
// If set, this is used as value for the "http.client_ip" attribute.
25+
HTTPClientIP string
26+
}
27+
2328
type CurrentHTTPServer struct{}
2429

2530
// RequestTraceAttrs returns trace attributes for an HTTP request received by a
@@ -38,7 +43,7 @@ type CurrentHTTPServer struct{}
3843
//
3944
// If the primary server name is not known, server should be an empty string.
4045
// The req Host will be used to determine the server instead.
41-
func (n CurrentHTTPServer) RequestTraceAttrs(server string, req *http.Request) []attribute.KeyValue {
46+
func (n CurrentHTTPServer) RequestTraceAttrs(server string, req *http.Request, opts RequestTraceAttrsOpts) []attribute.KeyValue {
4247
count := 3 // ServerAddress, Method, Scheme
4348

4449
var host string
@@ -65,7 +70,8 @@ func (n CurrentHTTPServer) RequestTraceAttrs(server string, req *http.Request) [
6570

6671
scheme := n.scheme(req.TLS != nil)
6772

68-
if peer, peerPort := SplitHostPort(req.RemoteAddr); peer != "" {
73+
peer, peerPort := SplitHostPort(req.RemoteAddr)
74+
if peer != "" {
6975
// The Go HTTP server sets RemoteAddr to "IP:port", this will not be a
7076
// file-path that would be interpreted with a sock family.
7177
count++
@@ -79,7 +85,17 @@ func (n CurrentHTTPServer) RequestTraceAttrs(server string, req *http.Request) [
7985
count++
8086
}
8187

82-
clientIP := serverClientIP(req.Header.Get("X-Forwarded-For"))
88+
// For client IP, use, in order:
89+
// 1. The value passed in the options
90+
// 2. The value in the X-Forwarded-For header
91+
// 3. The peer address
92+
clientIP := opts.HTTPClientIP
93+
if clientIP == "" {
94+
clientIP = serverClientIP(req.Header.Get("X-Forwarded-For"))
95+
if clientIP == "" {
96+
clientIP = peer
97+
}
98+
}
8399
if clientIP != "" {
84100
count++
85101
}

instrumentation/github.com/emicklei/go-restful/otelrestful/internal/semconv/httpconv_test.go

+52-1
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ func TestRequestTraceAttrs_HTTPRoute(t *testing.T) {
263263
req.Pattern = "/high/cardinality/path/{id}"
264264

265265
var found bool
266-
for _, attr := range (CurrentHTTPServer{}).RequestTraceAttrs("", req) {
266+
for _, attr := range (CurrentHTTPServer{}).RequestTraceAttrs("", req, RequestTraceAttrsOpts{}) {
267267
if attr.Key != "http.route" {
268268
continue
269269
}
@@ -272,3 +272,54 @@ func TestRequestTraceAttrs_HTTPRoute(t *testing.T) {
272272
}
273273
require.True(t, found)
274274
}
275+
276+
func TestRequestTraceAttrs_ClientIP(t *testing.T) {
277+
for _, tt := range []struct {
278+
name string
279+
requestModifierFn func(r *http.Request)
280+
requestTraceOpts RequestTraceAttrsOpts
281+
282+
wantClientIP string
283+
}{
284+
{
285+
name: "with a client IP from the network",
286+
wantClientIP: "1.2.3.4",
287+
},
288+
{
289+
name: "with a client IP from x-forwarded-for header",
290+
requestModifierFn: func(r *http.Request) {
291+
r.Header.Add("X-Forwarded-For", "5.6.7.8")
292+
},
293+
wantClientIP: "5.6.7.8",
294+
},
295+
{
296+
name: "with a client IP in options",
297+
requestModifierFn: func(r *http.Request) {
298+
r.Header.Add("X-Forwarded-For", "5.6.7.8")
299+
},
300+
requestTraceOpts: RequestTraceAttrsOpts{
301+
HTTPClientIP: "9.8.7.6",
302+
},
303+
wantClientIP: "9.8.7.6",
304+
},
305+
} {
306+
t.Run(tt.name, func(t *testing.T) {
307+
req := httptest.NewRequest("GET", "/example", nil)
308+
req.RemoteAddr = "1.2.3.4:5678"
309+
310+
if tt.requestModifierFn != nil {
311+
tt.requestModifierFn(req)
312+
}
313+
314+
var found bool
315+
for _, attr := range (CurrentHTTPServer{}).RequestTraceAttrs("", req, tt.requestTraceOpts) {
316+
if attr.Key != "client.address" {
317+
continue
318+
}
319+
found = true
320+
assert.Equal(t, tt.wantClientIP, attr.Value.AsString())
321+
}
322+
require.True(t, found)
323+
})
324+
}
325+
}

instrumentation/github.com/emicklei/go-restful/otelrestful/internal/semconv/test/common_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,5 +67,5 @@ func testTraceRequest(t *testing.T, serv semconv.HTTPServer, want func(testServe
6767
clientIP: clientIP,
6868
}
6969

70-
assert.ElementsMatch(t, want(srvReq), serv.RequestTraceAttrs("", req))
70+
assert.ElementsMatch(t, want(srvReq), serv.RequestTraceAttrs("", req, semconv.RequestTraceAttrsOpts{}))
7171
}

instrumentation/github.com/emicklei/go-restful/otelrestful/internal/semconv/v1.20.0.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ type OldHTTPServer struct{}
3838
// If the primary server name is not known, server should be an empty string.
3939
// The req Host will be used to determine the server instead.
4040
func (o OldHTTPServer) RequestTraceAttrs(server string, req *http.Request) []attribute.KeyValue {
41-
return semconvutil.HTTPServerRequest(server, req)
41+
return semconvutil.HTTPServerRequest(server, req, semconvutil.HTTPServerRequestOptions{})
4242
}
4343

4444
func (o OldHTTPServer) NetworkTransportAttr(network string) attribute.KeyValue {

instrumentation/github.com/emicklei/go-restful/otelrestful/internal/semconvutil/httpconv.go

+19-4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ import (
1616
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
1717
)
1818

19+
type HTTPServerRequestOptions struct {
20+
// If set, this is used as value for the "http.client_ip" attribute.
21+
HTTPClientIP string
22+
}
23+
1924
// HTTPClientResponse returns trace attributes for an HTTP response received by a
2025
// client from a server. It will return the following attributes if the related
2126
// values are defined in resp: "http.status.code",
@@ -75,8 +80,8 @@ func HTTPClientStatus(code int) (codes.Code, string) {
7580
// "http.target", "net.host.name". The following attributes are returned if
7681
// they related values are defined in req: "net.host.port", "net.sock.peer.addr",
7782
// "net.sock.peer.port", "user_agent.original", "http.client_ip".
78-
func HTTPServerRequest(server string, req *http.Request) []attribute.KeyValue {
79-
return hc.ServerRequest(server, req)
83+
func HTTPServerRequest(server string, req *http.Request, opts HTTPServerRequestOptions) []attribute.KeyValue {
84+
return hc.ServerRequest(server, req, opts)
8085
}
8186

8287
// HTTPServerRequestMetrics returns metric attributes for an HTTP request received by a
@@ -305,7 +310,7 @@ func (c *httpConv) ClientRequestMetrics(req *http.Request) []attribute.KeyValue
305310
// related values are defined in req: "net.host.port", "net.sock.peer.addr",
306311
// "net.sock.peer.port", "user_agent.original", "http.client_ip",
307312
// "net.protocol.name", "net.protocol.version".
308-
func (c *httpConv) ServerRequest(server string, req *http.Request) []attribute.KeyValue {
313+
func (c *httpConv) ServerRequest(server string, req *http.Request, opts HTTPServerRequestOptions) []attribute.KeyValue {
309314
/* The following semantic conventions are returned if present:
310315
http.method string
311316
http.scheme string
@@ -358,7 +363,17 @@ func (c *httpConv) ServerRequest(server string, req *http.Request) []attribute.K
358363
n++
359364
}
360365

361-
clientIP := serverClientIP(req.Header.Get("X-Forwarded-For"))
366+
// For client IP, use, in order:
367+
// 1. The value passed in the options
368+
// 2. The value in the X-Forwarded-For header
369+
// 3. The peer address
370+
clientIP := opts.HTTPClientIP
371+
if clientIP == "" {
372+
clientIP = serverClientIP(req.Header.Get("X-Forwarded-For"))
373+
if clientIP == "" {
374+
clientIP = peer
375+
}
376+
}
362377
if clientIP != "" {
363378
n++
364379
}

0 commit comments

Comments
 (0)