-
Notifications
You must be signed in to change notification settings - Fork 0
/
tracing.go
197 lines (169 loc) · 5.92 KB
/
tracing.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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
package tracing
import (
"crypto/sha256"
"fmt"
"io"
"net/http"
"github.com/containous/traefik/log"
"github.com/containous/traefik/middlewares/tracing/datadog"
"github.com/containous/traefik/middlewares/tracing/jaeger"
"github.com/containous/traefik/middlewares/tracing/zipkin"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
)
// ForwardMaxLengthNumber defines the number of static characters in the Forwarding Span Trace name : 8 chars for 'forward ' + 8 chars for hash + 2 chars for '_'.
const ForwardMaxLengthNumber = 18
// EntryPointMaxLengthNumber defines the number of static characters in the Entrypoint Span Trace name : 11 chars for 'Entrypoint ' + 8 chars for hash + 2 chars for '_'.
const EntryPointMaxLengthNumber = 21
// TraceNameHashLength defines the number of characters to use from the head of the generated hash.
const TraceNameHashLength = 8
// Tracing middleware
type Tracing struct {
Backend string `description:"Selects the tracking backend ('jaeger','zipkin', 'datadog')." export:"true"`
ServiceName string `description:"Set the name for this service" export:"true"`
SpanNameLimit int `description:"Set the maximum character limit for Span names (default 0 = no limit)" export:"true"`
Jaeger *jaeger.Config `description:"Settings for jaeger"`
Zipkin *zipkin.Config `description:"Settings for zipkin"`
DataDog *datadog.Config `description:"Settings for DataDog"`
tracer opentracing.Tracer
closer io.Closer
}
// StartSpan delegates to opentracing.Tracer
func (t *Tracing) StartSpan(operationName string, opts ...opentracing.StartSpanOption) opentracing.Span {
return t.tracer.StartSpan(operationName, opts...)
}
// Inject delegates to opentracing.Tracer
func (t *Tracing) Inject(sm opentracing.SpanContext, format interface{}, carrier interface{}) error {
return t.tracer.Inject(sm, format, carrier)
}
// Extract delegates to opentracing.Tracer
func (t *Tracing) Extract(format interface{}, carrier interface{}) (opentracing.SpanContext, error) {
return t.tracer.Extract(format, carrier)
}
// Backend describes things we can use to setup tracing
type Backend interface {
Setup(serviceName string) (opentracing.Tracer, io.Closer, error)
}
// Setup Tracing middleware
func (t *Tracing) Setup() {
var err error
switch t.Backend {
case jaeger.Name:
t.tracer, t.closer, err = t.Jaeger.Setup(t.ServiceName)
case zipkin.Name:
t.tracer, t.closer, err = t.Zipkin.Setup(t.ServiceName)
case datadog.Name:
t.tracer, t.closer, err = t.DataDog.Setup(t.ServiceName)
default:
log.Warnf("Unknown tracer %q", t.Backend)
return
}
if err != nil {
log.Warnf("Could not initialize %s tracing: %v", t.Backend, err)
}
}
// IsEnabled determines if tracing was successfully activated
func (t *Tracing) IsEnabled() bool {
if t == nil || t.tracer == nil {
return false
}
return true
}
// Close tracer
func (t *Tracing) Close() {
if t.closer != nil {
err := t.closer.Close()
if err != nil {
log.Warn(err)
}
}
}
// LogRequest used to create span tags from the request
func LogRequest(span opentracing.Span, r *http.Request) {
if span != nil && r != nil {
ext.HTTPMethod.Set(span, r.Method)
ext.HTTPUrl.Set(span, r.URL.String())
span.SetTag("http.host", r.Host)
}
}
// LogResponseCode used to log response code in span
func LogResponseCode(span opentracing.Span, code int) {
if span != nil {
ext.HTTPStatusCode.Set(span, uint16(code))
if code >= 400 {
ext.Error.Set(span, true)
}
}
}
// GetSpan used to retrieve span from request context
func GetSpan(r *http.Request) opentracing.Span {
return opentracing.SpanFromContext(r.Context())
}
// InjectRequestHeaders used to inject OpenTracing headers into the request
func InjectRequestHeaders(r *http.Request) {
if span := GetSpan(r); span != nil {
err := opentracing.GlobalTracer().Inject(
span.Context(),
opentracing.HTTPHeaders,
HTTPHeadersCarrier(r.Header))
if err != nil {
log.Error(err)
}
}
}
// LogEventf logs an event to the span in the request context.
func LogEventf(r *http.Request, format string, args ...interface{}) {
if span := GetSpan(r); span != nil {
span.LogKV("event", fmt.Sprintf(format, args...))
}
}
// StartSpan starts a new span from the one in the request context
func StartSpan(r *http.Request, operationName string, spanKinClient bool, opts ...opentracing.StartSpanOption) (opentracing.Span, *http.Request, func()) {
span, ctx := opentracing.StartSpanFromContext(r.Context(), operationName, opts...)
if spanKinClient {
ext.SpanKindRPCClient.Set(span)
}
r = r.WithContext(ctx)
return span, r, func() {
span.Finish()
}
}
// SetError flags the span associated with this request as in error
func SetError(r *http.Request) {
if span := GetSpan(r); span != nil {
ext.Error.Set(span, true)
}
}
// SetErrorAndDebugLog flags the span associated with this request as in error and create a debug log.
func SetErrorAndDebugLog(r *http.Request, format string, args ...interface{}) {
SetError(r)
log.Debugf(format, args...)
LogEventf(r, format, args...)
}
// SetErrorAndWarnLog flags the span associated with this request as in error and create a debug log.
func SetErrorAndWarnLog(r *http.Request, format string, args ...interface{}) {
SetError(r)
log.Warnf(format, args...)
LogEventf(r, format, args...)
}
// truncateString reduces the length of the 'str' argument to 'num' - 3 and adds a '...' suffix to the tail.
func truncateString(str string, num int) string {
text := str
if len(str) > num {
if num > 3 {
num -= 3
}
text = str[0:num] + "..."
}
return text
}
// computeHash returns the first TraceNameHashLength character of the sha256 hash for 'name' argument.
func computeHash(name string) string {
data := []byte(name)
hash := sha256.New()
if _, err := hash.Write(data); err != nil {
// Impossible case
log.Errorf("Fail to create Span name hash for %s: %v", name, err)
}
return fmt.Sprintf("%x", hash.Sum(nil))[:TraceNameHashLength]
}