-
Notifications
You must be signed in to change notification settings - Fork 8
/
transport_traces.go
116 lines (102 loc) · 3.64 KB
/
transport_traces.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package client
import (
"net/http"
"strings"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/propagation"
"go.opentelemetry.io/otel/trace"
otelhttp "github.com/krakend/krakend-otel/http"
)
// TransportTracesOptions defines what information
// is enabled, and extra fixed attributes to add to
// the trace.
type TransportTracesOptions struct {
RoundTrip bool // use a span for the round trip
ReadPayload bool // use a span for the process of reading the full body
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
}
// Enabled returns if the transport should create a trace.
func (o *TransportTracesOptions) Enabled() bool {
return o.RoundTrip || o.ReadPayload
}
type transportTraces struct {
tracer trace.Tracer
spanName string
fixedAttrs []attribute.KeyValue
detailedConnection bool
reportHeaders bool
}
func newTransportTraces(tracesOpts *TransportTracesOptions, tracer trace.Tracer, spanName string) *transportTraces {
return &transportTraces{
tracer: tracer,
spanName: spanName,
fixedAttrs: tracesOpts.FixedAttributes,
detailedConnection: tracesOpts.DetailedConnection,
reportHeaders: tracesOpts.ReportHeaders,
}
}
func (t *transportTraces) start(rtt *roundTripTracking,
propagator propagation.TextMapPropagator,
) {
if t == nil || rtt.req == nil {
return
}
ctx, span := t.tracer.Start(rtt.req.Context(), t.spanName, trace.WithSpanKind(trace.SpanKindClient))
if span == nil || !span.IsRecording() {
// we might not be recording because of sampling
return
}
rtt.span = span
rtt.req = rtt.req.WithContext(ctx)
reqAttrs := otelhttp.TraceRequestAttrs(rtt.req)
// propagate the context, see `example/passthrough/main.go` in OTEL repo
// SpanContextToRequest will modify its Request argument, which is
// contrary to the contract for http.RoundTripper, so we need to
// pass it a copy of the Request.
// However, the Request struct itself was already copied by
// the WithContext calls above and so we just need to copy the header.
header := make(http.Header, len(rtt.req.Header)+1)
for k, v := range rtt.req.Header {
header[k] = v
if t.reportHeaders {
reqAttrs = append(reqAttrs,
attribute.StringSlice("http.request.header."+strings.ToLower(k), v))
}
}
rtt.req.Header = header
propagator.Inject(rtt.req.Context(), propagation.HeaderCarrier(rtt.req.Header))
rtt.span.SetAttributes(t.fixedAttrs...)
rtt.span.SetAttributes(reqAttrs...)
}
func (t *transportTraces) end(rtt *roundTripTracking) {
if t == nil || rtt.span == nil || !rtt.span.IsRecording() {
return
}
if rtt.err != nil {
rtt.span.RecordError(rtt.err)
rtt.span.SetStatus(codes.Error, rtt.err.Error())
} else {
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))
}
}
rtt.span.SetAttributes(respAttrs...)
rtt.span.SetAttributes(attribute.Float64("response-duration", rtt.latencyInSecs))
if t.detailedConnection {
rtt.span.SetAttributes(
attribute.Float64("get-conn-duration", rtt.getConnLatency),
attribute.Float64("dns-duration", rtt.dnsLatency),
attribute.Float64("tls-duration", rtt.tlsLatency),
)
rtt.span.AddEvent("first-byte-time", trace.WithTimestamp(rtt.firstByteTime))
}
rtt.span.SetStatus(codes.Ok, "")
}
rtt.span.End()
}