dd-trace 2.0.0-appsec-beta.3 → 2.0.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 (91) hide show
  1. package/MIGRATING.md +65 -0
  2. package/NOTICE +4 -0
  3. package/ci/cypress/plugin.js +3 -0
  4. package/ci/cypress/support.js +1 -0
  5. package/ci/init.js +13 -0
  6. package/ci/jest/env.js +14 -0
  7. package/index.d.ts +35 -48
  8. package/package.json +7 -4
  9. package/packages/datadog-instrumentations/index.js +10 -0
  10. package/packages/datadog-instrumentations/src/bluebird.js +26 -0
  11. package/packages/datadog-instrumentations/src/dns.js +94 -0
  12. package/packages/datadog-instrumentations/src/helpers/instrument.js +120 -0
  13. package/packages/datadog-instrumentations/src/helpers/promise.js +29 -0
  14. package/packages/datadog-instrumentations/src/memcached.js +53 -0
  15. package/packages/datadog-instrumentations/src/mysql.js +67 -0
  16. package/packages/datadog-instrumentations/src/promise-js.js +15 -0
  17. package/packages/datadog-instrumentations/src/promise.js +14 -0
  18. package/packages/datadog-instrumentations/src/q.js +13 -0
  19. package/packages/datadog-instrumentations/src/when.js +14 -0
  20. package/packages/datadog-plugin-cucumber/src/index.js +4 -4
  21. package/packages/datadog-plugin-cypress/src/plugin.js +12 -2
  22. package/packages/datadog-plugin-cypress/src/support.js +21 -6
  23. package/packages/datadog-plugin-dns/src/index.js +65 -178
  24. package/packages/datadog-plugin-fs/src/index.js +7 -3
  25. package/packages/datadog-plugin-http/src/client.js +9 -24
  26. package/packages/datadog-plugin-http/src/server.js +5 -0
  27. package/packages/datadog-plugin-http2/src/client.js +1 -24
  28. package/packages/datadog-plugin-http2/src/server.js +2 -2
  29. package/packages/datadog-plugin-jest/src/jest-environment.js +4 -4
  30. package/packages/datadog-plugin-jest/src/jest-jasmine2.js +2 -2
  31. package/packages/datadog-plugin-knex/src/index.js +3 -3
  32. package/packages/datadog-plugin-memcached/src/index.js +41 -63
  33. package/packages/datadog-plugin-mocha/src/index.js +3 -2
  34. package/packages/datadog-plugin-moleculer/src/client.js +60 -0
  35. package/packages/datadog-plugin-moleculer/src/index.js +8 -0
  36. package/packages/datadog-plugin-moleculer/src/server.js +61 -0
  37. package/packages/datadog-plugin-moleculer/src/util.js +21 -0
  38. package/packages/datadog-plugin-mongoose/src/index.js +2 -2
  39. package/packages/datadog-plugin-mysql/src/index.js +37 -89
  40. package/packages/datadog-plugin-net/src/index.js +5 -0
  41. package/packages/datadog-plugin-pino/src/index.js +25 -1
  42. package/packages/datadog-plugin-redis/src/index.js +31 -1
  43. package/packages/datadog-plugin-router/src/index.js +28 -3
  44. package/packages/dd-trace/lib/version.js +1 -1
  45. package/packages/dd-trace/src/appsec/addresses.js +11 -4
  46. package/packages/dd-trace/src/appsec/callbacks/ddwaf.js +5 -8
  47. package/packages/dd-trace/src/{gateway → appsec/gateway}/als.js +1 -0
  48. package/packages/dd-trace/src/appsec/gateway/channels.js +11 -0
  49. package/packages/dd-trace/src/{gateway → appsec/gateway}/engine/engine.js +20 -30
  50. package/packages/dd-trace/src/{gateway → appsec/gateway}/engine/index.js +0 -0
  51. package/packages/dd-trace/src/{gateway → appsec/gateway}/engine/runner.js +2 -0
  52. package/packages/dd-trace/src/appsec/index.js +54 -38
  53. package/packages/dd-trace/src/appsec/recommended.json +1 -1
  54. package/packages/dd-trace/src/appsec/reporter.js +27 -10
  55. package/packages/dd-trace/src/config.js +31 -27
  56. package/packages/dd-trace/src/constants.js +6 -3
  57. package/packages/dd-trace/src/exporters/agent/request.js +8 -0
  58. package/packages/dd-trace/src/format.js +26 -39
  59. package/packages/dd-trace/src/instrumenter.js +6 -1
  60. package/packages/dd-trace/src/log.js +6 -15
  61. package/packages/dd-trace/src/noop/span_context.js +0 -1
  62. package/packages/dd-trace/src/noop/tracer.js +0 -6
  63. package/packages/dd-trace/src/opentracing/propagation/text_map.js +79 -46
  64. package/packages/dd-trace/src/opentracing/span.js +2 -7
  65. package/packages/dd-trace/src/opentracing/span_context.js +2 -4
  66. package/packages/dd-trace/src/opentracing/tracer.js +5 -23
  67. package/packages/dd-trace/src/plugin_manager.js +65 -0
  68. package/packages/dd-trace/src/plugins/index.js +1 -5
  69. package/packages/dd-trace/src/plugins/plugin.js +63 -0
  70. package/packages/dd-trace/src/plugins/util/ci.js +13 -4
  71. package/packages/dd-trace/src/plugins/util/redis.js +0 -2
  72. package/packages/dd-trace/src/plugins/util/test.js +9 -4
  73. package/packages/dd-trace/src/plugins/util/user-provided-git.js +17 -2
  74. package/packages/dd-trace/src/plugins/util/web.js +6 -16
  75. package/packages/dd-trace/src/priority_sampler.js +71 -19
  76. package/packages/dd-trace/src/profiling/exporters/agent.js +35 -34
  77. package/packages/dd-trace/src/proxy.js +39 -35
  78. package/packages/dd-trace/src/ritm.js +40 -16
  79. package/packages/dd-trace/src/span_processor.js +0 -7
  80. package/packages/dd-trace/src/tracer.js +5 -6
  81. package/scripts/install_plugin_modules.js +30 -1
  82. package/packages/datadog-plugin-bluebird/src/index.js +0 -69
  83. package/packages/datadog-plugin-promise/src/index.js +0 -17
  84. package/packages/datadog-plugin-promise-js/src/index.js +0 -20
  85. package/packages/datadog-plugin-q/src/index.js +0 -16
  86. package/packages/datadog-plugin-when/src/index.js +0 -17
  87. package/packages/dd-trace/src/gateway/channels.js +0 -8
  88. package/packages/dd-trace/src/gateway/dc_block.js +0 -68
  89. package/packages/dd-trace/src/plugins/util/ci-app-spec.json +0 -36
  90. package/packages/dd-trace/src/plugins/util/promise.js +0 -31
  91. package/packages/dd-trace/src/scope/noop/scope_manager.js +0 -28
