Merge pull request #60 from krakend/sc-842/traces_skip_headers

Traces skip headers options
This commit is contained in:
Daniel Ortiz
2025-09-19 17:57:03 +02:00
committed by GitHub
8 changed files with 93 additions and 29 deletions
+3
View File
@@ -144,6 +144,7 @@ type GlobalOpts struct {
DisableTraces bool `json:"disable_traces"`
DisablePropagation bool `json:"disable_propagation"`
ReportHeaders bool `json:"report_headers"`
SkipHeaders []string `json:"skip_headers"`
MetricsStaticAttributes Attributes `json:"metrics_static_attributes"`
TracesStaticAttributes Attributes `json:"traces_static_attributes"`
SemConv string `json:"semantic_convention"`
@@ -155,6 +156,7 @@ type PipeOpts struct {
DisableMetrics bool `json:"disable_metrics"`
DisableTraces bool `json:"disable_traces"`
ReportHeaders bool `json:"report_headers"`
SkipHeaders []string `json:"skip_headers"`
MetricsStaticAttributes Attributes `json:"metrics_static_attributes"`
TracesStaticAttributes Attributes `json:"traces_static_attributes"`
}
@@ -255,6 +257,7 @@ type BackendTraceOpts struct {
DetailedConnection bool `json:"detailed_connection"`
StaticAttributes Attributes `json:"static_attributes"`
ReportHeaders bool `json:"report_headers"`
SkipHeaders []string `json:"skip_headers"`
}
// Enabled tells if there are any traces to be reported.
+15 -4
View File
@@ -21,6 +21,7 @@ type TransportTracesOptions struct {
DetailedConnection bool // add extra detail about the connection to the server: dns lookup, tls...
FixedAttributes []attribute.KeyValue // "static" attributes set at config time.
ReportHeaders bool
SkipHeaders []string
}
// Enabled returns if the transport should create a trace.
@@ -34,19 +35,27 @@ type transportTraces struct {
fixedAttrs []attribute.KeyValue
detailedConnection bool
reportHeaders bool
skipHeaders map[string]bool
}
func newTransportTraces(tracesOpts *TransportTracesOptions, tracer trace.Tracer, spanName string) *transportTraces {
if tracer == nil {
return nil
}
var sh map[string]bool
if len(tracesOpts.SkipHeaders) > 0 {
sh = make(map[string]bool, len(tracesOpts.SkipHeaders))
for _, v := range tracesOpts.SkipHeaders {
sh[v] = true
}
}
return &transportTraces{
tracer: tracer,
spanName: spanName,
fixedAttrs: tracesOpts.FixedAttributes,
detailedConnection: tracesOpts.DetailedConnection,
reportHeaders: tracesOpts.ReportHeaders,
skipHeaders: sh,
}
}
@@ -75,7 +84,7 @@ func (t *transportTraces) start(rtt *roundTripTracking,
header := make(http.Header, len(rtt.req.Header)+1)
for k, v := range rtt.req.Header {
header[k] = v
if t.reportHeaders {
if t.reportHeaders && (t.skipHeaders == nil || !t.skipHeaders[k]) {
reqAttrs = append(reqAttrs,
attribute.StringSlice("http.request.header."+strings.ToLower(k), v))
}
@@ -99,8 +108,10 @@ func (t *transportTraces) end(rtt *roundTripTracking) {
respAttrs := otelhttp.TraceResponseAttrs(rtt.resp)
if t.reportHeaders {
for k, v := range rtt.resp.Header {
respAttrs = append(respAttrs,
attribute.StringSlice("http.response.header."+strings.ToLower(k), v))
if t.skipHeaders == nil || !t.skipHeaders[k] {
respAttrs = append(respAttrs,
attribute.StringSlice("http.response.header."+strings.ToLower(k), v))
}
}
}
rtt.span.SetAttributes(respAttrs...)
+6 -2
View File
@@ -10,6 +10,7 @@ import (
type TrackingResponseWriter struct {
track *tracking
recordHeaders bool
skipHeaders map[string]bool
rw http.ResponseWriter
flusher http.Flusher
hijacker http.Hijacker
@@ -27,7 +28,9 @@ func (w *TrackingResponseWriter) gatherHeaders() {
h := w.rw.Header()
w.track.responseHeaders = make(map[string][]string, len(h))
for k, v := range h {
w.track.responseHeaders[k] = v
if w.skipHeaders == nil || !w.skipHeaders[k] {
w.track.responseHeaders[k] = v
}
}
}
@@ -80,13 +83,14 @@ func (w *TrackingResponseWriter) Flush() {
}
func newTrackingResponseWriter(rw http.ResponseWriter, t *tracking, recordHeaders bool,
hijackCallback func(net.Conn, error) (net.Conn, error),
skipHeaders map[string]bool, hijackCallback func(net.Conn, error) (net.Conn, error),
) *TrackingResponseWriter {
flusher, _ := rw.(http.Flusher)
hijacker, _ := rw.(http.Hijacker)
return &TrackingResponseWriter{
track: t,
recordHeaders: recordHeaders,
skipHeaders: skipHeaders,
rw: rw,
flusher: flusher,
hijacker: hijacker,
+13 -3
View File
@@ -19,6 +19,7 @@ type trackingHandler struct {
metrics *metricsHTTP
traces *tracesHTTP
reportHeaders bool
skipHeaders map[string]bool
config state.Config
}
@@ -40,7 +41,7 @@ func (h *trackingHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
r = r.WithContext(t.ctx)
if h.metrics != nil || h.traces != nil {
rw = newTrackingResponseWriter(rw, t, h.reportHeaders, func(c net.Conn, _ error) (net.Conn, error) {
rw = newTrackingResponseWriter(rw, t, h.reportHeaders, h.skipHeaders, func(c net.Conn, _ error) (net.Conn, error) {
t.Finish()
h.traces.end(t)
h.metrics.report(t, r)
@@ -60,7 +61,7 @@ func NewTrackingHandler(next http.Handler) http.Handler {
return NewTrackingHandlerWithTrustedProxies(next, nil)
}
func NewTrackingHandlerWithTrustedProxies(next http.Handler, trustedProxies []string) http.Handler {
func NewTrackingHandlerWithTrustedProxies(next http.Handler, trustedProxies []string) http.Handler { // skipcq: GO-R1005
otelCfg := state.GlobalConfig()
if otelCfg == nil {
return next
@@ -88,6 +89,14 @@ func NewTrackingHandlerWithTrustedProxies(next http.Handler, trustedProxies []st
m = newMetricsHTTP(s.Meter(), metricsAttrs, gCfg.SemConv)
}
var sh map[string]bool
if len(gCfg.SkipHeaders) > 0 {
sh = make(map[string]bool, len(gCfg.SkipHeaders))
for _, v := range gCfg.SkipHeaders {
sh[v] = true
}
}
var t *tracesHTTP
if !gCfg.DisableTraces {
tracesAttrs := []attribute.KeyValue{attribute.String("krakend.stage", "global")}
@@ -97,7 +106,7 @@ func NewTrackingHandlerWithTrustedProxies(next http.Handler, trustedProxies []st
}
}
t = newTracesHTTP(s.Tracer(), tracesAttrs, gCfg.ReportHeaders, trustedProxies)
t = newTracesHTTP(s.Tracer(), tracesAttrs, gCfg.ReportHeaders, sh, trustedProxies)
}
return &trackingHandler{
@@ -106,6 +115,7 @@ func NewTrackingHandlerWithTrustedProxies(next http.Handler, trustedProxies []st
metrics: m,
traces: t,
reportHeaders: gCfg.ReportHeaders,
skipHeaders: sh,
config: otelCfg,
}
}
+25 -7
View File
@@ -16,11 +16,12 @@ type tracesHTTP struct {
tracer trace.Tracer
fixedAttrs []attribute.KeyValue
reportHeaders bool
skipHeaders map[string]bool
trustedProxies map[string]bool
}
func newTracesHTTP(tracer trace.Tracer, attrs []attribute.KeyValue,
reportHeaders bool, trustedProxies []string,
reportHeaders bool, skipHeaders map[string]bool, trustedProxies []string,
) *tracesHTTP {
var fa []attribute.KeyValue
if len(attrs) > 0 {
@@ -36,6 +37,7 @@ func newTracesHTTP(tracer trace.Tracer, attrs []attribute.KeyValue,
tracer: tracer,
fixedAttrs: fa,
reportHeaders: reportHeaders,
skipHeaders: skipHeaders,
trustedProxies: tpm,
}
}
@@ -58,9 +60,17 @@ func (t *tracesHTTP) start(r *http.Request, tr *tracking) *http.Request {
tr.span.SetAttributes(t.fixedAttrs...)
}
if t.reportHeaders {
// report all incoming headers
for hk, hv := range r.Header {
tr.span.SetAttributes(attribute.StringSlice("http.request.header."+strings.ToLower(hk), hv))
if len(t.skipHeaders) == 0 {
// report all incoming headers
for hk, hv := range r.Header {
tr.span.SetAttributes(attribute.StringSlice("http.request.header."+strings.ToLower(hk), hv))
}
} else {
for hk, hv := range r.Header {
if !t.skipHeaders[hk] {
tr.span.SetAttributes(attribute.StringSlice("http.request.header."+strings.ToLower(hk), hv))
}
}
}
}
return r
@@ -86,9 +96,17 @@ func (t *tracesHTTP) end(tr *tracking) {
tr.span.SetAttributes(tr.tracesStaticAttrs...)
if tr.responseHeaders != nil {
// report all incoming headers
for hk, hv := range tr.responseHeaders {
tr.span.SetAttributes(attribute.StringSlice("http.response.header."+strings.ToLower(hk), hv))
if len(t.skipHeaders) == 0 {
// report all incoming headers
for hk, hv := range tr.responseHeaders {
tr.span.SetAttributes(attribute.StringSlice("http.response.header."+strings.ToLower(hk), hv))
}
} else {
for hk, hv := range tr.responseHeaders {
if !t.skipHeaders[hk] {
tr.span.SetAttributes(attribute.StringSlice("http.response.header."+strings.ToLower(hk), hv))
}
}
}
}
if len(tr.writeErrs) > 0 {
+3 -2
View File
@@ -82,7 +82,7 @@ func InstrumentedHTTPClientFactory(clientFactory transport.HTTPClientFactory,
metricAttrs := attrs
for _, kv := range opts.Metrics.StaticAttributes {
if len(kv.Key) > 0 && len(kv.Value) > 0 {
if kv.Key != "" && kv.Value != "" {
metricAttrs = append(metricAttrs, attribute.String(kv.Key, kv.Value))
}
}
@@ -93,7 +93,7 @@ func InstrumentedHTTPClientFactory(clientFactory transport.HTTPClientFactory,
traceAttrs = append(traceAttrs,
attribute.String("krakend.stage", "backend-request"))
for _, kv := range opts.Traces.StaticAttributes {
if len(kv.Key) > 0 && len(kv.Value) > 0 {
if kv.Key != "" && kv.Value != "" {
traceAttrs = append(traceAttrs, attribute.String(kv.Key, kv.Value))
}
}
@@ -112,6 +112,7 @@ func InstrumentedHTTPClientFactory(clientFactory transport.HTTPClientFactory,
DetailedConnection: opts.Traces.DetailedConnection,
FixedAttributes: traceAttrs,
ReportHeaders: opts.Traces.ReportHeaders,
SkipHeaders: opts.Traces.SkipHeaders,
},
OTELInstance: otelState,
}
+10 -8
View File
@@ -49,7 +49,7 @@ func metricsAndTracesMiddleware(next proxy.Proxy, mm *middlewareMeter, mt *middl
// and report the duration of this stage in metrics if enabled.
func middleware(gs state.OTEL, metricsEnabled bool, tracesEnabled bool,
stageName string, urlPattern string, metricsAttrs, tracesAttrs []attribute.KeyValue,
reportHeaders bool,
reportHeaders bool, skipHeaders []string,
) proxy.Middleware {
var mt *middlewareTracer
var mm *middlewareMeter
@@ -62,7 +62,7 @@ func middleware(gs state.OTEL, metricsEnabled bool, tracesEnabled bool,
}
}
if tracesEnabled {
mt = newMiddlewareTracer(gs, urlPattern, stageName, reportHeaders, tracesAttrs)
mt = newMiddlewareTracer(gs, urlPattern, stageName, reportHeaders, skipHeaders, tracesAttrs)
if mt == nil {
// TODO: log the error
tracesEnabled = false
@@ -124,19 +124,20 @@ func ProxyFactory(pf proxy.Factory) proxy.FactoryFunc {
metricsAttrs := attrs
tracesAttrs := attrs
for _, kv := range pipeOpts.MetricsStaticAttributes {
if len(kv.Key) > 0 && len(kv.Value) > 0 {
if kv.Key != "" && kv.Value != "" {
metricsAttrs = append(metricsAttrs, attribute.String(kv.Key, kv.Value))
}
}
for _, kv := range pipeOpts.TracesStaticAttributes {
if len(kv.Key) > 0 && len(kv.Value) > 0 {
if kv.Key != "" && kv.Value != "" {
tracesAttrs = append(tracesAttrs, attribute.String(kv.Key, kv.Value))
}
}
return middleware(gs, !pipeOpts.DisableMetrics, !pipeOpts.DisableTraces,
"proxy", urlPattern, metricsAttrs, tracesAttrs, pipeOpts.ReportHeaders)(next), nil
"proxy", urlPattern, metricsAttrs, tracesAttrs, pipeOpts.ReportHeaders,
pipeOpts.SkipHeaders)(next), nil
}
}
@@ -175,7 +176,7 @@ func BackendFactory(bf proxy.BackendFactory) proxy.BackendFactory {
tracesAttrs := attrs
if backendOpts.Metrics != nil {
for _, kv := range backendOpts.Metrics.StaticAttributes {
if len(kv.Key) > 0 && len(kv.Value) > 0 {
if kv.Key != "" && kv.Value != "" {
metricsAttrs = append(metricsAttrs, attribute.String(kv.Key, kv.Value))
}
}
@@ -185,13 +186,14 @@ func BackendFactory(bf proxy.BackendFactory) proxy.BackendFactory {
if backendOpts.Traces != nil {
reportHeaders = backendOpts.Traces.ReportHeaders
for _, kv := range backendOpts.Traces.StaticAttributes {
if len(kv.Key) > 0 && len(kv.Value) > 0 {
if kv.Key != "" && kv.Value != "" {
tracesAttrs = append(tracesAttrs, attribute.String(kv.Key, kv.Value))
}
}
}
return middleware(gs, !metricsDisabled, !tracesDisabled,
"backend", urlPattern, metricsAttrs, tracesAttrs, reportHeaders)(next)
"backend", urlPattern, metricsAttrs, tracesAttrs, reportHeaders,
backendOpts.Traces.SkipHeaders)(next)
}
}
+18 -3
View File
@@ -18,10 +18,13 @@ type middlewareTracer struct {
name string
tracer trace.Tracer
reportHeaders bool
skipHeaders map[string]bool
attrs []attribute.KeyValue
}
func newMiddlewareTracer(s state.OTEL, name string, stageName string, reportHeaders bool, attrs []attribute.KeyValue) *middlewareTracer {
func newMiddlewareTracer(s state.OTEL, name string, stageName string, reportHeaders bool,
skipHeaders []string, attrs []attribute.KeyValue,
) *middlewareTracer {
tracer := s.Tracer()
if tracer == nil {
return nil
@@ -29,10 +32,18 @@ func newMiddlewareTracer(s state.OTEL, name string, stageName string, reportHead
tAttrs := make([]attribute.KeyValue, 0, len(attrs)+1)
tAttrs = append(tAttrs, attrs...)
tAttrs = append(tAttrs, attribute.String("krakend.stage", stageName))
var sh map[string]bool
if len(skipHeaders) > 0 {
sh = make(map[string]bool, len(skipHeaders))
for _, k := range skipHeaders {
sh[k] = true
}
}
return &middlewareTracer{
name: name,
tracer: tracer,
reportHeaders: reportHeaders,
skipHeaders: sh,
attrs: tAttrs,
}
}
@@ -42,7 +53,9 @@ func (t *middlewareTracer) start(ctx context.Context, req *proxy.Request) (conte
span.SetAttributes(t.attrs...)
if t.reportHeaders {
for hk, hv := range req.Headers {
span.SetAttributes(attribute.StringSlice("http.request.header."+strings.ToLower(hk), hv))
if t.skipHeaders == nil || !t.skipHeaders[hk] {
span.SetAttributes(attribute.StringSlice("http.request.header."+strings.ToLower(hk), hv))
}
}
}
return ctx, span
@@ -60,7 +73,9 @@ func (t *middlewareTracer) end(span trace.Span, resp *proxy.Response, err error)
span.SetAttributes(semconv.HTTPResponseStatusCodeKey.Int(resp.Metadata.StatusCode))
if t.reportHeaders {
for hk, hv := range resp.Metadata.Headers {
span.SetAttributes(attribute.StringSlice("http.response.header."+strings.ToLower(hk), hv))
if t.skipHeaders == nil || !t.skipHeaders[hk] {
span.SetAttributes(attribute.StringSlice("http.response.header."+strings.ToLower(hk), hv))
}
}
}
}