dd-trace 4.5.0 → 4.7.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 (62) hide show
  1. package/package.json +3 -3
  2. package/packages/datadog-instrumentations/src/cucumber.js +5 -2
  3. package/packages/datadog-instrumentations/src/grpc/client.js +44 -42
  4. package/packages/datadog-instrumentations/src/grpc/server.js +69 -60
  5. package/packages/datadog-instrumentations/src/http2/client.js +25 -26
  6. package/packages/datadog-instrumentations/src/jest.js +3 -1
  7. package/packages/datadog-instrumentations/src/kafkajs.js +11 -2
  8. package/packages/datadog-instrumentations/src/mocha.js +5 -3
  9. package/packages/datadog-instrumentations/src/redis.js +48 -5
  10. package/packages/datadog-plugin-cypress/src/plugin.js +4 -2
  11. package/packages/datadog-plugin-grpc/src/client.js +29 -11
  12. package/packages/datadog-plugin-grpc/src/server.js +22 -6
  13. package/packages/datadog-plugin-http2/src/client.js +46 -29
  14. package/packages/datadog-plugin-kafkajs/src/producer.js +6 -1
  15. package/packages/datadog-plugin-openai/src/services.js +14 -10
  16. package/packages/datadog-plugin-router/src/index.js +1 -1
  17. package/packages/dd-trace/src/appsec/iast/analyzers/command-injection-analyzer.js +3 -0
  18. package/packages/dd-trace/src/appsec/iast/analyzers/cookie-analyzer.js +7 -1
  19. package/packages/dd-trace/src/appsec/iast/analyzers/ldap-injection-analyzer.js +3 -0
  20. package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +19 -15
  21. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +5 -2
  22. package/packages/dd-trace/src/appsec/iast/analyzers/ssrf-analyzer.js +2 -0
  23. package/packages/dd-trace/src/appsec/iast/analyzers/unvalidated-redirect-analyzer.js +3 -1
  24. package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +18 -19
  25. package/packages/dd-trace/src/appsec/iast/analyzers/weak-cipher-analyzer.js +3 -0
  26. package/packages/dd-trace/src/appsec/iast/analyzers/weak-hash-analyzer.js +3 -0
  27. package/packages/dd-trace/src/appsec/iast/iast-log.js +1 -1
  28. package/packages/dd-trace/src/appsec/iast/iast-plugin.js +205 -0
  29. package/packages/dd-trace/src/appsec/iast/index.js +6 -5
  30. package/packages/dd-trace/src/appsec/iast/tags.js +2 -1
  31. package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +6 -6
  32. package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +3 -3
  33. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +23 -4
  34. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +32 -16
  35. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-telemetry.js +33 -0
  36. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +23 -16
  37. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +76 -37
  38. package/packages/dd-trace/src/appsec/iast/telemetry/iast-metric.js +101 -0
  39. package/packages/dd-trace/src/appsec/iast/telemetry/index.js +45 -0
  40. package/packages/dd-trace/src/appsec/iast/telemetry/{logs.js → log/index.js} +5 -5
  41. package/packages/dd-trace/src/appsec/iast/telemetry/{log_collector.js → log/log-collector.js} +1 -1
  42. package/packages/dd-trace/src/appsec/iast/telemetry/namespaces.js +76 -0
  43. package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +53 -0
  44. package/packages/dd-trace/src/appsec/iast/telemetry/verbosity.js +42 -0
  45. package/packages/dd-trace/src/config.js +21 -2
  46. package/packages/dd-trace/src/constants.js +1 -0
  47. package/packages/dd-trace/src/dogstatsd.js +14 -1
  48. package/packages/dd-trace/src/metrics.js +2 -2
  49. package/packages/dd-trace/src/opentracing/tracer.js +1 -0
  50. package/packages/dd-trace/src/plugins/ci_plugin.js +6 -1
  51. package/packages/dd-trace/src/plugins/outbound.js +29 -12
  52. package/packages/dd-trace/src/plugins/plugin.js +28 -0
  53. package/packages/dd-trace/src/plugins/tracing.js +33 -16
  54. package/packages/dd-trace/src/plugins/util/ci.js +1 -1
  55. package/packages/dd-trace/src/plugins/util/test.js +55 -11
  56. package/packages/dd-trace/src/plugins/util/user-provided-git.js +1 -22
  57. package/packages/dd-trace/src/profiling/config.js +0 -3
  58. package/packages/dd-trace/src/profiling/index.js +0 -2
  59. package/packages/dd-trace/src/profiling/profilers/wall.js +23 -11
  60. package/packages/diagnostics_channel/src/index.js +64 -0
  61. package/packages/dd-trace/src/profiling/profilers/cpu.js +0 -126
  62. /package/packages/dd-trace/src/appsec/iast/taint-tracking/{origin-types.js → source-types.js} +0 -0
