chore: bump deps
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2025-08-12 07:04:57 +02:00
committed by decentral1se
parent 157d131b37
commit 56a68dfa91
981 changed files with 36486 additions and 39650 deletions

View File

@ -176,6 +176,10 @@ func WithMessageEvents(events ...event) Option {
// WithSpanNameFormatter takes a function that will be called on every
// request and the returned string will become the Span Name.
//
// When using [http.ServeMux] (or any middleware that sets the Pattern of [http.Request]),
// the span name formatter will run twice. Once when the span is created, and
// second time after the middleware, so the pattern can be used.
func WithSpanNameFormatter(f func(operation string, r *http.Request) string) Option {
return optionFunc(func(c *config) {
c.SpanNameFormatter = f

View File

@ -98,7 +98,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.semconv.RequestTraceAttrs(h.server, r)...),
trace.WithAttributes(h.semconv.RequestTraceAttrs(h.server, r, semconv.RequestTraceAttrsOpts{})...),
}
opts = append(opts, h.spanStartOptions...)
@ -176,7 +176,12 @@ func (h *middleware) serveHTTP(w http.ResponseWriter, r *http.Request, next http
ctx = ContextWithLabeler(ctx, labeler)
}
next.ServeHTTP(w, r.WithContext(ctx))
r = r.WithContext(ctx)
next.ServeHTTP(w, r)
if r.Pattern != "" {
span.SetName(h.spanNameFormatter(h.operation, r))
}
statusCode := rww.StatusCode()
bytesWritten := rww.BytesWritten()

View File

@ -1,9 +1,11 @@
// Code created by gotmpl. DO NOT MODIFY.
// Code generated by gotmpl. DO NOT MODIFY.
// source: internal/shared/request/body_wrapper.go.tmpl
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
// Package request provides types and functionality to handle HTTP request
// handling.
package request // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/request"
import (
@ -56,7 +58,7 @@ func (w *BodyWrapper) updateReadData(n int64, err error) {
}
}
// Closes closes the io.ReadCloser.
// Close closes the io.ReadCloser.
func (w *BodyWrapper) Close() error {
return w.ReadCloser.Close()
}

View File

@ -1,4 +1,4 @@
// Code created by gotmpl. DO NOT MODIFY.
// Code generated by gotmpl. DO NOT MODIFY.
// source: internal/shared/request/resp_writer_wrapper.go.tmpl
// Copyright The OpenTelemetry Authors
@ -105,7 +105,7 @@ func (w *RespWriterWrapper) BytesWritten() int64 {
return w.written
}
// BytesWritten returns the HTTP status code that was sent.
// StatusCode returns the HTTP status code that was sent.
func (w *RespWriterWrapper) StatusCode() int {
w.mu.RLock()
defer w.mu.RUnlock()

View File

@ -1,4 +1,4 @@
// Code created by gotmpl. DO NOT MODIFY.
// Code generated by gotmpl. DO NOT MODIFY.
// source: internal/shared/semconv/env.go.tmpl
// Copyright The OpenTelemetry Authors
@ -17,10 +17,11 @@ import (
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/semconv/v1.34.0/httpconv"
)
// OTelSemConvStabilityOptIn is an environment variable.
// That can be set to "old" or "http/dup" to opt into the new HTTP semantic conventions.
// That can be set to "http/dup" to keep getting the old HTTP semantic conventions.
const OTelSemConvStabilityOptIn = "OTEL_SEMCONV_STABILITY_OPT_IN"
type ResponseTelemetry struct {
@ -40,9 +41,9 @@ type HTTPServer struct {
serverLatencyMeasure metric.Float64Histogram
// New metrics
requestBodySizeHistogram metric.Int64Histogram
responseBodySizeHistogram metric.Int64Histogram
requestDurationHistogram metric.Float64Histogram
requestBodySizeHistogram httpconv.ServerRequestBodySize
responseBodySizeHistogram httpconv.ServerResponseBodySize
requestDurationHistogram httpconv.ServerRequestDuration
}
// RequestTraceAttrs returns trace attributes for an HTTP request received by a
@ -61,19 +62,23 @@ type HTTPServer struct {
//
// If the primary server name is not known, server should be an empty string.
// The req Host will be used to determine the server instead.
func (s HTTPServer) RequestTraceAttrs(server string, req *http.Request) []attribute.KeyValue {
func (s HTTPServer) RequestTraceAttrs(server string, req *http.Request, opts RequestTraceAttrsOpts) []attribute.KeyValue {
attrs := CurrentHTTPServer{}.RequestTraceAttrs(server, req, opts)
if s.duplicate {
return append(OldHTTPServer{}.RequestTraceAttrs(server, req), CurrentHTTPServer{}.RequestTraceAttrs(server, req)...)
return OldHTTPServer{}.RequestTraceAttrs(server, req, attrs)
}
return OldHTTPServer{}.RequestTraceAttrs(server, req)
return attrs
}
func (s HTTPServer) NetworkTransportAttr(network string) []attribute.KeyValue {
if s.duplicate {
return append([]attribute.KeyValue{OldHTTPServer{}.NetworkTransportAttr(network)}, CurrentHTTPServer{}.NetworkTransportAttr(network))
return []attribute.KeyValue{
OldHTTPServer{}.NetworkTransportAttr(network),
CurrentHTTPServer{}.NetworkTransportAttr(network),
}
}
return []attribute.KeyValue{
OldHTTPServer{}.NetworkTransportAttr(network),
CurrentHTTPServer{}.NetworkTransportAttr(network),
}
}
@ -81,15 +86,16 @@ func (s HTTPServer) NetworkTransportAttr(network string) []attribute.KeyValue {
//
// If any of the fields in the ResponseTelemetry are not set the attribute will be omitted.
func (s HTTPServer) ResponseTraceAttrs(resp ResponseTelemetry) []attribute.KeyValue {
attrs := CurrentHTTPServer{}.ResponseTraceAttrs(resp)
if s.duplicate {
return append(OldHTTPServer{}.ResponseTraceAttrs(resp), CurrentHTTPServer{}.ResponseTraceAttrs(resp)...)
return OldHTTPServer{}.ResponseTraceAttrs(resp, attrs)
}
return OldHTTPServer{}.ResponseTraceAttrs(resp)
return attrs
}
// Route returns the attribute for the route.
func (s HTTPServer) Route(route string) attribute.KeyValue {
return OldHTTPServer{}.Route(route)
return CurrentHTTPServer{}.Route(route)
}
// Status returns a span status code and message for an HTTP status code
@ -121,6 +127,8 @@ type MetricAttributes struct {
type MetricData struct {
RequestSize int64
// The request duration, in milliseconds
ElapsedTime float64
}
@ -139,7 +147,17 @@ var (
)
func (s HTTPServer) RecordMetrics(ctx context.Context, md ServerMetricData) {
if s.requestBytesCounter != nil && s.responseBytesCounter != nil && s.serverLatencyMeasure != nil {
attributes := CurrentHTTPServer{}.MetricAttributes(md.ServerName, md.Req, md.StatusCode, md.AdditionalAttributes)
o := metric.WithAttributeSet(attribute.NewSet(attributes...))
recordOpts := metricRecordOptionPool.Get().(*[]metric.RecordOption)
*recordOpts = append(*recordOpts, o)
s.requestBodySizeHistogram.Inst().Record(ctx, md.RequestSize, *recordOpts...)
s.responseBodySizeHistogram.Inst().Record(ctx, md.ResponseSize, *recordOpts...)
s.requestDurationHistogram.Inst().Record(ctx, md.ElapsedTime/1000.0, o)
*recordOpts = (*recordOpts)[:0]
metricRecordOptionPool.Put(recordOpts)
if s.duplicate && s.requestBytesCounter != nil && s.responseBytesCounter != nil && s.serverLatencyMeasure != nil {
attributes := OldHTTPServer{}.MetricAttributes(md.ServerName, md.Req, md.StatusCode, md.AdditionalAttributes)
o := metric.WithAttributeSet(attribute.NewSet(attributes...))
addOpts := metricAddOptionPool.Get().(*[]metric.AddOption)
@ -150,29 +168,44 @@ func (s HTTPServer) RecordMetrics(ctx context.Context, md ServerMetricData) {
*addOpts = (*addOpts)[:0]
metricAddOptionPool.Put(addOpts)
}
}
if s.duplicate && s.requestDurationHistogram != nil && s.requestBodySizeHistogram != nil && s.responseBodySizeHistogram != nil {
attributes := CurrentHTTPServer{}.MetricAttributes(md.ServerName, md.Req, md.StatusCode, md.AdditionalAttributes)
o := metric.WithAttributeSet(attribute.NewSet(attributes...))
recordOpts := metricRecordOptionPool.Get().(*[]metric.RecordOption)
*recordOpts = append(*recordOpts, o)
s.requestBodySizeHistogram.Record(ctx, md.RequestSize, *recordOpts...)
s.responseBodySizeHistogram.Record(ctx, md.ResponseSize, *recordOpts...)
s.requestDurationHistogram.Record(ctx, md.ElapsedTime, o)
*recordOpts = (*recordOpts)[:0]
metricRecordOptionPool.Put(recordOpts)
// hasOptIn returns true if the comma-separated version string contains the
// exact optIn value.
func hasOptIn(version, optIn string) bool {
for _, v := range strings.Split(version, ",") {
if strings.TrimSpace(v) == optIn {
return true
}
}
return false
}
func NewHTTPServer(meter metric.Meter) HTTPServer {
env := strings.ToLower(os.Getenv(OTelSemConvStabilityOptIn))
duplicate := env == "http/dup"
duplicate := hasOptIn(env, "http/dup")
server := HTTPServer{
duplicate: duplicate,
}
server.requestBytesCounter, server.responseBytesCounter, server.serverLatencyMeasure = OldHTTPServer{}.createMeasures(meter)
var err error
server.requestBodySizeHistogram, err = httpconv.NewServerRequestBodySize(meter)
handleErr(err)
server.responseBodySizeHistogram, err = httpconv.NewServerResponseBodySize(meter)
handleErr(err)
server.requestDurationHistogram, err = httpconv.NewServerRequestDuration(
meter,
metric.WithExplicitBucketBoundaries(
0.005, 0.01, 0.025, 0.05, 0.075, 0.1,
0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10,
),
)
handleErr(err)
if duplicate {
server.requestBodySizeHistogram, server.responseBodySizeHistogram, server.requestDurationHistogram = CurrentHTTPServer{}.createMeasures(meter)
server.requestBytesCounter, server.responseBytesCounter, server.serverLatencyMeasure = OldHTTPServer{}.createMeasures(meter)
}
return server
}
@ -186,19 +219,29 @@ type HTTPClient struct {
latencyMeasure metric.Float64Histogram
// new metrics
requestBodySize metric.Int64Histogram
requestDuration metric.Float64Histogram
requestBodySize httpconv.ClientRequestBodySize
requestDuration httpconv.ClientRequestDuration
}
func NewHTTPClient(meter metric.Meter) HTTPClient {
env := strings.ToLower(os.Getenv(OTelSemConvStabilityOptIn))
duplicate := env == "http/dup"
duplicate := hasOptIn(env, "http/dup")
client := HTTPClient{
duplicate: duplicate,
}
client.requestBytesCounter, client.responseBytesCounter, client.latencyMeasure = OldHTTPClient{}.createMeasures(meter)
var err error
client.requestBodySize, err = httpconv.NewClientRequestBodySize(meter)
handleErr(err)
client.requestDuration, err = httpconv.NewClientRequestDuration(
meter,
metric.WithExplicitBucketBoundaries(0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10),
)
handleErr(err)
if duplicate {
client.requestBodySize, client.requestDuration = CurrentHTTPClient{}.createMeasures(meter)
client.requestBytesCounter, client.responseBytesCounter, client.latencyMeasure = OldHTTPClient{}.createMeasures(meter)
}
return client
@ -206,19 +249,20 @@ func NewHTTPClient(meter metric.Meter) HTTPClient {
// RequestTraceAttrs returns attributes for an HTTP request made by a client.
func (c HTTPClient) RequestTraceAttrs(req *http.Request) []attribute.KeyValue {
attrs := CurrentHTTPClient{}.RequestTraceAttrs(req)
if c.duplicate {
return append(OldHTTPClient{}.RequestTraceAttrs(req), CurrentHTTPClient{}.RequestTraceAttrs(req)...)
return OldHTTPClient{}.RequestTraceAttrs(req, attrs)
}
return OldHTTPClient{}.RequestTraceAttrs(req)
return attrs
}
// ResponseTraceAttrs returns metric attributes for an HTTP request made by a client.
func (c HTTPClient) ResponseTraceAttrs(resp *http.Response) []attribute.KeyValue {
attrs := CurrentHTTPClient{}.ResponseTraceAttrs(resp)
if c.duplicate {
return append(OldHTTPClient{}.ResponseTraceAttrs(resp), CurrentHTTPClient{}.ResponseTraceAttrs(resp)...)
return OldHTTPClient{}.ResponseTraceAttrs(resp, attrs)
}
return OldHTTPClient{}.ResponseTraceAttrs(resp)
return attrs
}
func (c HTTPClient) Status(code int) (codes.Code, string) {
@ -232,11 +276,7 @@ func (c HTTPClient) Status(code int) (codes.Code, string) {
}
func (c HTTPClient) ErrorType(err error) attribute.KeyValue {
if c.duplicate {
return CurrentHTTPClient{}.ErrorType(err)
}
return attribute.KeyValue{}
return CurrentHTTPClient{}.ErrorType(err)
}
type MetricOpts struct {
@ -255,17 +295,17 @@ func (o MetricOpts) AddOptions() metric.AddOption {
func (c HTTPClient) MetricOptions(ma MetricAttributes) map[string]MetricOpts {
opts := map[string]MetricOpts{}
attributes := OldHTTPClient{}.MetricAttributes(ma.Req, ma.StatusCode, ma.AdditionalAttributes)
attributes := CurrentHTTPClient{}.MetricAttributes(ma.Req, ma.StatusCode, ma.AdditionalAttributes)
set := metric.WithAttributeSet(attribute.NewSet(attributes...))
opts["old"] = MetricOpts{
opts["new"] = MetricOpts{
measurement: set,
addOptions: set,
}
if c.duplicate {
attributes := CurrentHTTPClient{}.MetricAttributes(ma.Req, ma.StatusCode, ma.AdditionalAttributes)
attributes := OldHTTPClient{}.MetricAttributes(ma.Req, ma.StatusCode, ma.AdditionalAttributes)
set := metric.WithAttributeSet(attribute.NewSet(attributes...))
opts["new"] = MetricOpts{
opts["old"] = MetricOpts{
measurement: set,
addOptions: set,
}
@ -275,17 +315,12 @@ func (c HTTPClient) MetricOptions(ma MetricAttributes) map[string]MetricOpts {
}
func (s HTTPClient) RecordMetrics(ctx context.Context, md MetricData, opts map[string]MetricOpts) {
if s.requestBytesCounter == nil || s.latencyMeasure == nil {
// This will happen if an HTTPClient{} is used instead of NewHTTPClient().
return
}
s.requestBytesCounter.Add(ctx, md.RequestSize, opts["old"].AddOptions())
s.latencyMeasure.Record(ctx, md.ElapsedTime, opts["old"].MeasurementOption())
s.requestBodySize.Inst().Record(ctx, md.RequestSize, opts["new"].MeasurementOption())
s.requestDuration.Inst().Record(ctx, md.ElapsedTime/1000, opts["new"].MeasurementOption())
if s.duplicate {
s.requestBodySize.Record(ctx, md.RequestSize, opts["new"].MeasurementOption())
s.requestDuration.Record(ctx, md.ElapsedTime, opts["new"].MeasurementOption())
s.requestBytesCounter.Add(ctx, md.RequestSize, opts["old"].AddOptions())
s.latencyMeasure.Record(ctx, md.ElapsedTime, opts["old"].MeasurementOption())
}
}
@ -299,9 +334,10 @@ func (s HTTPClient) RecordResponseSize(ctx context.Context, responseData int64,
}
func (s HTTPClient) TraceAttributes(host string) []attribute.KeyValue {
attrs := CurrentHTTPClient{}.TraceAttributes(host)
if s.duplicate {
return append(OldHTTPClient{}.TraceAttributes(host), CurrentHTTPClient{}.TraceAttributes(host)...)
return OldHTTPClient{}.TraceAttributes(host, attrs)
}
return OldHTTPClient{}.TraceAttributes(host)
return attrs
}

View File

@ -5,10 +5,12 @@ package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/
// Generate semconv package:
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/bench_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=bench_test.go
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/common_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=common_test.go
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/env.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=env.go
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/env_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=env_test.go
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/httpconv.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=httpconv.go
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/httpconv_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=httpconv_test.go
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/httpconvtest_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=httpconvtest_test.go
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/util.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=util.go
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/util_test.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=util_test.go
//go:generate gotmpl --body=../../../../../../internal/shared/semconv/v1.20.0.go.tmpl "--data={ \"pkg\": \"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp\" }" --out=v1.20.0.go

View File

@ -1,9 +1,11 @@
// Code created by gotmpl. DO NOT MODIFY.
// Code generated by gotmpl. DO NOT MODIFY.
// source: internal/shared/semconv/httpconv.go.tmpl
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
// Package semconv provides OpenTelemetry semantic convention types and
// functionality.
package semconv // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv"
import (
@ -15,14 +17,17 @@ import (
"strings"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
"go.opentelemetry.io/otel/metric/noop"
semconvNew "go.opentelemetry.io/otel/semconv/v1.26.0"
semconvNew "go.opentelemetry.io/otel/semconv/v1.34.0"
)
type RequestTraceAttrsOpts struct {
// If set, this is used as value for the "http.client_ip" attribute.
HTTPClientIP string
}
type CurrentHTTPServer struct{}
// TraceRequest returns trace attributes for an HTTP request received by a
// RequestTraceAttrs returns trace attributes for an HTTP request received by a
// server.
//
// The server must be the primary server name if it is known. For example this
@ -38,7 +43,7 @@ type CurrentHTTPServer struct{}
//
// If the primary server name is not known, server should be an empty string.
// The req Host will be used to determine the server instead.
func (n CurrentHTTPServer) RequestTraceAttrs(server string, req *http.Request) []attribute.KeyValue {
func (n CurrentHTTPServer) RequestTraceAttrs(server string, req *http.Request, opts RequestTraceAttrsOpts) []attribute.KeyValue {
count := 3 // ServerAddress, Method, Scheme
var host string
@ -65,7 +70,8 @@ func (n CurrentHTTPServer) RequestTraceAttrs(server string, req *http.Request) [
scheme := n.scheme(req.TLS != nil)
if peer, peerPort := SplitHostPort(req.RemoteAddr); peer != "" {
peer, peerPort := SplitHostPort(req.RemoteAddr)
if peer != "" {
// The Go HTTP server sets RemoteAddr to "IP:port", this will not be a
// file-path that would be interpreted with a sock family.
count++
@ -79,7 +85,17 @@ func (n CurrentHTTPServer) RequestTraceAttrs(server string, req *http.Request) [
count++
}
clientIP := serverClientIP(req.Header.Get("X-Forwarded-For"))
// For client IP, use, in order:
// 1. The value passed in the options
// 2. The value in the X-Forwarded-For header
// 3. The peer address
clientIP := opts.HTTPClientIP
if clientIP == "" {
clientIP = serverClientIP(req.Header.Get("X-Forwarded-For"))
if clientIP == "" {
clientIP = peer
}
}
if clientIP != "" {
count++
}
@ -96,6 +112,11 @@ func (n CurrentHTTPServer) RequestTraceAttrs(server string, req *http.Request) [
count++
}
route := httpRoute(req.Pattern)
if route != "" {
count++
}
attrs := make([]attribute.KeyValue, 0, count)
attrs = append(attrs,
semconvNew.ServerAddress(host),
@ -119,7 +140,7 @@ func (n CurrentHTTPServer) RequestTraceAttrs(server string, req *http.Request) [
}
}
if useragent := req.UserAgent(); useragent != "" {
if useragent != "" {
attrs = append(attrs, semconvNew.UserAgentOriginal(useragent))
}
@ -138,10 +159,14 @@ func (n CurrentHTTPServer) RequestTraceAttrs(server string, req *http.Request) [
attrs = append(attrs, semconvNew.NetworkProtocolVersion(protoVersion))
}
if route != "" {
attrs = append(attrs, n.Route(route))
}
return attrs
}
func (o CurrentHTTPServer) NetworkTransportAttr(network string) attribute.KeyValue {
func (n CurrentHTTPServer) NetworkTransportAttr(network string) attribute.KeyValue {
switch network {
case "tcp", "tcp4", "tcp6":
return semconvNew.NetworkTransportTCP
@ -176,9 +201,11 @@ func (n CurrentHTTPServer) scheme(https bool) attribute.KeyValue { // nolint:rev
return semconvNew.URLScheme("http")
}
// TraceResponse returns trace attributes for telemetry from an HTTP response.
// ResponseTraceAttrs returns trace attributes for telemetry from an HTTP
// response.
//
// If any of the fields in the ResponseTelemetry are not set the attribute will be omitted.
// If any of the fields in the ResponseTelemetry are not set the attribute will
// be omitted.
func (n CurrentHTTPServer) ResponseTraceAttrs(resp ResponseTelemetry) []attribute.KeyValue {
var count int
@ -218,35 +245,6 @@ func (n CurrentHTTPServer) Route(route string) attribute.KeyValue {
return semconvNew.HTTPRoute(route)
}
func (n CurrentHTTPServer) createMeasures(meter metric.Meter) (metric.Int64Histogram, metric.Int64Histogram, metric.Float64Histogram) {
if meter == nil {
return noop.Int64Histogram{}, noop.Int64Histogram{}, noop.Float64Histogram{}
}
var err error
requestBodySizeHistogram, err := meter.Int64Histogram(
semconvNew.HTTPServerRequestBodySizeName,
metric.WithUnit(semconvNew.HTTPServerRequestBodySizeUnit),
metric.WithDescription(semconvNew.HTTPServerRequestBodySizeDescription),
)
handleErr(err)
responseBodySizeHistogram, err := meter.Int64Histogram(
semconvNew.HTTPServerResponseBodySizeName,
metric.WithUnit(semconvNew.HTTPServerResponseBodySizeUnit),
metric.WithDescription(semconvNew.HTTPServerResponseBodySizeDescription),
)
handleErr(err)
requestDurationHistogram, err := meter.Float64Histogram(
semconvNew.HTTPServerRequestDurationName,
metric.WithUnit(semconvNew.HTTPServerRequestDurationUnit),
metric.WithDescription(semconvNew.HTTPServerRequestDurationDescription),
)
handleErr(err)
return requestBodySizeHistogram, responseBodySizeHistogram, requestDurationHistogram
}
func (n CurrentHTTPServer) MetricAttributes(server string, req *http.Request, statusCode int, additionalAttributes []attribute.KeyValue) []attribute.KeyValue {
num := len(additionalAttributes) + 3
var host string
@ -442,29 +440,6 @@ func (n CurrentHTTPClient) method(method string) (attribute.KeyValue, attribute.
return semconvNew.HTTPRequestMethodGet, orig
}
func (n CurrentHTTPClient) createMeasures(meter metric.Meter) (metric.Int64Histogram, metric.Float64Histogram) {
if meter == nil {
return noop.Int64Histogram{}, noop.Float64Histogram{}
}
var err error
requestBodySize, err := meter.Int64Histogram(
semconvNew.HTTPClientRequestBodySizeName,
metric.WithUnit(semconvNew.HTTPClientRequestBodySizeUnit),
metric.WithDescription(semconvNew.HTTPClientRequestBodySizeDescription),
)
handleErr(err)
requestDuration, err := meter.Float64Histogram(
semconvNew.HTTPClientRequestDurationName,
metric.WithUnit(semconvNew.HTTPClientRequestDurationUnit),
metric.WithDescription(semconvNew.HTTPClientRequestDurationDescription),
)
handleErr(err)
return requestBodySize, requestDuration
}
func (n CurrentHTTPClient) MetricAttributes(req *http.Request, statusCode int, additionalAttributes []attribute.KeyValue) []attribute.KeyValue {
num := len(additionalAttributes) + 2
var h string
@ -501,7 +476,7 @@ func (n CurrentHTTPClient) MetricAttributes(req *http.Request, statusCode int, a
attributes = append(attributes,
semconvNew.HTTPRequestMethodKey.String(standardizeHTTPMethod(req.Method)),
semconvNew.ServerAddress(requestHost),
n.scheme(req.TLS != nil),
n.scheme(req),
)
if port > 0 {
@ -520,15 +495,18 @@ func (n CurrentHTTPClient) MetricAttributes(req *http.Request, statusCode int, a
return attributes
}
// Attributes for httptrace.
// TraceAttributes returns attributes for httptrace.
func (n CurrentHTTPClient) TraceAttributes(host string) []attribute.KeyValue {
return []attribute.KeyValue{
semconvNew.ServerAddress(host),
}
}
func (n CurrentHTTPClient) scheme(https bool) attribute.KeyValue { // nolint:revive
if https {
func (n CurrentHTTPClient) scheme(req *http.Request) attribute.KeyValue {
if req.URL != nil && req.URL.Scheme != "" {
return semconvNew.URLScheme(req.URL.Scheme)
}
if req.TLS != nil {
return semconvNew.URLScheme("https")
}
return semconvNew.URLScheme("http")

View File

@ -1,4 +1,4 @@
// Code created by gotmpl. DO NOT MODIFY.
// Code generated by gotmpl. DO NOT MODIFY.
// source: internal/shared/semconv/util.go.tmpl
// Copyright The OpenTelemetry Authors
@ -14,7 +14,7 @@ import (
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
semconvNew "go.opentelemetry.io/otel/semconv/v1.26.0"
semconvNew "go.opentelemetry.io/otel/semconv/v1.34.0"
)
// SplitHostPort splits a network address hostport of the form "host",
@ -28,17 +28,17 @@ func SplitHostPort(hostport string) (host string, port int) {
port = -1
if strings.HasPrefix(hostport, "[") {
addrEnd := strings.LastIndex(hostport, "]")
addrEnd := strings.LastIndexByte(hostport, ']')
if addrEnd < 0 {
// Invalid hostport.
return
}
if i := strings.LastIndex(hostport[addrEnd:], ":"); i < 0 {
if i := strings.LastIndexByte(hostport[addrEnd:], ':'); i < 0 {
host = hostport[1:addrEnd]
return
}
} else {
if i := strings.LastIndex(hostport, ":"); i < 0 {
if i := strings.LastIndexByte(hostport, ':'); i < 0 {
host = hostport
return
}
@ -70,12 +70,19 @@ func requiredHTTPPort(https bool, port int) int { // nolint:revive
}
func serverClientIP(xForwardedFor string) string {
if idx := strings.Index(xForwardedFor, ","); idx >= 0 {
if idx := strings.IndexByte(xForwardedFor, ','); idx >= 0 {
xForwardedFor = xForwardedFor[:idx]
}
return xForwardedFor
}
func httpRoute(pattern string) string {
if idx := strings.IndexByte(pattern, '/'); idx >= 0 {
return pattern[idx:]
}
return ""
}
func netProtocol(proto string) (name string, version string) {
name, version, _ = strings.Cut(proto, "/")
switch name {

View File

@ -1,4 +1,4 @@
// Code created by gotmpl. DO NOT MODIFY.
// Code generated by gotmpl. DO NOT MODIFY.
// source: internal/shared/semconv/v120.0.go.tmpl
// Copyright The OpenTelemetry Authors
@ -37,8 +37,8 @@ type OldHTTPServer struct{}
//
// If the primary server name is not known, server should be an empty string.
// The req Host will be used to determine the server instead.
func (o OldHTTPServer) RequestTraceAttrs(server string, req *http.Request) []attribute.KeyValue {
return semconvutil.HTTPServerRequest(server, req)
func (o OldHTTPServer) RequestTraceAttrs(server string, req *http.Request, attrs []attribute.KeyValue) []attribute.KeyValue {
return semconvutil.HTTPServerRequest(server, req, semconvutil.HTTPServerRequestOptions{}, attrs)
}
func (o OldHTTPServer) NetworkTransportAttr(network string) attribute.KeyValue {
@ -48,9 +48,7 @@ func (o OldHTTPServer) NetworkTransportAttr(network string) attribute.KeyValue {
// ResponseTraceAttrs returns trace attributes for telemetry from an HTTP response.
//
// If any of the fields in the ResponseTelemetry are not set the attribute will be omitted.
func (o OldHTTPServer) ResponseTraceAttrs(resp ResponseTelemetry) []attribute.KeyValue {
attributes := []attribute.KeyValue{}
func (o OldHTTPServer) ResponseTraceAttrs(resp ResponseTelemetry, attributes []attribute.KeyValue) []attribute.KeyValue {
if resp.ReadBytes > 0 {
attributes = append(attributes, semconv.HTTPRequestContentLength(int(resp.ReadBytes)))
}
@ -179,12 +177,12 @@ func (o OldHTTPServer) scheme(https bool) attribute.KeyValue { // nolint:revive
type OldHTTPClient struct{}
func (o OldHTTPClient) RequestTraceAttrs(req *http.Request) []attribute.KeyValue {
return semconvutil.HTTPClientRequest(req)
func (o OldHTTPClient) RequestTraceAttrs(req *http.Request, attrs []attribute.KeyValue) []attribute.KeyValue {
return semconvutil.HTTPClientRequest(req, attrs)
}
func (o OldHTTPClient) ResponseTraceAttrs(resp *http.Response) []attribute.KeyValue {
return semconvutil.HTTPClientResponse(resp)
func (o OldHTTPClient) ResponseTraceAttrs(resp *http.Response, attrs []attribute.KeyValue) []attribute.KeyValue {
return semconvutil.HTTPClientResponse(resp, attrs)
}
func (o OldHTTPClient) MetricAttributes(req *http.Request, statusCode int, additionalAttributes []attribute.KeyValue) []attribute.KeyValue {
@ -269,9 +267,7 @@ func (o OldHTTPClient) createMeasures(meter metric.Meter) (metric.Int64Counter,
return requestBytesCounter, responseBytesCounter, latencyMeasure
}
// Attributes for httptrace.
func (c OldHTTPClient) TraceAttributes(host string) []attribute.KeyValue {
return []attribute.KeyValue{
semconv.NetHostName(host),
}
// TraceAttributes returns attributes for httptrace.
func (c OldHTTPClient) TraceAttributes(host string, attrs []attribute.KeyValue) []attribute.KeyValue {
return append(attrs, semconv.NetHostName(host))
}

View File

@ -1,14 +1,16 @@
// Code created by gotmpl. DO NOT MODIFY.
// Code generated by gotmpl. DO NOT MODIFY.
// source: internal/shared/semconvutil/httpconv.go.tmpl
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
// Package semconvutil provides OpenTelemetry semantic convention utilities.
package semconvutil // import "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil"
import (
"fmt"
"net/http"
"slices"
"strings"
"go.opentelemetry.io/otel/attribute"
@ -16,6 +18,11 @@ import (
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
)
type HTTPServerRequestOptions struct {
// If set, this is used as value for the "http.client_ip" attribute.
HTTPClientIP string
}
// HTTPClientResponse returns trace attributes for an HTTP response received by a
// client from a server. It will return the following attributes if the related
// values are defined in resp: "http.status.code",
@ -26,9 +33,9 @@ import (
// attributes. If a complete set of attributes can be generated using the
// request contained in resp. For example:
//
// append(HTTPClientResponse(resp), ClientRequest(resp.Request)...)
func HTTPClientResponse(resp *http.Response) []attribute.KeyValue {
return hc.ClientResponse(resp)
// HTTPClientResponse(resp, ClientRequest(resp.Request)))
func HTTPClientResponse(resp *http.Response, attrs []attribute.KeyValue) []attribute.KeyValue {
return hc.ClientResponse(resp, attrs)
}
// HTTPClientRequest returns trace attributes for an HTTP request made by a client.
@ -36,8 +43,8 @@ func HTTPClientResponse(resp *http.Response) []attribute.KeyValue {
// "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)
func HTTPClientRequest(req *http.Request, attrs []attribute.KeyValue) []attribute.KeyValue {
return hc.ClientRequest(req, attrs)
}
// HTTPClientRequestMetrics returns metric attributes for an HTTP request made by a client.
@ -75,8 +82,8 @@ func HTTPClientStatus(code int) (codes.Code, string) {
// "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)
func HTTPServerRequest(server string, req *http.Request, opts HTTPServerRequestOptions, attrs []attribute.KeyValue) []attribute.KeyValue {
return hc.ServerRequest(server, req, opts, attrs)
}
// HTTPServerRequestMetrics returns metric attributes for an HTTP request received by a
@ -153,8 +160,8 @@ var hc = &httpConv{
// attributes. If a complete set of attributes can be generated using the
// request contained in resp. For example:
//
// append(ClientResponse(resp), ClientRequest(resp.Request)...)
func (c *httpConv) ClientResponse(resp *http.Response) []attribute.KeyValue {
// ClientResponse(resp, ClientRequest(resp.Request))
func (c *httpConv) ClientResponse(resp *http.Response, attrs []attribute.KeyValue) []attribute.KeyValue {
/* The following semantic conventions are returned if present:
http.status_code int
http.response_content_length int
@ -166,8 +173,11 @@ func (c *httpConv) ClientResponse(resp *http.Response) []attribute.KeyValue {
if resp.ContentLength > 0 {
n++
}
if n == 0 {
return attrs
}
attrs := make([]attribute.KeyValue, 0, n)
attrs = slices.Grow(attrs, n)
if resp.StatusCode > 0 {
attrs = append(attrs, c.HTTPStatusCodeKey.Int(resp.StatusCode))
}
@ -182,7 +192,7 @@ func (c *httpConv) ClientResponse(resp *http.Response) []attribute.KeyValue {
// "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 {
func (c *httpConv) ClientRequest(req *http.Request, attrs []attribute.KeyValue) []attribute.KeyValue {
/* The following semantic conventions are returned if present:
http.method string
user_agent.original string
@ -221,8 +231,7 @@ func (c *httpConv) ClientRequest(req *http.Request) []attribute.KeyValue {
n++
}
attrs := make([]attribute.KeyValue, 0, n)
attrs = slices.Grow(attrs, n)
attrs = append(attrs, c.method(req.Method))
var u string
@ -305,7 +314,7 @@ func (c *httpConv) ClientRequestMetrics(req *http.Request) []attribute.KeyValue
// 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 {
func (c *httpConv) ServerRequest(server string, req *http.Request, opts HTTPServerRequestOptions, attrs []attribute.KeyValue) []attribute.KeyValue {
/* The following semantic conventions are returned if present:
http.method string
http.scheme string
@ -358,7 +367,17 @@ func (c *httpConv) ServerRequest(server string, req *http.Request) []attribute.K
n++
}
clientIP := serverClientIP(req.Header.Get("X-Forwarded-For"))
// For client IP, use, in order:
// 1. The value passed in the options
// 2. The value in the X-Forwarded-For header
// 3. The peer address
clientIP := opts.HTTPClientIP
if clientIP == "" {
clientIP = serverClientIP(req.Header.Get("X-Forwarded-For"))
if clientIP == "" {
clientIP = peer
}
}
if clientIP != "" {
n++
}
@ -378,7 +397,7 @@ func (c *httpConv) ServerRequest(server string, req *http.Request) []attribute.K
n++
}
attrs := make([]attribute.KeyValue, 0, n)
attrs = slices.Grow(attrs, n)
attrs = append(attrs, c.method(req.Method))
attrs = append(attrs, c.scheme(req.TLS != nil))

View File

@ -1,4 +1,4 @@
// Code created by gotmpl. DO NOT MODIFY.
// Code generated by gotmpl. DO NOT MODIFY.
// source: internal/shared/semconvutil/netconv.go.tmpl
// Copyright The OpenTelemetry Authors

View File

@ -35,14 +35,14 @@ func (l *Labeler) Get() []attribute.KeyValue {
type labelerContextKeyType int
const lablelerContextKey labelerContextKeyType = 0
const labelerContextKey labelerContextKeyType = 0
// ContextWithLabeler returns a new context with the provided Labeler instance.
// Attributes added to the specified labeler will be injected into metrics
// emitted by the instrumentation. Only one labeller can be injected into the
// context. Injecting it multiple times will override the previous calls.
func ContextWithLabeler(parent context.Context, l *Labeler) context.Context {
return context.WithValue(parent, lablelerContextKey, l)
return context.WithValue(parent, labelerContextKey, l)
}
// LabelerFromContext retrieves a Labeler instance from the provided context if
@ -50,7 +50,7 @@ func ContextWithLabeler(parent context.Context, l *Labeler) context.Context {
// Labeler is returned and the second return value is false. In this case it is
// safe to use the Labeler but any attributes added to it will not be used.
func LabelerFromContext(ctx context.Context) (*Labeler, bool) {
l, ok := ctx.Value(lablelerContextKey).(*Labeler)
l, ok := ctx.Value(labelerContextKey).(*Labeler)
if !ok {
l = &Labeler{}
}

View File

@ -129,6 +129,41 @@ func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) {
t.propagators.Inject(ctx, propagation.HeaderCarrier(r.Header))
res, err := t.rt.RoundTrip(r)
// Defer metrics recording function to record the metrics on error or no error.
defer func() {
metricAttributes := semconv.MetricAttributes{
Req: r,
AdditionalAttributes: append(labeler.Get(), t.metricAttributesFromRequest(r)...),
}
if err == nil {
metricAttributes.StatusCode = res.StatusCode
}
metricOpts := t.semconv.MetricOptions(metricAttributes)
metricData := semconv.MetricData{
RequestSize: bw.BytesRead(),
}
if err == nil {
// For handling response bytes we leverage a callback when the client reads the http response
readRecordFunc := func(n int64) {
t.semconv.RecordResponseSize(ctx, n, metricOpts)
}
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)
metricData.ElapsedTime = elapsedTime
t.semconv.RecordMetrics(ctx, metricData, metricOpts)
}()
if err != nil {
// set error type attribute if the error is part of the predefined
// error types.
@ -141,35 +176,14 @@ func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) {
span.SetStatus(codes.Error, err.Error())
span.End()
return res, err
}
// metrics
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.semconv.RecordResponseSize(ctx, n, metricOpts)
}
// traces
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.semconv.RecordMetrics(ctx, semconv.MetricData{
RequestSize: bw.BytesRead(),
ElapsedTime: elapsedTime,
}, metricOpts)
return res, nil
}

View File

@ -5,13 +5,6 @@ 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.60.0"
return "0.62.0"
// This string is updated by the pre_release.sh script during release
}
// SemVersion is the semantic version to be supplied to tracer/meter creation.
//
// Deprecated: Use [Version] instead.
func SemVersion() string {
return Version()
}