dd-trace 2.3.1 → 2.4.2
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/ci/init.js +26 -2
- package/index.d.ts +51 -0
- package/package.json +2 -2
- package/packages/datadog-instrumentations/index.js +10 -0
- package/packages/datadog-instrumentations/src/amqp10.js +70 -0
- package/packages/datadog-instrumentations/src/amqplib.js +58 -0
- package/packages/datadog-instrumentations/src/cassandra-driver.js +191 -0
- package/packages/datadog-instrumentations/src/cucumber.js +27 -12
- package/packages/datadog-instrumentations/src/helpers/hook.js +44 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +31 -58
- 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 +139 -0
- package/packages/datadog-instrumentations/src/mongodb-core.js +179 -0
- package/packages/datadog-instrumentations/src/net.js +117 -0
- package/packages/datadog-instrumentations/src/pg.js +75 -0
- package/packages/datadog-instrumentations/src/rhea.js +224 -0
- package/packages/datadog-instrumentations/src/tedious.js +66 -0
- package/packages/datadog-plugin-amqp10/src/index.js +79 -122
- package/packages/datadog-plugin-amqplib/src/index.js +77 -142
- package/packages/datadog-plugin-cassandra-driver/src/index.js +52 -224
- package/packages/datadog-plugin-cucumber/src/index.js +3 -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-jest/src/jest-jasmine2.js +5 -3
- package/packages/datadog-plugin-mocha/src/index.js +96 -207
- package/packages/datadog-plugin-mongodb-core/src/index.js +119 -3
- package/packages/datadog-plugin-net/src/index.js +65 -121
- package/packages/datadog-plugin-next/src/index.js +10 -10
- package/packages/datadog-plugin-pg/src/index.js +32 -69
- package/packages/datadog-plugin-rhea/src/index.js +59 -225
- package/packages/datadog-plugin-tedious/src/index.js +38 -86
- package/packages/dd-trace/lib/version.js +1 -1
- package/packages/dd-trace/src/appsec/recommended.json +235 -315
- package/packages/dd-trace/src/config.js +6 -0
- package/packages/dd-trace/src/iitm.js +5 -1
- package/packages/dd-trace/src/loader.js +6 -4
- package/packages/dd-trace/src/noop/tracer.js +4 -0
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +34 -1
- package/packages/dd-trace/src/opentracing/span.js +34 -0
- 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/web.js +99 -93
- package/packages/dd-trace/src/proxy.js +4 -0
- package/packages/dd-trace/src/ritm.js +60 -25
- package/packages/dd-trace/src/tracer.js +16 -0
- package/packages/datadog-plugin-mongodb-core/src/legacy.js +0 -59
- package/packages/datadog-plugin-mongodb-core/src/unified.js +0 -138
- package/packages/datadog-plugin-mongodb-core/src/util.js +0 -143
|
@@ -109,6 +109,11 @@ class Config {
|
|
|
109
109
|
process.env.DD_TRACE_EXPERIMENTAL_B3_ENABLED,
|
|
110
110
|
false
|
|
111
111
|
)
|
|
112
|
+
const DD_TRACE_TRACEPARENT_ENABLED = coalesce(
|
|
113
|
+
options.experimental && options.experimental.traceparent,
|
|
114
|
+
process.env.DD_TRACE_EXPERIMENTAL_TRACEPARENT_ENABLED,
|
|
115
|
+
false
|
|
116
|
+
)
|
|
112
117
|
const DD_TRACE_RUNTIME_ID_ENABLED = coalesce(
|
|
113
118
|
options.experimental && options.experimental.runtimeId,
|
|
114
119
|
process.env.DD_TRACE_EXPERIMENTAL_RUNTIME_ID_ENABLED,
|
|
@@ -187,6 +192,7 @@ class Config {
|
|
|
187
192
|
this.runtimeMetrics = isTrue(DD_RUNTIME_METRICS_ENABLED)
|
|
188
193
|
this.experimental = {
|
|
189
194
|
b3: isTrue(DD_TRACE_B3_ENABLED),
|
|
195
|
+
traceparent: isTrue(DD_TRACE_TRACEPARENT_ENABLED),
|
|
190
196
|
runtimeId: isTrue(DD_TRACE_RUNTIME_ID_ENABLED),
|
|
191
197
|
exporter: DD_TRACE_EXPORTER,
|
|
192
198
|
enableGetRumData: isTrue(DD_TRACE_GET_RUM_DATA_ENABLED),
|
|
@@ -8,5 +8,9 @@ if (semver.satisfies(process.versions.node, '^12.20.0 || >=14.13.1')) {
|
|
|
8
8
|
} else {
|
|
9
9
|
logger.warn('ESM is not fully supported by this version of Node.js, ' +
|
|
10
10
|
'so dd-trace will not intercept ESM loading.')
|
|
11
|
-
module.exports = () => {
|
|
11
|
+
module.exports = () => ({
|
|
12
|
+
unhook: () => {}
|
|
13
|
+
})
|
|
14
|
+
module.exports.addHook = () => {}
|
|
15
|
+
module.exports.removeHook = () => {}
|
|
12
16
|
}
|
|
@@ -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) {
|
|
@@ -26,6 +26,8 @@ const baggageExpr = new RegExp(`^${baggagePrefix}(.+)$`)
|
|
|
26
26
|
const ddKeys = [traceKey, spanKey, samplingKey, originKey]
|
|
27
27
|
const b3Keys = [b3TraceKey, b3SpanKey, b3ParentKey, b3SampledKey, b3FlagsKey, b3HeaderKey]
|
|
28
28
|
const logKeys = ddKeys.concat(b3Keys)
|
|
29
|
+
const traceparentExpr = /^(\d{2})-([A-Fa-f0-9]{32})-([A-Fa-f0-9]{16})-(\d{2})$/i
|
|
30
|
+
const traceparentKey = 'traceparent'
|
|
29
31
|
|
|
30
32
|
class TextMapPropagator {
|
|
31
33
|
constructor (config) {
|
|
@@ -40,6 +42,7 @@ class TextMapPropagator {
|
|
|
40
42
|
this._injectSamplingPriority(spanContext, carrier)
|
|
41
43
|
this._injectBaggageItems(spanContext, carrier)
|
|
42
44
|
this._injectB3(spanContext, carrier)
|
|
45
|
+
this._injectTraceparent(spanContext, carrier)
|
|
43
46
|
|
|
44
47
|
log.debug(() => `Inject into carrier: ${JSON.stringify(pick(carrier, logKeys))}.`)
|
|
45
48
|
}
|
|
@@ -92,8 +95,20 @@ class TextMapPropagator {
|
|
|
92
95
|
}
|
|
93
96
|
}
|
|
94
97
|
|
|
98
|
+
_injectTraceparent (spanContext, carrier) {
|
|
99
|
+
if (!this._config.experimental.traceparent) return
|
|
100
|
+
|
|
101
|
+
const sampling = spanContext._sampling.priority >= AUTO_KEEP ? '01' : '00'
|
|
102
|
+
const traceId = spanContext._traceId.toString('hex').padStart(32, '0')
|
|
103
|
+
const spanId = spanContext._spanId.toString('hex').padStart(16, '0')
|
|
104
|
+
carrier[traceparentKey] = `01-${traceId}-${spanId}-${sampling}`
|
|
105
|
+
}
|
|
106
|
+
|
|
95
107
|
_extractSpanContext (carrier) {
|
|
96
|
-
return this._extractDatadogContext(carrier) ||
|
|
108
|
+
return this._extractDatadogContext(carrier) ||
|
|
109
|
+
this._extractTraceparentContext(carrier) ||
|
|
110
|
+
this._extractB3Context(carrier) ||
|
|
111
|
+
this._extractSqsdContext(carrier)
|
|
97
112
|
}
|
|
98
113
|
|
|
99
114
|
_extractDatadogContext (carrier) {
|
|
@@ -146,6 +161,24 @@ class TextMapPropagator {
|
|
|
146
161
|
return this._extractDatadogContext(parsed)
|
|
147
162
|
}
|
|
148
163
|
|
|
164
|
+
_extractTraceparentContext (carrier) {
|
|
165
|
+
if (!this._config.experimental.traceparent) return null
|
|
166
|
+
|
|
167
|
+
const headerValue = carrier[traceparentKey]
|
|
168
|
+
if (!headerValue) {
|
|
169
|
+
return null
|
|
170
|
+
}
|
|
171
|
+
const matches = headerValue.match(traceparentExpr)
|
|
172
|
+
if (matches.length) {
|
|
173
|
+
return new DatadogSpanContext({
|
|
174
|
+
traceId: id(matches[2], 16),
|
|
175
|
+
spanId: id(matches[3], 16),
|
|
176
|
+
sampling: { priority: matches[4] === '01' ? 1 : 0 }
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
return null
|
|
180
|
+
}
|
|
181
|
+
|
|
149
182
|
_extractGenericContext (carrier, traceKey, spanKey, radix) {
|
|
150
183
|
if (carrier[traceKey] && carrier[spanKey]) {
|
|
151
184
|
return new DatadogSpanContext({
|
|
@@ -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
|
|
@@ -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
|
|
@@ -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
|
|
|
@@ -141,6 +141,10 @@ class Tracer extends BaseTracer {
|
|
|
141
141
|
getRumData () {
|
|
142
142
|
return this._tracer.getRumData.apply(this._tracer, arguments)
|
|
143
143
|
}
|
|
144
|
+
|
|
145
|
+
setUser () {
|
|
146
|
+
return this._tracer.setUser.apply(this.tracer, arguments)
|
|
147
|
+
}
|
|
144
148
|
}
|
|
145
149
|
|
|
146
150
|
module.exports = Tracer
|
|
@@ -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
|
}
|
|
@@ -122,6 +122,22 @@ class DatadogTracer extends Tracer {
|
|
|
122
122
|
<meta name="dd-trace-id" content="${traceId}" />\
|
|
123
123
|
<meta name="dd-trace-time" content="${traceTime}" />`
|
|
124
124
|
}
|
|
125
|
+
|
|
126
|
+
setUser (user) {
|
|
127
|
+
if (!user || !user.id) return this
|
|
128
|
+
|
|
129
|
+
const span = this.scope().active()
|
|
130
|
+
if (!span) return this
|
|
131
|
+
|
|
132
|
+
const rootSpan = span._spanContext._trace.started[0]
|
|
133
|
+
if (!rootSpan) return this
|
|
134
|
+
|
|
135
|
+
for (const k of Object.keys(user)) {
|
|
136
|
+
rootSpan.setTag(`usr.${k}`, '' + user[k])
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return this
|
|
140
|
+
}
|
|
125
141
|
}
|
|
126
142
|
|
|
127
143
|
function addError (span, error) {
|