Skip to content
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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
- `go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho`
- `go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace`
- `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp`
- Improve performance by reducing allocations for http request when using `OTEL_SEMCONV_STABILITY_OPT_IN=http/dup` in the modules below. (#7180)
- `go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful`
- `go.opentelemetry.io/contrib/instrumentation/github.com/gin-gonic/gin/otelgin`
- `go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux`
- `go.opentelemetry.io/contrib/instrumentation/github.com/labstack/echo/otelecho`
- `go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace`
- `go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp`

The `OTEL_SEMCONV_STABILITY_OPT_IN=http/dup` environment variable can be still used to emit both the v1.20.0 and v1.26.0 semantic conventions.
It is however impossible to emit only the 1.20.0 semantic conventions, as the next release will drop support for that environment variable. (#6899)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,19 @@
// 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, opts RequestTraceAttrsOpts) []attribute.KeyValue {
attrs := CurrentHTTPServer{}.RequestTraceAttrs(server, req, opts)
if s.duplicate {
return append(OldHTTPServer{}.RequestTraceAttrs(server, req), CurrentHTTPServer{}.RequestTraceAttrs(server, req, opts)...)
return OldHTTPServer{}.RequestTraceAttrs(server, req, attrs)
}
return CurrentHTTPServer{}.RequestTraceAttrs(server, req, opts)
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{
CurrentHTTPServer{}.NetworkTransportAttr(network),
Expand All @@ -81,10 +85,11 @@
//
// 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)

Check warning on line 90 in instrumentation/github.com/emicklei/go-restful/otelrestful/internal/semconv/env.go

View check run for this annotation

Codecov / codecov/patch

instrumentation/github.com/emicklei/go-restful/otelrestful/internal/semconv/env.go#L90

Added line #L90 was not covered by tests
}
return CurrentHTTPServer{}.ResponseTraceAttrs(resp)
return attrs
}

// Route returns the attribute for the route.
Expand Down Expand Up @@ -208,19 +213,20 @@

// 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 CurrentHTTPClient{}.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 CurrentHTTPClient{}.ResponseTraceAttrs(resp)
return attrs
}

