-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
trace.go
221 lines (184 loc) · 7.38 KB
/
trace.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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.
package trace
import (
"context"
"fmt"
"strings"
"github.com/DataDog/datadog-agent/cmd/serverless-init/cloudservice"
compcorecfg "github.com/DataDog/datadog-agent/comp/core/config"
comptracecfg "github.com/DataDog/datadog-agent/comp/trace/config"
ddConfig "github.com/DataDog/datadog-agent/pkg/config"
"github.com/DataDog/datadog-agent/pkg/config/model"
pb "github.com/DataDog/datadog-agent/pkg/proto/pbgo/trace"
"github.com/DataDog/datadog-agent/pkg/trace/agent"
"github.com/DataDog/datadog-agent/pkg/trace/config"
"github.com/DataDog/datadog-agent/pkg/trace/telemetry"
"github.com/DataDog/datadog-agent/pkg/util/log"
"github.com/DataDog/datadog-go/v5/statsd"
)
// ServerlessTraceAgent represents a trace agent in a serverless context
type ServerlessTraceAgent struct {
ta *agent.Agent
spanModifier *spanModifier
cancel context.CancelFunc
}
// Load abstracts the file configuration loading
type Load interface {
Load() (*config.AgentConfig, error)
}
// LoadConfig is implementing Load to retrieve the config
type LoadConfig struct {
Path string
}
// httpURLMetaKey is the key of the span meta containing the HTTP URL
const httpURLMetaKey = "http.url"
// tcpRemoteHostMetaKey is the key of the span meta containing the TCP remote host i.e 127.0.0.1
const tcpRemoteHostMetaKey = "tcp.remote.host"
// tcpRemotePortMetaKey is the the key of the span meta containing the TCP remote port e.g 8124
const tcpRemotePortMetaKey = "tcp.remote.port"
// dnsAddressMetaKey is the key of the span meta containing the DNS address
const dnsAddressMetaKey = "dns.address"
// lambdaRuntimeUrlPrefix is the first part of a URL for a call to the Lambda runtime API
const lambdaRuntimeURLPrefix = "http://127.0.0.1:9001"
// lambdaExtensionURLPrefix is the first part of a URL for a call from the Datadog Lambda Library to the Lambda Extension
const lambdaExtensionURLPrefix = "http://127.0.0.1:8124"
// lambdaStatsDURLPrefix is the first part of a URL for a call from Statsd
const lambdaStatsDURLPrefix = "http://127.0.0.1:8125"
// dnsNonRoutableAddressURLPrefix is the first part of a URL from the non-routable address for DNS traces
const dnsNonRoutableAddressURLPrefix = "0.0.0.0"
// dnsLocalHostAddressURLPrefix is the first part of a URL from the localhost address for DNS traces
const dnsLocalHostAddressURLPrefix = "127.0.0.1"
// awsXrayDaemonAddressURLPrefix is the first part of a URL from the _AWS_XRAY_DAEMON_ADDRESS for DNS traces
const awsXrayDaemonAddressURLPrefix = "169.254.79.129"
const invocationSpanResource = "dd-tracer-serverless-span"
// Load loads the config from a file path
func (l *LoadConfig) Load() (*config.AgentConfig, error) {
c, err := compcorecfg.NewServerlessConfig(l.Path)
if err != nil {
return nil, err
} else if c == nil {
return nil, fmt.Errorf("No error, but no configuration component was produced - bailing out")
}
return comptracecfg.LoadConfigFile(l.Path, c)
}
// Start starts the agent
//
//nolint:revive // TODO(SERV) Fix revive linter
func (s *ServerlessTraceAgent) Start(enabled bool, loadConfig Load, lambdaSpanChan chan<- *pb.Span, coldStartSpanId uint64) {
if enabled {
// Set the serverless config option which will be used to determine if
// hostname should be resolved. Skipping hostname resolution saves >1s
// in load time between gRPC calls and agent commands.
ddConfig.Datadog.Set("serverless.enabled", true, model.SourceAgentRuntime)
tc, confErr := loadConfig.Load()
if confErr != nil {
log.Errorf("Unable to load trace agent config: %s", confErr)
} else {
context, cancel := context.WithCancel(context.Background())
tc.Hostname = ""
tc.SynchronousFlushing = true
s.ta = agent.NewAgent(context, tc, telemetry.NewNoopCollector(), &statsd.NoOpClient{})
s.spanModifier = &spanModifier{
coldStartSpanId: coldStartSpanId,
lambdaSpanChan: lambdaSpanChan,
ddOrigin: getDDOrigin(),
}
s.ta.ModifySpan = s.spanModifier.ModifySpan
s.ta.DiscardSpan = filterSpanFromLambdaLibraryOrRuntime
s.cancel = cancel
go s.ta.Run()
}
}
}
// Flush performs a synchronous flushing in the trace agent
func (s *ServerlessTraceAgent) Flush() {
if s.Get() != nil {
s.ta.FlushSync()
}
}
// Get returns the trace agent instance
func (s *ServerlessTraceAgent) Get() *agent.Agent {
return s.ta
}
// SetTags sets the tags to the trace agent config and span processor
func (s *ServerlessTraceAgent) SetTags(tagMap map[string]string) {
if s.Get() != nil {
s.ta.SetGlobalTagsUnsafe(tagMap)
s.spanModifier.tags = tagMap
} else {
log.Debug("could not set tags as the trace agent has not been initialized")
}
}
// Stop stops the trace agent
func (s *ServerlessTraceAgent) Stop() {
if s.cancel != nil {
s.cancel()
}
}
//nolint:revive // TODO(SERV) Fix revive linter
func (s *ServerlessTraceAgent) SetSpanModifier(fn func(*pb.TraceChunk, *pb.Span)) {
s.ta.ModifySpan = fn
}
// filterSpanFromLambdaLibraryOrRuntime returns true if a span was generated by internal HTTP calls within the Datadog
// Lambda Library or the Lambda runtime
func filterSpanFromLambdaLibraryOrRuntime(span *pb.Span) bool {
// Filters out HTTP calls
if httpURL, ok := span.Meta[httpURLMetaKey]; ok {
if strings.HasPrefix(httpURL, lambdaExtensionURLPrefix) {
log.Debugf("Detected span with http url %s, removing it", httpURL)
return true
}
if strings.HasPrefix(httpURL, lambdaStatsDURLPrefix) {
log.Debugf("Detected span with http url %s, removing it", httpURL)
return true
}
if strings.HasPrefix(httpURL, lambdaRuntimeURLPrefix) {
log.Debugf("Detected span with http url %s, removing it", httpURL)
return true
}
}
// Filers out TCP spans
if tcpHost, ok := span.Meta[tcpRemoteHostMetaKey]; ok {
if tcpPort, ok := span.Meta[tcpRemotePortMetaKey]; ok {
tcpLambdaURLPrefix := fmt.Sprint("http://" + tcpHost + ":" + tcpPort)
if strings.HasPrefix(tcpLambdaURLPrefix, lambdaExtensionURLPrefix) {
log.Debugf("Detected span with tcp url %s, removing it", tcpLambdaURLPrefix)
return true
}
if strings.HasPrefix(tcpLambdaURLPrefix, lambdaStatsDURLPrefix) {
log.Debugf("Detected span with tcp url %s, removing it", tcpLambdaURLPrefix)
return true
}
if strings.HasPrefix(tcpLambdaURLPrefix, lambdaRuntimeURLPrefix) {
log.Debugf("Detected span with tcp url %s, removing it", tcpLambdaURLPrefix)
return true
}
}
}
// Filters out DNS spans
if dnsAddress, ok := span.Meta[dnsAddressMetaKey]; ok {
if strings.HasPrefix(dnsAddress, dnsNonRoutableAddressURLPrefix) ||
strings.HasPrefix(dnsAddress, dnsLocalHostAddressURLPrefix) ||
strings.HasPrefix(dnsAddress, awsXrayDaemonAddressURLPrefix) {
log.Debugf("Detected span with dns url %s, removing it", dnsAddress)
return true
}
}
// Filter out the serverless span from the tracer
if span != nil && span.Resource == invocationSpanResource {
log.Debugf("Detected invocation span from tracer, removing it")
return true
}
return false
}
// getDDOrigin returns the value for the _dd.origin tag based on the cloud service type
func getDDOrigin() string {
origin := ddOriginTagValue
if cloudServiceOrigin := cloudservice.GetCloudServiceType().GetOrigin(); cloudServiceOrigin != "local" {
origin = cloudServiceOrigin
}
return origin
}