-
Notifications
You must be signed in to change notification settings - Fork 576
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
HTTP Semconv migration Part1 Server - v1.20.0 support (#5333)
* added interface around semconvutil --------- Signed-off-by: Aaron Clawson <[email protected]>
- Loading branch information
1 parent
0ebeecf
commit 30ed923
Showing
8 changed files
with
446 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
45 changes: 45 additions & 0 deletions
45
instrumentation/net/http/otelhttp/internal/semconv/bench_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package semconv | ||
|
||
import ( | ||
"net/http" | ||
"net/url" | ||
"testing" | ||
|
||
"go.opentelemetry.io/otel/attribute" | ||
) | ||
|
||
var benchHTTPServerRequestResults []attribute.KeyValue | ||
|
||
// BenchmarkHTTPServerRequest allows comparison between different version of the HTTP server. | ||
// To use an alternative start this test with OTEL_HTTP_CLIENT_COMPATIBILITY_MODE set to the | ||
// version under test. | ||
func BenchmarkHTTPServerRequest(b *testing.B) { | ||
// Request was generated from TestHTTPServerRequest request. | ||
req := &http.Request{ | ||
Method: http.MethodGet, | ||
URL: &url.URL{ | ||
Path: "/", | ||
}, | ||
Proto: "HTTP/1.1", | ||
ProtoMajor: 1, | ||
ProtoMinor: 1, | ||
Header: http.Header{ | ||
"User-Agent": []string{"Go-http-client/1.1"}, | ||
"Accept-Encoding": []string{"gzip"}, | ||
}, | ||
Body: http.NoBody, | ||
Host: "127.0.0.1:39093", | ||
RemoteAddr: "127.0.0.1:38738", | ||
RequestURI: "/", | ||
} | ||
serv := NewHTTPServer() | ||
|
||
b.ReportAllocs() | ||
b.ResetTimer() | ||
for i := 0; i < b.N; i++ { | ||
benchHTTPServerRequestResults = serv.RequestTraceAttrs("", req) | ||
} | ||
} |
67 changes: 67 additions & 0 deletions
67
instrumentation/net/http/otelhttp/internal/semconv/common_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package semconv | ||
|
||
import ( | ||
"net/http" | ||
"net/http/httptest" | ||
"net/url" | ||
"strconv" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
|
||
"go.opentelemetry.io/otel/attribute" | ||
) | ||
|
||
type testServerReq struct { | ||
hostname string | ||
serverPort int | ||
peerAddr string | ||
peerPort int | ||
clientIP string | ||
} | ||
|
||
func testTraceRequest(t *testing.T, serv HTTPServer, want func(testServerReq) []attribute.KeyValue) { | ||
t.Helper() | ||
|
||
got := make(chan *http.Request, 1) | ||
handler := func(w http.ResponseWriter, r *http.Request) { | ||
got <- r | ||
close(got) | ||
w.WriteHeader(http.StatusOK) | ||
} | ||
|
||
srv := httptest.NewServer(http.HandlerFunc(handler)) | ||
defer srv.Close() | ||
|
||
srvURL, err := url.Parse(srv.URL) | ||
require.NoError(t, err) | ||
srvPort, err := strconv.ParseInt(srvURL.Port(), 10, 32) | ||
require.NoError(t, err) | ||
|
||
resp, err := srv.Client().Get(srv.URL) | ||
require.NoError(t, err) | ||
require.NoError(t, resp.Body.Close()) | ||
|
||
req := <-got | ||
peer, peerPort := splitHostPort(req.RemoteAddr) | ||
|
||
const user = "alice" | ||
req.SetBasicAuth(user, "pswrd") | ||
|
||
const clientIP = "127.0.0.5" | ||
req.Header.Add("X-Forwarded-For", clientIP) | ||
|
||
srvReq := testServerReq{ | ||
hostname: srvURL.Hostname(), | ||
serverPort: int(srvPort), | ||
peerAddr: peer, | ||
peerPort: peerPort, | ||
clientIP: clientIP, | ||
} | ||
|
||
assert.ElementsMatch(t, want(srvReq), serv.RequestTraceAttrs("", req)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv" | ||
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
|
||
"go.opentelemetry.io/otel/attribute" | ||
"go.opentelemetry.io/otel/codes" | ||
) | ||
|
||
type ResponseTelemetry struct { | ||
StatusCode int | ||
ReadBytes int64 | ||
ReadError error | ||
WriteBytes int64 | ||
WriteError error | ||
} | ||
|
||
type HTTPServer interface { | ||
// RequestTraceAttrs returns trace attributes for an HTTP request received by a | ||
// server. | ||
// | ||
// The server must be the primary server name if it is known. For example this | ||
// would be the ServerName directive | ||
// (https://httpd.apache.org/docs/2.4/mod/core.html#servername) for an Apache | ||
// server, and the server_name directive | ||
// (http://nginx.org/en/docs/http/ngx_http_core_module.html#server_name) for an | ||
// nginx server. More generically, the primary server name would be the host | ||
// header value that matches the default virtual host of an HTTP server. It | ||
// should include the host identifier and if a port is used to route to the | ||
// server that port identifier should be included as an appropriate port | ||
// suffix. | ||
// | ||
// If the primary server name is not known, server should be an empty string. | ||
// The req Host will be used to determine the server instead. | ||
RequestTraceAttrs(server string, req *http.Request) []attribute.KeyValue | ||
|
||
// ResponseTraceAttrs returns trace attributes for telemetry from an HTTP response. | ||
// | ||
// If any of the fields in the ResponseTelemetry are not set the attribute will be omitted. | ||
ResponseTraceAttrs(ResponseTelemetry) []attribute.KeyValue | ||
|
||
// Route returns the attribute for the route. | ||
Route(string) attribute.KeyValue | ||
} | ||
|
||
// var warnOnce = sync.Once{} | ||
|
||
func NewHTTPServer() HTTPServer { | ||
// TODO (#5331): Detect version based on environment variable OTEL_HTTP_CLIENT_COMPATIBILITY_MODE. | ||
// TODO (#5331): Add warning of use of a deprecated version of Semantic Versions. | ||
return oldHTTPServer{} | ||
} | ||
|
||
// ServerStatus returns a span status code and message for an HTTP status code | ||
// value returned by a server. Status codes in the 400-499 range are not | ||
// returned as errors. | ||
func ServerStatus(code int) (codes.Code, string) { | ||
if code < 100 || code >= 600 { | ||
return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code) | ||
} | ||
if code >= 500 { | ||
return codes.Error, "" | ||
} | ||
return codes.Unset, "" | ||
} |
49 changes: 49 additions & 0 deletions
49
instrumentation/net/http/otelhttp/internal/semconv/util.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv" | ||
|
||
import ( | ||
"net" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
// splitHostPort splits a network address hostport of the form "host", | ||
// "host%zone", "[host]", "[host%zone], "host:port", "host%zone:port", | ||
// "[host]:port", "[host%zone]:port", or ":port" into host or host%zone and | ||
// port. | ||
// | ||
// An empty host is returned if it is not provided or unparsable. A negative | ||
// port is returned if it is not provided or unparsable. | ||
func splitHostPort(hostport string) (host string, port int) { | ||
port = -1 | ||
|
||
if strings.HasPrefix(hostport, "[") { | ||
addrEnd := strings.LastIndex(hostport, "]") | ||
if addrEnd < 0 { | ||
// Invalid hostport. | ||
return | ||
} | ||
if i := strings.LastIndex(hostport[addrEnd:], ":"); i < 0 { | ||
host = hostport[1:addrEnd] | ||
return | ||
} | ||
} else { | ||
if i := strings.LastIndex(hostport, ":"); i < 0 { | ||
host = hostport | ||
return | ||
} | ||
} | ||
|
||
host, pStr, err := net.SplitHostPort(hostport) | ||
if err != nil { | ||
return | ||
} | ||
|
||
p, err := strconv.ParseUint(pStr, 10, 16) | ||
if err != nil { | ||
return | ||
} | ||
return host, int(p) | ||
} |
41 changes: 41 additions & 0 deletions
41
instrumentation/net/http/otelhttp/internal/semconv/util_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package semconv | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestSplitHostPort(t *testing.T) { | ||
tests := []struct { | ||
hostport string | ||
host string | ||
port int | ||
}{ | ||
{"", "", -1}, | ||
{":8080", "", 8080}, | ||
{"127.0.0.1", "127.0.0.1", -1}, | ||
{"www.example.com", "www.example.com", -1}, | ||
{"127.0.0.1%25en0", "127.0.0.1%25en0", -1}, | ||
{"[]", "", -1}, // Ensure this doesn't panic. | ||
{"[fe80::1", "", -1}, | ||
{"[fe80::1]", "fe80::1", -1}, | ||
{"[fe80::1%25en0]", "fe80::1%25en0", -1}, | ||
{"[fe80::1]:8080", "fe80::1", 8080}, | ||
{"[fe80::1]::", "", -1}, // Too many colons. | ||
{"127.0.0.1:", "127.0.0.1", -1}, | ||
{"127.0.0.1:port", "127.0.0.1", -1}, | ||
{"127.0.0.1:8080", "127.0.0.1", 8080}, | ||
{"www.example.com:8080", "www.example.com", 8080}, | ||
{"127.0.0.1%25en0:8080", "127.0.0.1%25en0", 8080}, | ||
} | ||
|
||
for _, test := range tests { | ||
h, p := splitHostPort(test.hostport) | ||
assert.Equal(t, test.host, h, test.hostport) | ||
assert.Equal(t, test.port, p, test.hostport) | ||
} | ||
} |
Oops, something went wrong.