-
Notifications
You must be signed in to change notification settings - Fork 213
/
spanattributes.go
146 lines (131 loc) · 3.6 KB
/
spanattributes.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
package utils
import (
"fmt"
"net/url"
"github.com/getsentry/sentry-go"
otelSdkTrace "go.opentelemetry.io/otel/sdk/trace"
semconv "go.opentelemetry.io/otel/semconv/v1.12.0"
otelTrace "go.opentelemetry.io/otel/trace"
)
type SpanAttributes struct {
Op string
Description string
Source sentry.TransactionSource
}
func ParseSpanAttributes(s otelSdkTrace.ReadOnlySpan) SpanAttributes {
for _, attribute := range s.Attributes() {
if attribute.Key == semconv.HTTPMethodKey {
return descriptionForHttpMethod(s)
}
if attribute.Key == semconv.DBSystemKey {
return descriptionForDbSystem(s)
}
if attribute.Key == semconv.RPCSystemKey {
return SpanAttributes{
Op: "rpc",
Description: s.Name(),
Source: sentry.SourceRoute,
}
}
if attribute.Key == semconv.MessagingSystemKey {
return SpanAttributes{
Op: "messaging",
Description: s.Name(),
Source: sentry.SourceRoute,
}
}
// TODO(michi) Check if this works for AWS Lambda and such.
if attribute.Key == semconv.FaaSTriggerKey {
return SpanAttributes{
Op: attribute.Value.AsString(),
Description: s.Name(),
Source: sentry.SourceRoute,
}
}
}
return SpanAttributes{
Op: "", // becomes "default" in Relay
Description: s.Name(),
Source: sentry.SourceCustom,
}
}
func descriptionForDbSystem(s otelSdkTrace.ReadOnlySpan) SpanAttributes {
description := s.Name()
for _, attribute := range s.Attributes() {
if attribute.Key == semconv.DBStatementKey {
// TODO(michi)
// Note: The value may be sanitized to exclude sensitive information.
// See: https://pkg.go.dev/go.opentelemetry.io/otel/semconv/v1.12.0
description = attribute.Value.AsString()
break
}
}
return SpanAttributes{
Op: "db",
Description: description,
Source: sentry.SourceTask,
}
}
func descriptionForHttpMethod(s otelSdkTrace.ReadOnlySpan) SpanAttributes {
op := "http"
switch s.SpanKind() {
case otelTrace.SpanKindClient:
op = "http.client"
case otelTrace.SpanKindServer:
op = "http.server"
}
var httpTarget string
var httpRoute string
var httpMethod string
var httpUrl string
for _, attribute := range s.Attributes() {
switch attribute.Key {
case semconv.HTTPTargetKey:
httpTarget = attribute.Value.AsString()
case semconv.HTTPRouteKey:
httpRoute = attribute.Value.AsString()
case semconv.HTTPMethodKey:
httpMethod = attribute.Value.AsString()
case semconv.HTTPURLKey:
httpUrl = attribute.Value.AsString()
}
}
var httpPath string
if httpTarget != "" {
if parsedUrl, err := url.Parse(httpTarget); err == nil {
// Do not include the query and fragment parts
httpPath = parsedUrl.Path
} else {
httpPath = httpTarget
}
} else if httpRoute != "" {
httpPath = httpRoute
} else if httpUrl != "" {
// This is normally the HTTP-client case
if parsedUrl, err := url.Parse(httpUrl); err == nil {
// Do not include the query and fragment parts
httpPath = fmt.Sprintf("%s://%s%s", parsedUrl.Scheme, parsedUrl.Host, parsedUrl.Path)
}
}
if httpPath == "" {
return SpanAttributes{
Op: op,
Description: s.Name(),
Source: sentry.SourceCustom,
}
}
// Ex. description="GET /api/users".
description := fmt.Sprintf("%s %s", httpMethod, httpPath)
var source sentry.TransactionSource
// If `httpPath` is a root path, then we can categorize the transaction source as route.
if httpRoute != "" || httpPath == "/" {
source = sentry.SourceRoute
} else {
source = sentry.SourceURL
}
return SpanAttributes{
Op: op,
Description: description,
Source: source,
}
}