dd-trace 2.0.0-beta.0 → 2.1.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 (31) hide show
  1. package/MIGRATING.md +65 -0
  2. package/NOTICE +4 -0
  3. package/package.json +2 -2
  4. package/packages/datadog-instrumentations/index.js +1 -0
  5. package/packages/datadog-instrumentations/src/dns.js +2 -2
  6. package/packages/datadog-instrumentations/src/helpers/instrument.js +24 -25
  7. package/packages/datadog-instrumentations/src/memcached.js +3 -5
  8. package/packages/datadog-instrumentations/src/mysql.js +7 -9
  9. package/packages/datadog-instrumentations/src/mysql2.js +76 -0
  10. package/packages/datadog-instrumentations/src/q.js +9 -1
  11. package/packages/datadog-plugin-aws-sdk/src/helpers.js +4 -3
  12. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +48 -0
  13. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +56 -6
  14. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +33 -6
  15. package/packages/datadog-plugin-mysql/src/index.js +4 -4
  16. package/packages/datadog-plugin-mysql2/src/index.js +5 -88
  17. package/packages/datadog-plugin-next/src/index.js +10 -6
  18. package/packages/datadog-plugin-pino/src/index.js +25 -1
  19. package/packages/datadog-plugin-winston/src/index.js +30 -12
  20. package/packages/dd-trace/lib/version.js +1 -1
  21. package/packages/dd-trace/src/appsec/index.js +13 -16
  22. package/packages/dd-trace/src/appsec/recommended.json +5708 -1
  23. package/packages/dd-trace/src/appsec/reporter.js +15 -3
  24. package/packages/dd-trace/src/config.js +7 -1
  25. package/packages/dd-trace/src/profiling/config.js +5 -1
  26. package/packages/dd-trace/src/profiling/profiler.js +15 -6
  27. package/packages/dd-trace/src/profiling/profilers/cpu.js +1 -1
  28. package/packages/dd-trace/src/profiling/profilers/heap.js +3 -2
  29. package/scripts/publish_docs.js +1 -1
  30. package/scripts/tracer-runner.js +13 -0
  31. package/packages/dd-trace/src/profiling/mapper.js +0 -91
