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.
Files changed (33) hide show
  1. package/index.d.ts +51 -0
  2. package/package.json +2 -2
  3. package/packages/datadog-instrumentations/index.js +8 -0
  4. package/packages/datadog-instrumentations/src/amqp10.js +70 -0
  5. package/packages/datadog-instrumentations/src/amqplib.js +58 -0
  6. package/packages/datadog-instrumentations/src/cassandra-driver.js +191 -0
  7. package/packages/datadog-instrumentations/src/cucumber.js +2 -0
  8. package/packages/datadog-instrumentations/src/helpers/instrument.js +3 -3
  9. package/packages/datadog-instrumentations/src/mocha.js +122 -0
  10. package/packages/datadog-instrumentations/src/mongodb-core.js +179 -0
  11. package/packages/datadog-instrumentations/src/pg.js +75 -0
  12. package/packages/datadog-instrumentations/src/rhea.js +224 -0
  13. package/packages/datadog-instrumentations/src/tedious.js +66 -0
  14. package/packages/datadog-plugin-amqp10/src/index.js +79 -122
  15. package/packages/datadog-plugin-amqplib/src/index.js +77 -142
  16. package/packages/datadog-plugin-cassandra-driver/src/index.js +52 -224
  17. package/packages/datadog-plugin-cucumber/src/index.js +3 -1
  18. package/packages/datadog-plugin-jest/src/jest-jasmine2.js +5 -3
  19. package/packages/datadog-plugin-mocha/src/index.js +96 -207
  20. package/packages/datadog-plugin-mongodb-core/src/index.js +119 -3
  21. package/packages/datadog-plugin-pg/src/index.js +32 -69
  22. package/packages/datadog-plugin-rhea/src/index.js +59 -225
  23. package/packages/datadog-plugin-tedious/src/index.js +38 -86
  24. package/packages/dd-trace/lib/version.js +1 -1
  25. package/packages/dd-trace/src/appsec/recommended.json +137 -116
  26. package/packages/dd-trace/src/config.js +6 -0
  27. package/packages/dd-trace/src/noop/tracer.js +4 -0
  28. package/packages/dd-trace/src/opentracing/propagation/text_map.js +34 -1
  29. package/packages/dd-trace/src/proxy.js +4 -0
  30. package/packages/dd-trace/src/tracer.js +16 -0
  31. package/packages/datadog-plugin-mongodb-core/src/legacy.js +0 -59
  32. package/packages/datadog-plugin-mongodb-core/src/unified.js +0 -138
  33. package/packages/datadog-plugin-mongodb-core/src/util.js +0 -143
@@ -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
+ }
@@ -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
+ }
@@ -0,0 +1,66 @@
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: 'tedious', versions: [ '>=1.0.0' ] }, tedious => {
11
+ const startCh = channel('apm:tedious:request:start')
12
+ const asyncEndCh = channel('apm:tedious:request:async-end')
13
+ const endCh = channel('apm:tedious:request:end')
14
+ const errorCh = channel('apm:tedious:request:error')
15
+ shimmer.wrap(tedious.Connection.prototype, 'makeRequest', makeRequest => function (request) {
16
+ if (!startCh.hasSubscribers) {
17
+ return request.apply(this, arguments)
18
+ }
19
+
20
+ const queryOrProcedure = getQueryOrProcedure(request)
21
+
22
+ if (!queryOrProcedure) {
23
+ return makeRequest.apply(this, arguments)
24
+ }
25
+
26
+ const asyncResource = new AsyncResource('bound-anonymous-fn')
27
+
28
+ const connectionConfig = this.config
29
+
30
+ startCh.publish({ queryOrProcedure, connectionConfig })
31
+
32
+ const cb = asyncResource.bind(request.callback, request)
33
+ request.callback = AsyncResource.bind(function (error) {
34
+ if (error) {
35
+ errorCh.publish(error)
36
+ }
37
+ asyncEndCh.publish(undefined)
38
+
39
+ return cb.apply(this, arguments)
40
+ }, null, request)
41
+
42
+ try {
43
+ return makeRequest.apply(this, arguments)
44
+ } catch (error) {
45
+ errorCh.publish(error)
46
+
47
+ throw error
48
+ } finally {
49
+ endCh.publish(undefined)
50
+ }
51
+ })
52
+
53
+ return tedious
54
+ })
55
+
56
+ function getQueryOrProcedure (request) {
57
+ if (!request.parameters) return
58
+
59
+ const statement = request.parametersByName.statement || request.parametersByName.stmt
60
+
61
+ if (!statement) {
62
+ return request.sqlTextOrProcedure
63
+ }
64
+
65
+ return statement.value
66
+ }