-
Notifications
You must be signed in to change notification settings - Fork 8
/
tracing.go
165 lines (143 loc) · 5.57 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
// Package opengintracing provides requests tracing functional using opentracing specification.
//
// See https://github.com/opentracing/opentracing-go for more information
package opengintracing
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
"github.com/opentracing/opentracing-go"
)
const spanContextKey = "span"
// Errors which may occur at operation time.
var (
ErrSpanNotFound = errors.New("span was not found in context")
)
// NewSpan returns gin.HandlerFunc (middleware) that starts a new span and injects it to request context.
//
// It calls ctx.Next() to measure execution time of all following handlers.
func NewSpan(tracer opentracing.Tracer, operationName string, opts ...opentracing.StartSpanOption) gin.HandlerFunc {
return func(ctx *gin.Context) {
span := tracer.StartSpan(operationName, opts...)
ctx.Set(spanContextKey, span)
defer span.Finish()
ctx.Next()
}
}
// ParentSpanReferenceFunc determines how to reference parent span
//
// See opentracing.SpanReferenceType
type ParentSpanReferenceFunc func(opentracing.SpanContext) opentracing.StartSpanOption
// SpanFromHeaders returns gin.HandlerFunc (middleware)
// that extracts parent span data from HTTP headers in TextMap format and
// starts a new span referenced to parent with ParentSpanReferenceFunc.
//
// It calls ctx.Next() to measure execution time of all following handlers.
//
// Behaviour on errors determined by abortOnErrors option.
// If it set to true request handling will be aborted with error.
func SpanFromHeaders(tracer opentracing.Tracer, operationName string, psr ParentSpanReferenceFunc,
abortOnErrors bool, advancedOpts ...opentracing.StartSpanOption,
) gin.HandlerFunc {
return func(ctx *gin.Context) {
spanContext, err := tracer.Extract(opentracing.TextMap, opentracing.HTTPHeadersCarrier(ctx.Request.Header))
if err != nil {
if abortOnErrors {
_ = ctx.AbortWithError(http.StatusInternalServerError, err)
}
return
}
opts := append([]opentracing.StartSpanOption{psr(spanContext)}, advancedOpts...)
span := tracer.StartSpan(operationName, opts...)
ctx.Set(spanContextKey, span)
defer span.Finish()
ctx.Next()
}
}
// SpanFromHeadersHTTPFmt returns gin.HandlerFunc (middleware)
// that extracts parent span data from HTTP headers in HTTPHeaders format and
// starts a new span referenced to parent with ParentSpanReferenceFunc.
//
// It calls ctx.Next() to measure execution time of all following handlers.
//
// Behaviour on errors determined by abortOnErrors option.
// If it set to true request handling will be aborted with error.
func SpanFromHeadersHTTPFmt(tracer opentracing.Tracer, operationName string, psr ParentSpanReferenceFunc,
abortOnErrors bool, advancedOpts ...opentracing.StartSpanOption,
) gin.HandlerFunc {
return func(ctx *gin.Context) {
spanContext, err := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(ctx.Request.Header))
if err != nil {
if abortOnErrors {
_ = ctx.AbortWithError(http.StatusInternalServerError, err)
}
return
}
opts := append([]opentracing.StartSpanOption{psr(spanContext)}, advancedOpts...)
span := tracer.StartSpan(operationName, opts...)
ctx.Set(spanContextKey, span)
defer span.Finish()
ctx.Next()
}
}
// SpanFromContext returns gin.HandlerFunc (middleware) that extracts parent span from request context
// and starts a new span as child of parent span.
//
// It calls ctx.Next() to measure execution time of all following handlers.
//
// Behaviour on errors determined by abortOnErrors option.
// If it set to true request handling will be aborted with error.
func SpanFromContext(tracer opentracing.Tracer, operationName string, abortOnErrors bool,
advancedOpts ...opentracing.StartSpanOption,
) gin.HandlerFunc {
return func(ctx *gin.Context) {
var opts []opentracing.StartSpanOption
parentSpanI, _ := ctx.Get(spanContextKey)
if parentSpan, typeOk := parentSpanI.(opentracing.Span); parentSpan != nil && typeOk {
opts = append(opts, opentracing.ChildOf(parentSpan.Context()))
} else {
if abortOnErrors {
_ = ctx.AbortWithError(http.StatusInternalServerError, ErrSpanNotFound)
}
return
}
opts = append(opts, advancedOpts...)
span := tracer.StartSpan(operationName, opts...)
ctx.Set(spanContextKey, span)
defer span.Finish()
ctx.Next()
}
}
// InjectToHeaders injects span meta-information to request headers.
//
// It may be useful when you want to trace chained request (client->service 1->service 2).
// In this case you have to save request headers (ctx.Request.Header) and pass it to next level request.
//
// Behaviour on errors determined by abortOnErrors option.
// If it set to true request handling will be aborted with error.
func InjectToHeaders(tracer opentracing.Tracer, abortOnErrors bool) gin.HandlerFunc {
return func(ctx *gin.Context) {
var spanContext opentracing.SpanContext
spanI, _ := ctx.Get(spanContextKey)
if span, typeOk := spanI.(opentracing.Span); span != nil && typeOk {
spanContext = span.Context()
} else {
if abortOnErrors {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, ErrSpanNotFound)
}
return
}
_ = tracer.Inject(spanContext, opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(ctx.Request.Header))
}
}
// GetSpan extracts span from context.
func GetSpan(ctx *gin.Context) (span opentracing.Span, exists bool) {
spanI, _ := ctx.Get(spanContextKey)
span, ok := spanI.(opentracing.Span)
exists = span != nil && ok
return
}
// MustGetSpan extracts span from context. It panics if span was not set.
func MustGetSpan(ctx *gin.Context) opentracing.Span {
return ctx.MustGet(spanContextKey).(opentracing.Span)
}