package/MIGRATING.md ADDED
@@ -0,0 +1,65 @@
1
+ # Migrating
2
+
3
+ This guide describes the steps to upgrade dd-trace from a major version to the
4
+ next. If you are having any issues related to migrating, please feel free to
5
+ open an issue or contact our [support](https://www.datadoghq.com/support/) team.
6
+
7
+ ## 1.0 to 2.0
8
+
9
+ ### Configuration
10
+
11
+ The following configuraton options are no longer available programmatically and
12
+ must be configured using these environment variables:
13
+
14
+ * `enabled` -> `DD_TRACE_ENABLED=true|false`
15
+ * `debug` -> `DD_TRACE_DEBUG=true|false`
16
+
17
+ If environment variables were already used for these options, no action is
18
+ needed.
19
+
20
+ The following configuration options were completely removed and will no longer
21
+ have any effect:
22
+
23
+ * `scope`
24
+
25
+ Startup logs are now disabled by default and can be enabled if needed with
26
+ `DD_TRACE_STARTUP_LOGS=true`.
27
+
28
+ ### Removed APIs
29
+
30
+ The original scope manager has been replaced several years ago and has now been
31
+ removed. Any code referencing `tracer.scopeManager()` should be removed or
32
+ replaced with `tracer.scope()` which is documented
33
+ [here](https://datadoghq.dev/dd-trace-js/#scope-manager).
34
+
35
+ ### Nested objects as tags
36
+
37
+ Support for nested objects as tags as been removed. When adding an object as a
38
+ tag value, only properties that exist on that object directly will be added as
39
+ tags. If nested properties are also needed, these should be added by hand.
40
+
41
+ For example:
42
+
43
+ ```js
44
+ const obj = {
45
+ a: 'foo',
46
+ b: {
47
+ c: 'bar'
48
+ }
49
+ }
50
+
51
+ // 1.0
52
+ span.setTag('test', obj) // add test.a and test.b.c
53
+
54
+ // 2.0
55
+ span.setTag('test', obj) // add test.a
56
+ span.setTag('test.b', obj.b) // add test.b.c
57
+ ```
58
+
59
+ Arrays are no longer supported and must be converted to string manually.
60
+
61
+ ### Outgoing request filtering
62
+
63
+ Outgoing request filtering is no longer supported and is now only available for
64
+ incoming requests. This means that the `blocklist` and `allowlist` options on
65
+ the `http` integration no longer have any effect.
package/NOTICE ADDED
@@ -0,0 +1,4 @@
1
+ Datadog dd-trace-js
2
+ Copyright 2016-Present Datadog, Inc.
3
+
4
+ This product includes software developed at Datadog, Inc. (https://www.datadoghq.com/).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "2.0.0-beta.0",
3
+ "version": "2.1.1",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -61,7 +61,7 @@
61
61
  "node": ">=12"
62
62
  },
63
63
  "dependencies": {
64
- "@datadog/native-appsec": "^0.8.0",
64
+ "@datadog/native-appsec": "^0.8.1",
65
65
  "@datadog/native-metrics": "^1.1.0",
66
66
  "@datadog/pprof": "^0.3.0",
67
67
  "@datadog/sketches-js": "^1.0.4",
@@ -3,6 +3,7 @@
3
3
  require('./src/dns')
4
4
  require('./src/memcached')
5
5
  require('./src/mysql')
6
+ require('./src/mysql2')
6
7
  require('./src/bluebird')
7
8
  require('./src/when')
8
9
  require('./src/promise')
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { channel, addHook, bind } = require('./helpers/instrument')
3
+ const { channel, addHook, AsyncResource } = require('./helpers/instrument')
4
4
  const shimmer = require('../../datadog-shimmer')
5
5
 
6
6
  const rrtypes = {
@@ -53,7 +53,7 @@ function wrap (prefix, fn, expectedArgs, rrtype) {
53
53
  const errorCh = channel(prefix + ':error')
54
54
 
55
55
  const wrapped = function () {
56
- const cb = bind(arguments[arguments.length - 1])
56
+ const cb = AsyncResource.bind(arguments[arguments.length - 1])
57
57
  if (
58
58
  !startCh.hasSubscribers ||
59
59
  arguments.length < expectedArgs ||
@@ -90,32 +90,31 @@ function getBasedir (id) {
90
90
  }
91
91
 
92
92
  if (semver.satisfies(process.versions.node, '>=16.0.0')) {
93
- exports.bind = AsyncResource.bind
94
- exports.bindAsyncResource = AsyncResource.prototype.bind
93
+ exports.AsyncResource = AsyncResource
95
94
  } else {
96
- exports.bindAsyncResource = function bindAsyncResource (fn, thisArg) {
97
- thisArg = thisArg || this
98
- const ret = this.runInAsyncScope.bind(this, fn, thisArg)
99
- Object.defineProperties(ret, {
100
- 'length': {
101
- configurable: true,
102
- enumerable: false,
103
- value: fn.length,
104
- writable: false
105
- },
106
- 'asyncResource': {
107
- configurable: true,
108
- enumerable: true,
109
- value: this,
110
- writable: true
111
- }
112
- })
113
- return ret
114
- }
95
+ exports.AsyncResource = class extends AsyncResource {
96
+ static bind (fn, type, thisArg) {
97
+ type = type || fn.name
98
+ return (new exports.AsyncResource(type || 'bound-anonymous-fn')).bind(fn, thisArg)
99
+ }
115
100
 
116
- exports.bind = function bind (fn, type, thisArg) {
117
- type = type || fn.name
118
- const ar = new AsyncResource(type || 'bound-anonymous-fn')
119
- return exports.bindAsyncResource.call(ar, fn, thisArg)
101
+ bind (fn, thisArg = this) {
102
+ const ret = this.runInAsyncScope.bind(this, fn, thisArg)
103
+ Object.defineProperties(ret, {
104
+ 'length': {
105
+ configurable: true,
106
+ enumerable: false,
107
+ value: fn.length,
108
+ writable: false
109
+ },
110
+ 'asyncResource': {
111
+ configurable: true,
112
+ enumerable: true,
113
+ value: this,
114
+ writable: true
115
+ }
116
+ })
117
+ return ret
118
+ }
120
119
  }
121
120
  }
@@ -1,11 +1,9 @@
1
1
  'use strict'
2
2
 
3
- const { AsyncResource } = require('async_hooks')
4
3
  const {
5
4
  channel,
6
5
  addHook,
7
- bind,
8
- bindAsyncResource
6
+ AsyncResource
9
7
  } = require('./helpers/instrument')
10
8
  const shimmer = require('../../datadog-shimmer')
11
9
 
@@ -27,9 +25,9 @@ addHook({ name: 'memcached', versions: ['>=2.2'] }, Memcached => {
27
25
 
28
26
  const wrappedQueryCompiler = function () {
29
27
  const query = queryCompiler.apply(this, arguments)
30
- const callback = bindAsyncResource.call(asyncResource, query.callback)
28
+ const callback = asyncResource.bind(query.callback)
31
29
 
32
- query.callback = bind(function (err) {
30
+ query.callback = AsyncResource.bind(function (err) {
33
31
  if (err) {
34
32
  errorCh.publish(err)
35
33
  }
@@ -1,11 +1,9 @@
1
1
  'use strict'
2
2
 
3
- const { AsyncResource } = require('async_hooks')
4
3
  const {
5
4
  channel,
6
5
  addHook,
7
- bind,
8
- bindAsyncResource
6
+ AsyncResource
9
7
  } = require('./helpers/instrument')
10
8
  const shimmer = require('../../datadog-shimmer')
11
9
 
@@ -22,16 +20,16 @@ addHook({ name: 'mysql', file: 'lib/Connection.js', versions: ['>=2'] }, Connect
22
20
  }
23
21
 
24
22
  const sql = arguments[0].sql ? arguments[0].sql : arguments[0]
25
- const startArgs = [sql, this.config]
23
+ const conf = this.config
26
24
 
27
- startCh.publish(startArgs)
25
+ startCh.publish({ sql, conf })
28
26
 
29
27
  try {
30
28
  const res = query.apply(this, arguments)
31
29
 
32
30
  if (res._callback) {
33
- const cb = bindAsyncResource.call(asyncResource, res._callback)
34
- res._callback = bind(function (error, result) {
31
+ const cb = asyncResource.bind(res._callback)
32
+ res._callback = AsyncResource.bind(function (error, result) {
35
33
  if (error) {
36
34
  errorCh.publish(error)
37
35
  }
@@ -40,7 +38,7 @@ addHook({ name: 'mysql', file: 'lib/Connection.js', versions: ['>=2'] }, Connect
40
38
  return cb.apply(this, arguments)
41
39
  })
42
40
  } else {
43
- const cb = bind(function () {
41
+ const cb = AsyncResource.bind(function () {
44
42
  asyncEndCh.publish(undefined)
45
43
  })
46
44
  res.on('end', cb)
@@ -62,7 +60,7 @@ addHook({ name: 'mysql', file: 'lib/Connection.js', versions: ['>=2'] }, Connect
62
60
 
63
61
  addHook({ name: 'mysql', file: 'lib/Pool.js', versions: ['>=2'] }, Pool => {
64
62
  shimmer.wrap(Pool.prototype, 'getConnection', getConnection => function (cb) {
65
- arguments[0] = bind(cb)
63
+ arguments[0] = AsyncResource.bind(cb)
66
64
  return getConnection.apply(this, arguments)
67
65
  })
68
66
  return Pool
@@ -0,0 +1,76 @@
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: 'mysql2', file: 'lib/connection.js', versions: ['>=1'] }, Connection => {
11
+ const startCh = channel('apm:mysql2:query:start')
12
+ const asyncEndCh = channel('apm:mysql2:query:async-end')
13
+ const endCh = channel('apm:mysql2:query:end')
14
+ const errorCh = channel('apm:mysql2:query:error')
15
+
16
+ shimmer.wrap(Connection.prototype, 'addCommand', addCommand => function (cmd) {
17
+ if (!startCh.hasSubscribers) return addCommand.apply(this, arguments)
18
+
19
+ const asyncResource = new AsyncResource('bound-anonymous-fn')
20
+ const name = cmd && cmd.constructor && cmd.constructor.name
21
+ const isCommand = typeof cmd.execute === 'function'
22
+ const isQuery = isCommand && (name === 'Execute' || name === 'Query')
23
+
24
+ // TODO: consider supporting all commands and not just queries
25
+ cmd.execute = isQuery
26
+ ? wrapExecute(cmd, cmd.execute, asyncResource, this.config)
27
+ : bindExecute(cmd, cmd.execute, asyncResource)
28
+
29
+ return asyncResource.bind(addCommand, this).apply(this, arguments)
30
+ })
31
+
32
+ return Connection
33
+
34
+ function bindExecute (cmd, execute, asyncResource) {
35
+ return asyncResource.bind(function executeWithTrace (packet, connection) {
36
+ if (this.onResult) {
37
+ this.onResult = asyncResource.bind(this.onResult)
38
+ }
39
+
40
+ return execute.apply(this, arguments)
41
+ }, cmd)
42
+ }
43
+
44
+ function wrapExecute (cmd, execute, asyncResource, config) {
45
+ return asyncResource.bind(function executeWithTrace (packet, connection) {
46
+ const sql = cmd.statement ? cmd.statement.query : cmd.sql
47
+
48
+ startCh.publish({ sql, conf: config })
49
+
50
+ if (this.onResult) {
51
+ const onResult = asyncResource.bind(this.onResult)
52
+
53
+ this.onResult = AsyncResource.bind(function (error) {
54
+ if (error) {
55
+ errorCh.publish(error)
56
+ }
57
+ asyncEndCh.publish(undefined)
58
+ onResult.apply(this, arguments)
59
+ }, 'bound-anonymous-fn', this)
60
+ } else {
61
+ this.on('error', AsyncResource.bind(error => errorCh.publish(error)))
62
+ this.on('end', AsyncResource.bind(() => asyncEndCh.publish(undefined)))
63
+ }
64
+
65
+ this.execute = execute
66
+
67
+ try {
68
+ return execute.apply(this, arguments)
69
+ } catch (err) {
70
+ errorCh.publish(err)
71
+ } finally {
72
+ endCh.publish(undefined)
73
+ }
74
+ }, cmd)
75
+ }
76
+ })
@@ -6,8 +6,16 @@ const shimmer = require('../../datadog-shimmer')
6
6
 
7
7
  addHook({
8
8
  name: 'q',
9
- versions: ['>=1']
9
+ versions: ['1']
10
10
  }, Q => {
11
11
  shimmer.wrap(Q.makePromise.prototype, 'then', wrapThen)
12
12
  return Q
13
13
  })
14
+
15
+ addHook({
16
+ name: 'q',
17
+ versions: ['>=2']
18
+ }, Q => {
19
+ shimmer.wrap(Q.Promise.prototype, 'then', wrapThen)
20
+ return Q
21
+ })
@@ -10,7 +10,8 @@ const services = {
10
10
  s3: getService(require('./services/s3')),
11
11
  redshift: getService(require('./services/redshift')),
12
12
  sns: getService(require('./services/sns')),
13
- sqs: getService(require('./services/sqs'))
13
+ sqs: getService(require('./services/sqs')),
14
+ eventbridge: getService(require('./services/eventbridge'))
14
15
  }
15
16
 
16
17
  function getService (Service) {
@@ -78,8 +79,8 @@ const helpers = {
78
79
  requestInject (span, request, serviceName, tracer) {
79
80
  if (!span) return
80
81
 
81
- const inject = services[serviceName] && services[serviceName].requestInject
82
- if (inject) inject(span, request, tracer)
82
+ const service = services[serviceName] && services[serviceName]
83
+ if (service && service.requestInject) service.requestInject(span, request, tracer)
83
84
  },
84
85
 
85
86
  wrapCb (cb, serviceName, tags, request, tracer, childOf) {
@@ -0,0 +1,48 @@
1
+ 'use strict'
2
+ const log = require('../../../dd-trace/src/log')
3
+ class EventBridge {
4
+ generateTags (params, operation, response) {
5
+ if (!params || !params.source) return {}
6
+
7
+ return {
8
+ 'resource.name': `${operation} ${params.source}`,
9
+ 'aws.eventbridge.source': params.source
10
+ }
11
+ }
12
+
13
+ /**
14
+ * requestInject
15
+ * @param {*} span
16
+ * @param {*} request
17
+ * @param {*} tracer
18
+ *
19
+ * Docs: https://docs.aws.amazon.com/eventbridge/latest/APIReference/API_PutEventsRequestEntry.html
20
+ * We cannot use the traceHeader field as that's reserved for X-Ray.
21
+ * Detail must be a valid JSON string
22
+ * Max size per event is 256kb (https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-putevent-size.html)
23
+ */
24
+ requestInject (span, request, tracer) {
25
+ const operation = request.operation
26
+ if (operation === 'putEvents' &&
27
+ request.params &&
28
+ request.params.Entries &&
29
+ request.params.Entries.length > 0 &&
30
+ request.params.Entries[0].Detail) {
31
+ try {
32
+ const details = JSON.parse(request.params.Entries[0].Detail)
33
+ details._datadog = {}
34
+ tracer.inject(span, 'text_map', details._datadog)
35
+ const finalData = JSON.stringify(details)
36
+ const byteSize = Buffer.byteLength(finalData)
37
+ if (byteSize >= (1024 * 256)) {
38
+ log.info('Payload size too large to pass context')
39
+ return
40
+ }
41
+ request.params.Entries[0].Detail = finalData
42
+ } catch (e) {
43
+ log.error(e)
44
+ }
45
+ }
46
+ }
47
+ }
48
+ module.exports = EventBridge
@@ -1,15 +1,65 @@
1
1
  'use strict'
2
-
2
+ const log = require('../../../dd-trace/src/log')
3
3
  class Kinesis {
4
4
  generateTags (params, operation, response) {
5
- const tags = {}
6
-
7
- if (!params || !params.StreamName) return tags
5
+ if (!params || !params.StreamName) return {}
8
6
 
9
- return Object.assign(tags, {
7
+ return {
10
8
  'resource.name': `${operation} ${params.StreamName}`,
11
9
  'aws.kinesis.stream_name': params.StreamName
12
- })
10
+ }
11
+ }
12
+
13
+ // AWS-SDK will b64 kinesis payloads
14
+ // or will accept an already b64 encoded payload
15
+ // This method handles both
16
+ _tryParse (body) {
17
+ try {
18
+ return JSON.parse(body)
19
+ } catch (e) {
20
+ log.info('Not JSON string. Trying Base64 encoded JSON string')
21
+ }
22
+ try {
23
+ return JSON.parse(Buffer.from(body, 'base64').toString('ascii'), true)
24
+ } catch (e) {
25
+ return null
26
+ }
27
+ }
28
+
29
+ requestInject (span, request, tracer) {
30
+ const operation = request.operation
31
+ if (operation === 'putRecord' || operation === 'putRecords') {
32
+ if (!request.params) {
33
+ return
34
+ }
35
+
36
+ const traceData = {}
37
+ tracer.inject(span, 'text_map', traceData)
38
+ let injectPath
39
+ if (request.params.Records && request.params.Records.length > 0) {
40
+ injectPath = request.params.Records[0]
41
+ } else if (request.params.Data) {
42
+ injectPath = request.params
43
+ } else {
44
+ log.error('No valid payload passed, unable to pass trace context')
45
+ return
46
+ }
47
+ const parsedData = this._tryParse(injectPath.Data)
48
+ if (parsedData) {
49
+ parsedData._datadog = traceData
50
+ const finalData = JSON.stringify(parsedData)
51
+ const byteSize = Buffer.byteLength(finalData, 'ascii')
52
+ // Kinesis max payload size is 1MB
53
+ // So we must ensure adding DD context won't go over that (512b is an estimate)
54
+ if (byteSize >= 1048576) {
55
+ log.info('Payload size too large to pass context')
56
+ return
57
+ }
58
+ injectPath.Data = finalData
59
+ } else {
60
+ log.error('Unable to parse payload, unable to pass trace context')
61
+ }
62
+ }
13
63
  }
14
64
  }
15
65
 
@@ -1,21 +1,48 @@
1
1
  'use strict'
2
+ const log = require('../../../dd-trace/src/log')
2
3
 
3
4
  class Sns {
4
5
  generateTags (params, operation, response) {
5
- const tags = {}
6
+ if (!params) return {}
6
7
 
7
- if (!params) return tags
8
+ if (!params.TopicArn && !(response.data && response.data.TopicArn)) return {}
8
9
 
9
- if (!params.TopicArn && !(response.data && response.data.TopicArn)) return tags
10
-
11
- return Object.assign(tags, {
10
+ return {
12
11
  'resource.name': `${operation} ${params.TopicArn || response.data.TopicArn}`,
13
12
  'aws.sns.topic_arn': params.TopicArn || response.data.TopicArn
14
- })
13
+ }
15
14
 
16
15
  // TODO: should arn be sanitized or quantized in some way here,
17
16
  // for example if it contains a phone number?
18
17
  }
18
+
19
+ requestInject (span, request, tracer) {
20
+ const operation = request.operation
21
+ if (operation === 'publish' || operation === 'publishBatch') {
22
+ if (!request.params) {
23
+ request.params = {}
24
+ }
25
+ let injectPath
26
+ if (request.params.PublishBatchRequestEntries && request.params.PublishBatchRequestEntries.length > 0) {
27
+ injectPath = request.params.PublishBatchRequestEntries[0]
28
+ } else if (request.params.Message) {
29
+ injectPath = request.params
30
+ }
31
+ if (!injectPath.MessageAttributes) {
32
+ injectPath.MessageAttributes = {}
33
+ }
34
+ if (Object.keys(injectPath.MessageAttributes).length >= 10) { // SNS quota
35
+ log.info('Message attributes full, skipping trace context injection')
36
+ return
37
+ }
38
+ const ddInfo = {}
39
+ tracer.inject(span, 'text_map', ddInfo)
40
+ injectPath.MessageAttributes._datadog = {
41
+ DataType: 'String',
42
+ StringValue: JSON.stringify(ddInfo)
43
+ }
44
+ }
45
+ }
19
46
  }
20
47
 
21
48
  module.exports = Sns
@@ -12,7 +12,7 @@ class MySQLPlugin extends Plugin {
12
12
  constructor (...args) {
13
13
  super(...args)
14
14
 
15
- this.addSub('apm:mysql:query:start', ([sql, conf]) => {
15
+ this.addSub(`apm:${this.constructor.name}:query:start`, ({ sql, conf }) => {
16
16
  const store = storage.getStore()
17
17
  const childOf = store ? store.span : store
18
18
  const span = this.tracer.startSpan('mysql.query', {
@@ -37,18 +37,18 @@ class MySQLPlugin extends Plugin {
37
37
  this.enter(span, store)
38
38
  })
39
39
 
40
- this.addSub('apm:mysql:query:end', () => {
40
+ this.addSub(`apm:${this.constructor.name}:query:end`, () => {
41
41
  this.exit()
42
42
  })
43
43
 
44
- this.addSub('apm:mysql:query:error', err => {
44
+ this.addSub(`apm:${this.constructor.name}:query:error`, err => {
45
45
  if (err) {
46
46
  const span = storage.getStore().span
47
47
  span.setTag('error', err)
48
48
  }
49
49
  })
50
50
 
51
- this.addSub('apm:mysql:query:async-end', () => {
51
+ this.addSub(`apm:${this.constructor.name}:query:async-end`, () => {
52
52
  const span = storage.getStore().span
53
53
  span.finish()
54
54
  })
@@ -1,94 +1,11 @@
1
1
  'use strict'
2
2
 
3
- const Tags = require('opentracing').Tags
4
- const analyticsSampler = require('../../dd-trace/src/analytics_sampler')
3
+ const MySQLPlugin = require('../../datadog-plugin-mysql/src')
5
4
 
6
- function createWrapAddCommand (tracer, config) {
7
- return function wrapAddCommand (addCommand) {
8
- return function addCommandWithTrace (cmd) {
9
- const name = cmd && cmd.constructor && cmd.constructor.name
10
- const isCommand = typeof cmd.execute === 'function'
11
- const isSupported = name === 'Execute' || name === 'Query'
12
-
13
- if (isCommand && isSupported) {
14
- cmd.execute = wrapExecute(tracer, config, cmd.execute)
15
- }
16
-
17
- return addCommand.apply(this, arguments)
18
- }
5
+ class MySQL2Plugin extends MySQLPlugin {
6
+ static get name () {
7
+ return 'mysql2'
19
8
  }
20
9
  }
21
10
 
22
- function wrapExecute (tracer, config, execute) {
23
- const scope = tracer.scope()
24
- const childOf = scope.active()
25
-
26
- return function executeWithTrace (packet, connection) {
27
- const connectionConfig = (connection && connection.config) || {}
28
- const sql = this.statement ? this.statement.query : this.sql
29
- const span = tracer.startSpan('mysql.query', {
30
- childOf,
31
- tags: {
32
- [Tags.SPAN_KIND]: Tags.SPAN_KIND_RPC_CLIENT,
33
- 'service.name': config.service || `${tracer._service}-mysql`,
34
- 'resource.name': sql,
35
- 'span.type': 'sql',
36
- 'span.kind': 'client',
37
- 'db.type': 'mysql',
38
- 'db.user': connectionConfig.user,
39
- 'db.name': connectionConfig.database,
40
- 'out.host': connectionConfig.host,
41
- 'out.port': connectionConfig.port
42
- }
43
- })
44
-
45
- analyticsSampler.sample(span, config.measured)
46
-
47
- if (typeof this.onResult === 'function') {
48
- this.onResult = wrapCallback(tracer, span, childOf, this.onResult)
49
- } else {
50
- this.on('error', error => span.addTags({ error }))
51
- this.on('end', () => span.finish())
52
- }
53
-
54
- this.execute = execute
55
-
56
- return scope.bind(execute, span).apply(this, arguments)
57
- }
58
- }
59
-
60
- function wrapCallback (tracer, span, parent, done) {
61
- return tracer.scope().bind((...args) => {
62
- const [ error ] = args
63
- span.addTags({ error })
64
-
65
- span.finish()
66
-
67
- done(...args)
68
- }, parent)
69
- }
70
-
71
- module.exports = [
72
- {
73
- name: 'mysql2',
74
- file: 'lib/connection.js',
75
- versions: ['>=1'],
76
- patch (Connection, tracer, config) {
77
- this.wrap(Connection.prototype, 'addCommand', createWrapAddCommand(tracer, config))
78
- },
79
- unpatch (Connection) {
80
- this.unwrap(Connection.prototype, 'addCommand')
81
- }
82
- },
83
- {
84
- name: 'mysql2',
85
- file: 'lib/commands/command.js',
86
- versions: ['>=1'],
87
- patch (Command, tracer, config) {
88
- tracer.scope().bind(Command.prototype)
89
- },
90
- unpatch (Command, tracer) {
91
- tracer.scope().unbind(Command.prototype)
92
- }
93
- }
94
- ]
11
+ module.exports = MySQL2Plugin