@@ -8,7 +8,7 @@ const tags = require('../../../../../ext/tags')
8
8
  const types = require('../../../../../ext/types')
9
9
  const kinds = require('../../../../../ext/kinds')
10
10
  const urlFilter = require('./urlfilter')
11
- const { INCOMING_HTTP_REQUEST_START, INCOMING_HTTP_REQUEST_END } = require('../../gateway/channels')
11
+ const { incomingHttpRequestEnd } = require('../../appsec/gateway/channels')
12
12
 
13
13
  const WEB = types.WEB
14
14
  const SERVER = kinds.SERVER
@@ -23,6 +23,7 @@ const HTTP_STATUS_CODE = tags.HTTP_STATUS_CODE
23
23
  const HTTP_ROUTE = tags.HTTP_ROUTE
24
24
  const HTTP_REQUEST_HEADERS = tags.HTTP_REQUEST_HEADERS
25
25
  const HTTP_RESPONSE_HEADERS = tags.HTTP_RESPONSE_HEADERS
26
+ const MANUAL_DROP = tags.MANUAL_DROP
26
27
 
27
28
  const HTTP2_HEADER_AUTHORITY = ':authority'
28
29
  const HTTP2_HEADER_SCHEME = ':scheme'
@@ -54,9 +55,8 @@ const web = {
54
55
 
55
56
  const span = startSpan(tracer, config, req, res, name)
56
57
 
57
- // TODO: replace this with a REFERENCE_NOOP after we split http/express/etc
58
58
  if (!config.filter(req.url)) {
59
- span.context()._traceFlags.sampled = false
59
+ span.setTag(MANUAL_DROP, true)
60
60
  }
61
61
 
62
62
  if (config.service) {
@@ -72,17 +72,7 @@ const web = {
72
72
  req._datadog.instrumented = true
73
73
  }
74
74
 
75
- if (callback) {
76
- return tracer.scope().activate(span, () => {
77
- if (INCOMING_HTTP_REQUEST_START.hasSubscribers) {
78
- INCOMING_HTTP_REQUEST_START.publish({ req, res })
79
- }
80
-
81
- callback(span)
82
- })
83
- } else if (INCOMING_HTTP_REQUEST_START.hasSubscribers) {
84
- INCOMING_HTTP_REQUEST_START.publish({ req, res })
85
- }
75
+ return callback && tracer.scope().activate(span, () => callback(span))
86
76
  },
87
77
 
88
78
  // Reactivate the request scope in case it was changed by a middleware.
@@ -277,7 +267,7 @@ function wrapEnd (req) {
277
267
 
278
268
  finishMiddleware(req, res)
279
269
 
280
- if (INCOMING_HTTP_REQUEST_END.hasSubscribers) INCOMING_HTTP_REQUEST_END.publish({ req, res })
270
+ if (incomingHttpRequestEnd.hasSubscribers) incomingHttpRequestEnd.publish({ req, res })
281
271
 
282
272
  const returnValue = end.apply(res, arguments)
283
273
 
@@ -320,7 +310,7 @@ function addAllowHeaders (req, headers) {
320
310
  const contextHeaders = [
321
311
  'x-datadog-origin',
322
312
  'x-datadog-parent-id',
323
- 'x-datadog-sampled',
313
+ 'x-datadog-sampled', // Deprecated, but still accept it in case it's sent.
324
314
  'x-datadog-sampling-priority',
325
315
  'x-datadog-trace-id'
326
316
  ]
@@ -1,14 +1,20 @@
1
1
  'use strict'
2
2
 
3
+ const coalesce = require('koalas')
3
4
  const RateLimiter = require('./rate_limiter')
4
5
  const Sampler = require('./sampler')
5
6
  const ext = require('../../../ext')
6
7
  const { setSamplingRules } = require('./startup-log')
7
8
 
8
9
  const {
10
+ SAMPLING_MECHANISM_DEFAULT,
11
+ SAMPLING_MECHANISM_AGENT,
12
+ SAMPLING_MECHANISM_RULE,
13
+ SAMPLING_MECHANISM_MANUAL,
9
14
  SAMPLING_RULE_DECISION,
10
15
  SAMPLING_LIMIT_DECISION,
11
- SAMPLING_AGENT_DECISION
16
+ SAMPLING_AGENT_DECISION,
17
+ UPSTREAM_SERVICES_KEY
12
18
  } = require('./constants')
13
19
 
14
20
  const SERVICE_NAME = ext.tags.SERVICE_NAME
@@ -21,6 +27,9 @@ const AUTO_KEEP = ext.priority.AUTO_KEEP
21
27
  const USER_KEEP = ext.priority.USER_KEEP
22
28
  const DEFAULT_KEY = 'service:,env:'
23
29
 
30
+ const defaultSampler = new Sampler(AUTO_KEEP)
31
+ const serviceNames = new Map()
32
+
24
33
  class PrioritySampler {
25
34
  constructor (env, { sampleRate, rateLimit = 100, rules = [] } = {}) {
26
35
  this._env = env
@@ -33,12 +42,8 @@ class PrioritySampler {
33
42
  }
34
43
 
35
44
  isSampled (span) {
36
- const context = this._getContext(span)
37
- const rule = this._findRule(context)
38
-
39
- return rule
40
- ? this._isSampledByRule(context, rule) && this._isSampledByRateLimit(context)
41
- : this._isSampledByAgent(context)
45
+ const priority = this._getPriorityFromAuto(span)
46
+ return priority === USER_KEEP || priority === AUTO_KEEP
42
47
  }
43
48
 
44
49
  sample (span, auto = true) {
@@ -50,16 +55,18 @@ class PrioritySampler {
50
55
  if (context._sampling.priority !== undefined) return
51
56
  if (!root) return // noop span
52
57
 
53
- const tag = this._getPriority(context._tags)
58
+ const tag = this._getPriorityFromTags(context._tags)
54
59
 
55
60
  if (this.validate(tag)) {
56
61
  context._sampling.priority = tag
62
+ context._sampling.mechanism = SAMPLING_MECHANISM_MANUAL
63
+ } else if (auto) {
64
+ context._sampling.priority = this._getPriorityFromAuto(root)
65
+ } else {
57
66
  return
58
67
  }
59
68
 
60
- if (auto) {
61
- context._sampling.priority = this.isSampled(root) ? AUTO_KEEP : AUTO_REJECT
62
- }
69
+ this._addUpstreamService(root)
63
70
  }
64
71
 
65
72
  update (rates) {
@@ -72,7 +79,7 @@ class PrioritySampler {
72
79
  samplers[key] = sampler
73
80
  }
74
81
 
75
- samplers[DEFAULT_KEY] = samplers[DEFAULT_KEY] || new Sampler(AUTO_KEEP)
82
+ samplers[DEFAULT_KEY] = samplers[DEFAULT_KEY] || defaultSampler
76
83
 
77
84
  this._samplers = samplers
78
85
  }
@@ -93,7 +100,16 @@ class PrioritySampler {
93
100
  return typeof span.context === 'function' ? span.context() : span
94
101
  }
95
102
 
96
- _getPriority (tags) {
103
+ _getPriorityFromAuto (span) {
104
+ const context = this._getContext(span)
105
+ const rule = this._findRule(context)
106
+
107
+ return rule
108
+ ? this._getPriorityByRule(context, rule)
109
+ : this._getPriorityByAgent(context)
110
+ }
111
+
112
+ _getPriorityFromTags (tags) {
97
113
  if (tags.hasOwnProperty(MANUAL_KEEP) && tags[MANUAL_KEEP] !== false) {
98
114
  return USER_KEEP
99
115
  } else if (tags.hasOwnProperty(MANUAL_DROP) && tags[MANUAL_DROP] !== false) {
@@ -109,10 +125,11 @@ class PrioritySampler {
109
125
  }
110
126
  }
111
127
 
112
- _isSampledByRule (context, rule) {
128
+ _getPriorityByRule (context, rule) {
113
129
  context._trace[SAMPLING_RULE_DECISION] = rule.sampleRate
130
+ context._sampling.mechanism = SAMPLING_MECHANISM_RULE
114
131
 
115
- return rule.sampler.isSampled(context)
132
+ return rule.sampler.isSampled(context) && this._isSampledByRateLimit(context) ? USER_KEEP : USER_REJECT
116
133
  }
117
134
 
118
135
  _isSampledByRateLimit (context) {
@@ -123,13 +140,48 @@ class PrioritySampler {
123
140
  return allowed
124
141
  }
125
142
 
126
- _isSampledByAgent (context) {
143
+ _getPriorityByAgent (context) {
127
144
  const key = `service:${context._tags[SERVICE_NAME]},env:${this._env}`
128
145
  const sampler = this._samplers[key] || this._samplers[DEFAULT_KEY]
129
146
 
130
147
  context._trace[SAMPLING_AGENT_DECISION] = sampler.rate()
131
148
 
132
- return sampler.isSampled(context)
149
+ if (sampler === defaultSampler) {
150
+ context._sampling.mechanism = SAMPLING_MECHANISM_DEFAULT
151
+ } else {
152
+ context._sampling.mechanism = SAMPLING_MECHANISM_AGENT
153
+ }
154
+
155
+ return sampler.isSampled(context) ? AUTO_KEEP : AUTO_REJECT
156
+ }
157
+
158
+ _addUpstreamService (span) {
159
+ const context = span.context()
160
+ const trace = context._trace
161
+ const service = this._toBase64(context._tags['service.name'])
162
+ const priority = context._sampling.priority
163
+ const mechanism = context._sampling.mechanism
164
+ const rate = Math.ceil(coalesce(
165
+ context._trace[SAMPLING_RULE_DECISION],
166
+ context._trace[SAMPLING_AGENT_DECISION]
167
+ ) * 10000) / 10000
168
+ const group = `${service}|${priority}|${mechanism}|${rate}`
169
+ const groups = trace.tags[UPSTREAM_SERVICES_KEY]
170
+ ? `${trace.tags[UPSTREAM_SERVICES_KEY]};${group}`
171
+ : group
172
+
173
+ trace.tags[UPSTREAM_SERVICES_KEY] = groups
174
+ }
175
+
176
+ _toBase64 (serviceName) {
177
+ let encoded = serviceNames.get(serviceName)
178
+
179
+ if (!encoded) {
180
+ encoded = Buffer.from(serviceName).toString('base64')
181
+ serviceNames.set(serviceName, encoded)
182
+ }
183
+
184
+ return encoded
133
185
  }
134
186
 
135
187
  _normalizeRules (rules, sampleRate) {
@@ -151,9 +203,9 @@ class PrioritySampler {
151
203
  const service = context._tags['service.name']
152
204
 
153
205
  if (rule.name instanceof RegExp && !rule.name.test(name)) return false
154
- if (rule.name && rule.name !== name) return false
206
+ if (typeof rule.name === 'string' && rule.name !== name) return false
155
207
  if (rule.service instanceof RegExp && !rule.service.test(service)) return false
156
- if (rule.service && rule.service !== service) return false
208
+ if (typeof rule.service === 'string' && rule.service !== service) return false
157
209
 
158
210
  return true
159
211
  }
@@ -40,7 +40,7 @@ function computeRetries (uploadTimeout) {
40
40
  tries++
41
41
  uploadTimeout /= 2
42
42
  }
43
- return [tries, uploadTimeout]
43
+ return [tries, Math.floor(uploadTimeout)]
44
44
  }
45
45
 
46
46
  class AgentExporter {
@@ -55,7 +55,6 @@ class AgentExporter {
55
55
  }
56
56
 
57
57
  export ({ profiles, start, end, tags }) {
58
- const form = new FormData()
59
58
  const types = Object.keys(profiles)
60
59
 
61
60
  const fields = [
@@ -75,10 +74,6 @@ class AgentExporter {
75
74
  ...Object.entries(tags).map(([key, value]) => ['tags[]', `${key}:${value}`])
76
75
  ]
77
76
 
78
- for (const [key, value] of fields) {
79
- form.append(key, value)
80
- }
81
-
82
77
  this._logger.debug(() => {
83
78
  const body = fields.map(([key, value]) => ` ${key}: ${value}`).join('\n')
84
79
  return `Building agent export report: ${'\n' + body}`
@@ -93,36 +88,14 @@ class AgentExporter {
93
88
  return `Adding ${type} profile to agent export: ` + bytes
94
89
  })
95
90
 
96
- form.append(`types[${index}]`, type)
97
- form.append(`data[${index}]`, buffer, {
91
+ fields.push([`types[${index}]`, type])
92
+ fields.push([`data[${index}]`, buffer, {
98
93
  filename: `${type}.pb.gz`,
99
94
  contentType: 'application/octet-stream',
100
95
  knownLength: buffer.length
101
- })
102
- }
103
-
104
- const options = {
105
- method: 'POST',
106
- path: '/profiling/v1/input',
107
- headers: form.getHeaders()
108
- }
109
-
110
- if (containerId) {
111
- options.headers['Datadog-Container-ID'] = containerId
96
+ }])
112
97
  }
113
98
 
114
- if (this._url.protocol === 'unix:') {
115
- options.socketPath = this._url.pathname
116
- } else {
117
- options.protocol = this._url.protocol
118
- options.hostname = this._url.hostname
119
- options.port = this._url.port
120
- }
121
-
122
- this._logger.debug(() => {
123
- return `Submitting agent report to: ${JSON.stringify(options)}`
124
- })
125
-
126
99
  return new Promise((resolve, reject) => {
127
100
  const operation = retry.operation({
128
101
  randomize: true,
@@ -131,8 +104,36 @@ class AgentExporter {
131
104
  })
132
105
 
133
106
  operation.attempt((attempt) => {
134
- const timeout = Math.pow(this._backoffTime, attempt)
135
- sendRequest({ ...options, timeout }, form, (err, response) => {
107
+ const form = new FormData()
108
+
109
+ for (const [key, value, options] of fields) {
110
+ form.append(key, value, options)
111
+ }
112
+
113
+ const options = {
114
+ method: 'POST',
115
+ path: '/profiling/v1/input',
116
+ headers: form.getHeaders(),
117
+ timeout: this._backoffTime * Math.pow(2, attempt)
118
+ }
119
+
120
+ if (containerId) {
121
+ options.headers['Datadog-Container-ID'] = containerId
122
+ }
123
+
124
+ if (this._url.protocol === 'unix:') {
125
+ options.socketPath = this._url.pathname
126
+ } else {
127
+ options.protocol = this._url.protocol
128
+ options.hostname = this._url.hostname
129
+ options.port = this._url.port
130
+ }
131
+
132
+ this._logger.debug(() => {
133
+ return `Submitting profiler agent report attempt #${attempt} to: ${JSON.stringify(options)}`
134
+ })
135
+
136
+ sendRequest(options, form, (err, response) => {
136
137
  if (operation.retry(err)) {
137
138
  this._logger.error(`Error from the agent: ${err.message}`)
138
139
  return
@@ -159,4 +160,4 @@ class AgentExporter {
159
160
  }
160
161
  }
161
162
 
162
- module.exports = { AgentExporter }
163
+ module.exports = { AgentExporter, computeRetries }
@@ -5,8 +5,10 @@ const NoopTracer = require('./noop/tracer')
5
5
  const DatadogTracer = require('./tracer')
6
6
  const Config = require('./config')
7
7
  const Instrumenter = require('./instrumenter')
8
+ const PluginManager = require('./plugin_manager')
8
9
  const metrics = require('./metrics')
9
10
  const log = require('./log')
11
+ const { isFalse } = require('./util')
10
12
  const { setStartupLogInstrumenter } = require('./startup-log')
11
13
 
12
14
  const noop = new NoopTracer()
@@ -14,8 +16,11 @@ const noop = new NoopTracer()
14
16
  class Tracer extends BaseTracer {
15
17
  constructor () {
16
18
  super()
19
+
20
+ this._initialized = false
17
21
  this._tracer = noop
18
22
  this._instrumenter = new Instrumenter(this)
23
+ this._pluginManager = new PluginManager(this)
19
24
  this._deprecate = method => log.deprecate(`tracer.${method}`, [
20
25
  `tracer.${method}() is deprecated.`,
21
26
  'Please use tracer.startSpan() and tracer.scope() instead.',
@@ -24,47 +29,51 @@ class Tracer extends BaseTracer {
24
29
  }
25
30
 
26
31
  init (options) {
27
- if (this._tracer === noop) {
28
- try {
29
- const config = new Config(options)
30
-
31
- log.use(config.logger)
32
- log.toggle(config.debug, config.logLevel, this)
33
-
34
- if (config.hasOwnProperty('profiling') && config.profiling.enabled) {
35
- // do not stop tracer initialization if the profiler fails to be imported
36
- try {
37
- const profiler = require('./profiler')
38
- profiler.start(config)
39
- } catch (e) {
40
- log.error(e)
41
- }
42
- }
32
+ if (isFalse(process.env.DD_TRACE_ENABLED) || this._initialized) return this
33
+
34
+ this._initialized = true
43
35
 
44
- if (config.enabled) {
45
- if (config.runtimeMetrics) {
46
- metrics.start(config)
47
- }
36
+ try {
37
+ const config = new Config(options) // TODO: support dynamic config
48
38
 
49
- // dirty require for now so zero appsec code is executed unless explicitly enabled
50
- if (config.appsec.enabled) {
51
- require('./appsec').enable(config)
52
- }
39
+ log.use(config.logger)
40
+ log.toggle(config.debug, config.logLevel, this)
53
41
 
54
- this._tracer = new DatadogTracer(config)
55
- this._instrumenter.enable(config)
56
- setStartupLogInstrumenter(this._instrumenter)
42
+ if (config.profiling.enabled) {
43
+ // do not stop tracer initialization if the profiler fails to be imported
44
+ try {
45
+ const profiler = require('./profiler')
46
+ profiler.start(config)
47
+ } catch (e) {
48
+ log.error(e)
57
49
  }
58
- } catch (e) {
59
- log.error(e)
60
50
  }
51
+
52
+ if (config.runtimeMetrics) {
53
+ metrics.start(config)
54
+ }
55
+
56
+ if (config.tracing) {
57
+ // dirty require for now so zero appsec code is executed unless explicitly enabled
58
+ if (config.appsec.enabled) {
59
+ require('./appsec').enable(config)
60
+ }
61
+
62
+ this._tracer = new DatadogTracer(config)
63
+ this._instrumenter.enable(config)
64
+ this._pluginManager.configure(config)
65
+ setStartupLogInstrumenter(this._instrumenter)
66
+ }
67
+ } catch (e) {
68
+ log.error(e)
61
69
  }
62
70
 
63
71
  return this
64
72
  }
65
73
 
66
74
  use () {
67
- this._instrumenter.use.apply(this._instrumenter, arguments)
75
+ this._instrumenter.use(...arguments)
76
+ this._pluginManager.configurePlugin(...arguments)
68
77
  return this
69
78
  }
70
79
 
@@ -111,11 +120,6 @@ class Tracer extends BaseTracer {
111
120
  return this._tracer.extract.apply(this._tracer, arguments)
112
121
  }
113
122
 
114
- scopeManager () {
115
- this._deprecate('scopeManager')
116
- return this._tracer.scopeManager.apply(this._tracer, arguments)
117
- }
118
-
119
123
  scope () {
120
124
  return this._tracer.scope.apply(this._tracer, arguments)
121
125
  }
@@ -4,32 +4,56 @@ const path = require('path')
4
4
  const Module = require('module')
5
5
  const parse = require('module-details-from-path')
6
6
 
7
- module.exports = function hook (modules, onrequire) {
8
- if (!hook.orig) {
9
- hook.orig = Module.prototype.require
7
+ const origRequire = Module.prototype.require
10
8
 
11
- Module.prototype.require = function (request) {
12
- return hook.require.apply(this, arguments)
13
- }
9
+ // derived from require-in-the-middle@3 with tweaks
10
+
11
+ module.exports = Hook
12
+
13
+ Hook.reset = function () {
14
+ Module.prototype.require = origRequire
15
+ }
16
+
17
+ function Hook (modules, options, onrequire) {
18
+ if (!(this instanceof Hook)) return new Hook(modules, options, onrequire)
19
+ if (typeof modules === 'function') {
20
+ onrequire = modules
21
+ modules = null
22
+ options = {}
23
+ } else if (typeof options === 'function') {
24
+ onrequire = options
25
+ options = {}
14
26
  }
15
27
 
16
- hook.cache = {}
28
+ options = options || {}
29
+
30
+ this.cache = {}
31
+ this._unhooked = false
32
+ this._origRequire = Module.prototype.require
17
33
 
34
+ const self = this
18
35
  const patching = {}
19
36
 
20
- hook.require = function (request) {
37
+ this._require = Module.prototype.require = function (request) {
38
+ if (self._unhooked) {
39
+ // if the patched require function could not be removed because
40
+ // someone else patched it after it was patched here, we just
41
+ // abort and pass the request onwards to the original require
42
+ return self._origRequire.apply(this, arguments)
43
+ }
44
+
21
45
  const filename = Module._resolveFilename(request, this)
22
46
  const core = filename.indexOf(path.sep) === -1
23
47
  let name, basedir
24
48
 
25
49
  // return known patched modules immediately
26
- if (hook.cache.hasOwnProperty(filename)) {
50
+ if (self.cache.hasOwnProperty(filename)) {
27
51
  // require.cache was potentially altered externally
28
- if (require.cache[filename] && require.cache[filename].exports !== hook.cache[filename].original) {
52
+ if (require.cache[filename] && require.cache[filename].exports !== self.cache[filename].original) {
29
53
  return require.cache[filename].exports
30
54
  }
31
55
 
32
- return hook.cache[filename].exports
56
+ return self.cache[filename].exports
33
57
  }
34
58
 
35
59
  // Check if this module has a patcher in-progress already.
@@ -39,7 +63,7 @@ module.exports = function hook (modules, onrequire) {
39
63
  patching[filename] = true
40
64
  }
41
65
 
42
- const exports = hook.orig.apply(this, arguments)
66
+ const exports = self._origRequire.apply(this, arguments)
43
67
 
44
68
  // If it's already patched, just return it as-is.
45
69
  if (patched) return exports
@@ -75,10 +99,10 @@ module.exports = function hook (modules, onrequire) {
75
99
 
76
100
  // ensure that the cache entry is assigned a value before calling
77
101
  // onrequire, in case calling onrequire requires the same module.
78
- hook.cache[filename] = { exports }
79
- hook.cache[filename].original = exports
80
- hook.cache[filename].exports = onrequire(exports, name, basedir)
102
+ self.cache[filename] = { exports }
103
+ self.cache[filename].original = exports
104
+ self.cache[filename].exports = onrequire(exports, name, basedir)
81
105
 
82
- return hook.cache[filename].exports
106
+ return self.cache[filename].exports
83
107
  }
84
108
  }
@@ -16,13 +16,6 @@ class SpanProcessor {
16
16
 
17
17
  if (trace.started.length === trace.finished.length) {
18
18
  this._prioritySampler.sample(spanContext)
19
-
20
- if (spanContext._traceFlags.sampled === false) {
21
- log.debug(() => `Dropping trace due to user configured filtering: ${trace.started}`)
22
- this._erase(trace)
23
- return
24
- }
25
-
26
19
  const formattedSpans = trace.finished.map(format)
27
20
  this._exporter.export(formattedSpans)
28
21
  this._erase(trace)
@@ -2,8 +2,8 @@
2
2
 
3
3
  const Tracer = require('./opentracing/tracer')
4
4
  const tags = require('../../../ext/tags')
5
- const ScopeManager = require('./scope/noop/scope_manager')
6
5
  const Scope = require('./scope')
6
+ const { storage } = require('../../datadog-core')
7
7
  const { isError } = require('./util')
8
8
  const { setStartupLogConfig } = require('./startup-log')
9
9
 
@@ -16,7 +16,6 @@ class DatadogTracer extends Tracer {
16
16
  constructor (config) {
17
17
  super(config)
18
18
 
19
- this._scopeManager = new ScopeManager()
20
19
  this._scope = new Scope()
21
20
  setStartupLogConfig(config)
22
21
  }
@@ -68,6 +67,10 @@ class DatadogTracer extends Tracer {
68
67
  const tracer = this
69
68
 
70
69
  return function () {
70
+ const store = storage.getStore()
71
+
72
+ if (store && store.noop) return fn.apply(this, arguments)
73
+
71
74
  let optionsObj = options
72
75
  if (typeof optionsObj === 'function' && typeof fn === 'function') {
73
76
  optionsObj = optionsObj.apply(this, arguments)
@@ -100,10 +103,6 @@ class DatadogTracer extends Tracer {
100
103
  this._exporter.setUrl(url)
101
104
  }
102
105
 
103
- scopeManager () {
104
- return this._scopeManager
105
- }
106
-
107
106
  scope () {
108
107
  return this._scope
109
108
  }
@@ -4,9 +4,11 @@ const fs = require('fs')
4
4
  const path = require('path')
5
5
  const crypto = require('crypto')
6
6
  const semver = require('semver')
7
+ const proxyquire = require('proxyquire')
7
8
  const exec = require('./helpers/exec')
8
9
  const childProcess = require('child_process')
9
10
  const plugins = require('../packages/dd-trace/src/plugins')
11
+ const Plugin = require('../packages/dd-trace/src/plugins/plugin')
10
12
  const externals = require('../packages/dd-trace/test/plugins/externals')
11
13
 
12
14
  const requirePackageJsonPath = require.resolve('../packages/dd-trace/src/require-package-json')
@@ -23,6 +25,13 @@ Object.keys(externals).forEach(external => externals[external].forEach(thing =>
23
25
  }
24
26
  }))
25
27
 
28
+ fs.readdirSync(path.join(__dirname, '../packages/datadog-instrumentations/src'))
29
+ .filter(file => file.endsWith('js'))
30
+ .forEach(file => {
31
+ file = file.replace('.js', '')
32
+ plugins[file] = { name: file, prototype: Object.create(Plugin.prototype) }
33
+ })
34
+
26
35
  run()
27
36
 
28
37
  async function run () {
@@ -42,7 +51,27 @@ async function assertVersions () {
42
51
  }
43
52
 
44
53
  const internals = names
45
- .map(key => plugins[key])
54
+ .map(key => {
55
+ const plugin = plugins[key]
56
+ if (plugin.prototype instanceof Plugin) {
57
+ const instrumentations = []
58
+ const instrument = {
59
+ addHook (instrumentation) {
60
+ instrumentations.push(instrumentation)
61
+ }
62
+ }
63
+ const instPath = path.join(
64
+ __dirname,
65
+ `../packages/datadog-instrumentations/src/${plugin.name}.js`
66
+ )
67
+ proxyquire.noPreserveCache()(instPath, {
68
+ './helpers/instrument': instrument
69
+ })
70
+ return instrumentations
71
+ } else {
72
+ return plugin
73
+ }
74
+ })
46
75
  .reduce((prev, next) => prev.concat(next), [])
47
76
 
48
77
  for (const inst of internals) {