1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package internal
16
17 import (
18 "fmt"
19 "net"
20 "net/http"
21 "strconv"
22 "strings"
23
24 "go.opentelemetry.io/otel/attribute"
25 "go.opentelemetry.io/otel/codes"
26 "go.opentelemetry.io/otel/trace"
27 )
28
29
30
31 type SemanticConventions struct {
32 EnduserIDKey attribute.Key
33 HTTPClientIPKey attribute.Key
34 HTTPFlavorKey attribute.Key
35 HTTPHostKey attribute.Key
36 HTTPMethodKey attribute.Key
37 HTTPRequestContentLengthKey attribute.Key
38 HTTPRouteKey attribute.Key
39 HTTPSchemeHTTP attribute.KeyValue
40 HTTPSchemeHTTPS attribute.KeyValue
41 HTTPServerNameKey attribute.Key
42 HTTPStatusCodeKey attribute.Key
43 HTTPTargetKey attribute.Key
44 HTTPURLKey attribute.Key
45 HTTPUserAgentKey attribute.Key
46 NetHostIPKey attribute.Key
47 NetHostNameKey attribute.Key
48 NetHostPortKey attribute.Key
49 NetPeerIPKey attribute.Key
50 NetPeerNameKey attribute.Key
51 NetPeerPortKey attribute.Key
52 NetTransportIP attribute.KeyValue
53 NetTransportOther attribute.KeyValue
54 NetTransportTCP attribute.KeyValue
55 NetTransportUDP attribute.KeyValue
56 NetTransportUnix attribute.KeyValue
57 }
58
59
60
61
62
63 func (sc *SemanticConventions) NetAttributesFromHTTPRequest(network string, request *http.Request) []attribute.KeyValue {
64 attrs := []attribute.KeyValue{}
65
66 switch network {
67 case "tcp", "tcp4", "tcp6":
68 attrs = append(attrs, sc.NetTransportTCP)
69 case "udp", "udp4", "udp6":
70 attrs = append(attrs, sc.NetTransportUDP)
71 case "ip", "ip4", "ip6":
72 attrs = append(attrs, sc.NetTransportIP)
73 case "unix", "unixgram", "unixpacket":
74 attrs = append(attrs, sc.NetTransportUnix)
75 default:
76 attrs = append(attrs, sc.NetTransportOther)
77 }
78
79 peerIP, peerName, peerPort := hostIPNamePort(request.RemoteAddr)
80 if peerIP != "" {
81 attrs = append(attrs, sc.NetPeerIPKey.String(peerIP))
82 }
83 if peerName != "" {
84 attrs = append(attrs, sc.NetPeerNameKey.String(peerName))
85 }
86 if peerPort != 0 {
87 attrs = append(attrs, sc.NetPeerPortKey.Int(peerPort))
88 }
89
90 hostIP, hostName, hostPort := "", "", 0
91 for _, someHost := range []string{request.Host, request.Header.Get("Host"), request.URL.Host} {
92 hostIP, hostName, hostPort = hostIPNamePort(someHost)
93 if hostIP != "" || hostName != "" || hostPort != 0 {
94 break
95 }
96 }
97 if hostIP != "" {
98 attrs = append(attrs, sc.NetHostIPKey.String(hostIP))
99 }
100 if hostName != "" {
101 attrs = append(attrs, sc.NetHostNameKey.String(hostName))
102 }
103 if hostPort != 0 {
104 attrs = append(attrs, sc.NetHostPortKey.Int(hostPort))
105 }
106
107 return attrs
108 }
109
110
111
112
113
114 func hostIPNamePort(hostWithPort string) (ip string, name string, port int) {
115 var (
116 hostPart, portPart string
117 parsedPort uint64
118 err error
119 )
120 if hostPart, portPart, err = net.SplitHostPort(hostWithPort); err != nil {
121 hostPart, portPart = hostWithPort, ""
122 }
123 if parsedIP := net.ParseIP(hostPart); parsedIP != nil {
124 ip = parsedIP.String()
125 } else {
126 name = hostPart
127 }
128 if parsedPort, err = strconv.ParseUint(portPart, 10, 16); err == nil {
129 port = int(parsedPort)
130 }
131 return
132 }
133
134
135
136
137 func (sc *SemanticConventions) EndUserAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue {
138 if username, _, ok := request.BasicAuth(); ok {
139 return []attribute.KeyValue{sc.EnduserIDKey.String(username)}
140 }
141 return nil
142 }
143
144
145
146
147 func (sc *SemanticConventions) HTTPClientAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue {
148 attrs := []attribute.KeyValue{}
149
150
151
152 userinfo := request.URL.User
153 request.URL.User = nil
154
155 attrs = append(attrs, sc.HTTPURLKey.String(request.URL.String()))
156
157
158 request.URL.User = userinfo
159
160 return append(attrs, sc.httpCommonAttributesFromHTTPRequest(request)...)
161 }
162
163 func (sc *SemanticConventions) httpCommonAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue {
164 attrs := []attribute.KeyValue{}
165 if ua := request.UserAgent(); ua != "" {
166 attrs = append(attrs, sc.HTTPUserAgentKey.String(ua))
167 }
168 if request.ContentLength > 0 {
169 attrs = append(attrs, sc.HTTPRequestContentLengthKey.Int64(request.ContentLength))
170 }
171
172 return append(attrs, sc.httpBasicAttributesFromHTTPRequest(request)...)
173 }
174
175 func (sc *SemanticConventions) httpBasicAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue {
176
177 attrs := []attribute.KeyValue{}
178
179 if request.TLS != nil {
180 attrs = append(attrs, sc.HTTPSchemeHTTPS)
181 } else {
182 attrs = append(attrs, sc.HTTPSchemeHTTP)
183 }
184
185 if request.Host != "" {
186 attrs = append(attrs, sc.HTTPHostKey.String(request.Host))
187 } else if request.URL != nil && request.URL.Host != "" {
188 attrs = append(attrs, sc.HTTPHostKey.String(request.URL.Host))
189 }
190
191 flavor := ""
192 if request.ProtoMajor == 1 {
193 flavor = fmt.Sprintf("1.%d", request.ProtoMinor)
194 } else if request.ProtoMajor == 2 {
195 flavor = "2"
196 }
197 if flavor != "" {
198 attrs = append(attrs, sc.HTTPFlavorKey.String(flavor))
199 }
200
201 if request.Method != "" {
202 attrs = append(attrs, sc.HTTPMethodKey.String(request.Method))
203 } else {
204 attrs = append(attrs, sc.HTTPMethodKey.String(http.MethodGet))
205 }
206
207 return attrs
208 }
209
210
211
212 func (sc *SemanticConventions) HTTPServerMetricAttributesFromHTTPRequest(serverName string, request *http.Request) []attribute.KeyValue {
213 attrs := []attribute.KeyValue{}
214 if serverName != "" {
215 attrs = append(attrs, sc.HTTPServerNameKey.String(serverName))
216 }
217 return append(attrs, sc.httpBasicAttributesFromHTTPRequest(request)...)
218 }
219
220
221
222
223
224 func (sc *SemanticConventions) HTTPServerAttributesFromHTTPRequest(serverName, route string, request *http.Request) []attribute.KeyValue {
225 attrs := []attribute.KeyValue{
226 sc.HTTPTargetKey.String(request.RequestURI),
227 }
228
229 if serverName != "" {
230 attrs = append(attrs, sc.HTTPServerNameKey.String(serverName))
231 }
232 if route != "" {
233 attrs = append(attrs, sc.HTTPRouteKey.String(route))
234 }
235 if values := request.Header["X-Forwarded-For"]; len(values) > 0 {
236 addr := values[0]
237 if i := strings.Index(addr, ","); i > 0 {
238 addr = addr[:i]
239 }
240 attrs = append(attrs, sc.HTTPClientIPKey.String(addr))
241 }
242
243 return append(attrs, sc.httpCommonAttributesFromHTTPRequest(request)...)
244 }
245
246
247
248
249 func (sc *SemanticConventions) HTTPAttributesFromHTTPStatusCode(code int) []attribute.KeyValue {
250 attrs := []attribute.KeyValue{
251 sc.HTTPStatusCodeKey.Int(code),
252 }
253 return attrs
254 }
255
256 type codeRange struct {
257 fromInclusive int
258 toInclusive int
259 }
260
261 func (r codeRange) contains(code int) bool {
262 return r.fromInclusive <= code && code <= r.toInclusive
263 }
264
265 var validRangesPerCategory = map[int][]codeRange{
266 1: {
267 {http.StatusContinue, http.StatusEarlyHints},
268 },
269 2: {
270 {http.StatusOK, http.StatusAlreadyReported},
271 {http.StatusIMUsed, http.StatusIMUsed},
272 },
273 3: {
274 {http.StatusMultipleChoices, http.StatusUseProxy},
275 {http.StatusTemporaryRedirect, http.StatusPermanentRedirect},
276 },
277 4: {
278 {http.StatusBadRequest, http.StatusTeapot},
279 {http.StatusMisdirectedRequest, http.StatusUpgradeRequired},
280 {http.StatusPreconditionRequired, http.StatusTooManyRequests},
281 {http.StatusRequestHeaderFieldsTooLarge, http.StatusRequestHeaderFieldsTooLarge},
282 {http.StatusUnavailableForLegalReasons, http.StatusUnavailableForLegalReasons},
283 },
284 5: {
285 {http.StatusInternalServerError, http.StatusLoopDetected},
286 {http.StatusNotExtended, http.StatusNetworkAuthenticationRequired},
287 },
288 }
289
290
291
292 func SpanStatusFromHTTPStatusCode(code int) (codes.Code, string) {
293 spanCode, valid := validateHTTPStatusCode(code)
294 if !valid {
295 return spanCode, fmt.Sprintf("Invalid HTTP status code %d", code)
296 }
297 return spanCode, ""
298 }
299
300
301
302
303 func SpanStatusFromHTTPStatusCodeAndSpanKind(code int, spanKind trace.SpanKind) (codes.Code, string) {
304 spanCode, valid := validateHTTPStatusCode(code)
305 if !valid {
306 return spanCode, fmt.Sprintf("Invalid HTTP status code %d", code)
307 }
308 category := code / 100
309 if spanKind == trace.SpanKindServer && category == 4 {
310 return codes.Unset, ""
311 }
312 return spanCode, ""
313 }
314
315
316
317
318 func validateHTTPStatusCode(code int) (codes.Code, bool) {
319 category := code / 100
320 ranges, ok := validRangesPerCategory[category]
321 if !ok {
322 return codes.Error, false
323 }
324 ok = false
325 for _, crange := range ranges {
326 ok = crange.contains(code)
327 if ok {
328 break
329 }
330 }
331 if !ok {
332 return codes.Error, false
333 }
334 if category > 0 && category < 4 {
335 return codes.Unset, true
336 }
337 return codes.Error, true
338 }
339
View as plain text