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.
Files changed (53) hide show
  1. package/ci/init.js +26 -2
  2. package/index.d.ts +51 -0
  3. package/package.json +2 -2
  4. package/packages/datadog-instrumentations/index.js +10 -0
  5. package/packages/datadog-instrumentations/src/amqp10.js +70 -0
  6. package/packages/datadog-instrumentations/src/amqplib.js +58 -0
  7. package/packages/datadog-instrumentations/src/cassandra-driver.js +191 -0
  8. package/packages/datadog-instrumentations/src/cucumber.js +27 -12
  9. package/packages/datadog-instrumentations/src/helpers/hook.js +44 -0
  10. package/packages/datadog-instrumentations/src/helpers/instrument.js +31 -58
  11. package/packages/datadog-instrumentations/src/http/client.js +170 -0
  12. package/packages/datadog-instrumentations/src/http/server.js +61 -0
  13. package/packages/datadog-instrumentations/src/http.js +4 -0
  14. package/packages/datadog-instrumentations/src/mocha.js +139 -0
  15. package/packages/datadog-instrumentations/src/mongodb-core.js +179 -0
  16. package/packages/datadog-instrumentations/src/net.js +117 -0
  17. package/packages/datadog-instrumentations/src/pg.js +75 -0
  18. package/packages/datadog-instrumentations/src/rhea.js +224 -0
  19. package/packages/datadog-instrumentations/src/tedious.js +66 -0
  20. package/packages/datadog-plugin-amqp10/src/index.js +79 -122
  21. package/packages/datadog-plugin-amqplib/src/index.js +77 -142
  22. package/packages/datadog-plugin-cassandra-driver/src/index.js +52 -224
  23. package/packages/datadog-plugin-cucumber/src/index.js +3 -1
  24. package/packages/datadog-plugin-elasticsearch/src/index.js +4 -2
  25. package/packages/datadog-plugin-http/src/client.js +112 -252
  26. package/packages/datadog-plugin-http/src/index.js +29 -3
  27. package/packages/datadog-plugin-http/src/server.js +54 -32
  28. package/packages/datadog-plugin-jest/src/jest-environment.js +3 -3
  29. package/packages/datadog-plugin-jest/src/jest-jasmine2.js +5 -3
  30. package/packages/datadog-plugin-mocha/src/index.js +96 -207
  31. package/packages/datadog-plugin-mongodb-core/src/index.js +119 -3
  32. package/packages/datadog-plugin-net/src/index.js +65 -121
  33. package/packages/datadog-plugin-next/src/index.js +10 -10
  34. package/packages/datadog-plugin-pg/src/index.js +32 -69
  35. package/packages/datadog-plugin-rhea/src/index.js +59 -225
  36. package/packages/datadog-plugin-tedious/src/index.js +38 -86
  37. package/packages/dd-trace/lib/version.js +1 -1
  38. package/packages/dd-trace/src/appsec/recommended.json +235 -315
  39. package/packages/dd-trace/src/config.js +6 -0
  40. package/packages/dd-trace/src/iitm.js +5 -1
  41. package/packages/dd-trace/src/loader.js +6 -4
  42. package/packages/dd-trace/src/noop/tracer.js +4 -0
  43. package/packages/dd-trace/src/opentracing/propagation/text_map.js +34 -1
  44. package/packages/dd-trace/src/opentracing/span.js +34 -0
  45. package/packages/dd-trace/src/plugin_manager.js +4 -0
  46. package/packages/dd-trace/src/plugins/plugin.js +3 -1
  47. package/packages/dd-trace/src/plugins/util/web.js +99 -93
  48. package/packages/dd-trace/src/proxy.js +4 -0
  49. package/packages/dd-trace/src/ritm.js +60 -25
  50. package/packages/dd-trace/src/tracer.js +16 -0
  51. package/packages/datadog-plugin-mongodb-core/src/legacy.js +0 -59
  52. package/packages/datadog-plugin-mongodb-core/src/unified.js +0 -138
  53. package/packages/datadog-plugin-mongodb-core/src/util.js +0 -143
