dd-trace 2.11.0 → 2.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/LICENSE-3rdparty.csv +0 -1
  2. package/index.d.ts +3 -3
  3. package/package.json +4 -5
  4. package/packages/datadog-core/src/storage/async_hooks.js +4 -4
  5. package/packages/datadog-core/src/storage/async_resource.js +14 -4
  6. package/packages/datadog-instrumentations/src/connect.js +4 -4
  7. package/packages/datadog-instrumentations/src/couchbase.js +166 -61
  8. package/packages/datadog-instrumentations/src/fastify.js +12 -25
  9. package/packages/datadog-instrumentations/src/graphql.js +17 -5
  10. package/packages/datadog-instrumentations/src/koa.js +4 -4
  11. package/packages/datadog-instrumentations/src/mocha.js +88 -18
  12. package/packages/datadog-instrumentations/src/restify.js +4 -8
  13. package/packages/datadog-instrumentations/src/router.js +4 -4
  14. package/packages/datadog-plugin-couchbase/src/index.js +8 -10
  15. package/packages/datadog-plugin-graphql/src/resolve.js +2 -0
  16. package/packages/datadog-plugin-http/src/server.js +3 -8
  17. package/packages/datadog-plugin-mocha/src/index.js +80 -3
  18. package/packages/datadog-plugin-next/src/index.js +1 -1
  19. package/packages/datadog-plugin-router/src/index.js +39 -10
  20. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +111 -15
  21. package/packages/dd-trace/src/plugin_manager.js +49 -33
  22. package/packages/dd-trace/src/plugins/util/test.js +32 -1
  23. package/packages/dd-trace/src/plugins/util/web.js +25 -17
  24. package/packages/dd-trace/src/profiling/config.js +10 -2
  25. package/packages/dd-trace/src/profiling/exporters/agent.js +1 -2
  26. package/packages/dd-trace/src/profiling/exporters/form-data.js +53 -0
  27. package/packages/dd-trace/src/profiling/index.js +2 -0
  28. package/packages/dd-trace/src/profiling/profiler.js +6 -1
  29. package/packages/dd-trace/src/profiling/profilers/cpu.js +126 -0
@@ -3,13 +3,25 @@ const { truncateSpan, normalizeSpan } = require('./tags-processors')
3
3
  const Chunk = require('./chunk')
4
4
  const { AgentEncoder } = require('./0.4')
5
5
  const { version: ddTraceVersion } = require('../../../../package.json')
6
+ const id = require('../../../dd-trace/src/id')
6
7
 
7
8
  const ENCODING_VERSION = 1
8
9
 
10
+ const ALLOWED_CONTENT_TYPES = ['test_session_end', 'test_suite_end', 'test']
11
+
12
+ const TEST_SUITE_KEYS_LENGTH = 11
13
+ const TEST_SESSION_KEYS_LENGTH = 10
14
+
15
+ const CHUNK_SIZE = 4 * 1024 * 1024 // 4MB
16
+
9
17
  function formatSpan (span) {
18
+ let encodingVersion = ENCODING_VERSION
19
+ if (span.type === 'test' && span.meta && span.meta.test_session_id) {
20
+ encodingVersion = 2
21
+ }
10
22
  return {
11
- type: span.type === 'test' ? 'test' : 'span',
12
- version: ENCODING_VERSION,
23
+ type: ALLOWED_CONTENT_TYPES.includes(span.type) ? span.type : 'span',
24
+ version: encodingVersion,
13
25
  content: normalizeSpan(truncateSpan(span))
14
26
  }
15
27
  }
@@ -21,8 +33,8 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
21
33
  this.runtimeId = runtimeId
22
34
  this.service = service
23
35
  this.env = env
24
- this._traceBytes = new Chunk()
25
- this._stringBytes = new Chunk()
36
+ this._traceBytes = new Chunk(CHUNK_SIZE)
37
+ this._stringBytes = new Chunk(CHUNK_SIZE)
26
38
  this._stringCount = 0
