dd-trace 2.3.1 → 2.4.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/index.d.ts +51 -0
- package/package.json +2 -2
- package/packages/datadog-instrumentations/index.js +8 -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 +2 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +3 -3
- package/packages/datadog-instrumentations/src/mocha.js +122 -0
- package/packages/datadog-instrumentations/src/mongodb-core.js +179 -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-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-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 +137 -116
- package/packages/dd-trace/src/config.js +6 -0
- 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/proxy.js +4 -0
- 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
package/index.d.ts
CHANGED
|
@@ -108,6 +108,13 @@ export declare interface Tracer extends opentracing.Tracer {
|
|
|
108
108
|
* should not be cached.
|
|
109
109
|
*/
|
|
110
110
|
getRumData(): string;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Links an authenticated user to the current trace.
|
|
114
|
+
* @param {User} user Properties of the authenticated user. Accepts custom fields.
|
|
115
|
+
* @returns {Tracer} The Tracer instance for chaining.
|
|
116
|
+
*/
|
|
117
|
+
setUser(user: User): Tracer;
|
|
111
118
|
}
|
|
112
119
|
|
|
113
120
|
export declare interface TraceOptions extends Analyzable {
|
|
@@ -311,6 +318,7 @@ export declare interface TracerOptions {
|
|
|
311
318
|
*/
|
|
312
319
|
experimental?: boolean | {
|
|
313
320
|
b3?: boolean
|
|
321
|
+
traceparent?: boolean
|
|
314
322
|
|
|
315
323
|
/**
|
|
316
324
|
* Whether to add an auto-generated `runtime-id` tag to metrics.
|
|
@@ -424,6 +432,49 @@ export declare interface TracerOptions {
|
|
|
424
432
|
};
|
|
425
433
|
}
|
|
426
434
|
|
|
435
|
+
/**
|
|
436
|
+
* User object that can be passed to `tracer.setUser()`.
|
|
437
|
+
*/
|
|
438
|
+
export declare interface User {
|
|
439
|
+
/**
|
|
440
|
+
* Unique identifier of the user.
|
|
441
|
+
* Mandatory.
|
|
442
|
+
*/
|
|
443
|
+
id: string,
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Email of the user.
|
|
447
|
+
*/
|
|
448
|
+
email?: string,
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* User-friendly name of the user.
|
|
452
|
+
*/
|
|
453
|
+
name?: string,
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Session ID of the user.
|
|
457
|
+
*/
|
|
458
|
+
session_id?: string,
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Role the user is making the request under.
|
|
462
|
+
*/
|
|
463
|
+
role?: string,
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Scopes or granted authorizations the user currently possesses.
|
|
467
|
+
* The value could come from the scope associated with an OAuth2
|
|
468
|
+
* Access Token or an attribute value in a SAML 2 Assertion.
|
|
469
|
+
*/
|
|
470
|
+
scope?: string,
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Custom fields to attach to the user (RBAC, Oauth, etc…).
|
|
474
|
+
*/
|
|
475
|
+
[key: string]: string | undefined
|
|
476
|
+
}
|
|
477
|
+
|
|
427
478
|
/** @hidden */
|
|
428
479
|
interface EventEmitter {
|
|
429
480
|
emit(eventName: string | symbol, ...args: any[]): any;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dd-trace",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "Datadog APM tracing client for JavaScript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"typings": "index.d.ts",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"node": ">=12"
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"@datadog/native-appsec": "^0.8.
|
|
64
|
+
"@datadog/native-appsec": "^0.8.2",
|
|
65
65
|
"@datadog/native-metrics": "^1.1.0",
|
|
66
66
|
"@datadog/pprof": "^0.3.0",
|
|
67
67
|
"@datadog/sketches-js": "^1.0.4",
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
require('./src/amqplib')
|
|
4
|
+
require('./src/amqp10')
|
|
3
5
|
require('./src/bluebird')
|
|
4
6
|
require('./src/bunyan')
|
|
7
|
+
require('./src/cassandra-driver')
|
|
5
8
|
require('./src/couchbase')
|
|
6
9
|
require('./src/cucumber')
|
|
7
10
|
require('./src/dns')
|
|
@@ -9,14 +12,19 @@ require('./src/elasticsearch')
|
|
|
9
12
|
require('./src/generic-pool')
|
|
10
13
|
require('./src/ioredis')
|
|
11
14
|
require('./src/memcached')
|
|
15
|
+
require('./src/mongodb-core')
|
|
12
16
|
require('./src/mongoose')
|
|
13
17
|
require('./src/mysql')
|
|
14
18
|
require('./src/mysql2')
|
|
19
|
+
require('./src/mocha')
|
|
15
20
|
require('./src/pino')
|
|
21
|
+
require('./src/pg')
|
|
16
22
|
require('./src/promise')
|
|
17
23
|
require('./src/promise-js')
|
|
18
24
|
require('./src/q')
|
|
19
25
|
require('./src/redis')
|
|
26
|
+
require('./src/rhea')
|
|
20
27
|
require('./src/sharedb')
|
|
28
|
+
require('./src/tedious')
|
|
21
29
|
require('./src/when')
|
|
22
30
|
require('./src/winston')
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
channel,
|
|
5
|
+
addHook,
|
|
6
|
+
AsyncResource
|
|
7
|
+
} = require('./helpers/instrument')
|
|
8
|
+
const shimmer = require('../../datadog-shimmer')
|
|
9
|
+
|
|
10
|
+
addHook({ name: 'amqp10', file: 'lib/sender_link.js', versions: ['>=3'] }, SenderLink => {
|
|
11
|
+
const startCh = channel('apm:amqp10:send:start')
|
|
12
|
+
const asyncEndCh = channel('apm:amqp10:send:async-end')
|
|
13
|
+
const endCh = channel('apm:amqp10:send:end')
|
|
14
|
+
const errorCh = channel('apm:amqp10:send:error')
|
|
15
|
+
shimmer.wrap(SenderLink.prototype, 'send', send => function (msg, options) {
|
|
16
|
+
if (!startCh.hasSubscribers) {
|
|
17
|
+
return send.apply(this, arguments)
|
|
18
|
+
}
|
|
19
|
+
startCh.publish({ link: this })
|
|
20
|
+
try {
|
|
21
|
+
const promise = send.apply(this, arguments)
|
|
22
|
+
|
|
23
|
+
if (!promise) {
|
|
24
|
+
finish(asyncEndCh, errorCh)
|
|
25
|
+
return promise
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
29
|
+
|
|
30
|
+
promise.then(asyncResource.bind(() => finish(asyncEndCh, errorCh)),
|
|
31
|
+
asyncResource.bind(e => finish(asyncEndCh, errorCh, e)))
|
|
32
|
+
|
|
33
|
+
return promise
|
|
34
|
+
} catch (err) {
|
|
35
|
+
finish(asyncEndCh, errorCh, err)
|
|
36
|
+
throw err
|
|
37
|
+
} finally {
|
|
38
|
+
endCh.publish(undefined)
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
return SenderLink
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
addHook({ name: 'amqp10', file: 'lib/receiver_link.js', versions: ['>=3'] }, ReceiverLink => {
|
|
45
|
+
const startCh = channel('apm:amqp10:receive:start')
|
|
46
|
+
const endCh = channel('apm:amqp10:receive:end')
|
|
47
|
+
const errorCh = channel('apm:amqp10:receive:error')
|
|
48
|
+
shimmer.wrap(ReceiverLink.prototype, '_messageReceived', messageReceived => function (transferFrame) {
|
|
49
|
+
if (!transferFrame || transferFrame.aborted || transferFrame.more) {
|
|
50
|
+
return messageReceived.apply(this, arguments)
|
|
51
|
+
}
|
|
52
|
+
startCh.publish({ link: this })
|
|
53
|
+
try {
|
|
54
|
+
return messageReceived.apply(this, arguments)
|
|
55
|
+
} catch (err) {
|
|
56
|
+
errorCh.publish(err)
|
|
57
|
+
throw err
|
|
58
|
+
} finally {
|
|
59
|
+
endCh.publish(undefined)
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
return ReceiverLink
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
function finish (asyncEndCh, errorCh, error) {
|
|
66
|
+
if (error) {
|
|
67
|
+
errorCh.publish(error)
|
|
68
|
+
}
|
|
69
|
+
asyncEndCh.publish(undefined)
|
|
70
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
channel,
|
|
5
|
+
addHook
|
|
6
|
+
} = require('./helpers/instrument')
|
|
7
|
+
const kebabCase = require('lodash.kebabcase')
|
|
8
|
+
const shimmer = require('../../datadog-shimmer')
|
|
9
|
+
|
|
10
|
+
const startCh = channel('apm:amqplib:command:start')
|
|
11
|
+
const endCh = channel('apm:amqplib:command:end')
|
|
12
|
+
const errorCh = channel('apm:amqplib:command:error')
|
|
13
|
+
|
|
14
|
+
let methods = {}
|
|
15
|
+
|
|
16
|
+
addHook({ name: 'amqplib', file: 'lib/defs.js', versions: ['>=0.5'] }, defs => {
|
|
17
|
+
methods = Object.keys(defs)
|
|
18
|
+
.filter(key => Number.isInteger(defs[key]))
|
|
19
|
+
.filter(key => isCamelCase(key))
|
|
20
|
+
.reduce((acc, key) => Object.assign(acc, { [defs[key]]: kebabCase(key).replace('-', '.') }), {})
|
|
21
|
+
return defs
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
addHook({ name: 'amqplib', file: 'lib/channel.js', versions: ['>=0.5'] }, channel => {
|
|
25
|
+
shimmer.wrap(channel.Channel.prototype, 'sendImmediately', sendImmediately => function (method, fields) {
|
|
26
|
+
return instrument(sendImmediately, this, arguments, methods[method], fields)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
shimmer.wrap(channel.Channel.prototype, 'sendMessage', sendMessage => function (fields) {
|
|
30
|
+
return instrument(sendMessage, this, arguments, 'basic.publish', fields)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
shimmer.wrap(channel.BaseChannel.prototype, 'dispatchMessage', dispatchMessage => function (fields, message) {
|
|
34
|
+
return instrument(dispatchMessage, this, arguments, 'basic.deliver', fields, message)
|
|
35
|
+
})
|
|
36
|
+
return channel
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
function instrument (send, channel, args, method, fields, message) {
|
|
40
|
+
if (!startCh.hasSubscribers) {
|
|
41
|
+
return send.apply(this, arguments)
|
|
42
|
+
}
|
|
43
|
+
startCh.publish({ channel, method, fields, message })
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
return send.apply(channel, args)
|
|
47
|
+
} catch (err) {
|
|
48
|
+
errorCh.publish(err)
|
|
49
|
+
|
|
50
|
+
throw err
|
|
51
|
+
} finally {
|
|
52
|
+
endCh.publish(undefined)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function isCamelCase (str) {
|
|
57
|
+
return /([A-Z][a-z0-9]+)+/.test(str)
|
|
58
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
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:cassandra:query:start')
|
|
11
|
+
const asyncEndCh = channel('apm:cassandra:query:async-end')
|
|
12
|
+
const endCh = channel('apm:cassandra:query:end')
|
|
13
|
+
const errorCh = channel('apm:cassandra:query:error')
|
|
14
|
+
const addConnectionCh = channel(`apm:cassandra:query:addConnection`)
|
|
15
|
+
|
|
16
|
+
addHook({ name: 'cassandra-driver', versions: ['>=3.0.0'] }, cassandra => {
|
|
17
|
+
shimmer.wrap(cassandra.Client.prototype, 'batch', batch => function (queries, options, callback) {
|
|
18
|
+
if (!startCh.hasSubscribers) {
|
|
19
|
+
return batch.apply(this, arguments)
|
|
20
|
+
}
|
|
21
|
+
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
22
|
+
startCh.publish({ keyspace: this.keyspace, query: queries })
|
|
23
|
+
|
|
24
|
+
const lastIndex = arguments.length - 1
|
|
25
|
+
let cb = arguments[lastIndex]
|
|
26
|
+
|
|
27
|
+
if (typeof cb === 'function') {
|
|
28
|
+
cb = asyncResource.bind(cb)
|
|
29
|
+
arguments[lastIndex] = wrapCallback(asyncEndCh, errorCh, cb)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const res = batch.apply(this, arguments)
|
|
34
|
+
if (typeof res === 'function' || !res) {
|
|
35
|
+
return wrapCallback(asyncEndCh, errorCh, res)
|
|
36
|
+
} else {
|
|
37
|
+
const promiseAsyncResource = new AsyncResource('bound-anonymous-fn')
|
|
38
|
+
return res.then(
|
|
39
|
+
promiseAsyncResource.bind(() => finish(asyncEndCh, errorCh)),
|
|
40
|
+
promiseAsyncResource.bind(err => finish(asyncEndCh, errorCh, err))
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
} catch (e) {
|
|
44
|
+
finish(asyncEndCh, errorCh, e)
|
|
45
|
+
throw e
|
|
46
|
+
} finally {
|
|
47
|
+
endCh.publish(undefined)
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
return cassandra
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
addHook({ name: 'cassandra-driver', versions: ['>=4.4'] }, cassandra => {
|
|
54
|
+
shimmer.wrap(cassandra.Client.prototype, '_execute', _execute => function (query, params, execOptions, callback) {
|
|
55
|
+
if (!startCh.hasSubscribers) {
|
|
56
|
+
return _execute.apply(this, arguments)
|
|
57
|
+
}
|
|
58
|
+
startCh.publish({ keyspace: this.keyspace, query })
|
|
59
|
+
const promise = _execute.apply(this, arguments)
|
|
60
|
+
|
|
61
|
+
const promiseAsyncResource = new AsyncResource('bound-anonymous-fn')
|
|
62
|
+
|
|
63
|
+
promise.then(
|
|
64
|
+
promiseAsyncResource.bind(() => finish(asyncEndCh, errorCh)),
|
|
65
|
+
promiseAsyncResource.bind(err => finish(asyncEndCh, errorCh, err))
|
|
66
|
+
)
|
|
67
|
+
endCh.publish(undefined)
|
|
68
|
+
return promise
|
|
69
|
+
})
|
|
70
|
+
return cassandra
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
addHook({ name: 'cassandra-driver', versions: ['3 - 4.3'] }, cassandra => {
|
|
74
|
+
shimmer.wrap(cassandra.Client.prototype, '_innerExecute', _innerExecute =>
|
|
75
|
+
function (query, params, execOptions, callback) {
|
|
76
|
+
if (!startCh.hasSubscribers) {
|
|
77
|
+
return _innerExecute.apply(this, arguments)
|
|
78
|
+
}
|
|
79
|
+
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
80
|
+
const isValid = (args) => {
|
|
81
|
+
return args.length === 4 || typeof args[3] === 'function'
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!isValid(arguments)) {
|
|
85
|
+
return _innerExecute.apply(this, arguments)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
startCh.publish({ keyspace: this.keyspace, query })
|
|
89
|
+
|
|
90
|
+
const lastIndex = arguments.length - 1
|
|
91
|
+
let cb = arguments[lastIndex]
|
|
92
|
+
|
|
93
|
+
if (typeof cb === 'function') {
|
|
94
|
+
cb = asyncResource.bind(cb)
|
|
95
|
+
arguments[lastIndex] = wrapCallback(asyncEndCh, errorCh, cb)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
return _innerExecute.apply(this, arguments)
|
|
100
|
+
} catch (e) {
|
|
101
|
+
finish(asyncEndCh, errorCh, e)
|
|
102
|
+
throw e
|
|
103
|
+
} finally {
|
|
104
|
+
endCh.publish(undefined)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
return cassandra
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
addHook({ name: 'cassandra-driver', versions: ['>=3.3'], file: 'lib/request-execution.js' }, RequestExecution => {
|
|
112
|
+
shimmer.wrap(RequestExecution.prototype, '_sendOnConnection', _sendOnConnection => function () {
|
|
113
|
+
if (!startCh.hasSubscribers) {
|
|
114
|
+
return _sendOnConnection.apply(this, arguments)
|
|
115
|
+
}
|
|
116
|
+
addConnectionCh.publish({ address: this._connection.address, port: this._connection.port })
|
|
117
|
+
return _sendOnConnection.apply(this, arguments)
|
|
118
|
+
})
|
|
119
|
+
return RequestExecution
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
addHook({ name: 'cassandra-driver', versions: ['3.3 - 4.3'], file: 'lib/request-execution.js' }, RequestExecution => {
|
|
123
|
+
shimmer.wrap(RequestExecution.prototype, 'start', start => function (getHostCallback) {
|
|
124
|
+
if (!startCh.hasSubscribers) {
|
|
125
|
+
return getHostCallback.apply(this, arguments)
|
|
126
|
+
}
|
|
127
|
+
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
128
|
+
const execution = this
|
|
129
|
+
|
|
130
|
+
if (!isRequestValid(this, arguments, 1)) {
|
|
131
|
+
return start.apply(this, arguments)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
getHostCallback = asyncResource.bind(getHostCallback)
|
|
135
|
+
|
|
136
|
+
arguments[0] = AsyncResource.bind(function () {
|
|
137
|
+
addConnectionCh.publish({ address: execution._connection.address, port: execution._connection.port })
|
|
138
|
+
return getHostCallback.apply(this, arguments)
|
|
139
|
+
})
|
|
140
|
+
|
|
141
|
+
return start.apply(this, arguments)
|
|
142
|
+
})
|
|
143
|
+
return RequestExecution
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
addHook({ name: 'cassandra-driver', versions: ['3 - 3.2'], file: 'lib/request-handler.js' }, RequestHandler => {
|
|
147
|
+
shimmer.wrap(RequestHandler.prototype, 'send', send => function (request, options, callback) {
|
|
148
|
+
if (!startCh.hasSubscribers) {
|
|
149
|
+
return send.apply(this, arguments)
|
|
150
|
+
}
|
|
151
|
+
const handler = this
|
|
152
|
+
|
|
153
|
+
if (!isRequestValid(this, arguments, 3)) {
|
|
154
|
+
return send.apply(this, arguments)
|
|
155
|
+
}
|
|
156
|
+
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
157
|
+
|
|
158
|
+
callback = asyncResource.bind(callback)
|
|
159
|
+
|
|
160
|
+
arguments[2] = AsyncResource.bind(function () {
|
|
161
|
+
addConnectionCh.publish({ address: handler.connection.address, port: handler.connection.port })
|
|
162
|
+
return callback.apply(this, arguments)
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
return send.apply(this, arguments)
|
|
166
|
+
})
|
|
167
|
+
return RequestHandler
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
function finish (asyncEndCh, errorCh, error) {
|
|
171
|
+
if (error) {
|
|
172
|
+
errorCh.publish(error)
|
|
173
|
+
}
|
|
174
|
+
asyncEndCh.publish(undefined)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function wrapCallback (asyncEndCh, errorCh, callback) {
|
|
178
|
+
return AsyncResource.bind(function (err) {
|
|
179
|
+
finish(asyncEndCh, errorCh, err)
|
|
180
|
+
if (callback) {
|
|
181
|
+
return callback.apply(this, arguments)
|
|
182
|
+
}
|
|
183
|
+
})
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function isRequestValid (exec, args, length) {
|
|
187
|
+
if (!exec) return false
|
|
188
|
+
if (args.length !== length || typeof args[length - 1] !== 'function') return false
|
|
189
|
+
|
|
190
|
+
return true
|
|
191
|
+
}
|
|
@@ -55,6 +55,7 @@ function wrapRun (pl, isLatestVersion) {
|
|
|
55
55
|
return promise
|
|
56
56
|
} catch (err) {
|
|
57
57
|
errorCh.publish(err)
|
|
58
|
+
throw err
|
|
58
59
|
} finally {
|
|
59
60
|
runEndCh.publish(undefined)
|
|
60
61
|
}
|
|
@@ -85,6 +86,7 @@ function wrapRun (pl, isLatestVersion) {
|
|
|
85
86
|
return promise
|
|
86
87
|
} catch (err) {
|
|
87
88
|
errorCh.publish(err)
|
|
89
|
+
throw err
|
|
88
90
|
} finally {
|
|
89
91
|
runStepEndCh.publish(undefined)
|
|
90
92
|
}
|
|
@@ -20,11 +20,11 @@ exports.channel = function channel (name) {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
exports.addHook = function addHook ({ name, versions, file }, hook) {
|
|
23
|
-
|
|
23
|
+
const fullFilename = filename(name, file)
|
|
24
24
|
const loaderHook = (moduleExports, moduleName, moduleBaseDir) => {
|
|
25
25
|
moduleName = moduleName.replace(pathSepExpr, '/')
|
|
26
26
|
const moduleVersion = getVersion(moduleBaseDir)
|
|
27
|
-
if (moduleName !==
|
|
27
|
+
if (moduleName !== fullFilename || !matchVersion(moduleVersion, versions)) {
|
|
28
28
|
return moduleExports
|
|
29
29
|
}
|
|
30
30
|
return hook(moduleExports)
|
|
@@ -65,7 +65,7 @@ function cjsPostLoad (instrumentation, hook) {
|
|
|
65
65
|
if (!id.includes(`/node_modules/${instrumentation.name}/`)) continue
|
|
66
66
|
|
|
67
67
|
if (instrumentation.file) {
|
|
68
|
-
if (!id.endsWith(`/node_modules/${filename(instrumentation)}`)) continue
|
|
68
|
+
if (!id.endsWith(`/node_modules/${filename(instrumentation.name, instrumentation.file)}`)) continue
|
|
69
69
|
|
|
70
70
|
const basedir = getBasedir(ids[i])
|
|
71
71
|
|
|
@@ -0,0 +1,122 @@
|
|
|
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
|
+
function isRetry (test) {
|
|
15
|
+
return test._currentRetry !== undefined && test._currentRetry !== 0
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function getAllTestsInSuite (root) {
|
|
19
|
+
const tests = []
|
|
20
|
+
function getTests (suiteOrTest) {
|
|
21
|
+
suiteOrTest.tests.forEach(test => {
|
|
22
|
+
tests.push(test)
|
|
23
|
+
})
|
|
24
|
+
suiteOrTest.suites.forEach(suite => {
|
|
25
|
+
getTests(suite)
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
getTests(root)
|
|
29
|
+
return tests
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
addHook({
|
|
33
|
+
name: 'mocha',
|
|
34
|
+
versions: ['>=5.2.0'],
|
|
35
|
+
file: 'lib/runner.js'
|
|
36
|
+
}, (Runner) => {
|
|
37
|
+
shimmer.wrap(Runner.prototype, 'runTest', runTest => function () {
|
|
38
|
+
if (!testStartCh.hasSubscribers) {
|
|
39
|
+
return runTest.apply(this, arguments)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!isRetry(this.test)) {
|
|
43
|
+
testStartCh.publish(this.test)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
this.once('test end', AsyncResource.bind(() => {
|
|
47
|
+
let status
|
|
48
|
+
|
|
49
|
+
if (this.test.pending) {
|
|
50
|
+
status = 'skipped'
|
|
51
|
+
} else if (this.test.state !== 'failed' && !this.test.timedOut) {
|
|
52
|
+
status = 'pass'
|
|
53
|
+
} else {
|
|
54
|
+
status = 'fail'
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
testAsyncEndCh.publish(status)
|
|
58
|
+
}))
|
|
59
|
+
|
|
60
|
+
this.once('fail', AsyncResource.bind((test, err) => {
|
|
61
|
+
errorCh.publish(err)
|
|
62
|
+
}))
|
|
63
|
+
|
|
64
|
+
this.once('pending', AsyncResource.bind((test) => {
|
|
65
|
+
skipCh.publish(test)
|
|
66
|
+
}))
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
return runTest.apply(this, arguments)
|
|
70
|
+
} catch (err) {
|
|
71
|
+
errorCh.publish(err)
|
|
72
|
+
throw err
|
|
73
|
+
} finally {
|
|
74
|
+
testEndCh.publish(undefined)
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
shimmer.wrap(Runner.prototype, 'runTests', runTests => function () {
|
|
79
|
+
if (!suiteEndCh.hasSubscribers) {
|
|
80
|
+
return runTests.apply(this, arguments)
|
|
81
|
+
}
|
|
82
|
+
this.once('end', AsyncResource.bind(() => {
|
|
83
|
+
testRunEndCh.publish(undefined)
|
|
84
|
+
}))
|
|
85
|
+
runTests.apply(this, arguments)
|
|
86
|
+
const suite = arguments[0]
|
|
87
|
+
// We call `getAllTestsInSuite` with the root suite so every skipped test
|
|
88
|
+
// should already have an associated test span.
|
|
89
|
+
const tests = getAllTestsInSuite(suite)
|
|
90
|
+
suiteEndCh.publish(tests)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
shimmer.wrap(Runner.prototype, 'fail', fail => function (hook, error) {
|
|
94
|
+
if (!hookErrorCh.hasSubscribers) {
|
|
95
|
+
return fail.apply(this, arguments)
|
|
96
|
+
}
|
|
97
|
+
if (error && hook.ctx && hook.ctx.currentTest) {
|
|
98
|
+
error.message = `${hook.title}: ${error.message}`
|
|
99
|
+
hookErrorCh.publish({ test: hook.ctx.currentTest, error })
|
|
100
|
+
}
|
|
101
|
+
return fail.apply(this, arguments)
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
return Runner
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
addHook({
|
|
108
|
+
name: 'mocha-each',
|
|
109
|
+
versions: ['>=2.0.1']
|
|
110
|
+
}, (mochaEach) => {
|
|
111
|
+
return shimmer.wrap(mochaEach, function () {
|
|
112
|
+
const [params] = arguments
|
|
113
|
+
const { it, ...rest } = mochaEach.apply(this, arguments)
|
|
114
|
+
return {
|
|
115
|
+
it: function (name) {
|
|
116
|
+
parameterizedTestCh.publish({ name, params })
|
|
117
|
+
it.apply(this, arguments)
|
|
118
|
+
},
|
|
119
|
+
...rest
|
|
120
|
+
}
|
|
121
|
+
})
|
|
122
|
+
})
|