dd-trace 2.2.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 +57 -0
- package/package.json +2 -2
- package/packages/datadog-instrumentations/index.js +9 -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 +118 -0
- package/packages/datadog-instrumentations/src/elasticsearch.js +9 -4
- package/packages/datadog-instrumentations/src/helpers/instrument.js +3 -3
- package/packages/datadog-instrumentations/src/ioredis.js +8 -5
- 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/redis.js +8 -8
- 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-aws-sdk/src/services/sns.js +2 -2
- package/packages/datadog-plugin-cassandra-driver/src/index.js +52 -224
- package/packages/datadog-plugin-cucumber/src/index.js +85 -128
- 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 +13 -1
- 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/tracer.js +1 -1
- package/packages/dd-trace/src/plugins/log_plugin.js +10 -5
- package/packages/dd-trace/src/plugins/util/git.js +1 -1
- package/packages/dd-trace/src/proxy.js +4 -0
- package/packages/dd-trace/src/span_processor.js +22 -7
- 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
|
@@ -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
|
+
})
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
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:pg:query:start')
|
|
11
|
+
const asyncEndCh = channel('apm:pg:query:async-end')
|
|
12
|
+
const endCh = channel('apm:pg:query:end')
|
|
13
|
+
const errorCh = channel('apm:pg:query:error')
|
|
14
|
+
|
|
15
|
+
addHook({ name: 'pg', versions: ['>=4'] }, pg => {
|
|
16
|
+
shimmer.wrap(pg.Client.prototype, 'query', query => wrapQuery(query))
|
|
17
|
+
return pg
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
addHook({ name: 'pg', file: 'lib/native/index.js', versions: ['>=4'] }, Client => {
|
|
21
|
+
shimmer.wrap(Client.prototype, 'query', query => wrapQuery(query))
|
|
22
|
+
return Client
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
function wrapQuery (query) {
|
|
26
|
+
return function () {
|
|
27
|
+
if (!startCh.hasSubscribers) {
|
|
28
|
+
return query.apply(this, arguments)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
32
|
+
|
|
33
|
+
const retval = query.apply(this, arguments)
|
|
34
|
+
|
|
35
|
+
const queryQueue = this.queryQueue || this._queryQueue
|
|
36
|
+
const activeQuery = this.activeQuery || this._activeQuery
|
|
37
|
+
const pgQuery = queryQueue[queryQueue.length - 1] || activeQuery
|
|
38
|
+
|
|
39
|
+
if (!pgQuery) {
|
|
40
|
+
return retval
|
|
41
|
+
}
|
|
42
|
+
const statement = pgQuery.text
|
|
43
|
+
|
|
44
|
+
startCh.publish({ params: this.connectionParameters, statement })
|
|
45
|
+
|
|
46
|
+
const finish = AsyncResource.bind(function (error) {
|
|
47
|
+
if (error) {
|
|
48
|
+
errorCh.publish(error)
|
|
49
|
+
}
|
|
50
|
+
asyncEndCh.publish(undefined)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
if (pgQuery.callback) {
|
|
54
|
+
const originalCallback = asyncResource.bind(pgQuery.callback)
|
|
55
|
+
pgQuery.callback = function (err, res) {
|
|
56
|
+
finish(err)
|
|
57
|
+
return originalCallback.apply(this, arguments)
|
|
58
|
+
}
|
|
59
|
+
} else if (pgQuery.once) {
|
|
60
|
+
pgQuery
|
|
61
|
+
.once('error', finish)
|
|
62
|
+
.once('end', () => finish())
|
|
63
|
+
} else {
|
|
64
|
+
pgQuery.then(() => finish(), finish)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
return retval
|
|
69
|
+
} catch (err) {
|
|
70
|
+
errorCh.publish(err)
|
|
71
|
+
} finally {
|
|
72
|
+
endCh.publish(undefined)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -21,14 +21,14 @@ addHook({ name: '@node-redis/client', file: 'dist/lib/client/commands-queue.js',
|
|
|
21
21
|
const name = command[0]
|
|
22
22
|
const args = command.slice(1)
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
start(this, name, args)
|
|
25
25
|
|
|
26
26
|
const res = addCommand.apply(this, arguments)
|
|
27
|
+
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
28
|
+
const onResolve = asyncResource.bind(() => finish(asyncEndCh, errorCh))
|
|
29
|
+
const onReject = asyncResource.bind(err => finish(asyncEndCh, errorCh, err))
|
|
27
30
|
|
|
28
|
-
res.then(
|
|
29
|
-
() => finish(asyncEndCh, errorCh),
|
|
30
|
-
err => finish(asyncEndCh, errorCh, err)
|
|
31
|
-
)
|
|
31
|
+
res.then(onResolve, onReject)
|
|
32
32
|
endCh.publish(undefined)
|
|
33
33
|
return res
|
|
34
34
|
})
|
|
@@ -45,7 +45,7 @@ addHook({ name: 'redis', versions: ['>=2.6 <4'] }, redis => {
|
|
|
45
45
|
|
|
46
46
|
const cb = asyncResource.bind(options.callback)
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
start(this, options.command, options.args)
|
|
49
49
|
|
|
50
50
|
options.callback = AsyncResource.bind(wrapCallback(asyncEndCh, errorCh, cb))
|
|
51
51
|
|
|
@@ -70,7 +70,7 @@ addHook({ name: 'redis', versions: ['>=0.12 <2.6'] }, redis => {
|
|
|
70
70
|
|
|
71
71
|
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
start(this, command, args)
|
|
74
74
|
|
|
75
75
|
if (typeof callback === 'function') {
|
|
76
76
|
const cb = asyncResource.bind(callback)
|
|
@@ -95,7 +95,7 @@ addHook({ name: 'redis', versions: ['>=0.12 <2.6'] }, redis => {
|
|
|
95
95
|
return redis
|
|
96
96
|
})
|
|
97
97
|
|
|
98
|
-
function
|
|
98
|
+
function start (client, command, args) {
|
|
99
99
|
const db = client.selected_db
|
|
100
100
|
const connectionOptions = client.connection_options || client.connection_option || client.connectionOption || {}
|
|
101
101
|
startCh.publish({ db, command, args, connectionOptions })
|
|
@@ -0,0 +1,224 @@
|
|
|
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 circularBufferConstructor = Symbol('circularBufferConstructor')
|
|
11
|
+
const inFlightDeliveries = Symbol('inFlightDeliveries')
|
|
12
|
+
|
|
13
|
+
const patched = new WeakSet()
|
|
14
|
+
const dispatchCh = channel('apm:rhea:dispatch')
|
|
15
|
+
const errorCh = channel('apm:rhea:error')
|
|
16
|
+
const asyncEndCh = channel('apm:rhea:async-end')
|
|
17
|
+
const endCh = channel('apm:rhea:end')
|
|
18
|
+
|
|
19
|
+
const contexts = new WeakMap()
|
|
20
|
+
|
|
21
|
+
addHook({ name: 'rhea', versions: ['>=1'], file: 'lib/link.js' }, obj => {
|
|
22
|
+
const startSendCh = channel('apm:rhea:send:start')
|
|
23
|
+
const startReceiveCh = channel('apm:rhea:receive:start')
|
|
24
|
+
|
|
25
|
+
const Sender = obj.Sender
|
|
26
|
+
const Receiver = obj.Receiver
|
|
27
|
+
shimmer.wrap(Sender.prototype, 'send', send => function (msg, tag, format) {
|
|
28
|
+
if (!canTrace(this)) {
|
|
29
|
+
// we can't handle disconnects or ending spans, so we can't safely instrument
|
|
30
|
+
return send.apply(this, arguments)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const { host, port } = getHostAndPort(this.connection)
|
|
34
|
+
|
|
35
|
+
const targetAddress = this.options && this.options.target &&
|
|
36
|
+
this.options.target.address ? this.options.target.address : undefined
|
|
37
|
+
|
|
38
|
+
startSendCh.publish({ targetAddress, host, port, msg })
|
|
39
|
+
const delivery = send.apply(this, arguments)
|
|
40
|
+
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
41
|
+
const context = {
|
|
42
|
+
asyncResource
|
|
43
|
+
}
|
|
44
|
+
contexts.set(delivery, context)
|
|
45
|
+
|
|
46
|
+
addToInFlightDeliveries(this.connection, delivery)
|
|
47
|
+
try {
|
|
48
|
+
return delivery
|
|
49
|
+
} catch (err) {
|
|
50
|
+
errorCh.publish(err)
|
|
51
|
+
|
|
52
|
+
throw err
|
|
53
|
+
} finally {
|
|
54
|
+
endCh.publish(undefined)
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
shimmer.wrap(Receiver.prototype, 'dispatch', dispatch => function (eventName, msgObj) {
|
|
59
|
+
if (!canTrace(this)) {
|
|
60
|
+
// we can't handle disconnects or ending spans, so we can't safely instrument
|
|
61
|
+
return dispatch.apply(this, arguments)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (eventName === 'message' && msgObj) {
|
|
65
|
+
startReceiveCh.publish({ msgObj, connection: this.connection })
|
|
66
|
+
|
|
67
|
+
if (msgObj.delivery) {
|
|
68
|
+
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
69
|
+
const context = {
|
|
70
|
+
asyncResource
|
|
71
|
+
}
|
|
72
|
+
contexts.set(msgObj.delivery, context)
|
|
73
|
+
msgObj.delivery.update = wrapDeliveryUpdate(msgObj.delivery, msgObj.delivery.update)
|
|
74
|
+
addToInFlightDeliveries(this.connection, msgObj.delivery)
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
return dispatch.apply(this, arguments)
|
|
78
|
+
} catch (err) {
|
|
79
|
+
errorCh.publish(err)
|
|
80
|
+
|
|
81
|
+
throw err
|
|
82
|
+
} finally {
|
|
83
|
+
endCh.publish(undefined)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return dispatch.apply(this, arguments)
|
|
88
|
+
})
|
|
89
|
+
return obj
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
addHook({ name: 'rhea', versions: ['>=1'], file: 'lib/connection.js' }, Connection => {
|
|
93
|
+
shimmer.wrap(Connection.prototype, 'dispatch', dispatch => function (eventName, obj) {
|
|
94
|
+
if (eventName === 'disconnected') {
|
|
95
|
+
const error = obj.error || this.saved_error
|
|
96
|
+
if (this[inFlightDeliveries]) {
|
|
97
|
+
this[inFlightDeliveries].forEach(delivery => {
|
|
98
|
+
const context = contexts.get(delivery)
|
|
99
|
+
const asyncResource = context.asyncResource
|
|
100
|
+
asyncResource.runInAsyncScope(() => {
|
|
101
|
+
errorCh.publish(error)
|
|
102
|
+
finish(delivery, null)
|
|
103
|
+
})
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return dispatch.apply(this, arguments)
|
|
108
|
+
})
|
|
109
|
+
return Connection
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
addHook({ name: 'rhea', versions: ['>=1'], file: 'lib/session.js' }, (Session) => {
|
|
113
|
+
patchCircularBuffer(Session.prototype, Session)
|
|
114
|
+
return Session
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
function canTrace (link) {
|
|
118
|
+
return link.connection && link.session && link.session.outgoing
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function getHostAndPort (connection) {
|
|
122
|
+
let host
|
|
123
|
+
let port
|
|
124
|
+
if (connection && connection.options) {
|
|
125
|
+
host = connection.options.host
|
|
126
|
+
port = connection.options.port
|
|
127
|
+
}
|
|
128
|
+
return { host, port }
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function wrapDeliveryUpdate (obj, update) {
|
|
132
|
+
const context = contexts.get(obj)
|
|
133
|
+
const asyncResource = context.asyncResource
|
|
134
|
+
if (obj && asyncResource) {
|
|
135
|
+
const cb = asyncResource.bind(update)
|
|
136
|
+
return AsyncResource.bind(function wrappedUpdate (settled, stateData) {
|
|
137
|
+
const state = getStateFromData(stateData)
|
|
138
|
+
dispatchCh.publish({ state })
|
|
139
|
+
return cb.apply(this, arguments)
|
|
140
|
+
})
|
|
141
|
+
}
|
|
142
|
+
return function wrappedUpdate (settled, stateData) {
|
|
143
|
+
return update.apply(this, arguments)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function patchCircularBuffer (proto, Session) {
|
|
148
|
+
Object.defineProperty(proto, 'outgoing', {
|
|
149
|
+
configurable: true,
|
|
150
|
+
get () {},
|
|
151
|
+
set (outgoing) {
|
|
152
|
+
delete proto.outgoing // removes the setter on the prototype
|
|
153
|
+
this.outgoing = outgoing // assigns on the instance, like normal
|
|
154
|
+
if (outgoing) {
|
|
155
|
+
let CircularBuffer
|
|
156
|
+
if (outgoing.deliveries) {
|
|
157
|
+
CircularBuffer = outgoing.deliveries.constructor
|
|
158
|
+
}
|
|
159
|
+
if (CircularBuffer && !patched.has(CircularBuffer.prototype)) {
|
|
160
|
+
shimmer.wrap(CircularBuffer.prototype, 'pop_if', popIf => function (fn) {
|
|
161
|
+
arguments[0] = AsyncResource.bind(function (entry) {
|
|
162
|
+
const context = contexts.get(entry)
|
|
163
|
+
const asyncResource = context.asyncResource
|
|
164
|
+
let shouldPop
|
|
165
|
+
if (asyncResource) {
|
|
166
|
+
fn = asyncResource.bind(fn, this)
|
|
167
|
+
shouldPop = fn(entry)
|
|
168
|
+
if (shouldPop && asyncResource) {
|
|
169
|
+
const remoteState = entry.remote_state
|
|
170
|
+
const state = remoteState && remoteState.constructor
|
|
171
|
+
? entry.remote_state.constructor.composite_type : undefined
|
|
172
|
+
asyncResource.runInAsyncScope(() => {
|
|
173
|
+
return finish(entry, state)
|
|
174
|
+
})
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return shouldPop
|
|
179
|
+
})
|
|
180
|
+
return popIf.apply(this, arguments)
|
|
181
|
+
})
|
|
182
|
+
patched.add(CircularBuffer.prototype)
|
|
183
|
+
const Session = proto.constructor
|
|
184
|
+
if (Session) {
|
|
185
|
+
Session[circularBufferConstructor] = CircularBuffer
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
})
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function addToInFlightDeliveries (connection, delivery) {
|
|
194
|
+
let deliveries = connection[inFlightDeliveries]
|
|
195
|
+
if (!deliveries) {
|
|
196
|
+
deliveries = new Set()
|
|
197
|
+
connection[inFlightDeliveries] = deliveries
|
|
198
|
+
}
|
|
199
|
+
deliveries.add(delivery)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function finish (delivery, state) {
|
|
203
|
+
const obj = contexts.get(delivery)
|
|
204
|
+
if (obj) {
|
|
205
|
+
if (state) {
|
|
206
|
+
dispatchCh.publish({ state })
|
|
207
|
+
}
|
|
208
|
+
asyncEndCh.publish(undefined)
|
|
209
|
+
if (obj.connection && obj.connection[inFlightDeliveries]) {
|
|
210
|
+
obj.connection[inFlightDeliveries].delete(delivery)
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function getStateFromData (stateData) {
|
|
216
|
+
if (stateData && stateData.descriptor && stateData.descriptor) {
|
|
217
|
+
switch (stateData.descriptor.value) {
|
|
218
|
+
case 0x24: return 'accepted'
|
|
219
|
+
case 0x25: return 'rejected'
|
|
220
|
+
case 0x26: return 'released'
|
|
221
|
+
case 0x27: return 'modified'
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|