dd-trace 2.1.0 → 2.2.1

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 (59) hide show
  1. package/LICENSE-3rdparty.csv +0 -2
  2. package/index.d.ts +22 -12
  3. package/package.json +3 -5
  4. package/packages/datadog-instrumentations/index.js +13 -3
  5. package/packages/datadog-instrumentations/src/bunyan.js +22 -0
  6. package/packages/datadog-instrumentations/src/couchbase.js +143 -0
  7. package/packages/datadog-instrumentations/src/elasticsearch.js +79 -0
  8. package/packages/datadog-instrumentations/src/generic-pool.js +48 -0
  9. package/packages/datadog-instrumentations/src/ioredis.js +49 -0
  10. package/packages/datadog-instrumentations/src/mongoose.js +30 -0
  11. package/packages/datadog-instrumentations/src/mysql.js +2 -2
  12. package/packages/datadog-instrumentations/src/mysql2.js +5 -5
  13. package/packages/datadog-instrumentations/src/pino.js +105 -0
  14. package/packages/datadog-instrumentations/src/redis.js +118 -0
  15. package/packages/datadog-instrumentations/src/sharedb.js +78 -0
  16. package/packages/datadog-instrumentations/src/winston.js +57 -0
  17. package/packages/datadog-plugin-aws-sdk/src/helpers.js +1 -1
  18. package/packages/datadog-plugin-bunyan/src/index.js +5 -22
  19. package/packages/datadog-plugin-couchbase/src/index.js +51 -148
  20. package/packages/datadog-plugin-elasticsearch/src/index.js +41 -82
  21. package/packages/datadog-plugin-fastify/src/fastify.js +22 -1
  22. package/packages/datadog-plugin-fastify/src/find-my-way.js +0 -1
  23. package/packages/datadog-plugin-google-cloud-pubsub/src/index.js +8 -6
  24. package/packages/datadog-plugin-graphql/src/index.js +34 -28
  25. package/packages/datadog-plugin-grpc/src/client.js +20 -6
  26. package/packages/datadog-plugin-http2/src/server.js +2 -0
  27. package/packages/datadog-plugin-ioredis/src/index.js +5 -35
  28. package/packages/datadog-plugin-jest/src/jest-environment.js +26 -30
  29. package/packages/datadog-plugin-koa/src/index.js +6 -2
  30. package/packages/datadog-plugin-microgateway-core/src/index.js +1 -3
  31. package/packages/datadog-plugin-mocha/src/index.js +5 -3
  32. package/packages/datadog-plugin-mongodb-core/src/util.js +31 -7
  33. package/packages/datadog-plugin-mysql/src/index.js +4 -5
  34. package/packages/datadog-plugin-mysql2/src/index.js +11 -0
  35. package/packages/datadog-plugin-next/src/index.js +9 -4
  36. package/packages/datadog-plugin-oracledb/src/index.js +10 -7
  37. package/packages/datadog-plugin-pino/src/index.js +5 -158
  38. package/packages/datadog-plugin-redis/src/index.js +96 -80
  39. package/packages/datadog-plugin-restify/src/index.js +18 -3
  40. package/packages/datadog-plugin-rhea/src/index.js +8 -5
  41. package/packages/datadog-plugin-router/src/index.js +23 -14
  42. package/packages/datadog-plugin-sharedb/src/index.js +47 -87
  43. package/packages/datadog-plugin-winston/src/index.js +5 -110
  44. package/packages/datadog-shimmer/src/shimmer.js +1 -1
  45. package/packages/dd-trace/lib/version.js +1 -1
  46. package/packages/dd-trace/src/appsec/index.js +6 -1
  47. package/packages/dd-trace/src/appsec/reporter.js +3 -2
  48. package/packages/dd-trace/src/constants.js +1 -6
  49. package/packages/dd-trace/src/opentracing/propagation/text_map.js +0 -34
  50. package/packages/dd-trace/src/plugins/index.js +1 -3
  51. package/packages/dd-trace/src/plugins/log_plugin.js +44 -0
  52. package/packages/dd-trace/src/plugins/plugin.js +7 -0
  53. package/packages/dd-trace/src/plugins/util/web.js +102 -84
  54. package/packages/dd-trace/src/priority_sampler.js +1 -49
  55. package/packages/dd-trace/src/scope.js +47 -23
  56. package/packages/dd-trace/src/tagger.js +14 -21
  57. package/packages/dd-trace/src/tracer.js +1 -1
  58. package/packages/datadog-plugin-generic-pool/src/index.js +0 -52
  59. package/packages/datadog-plugin-mongoose/src/index.js +0 -51
