dd-trace 4.47.0 → 4.48.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 (84) hide show
  1. package/LICENSE-3rdparty.csv +0 -1
  2. package/ext/types.d.ts +1 -0
  3. package/ext/types.js +1 -0
  4. package/index.d.ts +26 -0
  5. package/package.json +6 -7
  6. package/packages/datadog-code-origin/index.js +38 -0
  7. package/packages/datadog-core/index.js +2 -2
  8. package/packages/datadog-instrumentations/src/avsc.js +37 -0
  9. package/packages/datadog-instrumentations/src/azure-functions.js +48 -0
  10. package/packages/datadog-instrumentations/src/child_process.js +17 -8
  11. package/packages/datadog-instrumentations/src/express.js +37 -4
  12. package/packages/datadog-instrumentations/src/fastify.js +12 -1
  13. package/packages/datadog-instrumentations/src/fs.js +27 -7
  14. package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -0
  15. package/packages/datadog-instrumentations/src/jest.js +2 -1
  16. package/packages/datadog-instrumentations/src/mocha/common.js +1 -1
  17. package/packages/datadog-instrumentations/src/mysql2.js +220 -1
  18. package/packages/datadog-instrumentations/src/protobufjs.js +127 -0
  19. package/packages/datadog-instrumentations/src/winston.js +22 -0
  20. package/packages/datadog-plugin-avsc/src/index.js +9 -0
  21. package/packages/datadog-plugin-avsc/src/schema_iterator.js +169 -0
  22. package/packages/datadog-plugin-azure-functions/src/index.js +77 -0
  23. package/packages/datadog-plugin-fastify/src/code_origin.js +31 -0
  24. package/packages/datadog-plugin-fastify/src/index.js +10 -12
  25. package/packages/datadog-plugin-fastify/src/tracing.js +19 -0
  26. package/packages/datadog-plugin-protobufjs/src/index.js +14 -0
  27. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +180 -0
  28. package/packages/dd-trace/src/appsec/addresses.js +6 -1
  29. package/packages/dd-trace/src/appsec/channels.js +5 -1
  30. package/packages/dd-trace/src/appsec/iast/analyzers/cookie-analyzer.js +13 -1
  31. package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +8 -1
  32. package/packages/dd-trace/src/appsec/iast/iast-plugin.js +1 -1
  33. package/packages/dd-trace/src/appsec/iast/index.js +3 -0
  34. package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +1 -0
  35. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +15 -0
  36. package/packages/dd-trace/src/appsec/index.js +58 -43
  37. package/packages/dd-trace/src/appsec/rasp/fs-plugin.js +99 -0
  38. package/packages/dd-trace/src/appsec/rasp/index.js +24 -10
  39. package/packages/dd-trace/src/appsec/rasp/lfi.js +112 -0
  40. package/packages/dd-trace/src/appsec/rasp/sql_injection.js +24 -4
  41. package/packages/dd-trace/src/appsec/rasp/utils.js +2 -1
  42. package/packages/dd-trace/src/appsec/recommended.json +2 -4
  43. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +5 -1
  44. package/packages/dd-trace/src/appsec/remote_config/index.js +8 -0
  45. package/packages/dd-trace/src/appsec/reporter.js +12 -5
  46. package/packages/dd-trace/src/appsec/sdk/track_event.js +5 -0
  47. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +1 -1
  48. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +2 -14
  49. package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +53 -0
  50. package/packages/dd-trace/src/config.js +12 -1
  51. package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +25 -17
  52. package/packages/dd-trace/src/debugger/devtools_client/config.js +2 -0
  53. package/packages/dd-trace/src/debugger/devtools_client/index.js +56 -5
  54. package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +4 -4
  55. package/packages/dd-trace/src/debugger/devtools_client/send.js +14 -1
  56. package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +153 -0
  57. package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +30 -0
  58. package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +241 -0
  59. package/packages/dd-trace/src/debugger/devtools_client/state.js +10 -4
  60. package/packages/dd-trace/src/exporters/common/request.js +8 -34
  61. package/packages/dd-trace/src/exporters/common/url-to-http-options-polyfill.js +31 -0
  62. package/packages/dd-trace/src/payload-tagging/index.js +1 -1
  63. package/packages/dd-trace/src/payload-tagging/jsonpath-plus.js +2094 -0
  64. package/packages/dd-trace/src/plugin_manager.js +4 -2
  65. package/packages/dd-trace/src/plugins/ci_plugin.js +2 -0
  66. package/packages/dd-trace/src/plugins/index.js +3 -0
  67. package/packages/dd-trace/src/plugins/log_plugin.js +1 -1
  68. package/packages/dd-trace/src/plugins/schema.js +35 -0
  69. package/packages/dd-trace/src/plugins/util/ci.js +23 -1
  70. package/packages/dd-trace/src/plugins/util/serverless.js +7 -0
  71. package/packages/dd-trace/src/plugins/util/stacktrace.js +94 -0
  72. package/packages/dd-trace/src/plugins/util/tags.js +7 -0
  73. package/packages/dd-trace/src/plugins/util/test.js +20 -22
  74. package/packages/dd-trace/src/plugins/util/web.js +6 -4
  75. package/packages/dd-trace/src/profiling/profiler.js +24 -14
  76. package/packages/dd-trace/src/profiling/profilers/events.js +3 -3
  77. package/packages/dd-trace/src/profiling/profilers/wall.js +94 -66
  78. package/packages/dd-trace/src/proxy.js +12 -0
  79. package/packages/dd-trace/src/service-naming/schemas/v0/index.js +2 -1
  80. package/packages/dd-trace/src/service-naming/schemas/v0/serverless.js +12 -0
  81. package/packages/dd-trace/src/service-naming/schemas/v1/index.js +2 -1
  82. package/packages/dd-trace/src/service-naming/schemas/v1/serverless.js +12 -0
  83. package/packages/datadog-core/src/storage/async_resource.js +0 -108
  84. package/packages/datadog-core/src/storage/index.js +0 -5
