dd-trace 3.14.1 → 3.16.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 (187) hide show
  1. package/LICENSE-3rdparty.csv +2 -2
  2. package/README.md +9 -5
  3. package/ci/init.js +9 -1
  4. package/ext/exporters.d.ts +2 -1
  5. package/ext/exporters.js +2 -1
  6. package/index.d.ts +36 -3
  7. package/package.json +21 -19
  8. package/packages/datadog-instrumentations/src/cucumber.js +80 -3
  9. package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +100 -27
  10. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  11. package/packages/datadog-instrumentations/src/jest.js +35 -3
  12. package/packages/datadog-instrumentations/src/ldapjs.js +12 -2
  13. package/packages/datadog-instrumentations/src/mariadb.js +130 -11
  14. package/packages/datadog-instrumentations/src/mocha.js +30 -6
  15. package/packages/datadog-instrumentations/src/mongodb-core.js +8 -2
  16. package/packages/datadog-instrumentations/src/mongoose.js +1 -1
  17. package/packages/datadog-instrumentations/src/next.js +33 -4
  18. package/packages/datadog-instrumentations/src/playwright.js +42 -13
  19. package/packages/datadog-plugin-amqp10/src/consumer.js +1 -1
  20. package/packages/datadog-plugin-amqp10/src/index.js +1 -1
  21. package/packages/datadog-plugin-amqp10/src/producer.js +3 -2
  22. package/packages/datadog-plugin-amqplib/src/client.js +3 -2
  23. package/packages/datadog-plugin-amqplib/src/consumer.js +1 -1
  24. package/packages/datadog-plugin-amqplib/src/index.js +1 -1
  25. package/packages/datadog-plugin-amqplib/src/producer.js +3 -2
  26. package/packages/datadog-plugin-aws-sdk/src/base.js +7 -2
  27. package/packages/datadog-plugin-aws-sdk/src/index.js +1 -1
  28. package/packages/datadog-plugin-aws-sdk/src/services/cloudwatchlogs.js +2 -0
  29. package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +2 -0
  30. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +2 -0
  31. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +2 -0
  32. package/packages/datadog-plugin-aws-sdk/src/services/lambda.js +2 -0
  33. package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +2 -0
  34. package/packages/datadog-plugin-aws-sdk/src/services/s3.js +2 -0
  35. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +2 -0
  36. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +2 -0
  37. package/packages/datadog-plugin-bunyan/src/index.js +1 -1
  38. package/packages/datadog-plugin-cassandra-driver/src/index.js +3 -2
  39. package/packages/datadog-plugin-connect/src/index.js +1 -1
  40. package/packages/datadog-plugin-couchbase/src/index.js +1 -1
  41. package/packages/datadog-plugin-cucumber/src/index.js +33 -6
  42. package/packages/datadog-plugin-cypress/src/index.js +1 -1
  43. package/packages/datadog-plugin-cypress/src/plugin.js +40 -33
  44. package/packages/datadog-plugin-dns/src/index.js +1 -1
  45. package/packages/datadog-plugin-dns/src/lookup.js +1 -1
  46. package/packages/datadog-plugin-dns/src/lookup_service.js +1 -1
  47. package/packages/datadog-plugin-dns/src/resolve.js +1 -1
  48. package/packages/datadog-plugin-dns/src/reverse.js +1 -1
  49. package/packages/datadog-plugin-elasticsearch/src/index.js +1 -1
  50. package/packages/datadog-plugin-express/src/index.js +1 -1
  51. package/packages/datadog-plugin-fastify/src/index.js +1 -1
  52. package/packages/datadog-plugin-find-my-way/src/index.js +1 -1
  53. package/packages/datadog-plugin-fs/src/index.js +1 -1
  54. package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +5 -5
  55. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +1 -1
  56. package/packages/datadog-plugin-google-cloud-pubsub/src/index.js +1 -1
  57. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +7 -6
  58. package/packages/datadog-plugin-graphql/src/execute.js +1 -1
  59. package/packages/datadog-plugin-graphql/src/index.js +1 -1
  60. package/packages/datadog-plugin-graphql/src/parse.js +1 -1
  61. package/packages/datadog-plugin-graphql/src/resolve.js +1 -1
  62. package/packages/datadog-plugin-graphql/src/validate.js +1 -1
  63. package/packages/datadog-plugin-grpc/src/client.js +1 -1
  64. package/packages/datadog-plugin-grpc/src/index.js +1 -1
  65. package/packages/datadog-plugin-grpc/src/server.js +1 -1
  66. package/packages/datadog-plugin-hapi/src/index.js +1 -1
  67. package/packages/datadog-plugin-http/src/client.js +2 -2
  68. package/packages/datadog-plugin-http/src/index.js +1 -1
  69. package/packages/datadog-plugin-http/src/server.js +3 -3
  70. package/packages/datadog-plugin-http2/src/client.js +4 -3
  71. package/packages/datadog-plugin-http2/src/index.js +1 -1
  72. package/packages/datadog-plugin-http2/src/server.js +3 -3
  73. package/packages/datadog-plugin-ioredis/src/index.js +1 -1
  74. package/packages/datadog-plugin-jest/src/index.js +53 -19
  75. package/packages/datadog-plugin-kafkajs/src/consumer.js +1 -1
  76. package/packages/datadog-plugin-kafkajs/src/index.js +1 -1
  77. package/packages/datadog-plugin-kafkajs/src/producer.js +1 -1
  78. package/packages/datadog-plugin-koa/src/index.js +1 -1
  79. package/packages/datadog-plugin-mariadb/src/index.js +18 -1
  80. package/packages/datadog-plugin-memcached/src/index.js +3 -2
  81. package/packages/datadog-plugin-microgateway-core/src/index.js +1 -1
  82. package/packages/datadog-plugin-mocha/src/index.js +13 -9
  83. package/packages/datadog-plugin-moleculer/src/client.js +1 -1
  84. package/packages/datadog-plugin-moleculer/src/index.js +1 -1
  85. package/packages/datadog-plugin-moleculer/src/server.js +1 -1
  86. package/packages/datadog-plugin-mongodb-core/src/index.js +1 -1
  87. package/packages/datadog-plugin-mysql/src/index.js +3 -2
  88. package/packages/datadog-plugin-mysql2/src/index.js +1 -1
  89. package/packages/datadog-plugin-net/src/index.js +9 -75
  90. package/packages/datadog-plugin-net/src/ipc.js +1 -1
  91. package/packages/datadog-plugin-net/src/tcp.js +3 -2
  92. package/packages/datadog-plugin-next/src/index.js +3 -3
  93. package/packages/datadog-plugin-opensearch/src/index.js +1 -1
  94. package/packages/datadog-plugin-oracledb/src/index.js +3 -2
  95. package/packages/datadog-plugin-paperplane/src/index.js +1 -1
  96. package/packages/datadog-plugin-paperplane/src/logger.js +1 -1
  97. package/packages/datadog-plugin-paperplane/src/server.js +1 -1
  98. package/packages/datadog-plugin-pg/src/index.js +3 -2
  99. package/packages/datadog-plugin-pino/src/index.js +1 -1
  100. package/packages/datadog-plugin-playwright/src/index.js +5 -4
  101. package/packages/datadog-plugin-redis/src/index.js +3 -2
  102. package/packages/datadog-plugin-restify/src/index.js +1 -1
  103. package/packages/datadog-plugin-rhea/src/consumer.js +1 -1
  104. package/packages/datadog-plugin-rhea/src/index.js +1 -1
  105. package/packages/datadog-plugin-rhea/src/producer.js +3 -2
  106. package/packages/datadog-plugin-router/src/index.js +8 -8
  107. package/packages/datadog-plugin-sharedb/src/index.js +1 -1
  108. package/packages/datadog-plugin-tedious/src/index.js +3 -2
  109. package/packages/datadog-plugin-web/src/index.js +1 -1
  110. package/packages/datadog-plugin-winston/src/index.js +1 -1
  111. package/packages/dd-trace/src/appsec/addresses.js +3 -1
  112. package/packages/dd-trace/src/appsec/blocking.js +35 -9
  113. package/packages/dd-trace/src/appsec/gateway/engine/runner.js +2 -1
  114. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +2 -0
  115. package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +2 -2
  116. package/packages/dd-trace/src/appsec/iast/iast-context.js +6 -2
  117. package/packages/dd-trace/src/appsec/iast/iast-log.js +111 -0
  118. package/packages/dd-trace/src/appsec/iast/index.js +10 -6
  119. package/packages/dd-trace/src/appsec/iast/path-line.js +3 -6
  120. package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +2 -0
  121. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +2 -0
  122. package/packages/dd-trace/src/appsec/iast/taint-tracking/origin-types.js +2 -0
  123. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +2 -0
  124. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +9 -4
  125. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +5 -3
  126. package/packages/dd-trace/src/appsec/iast/telemetry/log_collector.js +96 -0
  127. package/packages/dd-trace/src/appsec/iast/telemetry/logs.js +87 -0
  128. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +27 -2
  129. package/packages/dd-trace/src/appsec/index.js +4 -4
  130. package/packages/dd-trace/src/appsec/recommended.json +76 -75
  131. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -1
  132. package/packages/dd-trace/src/appsec/remote_config/index.js +3 -0
  133. package/packages/dd-trace/src/appsec/sdk/index.js +19 -1
  134. package/packages/dd-trace/src/appsec/sdk/noop.js +6 -0
  135. package/packages/dd-trace/src/appsec/sdk/set_user.js +30 -0
  136. package/packages/dd-trace/src/appsec/sdk/track_event.js +2 -2
  137. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +73 -0
  138. package/packages/dd-trace/src/ci-visibility/encode/json-encoder.js +27 -0
  139. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +17 -9
  140. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +14 -8
  141. package/packages/dd-trace/src/ci-visibility/exporters/jest-worker/index.js +33 -0
  142. package/packages/dd-trace/src/ci-visibility/exporters/jest-worker/writer.js +37 -0
  143. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +12 -4
  144. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +12 -4
  145. package/packages/dd-trace/src/config.js +24 -5
  146. package/packages/dd-trace/src/constants.js +2 -1
  147. package/packages/dd-trace/src/datastreams/encoding.js +80 -0
  148. package/packages/dd-trace/src/exporter.js +7 -9
  149. package/packages/dd-trace/src/exporters/common/agents.js +42 -0
  150. package/packages/dd-trace/src/exporters/common/docker.js +4 -1
  151. package/packages/dd-trace/src/exporters/common/request.js +1 -4
  152. package/packages/dd-trace/src/lambda/handler.js +19 -12
  153. package/packages/dd-trace/src/log/writer.js +32 -24
  154. package/packages/dd-trace/src/metrics.js +18 -0
  155. package/packages/dd-trace/src/noop/proxy.js +2 -2
  156. package/packages/dd-trace/src/opentracing/span.js +5 -0
  157. package/packages/dd-trace/src/opentracing/span_context.js +1 -1
  158. package/packages/dd-trace/src/plugin_manager.js +7 -7
  159. package/packages/dd-trace/src/plugins/ci_plugin.js +20 -17
  160. package/packages/dd-trace/src/plugins/index.js +1 -0
  161. package/packages/dd-trace/src/plugins/log_plugin.js +1 -1
  162. package/packages/dd-trace/src/plugins/outgoing.js +2 -1
  163. package/packages/dd-trace/src/plugins/tracing.js +1 -1
  164. package/packages/dd-trace/src/plugins/util/ci.js +12 -0
  165. package/packages/dd-trace/src/plugins/util/exec.js +2 -2
  166. package/packages/dd-trace/src/plugins/util/git.js +16 -1
  167. package/packages/dd-trace/src/plugins/util/ip_extractor.js +23 -27
  168. package/packages/dd-trace/src/plugins/util/test.js +26 -7
  169. package/packages/dd-trace/src/profiler.js +3 -0
  170. package/packages/dd-trace/src/profiling/config.js +92 -20
  171. package/packages/dd-trace/src/profiling/constants.js +16 -0
  172. package/packages/dd-trace/src/profiling/exporter_cli.js +62 -0
  173. package/packages/dd-trace/src/profiling/exporters/agent.js +2 -1
  174. package/packages/dd-trace/src/profiling/exporters/file.js +13 -2
  175. package/packages/dd-trace/src/profiling/profiler.js +42 -12
  176. package/packages/dd-trace/src/profiling/profilers/space.js +21 -1
  177. package/packages/dd-trace/src/profiling/profilers/wall.js +1 -0
  178. package/packages/dd-trace/src/proxy.js +1 -1
  179. package/packages/dd-trace/src/span_processor.js +1 -1
  180. package/packages/dd-trace/src/span_sampler.js +71 -54
  181. package/packages/dd-trace/src/startup-log.js +3 -6
  182. package/packages/dd-trace/src/telemetry/index.js +16 -2
  183. package/packages/dd-trace/src/tracer.js +0 -16
  184. package/packages/dd-trace/src/util.js +10 -1
  185. package/scripts/install_plugin_modules.js +5 -1
  186. package/scripts/junit_report.js +0 -25
  187. package/scripts/tdd.js +0 -34
