dd-trace 3.12.1 → 3.15.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 (101) hide show
  1. package/LICENSE-3rdparty.csv +1 -0
  2. package/README.md +5 -5
  3. package/ci/init.js +3 -1
  4. package/index.d.ts +100 -1
  5. package/package.json +5 -4
  6. package/packages/datadog-instrumentations/src/aws-sdk.js +86 -0
  7. package/packages/datadog-instrumentations/src/cucumber.js +74 -15
  8. package/packages/datadog-instrumentations/src/cypress.js +1 -1
  9. package/packages/datadog-instrumentations/src/fs.js +358 -0
  10. package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
  11. package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
  12. package/packages/datadog-instrumentations/src/jest.js +24 -23
  13. package/packages/datadog-instrumentations/src/ldapjs.js +12 -2
  14. package/packages/datadog-instrumentations/src/mocha.js +10 -7
  15. package/packages/datadog-instrumentations/src/mongoose.js +1 -1
  16. package/packages/datadog-instrumentations/src/mysql.js +7 -1
  17. package/packages/datadog-instrumentations/src/mysql2.js +7 -1
  18. package/packages/datadog-instrumentations/src/next.js +2 -1
  19. package/packages/datadog-instrumentations/src/playwright.js +263 -0
  20. package/packages/datadog-plugin-aws-sdk/src/base.js +12 -5
  21. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +2 -2
  22. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +29 -24
  23. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +31 -16
  24. package/packages/datadog-plugin-cucumber/src/index.js +42 -11
  25. package/packages/datadog-plugin-cypress/src/plugin.js +129 -4
  26. package/packages/datadog-plugin-cypress/src/support.js +5 -0
  27. package/packages/datadog-plugin-fs/src/index.js +45 -0
  28. package/packages/datadog-plugin-hapi/src/index.js +5 -1
  29. package/packages/datadog-plugin-http/src/server.js +1 -1
  30. package/packages/datadog-plugin-http2/src/server.js +1 -1
  31. package/packages/datadog-plugin-jest/src/index.js +40 -70
  32. package/packages/datadog-plugin-mocha/src/index.js +44 -64
  33. package/packages/datadog-plugin-mysql/src/index.js +8 -7
  34. package/packages/datadog-plugin-playwright/src/index.js +112 -0
  35. package/packages/datadog-shimmer/src/shimmer.js +28 -11
  36. package/packages/dd-trace/src/appsec/addresses.js +3 -1
  37. package/packages/dd-trace/src/appsec/blocking.js +35 -9
  38. package/packages/dd-trace/src/appsec/callbacks/ddwaf.js +1 -1
  39. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
  40. package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +60 -0
  41. package/packages/dd-trace/src/appsec/iast/iast-context.js +6 -2
  42. package/packages/dd-trace/src/appsec/iast/index.js +3 -2
  43. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +5 -2
  44. package/packages/dd-trace/src/appsec/index.js +5 -5
  45. package/packages/dd-trace/src/appsec/recommended.json +320 -184
  46. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -1
  47. package/packages/dd-trace/src/appsec/remote_config/index.js +3 -0
  48. package/packages/dd-trace/src/appsec/reporter.js +14 -14
  49. package/packages/dd-trace/src/appsec/sdk/index.js +41 -0
  50. package/packages/dd-trace/src/appsec/sdk/noop.js +17 -0
  51. package/packages/dd-trace/src/appsec/sdk/set_user.js +30 -0
  52. package/packages/dd-trace/src/appsec/sdk/track_event.js +74 -0
  53. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +73 -0
  54. package/packages/dd-trace/src/appsec/sdk/utils.js +10 -0
  55. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +1 -5
  56. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +1 -5
  57. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +48 -11
  58. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +7 -1
  59. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +4 -2
  60. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +5 -3
  61. package/packages/dd-trace/src/config.js +63 -7
  62. package/packages/dd-trace/src/encode/0.4.js +1 -1
  63. package/packages/dd-trace/src/encode/0.5.js +1 -1
  64. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +44 -4
  65. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +52 -37
  66. package/packages/dd-trace/src/encode/tags-processors.js +3 -2
  67. package/packages/dd-trace/src/exporters/common/request.js +10 -3
  68. package/packages/dd-trace/src/lambda/handler.js +5 -6
  69. package/packages/dd-trace/src/log/channels.js +47 -0
  70. package/packages/dd-trace/src/log/index.js +79 -0
  71. package/packages/dd-trace/src/log/writer.js +124 -0
  72. package/packages/dd-trace/src/metrics.js +18 -0
  73. package/packages/dd-trace/src/noop/proxy.js +5 -2
  74. package/packages/dd-trace/src/opentracing/propagation/text_map.js +188 -36
  75. package/packages/dd-trace/src/opentracing/propagation/tracestate.js +99 -0
  76. package/packages/dd-trace/src/opentracing/span.js +2 -1
  77. package/packages/dd-trace/src/opentracing/span_context.js +6 -3
  78. package/packages/dd-trace/src/plugins/ci_plugin.js +72 -12
  79. package/packages/dd-trace/src/plugins/index.js +2 -0
  80. package/packages/dd-trace/src/plugins/util/ci.js +13 -21
  81. package/packages/dd-trace/src/plugins/util/exec.js +2 -2
  82. package/packages/dd-trace/src/plugins/util/git.js +16 -1
  83. package/packages/dd-trace/src/{appsec → plugins/util}/ip_extractor.js +1 -1
  84. package/packages/dd-trace/src/plugins/util/test.js +53 -10
  85. package/packages/dd-trace/src/plugins/util/user-provided-git.js +2 -7
  86. package/packages/dd-trace/src/plugins/util/web.js +11 -0
  87. package/packages/dd-trace/src/profiler.js +3 -0
  88. package/packages/dd-trace/src/profiling/config.js +8 -3
  89. package/packages/dd-trace/src/profiling/exporters/file.js +13 -2
  90. package/packages/dd-trace/src/profiling/profiler.js +23 -6
  91. package/packages/dd-trace/src/profiling/profilers/wall.js +1 -0
  92. package/packages/dd-trace/src/proxy.js +2 -0
  93. package/packages/dd-trace/src/span_processor.js +1 -1
  94. package/packages/dd-trace/src/span_sampler.js +68 -52
  95. package/packages/dd-trace/src/startup-log.js +3 -6
  96. package/packages/dd-trace/src/telemetry/index.js +23 -2
  97. package/packages/dd-trace/src/telemetry/send-data.js +4 -1
  98. package/packages/dd-trace/src/tracer.js +0 -16
  99. package/scripts/check-proposal-labels.js +71 -0
  100. package/packages/dd-trace/src/log.js +0 -143
  101. /package/packages/dd-trace/src/{appsec → plugins/util}/ip_blocklist.js +0 -0
