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.
Files changed (41) hide show
  1. package/index.d.ts +57 -0
  2. package/package.json +2 -2
  3. package/packages/datadog-instrumentations/index.js +9 -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 +118 -0
  8. package/packages/datadog-instrumentations/src/elasticsearch.js +9 -4
  9. package/packages/datadog-instrumentations/src/helpers/instrument.js +3 -3
  10. package/packages/datadog-instrumentations/src/ioredis.js +8 -5
  11. package/packages/datadog-instrumentations/src/mocha.js +122 -0
  12. package/packages/datadog-instrumentations/src/mongodb-core.js +179 -0
  13. package/packages/datadog-instrumentations/src/pg.js +75 -0
  14. package/packages/datadog-instrumentations/src/redis.js +8 -8
  15. package/packages/datadog-instrumentations/src/rhea.js +224 -0
  16. package/packages/datadog-instrumentations/src/tedious.js +66 -0
  17. package/packages/datadog-plugin-amqp10/src/index.js +79 -122
  18. package/packages/datadog-plugin-amqplib/src/index.js +77 -142
  19. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +2 -2
  20. package/packages/datadog-plugin-cassandra-driver/src/index.js +52 -224
  21. package/packages/datadog-plugin-cucumber/src/index.js +85 -128
  22. package/packages/datadog-plugin-jest/src/jest-jasmine2.js +5 -3
  23. package/packages/datadog-plugin-mocha/src/index.js +96 -207
  24. package/packages/datadog-plugin-mongodb-core/src/index.js +119 -3
  25. package/packages/datadog-plugin-pg/src/index.js +32 -69
  26. package/packages/datadog-plugin-rhea/src/index.js +59 -225
  27. package/packages/datadog-plugin-tedious/src/index.js +38 -86
  28. package/packages/dd-trace/lib/version.js +1 -1
  29. package/packages/dd-trace/src/appsec/recommended.json +137 -116
  30. package/packages/dd-trace/src/config.js +13 -1
  31. package/packages/dd-trace/src/noop/tracer.js +4 -0
  32. package/packages/dd-trace/src/opentracing/propagation/text_map.js +34 -1
  33. package/packages/dd-trace/src/opentracing/tracer.js +1 -1
  34. package/packages/dd-trace/src/plugins/log_plugin.js +10 -5
  35. package/packages/dd-trace/src/plugins/util/git.js +1 -1
  36. package/packages/dd-trace/src/proxy.js +4 -0
  37. package/packages/dd-trace/src/span_processor.js +22 -7
  38. package/packages/dd-trace/src/tracer.js +16 -0
  39. package/packages/datadog-plugin-mongodb-core/src/legacy.js +0 -59
  40. package/packages/datadog-plugin-mongodb-core/src/unified.js +0 -138
  41. package/packages/datadog-plugin-mongodb-core/src/util.js +0 -143
@@ -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
+ }
@@ -1,120 +1,100 @@
1
1
  'use strict'
2
2
 
3
+ const Plugin = require('../../dd-trace/src/plugins/plugin')
4
+ const { storage } = require('../../datadog-core')
3
5
  const analyticsSampler = require('../../dd-trace/src/analytics_sampler')
4
6
 
