Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use semconv migration package for httptrace #6720

Merged
merged 9 commits into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- Added the `WithMetricAttributesFn` option to allow setting dynamic, per-request metric attributes in `go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux`. (#6648)
- Added metrics support, and emit all stable metrics from the [Semantic Conventions](https://github.com/open-telemetry/semantic-conventions/blob/main/docs/http/http-metrics.md) in `go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux`. (#6648)
- Add support for configuring `Insecure` field for OTLP exporters in `go.opentelemetry.io/contrib/config`. (#6658)
- Support for the `OTEL_HTTP_CLIENT_COMPATIBILITY_MODE=http/dup` environment variable in `instrumentation/net/http/httptrace/otelhttptrace` to emit attributes for both the v1.20.0 and v1.26.0 semantic conventions. (#6720)

### Changed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ func (s HTTPServer) RequestTraceAttrs(server string, req *http.Request) []attrib
return OldHTTPServer{}.RequestTraceAttrs(server, req)
}

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),
}
}

// 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.
Expand Down Expand Up @@ -288,3 +297,11 @@ func (s HTTPClient) RecordResponseSize(ctx context.Context, responseData int64,

s.responseBytesCounter.Add(ctx, responseData, opts["old"].AddOptions())
}

func (s HTTPClient) TraceAttributes(host string) []attribute.KeyValue {
if s.duplicate {
return append(OldHTTPClient{}.TraceAttributes(host), CurrentHTTPClient{}.TraceAttributes(host)...)
}

return OldHTTPClient{}.TraceAttributes(host)
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import (
"net/http"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric/noop"
)

Expand Down Expand Up @@ -53,6 +55,51 @@ func TestHTTPServerDoesNotPanic(t *testing.T) {
}
}

func TestServerNetworkTransportAttr(t *testing.T) {
for _, tt := range []struct {
name string
optinVal string
network string

wantAttributes []attribute.KeyValue
}{
{
name: "without any opt-in",
network: "tcp",

wantAttributes: []attribute.KeyValue{
attribute.String("net.transport", "ip_tcp"),
},
},
{
name: "without an old optin",
optinVal: "old",
network: "tcp",

wantAttributes: []attribute.KeyValue{
attribute.String("net.transport", "ip_tcp"),
},
},
{
name: "without a dup optin",
optinVal: "http/dup",
network: "tcp",

wantAttributes: []attribute.KeyValue{
attribute.String("net.transport", "ip_tcp"),
attribute.String("network.transport", "tcp"),
},
},
} {
t.Run(tt.name, func(t *testing.T) {
t.Setenv(OTelSemConvStabilityOptIn, tt.optinVal)
s := NewHTTPServer(nil)

assert.Equal(t, tt.wantAttributes, s.NetworkTransportAttr(tt.network))
})
}
}

func TestHTTPClientDoesNotPanic(t *testing.T) {
testCases := []struct {
name string
Expand Down Expand Up @@ -94,6 +141,93 @@ func TestHTTPClientDoesNotPanic(t *testing.T) {
}
}

func TestHTTPClientTraceAttributes(t *testing.T) {
for _, tt := range []struct {
name string
optinVal string

wantAttributes []attribute.KeyValue
}{
{
name: "with no optin set",

wantAttributes: []attribute.KeyValue{
attribute.String("net.host.name", "example.com"),
},
},
{
name: "with optin set to old only",
optinVal: "old",

wantAttributes: []attribute.KeyValue{
attribute.String("net.host.name", "example.com"),
},
},
{
name: "with optin set to duplicate",
optinVal: "http/dup",

wantAttributes: []attribute.KeyValue{
attribute.String("net.host.name", "example.com"),
attribute.String("server.address", "example.com"),
},
},
} {
t.Run(tt.name, func(t *testing.T) {
t.Setenv(OTelSemConvStabilityOptIn, tt.optinVal)

c := NewHTTPClient(nil)
a := c.TraceAttributes("example.com")
assert.Equal(t, tt.wantAttributes, a)
})
}
}

func TestClientTraceAttributes(t *testing.T) {
for _, tt := range []struct {
name string
optinVal string
host string

wantAttributes []attribute.KeyValue
}{
{
name: "without any opt-in",
host: "example.com",

wantAttributes: []attribute.KeyValue{
attribute.String("net.host.name", "example.com"),
},
},
{
name: "without an old optin",
optinVal: "old",
host: "example.com",

wantAttributes: []attribute.KeyValue{
attribute.String("net.host.name", "example.com"),
},
},
{
name: "without a dup optin",
optinVal: "http/dup",
host: "example.com",

wantAttributes: []attribute.KeyValue{
attribute.String("net.host.name", "example.com"),
attribute.String("server.address", "example.com"),
},
},
} {
t.Run(tt.name, func(t *testing.T) {
t.Setenv(OTelSemConvStabilityOptIn, tt.optinVal)
s := NewHTTPClient(nil)

assert.Equal(t, tt.wantAttributes, s.TraceAttributes(tt.host))
})
}
}

func BenchmarkRecordMetrics(b *testing.B) {
benchmarks := []struct {
name string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,19 @@
return attrs
}

func (o CurrentHTTPServer) NetworkTransportAttr(network string) attribute.KeyValue {
switch network {
case "tcp", "tcp4", "tcp6":
return semconvNew.NetworkTransportTCP
case "udp", "udp4", "udp6":
return semconvNew.NetworkTransportUDP
case "unix", "unixgram", "unixpacket":
return semconvNew.NetworkTransportUnix
default:
return semconvNew.NetworkTransportPipe

Check warning on line 153 in instrumentation/github.com/gorilla/mux/otelmux/internal/semconv/httpconv.go

View check run for this annotation

Codecov / codecov/patch

instrumentation/github.com/gorilla/mux/otelmux/internal/semconv/httpconv.go#L148-L153

Added lines #L148 - L153 were not covered by tests
}
}

func (n CurrentHTTPServer) method(method string) (attribute.KeyValue, attribute.KeyValue) {
if method == "" {
return semconvNew.HTTPRequestMethodGet, attribute.KeyValue{}
Expand Down Expand Up @@ -507,6 +520,13 @@
return attributes
}

// 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 {
return semconvNew.URLScheme("https")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ func (o OldHTTPServer) RequestTraceAttrs(server string, req *http.Request) []att
return semconvutil.HTTPServerRequest(server, req)
}

func (o OldHTTPServer) NetworkTransportAttr(network string) attribute.KeyValue {
return semconvutil.NetTransport(network)
}

// 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.
Expand Down Expand Up @@ -264,3 +268,10 @@ 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),
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import (
"strings"
"sync"

"go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/internal/semconv"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
"go.opentelemetry.io/otel/trace"
)

Expand Down Expand Up @@ -125,6 +125,7 @@ type clientTracer struct {
redactedHeaders map[string]struct{}
addHeaders bool
useSpans bool
semconv semconv.HTTPClient
}

// NewClientTrace returns an httptrace.ClientTrace implementation that will
Expand All @@ -148,6 +149,7 @@ func NewClientTrace(ctx context.Context, opts ...ClientTraceOption) *httptrace.C
},
addHeaders: true,
useSpans: true,
semconv: semconv.NewHTTPClient(nil),
}