@@ -3,7 +3,8 @@
3
3
  const {
4
4
  CLIENT_PORT_KEY,
5
5
  PEER_SERVICE_KEY,
6
- PEER_SERVICE_SOURCE_KEY
6
+ PEER_SERVICE_SOURCE_KEY,
7
+ PEER_SERVICE_REMAP_KEY
7
8
  } = require('../constants')
8
9
  const TracingPlugin = require('./tracing')
9
10
 
@@ -34,9 +35,11 @@ class OutboundPlugin extends TracingPlugin {
34
35
  * - If `peer.service` was defined _before_ we compute it (for example in custom instrumentation),
35
36
  * `_dd.peer.service.source`'s value is `peer.service`
36
37
  */
37
-
38
- if (tags['peer.service'] !== undefined) {
39
- return { [PEER_SERVICE_SOURCE_KEY]: 'peer.service' }
38
+ if (tags[PEER_SERVICE_KEY] !== undefined) {
39
+ return {
40
+ [PEER_SERVICE_KEY]: tags[PEER_SERVICE_KEY],
41
+ [PEER_SERVICE_SOURCE_KEY]: PEER_SERVICE_KEY
42
+ }
40
43
  }
41
44
 
42
45
  const sourceTags = [
@@ -52,23 +55,37 @@ class OutboundPlugin extends TracingPlugin {
52
55
  }
53
56
  }
54
57
  }
55
- return {}
58
+ return undefined
56
59
  }
57
60
 