5
- function createWrapSend (tracer, config) {
6
- return function wrapSend (send) {
7
- return function sendWithTrace (msg, options) {
8
- const span = startSendSpan(tracer, config, this)
9
-
10
- try {
11
- const promise = tracer.scope().activate(span, () => {
12
- return send.apply(this, arguments)
13
- })
14
-
15
- return wrapPromise(promise, span)
16
- } catch (e) {
17
- finish(span, e)
18
- throw e
19
- }
20
- }
7
+ class Amqp10Plugin extends Plugin {
8
+ static get name () {
9
+ return 'amqp10'
21
10
  }
22
- }
23
-
24
- function createWrapMessageReceived (tracer, config) {
25
- return function wrapMessageReceived (messageReceived) {
26
- return function messageReceivedWithTrace (transferFrame) {
27
- if (!transferFrame || transferFrame.aborted || transferFrame.more) {
28
- return messageReceived.apply(this, arguments)
29
- }
30
11
 
31
- const span = startReceiveSpan(tracer, config, this)
32
-
33
- return tracer.scope().activate(span, () => {
34
- messageReceived.apply(this, arguments)
35
- span.finish()
12
+ constructor (...args) {
13
+ super(...args)
14
+
15
+ this.addSub(`apm:amqp10:send:start`, ({ link }) => {
16
+ const address = getAddress(link)
17
+ const target = getShortName(link)
18
+
19
+ const store = storage.getStore()
20
+ const childOf = store ? store.span : store
21
+
22
+ const span = this.tracer.startSpan('amqp.send', {
23
+ childOf,
24
+ tags: {
25
+ 'resource.name': ['send', target].filter(v => v).join(' '),
26
+ 'span.kind': 'producer',
27
+ 'amqp.link.target.address': target,
28
+ 'amqp.link.role': 'sender',
29
+ 'out.host': address.host,
30
+ 'out.port': address.port,
31
+ 'service.name': this.config.service || `${this.tracer._service}-amqp`,
32
+ 'amqp.link.name': link.name,
33
+ 'amqp.link.handle': link.handle,
34
+ 'amqp.connection.host': address.host,
35
+ 'amqp.connection.port': address.port,
36
+ 'amqp.connection.user': address.user
37
+ }
36
38
  })
37
- }
38
- }
39
- }
40
-
41
- function startSendSpan (tracer, config, link) {
42
- const address = getAddress(link)
43
- const target = getShortName(link)
44
-
45
- const span = tracer.startSpan(`amqp.send`, {
46
- tags: {
47
- 'resource.name': ['send', target].filter(v => v).join(' '),
48
- 'span.kind': 'producer',
49
- 'amqp.link.target.address': target,
50
- 'amqp.link.role': 'sender',
51
- 'out.host': address.host,
52
- 'out.port': address.port
53
- }
54
- })
55
39
 
56
- addTags(tracer, config, span, link)
40
+ analyticsSampler.sample(span, this.config.measured)
57
41
 
58
- analyticsSampler.sample(span, config.measured)
59
-
60
- return span
61
- }
62
-
63
- function startReceiveSpan (tracer, config, link) {
64
- const source = getShortName(link)
65
- const span = tracer.startSpan(`amqp.receive`, {
66
- tags: {
67
- 'resource.name': ['receive', source].filter(v => v).join(' '),
68
- 'span.kind': 'consumer',
69
- 'span.type': 'worker',
70
- 'amqp.link.source.address': source,
71
- 'amqp.link.role': 'receiver'
72
- }
73
- })
74
-
75
- addTags(tracer, config, span, link)
42
+ this.enter(span, store)
43
+ })
76
44
 
77
- analyticsSampler.sample(span, config.measured, true)
45
+ this.addSub(`apm:amqp10:send:end`, () => {
46
+ this.exit()
47
+ })
78
48
 
79
- return span
80
- }
49
+ this.addSub(`apm:amqp10:send:error`, err => {
50
+ const span = storage.getStore().span
51
+ span.setTag('error', err)
52
+ })
81
53
 
82
- function addTags (tracer, config, span, link = {}) {
83
- const address = getAddress(link)
54
+ this.addSub(`apm:amqp10:send:async-end`, () => {
55
+ const span = storage.getStore().span
56
+ span.finish()
57
+ })
84
58
 
85
- span.addTags({
86
- 'service.name': config.service || `${tracer._service}-amqp`,
87
- 'amqp.link.name': link.name,
88
- 'amqp.link.handle': link.handle,
89
- 'amqp.connection.host': address.host,
90
- 'amqp.connection.port': address.port,
91
- 'amqp.connection.user': address.user
92
- })
59
+ this.addSub(`apm:amqp10:receive:start`, ({ link }) => {
60
+ const source = getShortName(link)
61
+ const address = getAddress(link)
62
+
63
+ const store = storage.getStore()
64
+ const childOf = store ? store.span : store
65
+
66
+ const span = this.tracer.startSpan('amqp.receive', {
67
+ childOf,
68
+ tags: {
69
+ 'resource.name': ['receive', source].filter(v => v).join(' '),
70
+ 'span.kind': 'consumer',
71
+ 'span.type': 'worker',
72
+ 'amqp.link.source.address': source,
73
+ 'amqp.link.role': 'receiver',
74
+ 'service.name': this.config.service || `${this.tracer._service}-amqp`,
75
+ 'amqp.link.name': link.name,
76
+ 'amqp.link.handle': link.handle,
77
+ 'amqp.connection.host': address.host,
78
+ 'amqp.connection.port': address.port,
79
+ 'amqp.connection.user': address.user
80
+ }
81
+ })
93
82
 
94
- return span
95
- }
83
+ analyticsSampler.sample(span, this.config.measured)
96
84
 
97
- function finish (span, error) {
98
- if (error) {
99
- span.addTags({
100
- 'error.type': error.name,
101
- 'error.msg': error.message,
102
- 'error.stack': error.stack
85
+ this.enter(span, store)
103
86
  })
104
- }
105
87
 
106
- span.finish()
107
- }
88
+ this.addSub(`apm:amqp10:receive:end`, () => {
89
+ storage.getStore().span.finish()
90
+ this.exit()
91
+ })
108
92
 
109
- function wrapPromise (promise, span) {
110
- if (!promise) {
111
- finish(span)
112
- return promise
93
+ this.addSub(`apm:amqp10:receive:error`, err => {
94
+ const span = storage.getStore().span
95
+ span.setTag('error', err)
96
+ })
113
97
  }
114
-
115
- promise.then(() => finish(span), e => finish(span, e))
116
-
117
- return promise
118
98
  }
119
99
 
120
100
  function getShortName (link) {
@@ -129,27 +109,4 @@ function getAddress (link) {
129
109
  return link.session.connection.address || {}
130
110
  }
131
111
 
132
- module.exports = [
133
- {
134
- name: 'amqp10',
135
- file: 'lib/sender_link.js',
136
- versions: ['>=3'],
137
- patch (SenderLink, tracer, config) {
138
- this.wrap(SenderLink.prototype, 'send', createWrapSend(tracer, config))
139
- },
140
- unpatch (SenderLink) {
141
- this.unwrap(SenderLink.prototype, 'send')
142
- }
143
- },
144
- {
145
- name: 'amqp10',
146
- file: 'lib/receiver_link.js',
147
- versions: ['>=3'],
148
- patch (ReceiverLink, tracer, config) {
149
- this.wrap(ReceiverLink.prototype, '_messageReceived', createWrapMessageReceived(tracer, config))
150
- },
151
- unpatch (ReceiverLink) {
152
- this.unwrap(ReceiverLink.prototype, '_messageReceived')
153
- }
154
- }
155
- ]
112
+ module.exports = Amqp10Plugin
@@ -1,74 +1,92 @@
1
1
  'use strict'
2
2
 
3
- const kebabCase = require('lodash.kebabcase')
3
+ const Plugin = require('../../dd-trace/src/plugins/plugin')
4
+ const { storage } = require('../../datadog-core')
4
5
  const analyticsSampler = require('../../dd-trace/src/analytics_sampler')
5
6
  const { TEXT_MAP } = require('../../../ext/formats')
6
7
 
7
- let methods = {}
8
-
9
- function createWrapSendImmediately (tracer, config) {
10
- return function wrapSendImmediately (sendImmediately) {
11
- return function sendImmediatelyWithTrace (method, fields) {
12
- return sendWithTrace(sendImmediately, this, arguments, tracer, config, methods[method], fields)
13
- }
8
+ class AmqplibPlugin extends Plugin {
9
+ static get name () {
10
+ return 'amqplib'
14
11
  }
15
- }
16
-
17
- function createWrapSendMessage (tracer, config) {
18
- return function wrapSendMessage (sendMessage) {
19
- return function sendMessageWithTrace (fields) {
20
- return sendWithTrace(sendMessage, this, arguments, tracer, config, 'basic.publish', fields)
21
- }
22
- }
23
- }
24
12
 
25
- function createWrapDispatchMessage (tracer, config) {
26
- return function wrapDispatchMessage (dispatchMessage) {
27
- return function dispatchMessageWithTrace (fields, message) {
28
- const childOf = extract(tracer, message)
29
- const span = tracer.startSpan('amqp.command', { childOf })
30
-
31
- addTags(this, tracer, config, span, 'basic.deliver', fields)
32
-
33
- analyticsSampler.sample(span, config.measured, true)
34
-
35
- tracer.scope().activate(span, () => {
36
- try {
37
- dispatchMessage.apply(this, arguments)
38
- } catch (e) {
39
- throw addError(span, e)
40
- } finally {
41
- span.finish()
13
+ constructor (...args) {
14
+ super(...args)
15
+
16
+ this.addSub(`apm:amqplib:command:start`, ({ channel, method, fields, message }) => {
17
+ const store = storage.getStore()
18
+ let childOf
19
+
20
+ if (method === 'basic.deliver') {
21
+ childOf = extract(this.tracer, message)
22
+ } else {
23
+ fields.headers = fields.headers || {}
24
+ childOf = store ? store.span : store
25
+ }
26
+
27
+ const span = this.tracer.startSpan('amqp.command', {
28
+ childOf,
29
+ tags: {
30
+ 'service.name': this.config.service || `${this.tracer._service}-amqp`,
31
+ 'resource.name': getResourceName(method, fields)
42
32
  }
43
33
  })
44
- }
45
- }
46
- }
47
-
48
- function sendWithTrace (send, channel, args, tracer, config, method, fields) {
49
- const childOf = tracer.scope().active()
50
- const span = tracer.startSpan('amqp.command', { childOf })
51
-
52
- fields.headers = fields.headers || {}
53
-
54
- addTags(channel, tracer, config, span, method, fields)
55
- tracer.inject(span, TEXT_MAP, fields.headers)
56
34
 
57
- analyticsSampler.sample(span, config.measured)
35
+ if (channel && channel.connection && channel.connection.stream) {
36
+ span.addTags({
37
+ 'out.host': channel.connection.stream._host,
38
+ 'out.port': channel.connection.stream.remotePort
39
+ })
40
+ }
41
+ const fieldNames = [
42
+ 'queue',
43
+ 'exchange',
44
+ 'routingKey',
45
+ 'consumerTag',
46
+ 'source',
47
+ 'destination'
48
+ ]
49
+
50
+ switch (method) {
51
+ case 'basic.publish':
52
+ span.setTag('span.kind', 'producer')
53
+ break
54
+ case 'basic.consume':
55
+ case 'basic.get':
56
+ case 'basic.deliver':
57
+ span.addTags({
58
+ 'span.kind': 'consumer',
59
+ 'span.type': 'worker'
60
+ })
61
+ break
62
+ default:
63
+ span.setTag('span.kind', 'client')
64
+ }
65
+
66
+ fieldNames.forEach(field => {
67
+ fields[field] !== undefined && span.setTag(`amqp.${field}`, fields[field])
68
+ })
69
+ if (method === 'basic.deliver') {
70
+ analyticsSampler.sample(span, this.config.measured, true)
71
+ } else {
72
+ this.tracer.inject(span, TEXT_MAP, fields.headers)
73
+ analyticsSampler.sample(span, this.config.measured)
74
+ }
75
+
76
+ this.enter(span, store)
77
+ })
58
78
 
59
- return tracer.scope().activate(span, () => {
60
- try {
61
- return send.apply(channel, args)
62
- } catch (e) {
63
- throw addError(span, e)
64
- } finally {
79
+ this.addSub(`apm:amqplib:command:end`, () => {
80
+ const span = storage.getStore().span
65
81
  span.finish()
66
- }
67
- })
68
- }
82
+ this.exit()
83
+ })
69
84
 
70
- function isCamelCase (str) {
71
- return /([A-Z][a-z0-9]+)+/.test(str)
85
+ this.addSub(`apm:amqplib:command:error`, err => {
86
+ const span = storage.getStore().span
87
+ span.setTag('error', err)
88
+ })
89
+ }
72
90
  }
73
91
 
74
92
  function getResourceName (method, fields = {}) {
@@ -82,93 +100,10 @@ function getResourceName (method, fields = {}) {
82
100
  ].filter(val => val).join(' ')
83
101
  }
84
102
 
85
- function addError (span, error) {
86
- span.addTags({
87
- 'error.type': error.name,
88
- 'error.msg': error.message,
89
- 'error.stack': error.stack
90
- })
91
-
92
- return error
93
- }
94
-
95
- function addTags (channel, tracer, config, span, method, fields) {
96
- const fieldNames = [
97
- 'queue',
98
- 'exchange',
99
- 'routingKey',
100
- 'consumerTag',
101
- 'source',
102
- 'destination'
103
- ]
104
-
105
- span.addTags({
106
- 'service.name': config.service || `${tracer._service}-amqp`,
107
- 'resource.name': getResourceName(method, fields)
108
- })
109
-
110
- if (channel && channel.connection && channel.connection.stream) {
111
- span.addTags({
112
- 'out.host': channel.connection.stream._host,
113
- 'out.port': channel.connection.stream.remotePort
114
- })
115
- }
116
-
117
- switch (method) {
118
- case 'basic.publish':
119
- span.setTag('span.kind', 'producer')
120
- break
121
- case 'basic.consume':
122
- case 'basic.get':
123
- case 'basic.deliver':
124
- span.addTags({
125
- 'span.kind': 'consumer',
126
- 'span.type': 'worker'
127
- })
128
- break
129
- default:
130
- span.setTag('span.kind', 'client')
131
- }
132
-
133
- fieldNames.forEach(field => {
134
- fields[field] !== undefined && span.setTag(`amqp.${field}`, fields[field])
135
- })
136
- }
137
-
138
103
  function extract (tracer, message) {
139
104
  return message
140
105
  ? tracer.extract(TEXT_MAP, message.properties.headers)
141
106
  : null
142
107
  }
143
108
 
144
- module.exports = [
145
- {
146
- name: 'amqplib',
147
- file: 'lib/defs.js',
148
- versions: ['>=0.5'],
149
- patch (defs, tracer, config) {
150
- methods = Object.keys(defs)
151
- .filter(key => Number.isInteger(defs[key]))
152
- .filter(key => isCamelCase(key))
153
- .reduce((acc, key) => Object.assign(acc, { [defs[key]]: kebabCase(key).replace('-', '.') }), {})
154
- },
155
- unpatch (defs) {
156
- methods = {}
157
- }
158
- },
159
- {
160
- name: 'amqplib',
161
- file: 'lib/channel.js',
162
- versions: ['>=0.5'],
163
- patch (channel, tracer, config) {
164
- this.wrap(channel.Channel.prototype, 'sendImmediately', createWrapSendImmediately(tracer, config))
165
- this.wrap(channel.Channel.prototype, 'sendMessage', createWrapSendMessage(tracer, config))
166
- this.wrap(channel.BaseChannel.prototype, 'dispatchMessage', createWrapDispatchMessage(tracer, config))
167
- },
168
- unpatch (channel) {
169
- this.unwrap(channel.Channel.prototype, 'sendImmediately')
170
- this.unwrap(channel.Channel.prototype, 'sendMessage')
171
- this.unwrap(channel.BaseChannel.prototype, 'dispatchMessage')
172
- }
173
- }
174
- ]
109
+ module.exports = AmqplibPlugin
@@ -38,8 +38,8 @@ class Sns {
38
38
  const ddInfo = {}
39
39
  tracer.inject(span, 'text_map', ddInfo)
40
40
  injectPath.MessageAttributes._datadog = {
41
- DataType: 'String',
42
- StringValue: JSON.stringify(ddInfo)
41
+ DataType: 'Binary',
42
+ BinaryValue: JSON.stringify(ddInfo) // BINARY types are automatically base64 encoded
43
43
  }
44
44
  }
45
45
  }