@@ -14,7 +14,7 @@ float64Array[0] = -1
14
14
  const bigEndian = uInt8Float64Array[7] === 0
15
15
 
16
16
  function formatSpan (span) {
17
- return normalizeSpan(truncateSpan(span))
17
+ return normalizeSpan(truncateSpan(span, false))
18
18
  }
19
19
 
20
20
  class AgentEncoder {
@@ -7,7 +7,7 @@ const ARRAY_OF_TWO = 0x92
7
7
  const ARRAY_OF_TWELVE = 0x9c
8
8
 
9
9
  function formatSpan (span) {
10
- return normalizeSpan(truncateSpan(span))
10
+ return normalizeSpan(truncateSpan(span, false))
11
11
  }
12
12
 
13
13
  class AgentEncoder extends BaseEncoder {
@@ -5,9 +5,10 @@ const { version: ddTraceVersion } = require('../../../../package.json')
5
5
  const id = require('../../../dd-trace/src/id')
6
6
  const ENCODING_VERSION = 1
7
7
 
8
- const ALLOWED_CONTENT_TYPES = ['test_session_end', 'test_suite_end', 'test']
8
+ const ALLOWED_CONTENT_TYPES = ['test_session_end', 'test_module_end', 'test_suite_end', 'test']
9
9
 
10
- const TEST_SUITE_KEYS_LENGTH = 11
10
+ const TEST_SUITE_KEYS_LENGTH = 12
11
+ const TEST_MODULE_KEYS_LENGTH = 11
11
12
  const TEST_SESSION_KEYS_LENGTH = 10
12
13
 
13
14
  const INTAKE_SOFT_LIMIT = 2 * 1024 * 1024 // 2MB
@@ -46,6 +47,9 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
46
47
  this._encodeString(bytes, 'test_session_id')
47
48
  this._encodeId(bytes, content.trace_id)
48
49
 
50
+ this._encodeString(bytes, 'test_module_id')
51
+ this._encodeId(bytes, content.parent_id)
52
+
49
53
  this._encodeString(bytes, 'test_suite_id')
50
54
  this._encodeId(bytes, content.span_id)
51
55
 
@@ -67,6 +71,35 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
67
71
  this._encodeMap(bytes, content.metrics)
68
72
  }
69
73
 
74
+ _encodeTestModule (bytes, content) {
75
+ this._encodeMapPrefix(bytes, TEST_MODULE_KEYS_LENGTH)
76
+ this._encodeString(bytes, 'type')
77
+ this._encodeString(bytes, content.type)
78
+
79
+ this._encodeString(bytes, 'test_session_id')
80
+ this._encodeId(bytes, content.trace_id)
81
+
82
+ this._encodeString(bytes, 'test_module_id')
83
+ this._encodeId(bytes, content.span_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
+
70
103
  _encodeTestSession (bytes, content) {
71
104
  this._encodeMapPrefix(bytes, TEST_SESSION_KEYS_LENGTH)
72
105
  this._encodeString(bytes, 'type')
@@ -95,8 +128,9 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
95
128
 
96
129
  _encodeEventContent (bytes, content) {
97
130
  const keysLength = Object.keys(content).length
131
+
98
132
  if (content.meta.test_session_id) {
99
- this._encodeMapPrefix(bytes, keysLength + 2)
133
+ this._encodeMapPrefix(bytes, keysLength + 3)
100
134
  } else {
101
135
  this._encodeMapPrefix(bytes, keysLength)
102
136
  }
@@ -137,6 +171,10 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
137
171
  this._encodeId(bytes, id(content.meta.test_session_id, 10))
138
172
  delete content.meta.test_session_id
139
173
 
174
+ this._encodeString(bytes, 'test_module_id')
175
+ this._encodeId(bytes, id(content.meta.test_module_id, 10))
176
+ delete content.meta.test_module_id
177
+
140
178
  this._encodeString(bytes, 'test_suite_id')
141
179
  this._encodeId(bytes, id(content.meta.test_suite_id, 10))
142
180
  delete content.meta.test_suite_id
@@ -160,6 +198,8 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
160
198
  this._encodeEventContent(bytes, event.content)
161
199
  } else if (event.type === 'test_suite_end') {
162
200
  this._encodeTestSuite(bytes, event.content)
201
+ } else if (event.type === 'test_module_end') {
202
+ this._encodeTestModule(bytes, event.content)
163
203
  } else if (event.type === 'test_session_end') {
164
204
  this._encodeTestSession(bytes, event.content)
165
205
  }
@@ -200,7 +240,7 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
200
240
  const rawEvents = trace.map(formatSpan)
201
241
 
202
242
  const testSessionEvents = rawEvents.filter(
203
- event => event.type === 'test_session_end' || event.type === 'test_suite_end'
243
+ event => event.type === 'test_session_end' || event.type === 'test_suite_end' || event.type === 'test_module_end'
204
244
  )
205
245
 
206
246
  const isTestSessionTrace = !!testSessionEvents.length
@@ -1,56 +1,35 @@
1
1
  'use strict'
2
2
  const { AgentEncoder } = require('./0.4')
3
3
  const Chunk = require('./chunk')
4
- const log = require('../log')
5
4
 
6
5
  const FormData = require('../exporters/common/form-data')
7
6
 
8
- const COVERAGE_PAYLOAD_VERSION = 1
9
- const COVERAGE_KEYS_LENGTH = 4
10
- const MAXIMUM_NUM_COVERAGE_FILES = 100
7
+ const COVERAGE_PAYLOAD_VERSION = 2
8
+ const COVERAGE_KEYS_LENGTH = 2
11
9
 
12
10
  class CoverageCIVisibilityEncoder extends AgentEncoder {
13
11
  constructor () {
14
12
  super(...arguments)
15
13
  this._coverageBytes = new Chunk()
16
14
  this.form = new FormData()
17
- this.fileIndex = 1
15
+ this._coveragesCount = 0
18
16
  this.reset()
19
17
  }
20
18
 
21
19
  count () {
22
- return this.fileIndex - 1
20
+ return this._coveragesCount
23
21
  }
24
22
 
25
23
  encode (coverage) {
26
- const bytes = this._coverageBytes
27
- const coverageBuffer = this.encodeCodeCoverage(bytes, coverage)
28
- const coverageFilename = `coverage${this.fileIndex++}`
29
-
30
- this.form.append(
31
- coverageFilename,
32
- coverageBuffer,
33
- {
34
- filename: `${coverageFilename}.msgpack`,
35
- contentType: 'application/msgpack'
36
- }
37
- )
38
-
39
- if (this.fileIndex === MAXIMUM_NUM_COVERAGE_FILES) {
40
- log.debug('Coverage buffer reached the limit, flushing')
41
- this._writer.flush()
42
- }
43
-
44
- this.reset()
24
+ this._coveragesCount++
25
+ this.encodeCodeCoverage(this._coverageBytes, coverage)
45
26
  }
46
27
 
47
28
  encodeCodeCoverage (bytes, coverage) {
48
- this._encodeMapPrefix(bytes, COVERAGE_KEYS_LENGTH)
49
- this._encodeString(bytes, 'version')
50
- this._encodeInteger(bytes, COVERAGE_PAYLOAD_VERSION)
51
- this._encodeString(bytes, 'trace_id')
29
+ this._encodeMapPrefix(bytes, 3)
30
+ this._encodeString(bytes, 'test_session_id')
52
31
  this._encodeId(bytes, coverage.traceId)
53
- this._encodeString(bytes, 'span_id')
32
+ this._encodeString(bytes, 'test_suite_id')
54
33
  this._encodeId(bytes, coverage.spanId)
55
34
  this._encodeString(bytes, 'files')
56
35
  this._encodeArrayPrefix(bytes, coverage.files)
@@ -59,12 +38,6 @@ class CoverageCIVisibilityEncoder extends AgentEncoder {
59
38
  this._encodeString(bytes, 'filename')
60
39
  this._encodeString(bytes, filename)
61
40
  }
62
- const traceSize = bytes.length
63
- const buffer = Buffer.allocUnsafe(traceSize)
64
-
65
- bytes.buffer.copy(buffer, 0, 0, bytes.length)
66
-
67
- return buffer
68
41
  }
69
42
 
70
43
  reset () {
@@ -72,9 +45,51 @@ class CoverageCIVisibilityEncoder extends AgentEncoder {
72
45
  if (this._coverageBytes) {
73
46
  this._coverageBytes.length = 0
74
47
  }
48
+ this._coveragesCount = 0
49
+ this._encodePayloadStart(this._coverageBytes)
50
+ }
51
+
52
+ _encodePayloadStart (bytes) {
53
+ const payload = {
54
+ version: COVERAGE_PAYLOAD_VERSION,
55
+ coverages: []
56
+ }
57
+ this._encodeMapPrefix(bytes, COVERAGE_KEYS_LENGTH)
58
+ this._encodeString(bytes, 'version')
59
+ this._encodeInteger(bytes, payload.version)
60
+ this._encodeString(bytes, 'coverages')
61
+ // Get offset of the coverages list to update the length of the array when calling `makePayload`
62
+ this._coveragesOffset = bytes.length
63
+ bytes.reserve(5)
64
+ bytes.length += 5
75
65
  }
76
66
 
77
67
  makePayload () {
68
+ const bytes = this._coverageBytes
69
+
70
+ const coveragesOffset = this._coveragesOffset
71
+ const coveragesCount = this._coveragesCount
72
+
73
+ // update with number of coverages
74
+ bytes.buffer[coveragesOffset] = 0xdd
75
+ bytes.buffer[coveragesOffset + 1] = coveragesCount >> 24
76
+ bytes.buffer[coveragesOffset + 2] = coveragesCount >> 16
77
+ bytes.buffer[coveragesOffset + 3] = coveragesCount >> 8
78
+ bytes.buffer[coveragesOffset + 4] = coveragesCount
79
+
80
+ const traceSize = bytes.length
81
+ const buffer = Buffer.allocUnsafe(traceSize)
82
+
83
+ bytes.buffer.copy(buffer, 0, 0, bytes.length)
84
+
85
+ this.form.append(
86
+ 'coverage1',
87
+ buffer,
88
+ {
89
+ filename: `coverage1.msgpack`,
90
+ contentType: 'application/msgpack'
91
+ }
92
+ )
78
93
  this.form.append(
79
94
  'event',
80
95
  // The intake requires a populated dictionary here. Simply having {} is not valid.
@@ -86,7 +101,7 @@ class CoverageCIVisibilityEncoder extends AgentEncoder {
86
101
  const form = this.form
87
102
 
88
103
  this.form = new FormData()
89
- this.fileIndex = 1
104
+ this.reset()
90
105
 
91
106
  return form
92
107
  }
@@ -38,11 +38,12 @@ function truncateToLength (value, maxLength) {
38
38
  return value
39
39
  }
40
40
 
41
- function truncateSpan (span) {
41
+ // normally the agent truncates the resource and parses it in certain scenarios (e.g. SQL Queries)
42
+ function truncateSpan (span, shouldTruncateResourceName = true) {
42
43
  return fromEntries(Object.entries(span).map(([key, value]) => {
43
44
  switch (key) {
44
45
  case 'resource':
45
- return ['resource', truncateToLength(value, MAX_RESOURCE_NAME_LENGTH)]
46
+ return ['resource', shouldTruncateResourceName ? truncateToLength(value, MAX_RESOURCE_NAME_LENGTH) : value]
46
47
  case 'meta':
47
48
  return ['meta', fromEntries(Object.entries(value).map(([metaKey, metaValue]) =>
48
49
  [truncateToLength(metaKey, MAX_META_KEY_LENGTH), truncateToLength(metaValue, MAX_META_VALUE_LENGTH)]
@@ -98,9 +98,16 @@ function request (data, options, callback) {
98
98
  if (res.statusCode >= 200 && res.statusCode <= 299) {
99
99
  callback(null, responseData, res.statusCode)
100
100
  } else {
101
- const fullUrl = `${options.url || options.hostname || `localhost:${options.port}`}${options.path}`
102
- // eslint-disable-next-line
103
- let errorMessage = `Error from ${fullUrl}: ${res.statusCode} ${http.STATUS_CODES[res.statusCode]}.`
101
+ let errorMessage = ''
102
+ try {
103
+ const fullUrl = new URL(
104
+ options.path,
105
+ options.url || options.hostname || `http://localhost:${options.port}`
106
+ ).href
107
+ errorMessage = `Error from ${fullUrl}: ${res.statusCode} ${http.STATUS_CODES[res.statusCode]}.`
108
+ } catch (e) {
109
+ // ignore error
110
+ }
104
111
  if (responseData) {
105
112
  errorMessage += ` Response from the endpoint: "${responseData}"`
106
113
  }
@@ -22,15 +22,14 @@ let __lambdaTimeout
22
22
  * @param {*} context AWS Lambda context object.
23
23
  */
24
24
  function checkTimeout (context) {
25
- let remainingTimeInMillis = context.getRemainingTimeInMillis()
26
- const apmFlushDeadline = parseInt(process.env.DD_APM_FLUSH_DEADLINE)
27
- if (apmFlushDeadline && apmFlushDeadline <= remainingTimeInMillis) {
28
- remainingTimeInMillis = apmFlushDeadline
29
- }
25
+ const remainingTimeInMillis = context.getRemainingTimeInMillis()
26
+
27
+ let apmFlushDeadline = parseInt(process.env.DD_APM_FLUSH_DEADLINE_MILLISECONDS) || 100
28
+ apmFlushDeadline = apmFlushDeadline < 0 ? 100 : apmFlushDeadline
30
29
 
31
30
  __lambdaTimeout = setTimeout(() => {
32
31
  timeoutChannel.publish(undefined)
33
- }, remainingTimeInMillis - 50)
32
+ }, remainingTimeInMillis - apmFlushDeadline)
34
33
  }
35
34
 
36
35
  /**
@@ -0,0 +1,47 @@
1
+ 'use strict'
2
+
3
+ const dc = require('diagnostics_channel')
4
+
5
+ const Level = {
6
+ Debug: 'debug',
7
+ Info: 'info',
8
+ Warn: 'warn',
9
+ Error: 'error'
10
+ }
11
+
12
+ const defaultLevel = Level.Debug
13
+
14
+ class LogChannel extends dc.Channel {
15
+ constructor (name, logLevel) {
16
+ super(`datadog:log:${name}`)
17
+ this.logLevel = logLevel
18
+ }
19
+ }
20
+
21
+ // based on: https://github.com/trentm/node-bunyan#levels
22
+ const logChannels = {
23
+ [Level.Debug]: new LogChannel(Level.Debug, 20),
24
+ [Level.Info]: new LogChannel(Level.Info, 30),
25
+ [Level.Warn]: new LogChannel(Level.Warn, 40),
26
+ [Level.Error]: new LogChannel(Level.Error, 50)
27
+ }
28
+
29
+ function getChannelLogLevel (level) {
30
+ let logChannel
31
+ if (level && typeof level === 'string') {
32
+ logChannel = logChannels[level.toLowerCase().trim()] || logChannels[defaultLevel]
33
+ } else {
34
+ logChannel = logChannels[defaultLevel]
35
+ }
36
+ return logChannel.logLevel
37
+ }
38
+
39
+ module.exports = {
40
+ Level,
41
+ getChannelLogLevel,
42
+
43
+ debugChannel: logChannels[Level.Debug],
44
+ infoChannel: logChannels[Level.Info],
45
+ warnChannel: logChannels[Level.Warn],
46
+ errorChannel: logChannels[Level.Error]
47
+ }
@@ -0,0 +1,79 @@
1
+ 'use strict'
2
+
3
+ const { debugChannel, infoChannel, warnChannel, errorChannel } = require('./channels')
4
+ const logWriter = require('./writer')
5
+
6
+ const memoize = func => {
7
+ const cache = {}
8
+ const memoized = function (key) {
9
+ if (!cache[key]) {
10
+ cache[key] = func.apply(this, arguments)
11
+ }
12
+
13
+ return cache[key]
14
+ }
15
+
16
+ return memoized
17
+ }
18
+
19
+ function processMsg (msg) {
20
+ return typeof msg === 'function' ? msg() : msg
21
+ }
22
+
23
+ const log = {
24
+ use (logger) {
25
+ logWriter.use(logger)
26
+ return this
27
+ },
28
+
29
+ toggle (enabled, logLevel) {
30
+ logWriter.toggle(enabled, logLevel)
31
+ return this
32
+ },
33
+
34
+ reset () {
35
+ logWriter.reset()
36
+ this._deprecate = memoize((code, message) => {
37
+ errorChannel.publish(message)
38
+ return true
39
+ })
40
+
41
+ return this
42
+ },
43
+
44
+ debug (message) {
45
+ if (debugChannel.hasSubscribers) {
46
+ debugChannel.publish(processMsg(message))
47
+ }
48
+ return this
49
+ },
50
+
51
+ info (message) {
52
+ if (infoChannel.hasSubscribers) {
53
+ infoChannel.publish(processMsg(message))
54
+ }
55
+ return this
56
+ },
57
+
58
+ warn (message) {
59
+ if (warnChannel.hasSubscribers) {
60
+ warnChannel.publish(processMsg(message))
61
+ }
62
+ return this
63
+ },
64
+
65
+ error (err) {
66
+ if (errorChannel.hasSubscribers) {
67
+ errorChannel.publish(processMsg(err))
68
+ }
69
+ return this
70
+ },
71
+
72
+ deprecate (code, message) {
73
+ return this._deprecate(code, message)
74
+ }
75
+ }
76
+
77
+ log.reset()
78
+
79
+ module.exports = log
@@ -0,0 +1,124 @@
1
+ 'use strict'
2
+
3
+ const { storage } = require('../../../datadog-core')
4
+ const { getChannelLogLevel, debugChannel, infoChannel, warnChannel, errorChannel } = require('./channels')
5
+
6
+ const defaultLogger = {
7
+ debug: msg => console.debug(msg), /* eslint-disable-line no-console */
8
+ info: msg => console.info(msg), /* eslint-disable-line no-console */
9
+ warn: msg => console.warn(msg), /* eslint-disable-line no-console */
10
+ error: msg => console.error(msg) /* eslint-disable-line no-console */
11
+ }
12
+
13
+ let enabled = false
14
+ let logger = defaultLogger
15
+ let logLevel = getChannelLogLevel()
16
+
17
+ function withNoop (fn) {
18
+ const store = storage.getStore()
19
+
20
+ storage.enterWith({ noop: true })
21
+ fn()
22
+ storage.enterWith(store)
23
+ }
24
+
25
+ function unsubscribeAll () {
26
+ if (debugChannel.hasSubscribers) {
27
+ debugChannel.unsubscribe(onDebug)
28
+ }
29
+ if (infoChannel.hasSubscribers) {
30
+ infoChannel.unsubscribe(onInfo)
31
+ }
32
+ if (warnChannel.hasSubscribers) {
33
+ warnChannel.unsubscribe(onWarn)
34
+ }
35
+ if (errorChannel.hasSubscribers) {
36
+ errorChannel.unsubscribe(onError)
37
+ }
38
+ }
39
+
40
+ function toggleSubscription (enable) {
41
+ unsubscribeAll()
42
+
43
+ if (enable) {
44
+ if (debugChannel.logLevel >= logLevel) {
45
+ debugChannel.subscribe(onDebug)
46
+ }
47
+ if (infoChannel.logLevel >= logLevel) {
48
+ infoChannel.subscribe(onInfo)
49
+ }
50
+ if (warnChannel.logLevel >= logLevel) {
51
+ warnChannel.subscribe(onWarn)
52
+ }
53
+ if (errorChannel.logLevel >= logLevel) {
54
+ errorChannel.subscribe(onError)
55
+ }
56
+ }
57
+ }
58
+
59
+ function toggle (enable, level) {
60
+ if (level !== undefined) {
61
+ logLevel = getChannelLogLevel(level)
62
+ }
63
+ enabled = enable
64
+ toggleSubscription(enabled)
65
+ }
66
+
67
+ function use (newLogger) {
68
+ if (newLogger && newLogger.debug instanceof Function && newLogger.error instanceof Function) {
69
+ logger = newLogger
70
+ }
71
+ }
72
+
73
+ function reset () {
74
+ logger = defaultLogger
75
+ enabled = false
76
+ logLevel = getChannelLogLevel()
77
+ toggleSubscription(false)
78
+ }
79
+
80
+ function onError (err) {
81
+ if (enabled) error(err)
82
+ }
83
+
84
+ function onWarn (message) {
85
+ if (enabled) warn(message)
86
+ }
87
+
88
+ function onInfo (message) {
89
+ if (enabled) info(message)
90
+ }
91
+
92
+ function onDebug (message) {
93
+ if (enabled) debug(message)
94
+ }
95
+
96
+ function error (err) {
97
+ if (typeof err !== 'object' || !err) {
98
+ err = String(err)
99
+ } else if (!err.stack) {
100
+ err = String(err.message || err)
101
+ }
102
+
103
+ if (typeof err === 'string') {
104
+ err = new Error(err)
105
+ }
106
+
107
+ withNoop(() => logger.error(err))
108
+ }
109
+
110
+ function warn (message) {
111
+ if (!logger.warn) return debug(message)
112
+ withNoop(() => logger.warn(message))
113
+ }
114
+
115
+ function info (message) {
116
+ if (!logger.info) return debug(message)
117
+ withNoop(() => logger.info(message))
118
+ }
119
+
120
+ function debug (message) {
121
+ withNoop(() => logger.debug(message))
122
+ }
123
+
124
+ module.exports = { use, toggle, reset, error, warn, info, debug }
@@ -8,6 +8,7 @@ const os = require('os')
8
8
  const Client = require('./dogstatsd')
9
9
  const log = require('./log')
10
10
  const Histogram = require('./histogram')
11
+ const { performance } = require('perf_hooks')
11
12
 
12
13
  const INTERVAL = 10 * 1000
13
14
 
@@ -20,6 +21,7 @@ let cpuUsage
20
21
  let gauges
21
22
  let counters
22
23
  let histograms
24
+ let elu
23
25
 
24
26
  reset()
25
27
 
@@ -259,6 +261,21 @@ function captureHistograms () {
259
261
  })
260
262
  }
261
263
 
264
+ /**
265
+ * Gathers and reports Event Loop Utilization (ELU) since last run
266
+ *
267
+ * ELU is a measure of how busy the event loop is, like running JavaScript or
268
+ * waiting on *Sync functions. The value is between 0 (idle) and 1 (exhausted).
269
+ *
270
+ * performance.eventLoopUtilization available in Node.js >= v14.10, >= v12.19, >= v16
271
+ */
272
+ const captureELU = ('eventLoopUtilization' in performance) ? () => {
273
+ // if elu is undefined (first run) the measurement is from start of process
274
+ elu = performance.eventLoopUtilization(elu)
275
+
276
+ client.gauge('runtime.node.event_loop.utilization', elu.utilization)
277
+ } : () => {}
278
+
262
279
  function captureCommonMetrics () {
263
280
  captureMemoryUsage()
264
281
  captureProcess()
@@ -266,6 +283,7 @@ function captureCommonMetrics () {
266
283
  captureGauges()
267
284
  captureCounters()
268
285
  captureHistograms()
286
+ captureELU()
269
287
  }
270
288
 
271
289
  function captureNativeMetrics () {
@@ -1,12 +1,15 @@
1
1
  'use strict'
2
2
 
3
3
  const NoopTracer = require('./tracer')
4
+ const NoopAppsecSdk = require('../appsec/sdk/noop')
4
5
 
5
6
  const noop = new NoopTracer()
7
+ const noopAppsec = new NoopAppsecSdk()
6
8
 
7
9
  class Tracer {
8
10
  constructor () {
9
11
  this._tracer = noop
12
+ this.appsec = noopAppsec
10
13
  }
11
14
 
12
15
  init () {
@@ -68,8 +71,8 @@ class Tracer {
68
71
  return this._tracer.getRumData.apply(this._tracer, arguments)
69
72
  }
70
73
 
71
- setUser () {
72
- this._tracer.setUser.apply(this._tracer, arguments)
74
+ setUser (user) {
75
+ this.appsec.setUser(user)
73
76
  return this
74
77
  }
75
78
  }