@@ -2,48 +2,59 @@
2
2
 
3
3
  const coalesce = require('koalas')
4
4
  const os = require('os')
5
- const { URL, format } = require('url')
5
+ const path = require('path')
6
+ const { URL, format, pathToFileURL } = require('url')
6
7
  const { AgentExporter } = require('./exporters/agent')
7
8
  const { FileExporter } = require('./exporters/file')
8
9
  const { ConsoleLogger } = require('./loggers/console')
9
10
  const CpuProfiler = require('./profilers/cpu')
10
11
  const WallProfiler = require('./profilers/wall')
11
12
  const SpaceProfiler = require('./profilers/space')
13
+ const { oomExportStrategies, snapshotKinds } = require('./constants')
12
14
  const { tagger } = require('./tagger')
13
-
14
- const {
15
- DD_PROFILING_ENABLED,
16
- DD_PROFILING_PROFILERS,
17
- DD_PROFILING_ENDPOINT_COLLECTION_ENABLED,
18
- DD_ENV,
19
- DD_TAGS,
20
- DD_SERVICE,
21
- DD_VERSION,
22
- DD_TRACE_AGENT_URL,
23
- DD_AGENT_HOST,
24
- DD_TRACE_AGENT_PORT,
25
- DD_PROFILING_UPLOAD_TIMEOUT,
26
- DD_PROFILING_SOURCE_MAP
27
- } = process.env
15
+ const { isTrue } = require('../util')
28
16
 
