dd-trace 2.12.1 → 3.0.0-pre.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/ci/init.js CHANGED
@@ -1,10 +1,7 @@
1
1
  /* eslint-disable no-console */
2
-
3
- const path = require('path')
4
2
  const tracer = require('../packages/dd-trace')
5
3
  const { ORIGIN_KEY } = require('../packages/dd-trace/src/constants')
6
- const { mochaHook } = require('../packages/datadog-instrumentations/src/mocha')
7
- const { pickleHook, testCaseHook } = require('../packages/datadog-instrumentations/src/cucumber')
4
+ const { isTrue } = require('../packages/dd-trace/src/util')
8
5
 
9
6
  const options = {
10
7
  startupLogs: false,
@@ -15,9 +12,7 @@ const options = {
15
12
 
16
13
  let shouldInit = true
17
14
 
18
- const isAgentlessEnabled = process.env.DD_CIVISIBILITY_AGENTLESS_ENABLED &&
19
- process.env.DD_CIVISIBILITY_AGENTLESS_ENABLED !== 'false' &&
20
- process.env.DD_CIVISIBILITY_AGENTLESS_ENABLED !== '0'
15
+ const isAgentlessEnabled = isTrue(process.env.DD_CIVISIBILITY_AGENTLESS_ENABLED)
21
16
 
22
17
  if (isAgentlessEnabled) {
23
18
  if (process.env.DATADOG_API_KEY || process.env.DD_API_KEY) {
@@ -32,25 +27,6 @@ so dd-trace will not be initialized.`)
32
27
  }
33
28
  }
34
29
 
35
- // TODO: remove this in a later major version since we now recommend using
36
- // `NODE_OPTIONS='-r dd-trace/ci/init'`.
37
- try {
38
- for (const filename in require.cache) {
39
- const cache = require.cache[filename]
40
- const id = filename.split(path.sep).join('/')
41
-
42
- if (id.includes('/node_modules/mocha/lib/runner.js')) {
43
- cache.exports = mochaHook(cache.exports)
44
- } else if (id.includes('/node_modules/@cucumber/cucumber/lib/runtime/pickle_runner.js')) {
45
- cache.exports = pickleHook(cache.exports)
46
- } else if (id.includes('/node_modules/@cucumber/cucumber/lib/runtime/test_case_runner.js')) {
47
- cache.exports = testCaseHook(cache.exports)
48
- }
49
- }
50
- } catch (e) {
51
- // ignore error and let the tracer initialize anyway
52
- }
53
-
54
30
  if (shouldInit) {
55
31
  tracer.init(options)
56
32
  tracer.use('fs', false)
package/ci/jest/env.js CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  const tracer = require('../../packages/dd-trace')
4
4
  const { ORIGIN_KEY } = require('../../packages/dd-trace/src/constants')
5
+ const { isTrue } = require('../../packages/dd-trace/src/util')
5
6
 
6
7
  const options = {
7
8
  startupLogs: false,
@@ -10,9 +11,7 @@ const options = {
10
11
  }
11
12
  }
12
13
 
13
- const isAgentlessEnabled = process.env.DD_CIVISIBILITY_AGENTLESS_ENABLED &&
14
- process.env.DD_CIVISIBILITY_AGENTLESS_ENABLED !== 'false' &&
15
- process.env.DD_CIVISIBILITY_AGENTLESS_ENABLED !== '0'
14
+ const isAgentlessEnabled = isTrue(process.env.DD_CIVISIBILITY_AGENTLESS_ENABLED)
16
15
 
17
16
  if (isAgentlessEnabled) {
18
17
  if (process.env.DATADOG_API_KEY || process.env.DD_API_KEY) {
package/index.d.ts CHANGED
@@ -562,7 +562,6 @@ interface Plugins {
562
562
  "elasticsearch": plugins.elasticsearch;
563
563
  "express": plugins.express;
564
564
  "fastify": plugins.fastify;
565
- "fs": plugins.fs;
566
565
  "generic-pool": plugins.generic_pool;
567
566
  "google-cloud-pubsub": plugins.google_cloud_pubsub;
568
567
  "graphql": plugins.graphql;
@@ -903,12 +902,6 @@ declare namespace plugins {
903
902
  */
904
903
  interface fastify extends HttpServer {}
905
904
 
906
- /**
907
- * This plugin automatically instruments the
908
- * [fs](https://nodejs.org/api/fs.html) module.
909
- */
910
- interface fs extends Instrumentation {}
911
-
912
905
  /**
913
906
  * This plugin patches the [generic-pool](https://github.com/coopernurse/node-pool)
914
907
  * module to bind the callbacks the the caller context.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dd-trace",
3
- "version": "2.12.1",
3
+ "version": "3.0.0-pre.2",
4
4
  "description": "Datadog APM tracing client for JavaScript",
5
5
  "main": "index.js",
6
6
  "typings": "index.d.ts",
@@ -19,13 +19,13 @@ class GrpcClientPlugin extends Plugin {
19
19
  const metadataFilter = this.config.metadataFilter
20
20
  const store = storage.getStore()
21
21
  const childOf = store && store.span
22
- const span = this.tracer.startSpan('grpc.request', {
22
+ const span = this.tracer.startSpan('grpc.client', {
23
23
  childOf,
24
24
  tags: {
25
25
  [Tags.SPAN_KIND]: 'client',
26
26
  'span.type': 'http',
27
27
  'resource.name': path,
28
- 'service.name': this.config.service || `${this.tracer._service}-grpc-client`,
28
+ 'service.name': this.config.service || this.tracer._service,
29
29
  'component': 'grpc'
30
30
  }
31
31
  })
@@ -19,13 +19,13 @@ class GrpcServerPlugin extends Plugin {
19
19
  const metadataFilter = this.config.metadataFilter
20
20
  const store = storage.getStore()
21
21
  const childOf = extract(this.tracer, metadata)
22
- const span = this.tracer.startSpan('grpc.request', {
22
+ const span = this.tracer.startSpan('grpc.server', {
23
23
  childOf,
24
24
  tags: {
25
25
  [Tags.SPAN_KIND]: 'server',
26
26
  'span.type': 'web',
27
27
  'resource.name': name,
28
- 'service.name': this.config.service || `${this.tracer._service}`,
28
+ 'service.name': this.config.service || this.tracer._service,
29
29
  'component': 'grpc'
30
30
  }
31
31
  })
@@ -190,7 +190,7 @@ function getServiceName (tracer, config, options) {
190
190
  return config.service
191
191
  }
192
192
 
193
- return `${tracer._service}-http-client`
193
+ return tracer._service
194
194
  }
195
195
 
196
196
  function getHost (options) {
@@ -15,7 +15,7 @@ class HttpServerPlugin extends Plugin {
15
15
 
16
16
  this.addSub('apm:http:server:request:start', ({ req, res }) => {
17
17
  const store = storage.getStore()
18
- const span = web.startSpan(this.tracer, this.config, req, res, 'http.request')
18
+ const span = web.startSpan(this.tracer, this.config, req, res, 'web.request')
19
19
 
20
20
  this.enter(span, { ...store, req })
21
21
 
@@ -118,7 +118,7 @@ function getServiceName (tracer, config, sessionDetails) {
118
118
  return config.service
119
119
  }
120
120
 
121
- return `${tracer._service}-http-client`
121
+ return tracer._service
122
122
  }
123
123
 
124
124
  function hasAmazonSignature (headers, path) {
@@ -13,6 +13,9 @@ const uuid = require('crypto-randomuuid')
13
13
  const fromEntries = Object.fromEntries || (entries =>
14
14
  entries.reduce((obj, [k, v]) => Object.assign(obj, { [k]: v }), {}))
15
15
 
16
+ // eslint-disable-next-line max-len
17
+ const qsRegex = '(?:p(?:ass)?w(?:or)?d|pass(?:_?phrase)?|secret|(?:api_?|private_?|public_?|access_?|secret_?)key(?:_?id)?|token|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)(?:(?:\\s|%20)*(?:=|%3D)[^&]+|(?:"|%22)(?:\\s|%20)*(?::|%3A)(?:\\s|%20)*(?:"|%22)(?:%2[^2]|%[^2]|[^"%])+(?:"|%22))|bearer(?:\\s|%20)+[a-z0-9\\._\\-]|token(?::|%3A)[a-z0-9]{13}|gh[opsu]_[0-9a-zA-Z]{36}|ey[I-L](?:[\\w=-]|%3D)+\\.ey[I-L](?:[\\w=-]|%3D)+(?:\\.(?:[\\w.+\\/=-]|%3D|%2F|%2B)+)?|[\\-]{5}BEGIN(?:[a-z\\s]|%20)+PRIVATE(?:\\s|%20)KEY[\\-]{5}[^\\-]+[\\-]{5}END(?:[a-z\\s]|%20)+PRIVATE(?:\\s|%20)KEY|ssh-rsa(?:\\s|%20)*(?:[a-z0-9\\/\\.+]|%2F|%5C|%2B){100,}'
18
+
16
19
  class Config {
17
20
  constructor (options) {
18
21
  options = options || {}
@@ -109,6 +112,10 @@ class Config {
109
112
  parseInt(process.env.DD_TRACE_PARTIAL_FLUSH_MIN_SPANS),
110
113
  1000
111
114
  )
115
+ const DD_TRACE_OBFUSCATION_QUERY_STRING_REGEXP = coalesce(
116
+ process.env.DD_TRACE_OBFUSCATION_QUERY_STRING_REGEXP,
117
+ qsRegex
118
+ )
112
119
  const DD_TRACE_B3_ENABLED = coalesce(
113
120
  options.experimental && options.experimental.b3,
114
121
  process.env.DD_TRACE_EXPERIMENTAL_B3_ENABLED,
@@ -203,6 +210,7 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
203
210
  this.flushInterval = coalesce(parseInt(options.flushInterval, 10), defaultFlushInterval)
204
211
  this.flushMinSpans = DD_TRACE_PARTIAL_FLUSH_MIN_SPANS
205
212
  this.sampleRate = coalesce(Math.min(Math.max(sampler.sampleRate, 0), 1), 1)
213
+ this.queryStringObfuscation = DD_TRACE_OBFUSCATION_QUERY_STRING_REGEXP
206
214
  this.logger = options.logger
207
215
  this.plugins = !!coalesce(options.plugins, true)
208
216
  this.service = DD_SERVICE
@@ -57,6 +57,10 @@ class AgentEncoder {
57
57
  return buffer
58
58
  }
59
59
 
60
+ reset () {
61
+ this._reset()
62
+ }
63
+
60
64
  _encode (bytes, trace) {
61
65
  this._encodeArrayPrefix(bytes, trace)
62
66
 
@@ -1,22 +1,33 @@
1
1
  'use strict'
2
2
 
3
+ // TODO: Add test with slow or unresponsive agent.
4
+ // TODO: Add telemetry for things like dropped requests, errors, etc.
5
+
3
6
  const http = require('http')
4
7
  const https = require('https')
5
- const log = require('../../log')
6
8
  const docker = require('./docker')
7
9
  const { storage } = require('../../../../datadog-core')
8
10
 
9
- const httpAgent = new http.Agent({ keepAlive: true })
10
- const httpsAgent = new https.Agent({ keepAlive: true })
11
+ const keepAlive = true
12
+ const maxTotalSockets = 1
13
+ const maxActiveRequests = 8
14
+ const httpAgent = new http.Agent({ keepAlive, maxTotalSockets })
15
+ const httpsAgent = new https.Agent({ keepAlive, maxTotalSockets })
11
16
  const containerId = docker.id()
12
17
 
18
+ let activeRequests = 0
19
+
13
20
  function request (data, options, keepAlive, callback) {
14
21
  if (!options.headers) {
15
22
  options.headers = {}
16
23
  }
24
+
25
+ // The timeout should be kept low to avoid excessive queueing.
26
+ const timeout = options.timeout || 2000
17
27
  const isSecure = options.protocol === 'https:'
18
28
  const client = isSecure ? https : http
19
29
  const dataArray = [].concat(data)
30
+
20
31
  options.headers['Content-Length'] = byteLength(dataArray)
21
32
 
22
33
  if (containerId) {
@@ -27,39 +38,15 @@ function request (data, options, keepAlive, callback) {
27
38
  options.agent = isSecure ? httpsAgent : httpAgent
28
39
  }
29
40
 
30
- const firstRequest = retriableRequest(options, client, callback)
31
- dataArray.forEach(buffer => firstRequest.write(buffer))
32
-
33
- // The first request will be retried
34
- const firstRequestErrorHandler = () => {
35
- log.debug('Retrying request to the intake')
36
- const retriedReq = retriableRequest(options, client, callback)
37
- dataArray.forEach(buffer => retriedReq.write(buffer))
38
- // The retried request will fail normally
39
- retriedReq.on('error', e => callback(new Error(`Network error trying to reach the intake: ${e.message}`)))
40
- retriedReq.end()
41
- }
42
-
43
- firstRequest.on('error', firstRequestErrorHandler)
44
- firstRequest.end()
45
-
46
- return firstRequest
47
- }
48
-
49
- function retriableRequest (options, client, callback) {
50
- const store = storage.getStore()
51
-
52
- storage.enterWith({ noop: true })
53
-
54
- const timeout = options.timeout || 15000
55
-
56
- const request = client.request(options, res => {
41
+ const onResponse = res => {
57
42
  let responseData = ''
58
43
 
59
44
  res.setTimeout(timeout)
60
45
 
61
46
  res.on('data', chunk => { responseData += chunk })
62
47
  res.on('end', () => {
48
+ activeRequests--
49
+
63
50
  if (res.statusCode >= 200 && res.statusCode <= 299) {
64
51
  callback(null, responseData, res.statusCode)
65
52
  } else {
@@ -69,15 +56,43 @@ function retriableRequest (options, client, callback) {
69
56
  callback(error, null, res.statusCode)
70
57
  }
71
58
  })
72
- })
73
- request.setTimeout(timeout, request.abort)
74
- storage.enterWith(store)
59
+ }
75
60
 
76
- return request
61
+ const makeRequest = onError => {
62
+ if (!request.writable) return callback(null)
63
+
64
+ activeRequests++
65
+
66
+ const store = storage.getStore()
67
+
68
+ storage.enterWith({ noop: true })
69
+
70
+ const req = client.request(options, onResponse)
71
+
72
+ req.once('error', err => {
73
+ activeRequests--
74
+ onError(err)
75
+ })
76
+
77
+ dataArray.forEach(buffer => req.write(buffer))
78
+
79
+ req.setTimeout(timeout, req.abort)
80
+ req.end()
81
+
82
+ storage.enterWith(store)
83
+ }
84
+
85
+ makeRequest(() => makeRequest(callback))
77
86
  }
78
87
 
79
88
  function byteLength (data) {
80
89
  return data.length > 0 ? data.reduce((prev, next) => prev + next.length, 0) : 0
81
90
  }
82
91
 
92
+ Object.defineProperty(request, 'writable', {
93
+ get () {
94
+ return activeRequests < maxActiveRequests
95
+ }
96
+ })
97
+
83
98
  module.exports = request
@@ -1,4 +1,6 @@
1
1
  'use strict'
2
+
3
+ const request = require('./request')
2
4
  const log = require('../../log')
3
5
 
4
6
  class Writer {
@@ -9,7 +11,10 @@ class Writer {
9
11
  flush (done = () => {}) {
10
12
  const count = this._encoder.count()
11
13
 
12
- if (count > 0) {
14
+ if (!request.writable) {
15
+ this._encoder.reset()
16
+ done()
17
+ } else if (count > 0) {
13
18
  const payload = this._encoder.makePayload()
14
19
 
15
20
  this._sendPayload(payload, count, done)
@@ -19,6 +24,8 @@ class Writer {
19
24
  }
20
25
 
21
26
  append (spans) {
27
+ if (!request.writable) return
28
+
22
29
  log.debug(() => `Encoding trace: ${JSON.stringify(spans)}`)
23
30
 
24
31
  this._encode(spans)
@@ -24,10 +24,6 @@ class NoopTracer extends Tracer {
24
24
  return this._scope
25
25
  }
26
26
 
27
- currentSpan () {
28
- return null
29
- }
30
-
31
27
  getRumData () {
32
28
  return ''
33
29
  }
@@ -63,7 +63,7 @@ module.exports = class PluginManager {
63
63
 
64
64
  // like instrumenter.enable()
65
65
  configure (config = {}) {
66
- const { logInjection, serviceMapping, experimental } = config
66
+ const { logInjection, serviceMapping, experimental, queryStringObfuscation } = config
67
67
 
68
68
  for (const PluginClass of Object.values(plugins)) {
69
69
  const name = PluginClass.name
@@ -87,6 +87,10 @@ module.exports = class PluginManager {
87
87
  pluginConfig.logInjection = logInjection
88
88
  }
89
89
 
90
+ if (queryStringObfuscation !== undefined) {
91
+ pluginConfig.queryStringObfuscation = queryStringObfuscation
92
+ }
93
+
90
94
  // TODO: update so that it's available for every CI Visibility's plugin
91
95
  if (name === 'mocha') {
92
96
  pluginConfig.isAgentlessEnabled = experimental && experimental.exporter === 'datadog'
@@ -15,7 +15,6 @@ module.exports = {
15
15
  'express': require('../../../datadog-plugin-express/src'),
16
16
  'fastify': require('../../../datadog-plugin-fastify/src'),
17
17
  'find-my-way': require('../../../datadog-plugin-find-my-way/src'),
18
- 'fs': require('../../../datadog-plugin-fs/src'),
19
18
  'google-cloud-pubsub': require('../../../datadog-plugin-google-cloud-pubsub/src'),
20
19
  'graphql': require('../../../datadog-plugin-graphql/src'),
21
20
  'grpc': require('../../../datadog-plugin-grpc/src'),
@@ -43,13 +43,15 @@ const web = {
43
43
  const hooks = getHooks(config)
44
44
  const filter = urlFilter.getFilter(config)
45
45
  const middleware = getMiddlewareSetting(config)
46
+ const queryStringObfuscation = getQsObfuscator(config)
46
47
 
47
48
  return Object.assign({}, config, {
48
49
  headers,
49
50
  validateStatus,
50
51
  hooks,
51
52
  filter,
52
- middleware
53
+ middleware,
54
+ queryStringObfuscation
53
55
  })
54
56
  },
55
57
 
@@ -318,6 +320,23 @@ const web = {
318
320
  web.finishSpan(context)
319
321
  },
320
322
 
323
+ obfuscateQs (config, url) {
324
+ const { queryStringObfuscation } = config
325
+
326
+ if (queryStringObfuscation === false) return url
327
+
328
+ const i = url.indexOf('?')
329
+ if (i === -1) return url
330
+
331
+ const path = url.slice(0, i)
332
+ if (queryStringObfuscation === true) return path
333
+
334
+ let qs = url.slice(i + 1)
335
+
336
+ qs = qs.replace(queryStringObfuscation, '<redacted>')
337
+
338
+ return `${path}?${qs}`
339
+ },
321
340
  wrapWriteHead (context) {
322
341
  const { req, res } = context
323
342
  const writeHead = res.writeHead
@@ -407,11 +426,11 @@ function reactivate (req, fn) {
407
426
  }
408
427
 
409
428
  function addRequestTags (context) {
410
- const { req, span } = context
429
+ const { req, span, config } = context
411
430
  const url = extractURL(req)
412
431
 
413
432
  span.addTags({
414
- [HTTP_URL]: url.split('?')[0],
433
+ [HTTP_URL]: web.obfuscateQs(config, url),
415
434
  [HTTP_METHOD]: req.method,
416
435
  [SPAN_KIND]: SERVER,
417
436
  [SPAN_TYPE]: WEB,
@@ -522,4 +541,30 @@ function getMiddlewareSetting (config) {
522
541
  return true
523
542
  }
524
543
 
544
+ function getQsObfuscator (config) {
545
+ const obfuscator = config.queryStringObfuscation
546
+
547
+ if (typeof obfuscator === 'boolean') {
548
+ return obfuscator
549
+ }
550
+
551
+ if (typeof obfuscator === 'string') {
552
+ if (obfuscator === '') return false // disable obfuscator
553
+
554
+ if (obfuscator === '.*') return true // optimize full redact
555
+
556
+ try {
557
+ return new RegExp(obfuscator, 'gi')
558
+ } catch (err) {
559
+ log.error(err)
560
+ }
561
+ }
562
+
563
+ if (config.hasOwnProperty('queryStringObfuscation')) {
564
+ log.error('Expected `queryStringObfuscation` to be a regex string or boolean.')
565
+ }
566
+
567
+ return true
568
+ }
569
+
525
570
  module.exports = web
@@ -22,11 +22,6 @@ class Tracer extends BaseTracer {
22
22
  this._tracer = noop
23
23
  this._instrumenter = new Instrumenter(this)
24
24
  this._pluginManager = new PluginManager(this)
25
- this._deprecate = method => log.deprecate(`tracer.${method}`, [
26
- `tracer.${method}() is deprecated.`,
27
- 'Please use tracer.startSpan() and tracer.scope() instead.',
28
- 'See: https://datadog.github.io/dd-trace-js/#manual-instrumentation.'
29
- ].join(' '))
30
25
  }
31
26
 
32
27
  init (options) {
@@ -126,20 +121,6 @@ class Tracer extends BaseTracer {
126
121
  return this._tracer.scope.apply(this._tracer, arguments)
127
122
  }
128
123
 
129
- currentSpan () {
130
- this._deprecate('currentSpan')
131
- return this._tracer.currentSpan.apply(this._tracer, arguments)
132
- }
133
-
134
- bind (callback) {
135
- this._deprecate('bind')
136
- return callback
137
- }
138
-
139
- bindEmitter () {
140
- this._deprecate('bindEmitter')
141
- }
142
-
143
124
  getRumData () {
144
125
  return this._tracer.getRumData.apply(this._tracer, arguments)
145
126
  }
@@ -2,13 +2,9 @@
2
2
 
3
3
  const { storage } = require('../../datadog-core')
4
4
 
5
- // TODO: deprecate binding event emitters in 3.0
5
+ // TODO: refactor bind to use shimmer once the new internal tracer lands
6
6
 
7
7
  const originals = new WeakMap()
8
- const listenerMaps = new WeakMap()
9
- const emitterSpans = new WeakMap()
10
- const emitterScopes = new WeakMap()
11
- const emitters = new WeakSet()
12
8
 
13
9
  class Scope {
14
10
  active () {
@@ -39,7 +35,6 @@ class Scope {
39
35
  }
40
36
 
41
37
  bind (target, span) {
42
- target = this._bindEmitter(target, span)
43
38
  target = this._bindPromise(target, span)
44
39
  target = this._bindFn(target, span)
45
40
 
@@ -49,7 +44,6 @@ class Scope {
49
44
  unbind (target) {
50
45
  target = this._unbindFn(target)
51
46
  target = this._unbindPromise(target)
52
- target = this._unbindEmitter(target)
53
47
 
54
48
  return target
55
49
  }
@@ -77,37 +71,6 @@ class Scope {
77
71
  return originals.get(fn) || fn
78
72
  }
79
73
 
80
- _bindEmitter (emitter, span) {
81
- if (!this._isEmitter(emitter)) return emitter
82
- if (!emitters.has(emitter)) {
83
- Scope._wrapEmitter(emitter)
84
- }
85
- emitterSpans.set(emitter, span)
86
- emitterScopes.set(emitter, this)
87
- return emitter
88
- }
89
-
90
- // Occasionally we want to wrap a prototype rather than emitter instances,
91
- // so we're exposing this as a static method. This gives us a faster
92
- // path for binding instances of known EventEmitter subclasses.
93
- static _wrapEmitter (emitter) {
94
- wrapMethod(emitter, 'addListener', wrapAddListener)
95
- wrapMethod(emitter, 'prependListener', wrapAddListener)
96
- wrapMethod(emitter, 'on', wrapAddListener)
97
- wrapMethod(emitter, 'once', wrapAddListener)
98
- wrapMethod(emitter, 'removeListener', wrapRemoveListener)
99
- wrapMethod(emitter, 'off', wrapRemoveListener)
100
- wrapMethod(emitter, 'removeAllListeners', wrapRemoveAllListeners)
101
- emitters.add(emitter)
102
- }
103
-
104
- _unbindEmitter (emitter) {
105
- if (!this._isEmitter(emitter)) return emitter
106
- emitterScopes.delete(emitter)
107
- emitterSpans.delete(emitter)
108
- return emitter
109
- }
110
-
111
74
  _bindPromise (promise, span) {
112
75
  if (!this._isPromise(promise)) return promise
113
76
 
@@ -128,31 +91,11 @@ class Scope {
128
91
  return span !== undefined ? span : this.active()
129
92
  }
130
93
 
131
- _isEmitter (emitter) {
132
- return emitter &&
133
- typeof emitter.emit === 'function' &&
134
- typeof emitter.on === 'function' &&
135
- typeof emitter.addListener === 'function' &&
136
- typeof emitter.removeListener === 'function'
137
- }
138
-
139
94
  _isPromise (promise) {
140
95
  return promise && typeof promise.then === 'function'
141
96
  }
142
97
  }
143
98
 
144
- function getScope (emitter) {
145
- return emitterScopes.get(emitter) || emitterScopes.get(emitter.constructor.prototype)
146
- }
147
-
148
- function getSpan (emitter) {
149
- return emitterSpans.get(emitter) || emitterSpans.get(emitter.constructor.prototype)
150
- }
151
-
152
- function hasScope (emitter) {
153
- return emitterScopes.has(emitter) || emitterScopes.has(emitter.constructor.prototype)
154
- }
155
-
156
99
  function wrapThen (then, scope, span) {
157
100
  return function thenWithTrace (onFulfilled, onRejected) {
158
101
  const args = new Array(arguments.length)
@@ -165,74 +108,6 @@ function wrapThen (then, scope, span) {
165
108
  }
166
109
  }
167
110
 
168
- function wrapAddListener (addListener) {
169
- return function addListenerWithTrace (eventName, listener) {
170
- const scope = getScope(this)
171
- if (!scope || !listener || originals.has(listener) || listener.listener) {
172
- return addListener.apply(this, arguments)
173
- }
174
- const span = getSpan(this)
175
-
176
- const bound = scope.bind(listener, scope._spanOrActive(span))
177
- const listenerMap = listenerMaps.get(this) || {}
178
-
179
- listenerMaps.set(this, listenerMap)
180
-
181
- if (!listenerMap[eventName]) {
182
- listenerMap[eventName] = new WeakMap()
183
- }
184
-
185
- const events = listenerMap[eventName]
186
-
187
- if (!events.has(listener)) {
188
- events.set(listener, [])
189
- }
190
-
191
- events.get(listener).push(bound)
192
-
193
- return addListener.call(this, eventName, bound)
194
- }
195
- }
196
-
197
- function wrapRemoveListener (removeListener) {
198
- return function removeListenerWithTrace (eventName, listener) {
199
- if (!hasScope(this)) {
200
- return removeListener.apply(this, arguments)
201
- }
202
-
203
- const listenerMap = listenerMaps.get(this)
204
- const listeners = listenerMap && listenerMap[eventName]
205
-
206
- if (!listener || !listeners || !listeners.has(listener)) {
207
- return removeListener.apply(this, arguments)
208
- }
209
-
210
- for (const bound of listeners.get(listener)) {
211
- removeListener.call(this, eventName, bound)
212
- }
213
-
214
- listeners.delete(listener)
215
-
216
- return removeListener.apply(this, arguments)
217
- }
218
- }
219
-
220
- function wrapRemoveAllListeners (removeAllListeners) {
221
- return function removeAllListenersWithTrace (eventName) {
222
- const listenerMap = listenerMaps.get(this)
223
-
224
- if (hasScope(this) && listenerMap) {
225
- if (eventName) {
226
- delete listenerMap[eventName]
227
- } else {
228
- listenerMaps.delete(this)
229
- }
230
- }
231
-
232
- return removeAllListeners.apply(this, arguments)
233
- }
234
- }
235
-
236
111
  function wrapMethod (target, name, wrapper, ...args) {
237
112
  if (!target[name] || originals.has(target[name])) return
238
113
 
@@ -107,10 +107,6 @@ class DatadogTracer extends Tracer {
107
107
  return this._scope
108
108
  }
109
109
 
110
- currentSpan () {
111
- return this.scope().active()
112
- }
113
-
114
110
  getRumData () {
115
111
  if (!this._enableGetRumData) {
116
112
  return ''
package/cypress/plugin.js DELETED
@@ -1,5 +0,0 @@
1
- require('..').init({
2
- startupLogs: false
3
- })
4
-
5
- module.exports = require('../packages/datadog-plugin-cypress/src/plugin')
@@ -1 +0,0 @@
1
- require('../packages/datadog-plugin-cypress/src/support')
@@ -1,548 +0,0 @@
1
- 'use strict'
2
-
3
- const { storage } = require('../../datadog-core')
4
-
5
- let kDirReadPromisified
6
- let kDirClosePromisified
7
- let kHandle
8
-
9
- const ddFhSym = Symbol('ddFileHandle')
10
-
11
- const tagMakers = {
12
- open: createOpenTags,
13
- close: createCloseTags,
14
- readFile: createReadFileTags,
15
- writeFile: createWriteFileTags,
16
- appendFile: createAppendFileTags,
17
- access: createPathTags,
18
- copyFile: createCopyFileTags,
19
- stat: createPathTags,
20
- lstat: createPathTags,
21
- fstat: createFDTags,
22
- readdir: createPathTags,
23
- opendir: createPathTags,
24
- read: createFDTags,
25
- write: createFDTags,
26
- writev: createFDTags,
27
- chmod: createChmodTags,
28
- lchmod: createChmodTags,
29
- fchmod: createFchmodTags,
30
- chown: createChownTags,
31
- lchown: createChownTags,
32
- fchown: createFchownTags,
33
- realpath: createPathTags,
34
- readlink: createPathTags,
35
- unlink: createPathTags,
36
- symlink: createCopyFileTags,
37
- link: createCopyFileTags,
38
- rmdir: createPathTags,
39
- rename: createCopyFileTags,
40
- fsync: createFDTags,
41
- fdatasync: createFDTags,
42
- mkdir: createPathTags,
43
- truncate: createPathTags,
44
- ftruncate: createFDTags,
45
- utimes: createPathTags,
46
- futimes: createFDTags,
47
- mkdtemp: createPathTags
48
- }
49
-
50
- const promisifiable = ['read', 'readv', 'write', 'writev']
51
-
52
- const orphanable = false
53
-
54
- function createWrapCreateReadStream (config, tracer) {
55
- return function wrapCreateReadStream (createReadStream) {
56
- return function createReadStreamWithTrace (path, options) {
57
- if (!hasParent()) {
58
- return createReadStream.apply(this, arguments)
59
- }
60
- const tags = makeFSFlagTags('ReadStream', path, options, 'r', config, tracer)
61
- return tracer.trace('fs.operation', { tags, orphanable }, (span, done) => {
62
- const stream = createReadStream.apply(this, arguments)
63
- stream.once('close', done)
64
- stream.once('end', done)
65
- stream.once('error', done)
66
- return stream
67
- })
68
- }
69
- }
70
- }
71
-
72
- function createWrapCreateWriteStream (config, tracer) {
73
- return function wrapCreateWriteStream (createWriteStream) {
74
- return function createWriteStreamWithTrace (path, options) {
75
- const tags = makeFSFlagTags('WriteStream', path, options, 'w', config, tracer)
76
- return tracer.trace('fs.operation', { tags, orphanable }, (span, done) => {
77
- const stream = createWriteStream.apply(this, arguments)
78
- stream.once('close', done)
79
- stream.once('finish', done)
80
- stream.once('error', done)
81
- return stream
82
- })
83
- }
84
- }
85
- }
86
-
87
- function createWrapExists (config, tracer) {
88
- return function wrapExists (exists) {
89
- const existsWithTrace = function existsWithTrace (path, cb) {
90
- if (typeof cb !== 'function') {
91
- return exists.apply(this, arguments)
92
- }
93
- const tags = makeFSTags('exists', path, null, config, tracer)
94
- return tracer.trace('fs.operation', { tags, orphanable }, (span, done) => {
95
- arguments[1] = function (result) {
96
- done()
97
- cb.apply(this, arguments)
98
- }
99
- return exists.apply(this, arguments)
100
- })
101
- }
102
-
103
- copySymbols(exists, existsWithTrace)
104
-
105
- return existsWithTrace
106
- }
107
- }
108
-
109
- function createWrapDirRead (config, tracer, sync) {
110
- const name = sync ? 'dir.readSync' : 'dir.read'
111
- return function wrapDirRead (read) {
112
- function options () {
113
- const tags = makeFSTags(name, this.path, null, config, tracer)
114
- return { tags, orphanable }
115
- }
116
- return tracer.wrap('fs.operation', options, read, true)
117
- }
118
- }
119
-
120
- function createWrapDirClose (config, tracer, sync) {
121
- const name = sync ? 'dir.closeSync' : 'dir.close'
122
- return function wrapDirClose (close) {
123
- function options () {
124
- const tags = makeFSTags(name, this.path, null, config, tracer)
125
- return { tags, orphanable }
126
- }
127
- return tracer.wrap('fs.operation', options, close, true)
128
- }
129
- }
130
-
131
- function createWrapDirAsyncIterator (config, tracer, instrumenter) {
132
- return function wrapDirAsyncIterator (asyncIterator) {
133
- return function asyncIteratorWithTrace () {
134
- if (!kDirReadPromisified) {
135
- const keys = Reflect.ownKeys(this)
136
- for (const key of keys) {
137
- if (kDirReadPromisified && kDirClosePromisified) break
138
- if (typeof key !== 'symbol') continue
139
- if (!kDirReadPromisified && getSymbolName(key).includes('kDirReadPromisified')) {
140
- kDirReadPromisified = key
141
- }
142
- if (!kDirClosePromisified && getSymbolName(key).includes('kDirClosePromisified')) {
143
- kDirClosePromisified = key
144
- }
145
- }
146
- }
147
- instrumenter.wrap(this, kDirReadPromisified, createWrapDirRead(config, tracer))
148
- instrumenter.wrap(this, kDirClosePromisified, createWrapKDirClose(config, tracer, instrumenter))
149
- return asyncIterator.apply(this, arguments)
150
- }
151
- }
152
- }
153
-
154
- function createWrapKDirClose (config, tracer, instrumenter) {
155
- return function wrapKDirClose (kDirClose) {
156
- return function kDirCloseWithTrace () {
157
- const tags = makeFSTags('dir.close', this.path, null, config, tracer)
158
- return tracer.trace('fs.operation', { tags, orphanable }, (span) => {
159
- const p = kDirClose.apply(this, arguments)
160
- const unwrapBoth = () => {
161
- instrumenter.unwrap(this, kDirReadPromisified)
162
- instrumenter.unwrap(this, kDirClosePromisified)
163
- }
164
- p.then(unwrapBoth, unwrapBoth)
165
- return p
166
- })
167
- }
168
- }
169
- }
170
-
171
- function createOpenTags (resourceName, config, tracer) {
172
- return function openTags (path, flag, mode) {
173
- if (!flag || typeof flag === 'function') {
174
- flag = null
175
- }
176
- return makeFSFlagTags(resourceName, path, { flag }, 'r', config, tracer)
177
- }
178
- }
179
-
180
- function createCloseTags (resourceName, config, tracer) {
181
- return function closeTags (fd) {
182
- if (typeof fd === 'undefined' && this && this[ddFhSym]) {
183
- fd = this[ddFhSym].fd
184
- }
185
- if (typeof fd !== 'number' || !Number.isInteger(fd)) {
186
- return
187
- }
188
- return makeFSTags(resourceName, fd, null, config, tracer)
189
- }
190
- }
191
-
192
- function createReadFileTags (resourceName, config, tracer) {
193
- return function readFileTags (path, options) {
194
- return makeFSFlagTags(resourceName, path, options, 'r', config, tracer)
195
- }
196
- }
197
-
198
- function createWriteFileTags (resourceName, config, tracer) {
199
- return function writeFileTags (path, data, options) {
200
- return makeFSFlagTags(resourceName, path, options, 'w', config, tracer)
201
- }
202
- }
203
-
204
- function createAppendFileTags (resourceName, config, tracer) {
205
- return function appendFileTags (path, data, options) {
206
- return makeFSFlagTags(resourceName, path, options, 'a', config, tracer)
207
- }
208
- }
209
-
210
- function createCopyFileTags (resourceName, config, tracer) {
211
- return function copyFileTags (src, dest, flag) {
212
- return makeFSTags(resourceName, { src, dest }, null, config, tracer)
213
- }
214
- }
215
-
216
- function createChmodTags (resourceName, config, tracer) {
217
- return function chmodTags (fd, mode) {
218
- const tags = makeFSTags(resourceName, fd, null, config, tracer)
219
- tags['file.mode'] = mode.toString(8)
220
- return tags
221
- }
222
- }
223
-
224
- function createFchmodTags (resourceName, config, tracer) {
225
- return function fchmodTags (fd, mode) {
226
- if (typeof this === 'object' && this !== null && this.fd) {
227
- mode = fd
228
- fd = this.fd
229
- }
230
-
231
- const tags = makeFSTags(resourceName, fd, null, config, tracer)
232
- if (mode) {
233
- tags['file.mode'] = mode.toString(8)
234
- }
235
- return tags
236
- }
237
- }
238
-
239
- function createPathTags (resourceName, config, tracer) {
240
- return function pathTags (path) {
241
- return makeFSTags(resourceName, path, null, config, tracer)
242
- }
243
- }
244
-
245
- function createFDTags (resourceName, config, tracer) {
246
- return function fdTags (fd) {
247
- if (typeof this === 'object' && this !== null && this.fd) {
248
- fd = this.fd
249
- }
250
- return makeFSTags(resourceName, fd, null, config, tracer)
251
- }
252
- }
253
-
254
- function createChownTags (resourceName, config, tracer) {
255
- return function chownTags (fd, uid, gid) {
256
- const tags = makeFSTags(resourceName, fd, null, config, tracer)
257
- if (typeof uid === 'number') {
258
- tags['file.uid'] = uid.toString()
259
- }
260
- if (typeof gid === 'number') {
261
- tags['file.gid'] = gid.toString()
262
- }
263
- return tags
264
- }
265
- }
266
-
267
- function createFchownTags (resourceName, config, tracer) {
268
- return function fchownTags (fd, uid, gid) {
269
- if (typeof this === 'object' && this !== null && this.fd) {
270
- gid = uid
271
- uid = fd
272
- fd = this.fd
273
- }
274
- const tags = makeFSTags(resourceName, fd, null, config, tracer)
275
- if (typeof uid === 'number') {
276
- tags['file.uid'] = uid.toString()
277
- }
278
- if (typeof gid === 'number') {
279
- tags['file.gid'] = gid.toString()
280
- }
281
- return tags
282
- }
283
- }
284
-
285
- function getSymbolName (sym) {
286
- return sym.description || sym.toString()
287
- }
288
-
289
- function hasParent () {
290
- const store = storage.getStore()
291
-
292
- return store && store.span && !store.noop
293
- }
294
-
295
- function createWrapCb (tracer, config, name, tagMaker) {
296
- const makeTags = tagMaker(name, config, tracer)
297
- return function wrapFunction (fn) {
298
- return tracer.wrap('fs.operation', function () {
299
- if (typeof arguments[arguments.length - 1] !== 'function') {
300
- return
301
- }
302
- const tags = makeTags.apply(this, arguments)
303
- return tags ? { tags, orphanable } : { orphanable }
304
- }, fn, true)
305
- }
306
- }
307
-
308
- function createWrap (tracer, config, name, tagMaker) {
309
- const makeTags = tagMaker(name, config, tracer)
310
-
311
- return function wrapSyncFunction (fn) {
312
- return tracer.wrap('fs.operation', function () {
313
- const tags = makeTags.apply(this, arguments)
314
- return tags ? { tags, orphanable } : { orphanable }
315
- }, fn, true)
316
- }
317
- }
318
-
319
- function makeFSFlagTags (resourceName, path, options, defaultFlag, config, tracer) {
320
- const tags = makeFSTags(resourceName, path, options, config, tracer)
321
-
322
- if (tags) {
323
- let flag = defaultFlag
324
- if (typeof options === 'object' && options !== null) {
325
- if (options.flag) {
326
- flag = options.flag
327
- } else if (options.flags) {
328
- flag = options.flags
329
- }
330
- }
331
- tags['file.flag'] = flag
332
- return tags
333
- }
334
- }
335
-
336
- function makeFSTags (resourceName, path, options, config, tracer) {
337
- path = options && typeof options === 'object' && 'fd' in options ? options.fd : path
338
- const tags = {
339
- 'component': 'fs',
340
- 'span.kind': 'internal',
341
- 'resource.name': resourceName,
342
- 'service.name': config.service || tracer._service
343
- }
344
-
345
- switch (typeof path) {
346
- case 'object': {
347
- if (path === null) return tags
348
- const src = 'src' in path ? path.src : null
349
- const dest = 'dest' in path ? path.dest : null
350
- if (src || dest) {
351
- tags['file.src'] = src
352
- tags['file.dest'] = dest
353
- } else {
354
- tags['file.path'] = path
355
- }
356
- break
357
- }
358
- case 'string': {
359
- tags['file.path'] = path
360
- break
361
- }
362
- case 'number': {
363
- tags['file.descriptor'] = path.toString()
364
- break
365
- }
366
- }
367
-
368
- return tags
369
- }
370
-
371
- function copySymbols (from, to) {
372
- const props = Object.getOwnPropertyDescriptors(from)
373
- const keys = Reflect.ownKeys(props)
374
-
375
- for (const key of keys) {
376
- if (typeof key !== 'symbol' || to.hasOwnProperty(key)) continue
377
-
378
- Object.defineProperty(to, key, props[key])
379
- }
380
- }
381
-
382
- function getFileHandlePrototype (fs) {
383
- return fs.promises.open(__filename, 'r')
384
- .then(fh => {
385
- if (!kHandle) {
386
- kHandle = Reflect.ownKeys(fh).find(key => typeof key === 'symbol' && key.toString().includes('kHandle'))
387
- }
388
- fh.close()
389
-
390
- return Object.getPrototypeOf(fh)
391
- })
392
- }
393
-
394
- function patchClassicFunctions (fs, tracer, config) {
395
- for (const name in fs) {
396
- if (!fs[name]) continue
397
- const tagMakerName = name.endsWith('Sync') ? name.substr(0, name.length - 4) : name
398
- const original = fs[name]
399
- if (tagMakerName in tagMakers) {
400
- const tagMaker = tagMakers[tagMakerName]
401
- if (name.endsWith('Sync')) {
402
- this.wrap(fs, name, createWrap(tracer, config, name, tagMaker))
403
- } else {
404
- this.wrap(fs, name, createWrapCb(tracer, config, name, tagMaker))
405
- }
406
- if (name in promisifiable) {
407
- copySymbols(original, fs[name])
408
- }
409
- }
410
- }
411
- }
412
-
413
- function patchFileHandle (fs, tracer, config) {
414
- getFileHandlePrototype(fs).then((fileHandlePrototype) => {
415
- for (const name of Reflect.ownKeys(fileHandlePrototype)) {
416
- if (typeof name !== 'string' || name === 'constructor' || name === 'fd' || name === 'getAsyncId') {
417
- continue
418
- }
419
- let tagMaker
420
- const fName = 'f' + name
421
- if (fName in tagMakers) {
422
- tagMaker = tagMakers[fName]
423
- } else {
424
- tagMaker = createFDTags
425
- }
426
-
427
- const instrumenter = this
428
-
429
- const desc = Reflect.getOwnPropertyDescriptor(fileHandlePrototype, kHandle)
430
- if (!desc || !desc.get) {
431
- Reflect.defineProperty(fileHandlePrototype, kHandle, {
432
- get () {
433
- return this[ddFhSym]
434
- },
435
- set (h) {
436
- this[ddFhSym] = h
437
- instrumenter.wrap(this, 'close', createWrap(tracer, config, 'filehandle.close', tagMakers.close))
438
- },
439
- configurable: true
440
- })
441
- }
442
-
443
- this.wrap(fileHandlePrototype, name, createWrap(tracer, config, 'filehandle.' + name, tagMaker))
444
- }
445
- })
446
- }
447
-
448
- function patchPromiseFunctions (fs, tracer, config) {
449
- for (const name in fs.promises) {
450
- if (name in tagMakers) {
451
- const tagMaker = tagMakers[name]
452
- this.wrap(fs.promises, name, createWrap(tracer, config, 'promises.' + name, tagMaker))
453
- }
454
- }
455
- }
456
-
457
- function patchDirFunctions (fs, tracer, config) {
458
- this.wrap(fs.Dir.prototype, 'close', createWrapDirClose(config, tracer))
459
- this.wrap(fs.Dir.prototype, 'closeSync', createWrapDirClose(config, tracer, true))
460
- this.wrap(fs.Dir.prototype, 'read', createWrapDirRead(config, tracer))
461
- this.wrap(fs.Dir.prototype, 'readSync', createWrapDirRead(config, tracer, true))
462
- this.wrap(fs.Dir.prototype, Symbol.asyncIterator, createWrapDirAsyncIterator(config, tracer, this))
463
- }
464
-
465
- function unpatchClassicFunctions (fs) {
466
- for (const name in fs) {
467
- if (!fs[name]) continue
468
- const tagMakerName = name.endsWith('Sync') ? name.substr(0, name.length - 4) : name
469
- if (tagMakerName in tagMakers) {
470
- this.unwrap(fs, name)
471
- }
472
- }
473
- }
474
-
475
- function unpatchFileHandle (fs) {
476
- getFileHandlePrototype(fs).then(fileHandlePrototype => {
477
- for (const name of Reflect.ownKeys(fileHandlePrototype)) {
478
- if (typeof name !== 'string' || name === 'constructor' || name === 'fd' || name === 'getAsyncId') {
479
- continue
480
- }
481
- this.unwrap(fileHandlePrototype, name)
482
- }
483
- delete fileHandlePrototype[kHandle]
484
- })
485
- }
486
-
487
- function unpatchPromiseFunctions (fs) {
488
- for (const name in fs.promises) {
489
- if (name in tagMakers) {
490
- this.unwrap(fs.promises, name)
491
- }
492
- }
493
- }
494
-
495
- function unpatchDirFunctions (fs) {
496
- this.unwrap(fs.Dir.prototype, 'close')
497
- this.unwrap(fs.Dir.prototype, 'closeSync')
498
- this.unwrap(fs.Dir.prototype, 'read')
499
- this.unwrap(fs.Dir.prototype, 'readSync')
500
- this.unwrap(fs.Dir.prototype, Symbol.asyncIterator)
501
- }
502
-
503
- module.exports = {
504
- name: 'fs',
505
- patch (fs, tracer, config) {
506
- const realpathNative = fs.realpath.native
507
- const realpathSyncNative = fs.realpathSync.native
508
- patchClassicFunctions.call(this, fs, tracer, config)
509
- if (fs.promises) {
510
- patchFileHandle.call(this, fs, tracer, config)
511
- patchPromiseFunctions.call(this, fs, tracer, config)
512
- }
513
- if (fs.Dir) {
514
- patchDirFunctions.call(this, fs, tracer, config)
515
- }
516
- this.wrap(fs, 'createReadStream', createWrapCreateReadStream(config, tracer))
517
- this.wrap(fs, 'createWriteStream', createWrapCreateWriteStream(config, tracer))
518
- this.wrap(fs, 'existsSync', createWrap(tracer, config, 'existsSync', createPathTags))
519
- this.wrap(fs, 'exists', createWrapExists(config, tracer))
520
- if (realpathNative) {
521
- fs.realpath.native = createWrapCb(tracer, config, 'realpath.native', createPathTags)(realpathNative)
522
- }
523
- if (realpathSyncNative) {
524
- fs.realpathSync.native = createWrap(tracer, config, 'realpath.native', createPathTags)(realpathSyncNative)
525
- }
526
- },
527
- unpatch (fs) {
528
- unpatchClassicFunctions.call(this, fs)
529
- if (fs.promises) {
530
- unpatchFileHandle.call(this, fs)
531
- unpatchPromiseFunctions.call(this, fs)
532
- }
533
- if (fs.Dir) {
534
- unpatchDirFunctions.call(this, fs)
535
- }
536
- this.unwrap(fs, 'createReadStream')
537
- this.unwrap(fs, 'createWriteStream')
538
- this.unwrap(fs, 'existsSync')
539
- this.unwrap(fs, 'exists')
540
- }
541
- }
542
-
543
- /** TODO fs functions:
544
-
545
- unwatchFile
546
- watch
547
- watchFile
548
- */