dd-trace 3.3.1 → 3.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 (198) hide show
  1. package/LICENSE-3rdparty.csv +8 -0
  2. package/README.md +108 -43
  3. package/ci/init.js +6 -1
  4. package/ext/exporters.d.ts +2 -1
  5. package/ext/exporters.js +2 -1
  6. package/index.d.ts +129 -36
  7. package/package.json +18 -9
  8. package/packages/datadog-instrumentations/src/body-parser.js +26 -0
  9. package/packages/datadog-instrumentations/src/cassandra-driver.js +7 -7
  10. package/packages/datadog-instrumentations/src/child-process.js +30 -0
  11. package/packages/datadog-instrumentations/src/connect.js +15 -15
  12. package/packages/datadog-instrumentations/src/cucumber.js +1 -3
  13. package/packages/datadog-instrumentations/src/elasticsearch.js +51 -47
  14. package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +1 -1
  15. package/packages/datadog-instrumentations/src/helpers/hooks.js +9 -0
  16. package/packages/datadog-instrumentations/src/helpers/register.js +5 -0
  17. package/packages/datadog-instrumentations/src/http/server.js +20 -12
  18. package/packages/datadog-instrumentations/src/http2/server.js +67 -1
  19. package/packages/datadog-instrumentations/src/jest.js +182 -25
  20. package/packages/datadog-instrumentations/src/koa.js +32 -32
  21. package/packages/datadog-instrumentations/src/ldapjs.js +91 -0
  22. package/packages/datadog-instrumentations/src/mariadb.js +63 -0
  23. package/packages/datadog-instrumentations/src/memcached.js +1 -4
  24. package/packages/datadog-instrumentations/src/mocha.js +135 -24
  25. package/packages/datadog-instrumentations/src/next.js +10 -2
  26. package/packages/datadog-instrumentations/src/opensearch.js +10 -0
  27. package/packages/datadog-instrumentations/src/oracledb.js +8 -8
  28. package/packages/datadog-instrumentations/src/pg.js +7 -3
  29. package/packages/datadog-instrumentations/src/qs.js +24 -0
  30. package/packages/datadog-instrumentations/src/redis.js +12 -3
  31. package/packages/datadog-instrumentations/src/restify.js +5 -1
  32. package/packages/datadog-instrumentations/src/rhea.js +29 -15
  33. package/packages/datadog-instrumentations/src/router.js +23 -23
  34. package/packages/datadog-plugin-amqp10/src/consumer.js +32 -0
  35. package/packages/datadog-plugin-amqp10/src/index.js +11 -101
  36. package/packages/datadog-plugin-amqp10/src/producer.js +34 -0
  37. package/packages/datadog-plugin-amqp10/src/util.js +15 -0
  38. package/packages/datadog-plugin-amqplib/src/client.js +38 -0
  39. package/packages/datadog-plugin-amqplib/src/consumer.js +40 -0
  40. package/packages/datadog-plugin-amqplib/src/index.js +14 -102
  41. package/packages/datadog-plugin-amqplib/src/producer.js +37 -0
  42. package/packages/datadog-plugin-amqplib/src/util.js +14 -0
  43. package/packages/datadog-plugin-cassandra-driver/src/index.js +22 -60
  44. package/packages/datadog-plugin-cucumber/src/index.js +14 -33
  45. package/packages/datadog-plugin-cypress/src/plugin.js +2 -1
  46. package/packages/datadog-plugin-dns/src/index.js +16 -91
  47. package/packages/datadog-plugin-dns/src/lookup.js +40 -0
  48. package/packages/datadog-plugin-dns/src/lookup_service.js +24 -0
  49. package/packages/datadog-plugin-dns/src/resolve.js +24 -0
  50. package/packages/datadog-plugin-dns/src/reverse.js +21 -0
  51. package/packages/datadog-plugin-elasticsearch/src/index.js +24 -60
  52. package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +24 -0
  53. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +41 -0
  54. package/packages/datadog-plugin-google-cloud-pubsub/src/index.js +14 -99
  55. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +33 -0
  56. package/packages/datadog-plugin-graphql/src/execute.js +73 -0
  57. package/packages/datadog-plugin-graphql/src/index.js +14 -176
  58. package/packages/datadog-plugin-graphql/src/parse.js +32 -0
  59. package/packages/datadog-plugin-graphql/src/resolve.js +70 -76
  60. package/packages/datadog-plugin-graphql/src/validate.js +28 -0
  61. package/packages/datadog-plugin-grpc/src/client.js +46 -55
  62. package/packages/datadog-plugin-grpc/src/index.js +7 -24
  63. package/packages/datadog-plugin-grpc/src/server.js +50 -52
  64. package/packages/datadog-plugin-grpc/src/util.js +15 -14
  65. package/packages/datadog-plugin-http/src/client.js +15 -5
  66. package/packages/datadog-plugin-http/src/index.js +7 -22
  67. package/packages/datadog-plugin-http/src/server.js +19 -3
  68. package/packages/datadog-plugin-http2/src/client.js +20 -1
  69. package/packages/datadog-plugin-http2/src/index.js +8 -26
  70. package/packages/datadog-plugin-http2/src/server.js +44 -0
  71. package/packages/datadog-plugin-jest/src/index.js +41 -65
  72. package/packages/datadog-plugin-kafkajs/src/consumer.js +42 -0
  73. package/packages/datadog-plugin-kafkajs/src/index.js +11 -87
  74. package/packages/datadog-plugin-kafkajs/src/producer.js +31 -0
  75. package/packages/datadog-plugin-mariadb/src/index.js +10 -0
  76. package/packages/datadog-plugin-memcached/src/index.js +17 -52
  77. package/packages/datadog-plugin-mocha/src/index.js +40 -60
  78. package/packages/datadog-plugin-moleculer/src/client.js +22 -36
  79. package/packages/datadog-plugin-moleculer/src/index.js +8 -26
  80. package/packages/datadog-plugin-moleculer/src/server.js +18 -30
  81. package/packages/datadog-plugin-mongodb-core/src/index.js +23 -51
  82. package/packages/datadog-plugin-mysql/src/index.js +23 -52
  83. package/packages/datadog-plugin-mysql2/src/index.js +1 -3
  84. package/packages/datadog-plugin-net/src/index.js +4 -0
  85. package/packages/datadog-plugin-net/src/ipc.js +21 -0
  86. package/packages/datadog-plugin-net/src/tcp.js +46 -0
  87. package/packages/datadog-plugin-next/src/index.js +3 -0
  88. package/packages/datadog-plugin-opensearch/src/index.js +11 -0
  89. package/packages/datadog-plugin-oracledb/src/index.js +29 -55
  90. package/packages/datadog-plugin-pg/src/index.js +27 -51
  91. package/packages/datadog-plugin-redis/src/index.js +29 -60
  92. package/packages/datadog-plugin-rhea/src/consumer.js +55 -0
  93. package/packages/datadog-plugin-rhea/src/index.js +11 -96
  94. package/packages/datadog-plugin-rhea/src/producer.js +45 -0
  95. package/packages/datadog-plugin-router/src/index.js +13 -2
  96. package/packages/datadog-plugin-sharedb/src/index.js +22 -39
  97. package/packages/datadog-plugin-tedious/src/index.js +20 -41
  98. package/packages/dd-trace/src/appsec/addresses.js +3 -1
  99. package/packages/dd-trace/src/appsec/blocking.js +44 -0
  100. package/packages/dd-trace/src/appsec/callbacks/ddwaf.js +8 -6
  101. package/packages/dd-trace/src/appsec/gateway/engine/engine.js +1 -1
  102. package/packages/dd-trace/src/appsec/gateway/engine/index.js +7 -2
  103. package/packages/dd-trace/src/appsec/gateway/engine/runner.js +0 -1
  104. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +4 -1
  105. package/packages/dd-trace/src/appsec/iast/analyzers/command-injection-analyzer.js +11 -0
  106. package/packages/dd-trace/src/appsec/iast/analyzers/injection-analyzer.js +19 -0
  107. package/packages/dd-trace/src/appsec/iast/analyzers/ldap-injection-analyzer.js +11 -0
  108. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +13 -0
  109. package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +43 -5
  110. package/packages/dd-trace/src/appsec/iast/iast-context.js +3 -1
  111. package/packages/dd-trace/src/appsec/iast/index.js +24 -8
  112. package/packages/dd-trace/src/appsec/iast/overhead-controller.js +20 -1
  113. package/packages/dd-trace/src/appsec/iast/path-line.js +17 -6
  114. package/packages/dd-trace/src/appsec/iast/taint-tracking/csi-methods.js +17 -0
  115. package/packages/dd-trace/src/appsec/iast/taint-tracking/filter.js +16 -0
  116. package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +18 -0
  117. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +98 -0
  118. package/packages/dd-trace/src/appsec/iast/taint-tracking/origin-types.js +4 -0
  119. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +38 -0
  120. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +67 -0
  121. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +103 -0
  122. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +98 -30
  123. package/packages/dd-trace/src/appsec/index.js +79 -25
  124. package/packages/dd-trace/src/{plugins/util → appsec}/ip_blocklist.js +0 -0
  125. package/packages/dd-trace/src/appsec/ip_extractor.js +98 -0
  126. package/packages/dd-trace/src/appsec/recommended.json +134 -53
  127. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +7 -0
  128. package/packages/dd-trace/src/appsec/remote_config/index.js +56 -0
  129. package/packages/dd-trace/src/appsec/remote_config/manager.js +264 -0
  130. package/packages/dd-trace/src/{exporters → appsec/remote_config}/scheduler.js +9 -9
  131. package/packages/dd-trace/src/appsec/rule_manager.js +61 -1
  132. package/packages/dd-trace/src/appsec/templates/blocked.html +99 -0
  133. package/packages/dd-trace/src/appsec/templates/blocked.json +8 -0
  134. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +66 -0
  135. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +9 -5
  136. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +19 -51
  137. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +10 -5
  138. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +202 -0
  139. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +51 -62
  140. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +89 -0
  141. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +82 -0
  142. package/packages/dd-trace/src/config.js +119 -35
  143. package/packages/dd-trace/src/constants.js +9 -1
  144. package/packages/dd-trace/src/dogstatsd.js +42 -10
  145. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +10 -2
  146. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +0 -1
  147. package/packages/dd-trace/src/exporter.js +3 -0
  148. package/packages/dd-trace/src/exporters/agent/index.js +10 -2
  149. package/packages/dd-trace/src/exporters/agent/writer.js +2 -9
  150. package/packages/dd-trace/src/exporters/common/agent-info-exporter.js +82 -0
  151. package/packages/dd-trace/src/exporters/common/request.js +51 -1
  152. package/packages/dd-trace/src/exporters/span-stats/index.js +6 -2
  153. package/packages/dd-trace/src/format.js +29 -10
  154. package/packages/dd-trace/src/lambda/handler.js +72 -0
  155. package/packages/dd-trace/src/lambda/index.js +5 -0
  156. package/packages/dd-trace/src/lambda/runtime/errors.js +20 -0
  157. package/packages/dd-trace/src/lambda/runtime/patch.js +74 -0
  158. package/packages/dd-trace/src/lambda/runtime/ritm.js +138 -0
  159. package/packages/dd-trace/src/metrics.js +15 -2
  160. package/packages/dd-trace/src/opentracing/propagation/text_map.js +1 -5
  161. package/packages/dd-trace/src/opentracing/span.js +2 -1
  162. package/packages/dd-trace/src/opentracing/span_context.js +9 -0
  163. package/packages/dd-trace/src/plugin_manager.js +11 -17
  164. package/packages/dd-trace/src/plugins/cache.js +9 -0
  165. package/packages/dd-trace/src/plugins/ci_plugin.js +97 -0
  166. package/packages/dd-trace/src/plugins/client.js +9 -0
  167. package/packages/dd-trace/src/plugins/composite.js +26 -0
  168. package/packages/dd-trace/src/plugins/consumer.js +9 -0
  169. package/packages/dd-trace/src/plugins/database.js +55 -0
  170. package/packages/dd-trace/src/plugins/incoming.js +7 -0
  171. package/packages/dd-trace/src/plugins/index.js +3 -0
  172. package/packages/dd-trace/src/plugins/log_plugin.js +2 -2
  173. package/packages/dd-trace/src/plugins/outgoing.js +31 -0
  174. package/packages/dd-trace/src/plugins/plugin.js +3 -0
  175. package/packages/dd-trace/src/plugins/producer.js +9 -0
  176. package/packages/dd-trace/src/plugins/server.js +9 -0
  177. package/packages/dd-trace/src/plugins/storage.js +21 -0
  178. package/packages/dd-trace/src/plugins/tracing.js +94 -0
  179. package/packages/dd-trace/src/plugins/util/ci.js +40 -4
  180. package/packages/dd-trace/src/plugins/util/git.js +58 -18
  181. package/packages/dd-trace/src/plugins/util/test.js +71 -8
  182. package/packages/dd-trace/src/plugins/util/user-provided-git.js +14 -1
  183. package/packages/dd-trace/src/plugins/util/web.js +11 -114
  184. package/packages/dd-trace/src/priority_sampler.js +6 -2
  185. package/packages/dd-trace/src/profiling/config.js +11 -6
  186. package/packages/dd-trace/src/profiling/exporters/agent.js +4 -0
  187. package/packages/dd-trace/src/profiling/index.js +2 -2
  188. package/packages/dd-trace/src/profiling/profiler.js +43 -7
  189. package/packages/dd-trace/src/proxy.js +8 -16
  190. package/packages/dd-trace/src/ritm.js +25 -14
  191. package/packages/dd-trace/src/span_processor.js +17 -0
  192. package/packages/dd-trace/src/span_sampler.js +77 -0
  193. package/packages/dd-trace/src/span_stats.js +2 -2
  194. package/packages/dd-trace/src/telemetry/dependencies.js +21 -7
  195. package/packages/dd-trace/src/telemetry/index.js +7 -1
  196. package/packages/dd-trace/src/telemetry/send-data.js +3 -1
  197. package/packages/dd-trace/src/tracer.js +10 -5
  198. package/packages/dd-trace/src/util.js +43 -1