if span := trace.SpanFromContext(ctx); span.SpanContext().IsValid() {
Expand Down Expand Up @@ -266,7 +268,7 @@ func (ct *clientTracer) span(hook string) trace.Span {
}

func (ct *clientTracer) getConn(host string) {
ct.start("http.getconn", "http.getconn", semconv.NetHostName(host))
ct.start("http.getconn", "http.getconn", ct.semconv.TraceAttributes(host)...)
}

func (ct *clientTracer) gotConn(info httptrace.GotConnInfo) {
Expand All @@ -291,7 +293,7 @@ func (ct *clientTracer) gotFirstResponseByte() {
}

func (ct *clientTracer) dnsStart(info httptrace.DNSStartInfo) {
ct.start("http.dns", "http.dns", semconv.NetHostName(info.Host))
ct.start("http.dns", "http.dns", ct.semconv.TraceAttributes(info.Host)...)
}

func (ct *clientTracer) dnsDone(info httptrace.DNSDoneInfo) {
Expand Down
2 changes: 1 addition & 1 deletion instrumentation/net/http/httptrace/otelhttptrace/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/stretchr/testify v1.10.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0
go.opentelemetry.io/otel v1.34.0
go.opentelemetry.io/otel/metric v1.34.0
go.opentelemetry.io/otel/trace v1.34.0
)

Expand All @@ -17,7 +18,6 @@ require (
github.com/go-logr/stdr v1.2.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

Expand Down
15 changes: 8 additions & 7 deletions instrumentation/net/http/httptrace/otelhttptrace/httptrace.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@ import (
"context"
"net/http"

"go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/internal/semconvutil"
"go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace/internal/semconv"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/baggage"
"go.opentelemetry.io/otel/propagation"
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
"go.opentelemetry.io/otel/trace"
)

Expand Down Expand Up @@ -54,11 +53,13 @@ func Extract(ctx context.Context, req *http.Request, opts ...Option) ([]attribut
c := newConfig(opts)
ctx = c.propagators.Extract(ctx, propagation.HeaderCarrier(req.Header))

attrs := append(semconvutil.HTTPServerRequest("", req), semconvutil.NetTransport("tcp"))
if req.ContentLength > 0 {
a := semconv.HTTPRequestContentLength(int(req.ContentLength))
attrs = append(attrs, a)
}
semconvSrv := semconv.NewHTTPServer(nil)

attrs := append(semconvSrv.RequestTraceAttrs("", req), semconvSrv.NetworkTransportAttr("tcp")...)
attrs = append(attrs, semconvSrv.ResponseTraceAttrs(semconv.ResponseTelemetry{
ReadBytes: req.ContentLength,
})...)

return attrs, baggage.FromContext(ctx), trace.SpanContextFromContext(ctx)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Code created by gotmpl. DO NOT MODIFY.
// source: internal/shared/semconv/bench_test.go.tmpl

// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package semconv

import (
"net/http"
"net/url"
"testing"

"go.opentelemetry.io/otel/attribute"
)

var benchHTTPServerRequestResults []attribute.KeyValue

// BenchmarkHTTPServerRequest allows comparison between different version of the HTTP server.
// To use an alternative start this test with OTEL_SEMCONV_STABILITY_OPT_IN set to the
// version under test.
func BenchmarkHTTPServerRequest(b *testing.B) {
// Request was generated from TestHTTPServerRequest request.
req := &http.Request{
Method: http.MethodGet,
URL: &url.URL{
Path: "/",
},
Proto: "HTTP/1.1",
ProtoMajor: 1,
ProtoMinor: 1,
Header: http.Header{
"User-Agent": []string{"Go-http-client/1.1"},
"Accept-Encoding": []string{"gzip"},
},
Body: http.NoBody,
Host: "127.0.0.1:39093",
RemoteAddr: "127.0.0.1:38738",
RequestURI: "/",
}
serv := NewHTTPServer(nil)

b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
benchHTTPServerRequestResults = serv.RequestTraceAttrs("", req)
}
}
Loading