mirror of
https://gitea.com/Lydanne/buildx.git
synced 2025-07-09 21:17:09 +08:00
vendor: update buildkit to v0.19.0-rc1
Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
@ -1,16 +1,5 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otelhttptrace // import "go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace"
|
||||
|
||||
|
@ -1,16 +1,5 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otelhttptrace // import "go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace"
|
||||
|
||||
@ -25,7 +14,7 @@ import (
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
@ -226,6 +215,10 @@ func (ct *clientTracer) start(hook, spanName string, attrs ...attribute.KeyValue
|
||||
|
||||
func (ct *clientTracer) end(hook string, err error, attrs ...attribute.KeyValue) {
|
||||
if !ct.useSpans {
|
||||
// sometimes end may be called without previous start
|
||||
if ct.root == nil {
|
||||
ct.root = trace.SpanFromContext(ct.Context)
|
||||
}
|
||||
if err != nil {
|
||||
attrs = append(attrs, attribute.String(hook+".error", err.Error()))
|
||||
}
|
||||
|
@ -1,16 +1,5 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otelhttptrace // import "go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace"
|
||||
|
||||
@ -23,7 +12,7 @@ import (
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/baggage"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
|
@ -1,16 +1,5 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package semconvutil // import "go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/internal/semconvutil"
|
||||
|
||||
|
@ -2,18 +2,7 @@
|
||||
// source: internal/shared/semconvutil/httpconv.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package semconvutil // import "go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/internal/semconvutil"
|
||||
|
||||
@ -24,7 +13,7 @@ import (
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
|
||||
)
|
||||
|
||||
// HTTPClientResponse returns trace attributes for an HTTP response received by a
|
||||
@ -43,14 +32,22 @@ func HTTPClientResponse(resp *http.Response) []attribute.KeyValue {
|
||||
}
|
||||
|
||||
// HTTPClientRequest returns trace attributes for an HTTP request made by a client.
|
||||
// The following attributes are always returned: "http.url", "http.flavor",
|
||||
// "http.method", "net.peer.name". The following attributes are returned if the
|
||||
// related values are defined in req: "net.peer.port", "http.user_agent",
|
||||
// "http.request_content_length", "enduser.id".
|
||||
// The following attributes are always returned: "http.url", "http.method",
|
||||
// "net.peer.name". The following attributes are returned if the related values
|
||||
// are defined in req: "net.peer.port", "user_agent.original",
|
||||
// "http.request_content_length".
|
||||
func HTTPClientRequest(req *http.Request) []attribute.KeyValue {
|
||||
return hc.ClientRequest(req)
|
||||
}
|
||||
|
||||
// HTTPClientRequestMetrics returns metric attributes for an HTTP request made by a client.
|
||||
// The following attributes are always returned: "http.method", "net.peer.name".
|
||||
// The following attributes are returned if the
|
||||
// related values are defined in req: "net.peer.port".
|
||||
func HTTPClientRequestMetrics(req *http.Request) []attribute.KeyValue {
|
||||
return hc.ClientRequestMetrics(req)
|
||||
}
|
||||
|
||||
// HTTPClientStatus returns a span status code and message for an HTTP status code
|
||||
// value received by a client.
|
||||
func HTTPClientStatus(code int) (codes.Code, string) {
|
||||
@ -75,10 +72,9 @@ func HTTPClientStatus(code int) (codes.Code, string) {
|
||||
// The req Host will be used to determine the server instead.
|
||||
//
|
||||
// The following attributes are always returned: "http.method", "http.scheme",
|
||||
// "http.flavor", "http.target", "net.host.name". The following attributes are
|
||||
// returned if they related values are defined in req: "net.host.port",
|
||||
// "net.sock.peer.addr", "net.sock.peer.port", "http.user_agent", "enduser.id",
|
||||
// "http.client_ip".
|
||||
// "http.target", "net.host.name". The following attributes are returned if
|
||||
// they related values are defined in req: "net.host.port", "net.sock.peer.addr",
|
||||
// "net.sock.peer.port", "user_agent.original", "http.client_ip".
|
||||
func HTTPServerRequest(server string, req *http.Request) []attribute.KeyValue {
|
||||
return hc.ServerRequest(server, req)
|
||||
}
|
||||
@ -101,8 +97,8 @@ func HTTPServerRequest(server string, req *http.Request) []attribute.KeyValue {
|
||||
// The req Host will be used to determine the server instead.
|
||||
//
|
||||
// The following attributes are always returned: "http.method", "http.scheme",
|
||||
// "http.flavor", "net.host.name". The following attributes are
|
||||
// returned if they related values are defined in req: "net.host.port".
|
||||
// "net.host.name". The following attributes are returned if they related
|
||||
// values are defined in req: "net.host.port".
|
||||
func HTTPServerRequestMetrics(server string, req *http.Request) []attribute.KeyValue {
|
||||
return hc.ServerRequestMetrics(server, req)
|
||||
}
|
||||
@ -114,44 +110,12 @@ func HTTPServerStatus(code int) (codes.Code, string) {
|
||||
return hc.ServerStatus(code)
|
||||
}
|
||||
|
||||
// HTTPRequestHeader returns the contents of h as attributes.
|
||||
//
|
||||
// Instrumentation should require an explicit configuration of which headers to
|
||||
// captured and then prune what they pass here. Including all headers can be a
|
||||
// security risk - explicit configuration helps avoid leaking sensitive
|
||||
// information.
|
||||
//
|
||||
// The User-Agent header is already captured in the http.user_agent attribute
|
||||
// from ClientRequest and ServerRequest. Instrumentation may provide an option
|
||||
// to capture that header here even though it is not recommended. Otherwise,
|
||||
// instrumentation should filter that out of what is passed.
|
||||
func HTTPRequestHeader(h http.Header) []attribute.KeyValue {
|
||||
return hc.RequestHeader(h)
|
||||
}
|
||||
|
||||
// HTTPResponseHeader returns the contents of h as attributes.
|
||||
//
|
||||
// Instrumentation should require an explicit configuration of which headers to
|
||||
// captured and then prune what they pass here. Including all headers can be a
|
||||
// security risk - explicit configuration helps avoid leaking sensitive
|
||||
// information.
|
||||
//
|
||||
// The User-Agent header is already captured in the http.user_agent attribute
|
||||
// from ClientRequest and ServerRequest. Instrumentation may provide an option
|
||||
// to capture that header here even though it is not recommended. Otherwise,
|
||||
// instrumentation should filter that out of what is passed.
|
||||
func HTTPResponseHeader(h http.Header) []attribute.KeyValue {
|
||||
return hc.ResponseHeader(h)
|
||||
}
|
||||
|
||||
// httpConv are the HTTP semantic convention attributes defined for a version
|
||||
// of the OpenTelemetry specification.
|
||||
type httpConv struct {
|
||||
NetConv *netConv
|
||||
|
||||
EnduserIDKey attribute.Key
|
||||
HTTPClientIPKey attribute.Key
|
||||
HTTPFlavorKey attribute.Key
|
||||
HTTPMethodKey attribute.Key
|
||||
HTTPRequestContentLengthKey attribute.Key
|
||||
HTTPResponseContentLengthKey attribute.Key
|
||||
@ -161,15 +125,13 @@ type httpConv struct {
|
||||
HTTPStatusCodeKey attribute.Key
|
||||
HTTPTargetKey attribute.Key
|
||||
HTTPURLKey attribute.Key
|
||||
HTTPUserAgentKey attribute.Key
|
||||
UserAgentOriginalKey attribute.Key
|
||||
}
|
||||
|
||||
var hc = &httpConv{
|
||||
NetConv: nc,
|
||||
|
||||
EnduserIDKey: semconv.EnduserIDKey,
|
||||
HTTPClientIPKey: semconv.HTTPClientIPKey,
|
||||
HTTPFlavorKey: semconv.HTTPFlavorKey,
|
||||
HTTPMethodKey: semconv.HTTPMethodKey,
|
||||
HTTPRequestContentLengthKey: semconv.HTTPRequestContentLengthKey,
|
||||
HTTPResponseContentLengthKey: semconv.HTTPResponseContentLengthKey,
|
||||
@ -179,7 +141,7 @@ var hc = &httpConv{
|
||||
HTTPStatusCodeKey: semconv.HTTPStatusCodeKey,
|
||||
HTTPTargetKey: semconv.HTTPTargetKey,
|
||||
HTTPURLKey: semconv.HTTPURLKey,
|
||||
HTTPUserAgentKey: semconv.HTTPUserAgentKey,
|
||||
UserAgentOriginalKey: semconv.UserAgentOriginalKey,
|
||||
}
|
||||
|
||||
// ClientResponse returns attributes for an HTTP response received by a client
|
||||
@ -193,6 +155,10 @@ var hc = &httpConv{
|
||||
//
|
||||
// append(ClientResponse(resp), ClientRequest(resp.Request)...)
|
||||
func (c *httpConv) ClientResponse(resp *http.Response) []attribute.KeyValue {
|
||||
/* The following semantic conventions are returned if present:
|
||||
http.status_code int
|
||||
http.response_content_length int
|
||||
*/
|
||||
var n int
|
||||
if resp.StatusCode > 0 {
|
||||
n++
|
||||
@ -212,11 +178,31 @@ func (c *httpConv) ClientResponse(resp *http.Response) []attribute.KeyValue {
|
||||
}
|
||||
|
||||
// ClientRequest returns attributes for an HTTP request made by a client. The
|
||||
// following attributes are always returned: "http.url", "http.flavor",
|
||||
// "http.method", "net.peer.name". The following attributes are returned if the
|
||||
// related values are defined in req: "net.peer.port", "http.user_agent",
|
||||
// "http.request_content_length", "enduser.id".
|
||||
// following attributes are always returned: "http.url", "http.method",
|
||||
// "net.peer.name". The following attributes are returned if the related values
|
||||
// are defined in req: "net.peer.port", "user_agent.original",
|
||||
// "http.request_content_length", "user_agent.original".
|
||||
func (c *httpConv) ClientRequest(req *http.Request) []attribute.KeyValue {
|
||||
/* The following semantic conventions are returned if present:
|
||||
http.method string
|
||||
user_agent.original string
|
||||
http.url string
|
||||
net.peer.name string
|
||||
net.peer.port int
|
||||
http.request_content_length int
|
||||
*/
|
||||
|
||||
/* The following semantic conventions are not returned:
|
||||
http.status_code This requires the response. See ClientResponse.
|
||||
http.response_content_length This requires the response. See ClientResponse.
|
||||
net.sock.family This requires the socket used.
|
||||
net.sock.peer.addr This requires the socket used.
|
||||
net.sock.peer.name This requires the socket used.
|
||||
net.sock.peer.port This requires the socket used.
|
||||
http.resend_count This is something outside of a single request.
|
||||
net.protocol.name The value is the Request is ignored, and the go client will always use "http".
|
||||
net.protocol.version The value in the Request is ignored, and the go client will always use 1.1 or 2.0.
|
||||
*/
|
||||
n := 3 // URL, peer name, proto, and method.
|
||||
var h string
|
||||
if req.URL != nil {
|
||||
@ -234,14 +220,10 @@ func (c *httpConv) ClientRequest(req *http.Request) []attribute.KeyValue {
|
||||
if req.ContentLength > 0 {
|
||||
n++
|
||||
}
|
||||
userID, _, hasUserID := req.BasicAuth()
|
||||
if hasUserID {
|
||||
n++
|
||||
}
|
||||
|
||||
attrs := make([]attribute.KeyValue, 0, n)
|
||||
|
||||
attrs = append(attrs, c.method(req.Method))
|
||||
attrs = append(attrs, c.flavor(req.Proto))
|
||||
|
||||
var u string
|
||||
if req.URL != nil {
|
||||
@ -260,15 +242,43 @@ func (c *httpConv) ClientRequest(req *http.Request) []attribute.KeyValue {
|
||||
}
|
||||
|
||||
if useragent != "" {
|
||||
attrs = append(attrs, c.HTTPUserAgentKey.String(useragent))
|
||||
attrs = append(attrs, c.UserAgentOriginalKey.String(useragent))
|
||||
}
|
||||
|
||||
if l := req.ContentLength; l > 0 {
|
||||
attrs = append(attrs, c.HTTPRequestContentLengthKey.Int64(l))
|
||||
}
|
||||
|
||||
if hasUserID {
|
||||
attrs = append(attrs, c.EnduserIDKey.String(userID))
|
||||
return attrs
|
||||
}
|
||||
|
||||
// ClientRequestMetrics returns metric attributes for an HTTP request made by a client. The
|
||||
// following attributes are always returned: "http.method", "net.peer.name".
|
||||
// The following attributes are returned if the related values
|
||||
// are defined in req: "net.peer.port".
|
||||
func (c *httpConv) ClientRequestMetrics(req *http.Request) []attribute.KeyValue {
|
||||
/* The following semantic conventions are returned if present:
|
||||
http.method string
|
||||
net.peer.name string
|
||||
net.peer.port int
|
||||
*/
|
||||
|
||||
n := 2 // method, peer name.
|
||||
var h string
|
||||
if req.URL != nil {
|
||||
h = req.URL.Host
|
||||
}
|
||||
peer, p := firstHostPort(h, req.Header.Get("Host"))
|
||||
port := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", p)
|
||||
if port > 0 {
|
||||
n++
|
||||
}
|
||||
|
||||
attrs := make([]attribute.KeyValue, 0, n)
|
||||
attrs = append(attrs, c.method(req.Method), c.NetConv.PeerName(peer))
|
||||
|
||||
if port > 0 {
|
||||
attrs = append(attrs, c.NetConv.PeerPort(port))
|
||||
}
|
||||
|
||||
return attrs
|
||||
@ -291,18 +301,35 @@ func (c *httpConv) ClientRequest(req *http.Request) []attribute.KeyValue {
|
||||
// The req Host will be used to determine the server instead.
|
||||
//
|
||||
// The following attributes are always returned: "http.method", "http.scheme",
|
||||
// "http.flavor", "http.target", "net.host.name". The following attributes are
|
||||
// returned if they related values are defined in req: "net.host.port",
|
||||
// "net.sock.peer.addr", "net.sock.peer.port", "http.user_agent", "enduser.id",
|
||||
// "http.client_ip".
|
||||
// "http.target", "net.host.name". The following attributes are returned if they
|
||||
// related values are defined in req: "net.host.port", "net.sock.peer.addr",
|
||||
// "net.sock.peer.port", "user_agent.original", "http.client_ip",
|
||||
// "net.protocol.name", "net.protocol.version".
|
||||
func (c *httpConv) ServerRequest(server string, req *http.Request) []attribute.KeyValue {
|
||||
// TODO: This currently does not add the specification required
|
||||
// `http.target` attribute. It has too high of a cardinality to safely be
|
||||
// added. An alternate should be added, or this comment removed, when it is
|
||||
// addressed by the specification. If it is ultimately decided to continue
|
||||
// not including the attribute, the HTTPTargetKey field of the httpConv
|
||||
// should be removed as well.
|
||||
/* The following semantic conventions are returned if present:
|
||||
http.method string
|
||||
http.scheme string
|
||||
net.host.name string
|
||||
net.host.port int
|
||||
net.sock.peer.addr string
|
||||
net.sock.peer.port int
|
||||
user_agent.original string
|
||||
http.client_ip string
|
||||
net.protocol.name string Note: not set if the value is "http".
|
||||
net.protocol.version string
|
||||
http.target string Note: doesn't include the query parameter.
|
||||
*/
|
||||
|
||||
/* The following semantic conventions are not returned:
|
||||
http.status_code This requires the response.
|
||||
http.request_content_length This requires the len() of body, which can mutate it.
|
||||
http.response_content_length This requires the response.
|
||||
http.route This is not available.
|
||||
net.sock.peer.name This would require a DNS lookup.
|
||||
net.sock.host.addr The request doesn't have access to the underlying socket.
|
||||
net.sock.host.port The request doesn't have access to the underlying socket.
|
||||
|
||||
*/
|
||||
n := 4 // Method, scheme, proto, and host name.
|
||||
var host string
|
||||
var p int
|
||||
@ -330,19 +357,31 @@ func (c *httpConv) ServerRequest(server string, req *http.Request) []attribute.K
|
||||
if useragent != "" {
|
||||
n++
|
||||
}
|
||||
userID, _, hasUserID := req.BasicAuth()
|
||||
if hasUserID {
|
||||
n++
|
||||
}
|
||||
|
||||
clientIP := serverClientIP(req.Header.Get("X-Forwarded-For"))
|
||||
if clientIP != "" {
|
||||
n++
|
||||
}
|
||||
|
||||
var target string
|
||||
if req.URL != nil {
|
||||
target = req.URL.Path
|
||||
if target != "" {
|
||||
n++
|
||||
}
|
||||
}
|
||||
protoName, protoVersion := netProtocol(req.Proto)
|
||||
if protoName != "" && protoName != "http" {
|
||||
n++
|
||||
}
|
||||
if protoVersion != "" {
|
||||
n++
|
||||
}
|
||||
|
||||
attrs := make([]attribute.KeyValue, 0, n)
|
||||
|
||||
attrs = append(attrs, c.method(req.Method))
|
||||
attrs = append(attrs, c.scheme(req.TLS != nil))
|
||||
attrs = append(attrs, c.flavor(req.Proto))
|
||||
attrs = append(attrs, c.NetConv.HostName(host))
|
||||
|
||||
if hostPort > 0 {
|
||||
@ -359,17 +398,24 @@ func (c *httpConv) ServerRequest(server string, req *http.Request) []attribute.K
|
||||
}
|
||||
|
||||
if useragent != "" {
|
||||
attrs = append(attrs, c.HTTPUserAgentKey.String(useragent))
|
||||
}
|
||||
|
||||
if hasUserID {
|
||||
attrs = append(attrs, c.EnduserIDKey.String(userID))
|
||||
attrs = append(attrs, c.UserAgentOriginalKey.String(useragent))
|
||||
}
|
||||
|
||||
if clientIP != "" {
|
||||
attrs = append(attrs, c.HTTPClientIPKey.String(clientIP))
|
||||
}
|
||||
|
||||
if target != "" {
|
||||
attrs = append(attrs, c.HTTPTargetKey.String(target))
|
||||
}
|
||||
|
||||
if protoName != "" && protoName != "http" {
|
||||
attrs = append(attrs, c.NetConv.NetProtocolName.String(protoName))
|
||||
}
|
||||
if protoVersion != "" {
|
||||
attrs = append(attrs, c.NetConv.NetProtocolVersion.String(protoVersion))
|
||||
}
|
||||
|
||||
return attrs
|
||||
}
|
||||
|
||||
@ -391,17 +437,21 @@ func (c *httpConv) ServerRequest(server string, req *http.Request) []attribute.K
|
||||
// The req Host will be used to determine the server instead.
|
||||
//
|
||||
// The following attributes are always returned: "http.method", "http.scheme",
|
||||
// "http.flavor", "net.host.name". The following attributes are
|
||||
// returned if they related values are defined in req: "net.host.port".
|
||||
// "net.host.name". The following attributes are returned if they related
|
||||
// values are defined in req: "net.host.port".
|
||||
func (c *httpConv) ServerRequestMetrics(server string, req *http.Request) []attribute.KeyValue {
|
||||
// TODO: This currently does not add the specification required
|
||||
// `http.target` attribute. It has too high of a cardinality to safely be
|
||||
// added. An alternate should be added, or this comment removed, when it is
|
||||
// addressed by the specification. If it is ultimately decided to continue
|
||||
// not including the attribute, the HTTPTargetKey field of the httpConv
|
||||
// should be removed as well.
|
||||
/* The following semantic conventions are returned if present:
|
||||
http.scheme string
|
||||
http.route string
|
||||
http.method string
|
||||
http.status_code int
|
||||
net.host.name string
|
||||
net.host.port int
|
||||
net.protocol.name string Note: not set if the value is "http".
|
||||
net.protocol.version string
|
||||
*/
|
||||
|
||||
n := 4 // Method, scheme, proto, and host name.
|
||||
n := 3 // Method, scheme, and host name.
|
||||
var host string
|
||||
var p int
|
||||
if server == "" {
|
||||
@ -417,16 +467,29 @@ func (c *httpConv) ServerRequestMetrics(server string, req *http.Request) []attr
|
||||
if hostPort > 0 {
|
||||
n++
|
||||
}
|
||||
protoName, protoVersion := netProtocol(req.Proto)
|
||||
if protoName != "" {
|
||||
n++
|
||||
}
|
||||
if protoVersion != "" {
|
||||
n++
|
||||
}
|
||||
|
||||
attrs := make([]attribute.KeyValue, 0, n)
|
||||
|
||||
attrs = append(attrs, c.methodMetric(req.Method))
|
||||
attrs = append(attrs, c.scheme(req.TLS != nil))
|
||||
attrs = append(attrs, c.flavor(req.Proto))
|
||||
attrs = append(attrs, c.NetConv.HostName(host))
|
||||
|
||||
if hostPort > 0 {
|
||||
attrs = append(attrs, c.NetConv.HostPort(hostPort))
|
||||
}
|
||||
if protoName != "" {
|
||||
attrs = append(attrs, c.NetConv.NetProtocolName.String(protoName))
|
||||
}
|
||||
if protoVersion != "" {
|
||||
attrs = append(attrs, c.NetConv.NetProtocolVersion.String(protoVersion))
|
||||
}
|
||||
|
||||
return attrs
|
||||
}
|
||||
@ -455,21 +518,6 @@ func (c *httpConv) scheme(https bool) attribute.KeyValue { // nolint:revive
|
||||
return c.HTTPSchemeHTTP
|
||||
}
|
||||
|
||||
func (c *httpConv) flavor(proto string) attribute.KeyValue {
|
||||
switch proto {
|
||||
case "HTTP/1.0":
|
||||
return c.HTTPFlavorKey.String("1.0")
|
||||
case "HTTP/1.1":
|
||||
return c.HTTPFlavorKey.String("1.1")
|
||||
case "HTTP/2":
|
||||
return c.HTTPFlavorKey.String("2.0")
|
||||
case "HTTP/3":
|
||||
return c.HTTPFlavorKey.String("3.0")
|
||||
default:
|
||||
return c.HTTPFlavorKey.String(proto)
|
||||
}
|
||||
}
|
||||
|
||||
func serverClientIP(xForwardedFor string) string {
|
||||
if idx := strings.Index(xForwardedFor, ","); idx >= 0 {
|
||||
xForwardedFor = xForwardedFor[:idx]
|
||||
@ -501,31 +549,6 @@ func firstHostPort(source ...string) (host string, port int) {
|
||||
return
|
||||
}
|
||||
|
||||
// RequestHeader returns the contents of h as OpenTelemetry attributes.
|
||||
func (c *httpConv) RequestHeader(h http.Header) []attribute.KeyValue {
|
||||
return c.header("http.request.header", h)
|
||||
}
|
||||
|
||||
// ResponseHeader returns the contents of h as OpenTelemetry attributes.
|
||||
func (c *httpConv) ResponseHeader(h http.Header) []attribute.KeyValue {
|
||||
return c.header("http.response.header", h)
|
||||
}
|
||||
|
||||
func (c *httpConv) header(prefix string, h http.Header) []attribute.KeyValue {
|
||||
key := func(k string) attribute.Key {
|
||||
k = strings.ToLower(k)
|
||||
k = strings.ReplaceAll(k, "-", "_")
|
||||
k = fmt.Sprintf("%s.%s", prefix, k)
|
||||
return attribute.Key(k)
|
||||
}
|
||||
|
||||
attrs := make([]attribute.KeyValue, 0, len(h))
|
||||
for k, v := range h {
|
||||
attrs = append(attrs, key(k).StringSlice(v))
|
||||
}
|
||||
return attrs
|
||||
}
|
||||
|
||||
// ClientStatus returns a span status code and message for an HTTP status code
|
||||
// value received by a client.
|
||||
func (c *httpConv) ClientStatus(code int) (codes.Code, string) {
|
||||
|
@ -2,17 +2,7 @@
|
||||
// source: internal/shared/semconvutil/netconv.go.tmpl
|
||||
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package semconvutil // import "go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/internal/semconvutil"
|
||||
|
||||
@ -22,7 +12,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.17.0"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
|
||||
)
|
||||
|
||||
// NetTransport returns a trace attribute describing the transport protocol of the
|
||||
@ -32,24 +22,6 @@ func NetTransport(network string) attribute.KeyValue {
|
||||
return nc.Transport(network)
|
||||
}
|
||||
|
||||
// NetClient returns trace attributes for a client network connection to address.
|
||||
// See net.Dial for information about acceptable address values, address should
|
||||
// be the same as the one used to create conn. If conn is nil, only network
|
||||
// peer attributes will be returned that describe address. Otherwise, the
|
||||
// socket level information about conn will also be included.
|
||||
func NetClient(address string, conn net.Conn) []attribute.KeyValue {
|
||||
return nc.Client(address, conn)
|
||||
}
|
||||
|
||||
// NetServer returns trace attributes for a network listener listening at address.
|
||||
// See net.Listen for information about acceptable address values, address
|
||||
// should be the same as the one used to create ln. If ln is nil, only network
|
||||
// host attributes will be returned that describe address. Otherwise, the
|
||||
// socket level information about ln will also be included.
|
||||
func NetServer(address string, ln net.Listener) []attribute.KeyValue {
|
||||
return nc.Server(address, ln)
|
||||
}
|
||||
|
||||
// netConv are the network semantic convention attributes defined for a version
|
||||
// of the OpenTelemetry specification.
|
||||
type netConv struct {
|
||||
@ -57,6 +29,8 @@ type netConv struct {
|
||||
NetHostPortKey attribute.Key
|
||||
NetPeerNameKey attribute.Key
|
||||
NetPeerPortKey attribute.Key
|
||||
NetProtocolName attribute.Key
|
||||
NetProtocolVersion attribute.Key
|
||||
NetSockFamilyKey attribute.Key
|
||||
NetSockPeerAddrKey attribute.Key
|
||||
NetSockPeerPortKey attribute.Key
|
||||
@ -73,6 +47,8 @@ var nc = &netConv{
|
||||
NetHostPortKey: semconv.NetHostPortKey,
|
||||
NetPeerNameKey: semconv.NetPeerNameKey,
|
||||
NetPeerPortKey: semconv.NetPeerPortKey,
|
||||
NetProtocolName: semconv.NetProtocolNameKey,
|
||||
NetProtocolVersion: semconv.NetProtocolVersionKey,
|
||||
NetSockFamilyKey: semconv.NetSockFamilyKey,
|
||||
NetSockPeerAddrKey: semconv.NetSockPeerAddrKey,
|
||||
NetSockPeerPortKey: semconv.NetSockPeerPortKey,
|
||||
@ -116,57 +92,11 @@ func (c *netConv) Host(address string) []attribute.KeyValue {
|
||||
attrs := make([]attribute.KeyValue, 0, n)
|
||||
attrs = append(attrs, c.HostName(h))
|
||||
if p > 0 {
|
||||
attrs = append(attrs, c.HostPort(int(p)))
|
||||
attrs = append(attrs, c.HostPort(p))
|
||||
}
|
||||
return attrs
|
||||
}
|
||||
|
||||
// Server returns attributes for a network listener listening at address. See
|
||||
// net.Listen for information about acceptable address values, address should
|
||||
// be the same as the one used to create ln. If ln is nil, only network host
|
||||
// attributes will be returned that describe address. Otherwise, the socket
|
||||
// level information about ln will also be included.
|
||||
func (c *netConv) Server(address string, ln net.Listener) []attribute.KeyValue {
|
||||
if ln == nil {
|
||||
return c.Host(address)
|
||||
}
|
||||
|
||||
lAddr := ln.Addr()
|
||||
if lAddr == nil {
|
||||
return c.Host(address)
|
||||
}
|
||||
|
||||
hostName, hostPort := splitHostPort(address)
|
||||
sockHostAddr, sockHostPort := splitHostPort(lAddr.String())
|
||||
network := lAddr.Network()
|
||||
sockFamily := family(network, sockHostAddr)
|
||||
|
||||
n := nonZeroStr(hostName, network, sockHostAddr, sockFamily)
|
||||
n += positiveInt(hostPort, sockHostPort)
|
||||
attr := make([]attribute.KeyValue, 0, n)
|
||||
if hostName != "" {
|
||||
attr = append(attr, c.HostName(hostName))
|
||||
if hostPort > 0 {
|
||||
// Only if net.host.name is set should net.host.port be.
|
||||
attr = append(attr, c.HostPort(hostPort))
|
||||
}
|
||||
}
|
||||
if network != "" {
|
||||
attr = append(attr, c.Transport(network))
|
||||
}
|
||||
if sockFamily != "" {
|
||||
attr = append(attr, c.NetSockFamilyKey.String(sockFamily))
|
||||
}
|
||||
if sockHostAddr != "" {
|
||||
attr = append(attr, c.NetSockHostAddrKey.String(sockHostAddr))
|
||||
if sockHostPort > 0 {
|
||||
// Only if net.sock.host.addr is set should net.sock.host.port be.
|
||||
attr = append(attr, c.NetSockHostPortKey.Int(sockHostPort))
|
||||
}
|
||||
}
|
||||
return attr
|
||||
}
|
||||
|
||||
func (c *netConv) HostName(name string) attribute.KeyValue {
|
||||
return c.NetHostNameKey.String(name)
|
||||
}
|
||||
@ -175,85 +105,6 @@ func (c *netConv) HostPort(port int) attribute.KeyValue {
|
||||
return c.NetHostPortKey.Int(port)
|
||||
}
|
||||
|
||||
// Client returns attributes for a client network connection to address. See
|
||||
// net.Dial for information about acceptable address values, address should be
|
||||
// the same as the one used to create conn. If conn is nil, only network peer
|
||||
// attributes will be returned that describe address. Otherwise, the socket
|
||||
// level information about conn will also be included.
|
||||
func (c *netConv) Client(address string, conn net.Conn) []attribute.KeyValue {
|
||||
if conn == nil {
|
||||
return c.Peer(address)
|
||||
}
|
||||
|
||||
lAddr, rAddr := conn.LocalAddr(), conn.RemoteAddr()
|
||||
|
||||
var network string
|
||||
switch {
|
||||
case lAddr != nil:
|
||||
network = lAddr.Network()
|
||||
case rAddr != nil:
|
||||
network = rAddr.Network()
|
||||
default:
|
||||
return c.Peer(address)
|
||||
}
|
||||
|
||||
peerName, peerPort := splitHostPort(address)
|
||||
var (
|
||||
sockFamily string
|
||||
sockPeerAddr string
|
||||
sockPeerPort int
|
||||
sockHostAddr string
|
||||
sockHostPort int
|
||||
)
|
||||
|
||||
if lAddr != nil {
|
||||
sockHostAddr, sockHostPort = splitHostPort(lAddr.String())
|
||||
}
|
||||
|
||||
if rAddr != nil {
|
||||
sockPeerAddr, sockPeerPort = splitHostPort(rAddr.String())
|
||||
}
|
||||
|
||||
switch {
|
||||
case sockHostAddr != "":
|
||||
sockFamily = family(network, sockHostAddr)
|
||||
case sockPeerAddr != "":
|
||||
sockFamily = family(network, sockPeerAddr)
|
||||
}
|
||||
|
||||
n := nonZeroStr(peerName, network, sockPeerAddr, sockHostAddr, sockFamily)
|
||||
n += positiveInt(peerPort, sockPeerPort, sockHostPort)
|
||||
attr := make([]attribute.KeyValue, 0, n)
|
||||
if peerName != "" {
|
||||
attr = append(attr, c.PeerName(peerName))
|
||||
if peerPort > 0 {
|
||||
// Only if net.peer.name is set should net.peer.port be.
|
||||
attr = append(attr, c.PeerPort(peerPort))
|
||||
}
|
||||
}
|
||||
if network != "" {
|
||||
attr = append(attr, c.Transport(network))
|
||||
}
|
||||
if sockFamily != "" {
|
||||
attr = append(attr, c.NetSockFamilyKey.String(sockFamily))
|
||||
}
|
||||
if sockPeerAddr != "" {
|
||||
attr = append(attr, c.NetSockPeerAddrKey.String(sockPeerAddr))
|
||||
if sockPeerPort > 0 {
|
||||
// Only if net.sock.peer.addr is set should net.sock.peer.port be.
|
||||
attr = append(attr, c.NetSockPeerPortKey.Int(sockPeerPort))
|
||||
}
|
||||
}
|
||||
if sockHostAddr != "" {
|
||||
attr = append(attr, c.NetSockHostAddrKey.String(sockHostAddr))
|
||||
if sockHostPort > 0 {
|
||||
// Only if net.sock.host.addr is set should net.sock.host.port be.
|
||||
attr = append(attr, c.NetSockHostPortKey.Int(sockHostPort))
|
||||
}
|
||||
}
|
||||
return attr
|
||||
}
|
||||
|
||||
func family(network, address string) string {
|
||||
switch network {
|
||||
case "unix", "unixgram", "unixpacket":
|
||||
@ -269,26 +120,6 @@ func family(network, address string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func nonZeroStr(strs ...string) int {
|
||||
var n int
|
||||
for _, str := range strs {
|
||||
if str != "" {
|
||||
n++
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func positiveInt(ints ...int) int {
|
||||
var n int
|
||||
for _, i := range ints {
|
||||
if i > 0 {
|
||||
n++
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Peer returns attributes for a network peer address.
|
||||
func (c *netConv) Peer(address string) []attribute.KeyValue {
|
||||
h, p := splitHostPort(address)
|
||||
@ -307,7 +138,7 @@ func (c *netConv) Peer(address string) []attribute.KeyValue {
|
||||
attrs := make([]attribute.KeyValue, 0, n)
|
||||
attrs = append(attrs, c.PeerName(h))
|
||||
if p > 0 {
|
||||
attrs = append(attrs, c.PeerPort(int(p)))
|
||||
attrs = append(attrs, c.PeerPort(p))
|
||||
}
|
||||
return attrs
|
||||
}
|
||||
@ -364,5 +195,11 @@ func splitHostPort(hostport string) (host string, port int) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return host, int(p)
|
||||
return host, int(p) // nolint: gosec // Bitsize checked to be 16 above.
|
||||
}
|
||||
|
||||
func netProtocol(proto string) (name string, version string) {
|
||||
name, version, _ = strings.Cut(proto, "/")
|
||||
name = strings.ToLower(name)
|
||||
return name, version
|
||||
}
|
||||
|
@ -1,22 +1,11 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otelhttptrace // import "go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace"
|
||||
|
||||
// Version is the current release version of the httptrace instrumentation.
|
||||
func Version() string {
|
||||
return "0.46.1"
|
||||
return "0.56.0"
|
||||
// This string is updated by the pre_release.sh script during release
|
||||
}
|
||||
|
||||
|
14
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/common.go
generated
vendored
14
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/common.go
generated
vendored
@ -18,20 +18,6 @@ const (
|
||||
WriteErrorKey = attribute.Key("http.write_error") // if an error occurred while writing a reply, the string of the error (io.EOF is not recorded)
|
||||
)
|
||||
|
||||
// Server HTTP metrics.
|
||||
const (
|
||||
serverRequestSize = "http.server.request.size" // Incoming request bytes total
|
||||
serverResponseSize = "http.server.response.size" // Incoming response bytes total
|
||||
serverDuration = "http.server.duration" // Incoming end to end duration, milliseconds
|
||||
)
|
||||
|
||||
// Client HTTP metrics.
|
||||
const (
|
||||
clientRequestSize = "http.client.request.size" // Outgoing request bytes total
|
||||
clientResponseSize = "http.client.response.size" // Outgoing response bytes total
|
||||
clientDuration = "http.client.duration" // Outgoing end to end duration, milliseconds
|
||||
)
|
||||
|
||||
// Filter is a predicate used to determine whether a given http.request should
|
||||
// be traced. A Filter must return true if the request should be traced.
|
||||
type Filter func(*http.Request) bool
|
||||
|
15
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/config.go
generated
vendored
15
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/config.go
generated
vendored
@ -8,6 +8,8 @@ import (
|
||||
"net/http"
|
||||
"net/http/httptrace"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
@ -33,8 +35,9 @@ type config struct {
|
||||
SpanNameFormatter func(string, *http.Request) string
|
||||
ClientTrace func(context.Context) *httptrace.ClientTrace
|
||||
|
||||
TracerProvider trace.TracerProvider
|
||||
MeterProvider metric.MeterProvider
|
||||
TracerProvider trace.TracerProvider
|
||||
MeterProvider metric.MeterProvider
|
||||
MetricAttributesFn func(*http.Request) []attribute.KeyValue
|
||||
}
|
||||
|
||||
// Option interface used for setting optional config properties.
|
||||
@ -194,3 +197,11 @@ func WithServerName(server string) Option {
|
||||
c.ServerName = server
|
||||
})
|
||||
}
|
||||
|
||||
// WithMetricAttributesFn returns an Option to set a function that maps an HTTP request to a slice of attribute.KeyValue.
|
||||
// These attributes will be included in metrics for every request.
|
||||
func WithMetricAttributesFn(metricAttributesFn func(r *http.Request) []attribute.KeyValue) Option {
|
||||
return optionFunc(func(c *config) {
|
||||
c.MetricAttributesFn = metricAttributesFn
|
||||
})
|
||||
}
|
||||
|
103
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/handler.go
generated
vendored
103
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/handler.go
generated
vendored
@ -9,11 +9,9 @@ import (
|
||||
|
||||
"github.com/felixge/httpsnoop"
|
||||
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/request"
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv"
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
@ -24,7 +22,6 @@ type middleware struct {
|
||||
server string
|
||||
|
||||
tracer trace.Tracer
|
||||
meter metric.Meter
|
||||
propagators propagation.TextMapPropagator
|
||||
spanStartOptions []trace.SpanStartOption
|
||||
readEvent bool
|
||||
@ -34,10 +31,7 @@ type middleware struct {
|
||||
publicEndpoint bool
|
||||
publicEndpointFn func(*http.Request) bool
|
||||
|
||||
traceSemconv semconv.HTTPServer
|
||||
requestBytesCounter metric.Int64Counter
|
||||
responseBytesCounter metric.Int64Counter
|
||||
serverLatencyMeasure metric.Float64Histogram
|
||||
semconv semconv.HTTPServer
|
||||
}
|
||||
|
||||
func defaultHandlerFormatter(operation string, _ *http.Request) string {
|
||||
@ -56,8 +50,6 @@ func NewHandler(handler http.Handler, operation string, opts ...Option) http.Han
|
||||
func NewMiddleware(operation string, opts ...Option) func(http.Handler) http.Handler {
|
||||
h := middleware{
|
||||
operation: operation,
|
||||
|
||||
traceSemconv: semconv.NewHTTPServer(),
|
||||
}
|
||||
|
||||
defaultOpts := []Option{
|
||||
@ -67,7 +59,6 @@ func NewMiddleware(operation string, opts ...Option) func(http.Handler) http.Han
|
||||
|
||||
c := newConfig(append(defaultOpts, opts...)...)
|
||||
h.configure(c)
|
||||
h.createMeasures()
|
||||
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
@ -78,7 +69,6 @@ func NewMiddleware(operation string, opts ...Option) func(http.Handler) http.Han
|
||||
|
||||
func (h *middleware) configure(c *config) {
|
||||
h.tracer = c.Tracer
|
||||
h.meter = c.Meter
|
||||
h.propagators = c.Propagators
|
||||
h.spanStartOptions = c.SpanStartOptions
|
||||
h.readEvent = c.ReadEvent
|
||||
@ -88,36 +78,7 @@ func (h *middleware) configure(c *config) {
|
||||
h.publicEndpoint = c.PublicEndpoint
|
||||
h.publicEndpointFn = c.PublicEndpointFn
|
||||
h.server = c.ServerName
|
||||
}
|
||||
|
||||
func handleErr(err error) {
|
||||
if err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *middleware) createMeasures() {
|
||||
var err error
|
||||
h.requestBytesCounter, err = h.meter.Int64Counter(
|
||||
serverRequestSize,
|
||||
metric.WithUnit("By"),
|
||||
metric.WithDescription("Measures the size of HTTP request messages."),
|
||||
)
|
||||
handleErr(err)
|
||||
|
||||
h.responseBytesCounter, err = h.meter.Int64Counter(
|
||||
serverResponseSize,
|
||||
metric.WithUnit("By"),
|
||||
metric.WithDescription("Measures the size of HTTP response messages."),
|
||||
)
|
||||
handleErr(err)
|
||||
|
||||
h.serverLatencyMeasure, err = h.meter.Float64Histogram(
|
||||
serverDuration,
|
||||
metric.WithUnit("ms"),
|
||||
metric.WithDescription("Measures the duration of inbound HTTP requests."),
|
||||
)
|
||||
handleErr(err)
|
||||
h.semconv = semconv.NewHTTPServer(c.Meter)
|
||||
}
|
||||
|
||||
// serveHTTP sets up tracing and calls the given next http.Handler with the span
|
||||
@ -134,7 +95,7 @@ func (h *middleware) serveHTTP(w http.ResponseWriter, r *http.Request, next http
|
||||
|
||||
ctx := h.propagators.Extract(r.Context(), propagation.HeaderCarrier(r.Header))
|
||||
opts := []trace.SpanStartOption{
|
||||
trace.WithAttributes(h.traceSemconv.RequestTraceAttrs(h.server, r)...),
|
||||
trace.WithAttributes(h.semconv.RequestTraceAttrs(h.server, r)...),
|
||||
}
|
||||
|
||||
opts = append(opts, h.spanStartOptions...)
|
||||
@ -166,14 +127,12 @@ func (h *middleware) serveHTTP(w http.ResponseWriter, r *http.Request, next http
|
||||
}
|
||||
}
|
||||
|
||||
var bw bodyWrapper
|
||||
// if request body is nil or NoBody, we don't want to mutate the body as it
|
||||
// will affect the identity of it in an unforeseeable way because we assert
|
||||
// ReadCloser fulfills a certain interface and it is indeed nil or NoBody.
|
||||
bw := request.NewBodyWrapper(r.Body, readRecordFunc)
|
||||
if r.Body != nil && r.Body != http.NoBody {
|
||||
bw.ReadCloser = r.Body
|
||||
bw.record = readRecordFunc
|
||||
r.Body = &bw
|
||||
r.Body = bw
|
||||
}
|
||||
|
||||
writeRecordFunc := func(int64) {}
|
||||
@ -183,13 +142,7 @@ func (h *middleware) serveHTTP(w http.ResponseWriter, r *http.Request, next http
|
||||
}
|
||||
}
|
||||
|
||||
rww := &respWriterWrapper{
|
||||
ResponseWriter: w,
|
||||
record: writeRecordFunc,
|
||||
ctx: ctx,
|
||||
props: h.propagators,
|
||||
statusCode: http.StatusOK, // default status code in case the Handler doesn't write anything
|
||||
}
|
||||
rww := request.NewRespWriterWrapper(w, writeRecordFunc)
|
||||
|
||||
// Wrap w to use our ResponseWriter methods while also exposing
|
||||
// other interfaces that w may implement (http.CloseNotifier,
|
||||
@ -217,35 +170,39 @@ func (h *middleware) serveHTTP(w http.ResponseWriter, r *http.Request, next http
|
||||
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
|
||||
span.SetStatus(semconv.ServerStatus(rww.statusCode))
|
||||
span.SetAttributes(h.traceSemconv.ResponseTraceAttrs(semconv.ResponseTelemetry{
|
||||
StatusCode: rww.statusCode,
|
||||
ReadBytes: bw.read.Load(),
|
||||
ReadError: bw.err,
|
||||
WriteBytes: rww.written,
|
||||
WriteError: rww.err,
|
||||
statusCode := rww.StatusCode()
|
||||
bytesWritten := rww.BytesWritten()
|
||||
span.SetStatus(h.semconv.Status(statusCode))
|
||||
span.SetAttributes(h.semconv.ResponseTraceAttrs(semconv.ResponseTelemetry{
|
||||
StatusCode: statusCode,
|
||||
ReadBytes: bw.BytesRead(),
|
||||
ReadError: bw.Error(),
|
||||
WriteBytes: bytesWritten,
|
||||
WriteError: rww.Error(),
|
||||
})...)
|
||||
|
||||
// Add metrics
|
||||
attributes := append(labeler.Get(), semconvutil.HTTPServerRequestMetrics(h.server, r)...)
|
||||
if rww.statusCode > 0 {
|
||||
attributes = append(attributes, semconv.HTTPStatusCode(rww.statusCode))
|
||||
}
|
||||
o := metric.WithAttributeSet(attribute.NewSet(attributes...))
|
||||
addOpts := []metric.AddOption{o} // Allocate vararg slice once.
|
||||
h.requestBytesCounter.Add(ctx, bw.read.Load(), addOpts...)
|
||||
h.responseBytesCounter.Add(ctx, rww.written, addOpts...)
|
||||
|
||||
// Use floating point division here for higher precision (instead of Millisecond method).
|
||||
elapsedTime := float64(time.Since(requestStartTime)) / float64(time.Millisecond)
|
||||
|
||||
h.serverLatencyMeasure.Record(ctx, elapsedTime, o)
|
||||
h.semconv.RecordMetrics(ctx, semconv.ServerMetricData{
|
||||
ServerName: h.server,
|
||||
ResponseSize: bytesWritten,
|
||||
MetricAttributes: semconv.MetricAttributes{
|
||||
Req: r,
|
||||
StatusCode: statusCode,
|
||||
AdditionalAttributes: labeler.Get(),
|
||||
},
|
||||
MetricData: semconv.MetricData{
|
||||
RequestSize: bw.BytesRead(),
|
||||
ElapsedTime: elapsedTime,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// WithRouteTag annotates spans and metrics with the provided route name
|
||||
// with HTTP route attribute.
|
||||
func WithRouteTag(route string, h http.Handler) http.Handler {
|
||||
attr := semconv.NewHTTPServer().Route(route)
|
||||
attr := semconv.NewHTTPServer(nil).Route(route)
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
span := trace.SpanFromContext(r.Context())
|
||||
span.SetAttributes(attr)
|
||||
|
75
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/request/body_wrapper.go
generated
vendored
Normal file
75
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/request/body_wrapper.go
generated
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package request // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/request"
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var _ io.ReadCloser = &BodyWrapper{}
|
||||
|
||||
// BodyWrapper wraps a http.Request.Body (an io.ReadCloser) to track the number
|
||||
// of bytes read and the last error.
|
||||
type BodyWrapper struct {
|
||||
io.ReadCloser
|
||||
OnRead func(n int64) // must not be nil
|
||||
|
||||
mu sync.Mutex
|
||||
read int64
|
||||
err error
|
||||
}
|
||||
|
||||
// NewBodyWrapper creates a new BodyWrapper.
|
||||
//
|
||||
// The onRead attribute is a callback that will be called every time the data
|
||||
// is read, with the number of bytes being read.
|
||||
func NewBodyWrapper(body io.ReadCloser, onRead func(int64)) *BodyWrapper {
|
||||
return &BodyWrapper{
|
||||
ReadCloser: body,
|
||||
OnRead: onRead,
|
||||
}
|
||||
}
|
||||
|
||||
// Read reads the data from the io.ReadCloser, and stores the number of bytes
|
||||
// read and the error.
|
||||
func (w *BodyWrapper) Read(b []byte) (int, error) {
|
||||
n, err := w.ReadCloser.Read(b)
|
||||
n1 := int64(n)
|
||||
|
||||
w.updateReadData(n1, err)
|
||||
w.OnRead(n1)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (w *BodyWrapper) updateReadData(n int64, err error) {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
w.read += n
|
||||
if err != nil {
|
||||
w.err = err
|
||||
}
|
||||
}
|
||||
|
||||
// Closes closes the io.ReadCloser.
|
||||
func (w *BodyWrapper) Close() error {
|
||||
return w.ReadCloser.Close()
|
||||
}
|
||||
|
||||
// BytesRead returns the number of bytes read up to this point.
|
||||
func (w *BodyWrapper) BytesRead() int64 {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
return w.read
|
||||
}
|
||||
|
||||
// Error returns the last error.
|
||||
func (w *BodyWrapper) Error() error {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
return w.err
|
||||
}
|
119
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/request/resp_writer_wrapper.go
generated
vendored
Normal file
119
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/request/resp_writer_wrapper.go
generated
vendored
Normal file
@ -0,0 +1,119 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package request // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/request"
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var _ http.ResponseWriter = &RespWriterWrapper{}
|
||||
|
||||
// RespWriterWrapper wraps a http.ResponseWriter in order to track the number of
|
||||
// bytes written, the last error, and to catch the first written statusCode.
|
||||
// TODO: The wrapped http.ResponseWriter doesn't implement any of the optional
|
||||
// types (http.Hijacker, http.Pusher, http.CloseNotifier, etc)
|
||||
// that may be useful when using it in real life situations.
|
||||
type RespWriterWrapper struct {
|
||||
http.ResponseWriter
|
||||
OnWrite func(n int64) // must not be nil
|
||||
|
||||
mu sync.RWMutex
|
||||
written int64
|
||||
statusCode int
|
||||
err error
|
||||
wroteHeader bool
|
||||
}
|
||||
|
||||
// NewRespWriterWrapper creates a new RespWriterWrapper.
|
||||
//
|
||||
// The onWrite attribute is a callback that will be called every time the data
|
||||
// is written, with the number of bytes that were written.
|
||||
func NewRespWriterWrapper(w http.ResponseWriter, onWrite func(int64)) *RespWriterWrapper {
|
||||
return &RespWriterWrapper{
|
||||
ResponseWriter: w,
|
||||
OnWrite: onWrite,
|
||||
statusCode: http.StatusOK, // default status code in case the Handler doesn't write anything
|
||||
}
|
||||
}
|
||||
|
||||
// Write writes the bytes array into the [ResponseWriter], and tracks the
|
||||
// number of bytes written and last error.
|
||||
func (w *RespWriterWrapper) Write(p []byte) (int, error) {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
if !w.wroteHeader {
|
||||
w.writeHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
n, err := w.ResponseWriter.Write(p)
|
||||
n1 := int64(n)
|
||||
w.OnWrite(n1)
|
||||
w.written += n1
|
||||
w.err = err
|
||||
return n, err
|
||||
}
|
||||
|
||||
// WriteHeader persists initial statusCode for span attribution.
|
||||
// All calls to WriteHeader will be propagated to the underlying ResponseWriter
|
||||
// and will persist the statusCode from the first call.
|
||||
// Blocking consecutive calls to WriteHeader alters expected behavior and will
|
||||
// remove warning logs from net/http where developers will notice incorrect handler implementations.
|
||||
func (w *RespWriterWrapper) WriteHeader(statusCode int) {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
w.writeHeader(statusCode)
|
||||
}
|
||||
|
||||
// writeHeader persists the status code for span attribution, and propagates
|
||||
// the call to the underlying ResponseWriter.
|
||||
// It does not acquire a lock, and therefore assumes that is being handled by a
|
||||
// parent method.
|
||||
func (w *RespWriterWrapper) writeHeader(statusCode int) {
|
||||
if !w.wroteHeader {
|
||||
w.wroteHeader = true
|
||||
w.statusCode = statusCode
|
||||
}
|
||||
w.ResponseWriter.WriteHeader(statusCode)
|
||||
}
|
||||
|
||||
// Flush implements [http.Flusher].
|
||||
func (w *RespWriterWrapper) Flush() {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
|
||||
if !w.wroteHeader {
|
||||
w.writeHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
if f, ok := w.ResponseWriter.(http.Flusher); ok {
|
||||
f.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
// BytesWritten returns the number of bytes written.
|
||||
func (w *RespWriterWrapper) BytesWritten() int64 {
|
||||
w.mu.RLock()
|
||||
defer w.mu.RUnlock()
|
||||
|
||||
return w.written
|
||||
}
|
||||
|
||||
// BytesWritten returns the HTTP status code that was sent.
|
||||
func (w *RespWriterWrapper) StatusCode() int {
|
||||
w.mu.RLock()
|
||||
defer w.mu.RUnlock()
|
||||
|
||||
return w.statusCode
|
||||
}
|
||||
|
||||
// Error returns the last error.
|
||||
func (w *RespWriterWrapper) Error() error {
|
||||
w.mu.RLock()
|
||||
defer w.mu.RUnlock()
|
||||
|
||||
return w.err
|
||||
}
|
159
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/env.go
generated
vendored
159
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv/env.go
generated
vendored
@ -4,6 +4,7 @@
|
||||
package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
@ -11,6 +12,7 @@ import (
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
)
|
||||
|
||||
type ResponseTelemetry struct {
|
||||
@ -23,6 +25,11 @@ type ResponseTelemetry struct {
|
||||
|
||||
type HTTPServer struct {
|
||||
duplicate bool
|
||||
|
||||
// Old metrics
|
||||
requestBytesCounter metric.Int64Counter
|
||||
responseBytesCounter metric.Int64Counter
|
||||
serverLatencyMeasure metric.Float64Histogram
|
||||
}
|
||||
|
||||
// RequestTraceAttrs returns trace attributes for an HTTP request received by a
|
||||
@ -63,15 +70,10 @@ func (s HTTPServer) Route(route string) attribute.KeyValue {
|
||||
return oldHTTPServer{}.Route(route)
|
||||
}
|
||||
|
||||
func NewHTTPServer() HTTPServer {
|
||||
env := strings.ToLower(os.Getenv("OTEL_HTTP_CLIENT_COMPATIBILITY_MODE"))
|
||||
return HTTPServer{duplicate: env == "http/dup"}
|
||||
}
|
||||
|
||||
// ServerStatus returns a span status code and message for an HTTP status code
|
||||
// Status 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) {
|
||||
func (s HTTPServer) Status(code int) (codes.Code, string) {
|
||||
if code < 100 || code >= 600 {
|
||||
return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code)
|
||||
}
|
||||
@ -80,3 +82,146 @@ func ServerStatus(code int) (codes.Code, string) {
|
||||
}
|
||||
return codes.Unset, ""
|
||||
}
|
||||
|
||||
type ServerMetricData struct {
|
||||
ServerName string
|
||||
ResponseSize int64
|
||||
|
||||
MetricData
|
||||
MetricAttributes
|
||||
}
|
||||
|
||||
type MetricAttributes struct {
|
||||
Req *http.Request
|
||||
StatusCode int
|
||||
AdditionalAttributes []attribute.KeyValue
|
||||
}
|
||||
|
||||
type MetricData struct {
|
||||
RequestSize int64
|
||||
ElapsedTime float64
|
||||
}
|
||||
|
||||
func (s HTTPServer) RecordMetrics(ctx context.Context, md ServerMetricData) {
|
||||
if s.requestBytesCounter == nil || s.responseBytesCounter == nil || s.serverLatencyMeasure == nil {
|
||||
// This will happen if an HTTPServer{} is used insted of NewHTTPServer.
|
||||
return
|
||||
}
|
||||
|
||||
attributes := oldHTTPServer{}.MetricAttributes(md.ServerName, md.Req, md.StatusCode, md.AdditionalAttributes)
|
||||
o := metric.WithAttributeSet(attribute.NewSet(attributes...))
|
||||
addOpts := []metric.AddOption{o}
|
||||
s.requestBytesCounter.Add(ctx, md.RequestSize, addOpts...)
|
||||
s.responseBytesCounter.Add(ctx, md.ResponseSize, addOpts...)
|
||||
s.serverLatencyMeasure.Record(ctx, md.ElapsedTime, o)
|
||||
|
||||
// TODO: Duplicate Metrics
|
||||
}
|
||||
|
||||
func NewHTTPServer(meter metric.Meter) HTTPServer {
|
||||
env := strings.ToLower(os.Getenv("OTEL_SEMCONV_STABILITY_OPT_IN"))
|
||||
duplicate := env == "http/dup"
|
||||
server := HTTPServer{
|
||||
duplicate: duplicate,
|
||||
}
|
||||
server.requestBytesCounter, server.responseBytesCounter, server.serverLatencyMeasure = oldHTTPServer{}.createMeasures(meter)
|
||||
return server
|
||||
}
|
||||
|
||||
type HTTPClient struct {
|
||||
duplicate bool
|
||||
|
||||
// old metrics
|
||||
requestBytesCounter metric.Int64Counter
|
||||
responseBytesCounter metric.Int64Counter
|
||||
latencyMeasure metric.Float64Histogram
|
||||
}
|
||||
|
||||
func NewHTTPClient(meter metric.Meter) HTTPClient {
|
||||
env := strings.ToLower(os.Getenv("OTEL_SEMCONV_STABILITY_OPT_IN"))
|
||||
client := HTTPClient{
|
||||
duplicate: env == "http/dup",
|
||||
}
|
||||
client.requestBytesCounter, client.responseBytesCounter, client.latencyMeasure = oldHTTPClient{}.createMeasures(meter)
|
||||
return client
|
||||
}
|
||||
|
||||
// RequestTraceAttrs returns attributes for an HTTP request made by a client.
|
||||
func (c HTTPClient) RequestTraceAttrs(req *http.Request) []attribute.KeyValue {
|
||||
if c.duplicate {
|
||||
return append(oldHTTPClient{}.RequestTraceAttrs(req), newHTTPClient{}.RequestTraceAttrs(req)...)
|
||||
}
|
||||
return oldHTTPClient{}.RequestTraceAttrs(req)
|
||||
}
|
||||
|
||||
// ResponseTraceAttrs returns metric attributes for an HTTP request made by a client.
|
||||
func (c HTTPClient) ResponseTraceAttrs(resp *http.Response) []attribute.KeyValue {
|
||||
if c.duplicate {
|
||||
return append(oldHTTPClient{}.ResponseTraceAttrs(resp), newHTTPClient{}.ResponseTraceAttrs(resp)...)
|
||||
}
|
||||
|
||||
return oldHTTPClient{}.ResponseTraceAttrs(resp)
|
||||
}
|
||||
|
||||
func (c HTTPClient) Status(code int) (codes.Code, string) {
|
||||
if code < 100 || code >= 600 {
|
||||
return codes.Error, fmt.Sprintf("Invalid HTTP status code %d", code)
|
||||
}
|
||||
if code >= 400 {
|
||||
return codes.Error, ""
|
||||
}
|
||||
return codes.Unset, ""
|
||||
}
|
||||
|
||||
func (c HTTPClient) ErrorType(err error) attribute.KeyValue {
|
||||
if c.duplicate {
|
||||
return newHTTPClient{}.ErrorType(err)
|
||||
}
|
||||
|
||||
return attribute.KeyValue{}
|
||||
}
|
||||
|
||||
type MetricOpts struct {
|
||||
measurement metric.MeasurementOption
|
||||
addOptions metric.AddOption
|
||||
}
|
||||
|
||||
func (o MetricOpts) MeasurementOption() metric.MeasurementOption {
|
||||
return o.measurement
|
||||
}
|
||||
|
||||
func (o MetricOpts) AddOptions() metric.AddOption {
|
||||
return o.addOptions
|
||||
}
|
||||
|
||||
func (c HTTPClient) MetricOptions(ma MetricAttributes) MetricOpts {
|
||||
attributes := oldHTTPClient{}.MetricAttributes(ma.Req, ma.StatusCode, ma.AdditionalAttributes)
|
||||
// TODO: Duplicate Metrics
|
||||
set := metric.WithAttributeSet(attribute.NewSet(attributes...))
|
||||
return MetricOpts{
|
||||
measurement: set,
|
||||
addOptions: set,
|
||||
}
|
||||
}
|
||||
|
||||
func (s HTTPClient) RecordMetrics(ctx context.Context, md MetricData, opts MetricOpts) {
|
||||
if s.requestBytesCounter == nil || s.latencyMeasure == nil {
|
||||
// This will happen if an HTTPClient{} is used insted of NewHTTPClient().
|
||||
return
|
||||
}
|
||||
|
||||
s.requestBytesCounter.Add(ctx, md.RequestSize, opts.AddOptions())
|
||||
s.latencyMeasure.Record(ctx, md.ElapsedTime, opts.MeasurementOption())
|
||||
|
||||
// TODO: Duplicate Metrics
|
||||
}
|
||||
|
||||
func (s HTTPClient) RecordResponseSize(ctx context.Context, responseData int64, opts metric.AddOption) {
|
||||
if s.responseBytesCounter == nil {
|
||||
// This will happen if an HTTPClient{} is used insted of NewHTTPClient().
|
||||
return
|
||||
}
|
||||
|
||||
s.responseBytesCounter.Add(ctx, responseData, opts)
|
||||
// TODO: Duplicate Metrics
|
||||
}
|
||||
|
@ -4,11 +4,14 @@
|
||||
package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
semconvNew "go.opentelemetry.io/otel/semconv/v1.24.0"
|
||||
semconvNew "go.opentelemetry.io/otel/semconv/v1.26.0"
|
||||
)
|
||||
|
||||
type newHTTPServer struct{}
|
||||
@ -195,3 +198,151 @@ func (n newHTTPServer) ResponseTraceAttrs(resp ResponseTelemetry) []attribute.Ke
|
||||
func (n newHTTPServer) Route(route string) attribute.KeyValue {
|
||||
return semconvNew.HTTPRoute(route)
|
||||
}
|
||||
|
||||
type newHTTPClient struct{}
|
||||
|
||||
// RequestTraceAttrs returns trace attributes for an HTTP request made by a client.
|
||||
func (n newHTTPClient) RequestTraceAttrs(req *http.Request) []attribute.KeyValue {
|
||||
/*
|
||||
below attributes are returned:
|
||||
- http.request.method
|
||||
- http.request.method.original
|
||||
- url.full
|
||||
- server.address
|
||||
- server.port
|
||||
- network.protocol.name
|
||||
- network.protocol.version
|
||||
*/
|
||||
numOfAttributes := 3 // URL, server address, proto, and method.
|
||||
|
||||
var urlHost string
|
||||
if req.URL != nil {
|
||||
urlHost = req.URL.Host
|
||||
}
|
||||
var requestHost string
|
||||
var requestPort int
|
||||
for _, hostport := range []string{urlHost, req.Header.Get("Host")} {
|
||||
requestHost, requestPort = splitHostPort(hostport)
|
||||
if requestHost != "" || requestPort > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
eligiblePort := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", requestPort)
|
||||
if eligiblePort > 0 {
|
||||
numOfAttributes++
|
||||
}
|
||||
useragent := req.UserAgent()
|
||||
if useragent != "" {
|
||||
numOfAttributes++
|
||||
}
|
||||
|
||||
protoName, protoVersion := netProtocol(req.Proto)
|
||||
if protoName != "" && protoName != "http" {
|
||||
numOfAttributes++
|
||||
}
|
||||
if protoVersion != "" {
|
||||
numOfAttributes++
|
||||
}
|
||||
|
||||
method, originalMethod := n.method(req.Method)
|
||||
if originalMethod != (attribute.KeyValue{}) {
|
||||
numOfAttributes++
|
||||
}
|
||||
|
||||
attrs := make([]attribute.KeyValue, 0, numOfAttributes)
|
||||
|
||||
attrs = append(attrs, method)
|
||||
if originalMethod != (attribute.KeyValue{}) {
|
||||
attrs = append(attrs, originalMethod)
|
||||
}
|
||||
|
||||
var u string
|
||||
if req.URL != nil {
|
||||
// Remove any username/password info that may be in the URL.
|
||||
userinfo := req.URL.User
|
||||
req.URL.User = nil
|
||||
u = req.URL.String()
|
||||
// Restore any username/password info that was removed.
|
||||
req.URL.User = userinfo
|
||||
}
|
||||
attrs = append(attrs, semconvNew.URLFull(u))
|
||||
|
||||
attrs = append(attrs, semconvNew.ServerAddress(requestHost))
|
||||
if eligiblePort > 0 {
|
||||
attrs = append(attrs, semconvNew.ServerPort(eligiblePort))
|
||||
}
|
||||
|
||||
if protoName != "" && protoName != "http" {
|
||||
attrs = append(attrs, semconvNew.NetworkProtocolName(protoName))
|
||||
}
|
||||
if protoVersion != "" {
|
||||
attrs = append(attrs, semconvNew.NetworkProtocolVersion(protoVersion))
|
||||
}
|
||||
|
||||
return attrs
|
||||
}
|
||||
|
||||
// ResponseTraceAttrs returns trace attributes for an HTTP response made by a client.
|
||||
func (n newHTTPClient) ResponseTraceAttrs(resp *http.Response) []attribute.KeyValue {
|
||||
/*
|
||||
below attributes are returned:
|
||||
- http.response.status_code
|
||||
- error.type
|
||||
*/
|
||||
var count int
|
||||
if resp.StatusCode > 0 {
|
||||
count++
|
||||
}
|
||||
|
||||
if isErrorStatusCode(resp.StatusCode) {
|
||||
count++
|
||||
}
|
||||
|
||||
attrs := make([]attribute.KeyValue, 0, count)
|
||||
if resp.StatusCode > 0 {
|
||||
attrs = append(attrs, semconvNew.HTTPResponseStatusCode(resp.StatusCode))
|
||||
}
|
||||
|
||||
if isErrorStatusCode(resp.StatusCode) {
|
||||
errorType := strconv.Itoa(resp.StatusCode)
|
||||
attrs = append(attrs, semconvNew.ErrorTypeKey.String(errorType))
|
||||
}
|
||||
return attrs
|
||||
}
|
||||
|
||||
func (n newHTTPClient) ErrorType(err error) attribute.KeyValue {
|
||||
t := reflect.TypeOf(err)
|
||||
var value string
|
||||
if t.PkgPath() == "" && t.Name() == "" {
|
||||
// Likely a builtin type.
|
||||
value = t.String()
|
||||
} else {
|
||||
value = fmt.Sprintf("%s.%s", t.PkgPath(), t.Name())
|
||||
}
|
||||
|
||||
if value == "" {
|
||||
return semconvNew.ErrorTypeOther
|
||||
}
|
||||
|
||||
return semconvNew.ErrorTypeKey.String(value)
|
||||
}
|
||||
|
||||
func (n newHTTPClient) method(method string) (attribute.KeyValue, attribute.KeyValue) {
|
||||
if method == "" {
|
||||
return semconvNew.HTTPRequestMethodGet, attribute.KeyValue{}
|
||||
}
|
||||
if attr, ok := methodLookup[method]; ok {
|
||||
return attr, attribute.KeyValue{}
|
||||
}
|
||||
|
||||
orig := semconvNew.HTTPRequestMethodOriginal(method)
|
||||
if attr, ok := methodLookup[strings.ToUpper(method)]; ok {
|
||||
return attr, orig
|
||||
}
|
||||
return semconvNew.HTTPRequestMethodGet, orig
|
||||
}
|
||||
|
||||
func isErrorStatusCode(code int) bool {
|
||||
return code >= 400 || code < 100
|
||||
}
|
@ -9,8 +9,9 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
semconvNew "go.opentelemetry.io/otel/semconv/v1.24.0"
|
||||
semconvNew "go.opentelemetry.io/otel/semconv/v1.26.0"
|
||||
)
|
||||
|
||||
// splitHostPort splits a network address hostport of the form "host",
|
||||
@ -49,7 +50,7 @@ func splitHostPort(hostport string) (host string, port int) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return host, int(p)
|
||||
return host, int(p) // nolint: gosec // Byte size checked 16 above.
|
||||
}
|
||||
|
||||
func requiredHTTPPort(https bool, port int) int { // nolint:revive
|
||||
@ -89,3 +90,9 @@ var methodLookup = map[string]attribute.KeyValue{
|
||||
http.MethodPut: semconvNew.HTTPRequestMethodPut,
|
||||
http.MethodTrace: semconvNew.HTTPRequestMethodTrace,
|
||||
}
|
||||
|
||||
func handleErr(err error) {
|
||||
if err != nil {
|
||||
otel.Handle(err)
|
||||
}
|
||||
}
|
||||
|
@ -7,9 +7,13 @@ import (
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/metric/noop"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
|
||||
)
|
||||
|
||||
@ -72,3 +76,199 @@ func (o oldHTTPServer) Route(route string) attribute.KeyValue {
|
||||
func HTTPStatusCode(status int) attribute.KeyValue {
|
||||
return semconv.HTTPStatusCode(status)
|
||||
}
|
||||
|
||||
// Server HTTP metrics.
|
||||
const (
|
||||
serverRequestSize = "http.server.request.size" // Incoming request bytes total
|
||||
serverResponseSize = "http.server.response.size" // Incoming response bytes total
|
||||
serverDuration = "http.server.duration" // Incoming end to end duration, milliseconds
|
||||
)
|
||||
|
||||
func (h oldHTTPServer) createMeasures(meter metric.Meter) (metric.Int64Counter, metric.Int64Counter, metric.Float64Histogram) {
|
||||
if meter == nil {
|
||||
return noop.Int64Counter{}, noop.Int64Counter{}, noop.Float64Histogram{}
|
||||
}
|
||||
var err error
|
||||
requestBytesCounter, err := meter.Int64Counter(
|
||||
serverRequestSize,
|
||||
metric.WithUnit("By"),
|
||||
metric.WithDescription("Measures the size of HTTP request messages."),
|
||||
)
|
||||
handleErr(err)
|
||||
|
||||
responseBytesCounter, err := meter.Int64Counter(
|
||||
serverResponseSize,
|
||||
metric.WithUnit("By"),
|
||||
metric.WithDescription("Measures the size of HTTP response messages."),
|
||||
)
|
||||
handleErr(err)
|
||||
|
||||
serverLatencyMeasure, err := meter.Float64Histogram(
|
||||
serverDuration,
|
||||
metric.WithUnit("ms"),
|
||||
metric.WithDescription("Measures the duration of inbound HTTP requests."),
|
||||
)
|
||||
handleErr(err)
|
||||
|
||||
return requestBytesCounter, responseBytesCounter, serverLatencyMeasure
|
||||
}
|
||||
|
||||
func (o oldHTTPServer) MetricAttributes(server string, req *http.Request, statusCode int, additionalAttributes []attribute.KeyValue) []attribute.KeyValue {
|
||||
n := len(additionalAttributes) + 3
|
||||
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 {
|
||||
n++
|
||||
}
|
||||
protoName, protoVersion := netProtocol(req.Proto)
|
||||
if protoName != "" {
|
||||
n++
|
||||
}
|
||||
if protoVersion != "" {
|
||||
n++
|
||||
}
|
||||
|
||||
if statusCode > 0 {
|
||||
n++
|
||||
}
|
||||
|
||||
attributes := slices.Grow(additionalAttributes, n)
|
||||
attributes = append(attributes,
|
||||
standardizeHTTPMethodMetric(req.Method),
|
||||
o.scheme(req.TLS != nil),
|
||||
semconv.NetHostName(host))
|
||||
|
||||
if hostPort > 0 {
|
||||
attributes = append(attributes, semconv.NetHostPort(hostPort))
|
||||
}
|
||||
if protoName != "" {
|
||||
attributes = append(attributes, semconv.NetProtocolName(protoName))
|
||||
}
|
||||
if protoVersion != "" {
|
||||
attributes = append(attributes, semconv.NetProtocolVersion(protoVersion))
|
||||
}
|
||||
|
||||
if statusCode > 0 {
|
||||
attributes = append(attributes, semconv.HTTPStatusCode(statusCode))
|
||||
}
|
||||
return attributes
|
||||
}
|
||||
|
||||
func (o oldHTTPServer) scheme(https bool) attribute.KeyValue { // nolint:revive
|
||||
if https {
|
||||
return semconv.HTTPSchemeHTTPS
|
||||
}
|
||||
return semconv.HTTPSchemeHTTP
|
||||
}
|
||||
|
||||
type oldHTTPClient struct{}
|
||||
|
||||
func (o oldHTTPClient) RequestTraceAttrs(req *http.Request) []attribute.KeyValue {
|
||||
return semconvutil.HTTPClientRequest(req)
|
||||
}
|
||||
|
||||
func (o oldHTTPClient) ResponseTraceAttrs(resp *http.Response) []attribute.KeyValue {
|
||||
return semconvutil.HTTPClientResponse(resp)
|
||||
}
|
||||
|
||||
func (o oldHTTPClient) MetricAttributes(req *http.Request, statusCode int, additionalAttributes []attribute.KeyValue) []attribute.KeyValue {
|
||||
/* The following semantic conventions are returned if present:
|
||||
http.method string
|
||||
http.status_code int
|
||||
net.peer.name string
|
||||
net.peer.port int
|
||||
*/
|
||||
|
||||
n := 2 // method, peer name.
|
||||
var h string
|
||||
if req.URL != nil {
|
||||
h = req.URL.Host
|
||||
}
|
||||
var requestHost string
|
||||
var requestPort int
|
||||
for _, hostport := range []string{h, req.Header.Get("Host")} {
|
||||
requestHost, requestPort = splitHostPort(hostport)
|
||||
if requestHost != "" || requestPort > 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
port := requiredHTTPPort(req.URL != nil && req.URL.Scheme == "https", requestPort)
|
||||
if port > 0 {
|
||||
n++
|
||||
}
|
||||
|
||||
if statusCode > 0 {
|
||||
n++
|
||||
}
|
||||
|
||||
attributes := slices.Grow(additionalAttributes, n)
|
||||
attributes = append(attributes,
|
||||
standardizeHTTPMethodMetric(req.Method),
|
||||
semconv.NetPeerName(requestHost),
|
||||
)
|
||||
|
||||
if port > 0 {
|
||||
attributes = append(attributes, semconv.NetPeerPort(port))
|
||||
}
|
||||
|
||||
if statusCode > 0 {
|
||||
attributes = append(attributes, semconv.HTTPStatusCode(statusCode))
|
||||
}
|
||||
return attributes
|
||||
}
|
||||
|
||||
// Client HTTP metrics.
|
||||
const (
|
||||
clientRequestSize = "http.client.request.size" // Incoming request bytes total
|
||||
clientResponseSize = "http.client.response.size" // Incoming response bytes total
|
||||
clientDuration = "http.client.duration" // Incoming end to end duration, milliseconds
|
||||
)
|
||||
|
||||
func (o oldHTTPClient) createMeasures(meter metric.Meter) (metric.Int64Counter, metric.Int64Counter, metric.Float64Histogram) {
|
||||
if meter == nil {
|
||||
return noop.Int64Counter{}, noop.Int64Counter{}, noop.Float64Histogram{}
|
||||
}
|
||||
requestBytesCounter, err := meter.Int64Counter(
|
||||
clientRequestSize,
|
||||
metric.WithUnit("By"),
|
||||
metric.WithDescription("Measures the size of HTTP request messages."),
|
||||
)
|
||||
handleErr(err)
|
||||
|
||||
responseBytesCounter, err := meter.Int64Counter(
|
||||
clientResponseSize,
|
||||
metric.WithUnit("By"),
|
||||
metric.WithDescription("Measures the size of HTTP response messages."),
|
||||
)
|
||||
handleErr(err)
|
||||
|
||||
latencyMeasure, err := meter.Float64Histogram(
|
||||
clientDuration,
|
||||
metric.WithUnit("ms"),
|
||||
metric.WithDescription("Measures the duration of outbound HTTP requests."),
|
||||
)
|
||||
handleErr(err)
|
||||
|
||||
return requestBytesCounter, responseBytesCounter, latencyMeasure
|
||||
}
|
||||
|
||||
func standardizeHTTPMethodMetric(method string) attribute.KeyValue {
|
||||
method = strings.ToUpper(method)
|
||||
switch method {
|
||||
case http.MethodConnect, http.MethodDelete, http.MethodGet, http.MethodHead, http.MethodOptions, http.MethodPatch, http.MethodPost, http.MethodPut, http.MethodTrace:
|
||||
default:
|
||||
method = "_OTHER"
|
||||
}
|
||||
return semconv.HTTPMethod(method)
|
||||
}
|
||||
|
@ -195,7 +195,7 @@ func splitHostPort(hostport string) (host string, port int) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return host, int(p)
|
||||
return host, int(p) // nolint: gosec // Bitsize checked to be 16 above.
|
||||
}
|
||||
|
||||
func netProtocol(proto string) (name string, version string) {
|
||||
|
106
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/transport.go
generated
vendored
106
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/transport.go
generated
vendored
@ -11,13 +11,13 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil"
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/request"
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
|
||||
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
@ -26,17 +26,15 @@ import (
|
||||
type Transport struct {
|
||||
rt http.RoundTripper
|
||||
|
||||
tracer trace.Tracer
|
||||
meter metric.Meter
|
||||
propagators propagation.TextMapPropagator
|
||||
spanStartOptions []trace.SpanStartOption
|
||||
filters []Filter
|
||||
spanNameFormatter func(string, *http.Request) string
|
||||
clientTrace func(context.Context) *httptrace.ClientTrace
|
||||
tracer trace.Tracer
|
||||
propagators propagation.TextMapPropagator
|
||||
spanStartOptions []trace.SpanStartOption
|
||||
filters []Filter
|
||||
spanNameFormatter func(string, *http.Request) string
|
||||
clientTrace func(context.Context) *httptrace.ClientTrace
|
||||
metricAttributesFn func(*http.Request) []attribute.KeyValue
|
||||
|
||||
requestBytesCounter metric.Int64Counter
|
||||
responseBytesCounter metric.Int64Counter
|
||||
latencyMeasure metric.Float64Histogram
|
||||
semconv semconv.HTTPClient
|
||||
}
|
||||
|
||||
var _ http.RoundTripper = &Transport{}
|
||||
@ -63,43 +61,19 @@ func NewTransport(base http.RoundTripper, opts ...Option) *Transport {
|
||||
|
||||
c := newConfig(append(defaultOpts, opts...)...)
|
||||
t.applyConfig(c)
|
||||
t.createMeasures()
|
||||
|
||||
return &t
|
||||
}
|
||||
|
||||
func (t *Transport) applyConfig(c *config) {
|
||||
t.tracer = c.Tracer
|
||||
t.meter = c.Meter
|
||||
t.propagators = c.Propagators
|
||||
t.spanStartOptions = c.SpanStartOptions
|
||||
t.filters = c.Filters
|
||||
t.spanNameFormatter = c.SpanNameFormatter
|
||||
t.clientTrace = c.ClientTrace
|
||||
}
|
||||
|
||||
func (t *Transport) createMeasures() {
|
||||
var err error
|
||||
t.requestBytesCounter, err = t.meter.Int64Counter(
|
||||
clientRequestSize,
|
||||
metric.WithUnit("By"),
|
||||
metric.WithDescription("Measures the size of HTTP request messages."),
|
||||
)
|
||||
handleErr(err)
|
||||
|
||||
t.responseBytesCounter, err = t.meter.Int64Counter(
|
||||
clientResponseSize,
|
||||
metric.WithUnit("By"),
|
||||
metric.WithDescription("Measures the size of HTTP response messages."),
|
||||
)
|
||||
handleErr(err)
|
||||
|
||||
t.latencyMeasure, err = t.meter.Float64Histogram(
|
||||
clientDuration,
|
||||
metric.WithUnit("ms"),
|
||||
metric.WithDescription("Measures the duration of outbound HTTP requests."),
|
||||
)
|
||||
handleErr(err)
|
||||
t.semconv = semconv.NewHTTPClient(c.Meter)
|
||||
t.metricAttributesFn = c.MetricAttributesFn
|
||||
}
|
||||
|
||||
func defaultTransportFormatter(_ string, r *http.Request) string {
|
||||
@ -143,54 +117,68 @@ func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
|
||||
r = r.Clone(ctx) // According to RoundTripper spec, we shouldn't modify the origin request.
|
||||
|
||||
// use a body wrapper to determine the request size
|
||||
var bw bodyWrapper
|
||||
// if request body is nil or NoBody, we don't want to mutate the body as it
|
||||
// will affect the identity of it in an unforeseeable way because we assert
|
||||
// ReadCloser fulfills a certain interface and it is indeed nil or NoBody.
|
||||
bw := request.NewBodyWrapper(r.Body, func(int64) {})
|
||||
if r.Body != nil && r.Body != http.NoBody {
|
||||
bw.ReadCloser = r.Body
|
||||
// noop to prevent nil panic. not using this record fun yet.
|
||||
bw.record = func(int64) {}
|
||||
r.Body = &bw
|
||||
r.Body = bw
|
||||
}
|
||||
|
||||
span.SetAttributes(semconvutil.HTTPClientRequest(r)...)
|
||||
span.SetAttributes(t.semconv.RequestTraceAttrs(r)...)
|
||||
t.propagators.Inject(ctx, propagation.HeaderCarrier(r.Header))
|
||||
|
||||
res, err := t.rt.RoundTrip(r)
|
||||
if err != nil {
|
||||
span.RecordError(err)
|
||||
// set error type attribute if the error is part of the predefined
|
||||
// error types.
|
||||
// otherwise, record it as an exception
|
||||
if errType := t.semconv.ErrorType(err); errType.Valid() {
|
||||
span.SetAttributes(errType)
|
||||
} else {
|
||||
span.RecordError(err)
|
||||
}
|
||||
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
span.End()
|
||||
return res, err
|
||||
}
|
||||
|
||||
// metrics
|
||||
metricAttrs := append(labeler.Get(), semconvutil.HTTPClientRequestMetrics(r)...)
|
||||
if res.StatusCode > 0 {
|
||||
metricAttrs = append(metricAttrs, semconv.HTTPStatusCode(res.StatusCode))
|
||||
}
|
||||
o := metric.WithAttributeSet(attribute.NewSet(metricAttrs...))
|
||||
addOpts := []metric.AddOption{o} // Allocate vararg slice once.
|
||||
t.requestBytesCounter.Add(ctx, bw.read.Load(), addOpts...)
|
||||
metricOpts := t.semconv.MetricOptions(semconv.MetricAttributes{
|
||||
Req: r,
|
||||
StatusCode: res.StatusCode,
|
||||
AdditionalAttributes: append(labeler.Get(), t.metricAttributesFromRequest(r)...),
|
||||
})
|
||||
|
||||
// For handling response bytes we leverage a callback when the client reads the http response
|
||||
readRecordFunc := func(n int64) {
|
||||
t.responseBytesCounter.Add(ctx, n, addOpts...)
|
||||
t.semconv.RecordResponseSize(ctx, n, metricOpts.AddOptions())
|
||||
}
|
||||
|
||||
// traces
|
||||
span.SetAttributes(semconvutil.HTTPClientResponse(res)...)
|
||||
span.SetStatus(semconvutil.HTTPClientStatus(res.StatusCode))
|
||||
span.SetAttributes(t.semconv.ResponseTraceAttrs(res)...)
|
||||
span.SetStatus(t.semconv.Status(res.StatusCode))
|
||||
|
||||
res.Body = newWrappedBody(span, readRecordFunc, res.Body)
|
||||
|
||||
// Use floating point division here for higher precision (instead of Millisecond method).
|
||||
elapsedTime := float64(time.Since(requestStartTime)) / float64(time.Millisecond)
|
||||
|
||||
t.latencyMeasure.Record(ctx, elapsedTime, o)
|
||||
t.semconv.RecordMetrics(ctx, semconv.MetricData{
|
||||
RequestSize: bw.BytesRead(),
|
||||
ElapsedTime: elapsedTime,
|
||||
}, metricOpts)
|
||||
|
||||
return res, err
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (t *Transport) metricAttributesFromRequest(r *http.Request) []attribute.KeyValue {
|
||||
var attributeForRequest []attribute.KeyValue
|
||||
if t.metricAttributesFn != nil {
|
||||
attributeForRequest = t.metricAttributesFn(r)
|
||||
}
|
||||
return attributeForRequest
|
||||
}
|
||||
|
||||
// newWrappedBody returns a new and appropriately scoped *wrappedBody as an
|
||||
|
2
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/version.go
generated
vendored
2
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/version.go
generated
vendored
@ -5,7 +5,7 @@ package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http
|
||||
|
||||
// Version is the current release version of the otelhttp instrumentation.
|
||||
func Version() string {
|
||||
return "0.53.0"
|
||||
return "0.56.0"
|
||||
// This string is updated by the pre_release.sh script during release
|
||||
}
|
||||
|
||||
|
99
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/wrap.go
generated
vendored
99
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/wrap.go
generated
vendored
@ -1,99 +0,0 @@
|
||||
// Copyright The OpenTelemetry Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package otelhttp // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"sync/atomic"
|
||||
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
)
|
||||
|
||||
var _ io.ReadCloser = &bodyWrapper{}
|
||||
|
||||
// bodyWrapper wraps a http.Request.Body (an io.ReadCloser) to track the number
|
||||
// of bytes read and the last error.
|
||||
type bodyWrapper struct {
|
||||
io.ReadCloser
|
||||
record func(n int64) // must not be nil
|
||||
|
||||
read atomic.Int64
|
||||
err error
|
||||
}
|
||||
|
||||
func (w *bodyWrapper) Read(b []byte) (int, error) {
|
||||
n, err := w.ReadCloser.Read(b)
|
||||
n1 := int64(n)
|
||||
w.read.Add(n1)
|
||||
w.err = err
|
||||
w.record(n1)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (w *bodyWrapper) Close() error {
|
||||
return w.ReadCloser.Close()
|
||||
}
|
||||
|
||||
var _ http.ResponseWriter = &respWriterWrapper{}
|
||||
|
||||
// respWriterWrapper wraps a http.ResponseWriter in order to track the number of
|
||||
// bytes written, the last error, and to catch the first written statusCode.
|
||||
// TODO: The wrapped http.ResponseWriter doesn't implement any of the optional
|
||||
// types (http.Hijacker, http.Pusher, http.CloseNotifier, http.Flusher, etc)
|
||||
// that may be useful when using it in real life situations.
|
||||
type respWriterWrapper struct {
|
||||
http.ResponseWriter
|
||||
record func(n int64) // must not be nil
|
||||
|
||||
// used to inject the header
|
||||
ctx context.Context
|
||||
|
||||
props propagation.TextMapPropagator
|
||||
|
||||
written int64
|
||||
statusCode int
|
||||
err error
|
||||
wroteHeader bool
|
||||
}
|
||||
|
||||
func (w *respWriterWrapper) Header() http.Header {
|
||||
return w.ResponseWriter.Header()
|
||||
}
|
||||
|
||||
func (w *respWriterWrapper) Write(p []byte) (int, error) {
|
||||
if !w.wroteHeader {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
n, err := w.ResponseWriter.Write(p)
|
||||
n1 := int64(n)
|
||||
w.record(n1)
|
||||
w.written += n1
|
||||
w.err = err
|
||||
return n, err
|
||||
}
|
||||
|
||||
// WriteHeader persists initial statusCode for span attribution.
|
||||
// All calls to WriteHeader will be propagated to the underlying ResponseWriter
|
||||
// and will persist the statusCode from the first call.
|
||||
// Blocking consecutive calls to WriteHeader alters expected behavior and will
|
||||
// remove warning logs from net/http where developers will notice incorrect handler implementations.
|
||||
func (w *respWriterWrapper) WriteHeader(statusCode int) {
|
||||
if !w.wroteHeader {
|
||||
w.wroteHeader = true
|
||||
w.statusCode = statusCode
|
||||
}
|
||||
w.ResponseWriter.WriteHeader(statusCode)
|
||||
}
|
||||
|
||||
func (w *respWriterWrapper) Flush() {
|
||||
if !w.wroteHeader {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
|
||||
if f, ok := w.ResponseWriter.(http.Flusher); ok {
|
||||
f.Flush()
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user