@@ -2,7 +2,7 @@
2
2
 
3
3
  const coalesce = require('koalas')
4
4
  const os = require('os')
5
- const { URL } = require('url')
5
+ const { URL, format } = require('url')
6
6
  const { AgentExporter } = require('./exporters/agent')
7
7
  const { FileExporter } = require('./exporters/file')
8
8
  const { ConsoleLogger } = require('./loggers/console')
@@ -33,6 +33,7 @@ class Config {
33
33
  const service = options.service || DD_SERVICE || 'node'
34
34
  const host = os.hostname()
35
35
  const version = coalesce(options.version, DD_VERSION)
36
+ const functionname = process.env.AWS_LAMBDA_FUNCTION_NAME
36
37
  // Must be longer than one minute so pad with five seconds
37
38
  const flushInterval = coalesce(options.interval, 65 * 1000)
38
39
  const uploadTimeout = coalesce(options.uploadTimeout,
@@ -46,12 +47,13 @@ class Config {
46
47
  this.service = service
47
48
  this.env = env
48
49
  this.host = host
50
+ this.functionname = functionname
49
51
 
50
52
  this.version = version
51
53
  this.tags = Object.assign(
52
54
  tagger.parse(DD_TAGS),
53
55
  tagger.parse(options.tags),
54
- tagger.parse({ env, host, service, version })
56
+ tagger.parse({ env, host, service, version, functionname })
55
57
  )
56
58
  this.logger = ensureLogger(options.logger)
57
59
  this.flushInterval = flushInterval
@@ -59,10 +61,13 @@ class Config {
59
61
  this.sourceMap = sourceMap
60
62
  this.endpointCollection = endpointCollection
61
63
 
62
- const hostname = coalesce(options.hostname, DD_AGENT_HOST, 'localhost')
63
- const port = coalesce(options.port, DD_TRACE_AGENT_PORT, 8126)
64
- this.url = new URL(coalesce(options.url, DD_TRACE_AGENT_URL,
65
- `http://${hostname || 'localhost'}:${port || 8126}`))
64
+ const hostname = coalesce(options.hostname, DD_AGENT_HOST) || 'localhost'
65
+ const port = coalesce(options.port, DD_TRACE_AGENT_PORT) || 8126
66
+ this.url = new URL(coalesce(options.url, DD_TRACE_AGENT_URL, format({
67
+ protocol: 'http:',
68
+ hostname,
69
+ port
70
+ })))
66
71
 
67
72
  this.exporters = ensureExporters(options.exporters || [
68
73
  new AgentExporter(this)
@@ -6,11 +6,14 @@ const { request } = require('http')
6
6
  // TODO: avoid using dd-trace internals. Make this a separate module?
7
7
  const docker = require('../../exporters/common/docker')
8
8
  const FormData = require('../../exporters/common/form-data')
9
+ const { storage } = require('../../../../datadog-core')
9
10
  const version = require('../../../../../package.json').version
10
11
 
11
12
  const containerId = docker.id()
12
13
 
13
14
  function sendRequest (options, form, callback) {
15
+ const store = storage.getStore()
16
+ storage.enterWith({ noop: true })
14
17
  const req = request(options, res => {
15
18
  if (res.statusCode >= 400) {
16
19
  const error = new Error(`HTTP Error ${res.statusCode}`)
@@ -22,6 +25,7 @@ function sendRequest (options, form, callback) {
22
25
  })
23
26
  req.on('error', callback)
24
27
  if (form) form.pipe(req)
28
+ storage.enterWith(store)
25
29
  }
26
30
 
27
31
  function getBody (stream, callback) {
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { Profiler } = require('./profiler')
3
+ const { Profiler, ServerlessProfiler } = require('./profiler')
4
4
  const CpuProfiler = require('./profilers/cpu')
5
5
  const WallProfiler = require('./profilers/wall')
6
6
  const SpaceProfiler = require('./profilers/space')
@@ -8,7 +8,7 @@ const { AgentExporter } = require('./exporters/agent')
8
8
  const { FileExporter } = require('./exporters/file')
9
9
  const { ConsoleLogger } = require('./loggers/console')
10
10
 
11
- const profiler = new Profiler()
11
+ const profiler = process.env.AWS_LAMBDA_FUNCTION_NAME ? new ServerlessProfiler() : new Profiler()
12
12
 
13
13
  module.exports = {
14
14
  profiler,
@@ -5,7 +5,6 @@ const { Config } = require('./config')
5
5
 
6
6
  function maybeSourceMap (sourceMap) {
7
7
  if (!sourceMap) return
8
-
9
8
  const { SourceMapper } = require('@datadog/pprof')
10
9
  return SourceMapper.create([
11
10
  process.cwd()
@@ -20,6 +19,7 @@ class Profiler extends EventEmitter {
20
19
  this._config = undefined
21
20
  this._timer = undefined
22
21
  this._lastStart = undefined
22
+ this._timeoutInterval = undefined
23
23
  }
24
24
 
25
25
  start (options) {
@@ -35,23 +35,35 @@ class Profiler extends EventEmitter {
35
35
 
36
36
  this._logger = config.logger
37
37
  this._enabled = true
38
+ this._setInterval()
38
39
 
40
+ // Log errors if the source map finder fails, but don't prevent the rest
41
+ // of the profiler from running without source maps.
42
+ let mapper
39
43
  try {
40
- const mapper = await maybeSourceMap(config.sourceMap)
44
+ mapper = await maybeSourceMap(config.sourceMap)
45
+ } catch (err) {
46
+ this._logger.error(err)
47
+ }
41
48
 
49
+ try {
42
50
  for (const profiler of config.profilers) {
43
51
  // TODO: move this out of Profiler when restoring sourcemap support
44
52
  profiler.start({ mapper })
45
53
  this._logger.debug(`Started ${profiler.type} profiler`)
46
54
  }
47
55
 
48
- this._capture(config.flushInterval)
56
+ this._capture(this._timeoutInterval)
49
57
  } catch (e) {
50
58
  this._logger.error(e)
51
59
  this.stop()
52
60
  }
53
61
  }
54
62
 
63
+ _setInterval () {
64
+ this._timeoutInterval = this._config.flushInterval
65
+ }
66
+
55
67
  stop () {
56
68
  if (!this._enabled) return
57
69
 
@@ -71,8 +83,7 @@ class Profiler extends EventEmitter {
71
83
  _capture (timeout) {
72
84
  if (!this._enabled) return
73
85
  this._lastStart = new Date()
74
-
75
- if (!this._timer || timeout !== this._config.flushInterval) {
86
+ if (!this._timer || timeout !== this._timeoutInterval) {
76
87
  this._timer = setTimeout(() => this._collect(), timeout)
77
88
  this._timer.unref()
78
89
  } else {
@@ -99,7 +110,7 @@ class Profiler extends EventEmitter {
99
110
  })
100
111
  }
101
112
 
102
- this._capture(this._config.flushInterval)
113
+ this._capture(this._timeoutInterval)
103
114
  await this._submit(profiles, start, end)
104
115
  this._logger.debug('Submitted profiles')
105
116
  } catch (err) {
@@ -126,4 +137,29 @@ class Profiler extends EventEmitter {
126
137
  }
127
138
  }
128
139
 
129
- module.exports = { Profiler }
140
+ class ServerlessProfiler extends Profiler {
141
+ constructor () {
142
+ super()
143
+ this._profiledIntervals = 0
144
+ this._interval = 1
145
+ this._flushAfterIntervals = undefined
146
+ }
147
+
148
+ _setInterval () {
149
+ this._timeoutInterval = this._interval * 1000
150
+ this._flushAfterIntervals = this._config.flushInterval / 1000
151
+ }
152
+
153
+ async _collect () {
154
+ if (this._profiledIntervals >= this._flushAfterIntervals) {
155
+ this._profiledIntervals = 0
156
+ await super._collect()
157
+ } else {
158
+ this._profiledIntervals += 1
159
+ this._capture(this._timeoutInterval)
160
+ // Don't submit profile until 65 (flushAfterIntervals) intervals have elapsed
161
+ }
162
+ }
163
+ }
164
+
165
+ module.exports = { Profiler, ServerlessProfiler }
@@ -1,5 +1,4 @@
1
1
  'use strict'
2
-
3
2
  const NoopProxy = require('./noop/proxy')
4
3
  const DatadogTracer = require('./tracer')
5
4
  const Config = require('./config')
@@ -8,7 +7,7 @@ const log = require('./log')
8
7
  const { setStartupLogPluginManager } = require('./startup-log')
9
8
  const telemetry = require('./telemetry')
10
9
  const PluginManager = require('./plugin_manager')
11
- const { sendGitMetadata } = require('./ci-visibility/exporters/git/git_metadata')
10
+ const remoteConfig = require('./appsec/remote_config')
12
11
 
13
12
  class Tracer extends NoopProxy {
14
13
  constructor () {
@@ -26,8 +25,9 @@ class Tracer extends NoopProxy {
26
25
  try {
27
26
  const config = new Config(options) // TODO: support dynamic config
28
27
 
29
- log.use(config.logger)
30
- log.toggle(config.debug, config.logLevel, this)
28
+ if (config.remoteConfig.enabled && !config.isCiVisibility) {
29
+ remoteConfig.enable(config)
30
+ }
31
31
 
32
32
  if (config.profiling.enabled) {
33
33
  // do not stop tracer initialization if the profiler fails to be imported
@@ -48,25 +48,17 @@ class Tracer extends NoopProxy {
48
48
  if (config.appsec.enabled) {
49
49
  require('./appsec').enable(config)
50
50
  }
51
+
52
+ this._tracer = new DatadogTracer(config)
53
+
51
54
  if (config.iast.enabled) {
52
- require('./appsec/iast').enable(config)
55
+ require('./appsec/iast').enable(config, this._tracer)
53
56
  }
54
57
 
55
- this._tracer = new DatadogTracer(config)
56
58
  this._pluginManager.configure(config)
57
59
  setStartupLogPluginManager(this._pluginManager)
58
60
  telemetry.start(config, this._pluginManager)
59
61
  }
60
-
61
- if (config.isGitUploadEnabled) {
62
- sendGitMetadata(config.site, (err) => {
63
- if (err) {
64
- log.error(`Error uploading git metadata: ${err}`)
65
- } else {
66
- log.debug('Successfully uploaded git metadata')
67
- }
68
- })
69
- }
70
62
  } catch (e) {
71
63
  log.error(e)
72
64
  }
@@ -16,6 +16,8 @@ let cache = Object.create(null)
16
16
  let patching = Object.create(null)
17
17
  let patchedRequire = null
18
18
  const moduleLoadStartChannel = dc.channel('dd-trace:moduleLoadStart')
19
+ const moduleLoadEndChannel = dc.channel('dd-trace:moduleLoadEnd')
20
+
19
21
  function Hook (modules, options, onrequire) {
20
22
  if (!(this instanceof Hook)) return new Hook(modules, options, onrequire)
21
23
  if (typeof modules === 'function') {
@@ -52,7 +54,6 @@ function Hook (modules, options, onrequire) {
52
54
  const filename = Module._resolveFilename(request, this)
53
55
  const core = filename.indexOf(path.sep) === -1
54
56
  let name, basedir, hooks
55
-
56
57
  // return known patched modules immediately
57
58
  if (cache[filename]) {
58
59
  // require.cache was potentially altered externally
@@ -66,33 +67,43 @@ function Hook (modules, options, onrequire) {
66
67
  // Check if this module has a patcher in-progress already.
67
68
  // Otherwise, mark this module as patching in-progress.
68
69
  const patched = patching[filename]
69
- if (!patched) {
70
+ if (patched) {
71
+ // If it's already patched, just return it as-is.
72
+ return origRequire.apply(this, arguments)
73
+ } else {
70
74
  patching[filename] = true
71
75
  }
72
76
 
73
- const exports = origRequire.apply(this, arguments)
77
+ const payload = {
78
+ filename,
79
+ request
80
+ }
74
81
 
75
- // If it's already patched, just return it as-is.
76
- if (patched) return exports
82
+ if (moduleLoadStartChannel.hasSubscribers) {
83
+ moduleLoadStartChannel.publish(payload)
84
+ }
85
+ const exports = origRequire.apply(this, arguments)
86
+ payload.module = exports
87
+ if (moduleLoadEndChannel.hasSubscribers) {
88
+ moduleLoadEndChannel.publish(payload)
89
+ }
77
90
 
78
91
  // The module has already been loaded,
79
92
  // so the patching mark can be cleaned up.
80
93
  delete patching[filename]
81
94
 
82
- if (moduleLoadStartChannel.hasSubscribers) {
83
- moduleLoadStartChannel.publish({
84
- filename,
85
- module: exports,
86
- request
87
- })
88
- }
89
-
90
95
  if (core) {
91
96
  hooks = moduleHooks[filename]
92
97
  if (!hooks) return exports // abort if module name isn't on whitelist
93
98
  name = filename
94
99
  } else {
95
- const stat = parse(filename)
100
+ const inAWSLambda = process.env.AWS_LAMBDA_FUNCTION_NAME !== undefined
101
+ const hasLambdaHandler = process.env.DD_LAMBDA_HANDLER !== undefined
102
+ const segments = filename.split(path.sep)
103
+ const filenameFromNodeModule = segments.lastIndexOf('node_modules') !== -1
104
+ // decide how to assign the stat
105
+ // first case will only happen when patching an AWS Lambda Handler
106
+ const stat = inAWSLambda && hasLambdaHandler && !filenameFromNodeModule ? { name: filename } : parse(filename)
96
107
  if (!stat) return exports // abort if filename could not be parsed
97
108
  name = stat.name
98
109
  basedir = stat.basedir
@@ -2,6 +2,7 @@
2
2
 
3
3
  const log = require('./log')
4
4
  const format = require('./format')
5
+ const SpanSampler = require('./span_sampler')
5
6
 
6
7
  const { SpanStatsProcessor } = require('./span_stats')
7
8
 
@@ -13,8 +14,10 @@ class SpanProcessor {
13
14
  this._exporter = exporter
14
15
  this._prioritySampler = prioritySampler
15
16
  this._config = config
17
+ this._killAll = false
16
18
 
17
19
  this._stats = new SpanStatsProcessor(config)
20
+ this._spanSampler = new SpanSampler(config)
18
21
  }
19
22
 
20
23
  process (span) {
@@ -25,8 +28,10 @@ class SpanProcessor {
25
28
  const { flushMinSpans } = this._config
26
29
  const { started, finished } = trace
27
30
 
31
+ if (trace.record === false) return
28
32
  if (started.length === finished.length || finished.length >= flushMinSpans) {
29
33
  this._prioritySampler.sample(spanContext)
34
+ this._spanSampler.sample(spanContext)
30
35
 
31
36
  for (const span of started) {
32
37
  if (span._duration !== undefined) {
@@ -44,6 +49,18 @@ class SpanProcessor {
44
49
 
45
50
  this._erase(trace, active)
46
51
  }
52
+
53
+ if (this._killAll) {
54
+ started.map(startedSpan => {
55
+ if (!startedSpan._finished) {
56
+ startedSpan.finish()
57
+ }
58
+ })
59
+ }
60
+ }
61
+
62
+ killAll () {
63
+ this._killAll = true
47
64
  }
48
65
 
49
66
  _erase (trace, active) {
@@ -0,0 +1,77 @@
1
+ 'use strict'
2
+ const { globMatch } = require('../src/util')
3
+ const { USER_KEEP, AUTO_KEEP } = require('../../../ext').priority
4
+ const RateLimiter = require('./rate_limiter')
5
+
6
+ class SpanSampler {
7
+ constructor ({ spanSamplingRules = [] }) {
8
+ this._rules = spanSamplingRules
9
+ this._limiters = {}
10
+ }
11
+
12
+ sample (spanContext) {
13
+ const decision = spanContext._sampling.priority
14
+ if (decision === USER_KEEP || decision === AUTO_KEEP) return
15
+
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
+ }
36
+ }
37
+ }
38
+ }
39
+
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
46
+ }
47
+ }
48
+ }
49
+
50
+ function getLimiter (list, key, maxPerSecond) {
51
+ if (typeof list[key] === 'undefined') {
52
+ list[key] = new RateLimiter(maxPerSecond)
53
+ }
54
+ return list[key]
55
+ }
56
+
57
+ function sample (sampleRate) {
58
+ return Math.random() < sampleRate
59
+ }
60
+
61
+ function getService (service) {
62
+ return service || '*'
63
+ }
64
+
65
+ function getName (name) {
66
+ return name || '*'
67
+ }
68
+
69
+ function getSampleRate (sampleRate) {
70
+ return sampleRate || 1.0
71
+ }
72
+
73
+ function getMaxPerSecond (maxPerSecond) {
74
+ return maxPerSecond || Infinity
75
+ }
76
+
77
+ module.exports = SpanSampler
@@ -65,8 +65,8 @@ class SpanAggStats {
65
65
  TopLevelHits: this.topLevelHits,
66
66
  Errors: this.errors,
67
67
  Duration: this.duration,
68
- OkSummary: this.okDistribution.toProto(),
69
- ErrorSummary: this.errorDistribution.toProto()
68
+ OkSummary: this.okDistribution.toProto(), // TODO: custom proto encoding
69
+ ErrorSummary: this.errorDistribution.toProto() // TODO: custom proto encoding
70
70
  }
71
71
  }
72
72
  }
@@ -7,7 +7,7 @@ const { sendData } = require('./send-data')
7
7
  const dc = require('diagnostics_channel')
8
8
  const { fileURLToPath } = require('url')
9
9
 
10
- const savedDependencies = []
10
+ const savedDependencies = new Set()
11
11
  const detectedDependencyNames = new Set()
12
12
  const FILE_URI_START = `file://`
13
13
  const moduleLoadStartChannel = dc.channel('dd-trace:moduleLoadStart')
@@ -18,10 +18,14 @@ function waitAndSend (config, application, host) {
18
18
  if (!immediate) {
19
19
  immediate = setImmediate(() => {
20
20
  immediate = null
21
- if (savedDependencies.length > 0) {
22
- const dependencies = savedDependencies.splice(0, 1000)
21
+ if (savedDependencies.size > 0) {
22
+ const dependencies = Array.from(savedDependencies.values()).splice(0, 1000).map(pair => {
23
+ savedDependencies.delete(pair)
24
+ const [name, version] = pair.split(' ')
25
+ return { name, version }
26
+ })
23
27
  sendData(config, application, host, 'app-dependencies-loaded', { dependencies })
24
- if (savedDependencies.length > 0) {
28
+ if (savedDependencies.size > 0) {
25
29
  waitAndSend(config, application, host)
26
30
  }
27
31
  }
@@ -49,7 +53,7 @@ function onModuleLoad (data) {
49
53
  if (basedir) {
50
54
  try {
51
55
  const { version } = requirePackageJson(basedir, module)
52
- savedDependencies.push({ name, version })
56
+ savedDependencies.add(`${name} ${version}`)
53
57
  waitAndSend(config, application, host)
54
58
  } catch (e) {
55
59
  // can not read the package.json, do nothing
@@ -67,7 +71,17 @@ function start (_config, _application, _host) {
67
71
  }
68
72
 
69
73
  function isDependency (filename, request) {
70
- return request.indexOf(`.${path.sep}`) !== 0 && request.indexOf(path.sep) !== 0
74
+ const isDependencyWithSlash = isDependencyWithSeparator(filename, request, '/')
75
+ if (isDependencyWithSlash && process.platform === 'win32') {
76
+ return isDependencyWithSeparator(filename, request, path.sep)
77
+ }
78
+ return isDependencyWithSlash
79
+ }
80
+ function isDependencyWithSeparator (filename, request, sep) {
81
+ return request.indexOf(`..${sep}`) !== 0 &&
82
+ request.indexOf(`.${sep}`) !== 0 &&
83
+ request.indexOf(sep) !== 0 &&
84
+ request.indexOf(`:${sep}`) !== 1
71
85
  }
72
86
 
73
87
  function stop () {
@@ -75,7 +89,7 @@ function stop () {
75
89
  application = null
76
90
  host = null
77
91
  detectedDependencyNames.clear()
78
- savedDependencies.splice(0, savedDependencies.length)
92
+ savedDependencies.clear()
79
93
  if (moduleLoadStartChannel.hasSubscribers) {
80
94
  moduleLoadStartChannel.unsubscribe(onModuleLoad)
81
95
  }
@@ -6,6 +6,10 @@ const os = require('os')
6
6
  const dependencies = require('./dependencies')
7
7
  const { sendData } = require('./send-data')
8
8
 
9
+ const HEARTBEAT_INTERVAL = process.env.DD_TELEMETRY_HEARTBEAT_INTERVAL
10
+ ? Number(process.env.DD_TELEMETRY_HEARTBEAT_INTERVAL) * 1000
11
+ : 60000
12
+
9
13
  let config
10
14
  let pluginManager
11
15
 
@@ -88,7 +92,9 @@ function start (aConfig, thePluginManager) {
88
92
  host = createHostObject()
89
93
  dependencies.start(config, application, host)
90
94
  sendData(config, application, host, 'app-started', appStarted())
91
- interval = setInterval(() => sendData(config, application, host, 'app-heartbeat'), 60000)
95
+ interval = setInterval(() => {
96
+ sendData(config, application, host, 'app-heartbeat')
97
+ }, HEARTBEAT_INTERVAL)
92
98
  interval.unref()
93
99
  process.on('beforeExit', onBeforeExit)
94
100
  }
@@ -3,9 +3,11 @@ let seqId = 0
3
3
  function sendData (config, application, host, reqType, payload = {}) {
4
4
  const {
5
5
  hostname,
6
- port
6
+ port,
7
+ url
7
8
  } = config
8
9
  const options = {
10
+ url,
9
11
  hostname,
10
12
  port,
11
13
  method: 'POST',
@@ -6,6 +6,7 @@ const Scope = require('./scope')
6
6
  const { storage } = require('../../datadog-core')
7
7
  const { isError } = require('./util')
8
8
  const { setStartupLogConfig } = require('./startup-log')
9
+ const { ERROR_MESSAGE, ERROR_TYPE, ERROR_STACK } = require('../../dd-trace/src/constants')
9
10
 
10
11
  const SPAN_TYPE = tags.SPAN_TYPE
11
12
  const RESOURCE_NAME = tags.RESOURCE_NAME
@@ -44,11 +45,15 @@ class DatadogTracer extends Tracer {
44
45
  const result = this.scope().activate(span, () => fn(span))
45
46
 
46
47
  if (result && typeof result.then === 'function') {
47
- result.then(
48
- () => span.finish(),
48
+ return result.then(
49
+ value => {
50
+ span.finish()
51
+ return value
52
+ },
49
53
  err => {
50
54
  addError(span, err)
51
55
  span.finish()
56
+ throw err
52
57
  }
53
58
  )
54
59
  } else {
@@ -139,9 +144,9 @@ class DatadogTracer extends Tracer {
139
144
  function addError (span, error) {
140
145
  if (isError(error)) {
141
146
  span.addTags({
142
- 'error.type': error.name,
143
- 'error.msg': error.message,
144
- 'error.stack': error.stack
147
+ [ERROR_TYPE]: error.name,
148
+ [ERROR_MESSAGE]: error.message,
149
+ [ERROR_STACK]: error.stack
145
150
  })
146
151
  }
147
152
  }
@@ -20,8 +20,50 @@ function isError (value) {
20
20
  return false
21
21
  }
22
22
 
23
+ // Matches a glob pattern to a given subject string
24
+ function globMatch (pattern, subject) {
25
+ let px = 0 // [p]attern inde[x]
26
+ let sx = 0 // [s]ubject inde[x]
27
+ let nextPx = 0
28
+ let nextSx = 0
29
+ while (px < pattern.length || sx < subject.length) {
30
+ if (px < pattern.length) {
31
+ const c = pattern[px]
32
+ switch (c) {
33
+ default: // ordinary character
34
+ if (sx < subject.length && subject[sx] === c) {
35
+ px++
36
+ sx++
37
+ continue
38
+ }
39
+ break
40
+ case '?':
41
+ if (sx < subject.length) {
42
+ px++
43
+ sx++
44
+ continue
45
+ }
46
+ break
47
+ case '*':
48
+ nextPx = px
49
+ nextSx = sx + 1
50
+ px++
51
+ continue
52
+ }
53
+ }
54
+ if (nextSx > 0 && nextSx <= subject.length) {
55
+ px = nextPx
56
+ sx = nextSx
57
+ continue
58
+ }
59
+ return false
60
+ }
61
+ return true
62
+ }
63
+
23
64
  module.exports = {
24
65
  isTrue,
25
66
  isFalse,
26
- isError
67
+ isError,
68
+ globMatch
27
69
  }