forked from best-expendables/httpclient
-
Notifications
You must be signed in to change notification settings - Fork 0
/
spanner.go
122 lines (97 loc) · 3.54 KB
/
spanner.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
package opentrace
import (
"net/http"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
)
type (
// Spanner injects tags, logs and manages the lifecycle of a span
//
// Tagging and logging specification:
// https://github.com/opentracing/specification/blob/master/semantic_conventions.md
Spanner interface {
// OnRequest called on an HTTP request
OnRequest(tracer opentracing.Tracer, request *http.Request) opentracing.Span
// OnResponse called on an HTTP response
OnResponse(span opentracing.Span, response *http.Response, clientError error)
}
// StandardSpanner is used by default
// It only works with the existing span and does not create a new one
StandardSpanner struct{}
// CreatorSpanner - extended standard spanner with one difference - it creates a new span on each request
CreatorSpanner struct {
// CreateRootSpanOnMissingParent creates a "root" span if the parent span is missing
CreateRootSpanOnMissingParent bool
// OperationNameFn function creates an operation name for the new span
OperationNameFn func(r *http.Request) string
StandardSpanner
}
)
// OnRequest adds tags to an existing span: span.kind, http.url, http.method
func (StandardSpanner) OnRequest(tracer opentracing.Tracer, request *http.Request) opentracing.Span {
var span opentracing.Span
if span = opentracing.SpanFromContext(request.Context()); span == nil {
return nil
}
ext.SpanKindRPCClient.Set(span)
ext.HTTPUrl.Set(span, request.URL.String())
ext.HTTPMethod.Set(span, request.Method)
return span
}
// OnResponse adds a "HTTPStatusCode" or "Error" tag to the existing span
func (StandardSpanner) OnResponse(span opentracing.Span, response *http.Response, clientError error) {
if clientError != nil {
ext.Error.Set(span, true)
}
if response != nil {
ext.HTTPStatusCode.Set(span, uint16(response.StatusCode))
}
}
// OnRequest creates a new span and passes it to StandardSpanner
func (p *CreatorSpanner) OnRequest(tracer opentracing.Tracer, request *http.Request) opentracing.Span {
if SkipSpanCreatingFromContext(request.Context()) {
return p.StandardSpanner.OnRequest(tracer, request)
}
var (
span opentracing.Span
operationName string
)
if p.OperationNameFn == nil {
operationName = OperationNameFromRequest(request)
} else {
operationName = p.OperationNameFn(request)
}
if parentSpan := opentracing.SpanFromContext(request.Context()); parentSpan != nil {
span = tracer.StartSpan(
operationName, opentracing.ChildOf(parentSpan.Context()),
)
} else if p.CreateRootSpanOnMissingParent {
span = tracer.StartSpan(operationName)
} else {
return nil
}
ctx := opentracing.ContextWithSpan(request.Context(), span)
return p.StandardSpanner.OnRequest(tracer, request.WithContext(ctx))
}
// OnResponse passes a span to StandardSpanner
func (p *CreatorSpanner) OnResponse(span opentracing.Span, response *http.Response, clientError error) {
p.StandardSpanner.OnResponse(span, response, clientError)
if !SkipSpanCreatingFromContext(response.Request.Context()) {
span.Finish()
}
}
// WithCreateRootSpanOnMissingParent creates a "root" span if the parent span is missing
func (p *CreatorSpanner) WithCreateRootSpanOnMissingParent(flag bool) *CreatorSpanner {
p.CreateRootSpanOnMissingParent = flag
return p
}
// OperationNameFromRequest creates an operation name from the request
//
// E.g.:
// - out: [POST] /v1/users
// - out: [GET] /v1/users
//
// For RESTful, you will need to use your own implementation
func OperationNameFromRequest(request *http.Request) string {
return request.URL.Path
}