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
|
@@ -3,11 +3,10 @@
|
|
|
3
3
|
const dc = require('diagnostics_channel')
|
|
4
4
|
const path = require('path')
|
|
5
5
|
const semver = require('semver')
|
|
6
|
-
const
|
|
7
|
-
const ritm = require('../../../dd-trace/src/ritm')
|
|
8
|
-
const parse = require('module-details-from-path')
|
|
6
|
+
const Hook = require('./hook')
|
|
9
7
|
const requirePackageJson = require('../../../dd-trace/src/require-package-json')
|
|
10
8
|
const { AsyncResource } = require('async_hooks')
|
|
9
|
+
const log = require('../../../dd-trace/src/log')
|
|
11
10
|
|
|
12
11
|
const pathSepExpr = new RegExp(`\\${path.sep}`, 'g')
|
|
13
12
|
const channelMap = {}
|
|
@@ -20,18 +19,22 @@ exports.channel = function channel (name) {
|
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
exports.addHook = function addHook ({ name, versions, file }, hook) {
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
const fullFilename = filename(name, file)
|
|
23
|
+
|
|
24
|
+
Hook([name], (moduleExports, moduleName, moduleBaseDir) => {
|
|
25
25
|
moduleName = moduleName.replace(pathSepExpr, '/')
|
|
26
|
-
|
|
27
|
-
if (moduleName !==
|
|
26
|
+
|
|
27
|
+
if (moduleName !== fullFilename || !matchVersion(getVersion(moduleBaseDir), versions)) {
|
|
28
28
|
return moduleExports
|
|
29
29
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
return hook(moduleExports)
|
|
33
|
+
} catch (e) {
|
|
34
|
+
log.error(e)
|
|
35
|
+
return moduleExports
|
|
36
|
+
}
|
|
37
|
+
})
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
function matchVersion (version, ranges) {
|
|
@@ -48,48 +51,9 @@ function filename (name, file) {
|
|
|
48
51
|
return [name, file].filter(val => val).join('/')
|
|
49
52
|
}
|
|
50
53
|
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
let pkg
|
|
56
|
-
|
|
57
|
-
for (let i = 0, l = ids.length; i < l; i++) {
|
|
58
|
-
if (ids[i] === instrumentation.name) {
|
|
59
|
-
hook(require.cache[ids[i]].exports)
|
|
60
|
-
continue
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const id = ids[i].replace(pathSepExpr, '/')
|
|
64
|
-
|
|
65
|
-
if (!id.includes(`/node_modules/${instrumentation.name}/`)) continue
|
|
66
|
-
|
|
67
|
-
if (instrumentation.file) {
|
|
68
|
-
if (!id.endsWith(`/node_modules/${filename(instrumentation)}`)) continue
|
|
69
|
-
|
|
70
|
-
const basedir = getBasedir(ids[i])
|
|
71
|
-
|
|
72
|
-
pkg = requirePackageJson(basedir, module)
|
|
73
|
-
} else {
|
|
74
|
-
const basedir = getBasedir(ids[i])
|
|
75
|
-
|
|
76
|
-
pkg = requirePackageJson(basedir, module)
|
|
77
|
-
|
|
78
|
-
const mainFile = path.posix.normalize(pkg.main || 'index.js')
|
|
79
|
-
if (!id.endsWith(`/node_modules/${instrumentation.name}/${mainFile}`)) continue
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (!matchVersion(pkg.version, instrumentation.versions)) continue
|
|
83
|
-
|
|
84
|
-
hook(require.cache[ids[i]].exports)
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function getBasedir (id) {
|
|
89
|
-
return parse(id).basedir.replace(pathSepExpr, '/')
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (semver.satisfies(process.versions.node, '>=16.0.0')) {
|
|
54
|
+
// AsyncResource.bind exists and binds `this` properly only from 17.8.0 and up.
|
|
55
|
+
// https://nodejs.org/api/async_context.html#asyncresourcebindfn-thisarg
|
|
56
|
+
if (semver.satisfies(process.versions.node, '>=17.8.0')) {
|
|
93
57
|
exports.AsyncResource = AsyncResource
|
|
94
58
|
} else {
|
|
95
59
|
exports.AsyncResource = class extends AsyncResource {
|
|
@@ -98,9 +62,18 @@ if (semver.satisfies(process.versions.node, '>=16.0.0')) {
|
|
|
98
62
|
return (new exports.AsyncResource(type || 'bound-anonymous-fn')).bind(fn, thisArg)
|
|
99
63
|
}
|
|
100
64
|
|
|
101
|
-
bind (fn, thisArg
|
|
102
|
-
|
|
103
|
-
|
|
65
|
+
bind (fn, thisArg) {
|
|
66
|
+
let bound
|
|
67
|
+
if (thisArg === undefined) {
|
|
68
|
+
const resource = this
|
|
69
|
+
bound = function (...args) {
|
|
70
|
+
args.unshift(fn, this)
|
|
71
|
+
return Reflect.apply(resource.runInAsyncScope, resource, args)
|
|
72
|
+
}
|
|
73
|
+
} else {
|
|
74
|
+
bound = this.runInAsyncScope.bind(this, fn, thisArg)
|
|
75
|
+
}
|
|
76
|
+
Object.defineProperties(bound, {
|
|
104
77
|
'length': {
|
|
105
78
|
configurable: true,
|
|
106
79
|
enumerable: false,
|
|
@@ -114,7 +87,7 @@ if (semver.satisfies(process.versions.node, '>=16.0.0')) {
|
|
|
114
87
|
writable: true
|
|
115
88
|
}
|
|
116
89
|
})
|
|
117
|
-
return
|
|
90
|
+
return bound
|
|
118
91
|
}
|
|
119
92
|
}
|
|
120
93
|
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/* eslint-disable no-fallthrough */
|
|
4
|
+
|
|
5
|
+
const url = require('url')
|
|
6
|
+
const {
|
|
7
|
+
channel,
|
|
8
|
+
addHook,
|
|
9
|
+
AsyncResource
|
|
10
|
+
} = require('../helpers/instrument')
|
|
11
|
+
const shimmer = require('../../../datadog-shimmer')
|
|
12
|
+
|
|
13
|
+
const log = require('../../../dd-trace/src/log')
|
|
14
|
+
|
|
15
|
+
const startClientCh = channel('apm:http:client:request:start')
|
|
16
|
+
const asyncEndClientCh = channel('apm:http:client:request:async-end')
|
|
17
|
+
const endClientCh = channel('apm:http:client:request:end')
|
|
18
|
+
const errorClientCh = channel('apm:http:client:request:error')
|
|
19
|
+
|
|
20
|
+
addHook({ name: 'https' }, hookFn)
|
|
21
|
+
|
|
22
|
+
addHook({ name: 'http' }, hookFn)
|
|
23
|
+
|
|
24
|
+
function hookFn (http) {
|
|
25
|
+
patch(http, 'request')
|
|
26
|
+
patch(http, 'get')
|
|
27
|
+
|
|
28
|
+
return http
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function patch (http, methodName) {
|
|
32
|
+
shimmer.wrap(http, methodName, instrumentRequest)
|
|
33
|
+
|
|
34
|
+
function instrumentRequest (request) {
|
|
35
|
+
return function () {
|
|
36
|
+
if (!startClientCh.hasSubscribers) {
|
|
37
|
+
return request.apply(this, arguments)
|
|
38
|
+
}
|
|
39
|
+
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
40
|
+
|
|
41
|
+
let args
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
args = normalizeArgs.apply(null, arguments)
|
|
45
|
+
} catch (e) {
|
|
46
|
+
log.error(e)
|
|
47
|
+
return request.apply(this, arguments)
|
|
48
|
+
}
|
|
49
|
+
startClientCh.publish({ args, http })
|
|
50
|
+
|
|
51
|
+
const ar = new AsyncResource('bound-anonymous-fn')
|
|
52
|
+
|
|
53
|
+
let finished = false
|
|
54
|
+
let callback = args.callback
|
|
55
|
+
|
|
56
|
+
if (callback) {
|
|
57
|
+
callback = asyncResource.bind(callback)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const options = args.options
|
|
61
|
+
const req = ar.bind(request).call(this, options, callback)
|
|
62
|
+
const emit = req.emit
|
|
63
|
+
|
|
64
|
+
const finish = (req, res) => {
|
|
65
|
+
if (!finished) {
|
|
66
|
+
finished = true
|
|
67
|
+
asyncEndClientCh.publish({ req, res })
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
req.emit = function (eventName, arg) {
|
|
72
|
+
ar.runInAsyncScope(() => {
|
|
73
|
+
switch (eventName) {
|
|
74
|
+
case 'response': {
|
|
75
|
+
const res = arg
|
|
76
|
+
const listener = ar.bind(() => finish(req, res))
|
|
77
|
+
res.on('end', listener)
|
|
78
|
+
res.on('error', listener)
|
|
79
|
+
break
|
|
80
|
+
}
|
|
81
|
+
case 'connect':
|
|
82
|
+
case 'upgrade':
|
|
83
|
+
finish(req, arg)
|
|
84
|
+
break
|
|
85
|
+
case 'error':
|
|
86
|
+
errorClientCh.publish(arg)
|
|
87
|
+
case 'abort': // deprecated and replaced by `close` in node 17
|
|
88
|
+
case 'timeout':
|
|
89
|
+
case 'close':
|
|
90
|
+
finish(req)
|
|
91
|
+
}
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
return emit.apply(this, arguments)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
endClientCh.publish(undefined)
|
|
98
|
+
|
|
99
|
+
return req
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function normalizeArgs (inputURL, inputOptions, cb) {
|
|
104
|
+
inputURL = normalizeOptions(inputURL)
|
|
105
|
+
|
|
106
|
+
const [callback, inputOptionsNormalized] = normalizeCallback(inputOptions, cb, inputURL)
|
|
107
|
+
const options = combineOptions(inputURL, inputOptionsNormalized)
|
|
108
|
+
normalizeHeaders(options)
|
|
109
|
+
const uri = url.format(options)
|
|
110
|
+
|
|
111
|
+
return { uri, options, callback }
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function combineOptions (inputURL, inputOptions) {
|
|
115
|
+
if (typeof inputOptions === 'object') {
|
|
116
|
+
return Object.assign(inputURL || {}, inputOptions)
|
|
117
|
+
} else {
|
|
118
|
+
return inputURL
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function normalizeHeaders (options) {
|
|
122
|
+
options.headers = options.headers || {}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function normalizeCallback (inputOptions, callback, inputURL) {
|
|
126
|
+
if (typeof inputOptions === 'function') {
|
|
127
|
+
return [inputOptions, inputURL || {}]
|
|
128
|
+
} else {
|
|
129
|
+
return [callback, inputOptions]
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function normalizeOptions (inputURL) {
|
|
134
|
+
if (typeof inputURL === 'string') {
|
|
135
|
+
try {
|
|
136
|
+
return urlToOptions(new url.URL(inputURL))
|
|
137
|
+
} catch (e) {
|
|
138
|
+
return url.parse(inputURL)
|
|
139
|
+
}
|
|
140
|
+
} else if (inputURL instanceof url.URL) {
|
|
141
|
+
return urlToOptions(inputURL)
|
|
142
|
+
} else {
|
|
143
|
+
return inputURL
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function urlToOptions (url) {
|
|
148
|
+
const agent = url.agent || http.globalAgent
|
|
149
|
+
const options = {
|
|
150
|
+
protocol: url.protocol || agent.protocol,
|
|
151
|
+
hostname: typeof url.hostname === 'string' && url.hostname.startsWith('[')
|
|
152
|
+
? url.hostname.slice(1, -1)
|
|
153
|
+
: url.hostname ||
|
|
154
|
+
url.host ||
|
|
155
|
+
'localhost',
|
|
156
|
+
hash: url.hash,
|
|
157
|
+
search: url.search,
|
|
158
|
+
pathname: url.pathname,
|
|
159
|
+
path: `${url.pathname || ''}${url.search || ''}`,
|
|
160
|
+
href: url.href
|
|
161
|
+
}
|
|
162
|
+
if (url.port !== '') {
|
|
163
|
+
options.port = Number(url.port)
|
|
164
|
+
}
|
|
165
|
+
if (url.username || url.password) {
|
|
166
|
+
options.auth = `${url.username}:${url.password}`
|
|
167
|
+
}
|
|
168
|
+
return options
|
|
169
|
+
}
|
|
170
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
channel,
|
|
5
|
+
addHook
|
|
6
|
+
} = require('../helpers/instrument')
|
|
7
|
+
const shimmer = require('../../../datadog-shimmer')
|
|
8
|
+
|
|
9
|
+
const startServerCh = channel('apm:http:server:request:start')
|
|
10
|
+
const endServerCh = channel('apm:http:server:request:end')
|
|
11
|
+
const errorServerCh = channel('apm:http:server:request:error')
|
|
12
|
+
const asyncEndServerCh = channel('apm:http:server:request:async-end')
|
|
13
|
+
|
|
14
|
+
addHook({ name: 'https' }, http => {
|
|
15
|
+
// http.ServerResponse not present on https
|
|
16
|
+
shimmer.wrap(http.Server.prototype, 'emit', wrapEmit)
|
|
17
|
+
return http
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
addHook({ name: 'http' }, http => {
|
|
21
|
+
shimmer.wrap(http.ServerResponse.prototype, 'emit', wrapResponseEmit)
|
|
22
|
+
shimmer.wrap(http.Server.prototype, 'emit', wrapEmit)
|
|
23
|
+
return http
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
function wrapResponseEmit (emit) {
|
|
27
|
+
return function (eventName, event) {
|
|
28
|
+
if (!startServerCh.hasSubscribers) {
|
|
29
|
+
return emit.apply(this, arguments)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (eventName === 'finish') {
|
|
33
|
+
asyncEndServerCh.publish({ req: this.req })
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return emit.apply(this, arguments)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function wrapEmit (emit) {
|
|
40
|
+
return function (eventName, req, res) {
|
|
41
|
+
if (!startServerCh.hasSubscribers) {
|
|
42
|
+
return emit.apply(this, arguments)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (eventName === 'request') {
|
|
46
|
+
res.req = req
|
|
47
|
+
startServerCh.publish({ req, res })
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
return emit.apply(this, arguments)
|
|
51
|
+
} catch (err) {
|
|
52
|
+
errorServerCh.publish(err)
|
|
53
|
+
|
|
54
|
+
throw err
|
|
55
|
+
} finally {
|
|
56
|
+
endServerCh.publish(undefined)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return emit.apply(this, arguments)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
const { addHook, channel, AsyncResource } = require('./helpers/instrument')
|
|
2
|
+
const shimmer = require('../../datadog-shimmer')
|
|
3
|
+
|
|
4
|
+
const testStartCh = channel('ci:mocha:test:start')
|
|
5
|
+
const errorCh = channel('ci:mocha:test:error')
|
|
6
|
+
const skipCh = channel('ci:mocha:test:skip')
|
|
7
|
+
const testEndCh = channel('ci:mocha:test:end')
|
|
8
|
+
const testAsyncEndCh = channel('ci:mocha:test:async-end')
|
|
9
|
+
const suiteEndCh = channel('ci:mocha:suite:end')
|
|
10
|
+
const hookErrorCh = channel('ci:mocha:hook:error')
|
|
11
|
+
const parameterizedTestCh = channel('ci:mocha:test:parameterize')
|
|
12
|
+
const testRunEndCh = channel('ci:mocha:run:end')
|
|
13
|
+
|
|
14
|
+
// TODO: remove when root hooks and fixtures are implemented
|
|
15
|
+
const patched = new WeakSet()
|
|
16
|
+
|
|
17
|
+
function isRetry (test) {
|
|
18
|
+
return test._currentRetry !== undefined && test._currentRetry !== 0
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function getAllTestsInSuite (root) {
|
|
22
|
+
const tests = []
|
|
23
|
+
function getTests (suiteOrTest) {
|
|
24
|
+
suiteOrTest.tests.forEach(test => {
|
|
25
|
+
tests.push(test)
|
|
26
|
+
})
|
|
27
|
+
suiteOrTest.suites.forEach(suite => {
|
|
28
|
+
getTests(suite)
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
getTests(root)
|
|
32
|
+
return tests
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function mochaHook (Runner) {
|
|
36
|
+
if (patched.has(Runner)) return Runner
|
|
37
|
+
|
|
38
|
+
patched.add(Runner)
|
|
39
|
+
|
|
40
|
+
shimmer.wrap(Runner.prototype, 'runTest', runTest => function () {
|
|
41
|
+
if (!testStartCh.hasSubscribers) {
|
|
42
|
+
return runTest.apply(this, arguments)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!isRetry(this.test)) {
|
|
46
|
+
testStartCh.publish(this.test)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
this.once('test end', AsyncResource.bind(() => {
|
|
50
|
+
let status
|
|
51
|
+
|
|
52
|
+
if (this.test.pending) {
|
|
53
|
+
status = 'skipped'
|
|
54
|
+
} else if (this.test.state !== 'failed' && !this.test.timedOut) {
|
|
55
|
+
status = 'pass'
|
|
56
|
+
} else {
|
|
57
|
+
status = 'fail'
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
testAsyncEndCh.publish(status)
|
|
61
|
+
}))
|
|
62
|
+
|
|
63
|
+
this.once('fail', AsyncResource.bind((test, err) => {
|
|
64
|
+
errorCh.publish(err)
|
|
65
|
+
}))
|
|
66
|
+
|
|
67
|
+
this.once('pending', AsyncResource.bind((test) => {
|
|
68
|
+
skipCh.publish(test)
|
|
69
|
+
}))
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
return runTest.apply(this, arguments)
|
|
73
|
+
} catch (err) {
|
|
74
|
+
errorCh.publish(err)
|
|
75
|
+
throw err
|
|
76
|
+
} finally {
|
|
77
|
+
testEndCh.publish(undefined)
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
shimmer.wrap(Runner.prototype, 'runTests', runTests => function () {
|
|
82
|
+
if (!suiteEndCh.hasSubscribers) {
|
|
83
|
+
return runTests.apply(this, arguments)
|
|
84
|
+
}
|
|
85
|
+
this.once('end', AsyncResource.bind(() => {
|
|
86
|
+
testRunEndCh.publish(undefined)
|
|
87
|
+
}))
|
|
88
|
+
runTests.apply(this, arguments)
|
|
89
|
+
const suite = arguments[0]
|
|
90
|
+
// We call `getAllTestsInSuite` with the root suite so every skipped test
|
|
91
|
+
// should already have an associated test span.
|
|
92
|
+
const tests = getAllTestsInSuite(suite)
|
|
93
|
+
suiteEndCh.publish(tests)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
shimmer.wrap(Runner.prototype, 'fail', fail => function (hook, error) {
|
|
97
|
+
if (!hookErrorCh.hasSubscribers) {
|
|
98
|
+
return fail.apply(this, arguments)
|
|
99
|
+
}
|
|
100
|
+
if (error && hook.ctx && hook.ctx.currentTest) {
|
|
101
|
+
error.message = `${hook.title}: ${error.message}`
|
|
102
|
+
hookErrorCh.publish({ test: hook.ctx.currentTest, error })
|
|
103
|
+
}
|
|
104
|
+
return fail.apply(this, arguments)
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
return Runner
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function mochaEachHook (mochaEach) {
|
|
111
|
+
if (patched.has(mochaEach)) return mochaEach
|
|
112
|
+
|
|
113
|
+
patched.add(mochaEach)
|
|
114
|
+
|
|
115
|
+
return shimmer.wrap(mochaEach, function () {
|
|
116
|
+
const [params] = arguments
|
|
117
|
+
const { it, ...rest } = mochaEach.apply(this, arguments)
|
|
118
|
+
return {
|
|
119
|
+
it: function (name) {
|
|
120
|
+
parameterizedTestCh.publish({ name, params })
|
|
121
|
+
it.apply(this, arguments)
|
|
122
|
+
},
|
|
123
|
+
...rest
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
addHook({
|
|
129
|
+
name: 'mocha',
|
|
130
|
+
versions: ['>=5.2.0'],
|
|
131
|
+
file: 'lib/runner.js'
|
|
132
|
+
}, mochaHook)
|
|
133
|
+
|
|
134
|
+
addHook({
|
|
135
|
+
name: 'mocha-each',
|
|
136
|
+
versions: ['>=2.0.1']
|
|
137
|
+
}, mochaEachHook)
|
|
138
|
+
|
|
139
|
+
module.exports = { mochaHook, mochaEachHook }
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
channel,
|
|
5
|
+
addHook,
|
|
6
|
+
AsyncResource
|
|
7
|
+
} = require('./helpers/instrument')
|
|
8
|
+
const shimmer = require('../../datadog-shimmer')
|
|
9
|
+
|
|
10
|
+
const startCh = channel(`apm:mongodb:query:start`)
|
|
11
|
+
const endCh = channel(`apm:mongodb:query:end`)
|
|
12
|
+
const asyncEndCh = channel(`apm:mongodb:query:async-end`)
|
|
13
|
+
const errorCh = channel(`apm:mongodb:query:error`)
|
|
14
|
+
|
|
15
|
+
addHook({ name: 'mongodb-core', versions: ['2 - 3.1.9'] }, Server => {
|
|
16
|
+
const serverProto = Server.Server.prototype
|
|
17
|
+
shimmer.wrap(serverProto, 'command', command => wrapCommand(command, 'command'))
|
|
18
|
+
shimmer.wrap(serverProto, 'insert', insert => wrapCommand(insert, 'insert', 'insert'))
|
|
19
|
+
shimmer.wrap(serverProto, 'update', update => wrapCommand(update, 'update', 'update'))
|
|
20
|
+
shimmer.wrap(serverProto, 'remove', remove => wrapCommand(remove, 'remove', 'remove'))
|
|
21
|
+
|
|
22
|
+
const cursorProto = Server.Cursor.prototype
|
|
23
|
+
shimmer.wrap(cursorProto, '_getmore', _getmore => wrapCursor(_getmore, 'getMore', 'getMore'))
|
|
24
|
+
shimmer.wrap(cursorProto, '_find', _find => wrapQuery(_find, '_find'))
|
|
25
|
+
shimmer.wrap(cursorProto, 'kill', kill => wrapCursor(kill, 'killCursors', 'killCursors'))
|
|
26
|
+
return Server
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
addHook({ name: 'mongodb', versions: ['>=4'], file: 'lib/cmap/connection.js' }, Connection => {
|
|
30
|
+
const proto = Connection.Connection.prototype
|
|
31
|
+
shimmer.wrap(proto, 'command', command => wrapConnectionCommand(command, 'command'))
|
|
32
|
+
shimmer.wrap(proto, 'query', query => wrapConnectionCommand(query, 'query'))
|
|
33
|
+
return Connection
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
addHook({ name: 'mongodb', versions: ['>=3.3 <4'], file: 'lib/core/wireprotocol/index.js' }, wp => wrapWp(wp))
|
|
37
|
+
|
|
38
|
+
addHook({ name: 'mongodb-core', versions: ['>=3.2'], file: 'lib/wireprotocol/index.js' }, wp => wrapWp(wp))
|
|
39
|
+
|
|
40
|
+
addHook({ name: 'mongodb-core', versions: ['~3.1.10'], file: 'lib/wireprotocol/3_2_support.js' }, WireProtocol => {
|
|
41
|
+
shimmer.wrap(WireProtocol.prototype, 'command', command => wrapUnifiedCommand(command, 'command'))
|
|
42
|
+
return WireProtocol
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
addHook({ name: 'mongodb-core', versions: ['~3.1.10'], file: 'lib/wireprotocol/2_6_support.js' }, WireProtocol => {
|
|
46
|
+
shimmer.wrap(WireProtocol.prototype, 'command', command => wrapUnifiedCommand(command, 'command'))
|
|
47
|
+
return WireProtocol
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
addHook({ name: 'mongodb', versions: ['>=3.5.4'], file: 'lib/utils.js' }, util => {
|
|
51
|
+
shimmer.wrap(util, 'maybePromise', maybePromise => function (parent, callback, fn) {
|
|
52
|
+
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
53
|
+
const callbackIndex = arguments.length - 2
|
|
54
|
+
|
|
55
|
+
callback = arguments[callbackIndex]
|
|
56
|
+
|
|
57
|
+
if (typeof callback === 'function') {
|
|
58
|
+
arguments[callbackIndex] = asyncResource.bind(callback)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return maybePromise.apply(this, arguments)
|
|
62
|
+
})
|
|
63
|
+
return util
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
function wrapWp (wp) {
|
|
67
|
+
shimmer.wrap(wp, 'command', command => wrapUnifiedCommand(command, 'command'))
|
|
68
|
+
shimmer.wrap(wp, 'insert', insert => wrapUnifiedCommand(insert, 'insert', 'insert'))
|
|
69
|
+
shimmer.wrap(wp, 'update', update => wrapUnifiedCommand(update, 'update', 'update'))
|
|
70
|
+
shimmer.wrap(wp, 'remove', remove => wrapUnifiedCommand(remove, 'remove', 'remove'))
|
|
71
|
+
shimmer.wrap(wp, 'query', query => wrapUnifiedCommand(query, 'query'))
|
|
72
|
+
shimmer.wrap(wp, 'getMore', getMore => wrapUnifiedCommand(getMore, 'getMore', 'getMore'))
|
|
73
|
+
shimmer.wrap(wp, 'killCursors', killCursors => wrapUnifiedCommand(killCursors, 'killCursors', 'killCursors'))
|
|
74
|
+
return wp
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function wrapUnifiedCommand (command, operation, name) {
|
|
78
|
+
const wrapped = function (server, ns, ops) {
|
|
79
|
+
if (!startCh.hasSubscribers) {
|
|
80
|
+
return command.apply(this, arguments)
|
|
81
|
+
}
|
|
82
|
+
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
83
|
+
return instrument(asyncResource, operation, command, this, arguments, server, ns, ops, { name })
|
|
84
|
+
}
|
|
85
|
+
return shimmer.wrap(command, wrapped)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function wrapConnectionCommand (command, operation, name) {
|
|
89
|
+
const wrapped = function (ns, ops) {
|
|
90
|
+
if (!startCh.hasSubscribers) {
|
|
91
|
+
return command.apply(this, arguments)
|
|
92
|
+
}
|
|
93
|
+
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
94
|
+
const hostParts = typeof this.address === 'string' ? this.address.split(':') : ''
|
|
95
|
+
const options = hostParts.length === 2
|
|
96
|
+
? { host: hostParts[0], port: hostParts[1] }
|
|
97
|
+
: {} // no port means the address is a random UUID so no host either
|
|
98
|
+
const topology = { s: { options } }
|
|
99
|
+
|
|
100
|
+
ns = `${ns.db}.${ns.collection}`
|
|
101
|
+
return instrument(asyncResource, operation, command, this, arguments, topology, ns, ops, { name })
|
|
102
|
+
}
|
|
103
|
+
return shimmer.wrap(command, wrapped)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function wrapQuery (query, operation, name) {
|
|
107
|
+
const wrapped = function () {
|
|
108
|
+
if (!startCh.hasSubscribers) {
|
|
109
|
+
return query.apply(this, arguments)
|
|
110
|
+
}
|
|
111
|
+
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
112
|
+
const pool = this.server.s.pool
|
|
113
|
+
const ns = this.ns
|
|
114
|
+
const ops = this.cmd
|
|
115
|
+
return instrument(asyncResource, operation, query, this, arguments, pool, ns, ops)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return shimmer.wrap(query, wrapped)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function wrapCursor (cursor, operation, name) {
|
|
122
|
+
const wrapped = function () {
|
|
123
|
+
if (!startCh.hasSubscribers) {
|
|
124
|
+
return cursor.apply(this, arguments)
|
|
125
|
+
}
|
|
126
|
+
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
127
|
+
const pool = this.server.s.pool
|
|
128
|
+
const ns = this.ns
|
|
129
|
+
return instrument(asyncResource, operation, cursor, this, arguments, pool, ns, {}, { name })
|
|
130
|
+
}
|
|
131
|
+
return shimmer.wrap(cursor, wrapped)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function wrapCommand (command, operation, name) {
|
|
135
|
+
const wrapped = function (ns, ops) {
|
|
136
|
+
if (!startCh.hasSubscribers) {
|
|
137
|
+
return command.apply(this, arguments)
|
|
138
|
+
}
|
|
139
|
+
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
140
|
+
return instrument(asyncResource, operation, command, this, arguments, this, ns, ops, { name })
|
|
141
|
+
}
|
|
142
|
+
return shimmer.wrap(command, wrapped)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function instrument (ar, operation, command, ctx, args, server, ns, ops, options = {}) {
|
|
146
|
+
const name = options.name || (ops && Object.keys(ops)[0])
|
|
147
|
+
const index = args.length - 1
|
|
148
|
+
let callback = args[index]
|
|
149
|
+
|
|
150
|
+
if (typeof callback !== 'function') return command.apply(ctx, args)
|
|
151
|
+
|
|
152
|
+
callback = ar.bind(callback)
|
|
153
|
+
|
|
154
|
+
const serverInfo = server && server.s && server.s.options
|
|
155
|
+
|
|
156
|
+
startCh.publish({ ns, ops, options: serverInfo, name })
|
|
157
|
+
|
|
158
|
+
args[index] = AsyncResource.bind(function (err, res) {
|
|
159
|
+
if (err) {
|
|
160
|
+
errorCh.publish(err)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
asyncEndCh.publish(undefined)
|
|
164
|
+
|
|
165
|
+
if (callback) {
|
|
166
|
+
return callback.apply(this, arguments)
|
|
167
|
+
}
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
return command.apply(ctx, args)
|
|
172
|
+
} catch (err) {
|
|
173
|
+
errorCh.publish(err)
|
|
174
|
+
|
|
175
|
+
throw err
|
|
176
|
+
} finally {
|
|
177
|
+
endCh.publish(undefined)
|
|
178
|
+
}
|
|
179
|
+
}
|