58
- startSpan (name, options) {
59
- const span = super.startSpan(name, options)
60
- return span
61
+ getPeerServiceRemap (peerData) {
62
+ /**
63
+ * If DD_TRACE_PEER_SERVICE_MAPPING is matched, we need to override the existing
64
+ * peer service and add the value we overrode.
65
+ */
66
+ const peerService = peerData[PEER_SERVICE_KEY]
67
+ if (peerService && this.tracer._peerServiceMapping[peerService]) {
68
+ return {
69
+ ...peerData,
70
+ [PEER_SERVICE_KEY]: this.tracer._peerServiceMapping[peerService],
71
+ [PEER_SERVICE_REMAP_KEY]: peerService
72
+ }
73
+ }
74
+ return peerData
61
75
  }
62
76
 
63
77
  finish () {
64
- const span = this.activeSpan
78
+ this.tagPeerService(this.activeSpan)
79
+ super.finish(...arguments)
80
+ }
81
+
82
+ tagPeerService (span) {
65
83
  if (this.tracer._computePeerService) {
66
84
  const peerData = this.getPeerService(span.context()._tags)
67
- if (peerData) {
68
- span.addTags(peerData)
85
+ if (peerData !== undefined) {
86
+ span.addTags(this.getPeerServiceRemap(peerData))
69
87
  }
70
88
  }
71
- super.finish(...arguments)
72
89
  }
73
90
 
74
91
  connect (url) {
@@ -25,9 +25,31 @@ class Subscription {
25
25
  }
26
26
  }
27
27
 
28
+ class StoreBinding {
29
+ constructor (event, transform) {
30
+ this._channel = dc.channel(event)
31
+ this._transform = data => {
32
+ const store = storage.getStore()
33
+
34
+ return !store || !store.noop
35
+ ? transform(data)
36
+ : store
37
+ }
38
+ }
39
+
40
+ enable () {
41
+ this._channel.bindStore(storage, this._transform)
42
+ }
43
+
44
+ disable () {
45
+ this._channel.unbindStore(storage, this._transform)
46
+ }
47
+ }
48
+
28
49
  module.exports = class Plugin {
29
50
  constructor (tracer, tracerConfig) {
30
51
  this._subscriptions = []
52
+ this._bindings = []
31
53
  this._enabled = false
32
54
  this._tracer = tracer
33
55
  this.config = {} // plugin-specific configuration, unset until .configure() is called
@@ -53,6 +75,10 @@ module.exports = class Plugin {
53
75
  this._subscriptions.push(new Subscription(channelName, handler))
54
76
  }
55
77
 
78
+ addBind (channelName, transform) {
79
+ this._bindings.push(new StoreBinding(channelName, transform))
80
+ }
81
+
56
82
  addError (error) {
57
83
  const store = storage.getStore()
58
84
 
@@ -71,9 +97,11 @@ module.exports = class Plugin {
71
97
  if (config.enabled && !this._enabled) {
72
98
  this._enabled = true
73
99
  this._subscriptions.forEach(sub => sub.enable())
100
+ this._bindings.forEach(sub => sub.enable())
74
101
  } else if (!config.enabled && this._enabled) {
75
102
  this._enabled = false
76
103
  this._subscriptions.forEach(sub => sub.disable())
104
+ this._bindings.forEach(sub => sub.disable())
77
105
  }
78
106
  }
79
107
  }
@@ -13,17 +13,7 @@ class TracingPlugin extends Plugin {
13
13
  this.component = this.constructor.component || this.constructor.id
14
14
  this.operation = this.constructor.operation
15
15
 
16
- this.addTraceSub('start', message => {
17
- this.start(message)
18
- })
19
-
20
- this.addTraceSub('error', err => {
21
- this.error(err)
22
- })
23
-
24
- this.addTraceSub('finish', message => {
25
- this.finish(message)
26
- })
16
+ this.addTraceSubs()
27
17
  }
28
18
 
29
19
  get activeSpan () {
@@ -65,19 +55,43 @@ class TracingPlugin extends Plugin {
65
55
  this.addError(error)
66
56
  }
67
57
 
58
+ addTraceSubs () {
59
+ const events = ['start', 'end', 'asyncStart', 'asyncEnd', 'error', 'finish']
60
+
61
+ for (const event of events) {
62
+ const bindName = `bind${event.charAt(0).toUpperCase()}${event.slice(1)}`
63
+
64
+ if (this[event]) {
65
+ this.addTraceSub(event, message => {
66
+ this[event](message)
67
+ })
68
+ }
69
+
70
+ if (this[bindName]) {
71
+ this.addTraceBind(event, message => this[bindName](message))
72
+ }
73
+ }
74
+ }
75
+
68
76
  addTraceSub (eventName, handler) {
69
- this.addSub(`apm:${this.component}:${this.operation}:${eventName}`, handler)
77
+ const prefix = this.constructor.prefix || `apm:${this.component}:${this.operation}`
78
+ this.addSub(`${prefix}:${eventName}`, handler)
70
79
  }
71
80
 
72
- addError (error) {
73
- const span = this.activeSpan
81
+ addTraceBind (eventName, transform) {
82
+ const prefix = this.constructor.prefix || `apm:${this.component}:${this.operation}`
83
+ this.addBind(`${prefix}:${eventName}`, transform)
84
+ }
74
85
 
86
+ addError (error, span = this.activeSpan) {
75
87
  if (!span._spanContext._tags['error']) {
88
+ // Errors may be wrapped in a context.
89
+ error = (error && error.error) || error
76
90
  span.setTag('error', error || 1)
77
91
  }
78
92
  }
79
93
 
80
- startSpan (name, { childOf, kind, meta, metrics, service, resource, type } = {}) {
94
+ startSpan (name, { childOf, kind, meta, metrics, service, resource, type } = {}, enter = true) {
81
95
  const store = storage.getStore()
82
96
 
83
97
  if (store && childOf === undefined) {
@@ -100,7 +114,10 @@ class TracingPlugin extends Plugin {
100
114
 
101
115
  analyticsSampler.sample(span, this.config.measured)
102
116
 
103
- storage.enterWith({ ...store, span })
117
+ // TODO: Remove this after migration to TracingChannel is done.
118
+ if (enter) {
119
+ storage.enterWith({ ...store, span })
120
+ }
104
121
 
105
122
  return span
106
123
  }
@@ -77,7 +77,7 @@ function filterSensitiveInfoFromRepository (repositoryUrl) {
77
77
 
78
78
  return `${protocol}//${hostname}${pathname}`
79
79
  } catch (e) {
80
- return repositoryUrl
80
+ return ''
81
81
  }
82
82
  }
83
83
 
@@ -1,11 +1,13 @@
1
1
  const path = require('path')
2
2
  const fs = require('fs')
3
+ const { URL } = require('url')
4
+ const log = require('../../log')
3
5
 
4
6
  const istanbul = require('istanbul-lib-coverage')
5
7
  const ignore = require('ignore')
6
8
 
7
9
  const { getGitMetadata } = require('./git')
8
- const { getUserProviderGitMetadata } = require('./user-provided-git')
10
+ const { getUserProviderGitMetadata, validateGitRepositoryUrl, validateGitCommitSha } = require('./user-provided-git')
9
11
  const { getCIMetadata } = require('./ci')
10
12
  const { getRuntimeAndOSMetadata } = require('./env')
11
13
  const {
@@ -16,7 +18,8 @@ const {
16
18
  GIT_COMMIT_AUTHOR_EMAIL,
17
19
  GIT_COMMIT_AUTHOR_NAME,
18
20
  GIT_COMMIT_MESSAGE,
19
- CI_WORKSPACE_PATH
21
+ CI_WORKSPACE_PATH,
22
+ CI_PIPELINE_URL
20
23
  } = require('./tags')
21
24
  const id = require('../../id')
22
25
 
@@ -104,7 +107,8 @@ module.exports = {
104
107
  mergeCoverage,
105
108
  fromCoverageMapToCoverage,
106
109
  getTestLineStart,
107
- getCallSites
110
+ getCallSites,
111
+ removeInvalidMetadata
108
112
  }
109
113
 
110
114
  // Returns pkg manager and its version, separated by '-', e.g. npm-8.15.0 or yarn-1.22.19
@@ -116,6 +120,39 @@ function getPkgManager () {
116
120
  }
117
121
  }
118
122
 
123
+ function validateUrl (url) {
124
+ try {
125
+ const urlObject = new URL(url)
126
+ return (urlObject.protocol === 'https:' || urlObject.protocol === 'http:')
127
+ } catch (e) {
128
+ return false
129
+ }
130
+ }
131
+
132
+ function removeInvalidMetadata (metadata) {
133
+ return Object.keys(metadata).reduce((filteredTags, tag) => {
134
+ if (tag === GIT_REPOSITORY_URL) {
135
+ if (!validateGitRepositoryUrl(metadata[GIT_REPOSITORY_URL])) {
136
+ log.error('DD_GIT_REPOSITORY_URL must be a valid URL')
137
+ return filteredTags
138
+ }
139
+ }
140
+ if (tag === GIT_COMMIT_SHA) {
141
+ if (!validateGitCommitSha(metadata[GIT_COMMIT_SHA])) {
142
+ log.error('DD_GIT_COMMIT_SHA must be a full-length git SHA')
143
+ return filteredTags
144
+ }
145
+ }
146
+ if (tag === CI_PIPELINE_URL) {
147
+ if (!validateUrl(metadata[CI_PIPELINE_URL])) {
148
+ return filteredTags
149
+ }
150
+ }
151
+ filteredTags[tag] = metadata[tag]
152
+ return filteredTags
153
+ }, {})
154
+ }
155
+
119
156
  function getTestEnvironmentMetadata (testFramework, config) {
120
157
  // TODO: eventually these will come from the tracer (generally available)
121
158
  const ciMetadata = getCIMetadata()
@@ -155,7 +192,7 @@ function getTestEnvironmentMetadata (testFramework, config) {
155
192
  if (config && config.service) {
156
193
  metadata['service.name'] = config.service
157
194
  }
158
- return metadata
195
+ return removeInvalidMetadata(metadata)
159
196
  }
160
197
 
161
198
  function getTestParametersString (parametersByTestName, testName) {
@@ -173,6 +210,13 @@ function getTestParametersString (parametersByTestName, testName) {
173
210
  }
174
211
  }
175
212
 
213
+ function getTestTypeFromFramework (testFramework) {
214
+ if (testFramework === 'playwright' || testFramework === 'cypress') {
215
+ return 'browser'
216
+ }
217
+ return 'test'
218
+ }
219
+
176
220
  function finishAllTraceSpans (span) {
177
221
  span.context()._trace.started.forEach(traceSpan => {
178
222
  if (traceSpan !== span) {
@@ -188,10 +232,10 @@ function getTestParentSpan (tracer) {
188
232
  })
189
233
  }
190
234
 
191
- function getTestCommonTags (name, suite, version) {
235
+ function getTestCommonTags (name, suite, version, testFramework) {
192
236
  return {
193
237
  [SPAN_TYPE]: 'test',
194
- [TEST_TYPE]: 'test',
238
+ [TEST_TYPE]: getTestTypeFromFramework(testFramework),
195
239
  [SAMPLING_RULE_DECISION]: 1,
196
240
  [SAMPLING_PRIORITY]: AUTO_KEEP,
197
241
  [TEST_NAME]: name,
@@ -269,12 +313,12 @@ function getCodeOwnersForFilename (filename, entries) {
269
313
  return null
270
314
  }
271
315
 
272
- function getTestLevelCommonTags (command, testFrameworkVersion) {
316
+ function getTestLevelCommonTags (command, testFrameworkVersion, testFramework) {
273
317
  return {
274
318
  [TEST_FRAMEWORK_VERSION]: testFrameworkVersion,
275
319
  [LIBRARY_VERSION]: ddTraceVersion,
276
320
  [TEST_COMMAND]: command,
277
- [TEST_TYPE]: 'test'
321
+ [TEST_TYPE]: getTestTypeFromFramework(testFramework)
278
322
  }
279
323
  }
280
324
 
@@ -284,7 +328,7 @@ function getTestSessionCommonTags (command, testFrameworkVersion, testFramework)
284
328
  [RESOURCE_NAME]: `test_session.${command}`,
285
329
  [TEST_MODULE]: testFramework,
286
330
  [TEST_TOOLCHAIN]: getPkgManager(),
287
- ...getTestLevelCommonTags(command, testFrameworkVersion)
331
+ ...getTestLevelCommonTags(command, testFrameworkVersion, testFramework)
288
332
  }
289
333
  }
290
334
 
@@ -293,7 +337,7 @@ function getTestModuleCommonTags (command, testFrameworkVersion, testFramework)
293
337
  [SPAN_TYPE]: 'test_module_end',
294
338
  [RESOURCE_NAME]: `test_module.${command}`,
295
339
  [TEST_MODULE]: testFramework,
296
- ...getTestLevelCommonTags(command, testFrameworkVersion)
340
+ ...getTestLevelCommonTags(command, testFrameworkVersion, testFramework)
297
341
  }
298
342
  }
299
343
 
@@ -303,7 +347,7 @@ function getTestSuiteCommonTags (command, testFrameworkVersion, testSuite, testF
303
347
  [RESOURCE_NAME]: `test_suite.${testSuite}`,
304
348
  [TEST_MODULE]: testFramework,
305
349
  [TEST_SUITE]: testSuite,
306
- ...getTestLevelCommonTags(command, testFrameworkVersion)
350
+ ...getTestLevelCommonTags(command, testFrameworkVersion, testFramework)
307
351
  }
308
352
  }
309
353
 
@@ -13,7 +13,6 @@ const {
13
13
  } = require('./tags')
14
14
 
15
15
  const { normalizeRef } = require('./ci')
16
- const log = require('../../log')
17
16
  const { URL } = require('url')
18
17
 
19
18
  function removeEmptyValues (tags) {
@@ -53,25 +52,6 @@ function validateGitCommitSha (gitCommitSha) {
53
52
  return isValidSha1 || isValidSha256
54
53
  }
55
54
 
56
- function removeInvalidGitMetadata (metadata) {
57
- return Object.keys(metadata).reduce((filteredTags, tag) => {
58
- if (tag === GIT_REPOSITORY_URL) {
59
- if (!validateGitRepositoryUrl(metadata[GIT_REPOSITORY_URL])) {
60
- log.error('DD_GIT_REPOSITORY_URL must be a valid URL')
61
- return filteredTags
62
- }
63
- }
64
- if (tag === GIT_COMMIT_SHA) {
65
- if (!validateGitCommitSha(metadata[GIT_COMMIT_SHA])) {
66
- log.error('DD_GIT_COMMIT_SHA must be a full-length git SHA')
67
- return filteredTags
68
- }
69
- }
70
- filteredTags[tag] = metadata[tag]
71
- return filteredTags
72
- }, {})
73
- }
74
-
75
55
  function getUserProviderGitMetadata () {
76
56
  const {
77
57
  DD_GIT_COMMIT_SHA,
@@ -95,7 +75,7 @@ function getUserProviderGitMetadata () {
95
75
  tag = normalizeRef(DD_GIT_BRANCH)
96
76
  }
97
77
 
98
- const metadata = removeEmptyValues({
78
+ return removeEmptyValues({
99
79
  [GIT_COMMIT_SHA]: DD_GIT_COMMIT_SHA,
100
80
  [GIT_BRANCH]: branch,
101
81
  [GIT_REPOSITORY_URL]: filterSensitiveInfoFromRepository(DD_GIT_REPOSITORY_URL),
@@ -108,7 +88,6 @@ function getUserProviderGitMetadata () {
108
88
  [GIT_COMMIT_AUTHOR_EMAIL]: DD_GIT_COMMIT_AUTHOR_EMAIL,
109
89
  [GIT_COMMIT_AUTHOR_DATE]: DD_GIT_COMMIT_AUTHOR_DATE
110
90
  })
111
- return removeInvalidGitMetadata(metadata)
112
91
  }
113
92
 
114
93
  module.exports = { getUserProviderGitMetadata, validateGitRepositoryUrl, validateGitCommitSha }
@@ -7,7 +7,6 @@ const { URL, format, pathToFileURL } = require('url')
7
7
  const { AgentExporter } = require('./exporters/agent')
8
8
  const { FileExporter } = require('./exporters/file')
9
9
  const { ConsoleLogger } = require('./loggers/console')
10
- const CpuProfiler = require('./profilers/cpu')
11
10
  const WallProfiler = require('./profilers/wall')
12
11
  const SpaceProfiler = require('./profilers/space')
13
12
  const { oomExportStrategies, snapshotKinds } = require('./constants')
@@ -202,8 +201,6 @@ function getProfiler (name, options) {
202
201
  return new WallProfiler(options)
203
202
  case 'space':
204
203
  return new SpaceProfiler(options)
205
- case 'cpu-experimental':
206
- return new CpuProfiler(options)
207
204
  default:
208
205
  options.logger.error(`Unknown profiler "${name}"`)
209
206
  }
@@ -1,7 +1,6 @@
1
1
  'use strict'
2
2
 
3
3
  const { Profiler, ServerlessProfiler } = require('./profiler')
4
- const CpuProfiler = require('./profilers/cpu')
5
4
  const WallProfiler = require('./profilers/wall')
6
5
  const SpaceProfiler = require('./profilers/space')
7
6
  const { AgentExporter } = require('./exporters/agent')
@@ -14,7 +13,6 @@ module.exports = {
14
13
  profiler,
15
14
  AgentExporter,
16
15
  FileExporter,
17
- CpuProfiler,
18
16
  WallProfiler,
19
17
  SpaceProfiler,
20
18
  ConsoleLogger
@@ -3,12 +3,19 @@
3
3
  class NativeWallProfiler {
4
4
  constructor (options = {}) {
5
5
  this.type = 'wall'
6
- this._samplingInterval = options.samplingInterval || 1e6 / 99 // 99hz
6
+ this._samplingIntervalMicros = options.samplingInterval || 1e6 / 99 // 99hz
7
+ this._flushIntervalMillis = options.flushInterval || 60 * 1e3 // 60 seconds
8
+ this._codeHotspotsEnabled = !!options.codeHotspotsEnabled
7
9
  this._mapper = undefined
8
10
  this._pprof = undefined
11
+
12
+ this._logger = options.logger
13
+ this._started = false
9
14
  }
10
15
 
11
16
  start ({ mapper } = {}) {
17
+ if (this._started) return
18
+
12
19
  this._mapper = mapper
13
20
  this._pprof = require('@datadog/pprof')
14
21
 
@@ -20,12 +27,20 @@ class NativeWallProfiler {
20
27
  process._stopProfilerIdleNotifier = () => {}
21
28
  }
22
29
 
23
- this._record()
30
+ this._pprof.time.start({
31
+ intervalMicros: this._samplingIntervalMicros,
32
+ durationMillis: this._flushIntervalMillis,
33
+ sourceMapper: this._mapper,
34
+ customLabels: this._codeHotspotsEnabled,
35
+ lineNumbers: false
36
+ })
37
+
38
+ this._started = true
24
39
  }
25
40
 
26
41
  profile () {
27
- if (!this._stop) return
28
- return this._stop(true)
42
+ if (!this._started) return
43
+ return this._pprof.time.stop(true)
29
44
  }
30
45
 
31
46
  encode (profile) {
@@ -33,14 +48,11 @@ class NativeWallProfiler {
33
48
  }
34
49
 
35
50
  stop () {
36
- if (!this._stop) return
37
- this._stop()
38
- this._stop = undefined
39
- }
51
+ if (!this._started) return
40
52
 
41
- _record () {
42
- this._stop = this._pprof.time.start(this._samplingInterval, null,
43
- this._mapper, false)
53
+ const profile = this._pprof.time.stop()
54
+ this._started = false
55
+ return profile
44
56
  }
45
57
  }
46
58
 
@@ -54,4 +54,68 @@ if (major === '19' && minor === '9') {
54
54
  }
55
55
  }
56
56
 
57
+ if (!Channel.prototype.runStores) {
58
+ const ActiveChannelPrototype = getActiveChannelPrototype()
59
+
60
+ Channel.prototype.bindStore = ActiveChannelPrototype.bindStore = function (store, transform) {
61
+ if (!this._stores) {
62
+ this._stores = new Map()
63
+ }
64
+ this._stores.set(store, transform)
65
+ }
66
+
67
+ Channel.prototype.unbindStore = ActiveChannelPrototype.runStores = function (store) {
68
+ if (!this._stores) return
69
+ this._stores.delete(store)
70
+ }
71
+
72
+ Channel.prototype.runStores = ActiveChannelPrototype.runStores = function (data, fn, thisArg, ...args) {
73
+ if (!this._stores) return Reflect.apply(fn, thisArg, args)
74
+
75
+ let run = () => {
76
+ this.publish(data)
77
+ return Reflect.apply(fn, thisArg, args)
78
+ }
79
+
80
+ for (const entry of this._stores.entries()) {
81
+ const store = entry[0]
82
+ const transform = entry[1]
83
+ run = wrapStoreRun(store, data, run, transform)
84
+ }
85
+
86
+ return run()
87
+ }
88
+ }
89
+
90
+ function defaultTransform (data) {
91
+ return data
92
+ }
93
+
94
+ function wrapStoreRun (store, data, next, transform = defaultTransform) {
95
+ return () => {
96
+ let context
97
+ try {
98
+ context = transform(data)
99
+ } catch (err) {
100
+ process.nextTick(() => {
101
+ throw err
102
+ })
103
+ return next()
104
+ }
105
+
106
+ return store.run(context, next)
107
+ }
108
+ }
109
+
110
+ function getActiveChannelPrototype () {
111
+ const dummyChannel = channel('foo')
112
+ const listener = () => {}
113
+
114
+ dummyChannel.subscribe(listener)
115
+ const ActiveChannelPrototype = Object.getPrototypeOf(dummyChannel)
116
+ dummyChannel.unsubscribe(listener)
117
+
118
+ return ActiveChannelPrototype
119
+ }
120
+
57
121
  module.exports = dc