@@ -0,0 +1,118 @@
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:redis:command:start')
11
+ const asyncEndCh = channel('apm:redis:command:async-end')
12
+ const endCh = channel('apm:redis:command:end')
13
+ const errorCh = channel('apm:redis:command:error')
14
+
15
+ addHook({ name: '@node-redis/client', file: 'dist/lib/client/commands-queue.js', versions: ['>=1'] }, redis => {
16
+ shimmer.wrap(redis.default.prototype, 'addCommand', addCommand => function (command) {
17
+ if (!startCh.hasSubscribers) {
18
+ return addCommand.apply(this, arguments)
19
+ }
20
+
21
+ const name = command[0]
22
+ const args = command.slice(1)
23
+
24
+ startSpan(this, name, args)
25
+
26
+ const res = addCommand.apply(this, arguments)
27
+
28
+ res.then(
29
+ () => finish(asyncEndCh, errorCh),
30
+ err => finish(asyncEndCh, errorCh, err)
31
+ )
32
+ endCh.publish(undefined)
33
+ return res
34
+ })
35
+ return redis
36
+ })
37
+
38
+ addHook({ name: 'redis', versions: ['>=2.6 <4'] }, redis => {
39
+ shimmer.wrap(redis.RedisClient.prototype, 'internal_send_command', internalSendCommand => function (options) {
40
+ if (!startCh.hasSubscribers) return internalSendCommand.apply(this, arguments)
41
+
42
+ if (!options.callback) return internalSendCommand.apply(this, arguments)
43
+
44
+ const asyncResource = new AsyncResource('bound-anonymous-fn')
45
+
46
+ const cb = asyncResource.bind(options.callback)
47
+
48
+ startSpan(this, options.command, options.args)
49
+
50
+ options.callback = AsyncResource.bind(wrapCallback(asyncEndCh, errorCh, cb))
51
+
52
+ try {
53
+ return internalSendCommand.apply(this, arguments)
54
+ } catch (err) {
55
+ errorCh.publish(err)
56
+
57
+ throw err
58
+ } finally {
59
+ endCh.publish(undefined)
60
+ }
61
+ })
62
+ return redis
63
+ })
64
+
65
+ addHook({ name: 'redis', versions: ['>=0.12 <2.6'] }, redis => {
66
+ shimmer.wrap(redis.RedisClient.prototype, 'send_command', sendCommand => function (command, args, callback) {
67
+ if (!startCh.hasSubscribers) {
68
+ return sendCommand.apply(this, arguments)
69
+ }
70
+
71
+ const asyncResource = new AsyncResource('bound-anonymous-fn')
72
+
73
+ startSpan(this, command, args)
74
+
75
+ if (typeof callback === 'function') {
76
+ const cb = asyncResource.bind(callback)
77
+ arguments[2] = AsyncResource.bind(wrapCallback(asyncEndCh, errorCh, cb))
78
+ } else if (Array.isArray(args) && typeof args[args.length - 1] === 'function') {
79
+ const cb = asyncResource.bind(args[args.length - 1])
80
+ args[args.length - 1] = AsyncResource.bind(wrapCallback(asyncEndCh, errorCh, cb))
81
+ } else {
82
+ arguments[2] = AsyncResource.bind(wrapCallback(asyncEndCh, errorCh))
83
+ }
84
+
85
+ try {
86
+ return sendCommand.apply(this, arguments)
87
+ } catch (err) {
88
+ errorCh.publish(err)
89
+
90
+ throw err
91
+ } finally {
92
+ endCh.publish(undefined)
93
+ }
94
+ })
95
+ return redis
96
+ })
97
+
98
+ function startSpan (client, command, args) {
99
+ const db = client.selected_db
100
+ const connectionOptions = client.connection_options || client.connection_option || client.connectionOption || {}
101
+ startCh.publish({ db, command, args, connectionOptions })
102
+ }
103
+
104
+ function wrapCallback (asyncEndCh, errorCh, callback) {
105
+ return function (err) {
106
+ finish(asyncEndCh, errorCh, err)
107
+ if (callback) {
108
+ return callback.apply(this, arguments)
109
+ }
110
+ }
111
+ }
112
+
113
+ function finish (asyncEndCh, errorCh, error) {
114
+ if (error) {
115
+ errorCh.publish(error)
116
+ }
117
+ asyncEndCh.publish(undefined)
118
+ }
@@ -0,0 +1,78 @@
1
+ 'use strict'
2
+
3
+ const {
4
+ channel,
5
+ addHook,
6
+ AsyncResource
7
+ } = require('./helpers/instrument')
8
+ const shimmer = require('../../datadog-shimmer')
9
+
10
+ /**
11
+ * @description The enum values in this map are not exposed from ShareDB, so the keys are hard-coded here.
12
+ * The values were derived from: https://github.com/share/sharedb/blob/master/lib/client/connection.js#L196
13
+ */
14
+ const READABLE_ACTION_NAMES = {
15
+ hs: 'handshake',
16
+ qf: 'query-fetch',
17
+ qs: 'query-subscribe',
18
+ qu: 'query-unsubscribe',
19
+ bf: 'bulk-fetch',
20
+ bs: 'bulk-subscribe',
21
+ bu: 'bulk-unsubscribe',
22
+ f: 'fetch',
23
+ s: 'subscribe',
24
+ u: 'unsubscribe',
25
+ op: 'op',
26
+ nf: 'snapshot-fetch',
27
+ nt: 'snapshot-fetch-by-ts',
28
+ p: 'presence-broadcast',
29
+ pr: 'presence-request',
30
+ ps: 'presence-subscribe',
31
+ pu: 'presence-unsubscribe'
32
+ }
33
+
34
+ addHook({ name: 'sharedb', versions: ['>=1'], file: 'lib/agent.js' }, Agent => {
35
+ const startCh = channel('apm:sharedb:request:start')
36
+ const asyncEndCh = channel('apm:sharedb:request:async-end')
37
+ const endCh = channel('apm:sharedb:request:end')
38
+ const errorCh = channel('apm:sharedb:request:error')
39
+
40
+ shimmer.wrap(Agent.prototype, '_handleMessage', origHandleMessageFn => function (request, callback) {
41
+ const asyncResource = new AsyncResource('bound-anonymous-fn')
42
+ const action = request.a
43
+
44
+ const actionName = getReadableActionName(action)
45
+
46
+ startCh.publish({ actionName, request })
47
+
48
+ callback = asyncResource.bind(callback)
49
+
50
+ arguments[1] = AsyncResource.bind(function (error, res) {
51
+ if (error) {
52
+ errorCh.publish(error)
53
+ }
54
+ asyncEndCh.publish({ request, res })
55
+
56
+ return callback.apply(this, arguments)
57
+ })
58
+
59
+ try {
60
+ return origHandleMessageFn.apply(this, arguments)
61
+ } catch (error) {
62
+ errorCh.publish(error)
63
+
64
+ throw error
65
+ } finally {
66
+ endCh.publish(undefined)
67
+ }
68
+ })
69
+ return Agent
70
+ })
71
+
72
+ function getReadableActionName (action) {
73
+ const actionName = READABLE_ACTION_NAMES[action]
74
+ if (actionName === undefined) {
75
+ return action
76
+ }
77
+ return actionName
78
+ }
@@ -0,0 +1,57 @@
1
+ 'use strict'
2
+
3
+ const {
4
+ channel,
5
+ addHook
6
+ } = require('./helpers/instrument')
7
+ const shimmer = require('../../datadog-shimmer')
8
+
9
+ const patched = new WeakSet()
10
+
11
+ addHook({ name: 'winston', file: 'lib/winston/logger.js', versions: ['>=3'] }, Logger => {
12
+ const logCh = channel('apm:winston:log')
13
+ shimmer.wrap(Logger.prototype, 'write', write => {
14
+ return function wrappedWrite (chunk, enc, cb) {
15
+ if (logCh.hasSubscribers) {
16
+ const payload = { message: chunk }
17
+ logCh.publish(payload)
18
+ arguments[0] = payload.message
19
+ }
20
+ return write.apply(this, arguments)
21
+ }
22
+ })
23
+ return Logger
24
+ })
25
+
26
+ addHook({ name: 'winston', file: 'lib/winston/logger.js', versions: ['1', '2'] }, logger => {
27
+ const logCh = channel('apm:winston:log')
28
+ if (logger.Logger.prototype.configure) {
29
+ shimmer.wrap(logger.Logger.prototype, 'configure', configure => wrapMethod(configure, logCh))
30
+ }
31
+ shimmer.wrap(logger.Logger.prototype, 'add', configure => wrapMethod(configure, logCh))
32
+ return logger
33
+ })
34
+
35
+ function wrapMethod (method, logCh) {
36
+ return function methodWithTrace () {
37
+ const result = method.apply(this, arguments)
38
+
39
+ if (logCh.hasSubscribers) {
40
+ for (const name in this.transports) {
41
+ const transport = this.transports[name]
42
+
43
+ if (patched.has(transport) || typeof transport.log !== 'function') continue
44
+
45
+ const log = transport.log
46
+ transport.log = function wrappedLog (level, msg, meta, callback) {
47
+ const payload = { message: meta || {} }
48
+ logCh.publish(payload)
49
+ arguments[2] = payload.message
50
+ log.apply(this, arguments)
51
+ }
52
+ patched.add(transport)
53
+ }
54
+ }
55
+ return result
56
+ }
57
+ }
@@ -80,7 +80,7 @@ const helpers = {
80
80
  if (!span) return
81
81
 
82
82
  const service = services[serviceName] && services[serviceName]
83
- if (service.requestInject) service.requestInject(span, request, tracer)
83
+ if (service && service.requestInject) service.requestInject(span, request, tracer)
84
84
  },