@@ -0,0 +1,117 @@
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 startICPCh = channel('apm:net:ipc:start')
11
+ const asyncICPEndCh = channel('apm:net:ipc:async-end')
12
+ const endICPCh = channel('apm:net:ipc:end')
13
+ const errorICPCh = channel('apm:net:ipc:error')
14
+
15
+ const startTCPCh = channel('apm:net:tcp:start')
16
+ const asyncTCPEndCh = channel('apm:net:tcp:async-end')
17
+ const endTCPCh = channel('apm:net:tcp:end')
18
+ const errorTCPCh = channel('apm:net:tcp:error')
19
+
20
+ const connectionCh = channel(`apm:net:tcp:connection`)
21
+
22
+ addHook({ name: 'net' }, net => {
23
+ require('dns')
24
+
25
+ shimmer.wrap(net.Socket.prototype, 'connect', connect => function () {
26
+ if (!startICPCh.hasSubscribers || !startTCPCh.hasSubscribers) {
27
+ return connect.apply(this, arguments)
28
+ }
29
+
30
+ const options = getOptions(arguments)
31
+ const lastIndex = arguments.length - 1
32
+ const callback = arguments[lastIndex]
33
+
34
+ if (!options) return connect.apply(this, arguments)
35
+
36
+ const asyncResource = new AsyncResource('bound-anonymous-fn')
37
+
38
+ if (typeof callback === 'function') {
39
+ arguments[lastIndex] = asyncResource.bind(callback)
40
+ }
41
+
42
+ const protocol = options.path ? 'ipc' : 'tcp'
43
+
44
+ if (protocol === 'ipc') {
45
+ startICPCh.publish({ options })
46
+ setupListeners(this, 'ipc', asyncResource)
47
+ } else {
48
+ startTCPCh.publish({ options })
49
+ setupListeners(this, 'tcp', asyncResource)
50
+ }
51
+
52
+ try {
53
+ return connect.apply(this, arguments)
54
+ } catch (err) {
55
+ protocol === 'ipc' ? errorICPCh.publish(err) : errorTCPCh.publish(err)
56
+
57
+ throw err
58
+ } finally {
59
+ protocol === 'ipc' ? endICPCh.publish(undefined) : endTCPCh.publish(undefined)
60
+ }
61
+ })
62
+
63
+ return net
64
+ })
65
+
66
+ function getOptions (args) {
67
+ if (!args[0]) return
68
+
69
+ switch (typeof args[0]) {
70
+ case 'object':
71
+ if (Array.isArray(args[0])) return getOptions(args[0])
72
+ return args[0]
73
+ case 'string':
74
+ if (isNaN(parseFloat(args[0]))) {
75
+ return {
76
+ path: args[0]
77
+ }
78
+ }
79
+ case 'number': // eslint-disable-line no-fallthrough
80
+ return {
81
+ port: args[0],
82
+ host: typeof args[1] === 'string' ? args[1] : 'localhost'
83
+ }
84
+ }
85
+ }
86
+
87
+ function setupListeners (socket, protocol, ar) {
88
+ const events = ['connect', 'error', 'close', 'timeout']
89
+
90
+ const wrapListener = AsyncResource.bind(function (error) {
91
+ if (error) {
92
+ protocol === 'ipc' ? errorICPCh.publish(error) : errorTCPCh.publish(error)
93
+ }
94
+ protocol === 'ipc' ? asyncICPEndCh.publish(undefined) : asyncTCPEndCh.publish(undefined)
95
+ })
96
+
97
+ const localListener = AsyncResource.bind(function () {
98
+ connectionCh.publish({ socket })
99
+ })
100
+
101
+ const cleanupListener = function () {
102
+ socket.removeListener('connect', localListener)
103
+ events.forEach(event => {
104
+ socket.removeListener(event, wrapListener)
105
+ socket.removeListener(event, cleanupListener)
106
+ })
107
+ }
108
+
109
+ if (protocol === 'tcp') {
110
+ socket.once('connect', localListener)
111
+ }
112
+
113
+ events.forEach(event => {
114
+ socket.once(event, wrapListener)
115
+ socket.once(event, cleanupListener)
116
+ })
117
+ }
@@ -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
+ }