-
Notifications
You must be signed in to change notification settings - Fork 492
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
HTTP Semconv migration Part2 Server - duplicate support #5400
Open
MadVikingGod
wants to merge
4
commits into
open-telemetry:main
Choose a base branch
from
MadVikingGod:mvg/semconv/server/dup
base: main
Could not load branches
Branch not found: {{ refName }}
Could not load tags
Nothing to show
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
246 changes: 246 additions & 0 deletions
246
instrumentation/net/http/otelhttp/internal/semconv/dup.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,246 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv" | ||
|
||
import ( | ||
"io" | ||
"net/http" | ||
"strings" | ||
|
||
"go.opentelemetry.io/otel/attribute" | ||
semconvOld "go.opentelemetry.io/otel/semconv/v1.20.0" | ||
semconvNew "go.opentelemetry.io/otel/semconv/v1.24.0" | ||
) | ||
|
||
type dupHTTPServer struct{} | ||
|
||
var _ HTTPServer = dupHTTPServer{} | ||
|
||
// 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. | ||
func (d dupHTTPServer) RequestTraceAttrs(server string, req *http.Request) []attribute.KeyValue { | ||
count := 6 // HostName/ServerAdddress, Scheme, Method | ||
var host string | ||
var p int | ||
if server == "" { | ||
host, p = splitHostPort(req.Host) | ||
} else { | ||
// Prioritize the primary server name. | ||
host, p = splitHostPort(server) | ||
if p < 0 { | ||
_, p = splitHostPort(req.Host) | ||
} | ||
} | ||
|
||
hostPort := requiredHTTPPort(req.TLS != nil, p) | ||
if hostPort > 0 { | ||
count += 2 | ||
} | ||
|
||
methodOld, methodNew, methodOriginal := d.method(req.Method) | ||
if methodOriginal != (attribute.KeyValue{}) { | ||
count++ | ||
} | ||
|
||
schemeOld, schemeNew := d.scheme(req.TLS != nil) | ||
|
||
peer, peerPort := splitHostPort(req.RemoteAddr) | ||
if peer != "" { | ||
// The Go HTTP server sets RemoteAddr to "IP:port", this will not be a | ||
// file-path that would be interpreted with a sock family. | ||
count += 2 | ||
if peerPort > 0 { | ||
count += 2 | ||
} | ||
} | ||
useragent := req.UserAgent() | ||
if useragent != "" { | ||
// This is the same between v1.20, and v1.24 | ||
count++ | ||
} | ||
|
||
clientIP := serverClientIP(req.Header.Get("X-Forwarded-For")) | ||
if clientIP != "" { | ||
count += 2 | ||
} | ||
|
||
if req.URL != nil && req.URL.Path != "" { | ||
count += 2 | ||
} | ||
|
||
protoName, protoVersion := netProtocol(req.Proto) | ||
if protoName != "" && protoName != "http" { | ||
count += 2 | ||
} | ||
if protoVersion != "" { | ||
count += 2 | ||
} | ||
|
||
attrs := make([]attribute.KeyValue, 0, count) | ||
attrs = append(attrs, | ||
semconvOld.NetHostName(host), | ||
semconvNew.ServerAddress(host), | ||
methodOld, | ||
methodNew, | ||
schemeOld, | ||
schemeNew, | ||
) | ||
|
||
if hostPort > 0 { | ||
attrs = append(attrs, | ||
semconvOld.NetHostPort(hostPort), | ||
semconvNew.ServerPort(hostPort), | ||
) | ||
} | ||
if methodOriginal != (attribute.KeyValue{}) { | ||
attrs = append(attrs, methodOriginal) | ||
} | ||
|
||
if peer != "" { | ||
attrs = append(attrs, | ||
semconvOld.NetSockPeerAddr(peer), | ||
semconvNew.NetworkPeerAddress(peer), | ||
) | ||
if peerPort > 0 { | ||
attrs = append(attrs, | ||
semconvOld.NetSockPeerPort(peerPort), | ||
semconvNew.NetworkPeerPort(peerPort), | ||
) | ||
} | ||
} | ||
|
||
if useragent != "" { | ||
// This is the same between v1.20, and v1.24 | ||
attrs = append(attrs, semconvNew.UserAgentOriginal(useragent)) | ||
} | ||
|
||
if clientIP != "" { | ||
attrs = append(attrs, | ||
semconvOld.HTTPClientIP(clientIP), | ||
semconvNew.ClientAddress(clientIP), | ||
) | ||
} | ||
|
||
if req.URL != nil && req.URL.Path != "" { | ||
attrs = append(attrs, | ||
semconvOld.HTTPTarget(req.URL.Path), | ||
semconvNew.URLPath(req.URL.Path), | ||
) | ||
} | ||
|
||
if protoName != "" && protoName != "http" { | ||
attrs = append(attrs, | ||
semconvOld.NetProtocolName(protoName), | ||
semconvNew.NetworkProtocolName(protoName), | ||
) | ||
} | ||
if protoVersion != "" { | ||
attrs = append(attrs, | ||
semconvOld.NetProtocolVersion(protoVersion), | ||
semconvNew.NetworkProtocolVersion(protoVersion), | ||
) | ||
} | ||
|
||
return attrs | ||
} | ||
|
||
func (d dupHTTPServer) method(method string) (attribute.KeyValue, attribute.KeyValue, attribute.KeyValue) { | ||
if method == "" { | ||
return semconvOld.HTTPMethod(http.MethodGet), semconvNew.HTTPRequestMethodGet, attribute.KeyValue{} | ||
} | ||
|
||
attr, found := methodLookup[method] | ||
if found { | ||
return semconvOld.HTTPMethod(method), attr, attribute.KeyValue{} | ||
} | ||
|
||
attr, found = methodLookup[strings.ToUpper(method)] | ||
if !found { | ||
attr = semconvNew.HTTPRequestMethodGet | ||
} | ||
|
||
return semconvOld.HTTPMethod(method), attr, semconvNew.HTTPRequestMethodOriginal(method) | ||
} | ||
|
||
func (d dupHTTPServer) scheme(https bool) (attribute.KeyValue, attribute.KeyValue) { // nolint:revive | ||
if https { | ||
return semconvOld.HTTPSchemeHTTPS, semconvNew.URLScheme("https") | ||
} | ||
return semconvOld.HTTPSchemeHTTP, semconvNew.URLScheme("http") | ||
} | ||
|
||
// 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. | ||
func (d dupHTTPServer) ResponseTraceAttrs(resp ResponseTelemetry) []attribute.KeyValue { | ||
count := 0 | ||
|
||
if resp.ReadBytes > 0 { | ||
count += 2 | ||
} | ||
if resp.ReadError != nil && resp.ReadError != io.EOF { | ||
// This is not in the semantic conventions, but is historically provided | ||
count++ | ||
} | ||
if resp.WriteBytes > 0 { | ||
count += 2 | ||
} | ||
if resp.WriteError != nil && resp.WriteError != io.EOF { | ||
// This is not in the semantic conventions, but is historically provided | ||
count++ | ||
} | ||
if resp.StatusCode > 0 { | ||
count += 2 | ||
} | ||
|
||
attributes := make([]attribute.KeyValue, 0, count) | ||
|
||
if resp.ReadBytes > 0 { | ||
attributes = append(attributes, | ||
semconvOld.HTTPRequestContentLength(int(resp.ReadBytes)), | ||
semconvNew.HTTPRequestBodySize(int(resp.ReadBytes)), | ||
) | ||
} | ||
if resp.ReadError != nil && resp.ReadError != io.EOF { | ||
// This is not in the semantic conventions, but is historically provided | ||
attributes = append(attributes, attribute.String("http.read_error", resp.ReadError.Error())) | ||
} | ||
if resp.WriteBytes > 0 { | ||
attributes = append(attributes, | ||
semconvOld.HTTPResponseContentLength(int(resp.WriteBytes)), | ||
semconvNew.HTTPResponseBodySize(int(resp.WriteBytes)), | ||
) | ||
} | ||
if resp.WriteError != nil && resp.WriteError != io.EOF { | ||
// This is not in the semantic conventions, but is historically provided | ||
attributes = append(attributes, attribute.String("http.write_error", resp.WriteError.Error())) | ||
} | ||
if resp.StatusCode > 0 { | ||
attributes = append(attributes, | ||
semconvOld.HTTPStatusCode(resp.StatusCode), | ||
semconvNew.HTTPResponseStatusCode(resp.StatusCode), | ||
) | ||
} | ||
|
||
return attributes | ||
} | ||
|
||
// Route returns the attribute for the route. | ||
func (d dupHTTPServer) Route(route string) attribute.KeyValue { | ||
return semconvNew.HTTPRoute(route) | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
p < 0
doesn't invalidate the host. Shouldn't this check the host value as well first?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a copy of the logic from the current implementation.
If there is no server, then it will take what it can from the
request.Host
. If there is a server (server !="") it will use the host from that, and if the port is present it will use that.Notice that last
splitHostPort
only will set port.