29
17
  class Config {
30
18
  constructor (options = {}) {
31
- const enabled = coalesce(options.enabled, DD_PROFILING_ENABLED, true)
19
+ const {
20
+ DD_PROFILING_ENABLED,
21
+ DD_PROFILING_PROFILERS,
22
+ DD_PROFILING_ENDPOINT_COLLECTION_ENABLED,
23
+ DD_ENV,
24
+ DD_TAGS,
25
+ DD_SERVICE,
26
+ DD_VERSION,
27
+ DD_TRACE_AGENT_URL,
28
+ DD_AGENT_HOST,
29
+ DD_TRACE_AGENT_PORT,
30
+ DD_PROFILING_UPLOAD_TIMEOUT,
31
+ DD_PROFILING_SOURCE_MAP,
32
+ DD_PROFILING_UPLOAD_PERIOD,
33
+ DD_PROFILING_PPROF_PREFIX,
34
+ DD_PROFILING_EXPERIMENTAL_OOM_MONITORING_ENABLED,
35
+ DD_PROFILING_EXPERIMENTAL_OOM_HEAP_LIMIT_EXTENSION_SIZE,
36
+ DD_PROFILING_EXPERIMENTAL_OOM_MAX_HEAP_EXTENSION_COUNT,
37
+ DD_PROFILING_EXPERIMENTAL_OOM_EXPORT_STRATEGIES
38
+ } = process.env
39
+
40
+ const enabled = isTrue(coalesce(options.enabled, DD_PROFILING_ENABLED, true))
32
41
  const env = coalesce(options.env, DD_ENV)
33
42
  const service = options.service || DD_SERVICE || 'node'
34
43
  const host = os.hostname()
35
44
  const version = coalesce(options.version, DD_VERSION)
36
45
  const functionname = process.env.AWS_LAMBDA_FUNCTION_NAME
37
46
  // Must be longer than one minute so pad with five seconds
38
- const flushInterval = coalesce(options.interval, 65 * 1000)
47
+ const flushInterval = coalesce(options.interval, Number(DD_PROFILING_UPLOAD_PERIOD) * 1000, 65 * 1000)
39
48
  const uploadTimeout = coalesce(options.uploadTimeout,
40
- DD_PROFILING_UPLOAD_TIMEOUT, 60 * 1000)
49
+ Number(DD_PROFILING_UPLOAD_TIMEOUT), 60 * 1000)
41
50
  const sourceMap = coalesce(options.sourceMap,
42
51
  DD_PROFILING_SOURCE_MAP, true)
43
52
  const endpointCollection = coalesce(options.endpointCollection,
44
53
  DD_PROFILING_ENDPOINT_COLLECTION_ENABLED, false)
54
+ const pprofPrefix = coalesce(options.pprofPrefix,
55
+ DD_PROFILING_PPROF_PREFIX)
45
56
 
46
- this.enabled = String(enabled) !== 'false'
57
+ this.enabled = enabled
47
58
  this.service = service
48
59
  this.env = env
49
60
  this.host = host
@@ -60,6 +71,7 @@ class Config {
60
71
  this.uploadTimeout = uploadTimeout
61
72
  this.sourceMap = sourceMap
62
73
  this.endpointCollection = endpointCollection
74
+ this.pprofPrefix = pprofPrefix
63
75
 
64
76
  const hostname = coalesce(options.hostname, DD_AGENT_HOST) || 'localhost'
65
77
  const port = coalesce(options.port, DD_TRACE_AGENT_PORT) || 8126
@@ -73,6 +85,23 @@ class Config {
73
85
  new AgentExporter(this)
74
86
  ], this)
75
87
 
88
+ const oomMonitoringEnabled = isTrue(coalesce(options.oomMonitoring,
89
+ DD_PROFILING_EXPERIMENTAL_OOM_MONITORING_ENABLED, false))
90
+ const heapLimitExtensionSize = coalesce(options.oomHeapLimitExtensionSize,
91
+ Number(DD_PROFILING_EXPERIMENTAL_OOM_HEAP_LIMIT_EXTENSION_SIZE), 0)
92
+ const maxHeapExtensionCount = coalesce(options.oomMaxHeapExtensionCount,
93
+ Number(DD_PROFILING_EXPERIMENTAL_OOM_MAX_HEAP_EXTENSION_COUNT), 0)
94
+ const exportStrategies = ensureOOMExportStrategies(coalesce(options.oomExportStrategies,
95
+ DD_PROFILING_EXPERIMENTAL_OOM_EXPORT_STRATEGIES), this)
96
+ const exportCommand = oomMonitoringEnabled ? buildExportCommand(this) : undefined
97
+ this.oomMonitoring = {
98
+ enabled: oomMonitoringEnabled,
99
+ heapLimitExtensionSize,
100
+ maxHeapExtensionCount,
101
+ exportStrategies,
102
+ exportCommand
103
+ }
104
+
76
105
  const profilers = coalesce(options.profilers, DD_PROFILING_PROFILERS, [
77
106
  new WallProfiler(this),
78
107
  new SpaceProfiler(this)
@@ -84,6 +113,33 @@ class Config {
84
113
 
85
114
  module.exports = { Config }
86
115
 
116
+ function getExportStrategy (name, options) {
117
+ const strategy = Object.values(oomExportStrategies).find(value => value === name)
118
+ if (strategy === undefined) {
119
+ options.logger.error(`Unknown oom export strategy "${name}"`)
120
+ }
121
+ return strategy
122
+ }
123
+
124
+ function ensureOOMExportStrategies (strategies, options) {
125
+ if (!strategies) {
126
+ return []
127
+ }
128
+
129
+ if (typeof strategies === 'string') {
130
+ strategies = strategies.split(',')
131
+ }
132
+
133
+ for (let i = 0; i < strategies.length; i++) {
134
+ const strategy = strategies[i]
135
+ if (typeof strategy === 'string') {
136
+ strategies[i] = getExportStrategy(strategy, options)
137
+ }
138
+ }
139
+
140
+ return [ ...new Set(strategies) ]
141
+ }
142
+
87
143
  function getExporter (name, options) {
88
144
  switch (name) {
89
145
  case 'agent':
@@ -149,3 +205,19 @@ function ensureLogger (logger) {
149
205
 
150
206
  return logger
151
207
  }
208
+
209
+ function buildExportCommand (options) {
210
+ const tags = [...Object.entries(options.tags),
211
+ ['snapshot', snapshotKinds.ON_OUT_OF_MEMORY]].map(([key, value]) => `${key}:${value}`).join(',')
212
+ const urls = []
213
+ for (const exporter of options.exporters) {
214
+ if (exporter instanceof AgentExporter) {
215
+ urls.push(options.url.toString())
216
+ } else if (exporter instanceof FileExporter) {
217
+ urls.push(pathToFileURL(options.pprofPrefix).toString())
218
+ }
219
+ }
220
+ return [process.execPath,
221
+ path.join(__dirname, 'exporter_cli.js'),
222
+ urls.join(','), tags, 'space']
223
+ }
@@ -0,0 +1,16 @@
1
+ 'use strict'
2
+
3
+ const snapshotKinds = Object.freeze({
4
+ PERIODIC: 'periodic',
5
+ ON_SHUTDOWN: 'on_shutdown',
6
+ ON_OUT_OF_MEMORY: 'on_oom'
7
+ })
8
+
9
+ const oomExportStrategies = Object.freeze({
10
+ PROCESS: 'process',
11
+ ASYNC_CALLBACK: 'async',
12
+ INTERRUPT_CALLBACK: 'interrupt',
13
+ LOGS: 'logs'
14
+ })
15
+
16
+ module.exports = { snapshotKinds, oomExportStrategies }
@@ -0,0 +1,62 @@
1
+ 'use strict'
2
+
3
+ const { AgentExporter } = require('./exporters/agent')
4
+ const { FileExporter } = require('./exporters/file')
5
+
6
+ const { SourceMapper, heap, encode } = require('@datadog/pprof')
7
+ const { ConsoleLogger } = require('./loggers/console')
8
+ const { tagger } = require('./tagger')
9
+ const fs = require('fs')
10
+ const { fileURLToPath } = require('url')
11
+
12
+ const logger = new ConsoleLogger()
13
+ const timeoutMs = 10 * 1000
14
+
15
+ function exporterFromURL (url) {
16
+ if (url.protocol === 'file:') {
17
+ return new FileExporter({ pprofPrefix: fileURLToPath(url) })
18
+ } else {
19
+ return new AgentExporter({
20
+ url,
21
+ logger,
22
+ uploadTimeout: timeoutMs
23
+ })
24
+ }
25
+ }
26
+
27
+ async function exportProfile (urls, tags, profileType, profile) {
28
+ let mapper
29
+ try {
30
+ mapper = await SourceMapper.create([process.cwd()])
31
+ } catch (err) {
32
+ logger.error(err)
33
+ }
34
+
35
+ const encodedProfile = await encode(heap.convertProfile(profile, undefined, mapper))
36
+ const start = new Date()
37
+ for (const url of urls) {
38
+ const exporter = exporterFromURL(url)
39
+
40
+ await exporter.export({
41
+ profiles: {
42
+ [profileType]: encodedProfile
43
+ },
44
+ start,
45
+ end: start,
46
+ tags
47
+ })
48
+ }
49
+ }
50
+
51
+ /** Expected command line arguments are:
52
+ * - Comma separated list of URLs (eg. "http://127.0.0.1:8126/,file:///tmp/foo.pprof")
53
+ * - Tags (eg. "service:nodejs_oom_test,version:1.0.0")
54
+ * - Profiletype (eg. space,wall,cpu)
55
+ * - JSON profile filepath
56
+ **/
57
+ const urls = process.argv[2].split(',').map(s => new URL(s))
58
+ const tags = tagger.parse(process.argv[3])
59
+ const profileType = process.argv[4]
60
+ const profile = JSON.parse(fs.readFileSync(process.argv[5]))
61
+
62
+ exportProfile(urls, tags, profileType, profile)
@@ -103,7 +103,8 @@ class AgentExporter {
103
103
  const operation = retry.operation({
104
104
  randomize: true,
105
105
  minTimeout: this._backoffTime,
106
- retries: this._backoffTries
106
+ retries: this._backoffTries,
107
+ unref: true
107
108
  })
108
109
 
109
110
  operation.attempt((attempt) => {
@@ -4,11 +4,22 @@ const fs = require('fs')
4
4
  const { promisify } = require('util')
5
5
  const writeFile = promisify(fs.writeFile)
6
6
 
7
+ function formatDateTime (t) {
8
+ const pad = (n) => String(n).padStart(2, '0')
9
+ return `${t.getUTCFullYear()}${pad(t.getUTCMonth() + 1)}${pad(t.getUTCDate())}` +
10
+ `T${pad(t.getUTCHours())}${pad(t.getUTCMinutes())}${pad(t.getUTCSeconds())}Z`
11
+ }
12
+
7
13
  class FileExporter {
8
- export ({ profiles }) {
14
+ constructor ({ pprofPrefix } = {}) {
15
+ this._pprofPrefix = pprofPrefix || ''
16
+ }
17
+
18
+ export ({ profiles, end }) {
9
19
  const types = Object.keys(profiles)
20
+ const dateStr = formatDateTime(end)
10
21
  const tasks = types.map(type => {
11
- return writeFile(`${type}.pb.gz`, profiles[type])
22
+ return writeFile(`${this._pprofPrefix}${type}_${dateStr}.pprof`, profiles[type])
12
23
  })
13
24
 
14
25
  return Promise.all(tasks)
@@ -2,6 +2,7 @@
2
2
 
3
3
  const { EventEmitter } = require('events')
4
4
  const { Config } = require('./config')
5
+ const { snapshotKinds } = require('./constants')
5
6
 
6
7
  function maybeSourceMap (sourceMap) {
7
8
  if (!sourceMap) return
@@ -49,22 +50,42 @@ class Profiler extends EventEmitter {
49
50
  try {
50
51
  for (const profiler of config.profilers) {
51
52
  // TODO: move this out of Profiler when restoring sourcemap support
52
- profiler.start({ mapper })
53
+ profiler.start({
54
+ mapper,
55
+ nearOOMCallback: this._nearOOMExport.bind(this)
56
+ })
53
57
  this._logger.debug(`Started ${profiler.type} profiler`)
54
58
  }
55
59
 
56
60
  this._capture(this._timeoutInterval)
57
61
  } catch (e) {
58
62
  this._logger.error(e)
59
- this.stop()
63
+ this._stop()
60
64
  }
61
65
  }
62
66
 
67
+ _nearOOMExport (profileType, encodedProfile) {
68
+ const start = this._lastStart
69
+ const end = new Date()
70
+ this._submit({
71
+ [profileType]: encodedProfile
72
+ }, start, end, snapshotKinds.ON_OUT_OF_MEMORY)
73
+ }
74
+
63
75
  _setInterval () {
64
76
  this._timeoutInterval = this._config.flushInterval
65
77
  }
66
78
 
67
- stop () {
79
+ async stop () {
80
+ if (!this._enabled) return
81
+
82
+ // collect and export current profiles
83
+ // once collect returns, profilers can be safely stopped
84
+ this._collect(snapshotKinds.ON_SHUTDOWN)
85
+ this._stop()
86
+ }
87
+
88
+ _stop () {
68
89
  if (!this._enabled) return
69
90
 
70
91
  this._enabled = false
@@ -84,24 +105,32 @@ class Profiler extends EventEmitter {
84
105
  if (!this._enabled) return
85
106
  this._lastStart = new Date()
86
107
  if (!this._timer || timeout !== this._timeoutInterval) {
87
- this._timer = setTimeout(() => this._collect(), timeout)
108
+ this._timer = setTimeout(() => this._collect(snapshotKinds.PERIODIC), timeout)
88
109
  this._timer.unref()
89
110
  } else {
90
111
  this._timer.refresh()
91
112
  }
92
113
  }
93
114
 
94
- async _collect () {
115
+ async _collect (snapshotKind) {
116
+ if (!this._enabled) return
117
+
95
118
  const start = this._lastStart
96
119
  const end = new Date()
97
- const profiles = {}
120
+ const profiles = []
121
+ const encodedProfiles = {}
98
122
 
99
123
  try {
124
+ // collect profiles synchronously so that profilers can be safely stopped asynchronously
100
125
  for (const profiler of this._config.profilers) {
101
126
  const profile = profiler.profile()
102
127
  if (!profile) continue
128
+ profiles.push({ profiler, profile })
129
+ }
103
130
 
104
- profiles[profiler.type] = await profiler.encode(profile)
131
+ // encode and export asynchronously
132
+ for (const { profiler, profile } of profiles) {
133
+ encodedProfiles[profiler.type] = await profiler.encode(profile)
105
134
  this._logger.debug(() => {
106
135
  const profileJson = JSON.stringify(profile, (key, value) => {
107
136
  return typeof value === 'bigint' ? value.toString() : value
@@ -111,21 +140,22 @@ class Profiler extends EventEmitter {
111
140
  }
112
141
 
113
142
  this._capture(this._timeoutInterval)
114
- await this._submit(profiles, start, end)
143
+ await this._submit(encodedProfiles, start, end, snapshotKind)
115
144
  this._logger.debug('Submitted profiles')
116
145
  } catch (err) {
117
146
  this._logger.error(err)
118
- this.stop()
147
+ this._stop()
119
148
  }
120
149
  }
121
150
 
122
- _submit (profiles, start, end) {
151
+ _submit (profiles, start, end, snapshotKind) {
123
152
  if (!Object.keys(profiles).length) {
124
153
  return Promise.reject(new Error('No profiles to submit'))
125
154
  }
126
155
  const { tags } = this._config
127
156
  const tasks = []
128
157
 
158
+ tags.snapshot = snapshotKind
129
159
  for (const exporter of this._config.exporters) {
130
160
  const task = exporter.export({ profiles, start, end, tags })
131
161
  .catch(err => this._logger.error(err))
@@ -150,10 +180,10 @@ class ServerlessProfiler extends Profiler {
150
180
  this._flushAfterIntervals = this._config.flushInterval / 1000
151
181
  }
152
182
 
153
- async _collect () {
183
+ async _collect (snapshotKind) {
154
184
  if (this._profiledIntervals >= this._flushAfterIntervals) {
155
185
  this._profiledIntervals = 0
156
- await super._collect()
186
+ await super._collect(snapshotKind)
157
187
  } else {
158
188
  this._profiledIntervals += 1
159
189
  this._capture(this._timeoutInterval)
@@ -1,17 +1,37 @@
1
1
  'use strict'
2
2
 
3
+ const { oomExportStrategies } = require('../constants')
4
+
5
+ function strategiesToCallbackMode (strategies, callbackMode) {
6
+ const hasInterrupt = strategies.includes(oomExportStrategies.INTERRUPT_CALLBACK) ? callbackMode.Interrupt : 0
7
+ const hasCallback = strategies.includes(oomExportStrategies.ASYNC_CALLBACK) ? callbackMode.Async : 0
8
+ return hasInterrupt | hasCallback
9
+ }
10
+
3
11
  class NativeSpaceProfiler {
4
12
  constructor (options = {}) {
5
13
  this.type = 'space'
6
14
  this._samplingInterval = options.samplingInterval || 512 * 1024
7
15
  this._stackDepth = options.stackDepth || 64
8
16
  this._pprof = undefined
17
+ this._oomMonitoring = options.oomMonitoring || {}
9
18
  }
10
19
 
11
- start ({ mapper } = {}) {
20
+ start ({ mapper, nearOOMCallback } = {}) {
12
21
  this._mapper = mapper
13
22
  this._pprof = require('@datadog/pprof')
14
23
  this._pprof.heap.start(this._samplingInterval, this._stackDepth)
24
+ if (this._oomMonitoring.enabled) {
25
+ const strategies = this._oomMonitoring.exportStrategies
26
+ this._pprof.heap.monitorOutOfMemory(
27
+ this._oomMonitoring.heapLimitExtensionSize,
28
+ this._oomMonitoring.maxHeapExtensionCount,
29
+ strategies.includes(oomExportStrategies.LOGS),
30
+ strategies.includes(oomExportStrategies.PROCESS) ? this._oomMonitoring.exportCommand : [],
31
+ (profile) => nearOOMCallback(this.type, this._pprof.encodeSync(profile)),
32
+ strategiesToCallbackMode(strategies, this._pprof.heap.CallbackMode)
33
+ )
34
+ }
15
35
  }
16
36
 
17
37
  profile () {
@@ -35,6 +35,7 @@ class NativeWallProfiler {
35
35
  stop () {
36
36
  if (!this._stop) return
37
37
  this._stop()
38
+ this._stop = undefined
38
39
  }
39
40
 
40
41
  _record () {
@@ -51,7 +51,7 @@ class Tracer extends NoopProxy {
51
51
  }
52
52
 
53
53
  this._tracer = new DatadogTracer(config)
54
- this.appsec = new AppsecSdk(this._tracer)
54
+ this.appsec = new AppsecSdk(this._tracer, config)
55
55
 
56
56
  if (config.iast.enabled) {
57
57
  require('./appsec/iast').enable(config, this._tracer)
@@ -17,7 +17,7 @@ class SpanProcessor {
17
17
  this._killAll = false
18
18
 
19
19
  this._stats = new SpanStatsProcessor(config)
20
- this._spanSampler = new SpanSampler(config)
20
+ this._spanSampler = new SpanSampler(config.sampler)
21
21
  }
22
22
 
23
23
  process (span) {
@@ -2,76 +2,93 @@
2
2
  const { globMatch } = require('../src/util')
3
3
  const { USER_KEEP, AUTO_KEEP } = require('../../../ext').priority
4
4
  const RateLimiter = require('./rate_limiter')
5
+ const Sampler = require('./sampler')
5
6
 
6
- class SpanSampler {
7
- constructor ({ spanSamplingRules = [] }) {
8
- this._rules = spanSamplingRules
9
- this._limiters = {}
10
- }
7
+ class SpanSamplingRule {
8
+ constructor ({ service, name, sampleRate = 1.0, maxPerSecond } = {}) {
9
+ this.service = service
10
+ this.name = name
11
11
 
12
- sample (spanContext) {
13
- const decision = spanContext._sampling.priority
14
- if (decision === USER_KEEP || decision === AUTO_KEEP) return
12
+ this._sampler = new Sampler(sampleRate)
13
+ this._limiter = undefined
15
14
 
16
- const { started } = spanContext._trace
17
- for (const span of started) {
18
- const service = span.tracer()._service
19
- const name = span._name
20
- const rule = findRule(this._rules, service, name)
21
- if (!rule) continue
22
-
23
- const sampleRate = getSampleRate(rule.sampleRate)
24
- const maxPerSecond = getMaxPerSecond(rule.maxPerSecond)
25
- const sampled = sample(sampleRate)
26
- if (!sampled) continue
27
-
28
- const key = `${service}:${name}`
29
- const limiter = getLimiter(this._limiters, key, maxPerSecond)
30
- if (limiter.isAllowed()) {
31
- span.context()._sampling.spanSampling = {
32
- sampleRate,
33
- maxPerSecond
34
- }
35
- }
15
+ if (Number.isFinite(maxPerSecond)) {
16
+ this._limiter = new RateLimiter(maxPerSecond)
36
17
  }
37
18
  }
38
- }
39
19
 
40
- function findRule (rules, service, name) {
41
- for (const rule of rules) {
42
- const servicePattern = getService(rule.service)
43
- const namePattern = getName(rule.name)
44
- if (globMatch(servicePattern, service) && globMatch(namePattern, name)) {
45
- return rule
20
+ get sampleRate () {
21
+ return this._sampler.rate()
22
+ }
23
+
24
+ get maxPerSecond () {
25
+ return this._limiter && this._limiter._rateLimit
26
+ }
27
+
28
+ static from (config) {
29
+ return new SpanSamplingRule(config)
30
+ }
31
+
32
+ match (service, name) {
33
+ if (this.service && !globMatch(this.service, service)) {
34
+ return false
46
35
  }
36
+
37
+ if (this.name && !globMatch(this.name, name)) {
38
+ return false
39
+ }
40
+
41
+ return true
47
42
  }
48
- }
49
43
 
50
- function getLimiter (list, key, maxPerSecond) {
51
- if (typeof list[key] === 'undefined') {
52
- list[key] = new RateLimiter(maxPerSecond)
44
+ sample () {
45
+ if (!this._sampler.isSampled()) {
46
+ return false
47
+ }
48
+
49
+ if (this._limiter) {
50
+ return this._limiter.isAllowed()
51
+ }
52
+
53
+ return true
53
54
  }
54
- return list[key]
55
55
  }
56
56
 
57
- function sample (sampleRate) {
58
- return Math.random() < sampleRate
59
- }
57
+ class SpanSampler {
58
+ constructor ({ spanSamplingRules = [] } = {}) {
59
+ this._rules = spanSamplingRules.map(SpanSamplingRule.from)
60
+ }
60
61
 
61
- function getService (service) {
62
- return service || '*'
63
- }
62
+ findRule (service, name) {
63
+ for (const rule of this._rules) {
64
+ if (rule.match(service, name)) {
65
+ return rule
66
+ }
67
+ }
68
+ }
64
69
 
65
- function getName (name) {
66
- return name || '*'
67
- }
70
+ sample (spanContext) {
71
+ const decision = spanContext._sampling.priority
72
+ if (decision === USER_KEEP || decision === AUTO_KEEP) return
68
73
 
69
- function getSampleRate (sampleRate) {
70
- return sampleRate || 1.0
71
- }
74
+ const { started } = spanContext._trace
75
+ for (const span of started) {
76
+ const context = span.context()
77
+ const tags = context._tags || {}
78
+ const name = context._name
79
+ const service = tags.service ||
80
+ tags['service.name'] ||
81
+ span.tracer()._service
72
82
 
73
- function getMaxPerSecond (maxPerSecond) {
74
- return maxPerSecond || Infinity
83
+ const rule = this.findRule(service, name)
84
+ if (rule && rule.sample()) {
85
+ span.context()._sampling.spanSampling = {
86
+ sampleRate: rule.sampleRate,
87
+ maxPerSecond: rule.maxPerSecond
88
+ }
89
+ }
90
+ }
91
+ }
75
92
  }
76
93
 
77
94
  module.exports = SpanSampler
@@ -1,14 +1,11 @@
1
1
  'use strict'
2
2
 
3
- const mainLogger = require('./log')
3
+ const { info, warn } = require('./log/writer')
4
4
 
5
5
  const os = require('os')
6
6
  const { inspect } = require('util')
7
7
  const tracerVersion = require('../../../package.json').version
8
8
 
9
- const logger = Object.create(mainLogger)
10
- logger.toggle(true)
11
-
12
9
  let config
13
10
  let pluginManager
14
11
  let samplingRules = []
@@ -89,9 +86,9 @@ function startupLog ({ agentError } = {}) {
89
86
  // out.service_mapping
90
87
  // out.service_mapping_error
91
88
 
92
- logger.info('DATADOG TRACER CONFIGURATION - ' + out)
89
+ info('DATADOG TRACER CONFIGURATION - ' + out)
93
90
  if (agentError) {
94
- logger.warn('DATADOG TRACER DIAGNOSTIC - Agent Error: ' + agentError.message)
91
+ warn('DATADOG TRACER DIAGNOSTIC - Agent Error: ' + agentError.message)
95
92
  }
96
93
 
97
94
  config = undefined