27
39
  this._stringMap = {}
28
40
 
@@ -33,8 +45,69 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
33
45
  this.reset()
34
46
  }
35
47
 
48
+ _encodeTestSuite (bytes, content) {
49
+ this._encodeMapPrefix(bytes, TEST_SUITE_KEYS_LENGTH)
50
+ this._encodeString(bytes, 'type')
51
+ this._encodeString(bytes, content.type)
52
+
53
+ this._encodeString(bytes, 'test_session_id')
54
+ this._encodeId(bytes, content.trace_id)
55
+
56
+ this._encodeString(bytes, 'test_suite_id')
57
+ this._encodeId(bytes, content.span_id)
58
+
59
+ this._encodeString(bytes, 'error')
60
+ this._encodeNumber(bytes, content.error)
61
+ this._encodeString(bytes, 'name')
62
+ this._encodeString(bytes, content.name)
63
+ this._encodeString(bytes, 'service')
64
+ this._encodeString(bytes, content.service)
65
+ this._encodeString(bytes, 'resource')
66
+ this._encodeString(bytes, content.resource)
67
+ this._encodeString(bytes, 'start')
68
+ this._encodeNumber(bytes, content.start)
69
+ this._encodeString(bytes, 'duration')
70
+ this._encodeNumber(bytes, content.duration)
71
+ this._encodeString(bytes, 'meta')
72
+ this._encodeMap(bytes, content.meta)
73
+ this._encodeString(bytes, 'metrics')
74
+ this._encodeMap(bytes, content.metrics)
75
+ }
76
+
77
+ _encodeTestSession (bytes, content) {
78
+ this._encodeMapPrefix(bytes, TEST_SESSION_KEYS_LENGTH)
79
+ this._encodeString(bytes, 'type')
80
+ this._encodeString(bytes, content.type)
81
+
82
+ this._encodeString(bytes, 'test_session_id')
83
+ this._encodeId(bytes, content.trace_id)
84
+
85
+ this._encodeString(bytes, 'error')
86
+ this._encodeNumber(bytes, content.error)
87
+ this._encodeString(bytes, 'name')
88
+ this._encodeString(bytes, content.name)
89
+ this._encodeString(bytes, 'service')
90
+ this._encodeString(bytes, content.service)
91
+ this._encodeString(bytes, 'resource')
92
+ this._encodeString(bytes, content.resource)
93
+ this._encodeString(bytes, 'start')
94
+ this._encodeNumber(bytes, content.start)
95
+ this._encodeString(bytes, 'duration')
96
+ this._encodeNumber(bytes, content.duration)
97
+ this._encodeString(bytes, 'meta')
98
+ this._encodeMap(bytes, content.meta)
99
+ this._encodeString(bytes, 'metrics')
100
+ this._encodeMap(bytes, content.metrics)
101
+ }
102
+
36
103
  _encodeEventContent (bytes, content) {
37
- this._encodeMapPrefix(bytes, content)
104
+ const keysLength = Object.keys(content).length
105
+ if (content.meta.test_session_id) {
106
+ this._encodeMapPrefix(bytes, keysLength + 2)
107
+ } else {
108
+ this._encodeMapPrefix(bytes, keysLength)
109
+ }
110
+
38
111
  if (content.type) {
39
112
  this._encodeString(bytes, 'type')
40
113
  this._encodeString(bytes, content.type)
@@ -57,6 +130,24 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
57
130
  this._encodeNumber(bytes, content.start)
58
131
  this._encodeString(bytes, 'duration')
59
132
  this._encodeNumber(bytes, content.duration)
133
+ /**
134
+ * We include `test_session_id` and `test_suite_id`
135
+ * in the root of the event by passing them via the `meta` dict.
136
+ * This is to avoid changing the span format in packages/dd-trace/src/format.js,
137
+ * which can have undesired side effects in other products.
138
+ * But `test_session_id` and `test_suite_id` are *not* supposed to be in `meta`,
139
+ * so we delete them before enconding the dictionary.
140
+ * TODO: find a better way to do this.
141
+ */
142
+ if (content.meta.test_session_id) {
143
+ this._encodeString(bytes, 'test_session_id')
144
+ this._encodeId(bytes, id(content.meta.test_session_id))
145
+ delete content.meta.test_session_id
146
+
147
+ this._encodeString(bytes, 'test_suite_id')
148
+ this._encodeId(bytes, id(content.meta.test_suite_id))
149
+ delete content.meta.test_suite_id
150
+ }
60
151
  this._encodeString(bytes, 'meta')
61
152
  this._encodeMap(bytes, content.meta)
62
153
  this._encodeString(bytes, 'metrics')
@@ -64,7 +155,7 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
64
155
  }
65
156
 
66
157
  _encodeEvent (bytes, event) {
67
- this._encodeMapPrefix(bytes, event)
158
+ this._encodeMapPrefix(bytes, Object.keys(event).length)
68
159
  this._encodeString(bytes, 'type')
69
160
  this._encodeString(bytes, event.type)
70
161
 
@@ -72,7 +163,13 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
72
163
  this._encodeNumber(bytes, event.version)
73
164
 
74
165
  this._encodeString(bytes, 'content')
75
- this._encodeEventContent(bytes, event.content)
166
+ if (event.type === 'span' || event.type === 'test') {
167
+ this._encodeEventContent(bytes, event.content)
168
+ } else if (event.type === 'test_suite_end') {
169
+ this._encodeTestSuite(bytes, event.content)
170
+ } else if (event.type === 'test_session_end') {
171
+ this._encodeTestSession(bytes, event.content)
172
+ }
76
173
  }
77
174
 
78
175
  _encodeNumber (bytes, value) {
@@ -107,18 +204,17 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
107
204
  buffer[offset + 8] = lo
108
205
  }
109
206
 
110
- _encodeMapPrefix (bytes, map) {
111
- const keys = Object.keys(map)
207
+ _encodeMapPrefix (bytes, keysLength) {
112
208
  const buffer = bytes.buffer
113
209
  const offset = bytes.length
114
210
 
115
211
  bytes.reserve(5)
116
212
  bytes.length += 5
117
213
  buffer[offset] = 0xdf
118
- buffer[offset + 1] = keys.length >> 24
119
- buffer[offset + 2] = keys.length >> 16
120
- buffer[offset + 3] = keys.length >> 8
121
- buffer[offset + 4] = keys.length
214
+ buffer[offset + 1] = keysLength >> 24
215
+ buffer[offset + 2] = keysLength >> 16
216
+ buffer[offset + 3] = keysLength >> 8
217
+ buffer[offset + 4] = keysLength
122
218
  }
123
219
 
124
220
  _encode (bytes, trace) {
@@ -171,11 +267,11 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
171
267
  payload.metadata['*']['runtime-id'] = this.runtimeId
172
268
  }
173
269
 
174
- this._encodeMapPrefix(bytes, payload)
270
+ this._encodeMapPrefix(bytes, Object.keys(payload).length)
175
271
  this._encodeString(bytes, 'version')
176
272
  this._encodeNumber(bytes, payload.version)
177
273
  this._encodeString(bytes, 'metadata')
178
- this._encodeMapPrefix(bytes, payload.metadata)
274
+ this._encodeMapPrefix(bytes, Object.keys(payload.metadata).length)
179
275
  this._encodeString(bytes, '*')
180
276
  this._encodeMap(bytes, payload.metadata['*'])
181
277
  this._encodeString(bytes, 'events')
@@ -36,61 +36,77 @@ const collectDisabledPlugins = () => {
36
36
  // TODO this must always be a singleton.
37
37
  module.exports = class PluginManager {
38
38
  constructor (tracer) {
39
+ this._tracer = tracer
39
40
  this._pluginsByName = {}
40
41
  this._configsByName = {}
41
-
42
- const _disabledPlugins = collectDisabledPlugins()
43
-
44
- for (const PluginClass of Object.values(plugins)) {
45
- /**
46
- * disabling the plugin here instead of in `configure` so we don't waste subscriber
47
- * resources on a plugin that will eventually be disabled anyways
48
- */
49
- if (_disabledPlugins.has(PluginClass.name)) {
50
- log.debug(`Plugin "${PluginClass.name}" was disabled via configuration option.`)
51
- continue
52
- }
53
- if (typeof PluginClass !== 'function') continue
54
- this._pluginsByName[PluginClass.name] = new PluginClass(tracer)
55
- this._configsByName[PluginClass.name] = {}
56
- }
42
+ this._disabledPlugins = collectDisabledPlugins()
57
43
  }
58
44
 
59
45
  // like instrumenter.use()
60
46
  configurePlugin (name, pluginConfig) {
61
- if (!(name in this._pluginsByName)) return
62
47
  if (typeof pluginConfig === 'boolean') {
63
48
  pluginConfig = { enabled: pluginConfig }
64
49
  }
50
+ if (!pluginConfig) {
51
+ pluginConfig = { enabled: true }
52
+ }
65
53
 
66
- const config = {
54
+ this._configsByName[name] = {
67
55
  ...this._configsByName[name],
68
56
  ...pluginConfig
69
57
  }
70
58
 
71
- this._pluginsByName[name].configure(getConfig(name, config))
59
+ if (this._pluginsByName[name]) {
60
+ this._pluginsByName[name].configure(getConfig(name, this._configsByName[name]))
61
+ }
72
62
  }
73
63
 
74
64
  // like instrumenter.enable()
75
- configure (config) {
76
- const { logInjection, serviceMapping } = config
77
-
78
- if (config.plugins !== false) {
79
- for (const name in this._pluginsByName) {
80
- const pluginConfig = {
81
- ...this._configsByName[name],
82
- logInjection
83
- }
84
- if (serviceMapping && serviceMapping[name]) {
85
- pluginConfig.service = serviceMapping[name]
86
- }
87
- this.configurePlugin(name, pluginConfig)
65
+ configure (config = {}) {
66
+ const { logInjection, serviceMapping, experimental } = config
67
+
68
+ for (const PluginClass of Object.values(plugins)) {
69
+ const name = PluginClass.name
70
+
71
+ if (this._disabledPlugins.has(name)) {
72
+ log.debug(`Plugin "${name}" was disabled via configuration option.`)
73
+ continue
74
+ }
75
+
76
+ if (typeof PluginClass !== 'function') continue
77
+
78
+ this._pluginsByName[name] = new PluginClass(this._tracer)
79
+
80
+ if (config.plugins === false) continue
81
+
82
+ const pluginConfig = {
83
+ ...this._configsByName[name]
84
+ }
85
+
86
+ if (logInjection !== undefined) {
87
+ pluginConfig.logInjection = logInjection
88
+ }
89
+
90
+ // TODO: update so that it's available for every CI Visibility's plugin
91
+ if (name === 'mocha') {
92
+ pluginConfig.isAgentlessEnabled = experimental && experimental.exporter === 'datadog'
88
93
  }
94
+
95
+ if (serviceMapping && serviceMapping[name]) {
96
+ pluginConfig.service = serviceMapping[name]
97
+ }
98
+
99
+ this.configurePlugin(name, pluginConfig)
89
100
  }
90
101
  }
91
102
 
92
103
  // This is basically just for testing. like intrumenter.disable()
93
104
  destroy () {
94
- for (const name in this._pluginsByName) this._pluginsByName[name].configure({ enabled: false })
105
+ for (const name in this._pluginsByName) {
106
+ this._pluginsByName[name].configure({ enabled: false })
107
+ }
108
+
109
+ this._pluginsByName = {}
110
+ this._configsByName = {}
95
111
  }
96
112
  }
@@ -36,6 +36,9 @@ const TEST_IS_RUM_ACTIVE = 'test.is_rum_active'
36
36
  const TEST_CODE_OWNERS = 'test.codeowners'
37
37
  const TEST_SOURCE_FILE = 'test.source.file'
38
38
  const LIBRARY_VERSION = 'library_version'
39
+ const TEST_COMMAND = 'test.command'
40
+ const TEST_SESSION_ID = 'test_session_id'
41
+ const TEST_SUITE_ID = 'test_suite_id'
39
42
 
40
43
  const ERROR_TYPE = 'error.type'
41
44
  const ERROR_MESSAGE = 'error.msg'
@@ -70,7 +73,12 @@ module.exports = {
70
73
  getTestSuitePath,
71
74
  getCodeOwnersFileEntries,
72
75
  getCodeOwnersForFilename,
73
- getTestCommonTags
76
+ getTestCommonTags,
77
+ getTestSessionCommonTags,
78
+ getTestSuiteCommonTags,
79
+ TEST_COMMAND,
80
+ TEST_SESSION_ID,
81
+ TEST_SUITE_ID
74
82
  }
75
83
 
76
84
  function getTestEnvironmentMetadata (testFramework, config) {
@@ -225,3 +233,26 @@ function getCodeOwnersForFilename (filename, entries) {
225
233
  }
226
234
  return null
227
235
  }
236
+
237
+ function getTestSessionCommonTags (command, version) {
238
+ return {
239
+ [SPAN_TYPE]: 'test_session_end',
240
+ [TEST_TYPE]: 'test',
241
+ [RESOURCE_NAME]: `test_session.${command}`,
242
+ [TEST_FRAMEWORK_VERSION]: version,
243
+ [LIBRARY_VERSION]: ddTraceVersion,
244
+ [TEST_COMMAND]: command
245
+ }
246
+ }
247
+
248
+ function getTestSuiteCommonTags (command, version, testSuite) {
249
+ return {
250
+ [SPAN_TYPE]: 'test_suite_end',
251
+ [TEST_TYPE]: 'test',
252
+ [RESOURCE_NAME]: `test_suite.${testSuite}`,
253
+ [TEST_FRAMEWORK_VERSION]: version,
254
+ [LIBRARY_VERSION]: ddTraceVersion,
255
+ [TEST_SUITE]: testSuite,
256
+ [TEST_COMMAND]: command
257
+ }
258
+ }
@@ -259,9 +259,9 @@ const web = {
259
259
  const context = contexts.get(req)
260
260
  const span = context.span
261
261
  const error = context.error
262
- const hasMiddlewareError = span.context()._tags['error'] || span.context()._tags['error.msg']
262
+ const hasExistingError = span.context()._tags['error'] || span.context()._tags['error.msg']
263
263
 
264
- if (!hasMiddlewareError && !context.config.validateStatus(statusCode)) {
264
+ if (!hasExistingError && !context.config.validateStatus(statusCode)) {
265
265
  span.setTag(ERROR, error || true)
266
266
  }
267
267
  },
@@ -270,7 +270,10 @@ const web = {
270
270
  addError (req, error) {
271
271
  if (error instanceof Error) {
272
272
  const context = contexts.get(req)
273
- context.error = context.error || error
273
+
274
+ if (context) {
275
+ context.error = error
276
+ }
274
277
  }
275
278
  },
276
279
 
@@ -298,6 +301,23 @@ const web = {
298
301
  context.span.finish()
299
302
  context.finished = true
300
303
  },
304
+
305
+ finishAll (context) {
306
+ const { req, res } = context
307
+
308
+ for (const beforeEnd of context.beforeEnd) {
309
+ beforeEnd()
310
+ }
311
+
312
+ web.finishMiddleware(context)
313
+
314
+ if (incomingHttpRequestEnd.hasSubscribers) {
315
+ incomingHttpRequestEnd.publish({ req, res })
316
+ }
317
+
318
+ web.finishSpan(context)
319
+ },
320
+
301
321
  wrapWriteHead (context) {
302
322
  const { req, res } = context
303
323
  const writeHead = res.writeHead
@@ -318,21 +338,9 @@ const web = {
318
338
  },
319
339
  wrapRes (context, req, res, end) {
320
340
  return function () {
321
- for (const beforeEnd of context.beforeEnd) {
322
- beforeEnd()
323
- }
324
-
325
- web.finishMiddleware(context)
326
-
327
- if (incomingHttpRequestEnd.hasSubscribers) {
328
- incomingHttpRequestEnd.publish({ req, res })
329
- }
330
-
331
- const returnValue = end.apply(res, arguments)
332
-
333
- web.finishSpan(context)
341
+ web.finishAll(context)
334
342
 
335
- return returnValue
343
+ return end.apply(res, arguments)
336
344
  }
337
345
  },
338
346
  wrapEnd (context) {
@@ -6,6 +6,7 @@ const { URL } = require('url')
6
6
  const { AgentExporter } = require('./exporters/agent')
7
7
  const { FileExporter } = require('./exporters/file')
8
8
  const { ConsoleLogger } = require('./loggers/console')
9
+ const CpuProfiler = require('./profilers/cpu')
9
10
  const WallProfiler = require('./profilers/wall')
10
11
  const SpaceProfiler = require('./profilers/space')
11
12
  const { tagger } = require('./tagger')
@@ -13,6 +14,7 @@ const { tagger } = require('./tagger')
13
14
  const {
14
15
  DD_PROFILING_ENABLED,
15
16
  DD_PROFILING_PROFILERS,
17
+ DD_PROFILING_ENDPOINT_COLLECTION_ENABLED,
16
18
  DD_ENV,
17
19
  DD_TAGS,
18
20
  DD_SERVICE,
@@ -37,6 +39,8 @@ class Config {
37
39
  DD_PROFILING_UPLOAD_TIMEOUT, 60 * 1000)
38
40
  const sourceMap = coalesce(options.sourceMap,
39
41
  DD_PROFILING_SOURCE_MAP, true)
42
+ const endpointCollection = coalesce(options.endpointCollection,
43
+ DD_PROFILING_ENDPOINT_COLLECTION_ENABLED, false)
40
44
 
41
45
  this.enabled = String(enabled) !== 'false'
42
46
  this.service = service
@@ -53,6 +57,7 @@ class Config {
53
57
  this.flushInterval = flushInterval
54
58
  this.uploadTimeout = uploadTimeout
55
59
  this.sourceMap = sourceMap
60
+ this.endpointCollection = endpointCollection
56
61
 
57
62
  const hostname = coalesce(options.hostname, DD_AGENT_HOST, 'localhost')
58
63
  const port = coalesce(options.port, DD_TRACE_AGENT_PORT, 8126)
@@ -64,8 +69,8 @@ class Config {
64
69
  ], this)
65
70
 
66
71
  const profilers = coalesce(options.profilers, DD_PROFILING_PROFILERS, [
67
- new WallProfiler(),
68
- new SpaceProfiler()
72
+ new WallProfiler(this),
73
+ new SpaceProfiler(this)
69
74
  ])
70
75
 
71
76
  this.profilers = ensureProfilers(profilers, this)
@@ -100,10 +105,13 @@ function ensureExporters (exporters, options) {
100
105
 
101
106
  function getProfiler (name, options) {
102
107
  switch (name) {
108
+ case 'cpu':
103
109
  case 'wall':
104
110
  return new WallProfiler(options)
105
111
  case 'space':
106
112
  return new SpaceProfiler(options)
113
+ case 'cpu-experimental':
114
+ return new CpuProfiler(options)
107
115
  default:
108
116
  options.logger.error(`Unknown profiler "${name}"`)
109
117
  }
@@ -2,7 +2,7 @@
2
2
 
3
3
  const retry = require('retry')
4
4
  const { request } = require('http')
5
- const FormData = require('form-data')
5
+ const FormData = require('./form-data')
6
6
 
7
7
  // TODO: avoid using dd-trace internals. Make this a separate module?
8
8
  const docker = require('../../exporters/common/docker')
@@ -22,7 +22,6 @@ function sendRequest (options, form, callback) {
22
22
  })
23
23
  req.on('error', callback)
24
24
  if (form) form.pipe(req)
25
- req.end()
26
25
  }
27
26
 
28
27
  function getBody (stream, callback) {
@@ -0,0 +1,53 @@
1
+ 'use strict'
2
+
3
+ const { Readable } = require('stream')
4
+ const id = require('../../id')
5
+
6
+ class FormData extends Readable {
7
+ constructor () {
8
+ super()
9
+
10
+ this._boundary = id().toString()
11
+ this._data = []
12
+ }
13
+
14
+ append (key, value, options = {}) {
15
+ this._appendBoundary()
16
+
17
+ if (options.filename) {
18
+ this._appendFile(key, value, options)
19
+ } else {
20
+ this._appendMetadata(key, value, options)
21
+ }
22
+ }
23
+
24
+ getHeaders () {
25
+ return { 'Content-Type': 'multipart/form-data; boundary=' + this._boundary }
26
+ }
27
+
28
+ _appendBoundary () {
29
+ this._data.push(`--${this._boundary}\r\n`)
30
+ }
31
+
32
+ _appendMetadata (key, value) {
33
+ this._data.push(`Content-Disposition: form-data; name="${key}"\r\n\r\n${value}\r\n`)
34
+ }
35
+
36
+ _appendFile (key, value, { filename, contentType = 'application/octet-stream' }) {
37
+ this._data.push(`Content-Disposition: form-data; name="${key}"; filename="${filename}"\r\n`)
38
+ this._data.push(`Content-Type: ${contentType}\r\n\r\n`)
39
+ this._data.push(value)
40
+ this._data.push('\r\n')
41
+ }
42
+
43
+ _read () {
44
+ this.push(this._data.shift())
45
+
46
+ if (this._data.length === 0) {
47
+ this.push(`--${this._boundary}--\r\n`)
48
+ this.push(null)
49
+ }
50
+ }
51
+ }
52
+
53
+ module.exports = FormData
@@ -1,6 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const { Profiler } = require('./profiler')
4
+ const CpuProfiler = require('./profilers/cpu')
4
5
  const WallProfiler = require('./profilers/wall')
5
6
  const SpaceProfiler = require('./profilers/space')
6
7
  const { AgentExporter } = require('./exporters/agent')
@@ -13,6 +14,7 @@ module.exports = {
13
14
  profiler,
14
15
  AgentExporter,
15
16
  FileExporter,
17
+ CpuProfiler,
16
18
  WallProfiler,
17
19
  SpaceProfiler,
18
20
  ConsoleLogger
@@ -91,7 +91,12 @@ class Profiler extends EventEmitter {
91
91
  if (!profile) continue
92
92
 
93
93
  profiles[profiler.type] = await profiler.encode(profile)
94
- this._logger.debug(`Collected ${profiler.type} profile: ` + JSON.stringify(profile))
94
+ this._logger.debug(() => {
95
+ const profileJson = JSON.stringify(profile, (key, value) => {
96
+ return typeof value === 'bigint' ? value.toString() : value
97
+ })
98
+ return `Collected ${profiler.type} profile: ` + profileJson
99
+ })
95
100
  }
96
101
 
97
102
  this._capture(this._config.flushInterval)