1 package zipkin
2
3 import (
4 "context"
5 "net/http"
6 "strconv"
7
8 zipkin "github.com/openzipkin/zipkin-go"
9 "github.com/openzipkin/zipkin-go/model"
10 "github.com/openzipkin/zipkin-go/propagation/b3"
11
12 kithttp "github.com/go-kit/kit/transport/http"
13 "github.com/go-kit/log"
14 )
15
16
17
18
19
20
21
22
23
24
25
26
27
28 func HTTPClientTrace(tracer *zipkin.Tracer, options ...TracerOption) kithttp.ClientOption {
29 config := tracerOptions{
30 tags: make(map[string]string),
31 name: "",
32 logger: log.NewNopLogger(),
33 propagate: true,
34 }
35
36 for _, option := range options {
37 option(&config)
38 }
39
40 clientBefore := kithttp.ClientBefore(
41 func(ctx context.Context, req *http.Request) context.Context {
42 var (
43 spanContext model.SpanContext
44 name string
45 )
46
47 if config.name != "" {
48 name = config.name
49 } else {
50 name = req.Method
51 }
52
53 if parent := zipkin.SpanFromContext(ctx); parent != nil {
54 spanContext = parent.Context()
55 }
56
57 tags := map[string]string{
58 string(zipkin.TagHTTPMethod): req.Method,
59 string(zipkin.TagHTTPUrl): req.URL.String(),
60 }
61
62 span := tracer.StartSpan(
63 name,
64 zipkin.Kind(model.Client),
65 zipkin.Tags(config.tags),
66 zipkin.Tags(tags),
67 zipkin.Parent(spanContext),
68 zipkin.FlushOnFinish(false),
69 )
70
71 if config.propagate {
72 if err := b3.InjectHTTP(req)(span.Context()); err != nil {
73 config.logger.Log("err", err)
74 }
75 }
76
77 return zipkin.NewContext(ctx, span)
78 },
79 )
80
81 clientAfter := kithttp.ClientAfter(
82 func(ctx context.Context, res *http.Response) context.Context {
83 if span := zipkin.SpanFromContext(ctx); span != nil {
84 zipkin.TagHTTPResponseSize.Set(span, strconv.FormatInt(res.ContentLength, 10))
85 zipkin.TagHTTPStatusCode.Set(span, strconv.Itoa(res.StatusCode))
86 if res.StatusCode > 399 {
87 zipkin.TagError.Set(span, strconv.Itoa(res.StatusCode))
88 }
89 span.Finish()
90 }
91
92 return ctx
93 },
94 )
95
96 clientFinalizer := kithttp.ClientFinalizer(
97 func(ctx context.Context, err error) {
98 if span := zipkin.SpanFromContext(ctx); span != nil {
99 if err != nil {
100 zipkin.TagError.Set(span, err.Error())
101 }
102
103
104
105 span.Finish()
106
107 span.Flush()
108 }
109 },
110 )
111
112 return func(c *kithttp.Client) {
113 clientBefore(c)
114 clientAfter(c)
115 clientFinalizer(c)
116 }
117 }
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132 func HTTPServerTrace(tracer *zipkin.Tracer, options ...TracerOption) kithttp.ServerOption {
133 config := tracerOptions{
134 tags: make(map[string]string),
135 name: "",
136 logger: log.NewNopLogger(),
137 propagate: true,
138 }
139
140 for _, option := range options {
141 option(&config)
142 }
143
144 serverBefore := kithttp.ServerBefore(
145 func(ctx context.Context, req *http.Request) context.Context {
146 var (
147 spanContext model.SpanContext
148 name string
149 )
150
151 if config.name != "" {
152 name = config.name
153 } else {
154 name = req.Method
155 }
156
157 if config.propagate {
158 spanContext = tracer.Extract(b3.ExtractHTTP(req))
159
160 if spanContext.Sampled == nil && config.requestSampler != nil {
161 sample := config.requestSampler(req)
162 spanContext.Sampled = &sample
163 }
164
165 if spanContext.Err != nil {
166 config.logger.Log("err", spanContext.Err)
167 }
168 }
169
170 tags := map[string]string{
171 string(zipkin.TagHTTPMethod): req.Method,
172 string(zipkin.TagHTTPPath): req.URL.Path,
173 }
174
175 span := tracer.StartSpan(
176 name,
177 zipkin.Kind(model.Server),
178 zipkin.Tags(config.tags),
179 zipkin.Tags(tags),
180 zipkin.Parent(spanContext),
181 zipkin.FlushOnFinish(false),
182 )
183
184 return zipkin.NewContext(ctx, span)
185 },
186 )
187
188 serverAfter := kithttp.ServerAfter(
189 func(ctx context.Context, _ http.ResponseWriter) context.Context {
190 if span := zipkin.SpanFromContext(ctx); span != nil {
191 span.Finish()
192 }
193
194 return ctx
195 },
196 )
197
198 serverFinalizer := kithttp.ServerFinalizer(
199 func(ctx context.Context, code int, r *http.Request) {
200 if span := zipkin.SpanFromContext(ctx); span != nil {
201 zipkin.TagHTTPStatusCode.Set(span, strconv.Itoa(code))
202 if code > 399 {
203
204 zipkin.TagError.Set(span, http.StatusText(code))
205 }
206 if rs, ok := ctx.Value(kithttp.ContextKeyResponseSize).(int64); ok {
207 zipkin.TagHTTPResponseSize.Set(span, strconv.FormatInt(rs, 10))
208 }
209
210
211
212
213 span.Finish()
214
215 span.Flush()
216 }
217 },
218 )
219
220 return func(s *kithttp.Server) {
221 serverBefore(s)
222 serverAfter(s)
223 serverFinalizer(s)
224 }
225 }
226
View as plain text