85
85
 
86
86
  wrapCb (cb, serviceName, tags, request, tracer, childOf) {
@@ -1,27 +1,10 @@
1
1
  'use strict'
2
2
 
3
- const { LOG } = require('../../../ext/formats')
3
+ const LogPlugin = require('../../dd-trace/src/plugins/log_plugin')
4
4
 
5
- function createWrapEmit (tracer, config) {
6
- return function wrapEmit (emit) {
7
- return function emitWithTrace (rec, noemit) {
8
- const span = tracer.scope().active()
9
-
10
- tracer.inject(span, LOG, rec)
11
-
12
- return emit.apply(this, arguments)
13
- }
14
- }
15
- }
16
-
17
- module.exports = {
18
- name: 'bunyan',
19
- versions: ['>=1'],
20
- patch (Logger, tracer, config) {
21
- if (!tracer._logInjection) return
22
- this.wrap(Logger.prototype, '_emit', createWrapEmit(tracer, config))
23
- },
24
- unpatch (Logger) {
25
- this.unwrap(Logger.prototype, '_emit')
5
+ class BunyanPlugin extends LogPlugin {
6
+ static get name () {
7
+ return 'bunyan'
26
8
  }
27
9
  }
10
+ module.exports = BunyanPlugin
@@ -1,171 +1,74 @@
1
1
  'use strict'
2
2
 
3
- const Tags = require('../../../ext/tags')
4
- const Kinds = require('../../../ext/kinds')
3
+ const Plugin = require('../../dd-trace/src/plugins/plugin')
4
+ const { storage } = require('../../datadog-core')
5
5
  const analyticsSampler = require('../../dd-trace/src/analytics_sampler')
6
6
 
7
- function startSpan (tracer, config, operation, resource) {
8
- const childOf = tracer.scope().active()
9
- const span = tracer.startSpan(`couchbase.${operation}`, {
10
- childOf,
11
- tags: {
7
+ class CouchBasePlugin extends Plugin {
8
+ static get name () {
9
+ return 'couchbase'
10
+ }
11
+
12
+ addSubs (func, start, asyncEnd = defaultAsyncEnd) {
13
+ this.addSub(`apm:couchbase:${func}:start`, start)
14
+ this.addSub(`apm:couchbase:${func}:end`, this.exit.bind(this))
15
+ this.addSub(`apm:couchbase:${func}:error`, errorHandler)
16
+ this.addSub(`apm:couchbase:${func}:async-end`, asyncEnd)
17
+ }
18
+
19
+ startSpan (operation, customTags, store, bucket) {
20
+ const tags = {
12
21
  'db.type': 'couchbase',
13
22
  'component': 'couchbase',
14
- 'service.name': config.service || `${tracer._service}-couchbase`,
15
- 'resource.name': resource,
16
- [Tags.SPAN_KIND]: Kinds.CLIENT
23
+ 'service.name': this.config.service || `${this.tracer._service}-couchbase`,
24
+ 'resource.name': `couchbase.${operation}`,
25
+ 'span.kind': 'client'
17
26
  }
18
- })
19
-
20
- analyticsSampler.sample(span, config.measured)
21
-
22
- return span
23
- }
24
-
25
- function onRequestFinish (emitter, span) {
26
- emitter.once('rows', () => {
27
- span.finish()
28
- })
29
- emitter.once('error', (err) => {
30
- span.setTag(Tags.ERROR, err)
31
- span.finish()
32
- })
33
- }
34
-
35
- function createWrapMaybeInvoke (tracer) {
36
- return function wrapMaybeInvoke (_maybeInvoke) {
37
- return function maybeInvokeWithTrace (fn, args) {
38
- if (!Array.isArray(args)) return _maybeInvoke.apply(this, arguments)
39
27
 
40
- const scope = tracer.scope()
41
- const callbackIndex = args.length - 1
42
- const callback = args[callbackIndex]
43
-
44
- if (callback instanceof Function) {
45
- args[callbackIndex] = scope.bind(callback)
46
- }
47
-
48
- return scope.bind(_maybeInvoke).apply(this, arguments)
28
+ for (const tag in customTags) {
29
+ tags[tag] = customTags[tag]
49
30
  }
50
- }
51
- }
52
-
53
- function createWrapQuery (tracer) {
54
- return function wrapQuery (query) {
55
- return function queryWithTrace (q, params, callback) {
56
- const scope = tracer.scope()
31
+ const span = this.tracer.startSpan(`couchbase.${operation}`, {
32
+ childOf: store ? store.span : null,
33
+ tags
34
+ })
57
35
 
58
- callback = arguments[arguments.length - 1]
36
+ span.setTag('couchbase.bucket.name', bucket.name || bucket._name)
59
37
 
60
- if (typeof callback === 'function') {
61
- arguments[arguments.length - 1] = scope.bind(callback)
62
- }
63
-
64
- return scope.bind(query.apply(this, arguments))
65
- }
38
+ analyticsSampler.sample(span, this.config.measured)
39
+ return span
66
40
  }
67
- }
68
-
69
- function createWrapN1qlReq (tracer, config) {
70
- return function wrapN1qlReq (_n1qlReq) {
71
- return function n1qlReqWithTrace (host, q, adhoc, emitter) {
72
- if (!emitter || !emitter.once) return _n1qlReq.apply(this, arguments)
73
-
74
- const scope = tracer.scope()
75
- const n1qlQuery = q && q.statement
76
- const span = startSpan(tracer, config, 'query', n1qlQuery)
77
41
 
78
- span.setTag('span.type', 'sql')
42
+ constructor (...args) {
43
+ super(...args)
79
44
 
80
- addBucketTag(span, this)
81
- onRequestFinish(emitter, span)
45
+ this.addSubs('query', ({ resource, bucket }) => {
46
+ const store = storage.getStore()
47
+ const span = this.startSpan('query', { 'span.type': 'sql', 'resource.name': resource }, store, bucket)
48
+ this.enter(span, store)
49
+ })
82
50
 
83
- return scope.bind(_n1qlReq, span).apply(this, arguments)
84
- }
51
+ this._addCommandSubs('upsert')
52
+ this._addCommandSubs('insert')
53
+ this._addCommandSubs('replace')
54
+ this._addCommandSubs('append')
55
+ this._addCommandSubs('prepend')
85
56
  }
86
- }
87
-
88
- function createWrapStore (tracer, config, operation) {
89
- return function wrapStore (store) {
90
- return function storeWithTrace (key, value, options, callback) {
91
- const callbackIndex = findCallbackIndex(arguments)
92
-
93
- if (callbackIndex < 0) return store.apply(this, arguments)
94
-
95
- const scope = tracer.scope()
96
- const span = startSpan(tracer, config, operation)
97
-
98
- addBucketTag(span, this)
99
-
100
- arguments[callbackIndex] = wrapCallback(span, arguments[callbackIndex])
101
-
102
- return scope.bind(store, span).apply(this, arguments)
103
- }
57
+ _addCommandSubs (name) {
58
+ this.addSubs(name, ({ bucket }) => {
59
+ const store = storage.getStore()
60
+ const span = this.startSpan(name, {}, store, bucket)
61
+ this.enter(span, store)
62
+ })
104
63
  }
105
64
  }
106
65
 
107
- function addBucketTag (span, bucket) {
108
- span.setTag('couchbase.bucket.name', bucket.name || bucket._name)
109
- }
110
-
111
- function findCallbackIndex (args) {
112
- for (let i = args.length - 1; i >= 2; i--) {
113
- if (typeof args[i] === 'function') return i
114
- }
115
-
116
- return -1
66
+ function defaultAsyncEnd () {
67
+ storage.getStore().span.finish()
117
68
  }
118
69
 
119
- function wrapCallback (span, callback) {
120
- return function (err, result) {
121
- span.setTag('error', err)
122
- span.finish()
123
-
124
- return callback.apply(this, arguments)
125
- }
70
+ function errorHandler (error) {
71
+ storage.getStore().span.setTag('error', error)
126
72
  }
127
73
 
128
- module.exports = [
129
- {
130
- name: 'couchbase',
131
- versions: ['^2.6.5'],
132
- file: 'lib/bucket.js',
133
- patch (Bucket, tracer, config) {
134
- tracer.scope().bind(Bucket.prototype)
135
-
136
- this.wrap(Bucket.prototype, '_maybeInvoke', createWrapMaybeInvoke(tracer, config))
137
- this.wrap(Bucket.prototype, 'query', createWrapQuery(tracer))
138
- this.wrap(Bucket.prototype, '_n1qlReq', createWrapN1qlReq(tracer, config))
139
- this.wrap(Bucket.prototype, 'upsert', createWrapStore(tracer, config, 'upsert'))
140
- this.wrap(Bucket.prototype, 'insert', createWrapStore(tracer, config, 'insert'))
141
- this.wrap(Bucket.prototype, 'replace', createWrapStore(tracer, config, 'replace'))
142
- this.wrap(Bucket.prototype, 'append', createWrapStore(tracer, config, 'append'))
143
- this.wrap(Bucket.prototype, 'prepend', createWrapStore(tracer, config, 'prepend'))
144
- },
145
- unpatch (Bucket, tracer) {
146
- tracer.scope().unbind(Bucket.prototype)
147
-
148
- this.unwrap(Bucket.prototype, '_maybeInvoke')
149
- this.unwrap(Bucket.prototype, 'query')
150
- this.unwrap(Bucket.prototype, '_n1qlReq')
151
- this.unwrap(Bucket.prototype, 'upsert')
152
- this.unwrap(Bucket.prototype, 'insert')
153
- this.unwrap(Bucket.prototype, 'replace')
154
- this.unwrap(Bucket.prototype, 'append')
155
- this.unwrap(Bucket.prototype, 'prepend')
156
- }
157
- },
158
- {
159
- name: 'couchbase',
160
- versions: ['^2.6.5'],
161
- file: 'lib/cluster.js',
162
- patch (Cluster, tracer, config) {
163
- this.wrap(Cluster.prototype, '_maybeInvoke', createWrapMaybeInvoke(tracer, config))
164
- this.wrap(Cluster.prototype, 'query', createWrapQuery(tracer))
165
- },
166
- unpatch (Cluster) {
167
- this.unwrap(Cluster.prototype, '_maybeInvoke')
168
- this.unwrap(Cluster.prototype, 'query')
169
- }
170
- }
171
- ]
74
+ module.exports = CouchBasePlugin
@@ -1,23 +1,29 @@
1
1
  'use strict'
2
2
 
3
- const Tags = require('opentracing').Tags
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
 
6
- function createWrapRequest (tracer, config) {
7
- config = normalizeConfig(config)
8
- return function wrapRequest (request) {
9
- return function requestWithTrace (params, options, cb) {
10
- if (!params) return request.apply(this, arguments)
7
+ class ElasticsearchPlugin extends Plugin {
8
+ static get name () {
9
+ return 'elasticsearch'
10
+ }
11
+
12
+ constructor (...args) {
13
+ super(...args)
14
+
15
+ this.addSub('apm:elasticsearch:query:start', ({ params }) => {
16
+ this.config = normalizeConfig(this.config)
11
17
 
12
- const lastIndex = arguments.length - 1
18
+ const store = storage.getStore()
19
+ const childOf = store ? store.span : store
13
20
  const body = getBody(params.body || params.bulkBody)
14
- const childOf = tracer.scope().active()
15
- const span = tracer.startSpan('elasticsearch.query', {
21
+ const span = this.tracer.startSpan('elasticsearch.query', {
16
22
  childOf,
17
23
  tags: {
18
- [Tags.SPAN_KIND]: Tags.SPAN_KIND_RPC_CLIENT,
19
- [Tags.DB_TYPE]: 'elasticsearch',
20
- 'service.name': config.service || `${tracer._service}-elasticsearch`,
24
+ 'db.type': 'elasticsearch',
25
+ 'span.kind': 'client',
26
+ 'service.name': this.config.service || `${this.tracer._service}-elasticsearch`,
21
27
  'resource.name': `${params.method} ${quantizePath(params.path)}`,
22
28
  'span.type': 'elasticsearch',
23
29
  'elasticsearch.url': params.path,
@@ -26,57 +32,25 @@ function createWrapRequest (tracer, config) {
26
32
  'elasticsearch.params': JSON.stringify(params.querystring || params.query)
27
33
  }
28
34
  })
35
+ analyticsSampler.sample(span, this.config.measured)
36
+ this.enter(span, store)
37
+ })
29
38
 
30
- analyticsSampler.sample(span, config.measured)
31
-
32
- cb = arguments[lastIndex]
33
-
34
- if (typeof cb === 'function') {
35
- arguments[lastIndex] = wrapCallback(tracer, span, params, config, cb)
36
-
37
- return tracer.scope().activate(span, () => request.apply(this, arguments))
38
- } else {
39
- const promise = request.apply(this, arguments)
40
-
41
- if (promise && typeof promise.then === 'function') {
42
- promise.then(() => finish(span, params, config), e => finish(span, params, config, e))
43
- } else {
44
- finish(span, params, config)
45
- }
46
-
47
- return promise
48
- }
49
- }
50
- }
51
- }
39
+ this.addSub('apm:elasticsearch:query:end', () => {
40
+ this.exit()
41
+ })
52
42
 
53
- function wrapCallback (tracer, span, params, config, done) {
54
- return tracer.scope().bind(function (err) {
55
- finish(span, params, config, err)
56
- done.apply(null, arguments)
57
- })
58
- }
43
+ this.addSub('apm:elasticsearch:query:error', err => {
44
+ const span = storage.getStore().span
45
+ span.setTag('error', err)
46
+ })
59
47
 
60
- function finish (span, params, config, err) {
61
- if (err) {
62
- span.addTags({
63
- 'error.type': err.name,
64
- 'error.msg': err.message,
65
- 'error.stack': err.stack
48
+ this.addSub('apm:elasticsearch:query:async-end', ({ params }) => {
49
+ const span = storage.getStore().span
50
+ this.config.hooks.query(span, params)
51
+ span.finish()
66
52
  })
67
53
  }
68
-
69
- config.hooks.query(span, params)
70
-
71
- span.finish()
72
- }
73
-
74
- function quantizePath (path) {
75
- return path && path.replace(/[0-9]+/g, '?')
76
- }
77
-
78
- function getBody (body) {
79
- return body && JSON.stringify(body)
80
54
  }
81
55
 
82
56
  function normalizeConfig (config) {
@@ -94,27 +68,12 @@ function getHooks (config) {
94
68
  return { query }
95
69
  }
96
70
 
97
- module.exports = [
98
- {
99
- name: 'elasticsearch',
100
- file: 'src/lib/transport.js',
101
- versions: ['>=10'],
102
- patch (Transport, tracer, config) {
103
- this.wrap(Transport.prototype, 'request', createWrapRequest(tracer, config))
104
- },
105
- unpatch (Transport) {
106
- this.unwrap(Transport.prototype, 'request')
107
- }
108
- },
109
- {
110
- name: '@elastic/elasticsearch',
111
- file: 'lib/Transport.js',
112
- versions: ['>=5.6.16'], // initial version of this module
113
- patch (Transport, tracer, config) {
114
- this.wrap(Transport.prototype, 'request', createWrapRequest(tracer, config))
115
- },
116
- unpatch (Transport) {
117
- this.unwrap(Transport.prototype, 'request')
118
- }
119
- }
120
- ]
71
+ function getBody (body) {
72
+ return body && JSON.stringify(body)
73
+ }
74
+
75
+ function quantizePath (path) {
76
+ return path && path.replace(/[0-9]+/g, '?')
77
+ }
78
+
79
+ module.exports = ElasticsearchPlugin