1 package opencensus
2
3 import (
4 "context"
5 "net/http"
6
7 "go.opencensus.io/plugin/ochttp"
8 "go.opencensus.io/plugin/ochttp/propagation/b3"
9 "go.opencensus.io/trace"
10
11 kithttp "github.com/go-kit/kit/transport/http"
12 )
13
14
15 func HTTPClientTrace(options ...TracerOption) kithttp.ClientOption {
16 cfg := TracerOptions{}
17
18 for _, option := range options {
19 option(&cfg)
20 }
21
22 if !cfg.Public && cfg.HTTPPropagate == nil {
23 cfg.HTTPPropagate = &b3.HTTPFormat{}
24 }
25
26 clientBefore := kithttp.ClientBefore(
27 func(ctx context.Context, req *http.Request) context.Context {
28 var name string
29
30 if cfg.Name != "" {
31 name = cfg.Name
32 } else {
33
34 name = req.Method + " " + req.URL.Path
35 }
36
37 ctx, span := trace.StartSpan(
38 ctx,
39 name,
40 trace.WithSampler(cfg.Sampler),
41 trace.WithSpanKind(trace.SpanKindClient),
42 )
43
44 span.AddAttributes(
45 trace.StringAttribute(ochttp.HostAttribute, req.URL.Host),
46 trace.StringAttribute(ochttp.MethodAttribute, req.Method),
47 trace.StringAttribute(ochttp.PathAttribute, req.URL.Path),
48 trace.StringAttribute(ochttp.UserAgentAttribute, req.UserAgent()),
49 )
50
51 if !cfg.Public {
52 cfg.HTTPPropagate.SpanContextToRequest(span.SpanContext(), req)
53 }
54
55 return ctx
56 },
57 )
58
59 clientAfter := kithttp.ClientAfter(
60 func(ctx context.Context, res *http.Response) context.Context {
61 if span := trace.FromContext(ctx); span != nil {
62 span.SetStatus(ochttp.TraceStatus(res.StatusCode, http.StatusText(res.StatusCode)))
63 span.AddAttributes(
64 trace.Int64Attribute(ochttp.StatusCodeAttribute, int64(res.StatusCode)),
65 )
66 }
67 return ctx
68 },
69 )
70
71 clientFinalizer := kithttp.ClientFinalizer(
72 func(ctx context.Context, err error) {
73 if span := trace.FromContext(ctx); span != nil {
74 if err != nil {
75 span.SetStatus(trace.Status{
76 Code: trace.StatusCodeUnknown,
77 Message: err.Error(),
78 })
79 }
80 span.End()
81 }
82 },
83 )
84
85 return func(c *kithttp.Client) {
86 clientBefore(c)
87 clientAfter(c)
88 clientFinalizer(c)
89 }
90 }
91
92
93 func HTTPServerTrace(options ...TracerOption) kithttp.ServerOption {
94 cfg := TracerOptions{}
95
96 for _, option := range options {
97 option(&cfg)
98 }
99
100 if !cfg.Public && cfg.HTTPPropagate == nil {
101 cfg.HTTPPropagate = &b3.HTTPFormat{}
102 }
103
104 serverBefore := kithttp.ServerBefore(
105 func(ctx context.Context, req *http.Request) context.Context {
106 var (
107 spanContext trace.SpanContext
108 span *trace.Span
109 name string
110 ok bool
111 )
112
113 if cfg.Name != "" {
114 name = cfg.Name
115 } else {
116 name = req.Method + " " + req.URL.Path
117 }
118
119 spanContext, ok = cfg.HTTPPropagate.SpanContextFromRequest(req)
120 if ok && !cfg.Public {
121 ctx, span = trace.StartSpanWithRemoteParent(
122 ctx,
123 name,
124 spanContext,
125 trace.WithSpanKind(trace.SpanKindServer),
126 trace.WithSampler(cfg.Sampler),
127 )
128 } else {
129 ctx, span = trace.StartSpan(
130 ctx,
131 name,
132 trace.WithSpanKind(trace.SpanKindServer),
133 trace.WithSampler(cfg.Sampler),
134 )
135 if ok {
136 span.AddLink(trace.Link{
137 TraceID: spanContext.TraceID,
138 SpanID: spanContext.SpanID,
139 Type: trace.LinkTypeChild,
140 Attributes: nil,
141 })
142 }
143 }
144
145 span.AddAttributes(
146 trace.StringAttribute(ochttp.MethodAttribute, req.Method),
147 trace.StringAttribute(ochttp.PathAttribute, req.URL.Path),
148 )
149
150 return ctx
151 },
152 )
153
154 serverFinalizer := kithttp.ServerFinalizer(
155 func(ctx context.Context, code int, r *http.Request) {
156 if span := trace.FromContext(ctx); span != nil {
157 span.SetStatus(ochttp.TraceStatus(code, http.StatusText(code)))
158
159 if rs, ok := ctx.Value(kithttp.ContextKeyResponseSize).(int64); ok {
160 span.AddAttributes(
161 trace.Int64Attribute("http.response_size", rs),
162 )
163 }
164
165 span.End()
166 }
167 },
168 )
169
170 return func(s *kithttp.Server) {
171 serverBefore(s)
172 serverFinalizer(s)
173 }
174 }
175
View as plain text