1
0
Fork 0

Upgrade to OpenTelemetry Semantic Conventions v1.26.0

This commit is contained in:
Michael 2024-06-27 14:14:03 +02:00 committed by GitHub
parent 2f9905061e
commit 8cb1829698
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 558 additions and 217 deletions

View file

@ -6,6 +6,8 @@ import (
"io"
"net"
"net/http"
"net/url"
"slices"
"strconv"
"strings"
@ -17,7 +19,7 @@ import (
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/propagation"
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
"go.opentelemetry.io/otel/trace"
)
@ -47,7 +49,7 @@ func NewTracing(conf *static.Tracing) (*Tracer, io.Closer, error) {
return nil, nil, err
}
return NewTracer(tr, conf.CapturedRequestHeaders, conf.CapturedResponseHeaders), closer, nil
return NewTracer(tr, conf.CapturedRequestHeaders, conf.CapturedResponseHeaders, conf.SafeQueryParams), closer, nil
}
// TracerFromContext extracts the trace.Tracer from the given context.
@ -122,14 +124,16 @@ func (t TracerProvider) Tracer(name string, options ...trace.TracerOption) trace
type Tracer struct {
trace.Tracer
safeQueryParams []string
capturedRequestHeaders []string
capturedResponseHeaders []string
}
// NewTracer builds and configures a new Tracer.
func NewTracer(tracer trace.Tracer, capturedRequestHeaders, capturedResponseHeaders []string) *Tracer {
func NewTracer(tracer trace.Tracer, capturedRequestHeaders, capturedResponseHeaders, safeQueryParams []string) *Tracer {
return &Tracer{
Tracer: tracer,
safeQueryParams: safeQueryParams,
capturedRequestHeaders: capturedRequestHeaders,
capturedResponseHeaders: capturedResponseHeaders,
}
@ -153,37 +157,37 @@ func (t *Tracer) Start(ctx context.Context, spanName string, opts ...trace.SpanS
}
// CaptureClientRequest used to add span attributes from the request as a Client.
// TODO: need to update the semconv package as it does not implement fully Semantic Convention v1.23.0.
func (t *Tracer) CaptureClientRequest(span trace.Span, r *http.Request) {
if t == nil || span == nil || r == nil {
return
}
// Common attributes https://github.com/open-telemetry/semantic-conventions/blob/v1.23.0/docs/http/http-spans.md#common-attributes
// Common attributes https://github.com/open-telemetry/semantic-conventions/blob/v1.26.0/docs/http/http-spans.md#common-attributes
span.SetAttributes(semconv.HTTPRequestMethodKey.String(r.Method))
span.SetAttributes(semconv.NetworkProtocolVersion(proto(r.Proto)))
// Client attributes https://github.com/open-telemetry/semantic-conventions/blob/v1.23.0/docs/http/http-spans.md#http-client
span.SetAttributes(semconv.URLFull(r.URL.String()))
span.SetAttributes(semconv.URLScheme(r.URL.Scheme))
// Client attributes https://github.com/open-telemetry/semantic-conventions/blob/v1.26.0/docs/http/http-spans.md#http-client
sURL := t.safeURL(r.URL)
span.SetAttributes(semconv.URLFull(sURL.String()))
span.SetAttributes(semconv.URLScheme(sURL.Scheme))
span.SetAttributes(semconv.UserAgentOriginal(r.UserAgent()))
host, port, err := net.SplitHostPort(r.URL.Host)
host, port, err := net.SplitHostPort(sURL.Host)
if err != nil {
span.SetAttributes(attribute.String("network.peer.address", host))
span.SetAttributes(semconv.ServerAddress(r.URL.Host))
switch r.URL.Scheme {
span.SetAttributes(semconv.NetworkPeerAddress(host))
span.SetAttributes(semconv.ServerAddress(sURL.Host))
switch sURL.Scheme {
case "http":
span.SetAttributes(attribute.String("network.peer.port", "80"))
span.SetAttributes(semconv.NetworkPeerPort(80))
span.SetAttributes(semconv.ServerPort(80))
case "https":
span.SetAttributes(attribute.String("network.peer.port", "443"))
span.SetAttributes(semconv.NetworkPeerPort(443))
span.SetAttributes(semconv.ServerPort(443))
}
} else {
span.SetAttributes(attribute.String("network.peer.address", host))
span.SetAttributes(attribute.String("network.peer.port", port))
span.SetAttributes(semconv.NetworkPeerAddress(host))
intPort, _ := strconv.Atoi(port)
span.SetAttributes(semconv.NetworkPeerPort(intPort))
span.SetAttributes(semconv.ServerAddress(host))
span.SetAttributes(semconv.ServerPort(intPort))
}
@ -201,20 +205,20 @@ func (t *Tracer) CaptureClientRequest(span trace.Span, r *http.Request) {
}
// CaptureServerRequest used to add span attributes from the request as a Server.
// TODO: need to update the semconv package as it does not implement fully Semantic Convention v1.23.0.
func (t *Tracer) CaptureServerRequest(span trace.Span, r *http.Request) {
if t == nil || span == nil || r == nil {
return
}
// Common attributes https://github.com/open-telemetry/semantic-conventions/blob/v1.23.0/docs/http/http-spans.md#common-attributes
// Common attributes https://github.com/open-telemetry/semantic-conventions/blob/v1.26.0/docs/http/http-spans.md#common-attributes
span.SetAttributes(semconv.HTTPRequestMethodKey.String(r.Method))
span.SetAttributes(semconv.NetworkProtocolVersion(proto(r.Proto)))
// Server attributes https://github.com/open-telemetry/semantic-conventions/blob/v1.23.0/docs/http/http-spans.md#http-server-semantic-conventions
sURL := t.safeURL(r.URL)
// Server attributes https://github.com/open-telemetry/semantic-conventions/blob/v1.26.0/docs/http/http-spans.md#http-server-semantic-conventions
span.SetAttributes(semconv.HTTPRequestBodySize(int(r.ContentLength)))
span.SetAttributes(semconv.URLPath(r.URL.Path))
span.SetAttributes(semconv.URLQuery(r.URL.RawQuery))
span.SetAttributes(semconv.URLPath(sURL.Path))
span.SetAttributes(semconv.URLQuery(sURL.RawQuery))
span.SetAttributes(semconv.URLScheme(r.Header.Get("X-Forwarded-Proto")))
span.SetAttributes(semconv.UserAgentOriginal(r.UserAgent()))
span.SetAttributes(semconv.ServerAddress(r.Host))
@ -222,17 +226,15 @@ func (t *Tracer) CaptureServerRequest(span trace.Span, r *http.Request) {
host, port, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
span.SetAttributes(semconv.ClientAddress(r.RemoteAddr))
span.SetAttributes(attribute.String("network.peer.address", r.RemoteAddr))
span.SetAttributes(semconv.NetworkPeerAddress(r.Host))
} else {
span.SetAttributes(attribute.String("network.peer.address", host))
span.SetAttributes(attribute.String("network.peer.port", port))
span.SetAttributes(semconv.NetworkPeerAddress(host))
span.SetAttributes(semconv.ClientAddress(host))
intPort, _ := strconv.Atoi(port)
span.SetAttributes(semconv.ClientPort(intPort))
span.SetAttributes(semconv.NetworkPeerPort(intPort))
}
span.SetAttributes(semconv.ClientSocketAddress(r.Header.Get("X-Forwarded-For")))
for _, header := range t.capturedRequestHeaders {
// User-agent is already part of the semantic convention as a recommended attribute.
if strings.EqualFold(header, "User-Agent") {
@ -273,6 +275,32 @@ func (t *Tracer) CaptureResponse(span trace.Span, responseHeaders http.Header, c
}
}
func (t *Tracer) safeURL(originalURL *url.URL) *url.URL {
if originalURL == nil {
return nil
}
redactedURL := *originalURL
// Redact password if exists.
if redactedURL.User != nil {
redactedURL.User = url.UserPassword("REDACTED", "REDACTED")
}
// Redact query parameters.
query := redactedURL.Query()
for k := range query {
if slices.Contains(t.safeQueryParams, k) {
continue
}
query.Set(k, "REDACTED")
}
redactedURL.RawQuery = query.Encode()
return &redactedURL
}
func proto(proto string) string {
switch proto {
case "HTTP/1.0":