mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-05-05 11:00:12 -05:00
build(deps): bump go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp
Bumps [go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp](https://github.com/open-telemetry/opentelemetry-go-contrib) from 0.47.0 to 0.48.0. - [Release notes](https://github.com/open-telemetry/opentelemetry-go-contrib/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go-contrib/compare/zpages/v0.47.0...zpages/v0.48.0) --- updated-dependencies: - dependency-name: go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
committed by
Ralf Haferkamp
parent
f746f4d1c1
commit
8b403a4b86
@@ -88,7 +88,7 @@ require (
|
||||
github.com/xhit/go-simple-mail/v2 v2.16.0
|
||||
go-micro.dev/v4 v4.9.0
|
||||
go.etcd.io/bbolt v1.3.8
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0
|
||||
go.opentelemetry.io/contrib/zpages v0.48.0
|
||||
go.opentelemetry.io/otel v1.23.1
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.17.0
|
||||
|
||||
@@ -2094,8 +2094,8 @@ go.opentelemetry.io/contrib v1.0.0 h1:khwDCxdSspjOLmFnvMuSHd/5rPzbTx0+l6aURwtQdf
|
||||
go.opentelemetry.io/contrib v1.0.0/go.mod h1:EH4yDYeNoaTqn/8yCWQmfNB78VHfGX2Jt2bvnvzBlGM=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0 h1:PzIubN4/sjByhDRHLviCjJuweBXWFZWhghjg7cS28+M=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.0/go.mod h1:Ct6zzQEuGK3WpJs2n4dn+wfJYzd/+hNnxMRTWjGn30M=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 h1:sv9kVfal0MK0wBMCOGr+HeJm9v803BkJxGrk2au7j08=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0 h1:doUP+ExOpH3spVTLS0FcWGLnQrPct/hD/bCPbDRUEAU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0/go.mod h1:rdENBZMT2OE6Ne/KLwpiXudnAsbdrdBaqBvTN8M8BgA=
|
||||
go.opentelemetry.io/contrib/zpages v0.48.0 h1:7vnqarYRu2VYvUUwrLWXRk5HqPuCbtuZdgR4eaVeP3Y=
|
||||
go.opentelemetry.io/contrib/zpages v0.48.0/go.mod h1:fzewgBzh126TFUgnNhI9/1hjvnZes7wBKXAMQmyM01g=
|
||||
go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs=
|
||||
|
||||
+17
-3
@@ -31,10 +31,24 @@ const (
|
||||
|
||||
// Server HTTP metrics.
|
||||
const (
|
||||
RequestCount = "http.server.request_count" // Incoming request count total
|
||||
RequestContentLength = "http.server.request_content_length" // Incoming request bytes total
|
||||
// Deprecated: This field is unused.
|
||||
RequestCount = "http.server.request_count" // Incoming request count total
|
||||
// Deprecated: Use of this field has been migrated to serverRequestSize. It will be removed in a future version.
|
||||
RequestContentLength = "http.server.request_content_length" // Incoming request bytes total
|
||||
// Deprecated: Use of this field has been migrated to serverResponseSize. It will be removed in a future version.
|
||||
ResponseContentLength = "http.server.response_content_length" // Incoming response bytes total
|
||||
ServerLatency = "http.server.duration" // Incoming end to end duration, milliseconds
|
||||
// Deprecated: Use of this field has been migrated to serverDuration. It will be removed in a future version.
|
||||
ServerLatency = "http.server.duration" // Incoming end to end duration, milliseconds
|
||||
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
|
||||
|
||||
+6
-6
@@ -108,23 +108,23 @@ func handleErr(err error) {
|
||||
func (h *middleware) createMeasures() {
|
||||
var err error
|
||||
h.requestBytesCounter, err = h.meter.Int64Counter(
|
||||
RequestContentLength,
|
||||
serverRequestSize,
|
||||
metric.WithUnit("By"),
|
||||
metric.WithDescription("Measures the size of HTTP request content length (uncompressed)"),
|
||||
metric.WithDescription("Measures the size of HTTP request messages."),
|
||||
)
|
||||
handleErr(err)
|
||||
|
||||
h.responseBytesCounter, err = h.meter.Int64Counter(
|
||||
ResponseContentLength,
|
||||
serverResponseSize,
|
||||
metric.WithUnit("By"),
|
||||
metric.WithDescription("Measures the size of HTTP response content length (uncompressed)"),
|
||||
metric.WithDescription("Measures the size of HTTP response messages."),
|
||||
)
|
||||
handleErr(err)
|
||||
|
||||
h.serverLatencyMeasure, err = h.meter.Float64Histogram(
|
||||
ServerLatency,
|
||||
serverDuration,
|
||||
metric.WithUnit("ms"),
|
||||
metric.WithDescription("Measures the duration of HTTP request handling"),
|
||||
metric.WithDescription("Measures the duration of inbound HTTP requests."),
|
||||
)
|
||||
handleErr(err)
|
||||
}
|
||||
|
||||
Generated
Vendored
+40
-55
@@ -51,6 +51,14 @@ 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) {
|
||||
@@ -114,36 +122,6 @@ 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 {
|
||||
@@ -286,6 +264,38 @@ func (c *httpConv) ClientRequest(req *http.Request) []attribute.KeyValue {
|
||||
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
|
||||
}
|
||||
|
||||
// ServerRequest returns attributes for an HTTP request received by a server.
|
||||
//
|
||||
// The server must be the primary server name if it is known. For example this
|
||||
@@ -551,31 +561,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) {
|
||||
|
||||
vendor/go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil/netconv.go
Generated
Vendored
-163
@@ -32,24 +32,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 {
|
||||
@@ -125,52 +107,6 @@ func (c *netConv) Host(address string) []attribute.KeyValue {
|
||||
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)
|
||||
}
|
||||
@@ -179,85 +115,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":
|
||||
@@ -273,26 +130,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)
|
||||
|
||||
Generated
Vendored
+101
-8
@@ -19,31 +19,43 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptrace"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/otel/metric"
|
||||
|
||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil"
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/propagation"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
|
||||
)
|
||||
|
||||
// Transport implements the http.RoundTripper interface and wraps
|
||||
// outbound HTTP(S) requests with a span.
|
||||
// outbound HTTP(S) requests with a span and enriches it with metrics.
|
||||
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
|
||||
|
||||
requestBytesCounter metric.Int64Counter
|
||||
responseBytesCounter metric.Int64Counter
|
||||
latencyMeasure metric.Float64Histogram
|
||||
}
|
||||
|
||||
var _ http.RoundTripper = &Transport{}
|
||||
|
||||
// NewTransport wraps the provided http.RoundTripper with one that
|
||||
// starts a span and injects the span context into the outbound request headers.
|
||||
// starts a span, injects the span context into the outbound request headers,
|
||||
// and enriches it with metrics.
|
||||
//
|
||||
// If the provided http.RoundTripper is nil, http.DefaultTransport will be used
|
||||
// as the base http.RoundTripper.
|
||||
@@ -63,12 +75,14 @@ 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
|
||||
@@ -76,6 +90,30 @@ func (t *Transport) applyConfig(c *config) {
|
||||
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)
|
||||
}
|
||||
|
||||
func defaultTransportFormatter(_ string, r *http.Request) string {
|
||||
return "HTTP " + r.Method
|
||||
}
|
||||
@@ -84,6 +122,7 @@ func defaultTransportFormatter(_ string, r *http.Request) string {
|
||||
// before handing the request to the configured base RoundTripper. The created span will
|
||||
// end when the response body is closed or when a read from the body returns io.EOF.
|
||||
func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
requestStartTime := time.Now()
|
||||
for _, f := range t.filters {
|
||||
if !f(r) {
|
||||
// Simply pass through to the base RoundTripper if a filter rejects the request
|
||||
@@ -109,7 +148,23 @@ func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
ctx = httptrace.WithClientTrace(ctx, t.clientTrace(ctx))
|
||||
}
|
||||
|
||||
labeler := &Labeler{}
|
||||
ctx = injectLabeler(ctx, labeler)
|
||||
|
||||
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.
|
||||
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
|
||||
}
|
||||
|
||||
span.SetAttributes(semconvutil.HTTPClientRequest(r)...)
|
||||
t.propagators.Inject(ctx, propagation.HeaderCarrier(r.Header))
|
||||
|
||||
@@ -121,9 +176,28 @@ func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
return res, err
|
||||
}
|
||||
|
||||
// metrics
|
||||
metricAttrs := append(labeler.Get(), semconvutil.HTTPClientRequestMetrics(r)...)
|
||||
if res.StatusCode > 0 {
|
||||
metricAttrs = append(metricAttrs, semconv.HTTPStatusCode(res.StatusCode))
|
||||
}
|
||||
o := metric.WithAttributes(metricAttrs...)
|
||||
t.requestBytesCounter.Add(ctx, bw.read, o)
|
||||
// For handling response bytes we leverage a callback when the client reads the http response
|
||||
readRecordFunc := func(n int64) {
|
||||
t.responseBytesCounter.Add(ctx, n, o)
|
||||
}
|
||||
|
||||
// traces
|
||||
span.SetAttributes(semconvutil.HTTPClientResponse(res)...)
|
||||
span.SetStatus(semconvutil.HTTPClientStatus(res.StatusCode))
|
||||
res.Body = newWrappedBody(span, res.Body)
|
||||
|
||||
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)
|
||||
|
||||
return res, err
|
||||
}
|
||||
@@ -131,17 +205,17 @@ func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) {
|
||||
// newWrappedBody returns a new and appropriately scoped *wrappedBody as an
|
||||
// io.ReadCloser. If the passed body implements io.Writer, the returned value
|
||||
// will implement io.ReadWriteCloser.
|
||||
func newWrappedBody(span trace.Span, body io.ReadCloser) io.ReadCloser {
|
||||
func newWrappedBody(span trace.Span, record func(n int64), body io.ReadCloser) io.ReadCloser {
|
||||
// The successful protocol switch responses will have a body that
|
||||
// implement an io.ReadWriteCloser. Ensure this interface type continues
|
||||
// to be satisfied if that is the case.
|
||||
if _, ok := body.(io.ReadWriteCloser); ok {
|
||||
return &wrappedBody{span: span, body: body}
|
||||
return &wrappedBody{span: span, record: record, body: body}
|
||||
}
|
||||
|
||||
// Remove the implementation of the io.ReadWriteCloser and only implement
|
||||
// the io.ReadCloser.
|
||||
return struct{ io.ReadCloser }{&wrappedBody{span: span, body: body}}
|
||||
return struct{ io.ReadCloser }{&wrappedBody{span: span, record: record, body: body}}
|
||||
}
|
||||
|
||||
// wrappedBody is the response body type returned by the transport
|
||||
@@ -153,8 +227,11 @@ func newWrappedBody(span trace.Span, body io.ReadCloser) io.ReadCloser {
|
||||
// If the response body implements the io.Writer interface (i.e. for
|
||||
// successful protocol switches), the wrapped body also will.
|
||||
type wrappedBody struct {
|
||||
span trace.Span
|
||||
body io.ReadCloser
|
||||
span trace.Span
|
||||
recorded atomic.Bool
|
||||
record func(n int64)
|
||||
body io.ReadCloser
|
||||
read atomic.Int64
|
||||
}
|
||||
|
||||
var _ io.ReadWriteCloser = &wrappedBody{}
|
||||
@@ -171,11 +248,14 @@ func (wb *wrappedBody) Write(p []byte) (int, error) {
|
||||
|
||||
func (wb *wrappedBody) Read(b []byte) (int, error) {
|
||||
n, err := wb.body.Read(b)
|
||||
// Record the number of bytes read
|
||||
wb.read.Add(int64(n))
|
||||
|
||||
switch err {
|
||||
case nil:
|
||||
// nothing to do here but fall through to the return
|
||||
case io.EOF:
|
||||
wb.recordBytesRead()
|
||||
wb.span.End()
|
||||
default:
|
||||
wb.span.RecordError(err)
|
||||
@@ -184,7 +264,20 @@ func (wb *wrappedBody) Read(b []byte) (int, error) {
|
||||
return n, err
|
||||
}
|
||||
|
||||
// recordBytesRead is a function that ensures the number of bytes read is recorded once and only once.
|
||||
func (wb *wrappedBody) recordBytesRead() {
|
||||
// note: it is more performant (and equally correct) to use atomic.Bool over sync.Once here. In the event that
|
||||
// two goroutines are racing to call this method, the number of bytes read will no longer increase. Using
|
||||
// CompareAndSwap allows later goroutines to return quickly and not block waiting for the race winner to finish
|
||||
// calling wb.record(wb.read.Load()).
|
||||
if wb.recorded.CompareAndSwap(false, true) {
|
||||
// Record the total number of bytes read
|
||||
wb.record(wb.read.Load())
|
||||
}
|
||||
}
|
||||
|
||||
func (wb *wrappedBody) Close() error {
|
||||
wb.recordBytesRead()
|
||||
wb.span.End()
|
||||
if wb.body != nil {
|
||||
return wb.body.Close()
|
||||
|
||||
+1
-1
@@ -16,7 +16,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.47.0"
|
||||
return "0.48.0"
|
||||
// This string is updated by the pre_release.sh script during release
|
||||
}
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -1935,7 +1935,7 @@ go.opentelemetry.io/contrib
|
||||
## explicit; go 1.20
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/internal
|
||||
# go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0
|
||||
# go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0
|
||||
## explicit; go 1.20
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil
|
||||
|
||||
Reference in New Issue
Block a user