fork of https://github.com/sourcegraph/zoekt
1// Package trace provides a tracing API that in turn invokes both the `golang.org/x/net/trace` API
2// and creates an opentracing span if appropriate.
3//
4// This is similar to the github.com/sourcegraph/sourcegraph/internal/trace package in the main repo,
5// and it may make sense to factor both out into a common package at some point.
6package trace
7
8import (
9 "context"
10 "fmt"
11 "strconv"
12 "strings"
13
14 "github.com/opentracing/opentracing-go"
15 "github.com/opentracing/opentracing-go/ext"
16 "github.com/opentracing/opentracing-go/log"
17 nettrace "golang.org/x/net/trace"
18)
19
20// A Tracer for trace creation, parameterised over an
21// opentracing.Tracer. Use this if you don't want to use
22// the global tracer.
23type Tracer struct {
24 Tracer opentracing.Tracer
25}
26
27func New(ctx context.Context, family, title string) (*Trace, context.Context) {
28 tr := Tracer{Tracer: GetOpenTracer(ctx, nil)}
29 return tr.New(ctx, family, title)
30}
31
32// New returns a new Trace with the specified family and title.
33func (t Tracer) New(ctx context.Context, family, title string) (*Trace, context.Context) {
34 span, ctx := StartSpanFromContextWithTracer(
35 ctx,
36 t.Tracer,
37 family,
38 opentracing.Tag{Key: "title", Value: title},
39 )
40 tr := nettrace.New(family, title)
41 trace := &Trace{span: span, trace: tr, family: family}
42 if parent := TraceFromContext(ctx); parent != nil {
43 tr.LazyPrintf("parent: %s", parent.family)
44 trace.family = parent.family + " > " + family
45 }
46 return trace, ContextWithTrace(ctx, trace)
47}
48
49// Trace is a combined version of golang.org/x/net/trace.Trace and
50// opentracing.Span. Use New to construct one.
51type Trace struct {
52 trace nettrace.Trace
53 span opentracing.Span
54 family string
55}
56
57// LazyPrintf evaluates its arguments with fmt.Sprintf each time the
58// /debug/requests page is rendered. Any memory referenced by a will be
59// pinned until the trace is finished and later discarded.
60func (t *Trace) LazyPrintf(format string, a ...interface{}) {
61 t.span.LogFields(Printf("log", format, a...))
62 t.trace.LazyPrintf(format, a...)
63}
64
65func (t *Trace) LazyLog(x fmt.Stringer, sensitive bool) {
66 t.trace.LazyLog(x, sensitive)
67}
68
69// LogFields logs fields to the opentracing.Span
70// as well as the nettrace.Trace.
71func (t *Trace) LogFields(fields ...log.Field) {
72 t.span.LogFields(fields...)
73 t.trace.LazyLog(fieldsStringer(fields), false)
74}
75
76// SetError declares that this trace and span resulted in an error.
77func (t *Trace) SetError(err error) {
78 if err == nil {
79 return
80 }
81 t.trace.LazyPrintf("error: %v", err)
82 t.trace.SetError()
83 t.span.LogFields(log.Error(err))
84 ext.Error.Set(t.span, true)
85}
86
87// Finish declares that this trace and span is complete.
88// The trace should not be used after calling this method.
89func (t *Trace) Finish() {
90 t.trace.Finish()
91 t.span.Finish()
92}
93
94// Printf is an opentracing log.Field which is a LazyLogger. So the format
95// string will only be evaluated if the trace is collected. In the case of
96// net/trace, it will only be evaluated on page load.
97func Printf(key, f string, args ...interface{}) log.Field {
98 return log.Lazy(func(fv log.Encoder) {
99 fv.EmitString(key, fmt.Sprintf(f, args...))
100 })
101}
102
103type traceContextKey string
104
105const traceKey = traceContextKey("trace")
106
107// ContextWithTrace returns a new context.Context that holds a reference to
108// trace's SpanContext.
109func ContextWithTrace(ctx context.Context, tr *Trace) context.Context {
110 ctx = opentracing.ContextWithSpan(ctx, tr.span)
111 ctx = context.WithValue(ctx, traceKey, tr)
112 return ctx
113}
114
115// TraceFromContext returns the Trace previously associated with ctx, or
116// nil if no such Trace could be found.
117func TraceFromContext(ctx context.Context) *Trace {
118 tr, _ := ctx.Value(traceKey).(*Trace)
119 return tr
120}
121
122// fieldsStringer lazily marshals a slice of log.Field into a string for
123// printing in net/trace.
124type fieldsStringer []log.Field
125
126func (fs fieldsStringer) String() string {
127 var e encoder
128 for _, f := range fs {
129 f.Marshal(&e)
130 }
131 return e.Builder.String()
132}
133
134// encoder is a log.Encoder used by fieldsStringer.
135type encoder struct {
136 strings.Builder
137 prefixNewline bool
138}
139
140func (e *encoder) EmitString(key, value string) {
141 if e.prefixNewline {
142 // most times encoder is used is for one field
143 e.Builder.WriteString("\n")
144 }
145 if !e.prefixNewline {
146 e.prefixNewline = true
147 }
148
149 e.Builder.Grow(len(key) + 1 + len(value))
150 e.Builder.WriteString(key)
151 e.Builder.WriteString(":")
152 e.Builder.WriteString(value)
153}
154
155func (e *encoder) EmitBool(key string, value bool) {
156 e.EmitString(key, strconv.FormatBool(value))
157}
158
159func (e *encoder) EmitInt(key string, value int) {
160 e.EmitString(key, strconv.Itoa(value))
161}
162
163func (e *encoder) EmitInt32(key string, value int32) {
164 e.EmitString(key, strconv.FormatInt(int64(value), 10))
165}
166
167func (e *encoder) EmitInt64(key string, value int64) {
168 e.EmitString(key, strconv.FormatInt(value, 10))
169}
170
171func (e *encoder) EmitUint32(key string, value uint32) {
172 e.EmitString(key, strconv.FormatUint(uint64(value), 10))
173}
174
175func (e *encoder) EmitUint64(key string, value uint64) {
176 e.EmitString(key, strconv.FormatUint(value, 10))
177}
178
179func (e *encoder) EmitFloat32(key string, value float32) {
180 e.EmitString(key, strconv.FormatFloat(float64(value), 'E', -1, 64))
181}
182
183func (e *encoder) EmitFloat64(key string, value float64) {
184 e.EmitString(key, strconv.FormatFloat(value, 'E', -1, 64))
185}
186
187func (e *encoder) EmitObject(key string, value interface{}) {
188 e.EmitString(key, fmt.Sprintf("%+v", value))
189}
190
191func (e *encoder) EmitLazyLogger(value log.LazyLogger) {
192 value(e)
193}