@@ -137,7 +137,8 @@ module.exports = class PluginManager {
137
137
  dsmEnabled,
138
138
  clientIpEnabled,
139
139
  memcachedCommandEnabled,
140
- ciVisibilityTestSessionName
140
+ ciVisibilityTestSessionName,
141
+ ciVisAgentlessLogSubmissionEnabled
141
142
  } = this._tracerConfig
142
143
 
143
144
  const sharedConfig = {
@@ -147,7 +148,8 @@ module.exports = class PluginManager {
147
148
  site,
148
149
  url,
149
150
  headers: headerTags || [],
150
- ciVisibilityTestSessionName
151
+ ciVisibilityTestSessionName,
152
+ ciVisAgentlessLogSubmissionEnabled
151
153
  }
152
154
 
153
155
  if (logInjection !== undefined) {
@@ -100,7 +100,9 @@ module.exports = class CiPlugin extends Plugin {
100
100
  ...testSessionSpanMetadata
101
101
  }
102
102
  })
103
+ // TODO: add telemetry tag when we can add `is_agentless_log_submission_enabled` for agentless log submission
103
104
  this.telemetry.ciVisEvent(TELEMETRY_EVENT_CREATED, 'session')
105
+
104
106
  this.testModuleSpan = this.tracer.startSpan(`${this.constructor.id}.test_module`, {
105
107
  childOf: this.testSessionSpan,
106
108
  tags: {
@@ -3,6 +3,7 @@
3
3
  module.exports = {
4
4
  get '@apollo/gateway' () { return require('../../../datadog-plugin-apollo/src') },
5
5
  get '@aws-sdk/smithy-client' () { return require('../../../datadog-plugin-aws-sdk/src') },
6
+ get '@azure/functions' () { return require('../../../datadog-plugin-azure-functions/src') },
6
7
  get '@cucumber/cucumber' () { return require('../../../datadog-plugin-cucumber/src') },
7
8
  get '@playwright/test' () { return require('../../../datadog-plugin-playwright/src') },
8
9
  get '@elastic/elasticsearch' () { return require('../../../datadog-plugin-elasticsearch/src') },
@@ -22,6 +23,7 @@ module.exports = {
22
23
  get aerospike () { return require('../../../datadog-plugin-aerospike/src') },
23
24
  get amqp10 () { return require('../../../datadog-plugin-amqp10/src') },
24
25
  get amqplib () { return require('../../../datadog-plugin-amqplib/src') },
26
+ get avsc () { return require('../../../datadog-plugin-avsc/src') },
25
27
  get 'aws-sdk' () { return require('../../../datadog-plugin-aws-sdk/src') },
26
28
  get bunyan () { return require('../../../datadog-plugin-bunyan/src') },
27
29
  get 'cassandra-driver' () { return require('../../../datadog-plugin-cassandra-driver/src') },
@@ -77,6 +79,7 @@ module.exports = {
77
79
  get pino () { return require('../../../datadog-plugin-pino/src') },
78
80
  get 'pino-pretty' () { return require('../../../datadog-plugin-pino/src') },
79
81
  get playwright () { return require('../../../datadog-plugin-playwright/src') },
82
+ get protobufjs () { return require('../../../datadog-plugin-protobufjs/src') },
80
83
  get redis () { return require('../../../datadog-plugin-redis/src') },
81
84
  get restify () { return require('../../../datadog-plugin-restify/src') },
82
85
  get rhea () { return require('../../../datadog-plugin-rhea/src') },
@@ -54,7 +54,7 @@ module.exports = class LogPlugin extends Plugin {
54
54
  configure (config) {
55
55
  return super.configure({
56
56
  ...config,
57
- enabled: config.enabled && config.logInjection
57
+ enabled: config.enabled && (config.logInjection || config.ciVisAgentlessLogSubmissionEnabled)
58
58
  })
59
59
  }
60
60
  }
@@ -0,0 +1,35 @@
1
+ 'use strict'
2
+
3
+ const Plugin = require('./plugin')
4
+
5
+ const SERIALIZATION = 'serialization'
6
+ const DESERIALIZATION = 'deserialization'
7
+
8
+ class SchemaPlugin extends Plugin {
9
+ constructor (...args) {
10
+ super(...args)
11
+
12
+ this.addSub(`apm:${this.constructor.id}:serialize-start`, this.handleSerializeStart.bind(this))
13
+ this.addSub(`apm:${this.constructor.id}:deserialize-end`, this.handleDeserializeFinish.bind(this))
14
+ }
15
+
16
+ handleSerializeStart (args) {
17
+ const activeSpan = this.tracer.scope().active()
18
+ if (activeSpan && this.config.dsmEnabled) {
19
+ this.constructor.schemaExtractor.attachSchemaOnSpan(
20
+ args, activeSpan, SERIALIZATION, this.tracer
21
+ )
22
+ }
23
+ }
24
+
25
+ handleDeserializeFinish (args) {
26
+ const activeSpan = this.tracer.scope().active()
27
+ if (activeSpan && this.config.dsmEnabled) {
28
+ this.constructor.schemaExtractor.attachSchemaOnSpan(
29
+ args, activeSpan, DESERIALIZATION, this.tracer
30
+ )
31
+ }
32
+ }
33
+ }
34
+
35
+ module.exports = SchemaPlugin
@@ -1,3 +1,4 @@
1
+ const { readFileSync } = require('fs')
1
2
  const {
2
3
  GIT_BRANCH,
3
4
  GIT_COMMIT_SHA,
@@ -6,6 +7,9 @@ const {
6
7
  GIT_COMMIT_AUTHOR_NAME,
7
8
  GIT_COMMIT_MESSAGE,
8
9
  GIT_COMMIT_AUTHOR_DATE,
10
+ GIT_COMMIT_HEAD_SHA,
11
+ GIT_PULL_REQUEST_BASE_BRANCH_SHA,
12
+ GIT_PULL_REQUEST_BASE_BRANCH,
9
13
  GIT_REPOSITORY_URL,
10
14
  CI_PIPELINE_ID,
11
15
  CI_PIPELINE_NAME,
@@ -77,6 +81,13 @@ function resolveTilde (filePath) {
77
81
  return filePath
78
82
  }
79
83
 
84
+ function getGitHubEventPayload () {
85
+ if (!process.env.GITHUB_EVENT_PATH) {
86
+ return
87
+ }
88
+ return JSON.parse(readFileSync(process.env.GITHUB_EVENT_PATH, 'utf8'))
89
+ }
90
+
80
91
  module.exports = {
81
92
  normalizeRef,
82
93
  getCIMetadata () {
@@ -241,7 +252,8 @@ module.exports = {
241
252
  GITHUB_REPOSITORY,
242
253
  GITHUB_SERVER_URL,
243
254
  GITHUB_RUN_ATTEMPT,
244
- GITHUB_JOB
255
+ GITHUB_JOB,
256
+ GITHUB_BASE_REF
245
257
  } = env
246
258
 
247
259
  const repositoryURL = `${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git`
@@ -277,6 +289,16 @@ module.exports = {
277
289
  GITHUB_RUN_ATTEMPT
278
290
  })
279
291
  }
292
+ if (GITHUB_BASE_REF) { // `pull_request` or `pull_request_target` event
293
+ tags[GIT_PULL_REQUEST_BASE_BRANCH] = GITHUB_BASE_REF
294
+ try {
295
+ const eventContent = getGitHubEventPayload()
296
+ tags[GIT_PULL_REQUEST_BASE_BRANCH_SHA] = eventContent.pull_request.base.sha
297
+ tags[GIT_COMMIT_HEAD_SHA] = eventContent.pull_request.head.sha
298
+ } catch (e) {
299
+ // ignore malformed event content
300
+ }
301
+ }
280
302
  }
281
303
 
282
304
  if (env.APPVEYOR) {
@@ -0,0 +1,7 @@
1
+ const types = require('../../../../../ext/types')
2
+ const web = require('./web')
3
+
4
+ const serverless = { ...web }
5
+ serverless.TYPE = types.SERVERLESS
6
+
7
+ module.exports = serverless
@@ -0,0 +1,94 @@
1
+ 'use strict'
2
+
3
+ const { relative, sep, isAbsolute } = require('path')
4
+
5
+ const cwd = process.cwd()
6
+
7
+ module.exports = {
8
+ getCallSites,
9
+ getUserLandFrames
10
+ }
11
+
12
+ // From https://github.com/felixge/node-stack-trace/blob/ba06dcdb50d465cd440d84a563836e293b360427/index.js#L1
13
+ function getCallSites (constructorOpt) {
14
+ const oldLimit = Error.stackTraceLimit
15
+ Error.stackTraceLimit = Infinity
16
+
17
+ const dummy = {}
18
+
19
+ const v8Handler = Error.prepareStackTrace
20
+ Error.prepareStackTrace = function (_, v8StackTrace) {
21
+ return v8StackTrace
22
+ }
23
+ Error.captureStackTrace(dummy, constructorOpt)
24
+
25
+ const v8StackTrace = dummy.stack
26
+ Error.prepareStackTrace = v8Handler
27
+ Error.stackTraceLimit = oldLimit
28
+
29
+ return v8StackTrace
30
+ }
31
+
32
+ /**
33
+ * Get stack trace of user-land frames.
34
+ *
35
+ * @param {Function} constructorOpt - Function to pass along to Error.captureStackTrace
36
+ * @param {number} [limit=Infinity] - The maximum number of frames to return
37
+ * @returns {{ file: string, line: number, method: (string|undefined), type: (string|undefined) }[]} - A
38
+ */
39
+ function getUserLandFrames (constructorOpt, limit = Infinity) {
40
+ const callsites = getCallSites(constructorOpt)
41
+ const frames = []
42
+
43
+ for (const callsite of callsites) {
44
+ if (callsite.isNative()) {
45
+ continue
46
+ }
47
+
48
+ const filename = callsite.getFileName()
49
+
50
+ // If the callsite is native, there will be no associated filename. However, there might be other instances where
51
+ // this can happen, so to be sure, we add this additional check
52
+ if (filename === null) {
53
+ continue
54
+ }
55
+
56
+ // ESM module paths start with the "file://" protocol (because ESM supports https imports)
57
+ // TODO: Node.js also supports `data:` and `node:` imports, should we do something specific for `data:`?
58
+ const containsFileProtocol = filename.startsWith('file:')
59
+
60
+ // TODO: I'm not sure how stable this check is. Alternatively, we could consider reversing it if we can get
61
+ // a comprehensive list of all non-file-based values, eg:
62
+ //
63
+ // filename === '<anonymous>' || filename.startsWith('node:')
64
+ if (containsFileProtocol === false && isAbsolute(filename) === false) {
65
+ continue
66
+ }
67
+
68
+ // TODO: Technically, the algorithm below could be simplified to not use the relative path, but be simply:
69
+ //
70
+ // if (filename.includes(sep + 'node_modules' + sep)) continue
71
+ //
72
+ // However, the tests in `packages/dd-trace/test/plugins/util/stacktrace.spec.js` will fail on my machine
73
+ // because I have the source code in a parent folder called `node_modules`. So the code below thinks that
74
+ // it's not in user-land
75
+ const relativePath = relative(cwd, containsFileProtocol ? filename.substring(7) : filename)
76
+ if (relativePath.startsWith('node_modules' + sep) || relativePath.includes(sep + 'node_modules' + sep)) {
77
+ continue
78
+ }
79
+
80
+ const method = callsite.getFunctionName()
81
+ const type = callsite.getTypeName()
82
+ frames.push({
83
+ file: filename,
84
+ line: callsite.getLineNumber(),
85
+ column: callsite.getColumnNumber(),
86
+ method: method ?? undefined, // force to undefined if null so JSON.stringify will omit it
87
+ type: type ?? undefined // force to undefined if null so JSON.stringify will omit it
88
+ })
89
+
90
+ if (frames.length === limit) break
91
+ }
92
+
93
+ return frames
94
+ }
@@ -9,6 +9,10 @@ const GIT_COMMIT_COMMITTER_NAME = 'git.commit.committer.name'
9
9
  const GIT_COMMIT_AUTHOR_DATE = 'git.commit.author.date'
10
10
  const GIT_COMMIT_AUTHOR_EMAIL = 'git.commit.author.email'
11
11
  const GIT_COMMIT_AUTHOR_NAME = 'git.commit.author.name'
12
+ const GIT_COMMIT_HEAD_SHA = 'git.commit.head_sha'
13
+
14
+ const GIT_PULL_REQUEST_BASE_BRANCH_SHA = 'git.pull_request.base_branch_sha'
15
+ const GIT_PULL_REQUEST_BASE_BRANCH = 'git.pull_request.base_branch'
12
16
 
13
17
  const CI_PIPELINE_ID = 'ci.pipeline.id'
14
18
  const CI_PIPELINE_NAME = 'ci.pipeline.name'
@@ -36,6 +40,9 @@ module.exports = {
36
40
  GIT_COMMIT_AUTHOR_DATE,
37
41
  GIT_COMMIT_AUTHOR_EMAIL,
38
42
  GIT_COMMIT_AUTHOR_NAME,
43
+ GIT_COMMIT_HEAD_SHA,
44
+ GIT_PULL_REQUEST_BASE_BRANCH_SHA,
45
+ GIT_PULL_REQUEST_BASE_BRANCH,
39
46
  CI_PIPELINE_ID,
40
47
  CI_PIPELINE_NAME,
41
48
  CI_PIPELINE_NUMBER,
@@ -168,7 +168,6 @@ module.exports = {
168
168
  mergeCoverage,
169
169
  fromCoverageMapToCoverage,
170
170
  getTestLineStart,
171
- getCallSites,
172
171
  removeInvalidMetadata,
173
172
  parseAnnotations,
174
173
  EFD_STRING,
@@ -181,7 +180,8 @@ module.exports = {
181
180
  TEST_BROWSER_NAME,
182
181
  TEST_BROWSER_VERSION,
183
182
  getTestSessionName,
184
- TEST_LEVEL_EVENT_TYPES
183
+ TEST_LEVEL_EVENT_TYPES,
184
+ getNumFromKnownTests
185
185
  }
186
186
 
187
187
  // Returns pkg manager and its version, separated by '-', e.g. npm-8.15.0 or yarn-1.22.19
@@ -557,26 +557,6 @@ function getTestLineStart (err, testSuitePath) {
557
557
  }
558
558
  }
559
559
 
560
- // From https://github.com/felixge/node-stack-trace/blob/ba06dcdb50d465cd440d84a563836e293b360427/index.js#L1
561
- function getCallSites () {
562
- const oldLimit = Error.stackTraceLimit
563
- Error.stackTraceLimit = Infinity
564
-
565
- const dummy = {}
566
-
567
- const v8Handler = Error.prepareStackTrace
568
- Error.prepareStackTrace = function (_, v8StackTrace) {
569
- return v8StackTrace
570
- }
571
- Error.captureStackTrace(dummy)
572
-
573
- const v8StackTrace = dummy.stack
574
- Error.prepareStackTrace = v8Handler
575
- Error.stackTraceLimit = oldLimit
576
-
577
- return v8StackTrace
578
- }
579
-
580
560
  /**
581
561
  * Gets an object of test tags from an Playwright annotations array.
582
562
  * @param {Object[]} annotations - Annotations from a Playwright test.
@@ -639,3 +619,21 @@ function getTestSessionName (config, testCommand, envTags) {
639
619
  }
640
620
  return testCommand
641
621
  }
622
+
623
+ // Calculate the number of a tests from the known tests response, which has a shape like:
624
+ // { testModule1: { testSuite1: [test1, test2, test3] }, testModule2: { testSuite2: [test4, test5] } }
625
+ function getNumFromKnownTests (knownTests) {
626
+ if (!knownTests) {
627
+ return 0
628
+ }
629
+
630
+ let totalNumTests = 0
631
+
632
+ for (const testModule of Object.values(knownTests)) {
633
+ for (const testSuite of Object.values(testModule)) {
634
+ totalNumTests += testSuite.length
635
+ }
636
+ }
637
+
638
+ return totalNumTests
639
+ }
@@ -36,6 +36,8 @@ const contexts = new WeakMap()
36
36
  const ends = new WeakMap()
37
37
 
38
38
  const web = {
39
+ TYPE: WEB,
40
+
39
41
  // Ensure the configuration has the correct structure and defaults.
40
42
  normalizeConfig (config) {
41
43
  const headers = getHeadersToRecord(config)
@@ -103,7 +105,7 @@ const web = {
103
105
  context.res = res
104
106
 
105
107
  this.setConfig(req, config)
106
- addRequestTags(context)
108
+ addRequestTags(context, this.TYPE)
107
109
 
108
110
  return span
109
111
  },
@@ -296,7 +298,7 @@ const web = {
296
298
 
297
299
  if (context.finished && !req.stream) return
298
300
 
299
- addRequestTags(context)
301
+ addRequestTags(context, this.TYPE)
300
302
  addResponseTags(context)
301
303
 
302
304
  context.config.hooks.request(context.span, req, res)
@@ -423,7 +425,7 @@ function reactivate (req, fn) {
423
425
  : fn()
424
426
  }
425
427
 
426
- function addRequestTags (context) {
428
+ function addRequestTags (context, spanType) {
427
429
  const { req, span, config } = context
428
430
  const url = extractURL(req)
429
431
 
@@ -431,7 +433,7 @@ function addRequestTags (context) {
431
433
  [HTTP_URL]: web.obfuscateQs(config, url),
432
434
  [HTTP_METHOD]: req.method,
433
435
  [SPAN_KIND]: SERVER,
434
- [SPAN_TYPE]: WEB,
436
+ [SPAN_TYPE]: spanType,
435
437
  [HTTP_USERAGENT]: req.headers['user-agent']
436
438
  })
437
439
 
@@ -146,6 +146,10 @@ class Profiler extends EventEmitter {
146
146
  const encodedProfiles = {}
147
147
 
148
148
  try {
149
+ if (Object.keys(this._config.profilers).length === 0) {
150
+ throw new Error('No profile types configured.')
151
+ }
152
+
149
153
  // collect profiles synchronously so that profilers can be safely stopped asynchronously
150
154
  for (const profiler of this._config.profilers) {
151
155
  const profile = profiler.profile(restart, startDate, endDate)
@@ -156,23 +160,32 @@ class Profiler extends EventEmitter {
156
160
  profiles.push({ profiler, profile })
157
161
  }
158
162
 
163
+ if (restart) {
164
+ this._capture(this._timeoutInterval, endDate)
165
+ }
166
+
159
167
  // encode and export asynchronously
160
168
  for (const { profiler, profile } of profiles) {
161
- encodedProfiles[profiler.type] = await profiler.encode(profile)
162
- this._logger.debug(() => {
163
- const profileJson = JSON.stringify(profile, (key, value) => {
164
- return typeof value === 'bigint' ? value.toString() : value
169
+ try {
170
+ encodedProfiles[profiler.type] = await profiler.encode(profile)
171
+ this._logger.debug(() => {
172
+ const profileJson = JSON.stringify(profile, (key, value) => {
173
+ return typeof value === 'bigint' ? value.toString() : value
174
+ })
175
+ return `Collected ${profiler.type} profile: ` + profileJson
165
176
  })
166
- return `Collected ${profiler.type} profile: ` + profileJson
167
- })
177
+ } catch (err) {
178
+ // If encoding one of the profile types fails, we should still try to
179
+ // encode and submit the other profile types.
180
+ this._logError(err)
181
+ }
168
182
  }
169
183
 
170
- if (restart) {
171
- this._capture(this._timeoutInterval, endDate)
184
+ if (Object.keys(encodedProfiles).length > 0) {
185
+ await this._submit(encodedProfiles, startDate, endDate, snapshotKind)
186
+ profileSubmittedChannel.publish()
187
+ this._logger.debug('Submitted profiles')
172
188
  }
173
- await this._submit(encodedProfiles, startDate, endDate, snapshotKind)
174
- profileSubmittedChannel.publish()
175
- this._logger.debug('Submitted profiles')
176
189
  } catch (err) {
177
190
  this._logError(err)
178
191
  this._stop()
@@ -180,9 +193,6 @@ class Profiler extends EventEmitter {
180
193
  }
181
194
 
182
195
  _submit (profiles, start, end, snapshotKind) {
183
- if (!Object.keys(profiles).length) {
184
- return Promise.reject(new Error('No profiles to submit'))
185
- }
186
196
  const { tags } = this._config
187
197
  const tasks = []
188
198
 
@@ -330,13 +330,13 @@ class EventsProfiler {
330
330
  if (!restart) {
331
331
  this.stop()
332
332
  }
333
- const profile = this.eventSerializer.createProfile(startDate, endDate)
333
+ const thatEventSerializer = this.eventSerializer
334
334
  this.eventSerializer = new EventSerializer()
335
- return profile
335
+ return () => thatEventSerializer.createProfile(startDate, endDate)
336
336
  }
337
337
 
338
338
  encode (profile) {
339
- return pprof.encode(profile)
339
+ return pprof.encode(profile())
340
340
  }
341
341
  }
342
342