func (c HTTPClient) Status(code int) (codes.Code, string) {
Expand Down Expand Up @@ -297,9 +303,10 @@
}

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 CurrentHTTPClient{}.TraceAttributes(host)
return attrs
}
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,8 @@ func TestHTTPClientTraceAttributes(t *testing.T) {
optinVal: "http/dup",

wantAttributes: []attribute.KeyValue{
attribute.String("net.host.name", "example.com"),
attribute.String("server.address", "example.com"),
attribute.String("net.host.name", "example.com"),
},
},
} {
Expand Down Expand Up @@ -188,8 +188,8 @@ func TestClientTraceAttributes(t *testing.T) {
host: "example.com",

wantAttributes: []attribute.KeyValue{
attribute.String("net.host.name", "example.com"),
attribute.String("server.address", "example.com"),
attribute.String("net.host.name", "example.com"),
},
},
} {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@
//
// 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, semconvutil.HTTPServerRequestOptions{})
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 {
Expand All @@ -48,9 +48,7 @@
// 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 {

Check warning on line 51 in instrumentation/github.com/emicklei/go-restful/otelrestful/internal/semconv/v1.20.0.go

View check run for this annotation

Codecov / codecov/patch

instrumentation/github.com/emicklei/go-restful/otelrestful/internal/semconv/v1.20.0.go#L51

Added line #L51 was not covered by tests
if resp.ReadBytes > 0 {
attributes = append(attributes, semconv.HTTPRequestContentLength(int(resp.ReadBytes)))
}
Expand Down Expand Up @@ -179,12 +177,12 @@

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 {
Expand Down Expand Up @@ -270,8 +268,6 @@
}

// TraceAttributes returns attributes for httptrace.
func (c OldHTTPClient) TraceAttributes(host string) []attribute.KeyValue {
return []attribute.KeyValue{
semconv.NetHostName(host),
}
func (c OldHTTPClient) TraceAttributes(host string, attrs []attribute.KeyValue) []attribute.KeyValue {
return append(attrs, semconv.NetHostName(host))
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import (
"fmt"
"net/http"
"slices"
"strings"

"go.opentelemetry.io/otel/attribute"
Expand All @@ -32,18 +33,18 @@
// 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.
// 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)
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.
Expand Down Expand Up @@ -81,8 +82,8 @@
// "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, opts HTTPServerRequestOptions) []attribute.KeyValue {
return hc.ServerRequest(server, req, opts)
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
Expand Down Expand Up @@ -159,8 +160,8 @@
// 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
Expand All @@ -172,8 +173,11 @@
if resp.ContentLength > 0 {
n++
}
if n == 0 {
return attrs
}

Check warning on line 178 in instrumentation/github.com/emicklei/go-restful/otelrestful/internal/semconvutil/httpconv.go

View check run for this annotation

Codecov / codecov/patch

instrumentation/github.com/emicklei/go-restful/otelrestful/internal/semconvutil/httpconv.go#L177-L178

Added lines #L177 - L178 were not covered by tests

attrs := make([]attribute.KeyValue, 0, n)
attrs = slices.Grow(attrs, n)
if resp.StatusCode > 0 {
attrs = append(attrs, c.HTTPStatusCodeKey.Int(resp.StatusCode))
}
Expand All @@ -188,7 +192,7 @@
// "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
Expand Down Expand Up @@ -227,8 +231,7 @@
n++
}

attrs := make([]attribute.KeyValue, 0, n)

attrs = slices.Grow(attrs, n)
attrs = append(attrs, c.method(req.Method))

var u string
Expand Down Expand Up @@ -311,7 +314,7 @@
// 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, opts HTTPServerRequestOptions) []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
Expand Down Expand Up @@ -394,7 +397,7 @@
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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func TestHTTPClientResponse(t *testing.T) {
StatusCode: stat,
ContentLength: n,
}
got := HTTPClientResponse(resp)
got := HTTPClientResponse(resp, nil)
assert.Equal(t, 2, cap(got), "slice capacity")
assert.ElementsMatch(t, []attribute.KeyValue{
attribute.Key("http.status_code").Int(stat),
Expand Down Expand Up @@ -55,7 +55,7 @@ func TestHTTPSClientRequest(t *testing.T) {
attribute.String("http.url", "https://127.0.0.1:443/resource"),
attribute.String("net.peer.name", "127.0.0.1"),
},
HTTPClientRequest(req),
HTTPClientRequest(req, nil),
)
}

Expand Down Expand Up @@ -115,7 +115,7 @@ func TestHTTPClientRequest(t *testing.T) {
attribute.String("user_agent.original", agent),
attribute.Int("http.request_content_length", n),
},
HTTPClientRequest(req),
HTTPClientRequest(req, nil),
)
}

Expand Down Expand Up @@ -156,7 +156,7 @@ func TestHTTPClientRequestMetrics(t *testing.T) {
func TestHTTPClientRequestRequired(t *testing.T) {
req := new(http.Request)
var got []attribute.KeyValue
assert.NotPanics(t, func() { got = HTTPClientRequest(req) })
assert.NotPanics(t, func() { got = HTTPClientRequest(req, nil) })
want := []attribute.KeyValue{
attribute.String("http.method", "GET"),
attribute.String("http.url", ""),
Expand Down Expand Up @@ -248,7 +248,7 @@ func TestHTTPServerRequest(t *testing.T) {
attribute.String("net.protocol.version", "1.1"),
attribute.String("http.target", "/"),
},
HTTPServerRequest("", got, tt.httpServerRequestOpts))
HTTPServerRequest("", got, tt.httpServerRequestOpts, nil))
})
}
}
Expand Down Expand Up @@ -301,22 +301,22 @@ func TestHTTPServerName(t *testing.T) {
)
portStr := strconv.Itoa(port)
server := host + ":" + portStr
assert.NotPanics(t, func() { got = HTTPServerRequest(server, req, HTTPServerRequestOptions{}) })
assert.NotPanics(t, func() { got = HTTPServerRequest(server, req, HTTPServerRequestOptions{}, nil) })
assert.Contains(t, got, attribute.String("net.host.name", host))
assert.Contains(t, got, attribute.Int("net.host.port", port))

req = &http.Request{Host: "alt.host.name:" + portStr}
// The server parameter does not include a port, ServerRequest should use
// the port in the request Host field.
assert.NotPanics(t, func() { got = HTTPServerRequest(host, req, HTTPServerRequestOptions{}) })
assert.NotPanics(t, func() { got = HTTPServerRequest(host, req, HTTPServerRequestOptions{}, nil) })
assert.Contains(t, got, attribute.String("net.host.name", host))
assert.Contains(t, got, attribute.Int("net.host.port", port))
}

func TestHTTPServerRequestFailsGracefully(t *testing.T) {
req := new(http.Request)
var got []attribute.KeyValue
assert.NotPanics(t, func() { got = HTTPServerRequest("", req, HTTPServerRequestOptions{}) })
assert.NotPanics(t, func() { got = HTTPServerRequest("", req, HTTPServerRequestOptions{}, nil) })
want := []attribute.KeyValue{
attribute.String("http.method", "GET"),
attribute.String("http.scheme", "http"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ package otelrestful // import "go.opentelemetry.io/contrib/instrumentation/githu
import (
"github.com/emicklei/go-restful/v3"

"go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful/internal/semconv"
"go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful/internal/semconvutil"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/propagation"
oteltrace "go.opentelemetry.io/otel/trace"

"go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful/internal/semconv"
"go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful/internal/semconvutil"
)

// ScopeName is the instrumentation scope name.
Expand Down Expand Up @@ -45,7 +46,7 @@ func OTelFilter(service string, opts ...Option) restful.FilterFunction {
spanName := route

opts := []oteltrace.SpanStartOption{
oteltrace.WithAttributes(semconvutil.HTTPServerRequest(service, r, semconvutil.HTTPServerRequestOptions{})...),
oteltrace.WithAttributes(semconvutil.HTTPServerRequest(service, r, semconvutil.HTTPServerRequestOptions{}, nil)...),
oteltrace.WithSpanKind(oteltrace.SpanKindServer),
}
if route != "" {
Expand Down
Loading
Loading