dd-trace 2.4.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE-3rdparty.csv +1 -0
- package/ci/init.js +32 -2
- package/ci/jest/env.js +16 -3
- package/ext/exporters.d.ts +2 -1
- package/ext/exporters.js +2 -1
- package/package.json +3 -2
- package/packages/datadog-instrumentations/index.js +2 -0
- package/packages/datadog-instrumentations/src/amqplib.js +1 -1
- package/packages/datadog-instrumentations/src/cucumber.js +25 -12
- package/packages/datadog-instrumentations/src/cypress.js +8 -0
- package/packages/datadog-instrumentations/src/helpers/hook.js +44 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +30 -57
- package/packages/datadog-instrumentations/src/http/client.js +170 -0
- package/packages/datadog-instrumentations/src/http/server.js +61 -0
- package/packages/datadog-instrumentations/src/http.js +4 -0
- package/packages/datadog-instrumentations/src/mocha.js +28 -11
- package/packages/datadog-instrumentations/src/net.js +117 -0
- package/packages/datadog-plugin-aws-sdk/src/helpers.js +4 -4
- package/packages/datadog-plugin-aws-sdk/src/index.js +1 -1
- package/packages/datadog-plugin-cucumber/src/index.js +24 -12
- package/packages/datadog-plugin-cypress/src/index.js +10 -5
- package/packages/datadog-plugin-cypress/src/plugin.js +13 -1
- package/packages/datadog-plugin-elasticsearch/src/index.js +4 -2
- package/packages/datadog-plugin-http/src/client.js +112 -252
- package/packages/datadog-plugin-http/src/index.js +29 -3
- package/packages/datadog-plugin-http/src/server.js +54 -32
- package/packages/datadog-plugin-jest/src/jest-environment.js +3 -3
- package/packages/datadog-plugin-mocha/src/index.js +10 -1
- package/packages/datadog-plugin-net/src/index.js +65 -121
- package/packages/datadog-plugin-next/src/index.js +10 -10
- package/packages/dd-trace/lib/version.js +1 -1
- package/packages/dd-trace/src/appsec/callbacks/ddwaf.js +3 -1
- package/packages/dd-trace/src/appsec/recommended.json +119 -210
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +32 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +51 -0
- package/packages/dd-trace/src/config.js +8 -1
- package/packages/dd-trace/src/encode/0.4.js +0 -1
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +193 -0
- package/packages/dd-trace/src/encode/tags-processors.js +116 -0
- package/packages/dd-trace/src/exporter.js +3 -0
- package/packages/dd-trace/src/exporters/agent/index.js +1 -1
- package/packages/dd-trace/src/exporters/agent/writer.js +7 -32
- package/packages/dd-trace/src/exporters/{agent → common}/docker.js +0 -0
- package/packages/dd-trace/src/exporters/common/request.js +83 -0
- package/packages/dd-trace/src/exporters/common/writer.js +36 -0
- package/packages/dd-trace/src/exporters/{agent/scheduler.js → scheduler.js} +0 -0
- package/packages/dd-trace/src/iitm.js +5 -1
- package/packages/dd-trace/src/instrumenter.js +3 -0
- package/packages/dd-trace/src/loader.js +6 -4
- package/packages/dd-trace/src/opentracing/span.js +34 -0
- package/packages/dd-trace/src/pkg.js +11 -6
- package/packages/dd-trace/src/plugin_manager.js +4 -0
- package/packages/dd-trace/src/plugins/plugin.js +3 -1
- package/packages/dd-trace/src/plugins/util/test.js +60 -1
- package/packages/dd-trace/src/plugins/util/web.js +99 -93
- package/packages/dd-trace/src/profiling/exporters/agent.js +1 -1
- package/packages/dd-trace/src/proxy.js +2 -0
- package/packages/dd-trace/src/ritm.js +60 -25
- package/packages/dd-trace/src/telemetry.js +187 -0
- package/packages/dd-trace/src/exporters/agent/request.js +0 -86
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const semver = require('semver')
|
|
4
|
-
const
|
|
5
|
-
const esmHook = require('./iitm')
|
|
4
|
+
const Hook = require('../../datadog-instrumentations/src/helpers/hook')
|
|
6
5
|
const parse = require('module-details-from-path')
|
|
7
6
|
const path = require('path')
|
|
8
7
|
const uniq = require('lodash.uniq')
|
|
@@ -18,6 +17,7 @@ class Loader {
|
|
|
18
17
|
|
|
19
18
|
reload (plugins) {
|
|
20
19
|
this._plugins = plugins
|
|
20
|
+
this._patched = []
|
|
21
21
|
|
|
22
22
|
const instrumentations = Array.from(this._plugins.keys())
|
|
23
23
|
.reduce((prev, current) => prev.concat(current), [])
|
|
@@ -28,8 +28,10 @@ class Loader {
|
|
|
28
28
|
this._names = new Set(instrumentations
|
|
29
29
|
.map(instrumentation => filename(instrumentation)))
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
this._hook && this._hook.unhook()
|
|
32
|
+
this._hook = Hook(instrumentedModules, (moduleExports, moduleName, moduleBaseDir) => {
|
|
33
|
+
return this._hookModule(moduleExports, moduleName, moduleBaseDir)
|
|
34
|
+
})
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
load (instrumentation, config) {
|
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
// TODO (new internal tracer): use DC events for lifecycle metrics and test them
|
|
4
|
+
|
|
3
5
|
const opentracing = require('opentracing')
|
|
4
6
|
const now = require('performance-now')
|
|
7
|
+
const semver = require('semver')
|
|
5
8
|
const Span = opentracing.Span
|
|
6
9
|
const SpanContext = require('./span_context')
|
|
7
10
|
const id = require('../id')
|
|
8
11
|
const tagger = require('../tagger')
|
|
12
|
+
const metrics = require('../metrics')
|
|
9
13
|
const log = require('../log')
|
|
10
14
|
const { storage } = require('../../../datadog-core')
|
|
11
15
|
|
|
12
16
|
const { DD_TRACE_EXPERIMENTAL_STATE_TRACKING } = process.env
|
|
13
17
|
|
|
18
|
+
const unfinishedRegistry = createRegistry('unfinished')
|
|
19
|
+
const finishedRegistry = createRegistry('finished')
|
|
20
|
+
|
|
14
21
|
class DatadogSpan extends Span {
|
|
15
22
|
constructor (tracer, processor, prioritySampler, fields, debug) {
|
|
16
23
|
super()
|
|
@@ -25,6 +32,7 @@ class DatadogSpan extends Span {
|
|
|
25
32
|
this._processor = processor
|
|
26
33
|
this._prioritySampler = prioritySampler
|
|
27
34
|
this._store = storage.getStore()
|
|
35
|
+
this._name = operationName
|
|
28
36
|
|
|
29
37
|
this._spanContext = this._createContext(parent)
|
|
30
38
|
this._spanContext._name = operationName
|
|
@@ -32,6 +40,13 @@ class DatadogSpan extends Span {
|
|
|
32
40
|
this._spanContext._hostname = hostname
|
|
33
41
|
|
|
34
42
|
this._startTime = fields.startTime || this._getTime()
|
|
43
|
+
|
|
44
|
+
if (this._debug && unfinishedRegistry) {
|
|
45
|
+
metrics.increment('runtime.node.spans.unfinished')
|
|
46
|
+
metrics.increment('runtime.node.spans.unfinished.by.name', `span_name:${operationName}`)
|
|
47
|
+
|
|
48
|
+
unfinishedRegistry.register(this, operationName, this)
|
|
49
|
+
}
|
|
35
50
|
}
|
|
36
51
|
|
|
37
52
|
toString () {
|
|
@@ -122,6 +137,16 @@ class DatadogSpan extends Span {
|
|
|
122
137
|
}
|
|
123
138
|
}
|
|
124
139
|
|
|
140
|
+
if (this._debug && finishedRegistry) {
|
|
141
|
+
metrics.decrement('runtime.node.spans.unfinished')
|
|
142
|
+
metrics.decrement('runtime.node.spans.unfinished.by.name', `span_name:${this._name}`)
|
|
143
|
+
metrics.increment('runtime.node.spans.finished')
|
|
144
|
+
metrics.increment('runtime.node.spans.finished.by.name', `span_name:${this._name}`)
|
|
145
|
+
|
|
146
|
+
unfinishedRegistry.unregister(this)
|
|
147
|
+
finishedRegistry.register(this, this._name)
|
|
148
|
+
}
|
|
149
|
+
|
|
125
150
|
finishTime = parseFloat(finishTime) || this._getTime()
|
|
126
151
|
|
|
127
152
|
this._duration = finishTime - this._startTime
|
|
@@ -131,4 +156,13 @@ class DatadogSpan extends Span {
|
|
|
131
156
|
}
|
|
132
157
|
}
|
|
133
158
|
|
|
159
|
+
function createRegistry (type) {
|
|
160
|
+
if (!semver.satisfies(process.version, '>=14.6')) return
|
|
161
|
+
|
|
162
|
+
return new global.FinalizationRegistry(name => {
|
|
163
|
+
metrics.decrement(`runtime.node.spans.${type}`)
|
|
164
|
+
metrics.decrement(`runtime.node.spans.${type}.by.name`, [`span_name:${name}`])
|
|
165
|
+
})
|
|
166
|
+
}
|
|
167
|
+
|
|
134
168
|
module.exports = DatadogSpan
|
|
@@ -11,7 +11,14 @@ function findRoot () {
|
|
|
11
11
|
|
|
12
12
|
function findPkg () {
|
|
13
13
|
const cwd = findRoot()
|
|
14
|
-
const
|
|
14
|
+
const directory = path.resolve(cwd)
|
|
15
|
+
const res = path.parse(directory)
|
|
16
|
+
|
|
17
|
+
if (!res) return {}
|
|
18
|
+
|
|
19
|
+
const { root } = res
|
|
20
|
+
|
|
21
|
+
const filePath = findUp('package.json', root, directory)
|
|
15
22
|
|
|
16
23
|
try {
|
|
17
24
|
return JSON.parse(fs.readFileSync(filePath, 'utf8'))
|
|
@@ -20,18 +27,16 @@ function findPkg () {
|
|
|
20
27
|
}
|
|
21
28
|
}
|
|
22
29
|
|
|
23
|
-
function findUp (name,
|
|
24
|
-
let directory = path.resolve(cwd)
|
|
25
|
-
const { root } = path.parse(directory)
|
|
26
|
-
|
|
30
|
+
function findUp (name, root, directory) {
|
|
27
31
|
while (true) {
|
|
28
32
|
const current = path.resolve(directory, name)
|
|
29
33
|
|
|
30
34
|
if (fs.existsSync(current)) return current
|
|
35
|
+
|
|
31
36
|
if (directory === root) return
|
|
32
37
|
|
|
33
38
|
directory = path.dirname(directory)
|
|
34
39
|
}
|
|
35
40
|
}
|
|
36
41
|
|
|
37
|
-
module.exports = findPkg()
|
|
42
|
+
module.exports = Object.assign(findPkg(), { findRoot, findUp })
|
|
@@ -8,7 +8,6 @@ class Subscription {
|
|
|
8
8
|
this._channel = dc.channel(event)
|
|
9
9
|
this._handler = (message, name) => {
|
|
10
10
|
const store = storage.getStore()
|
|
11
|
-
|
|
12
11
|
if (!store || !store.noop) {
|
|
13
12
|
handler(message, name)
|
|
14
13
|
}
|
|
@@ -58,6 +57,9 @@ module.exports = class Plugin {
|
|
|
58
57
|
}
|
|
59
58
|
|
|
60
59
|
configure (config) {
|
|
60
|
+
if (typeof config === 'boolean') {
|
|
61
|
+
config = { enabled: config }
|
|
62
|
+
}
|
|
61
63
|
this.config = config
|
|
62
64
|
if (config.enabled && !this._enabled) {
|
|
63
65
|
this._enabled = true
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
const path = require('path')
|
|
2
|
+
const fs = require('fs')
|
|
3
|
+
|
|
4
|
+
const ignore = require('ignore')
|
|
2
5
|
|
|
3
6
|
const { getGitMetadata } = require('./git')
|
|
4
7
|
const { getUserProviderGitMetadata } = require('./user-provided-git')
|
|
@@ -25,6 +28,7 @@ const TEST_STATUS = 'test.status'
|
|
|
25
28
|
const TEST_PARAMETERS = 'test.parameters'
|
|
26
29
|
const TEST_SKIP_REASON = 'test.skip_reason'
|
|
27
30
|
const TEST_IS_RUM_ACTIVE = 'test.is_rum_active'
|
|
31
|
+
const TEST_CODE_OWNERS = 'test.codeowners'
|
|
28
32
|
|
|
29
33
|
const ERROR_TYPE = 'error.type'
|
|
30
34
|
const ERROR_MESSAGE = 'error.msg'
|
|
@@ -35,6 +39,7 @@ const CI_APP_ORIGIN = 'ciapp-test'
|
|
|
35
39
|
const JEST_TEST_RUNNER = 'test.jest.test_runner'
|
|
36
40
|
|
|
37
41
|
module.exports = {
|
|
42
|
+
TEST_CODE_OWNERS,
|
|
38
43
|
TEST_FRAMEWORK,
|
|
39
44
|
TEST_FRAMEWORK_VERSION,
|
|
40
45
|
JEST_TEST_RUNNER,
|
|
@@ -53,7 +58,9 @@ module.exports = {
|
|
|
53
58
|
getTestParametersString,
|
|
54
59
|
finishAllTraceSpans,
|
|
55
60
|
getTestParentSpan,
|
|
56
|
-
getTestSuitePath
|
|
61
|
+
getTestSuitePath,
|
|
62
|
+
getCodeOwnersFileEntries,
|
|
63
|
+
getCodeOwnersForFilename
|
|
57
64
|
}
|
|
58
65
|
|
|
59
66
|
function getTestEnvironmentMetadata (testFramework, config) {
|
|
@@ -140,3 +147,55 @@ function getTestSuitePath (testSuiteAbsolutePath, sourceRoot) {
|
|
|
140
147
|
|
|
141
148
|
return testSuitePath.replace(path.sep, '/')
|
|
142
149
|
}
|
|
150
|
+
|
|
151
|
+
const POSSIBLE_CODEOWNERS_LOCATIONS = [
|
|
152
|
+
'CODEOWNERS',
|
|
153
|
+
'.github/CODEOWNERS',
|
|
154
|
+
'docs/CODEOWNERS',
|
|
155
|
+
'.gitlab/CODEOWNERS'
|
|
156
|
+
]
|
|
157
|
+
|
|
158
|
+
function getCodeOwnersFileEntries (rootDir = process.cwd()) {
|
|
159
|
+
let codeOwnersContent
|
|
160
|
+
|
|
161
|
+
POSSIBLE_CODEOWNERS_LOCATIONS.forEach(location => {
|
|
162
|
+
try {
|
|
163
|
+
codeOwnersContent = fs.readFileSync(`${rootDir}/${location}`).toString()
|
|
164
|
+
} catch (e) {
|
|
165
|
+
// retry with next path
|
|
166
|
+
}
|
|
167
|
+
})
|
|
168
|
+
if (!codeOwnersContent) {
|
|
169
|
+
return null
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const entries = []
|
|
173
|
+
const lines = codeOwnersContent.split('\n')
|
|
174
|
+
|
|
175
|
+
for (const line of lines) {
|
|
176
|
+
const [content] = line.split('#')
|
|
177
|
+
const trimmed = content.trim()
|
|
178
|
+
if (trimmed === '') continue
|
|
179
|
+
const [pattern, ...owners] = trimmed.split(/\s+/)
|
|
180
|
+
entries.push({ pattern, owners })
|
|
181
|
+
}
|
|
182
|
+
// Reverse because rules defined last take precedence
|
|
183
|
+
return entries.reverse()
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function getCodeOwnersForFilename (filename, entries) {
|
|
187
|
+
if (!entries) {
|
|
188
|
+
return null
|
|
189
|
+
}
|
|
190
|
+
for (const entry of entries) {
|
|
191
|
+
try {
|
|
192
|
+
const isResponsible = ignore().add(entry.pattern).ignores(filename)
|
|
193
|
+
if (isResponsible) {
|
|
194
|
+
return JSON.stringify(entry.owners)
|
|
195
|
+
}
|
|
196
|
+
} catch (e) {
|
|
197
|
+
return null
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return null
|
|
201
|
+
}
|
|
@@ -52,10 +52,36 @@ const web = {
|
|
|
52
52
|
})
|
|
53
53
|
},
|
|
54
54
|
|
|
55
|
+
startSpan (tracer, config, req, res, name) {
|
|
56
|
+
const context = this.patch(req)
|
|
57
|
+
context.config = config
|
|
58
|
+
|
|
59
|
+
let span
|
|
60
|
+
|
|
61
|
+
if (context.span) {
|
|
62
|
+
context.span.context()._name = name
|
|
63
|
+
span = context.span
|
|
64
|
+
} else {
|
|
65
|
+
span = web.startChildSpan(tracer, name, req.headers)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
context.tracer = tracer
|
|
69
|
+
context.span = span
|
|
70
|
+
context.res = res
|
|
71
|
+
|
|
72
|
+
return span
|
|
73
|
+
},
|
|
74
|
+
wrap (req) {
|
|
75
|
+
const context = contexts.get(req)
|
|
76
|
+
if (!context.instrumented) {
|
|
77
|
+
this.wrapEnd(context)
|
|
78
|
+
this.wrapEvents(context)
|
|
79
|
+
context.instrumented = true
|
|
80
|
+
}
|
|
81
|
+
},
|
|
55
82
|
// Start a span and activate a scope for a request.
|
|
56
83
|
instrument (tracer, config, req, res, name, callback) {
|
|
57
|
-
const
|
|
58
|
-
const span = startSpan(tracer, config, req, res, name)
|
|
84
|
+
const span = this.startSpan(tracer, config, req, res, name)
|
|
59
85
|
|
|
60
86
|
if (!config.filter(req.url)) {
|
|
61
87
|
span.setTag(MANUAL_DROP, true)
|
|
@@ -67,12 +93,7 @@ const web = {
|
|
|
67
93
|
|
|
68
94
|
analyticsSampler.sample(span, config.measured, true)
|
|
69
95
|
|
|
70
|
-
|
|
71
|
-
wrapEnd(context)
|
|
72
|
-
wrapEvents(context)
|
|
73
|
-
|
|
74
|
-
context.instrumented = true
|
|
75
|
-
}
|
|
96
|
+
this.wrap(req)
|
|
76
97
|
|
|
77
98
|
return callback && tracer.scope().activate(span, () => callback(span))
|
|
78
99
|
},
|
|
@@ -199,6 +220,7 @@ const web = {
|
|
|
199
220
|
// Extract the parent span from the headers and start a new span as its child
|
|
200
221
|
startChildSpan (tracer, name, headers) {
|
|
201
222
|
const childOf = tracer.scope().active() || tracer.extract(FORMAT_HTTP_HEADERS, headers)
|
|
223
|
+
|
|
202
224
|
const span = tracer.startSpan(name, { childOf })
|
|
203
225
|
|
|
204
226
|
return span
|
|
@@ -221,103 +243,94 @@ const web = {
|
|
|
221
243
|
const context = contexts.get(req)
|
|
222
244
|
context.error = context.error || error
|
|
223
245
|
}
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
function startSpan (tracer, config, req, res, name) {
|
|
228
|
-
const context = contexts.get(req)
|
|
229
|
-
|
|
230
|
-
context.config = config
|
|
231
|
-
|
|
232
|
-
let span
|
|
233
|
-
|
|
234
|
-
if (context.span) {
|
|
235
|
-
context.span.context()._name = name
|
|
236
|
-
span = context.span
|
|
237
|
-
} else {
|
|
238
|
-
span = web.startChildSpan(tracer, name, req.headers)
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
context.tracer = tracer
|
|
242
|
-
context.span = span
|
|
243
|
-
context.res = res
|
|
244
|
-
|
|
245
|
-
return span
|
|
246
|
-
}
|
|
246
|
+
},
|
|
247
247
|
|
|
248
|
-
|
|
249
|
-
|
|
248
|
+
finishMiddleware (context) {
|
|
249
|
+
if (context.finished) return
|
|
250
250
|
|
|
251
|
-
|
|
251
|
+
let span
|
|
252
252
|
|
|
253
|
-
|
|
254
|
-
|
|
253
|
+
while ((span = context.middleware.pop())) {
|
|
254
|
+
span.finish()
|
|
255
|
+
}
|
|
256
|
+
},
|
|
255
257
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
+
finishSpan (context) {
|
|
259
|
+
const { req, res } = context
|
|
258
260
|
|
|
259
|
-
|
|
260
|
-
context.finished = true
|
|
261
|
-
}
|
|
261
|
+
if (context.finished && !req.stream) return
|
|
262
262
|
|
|
263
|
-
|
|
264
|
-
|
|
263
|
+
addRequestTags(context)
|
|
264
|
+
addResponseTags(context)
|
|
265
265
|
|
|
266
|
-
|
|
266
|
+
context.config.hooks.request(context.span, req, res)
|
|
267
|
+
addResourceTag(context)
|
|
267
268
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
}
|
|
271
|
-
|
|
269
|
+
context.span.finish()
|
|
270
|
+
context.finished = true
|
|
271
|
+
},
|
|
272
|
+
wrapWriteHead (context) {
|
|
273
|
+
const { req, res } = context
|
|
274
|
+
const writeHead = res.writeHead
|
|
272
275
|
|
|
273
|
-
function
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
const res = context.res
|
|
277
|
-
const end = res.end
|
|
276
|
+
return function (statusCode, statusMessage, headers) {
|
|
277
|
+
headers = typeof statusMessage === 'string' ? headers : statusMessage
|
|
278
|
+
headers = Object.assign(res.getHeaders(), headers)
|
|
278
279
|
|
|
279
|
-
|
|
280
|
+
if (req.method.toLowerCase() === 'options' && isOriginAllowed(req, headers)) {
|
|
281
|
+
addAllowHeaders(req, res, headers)
|
|
282
|
+
}
|
|
280
283
|
|
|
281
|
-
|
|
282
|
-
for (const beforeEnd of context.beforeEnd) {
|
|
283
|
-
beforeEnd()
|
|
284
|
+
return writeHead.apply(this, arguments)
|
|
284
285
|
}
|
|
286
|
+
},
|
|
287
|
+
getContext (req) {
|
|
288
|
+
return contexts.get(req)
|
|
289
|
+
},
|
|
290
|
+
wrapRes (context, req, res, end) {
|
|
291
|
+
return function () {
|
|
292
|
+
for (const beforeEnd of context.beforeEnd) {
|
|
293
|
+
beforeEnd()
|
|
294
|
+
}
|
|
285
295
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
if (incomingHttpRequestEnd.hasSubscribers) incomingHttpRequestEnd.publish({ req, res })
|
|
289
|
-
|
|
290
|
-
const returnValue = end.apply(res, arguments)
|
|
291
|
-
|
|
292
|
-
finish(context)
|
|
293
|
-
|
|
294
|
-
return returnValue
|
|
295
|
-
})
|
|
296
|
+
web.finishMiddleware(context)
|
|
296
297
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
return ends.get(this)
|
|
301
|
-
},
|
|
302
|
-
set (value) {
|
|
303
|
-
ends.set(this, scope.bind(value, context.span))
|
|
304
|
-
}
|
|
305
|
-
})
|
|
306
|
-
}
|
|
298
|
+
if (incomingHttpRequestEnd.hasSubscribers) {
|
|
299
|
+
incomingHttpRequestEnd.publish({ req, res })
|
|
300
|
+
}
|
|
307
301
|
|
|
308
|
-
|
|
309
|
-
const { req, res } = context
|
|
310
|
-
const writeHead = res.writeHead
|
|
302
|
+
const returnValue = end.apply(res, arguments)
|
|
311
303
|
|
|
312
|
-
|
|
313
|
-
headers = typeof statusMessage === 'string' ? headers : statusMessage
|
|
314
|
-
headers = Object.assign(res.getHeaders(), headers)
|
|
304
|
+
web.finishSpan(context)
|
|
315
305
|
|
|
316
|
-
|
|
317
|
-
addAllowHeaders(req, res, headers)
|
|
306
|
+
return returnValue
|
|
318
307
|
}
|
|
308
|
+
},
|
|
309
|
+
wrapEnd (context) {
|
|
310
|
+
const scope = context.tracer.scope()
|
|
311
|
+
const req = context.req
|
|
312
|
+
const res = context.res
|
|
313
|
+
const end = res.end
|
|
314
|
+
|
|
315
|
+
res.writeHead = web.wrapWriteHead(context)
|
|
316
|
+
|
|
317
|
+
ends.set(res, this.wrapRes(context, req, res, end))
|
|
318
|
+
|
|
319
|
+
Object.defineProperty(res, 'end', {
|
|
320
|
+
configurable: true,
|
|
321
|
+
get () {
|
|
322
|
+
return ends.get(this)
|
|
323
|
+
},
|
|
324
|
+
set (value) {
|
|
325
|
+
ends.set(this, scope.bind(value, context.span))
|
|
326
|
+
}
|
|
327
|
+
})
|
|
328
|
+
},
|
|
329
|
+
wrapEvents (context) {
|
|
330
|
+
const scope = context.tracer.scope()
|
|
331
|
+
const res = context.res
|
|
319
332
|
|
|
320
|
-
|
|
333
|
+
scope.bind(res, context.span)
|
|
321
334
|
}
|
|
322
335
|
}
|
|
323
336
|
|
|
@@ -354,13 +367,6 @@ function splitHeader (str) {
|
|
|
354
367
|
return typeof str === 'string' ? str.split(/\s*,\s*/) : []
|
|
355
368
|
}
|
|
356
369
|
|
|
357
|
-
function wrapEvents (context) {
|
|
358
|
-
const scope = context.tracer.scope()
|
|
359
|
-
const res = context.res
|
|
360
|
-
|
|
361
|
-
scope.bind(res, context.span)
|
|
362
|
-
}
|
|
363
|
-
|
|
364
370
|
function reactivate (req, fn) {
|
|
365
371
|
const context = contexts.get(req)
|
|
366
372
|
|
|
@@ -5,7 +5,7 @@ const { request } = require('http')
|
|
|
5
5
|
const FormData = require('form-data')
|
|
6
6
|
|
|
7
7
|
// TODO: avoid using dd-trace internals. Make this a separate module?
|
|
8
|
-
const docker = require('../../exporters/
|
|
8
|
+
const docker = require('../../exporters/common/docker')
|
|
9
9
|
const version = require('../../../lib/version')
|
|
10
10
|
|
|
11
11
|
const containerId = docker.id()
|
|
@@ -10,6 +10,7 @@ const metrics = require('./metrics')
|
|
|
10
10
|
const log = require('./log')
|
|
11
11
|
const { isFalse } = require('./util')
|
|
12
12
|
const { setStartupLogInstrumenter } = require('./startup-log')
|
|
13
|
+
const telemetry = require('./telemetry')
|
|
13
14
|
|
|
14
15
|
const noop = new NoopTracer()
|
|
15
16
|
|
|
@@ -63,6 +64,7 @@ class Tracer extends BaseTracer {
|
|
|
63
64
|
this._instrumenter.enable(config)
|
|
64
65
|
this._pluginManager.configure(config)
|
|
65
66
|
setStartupLogInstrumenter(this._instrumenter)
|
|
67
|
+
telemetry.start(config, this._instrumenter, this._pluginManager)
|
|
66
68
|
}
|
|
67
69
|
} catch (e) {
|
|
68
70
|
log.error(e)
|
|
@@ -10,9 +10,10 @@ const origRequire = Module.prototype.require
|
|
|
10
10
|
|
|
11
11
|
module.exports = Hook
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
let moduleHooks = Object.create(null)
|
|
14
|
+
let cache = Object.create(null)
|
|
15
|
+
let patching = Object.create(null)
|
|
16
|
+
let patchedRequire = null
|
|
16
17
|
|
|
17
18
|
function Hook (modules, options, onrequire) {
|
|
18
19
|
if (!(this instanceof Hook)) return new Hook(modules, options, onrequire)
|
|
@@ -25,35 +26,40 @@ function Hook (modules, options, onrequire) {
|
|
|
25
26
|
options = {}
|
|
26
27
|
}
|
|
27
28
|
|
|
29
|
+
modules = modules || []
|
|
28
30
|
options = options || {}
|
|
29
31
|
|
|
30
|
-
this.
|
|
31
|
-
this.
|
|
32
|
-
this.
|
|
32
|
+
this.modules = modules
|
|
33
|
+
this.options = options
|
|
34
|
+
this.onrequire = onrequire
|
|
33
35
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
if (Array.isArray(modules)) {
|
|
37
|
+
for (const mod of modules) {
|
|
38
|
+
const hooks = moduleHooks[mod]
|
|
36
39
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return self._origRequire.apply(this, arguments)
|
|
40
|
+
if (hooks) {
|
|
41
|
+
hooks.push(onrequire)
|
|
42
|
+
} else {
|
|
43
|
+
moduleHooks[mod] = [onrequire]
|
|
44
|
+
}
|
|
43
45
|
}
|
|
46
|
+
}
|
|
44
47
|
|
|
48
|
+
if (patchedRequire) return
|
|
49
|
+
|
|
50
|
+
patchedRequire = Module.prototype.require = function (request) {
|
|
45
51
|
const filename = Module._resolveFilename(request, this)
|
|
46
52
|
const core = filename.indexOf(path.sep) === -1
|
|
47
|
-
let name, basedir
|
|
53
|
+
let name, basedir, hooks
|
|
48
54
|
|
|
49
55
|
// return known patched modules immediately
|
|
50
|
-
if (
|
|
56
|
+
if (cache[filename]) {
|
|
51
57
|
// require.cache was potentially altered externally
|
|
52
|
-
if (require.cache[filename] && require.cache[filename].exports !==
|
|
58
|
+
if (require.cache[filename] && require.cache[filename].exports !== cache[filename].original) {
|
|
53
59
|
return require.cache[filename].exports
|
|
54
60
|
}
|
|
55
61
|
|
|
56
|
-
return
|
|
62
|
+
return cache[filename].exports
|
|
57
63
|
}
|
|
58
64
|
|
|
59
65
|
// Check if this module has a patcher in-progress already.
|
|
@@ -63,7 +69,7 @@ function Hook (modules, options, onrequire) {
|
|
|
63
69
|
patching[filename] = true
|
|
64
70
|
}
|
|
65
71
|
|
|
66
|
-
const exports =
|
|
72
|
+
const exports = origRequire.apply(this, arguments)
|
|
67
73
|
|
|
68
74
|
// If it's already patched, just return it as-is.
|
|
69
75
|
if (patched) return exports
|
|
@@ -73,7 +79,8 @@ function Hook (modules, options, onrequire) {
|
|
|
73
79
|
delete patching[filename]
|
|
74
80
|
|
|
75
81
|
if (core) {
|
|
76
|
-
|
|
82
|
+
hooks = moduleHooks[filename]
|
|
83
|
+
if (!hooks) return exports // abort if module name isn't on whitelist
|
|
77
84
|
name = filename
|
|
78
85
|
} else {
|
|
79
86
|
const stat = parse(filename)
|
|
@@ -81,7 +88,8 @@ function Hook (modules, options, onrequire) {
|
|
|
81
88
|
name = stat.name
|
|
82
89
|
basedir = stat.basedir
|
|
83
90
|
|
|
84
|
-
|
|
91
|
+
hooks = moduleHooks[name]
|
|
92
|
+
if (!hooks) return exports // abort if module name isn't on whitelist
|
|
85
93
|
|
|
86
94
|
// figure out if this is the main module file, or a file inside the module
|
|
87
95
|
const paths = Module._resolveLookupPaths(name, this, true)
|
|
@@ -99,10 +107,37 @@ function Hook (modules, options, onrequire) {
|
|
|
99
107
|
|
|
100
108
|
// ensure that the cache entry is assigned a value before calling
|
|
101
109
|
// onrequire, in case calling onrequire requires the same module.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
110
|
+
cache[filename] = { exports }
|
|
111
|
+
cache[filename].original = exports
|
|
112
|
+
|
|
113
|
+
for (const hook of hooks) {
|
|
114
|
+
cache[filename].exports = hook(cache[filename].exports, name, basedir)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return cache[filename].exports
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
Hook.reset = function () {
|
|
122
|
+
Module.prototype.require = origRequire
|
|
123
|
+
patchedRequire = null
|
|
124
|
+
patching = Object.create(null)
|
|
125
|
+
cache = Object.create(null)
|
|
126
|
+
moduleHooks = Object.create(null)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
Hook.prototype.unhook = function () {
|
|
130
|
+
for (const mod of this.modules) {
|
|
131
|
+
const hooks = (moduleHooks[mod] || []).filter(hook => hook !== this.onrequire)
|
|
132
|
+
|
|
133
|
+
if (hooks.length > 0) {
|
|
134
|
+
moduleHooks[mod] = hooks
|
|
135
|
+
} else {
|
|
136
|
+
delete moduleHooks[mod]
|
|
137
|
+
}
|
|
138
|
+
}
|
|
105
139
|
|
|
106
|
-
|
|
140
|
+
if (Object.keys(moduleHooks).length === 0) {
|
|
141
|
+
Hook.reset()
|
|
107
142
|
}
|
|
108
143
|
}
|