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
@@ -1,6 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const lookup = require('dns').lookup // cache to avoid instrumentation
4
+ const request = require('./exporters/common/request')
4
5
  const dgram = require('dgram')
5
6
  const isIP = require('net').isIP
6
7
  const log = require('./log')
@@ -11,6 +12,13 @@ class Client {
11
12
  constructor (options) {
12
13
  options = options || {}
13
14
 
15
+ if (options.metricsProxyUrl) {
16
+ this._httpOptions = {
17
+ url: options.metricsProxyUrl.toString(),
18
+ path: '/dogstatsd/v2/proxy'
19
+ }
20
+ }
21
+
14
22
  this._host = options.host || 'localhost'
15
23
  this._family = isIP(this._host)
16
24
  this._port = options.port || 8125
@@ -38,26 +46,50 @@ class Client {
38
46
 
39
47
  this._queue = []
40
48
 
49
+ if (this._httpOptions) {
50
+ this._sendHttp(queue)
51
+ } else {
52
+ this._sendUdp(queue)
53
+ }
54
+ }
55
+
56
+ _sendHttp (queue) {
57
+ const buffer = Buffer.concat(queue)
58
+ request(buffer, this._httpOptions, (err) => {
59
+ if (err) {
60
+ log.error('HTTP error from agent: ' + err.stack)
61
+ if (err.status) {
62
+ // Inside this if-block, we have connectivity to the agent, but
63
+ // we're not getting a 200 from the proxy endpoint. If it's a 404,
64
+ // then we know we'll never have the endpoint, so just clear out the
65
+ // options. Either way, we can give UDP a try.
66
+ if (err.status === 404) {
67
+ this._httpOptions = null
68
+ }
69
+ this._sendUdp(queue)
70
+ }
71
+ }
72
+ })
73
+ }
74
+
75
+ _sendUdp (queue) {
41
76
  if (this._family !== 0) {
42
- this._sendAll(queue, this._host, this._family)
77
+ this._sendUdpFromQueue(queue, this._host, this._family)
43
78
  } else {
44
79
  lookup(this._host, (err, address, family) => {
45
80
  if (err) return log.error(err)
46
- this._sendAll(queue, address, family)
81
+ this._sendUdpFromQueue(queue, address, family)
47
82
  })
48
83
  }
49
84
  }
50
85
 
51
- _send (address, family, buffer) {
86
+ _sendUdpFromQueue (queue, address, family) {
52
87
  const socket = family === 6 ? this._udp6 : this._udp4
53
88
 
54
- log.debug(`Sending to DogStatsD: ${buffer}`)
55
-
56
- socket.send(buffer, 0, buffer.length, this._port, address)
57
- }
58
-
59
- _sendAll (queue, address, family) {
60
- queue.forEach((buffer) => this._send(address, family, buffer))
89
+ queue.forEach((buffer) => {
90
+ log.debug(`Sending to DogStatsD: ${buffer}`)
91
+ socket.send(buffer, 0, buffer.length, this._port, address)
92
+ })
61
93
  }
62
94
 
63
95
  _add (stat, value, type, tags) {
@@ -197,8 +197,16 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
197
197
  }
198
198
 
199
199
  _encode (bytes, trace) {
200
- this._eventCount += trace.length
201
- const events = trace.map(formatSpan)
200
+ const rawEvents = trace.map(formatSpan)
201
+
202
+ const testSessionEvents = rawEvents.filter(
203
+ event => event.type === 'test_session_end' || event.type === 'test_suite_end'
204
+ )
205
+
206
+ const isTestSessionTrace = !!testSessionEvents.length
207
+ const events = isTestSessionTrace ? testSessionEvents : rawEvents
208
+
209
+ this._eventCount += events.length
202
210
 
203
211
  for (const event of events) {
204
212
  this._encodeEvent(bytes, event)
@@ -12,7 +12,6 @@ const MAXIMUM_NUM_COVERAGE_FILES = 100
12
12
  class CoverageCIVisibilityEncoder extends AgentEncoder {
13
13
  constructor () {
14
14
  super(...arguments)
15
- this.codeCoverageBuffers = []
16
15
  this._coverageBytes = new Chunk()
17
16
  this.form = new FormData()
18
17
  this.fileIndex = 1
@@ -3,6 +3,7 @@
3
3
  const AgentExporter = require('./exporters/agent')
4
4
  const LogExporter = require('./exporters/log')
5
5
  const AgentlessCiVisibilityExporter = require('./ci-visibility/exporters/agentless')
6
+ const AgentProxyCiVisibilityExporter = require('./ci-visibility/exporters/agent-proxy')
6
7
  const exporters = require('../../../ext/exporters')
7
8
  const fs = require('fs')
8
9
  const constants = require('./constants')
@@ -18,6 +19,8 @@ module.exports = name => {
18
19
  return AgentExporter
19
20
  case exporters.DATADOG:
20
21
  return AgentlessCiVisibilityExporter
22
+ case exporters.AGENT_PROXY:
23
+ return AgentProxyCiVisibilityExporter
21
24
  default:
22
25
  return inAWSLambda && !usingLambdaExtension ? LogExporter : AgentExporter
23
26
  }
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const URL = require('url').URL
3
+ const { URL, format } = require('url')
4
4
  const log = require('../../log')
5
5
  const Writer = require('./writer')
6
6
 
@@ -8,7 +8,11 @@ class AgentExporter {
8
8
  constructor (config, prioritySampler) {
9
9
  this._config = config
10
10
  const { url, hostname, port, lookup, protocolVersion, stats = {} } = config
11
- this._url = url || new URL(`http://${hostname || 'localhost'}:${port}`)
11
+ this._url = url || new URL(format({
12
+ protocol: 'http:',
13
+ hostname: hostname || 'localhost',
14
+ port
15
+ }))
12
16
 
13
17
  const headers = {}
14
18
  if (stats.enabled) {
@@ -51,6 +55,10 @@ class AgentExporter {
51
55
  }, flushInterval).unref()
52
56
  }
53
57
  }
58
+
59
+ flush (done = () => {}) {
60
+ this._writer.flush(done)
61
+ }
54
62
  }
55
63
 
56
64
  module.exports = AgentExporter
@@ -85,21 +85,14 @@ function makeRequest (version, data, count, url, headers, lookup, needsStartupLo
85
85
  'Datadog-Meta-Tracer-Version': tracerVersion,
86
86
  'X-Datadog-Trace-Count': String(count)
87
87
  },
88
- lookup
88
+ lookup,
89
+ url
89
90
  }
90
91
 
91
92
  setHeader(options.headers, 'Datadog-Meta-Lang', 'nodejs')
92
93
  setHeader(options.headers, 'Datadog-Meta-Lang-Version', process.version)
93
94
  setHeader(options.headers, 'Datadog-Meta-Lang-Interpreter', process.jsEngine || 'v8')
94
95
 
95
- if (url.protocol === 'unix:') {
96
- options.socketPath = url.pathname
97
- } else {
98
- options.protocol = url.protocol
99
- options.hostname = url.hostname
100
- options.port = url.port
101
- }
102
-
103
96
  log.debug(() => `Request to the agent: ${JSON.stringify(options)}`)
104
97
 
105
98
  request(data, options, (err, res, status) => {
@@ -0,0 +1,82 @@
1
+ const { URL, format } = require('url')
2
+
3
+ const request = require('./request')
4
+
5
+ function fetchAgentInfo (url, callback) {
6
+ request('', {
7
+ path: '/info',
8
+ url
9
+ }, (err, res) => {
10
+ if (err) {
11
+ return callback(err)
12
+ }
13
+ try {
14
+ const response = JSON.parse(res)
15
+ return callback(null, response)
16
+ } catch (e) {
17
+ return callback(e)
18
+ }
19
+ })
20
+ }
21
+
22
+ /**
23
+ * Exporter that exposes a way to query /info endpoint from the agent and gives you the response.
24
+ * While this._writer is not initialized, exported traces are stored as is.
25
+ */
26
+ class AgentInfoExporter {
27
+ constructor (tracerConfig) {
28
+ this._config = tracerConfig
29
+ const { url, hostname, port } = this._config
30
+ this._url = url || new URL(format({
31
+ protocol: 'http:',
32
+ hostname: hostname || 'localhost',
33
+ port
34
+ }))
35
+ this._traceBuffer = []
36
+ this._isInitialized = false
37
+ }
38
+
39
+ getAgentInfo (onReceivedInfo) {
40
+ fetchAgentInfo(this._url, onReceivedInfo)
41
+ }
42
+
43
+ export (trace) {
44
+ if (!this._isInitialized) {
45
+ this._traceBuffer.push(trace)
46
+ return
47
+ }
48
+ this._export(trace)
49
+ }
50
+
51
+ _export (payload, writer = this._writer, timerKey = '_timer') {
52
+ writer.append(payload)
53
+
54
+ const { flushInterval } = this._config
55
+
56
+ if (flushInterval === 0) {
57
+ writer.flush()
58
+ } else if (flushInterval > 0 && !this[timerKey]) {
59
+ this[timerKey] = setTimeout(() => {
60
+ writer.flush()
61
+ this[timerKey] = clearTimeout(this[timerKey])
62
+ }, flushInterval).unref()
63
+ }
64
+ }
65
+
66
+ getUncodedTraces () {
67
+ return this._traceBuffer
68
+ }
69
+
70
+ exportUncodedTraces () {
71
+ this.getUncodedTraces().forEach(uncodedTrace => {
72
+ this.export(uncodedTrace)
73
+ })
74
+ this.resetUncodedTraces()
75
+ }
76
+
77
+ resetUncodedTraces () {
78
+ this._traceBuffer = []
79
+ }
80
+ }
81
+
82
+ module.exports = AgentInfoExporter
@@ -6,6 +6,7 @@
6
6
  const { Readable } = require('stream')
7
7
  const http = require('http')
8
8
  const https = require('https')
9
+ const { parse: urlParse } = require('url')
9
10
  const docker = require('./docker')
10
11
  const { storage } = require('../../../../datadog-core')
11
12
  const log = require('../../log')
@@ -19,11 +20,54 @@ const containerId = docker.id()
19
20
 
20
21
  let activeRequests = 0
21
22
 
23
+ // TODO: Replace with `url.urlToHttpOptions` when supported by all versions
24
+ function urlToOptions (url) {
25
+ const agent = url.agent || http.globalAgent
26
+ const options = {
27
+ protocol: url.protocol || agent.protocol,
28
+ hostname: typeof url.hostname === 'string' && url.hostname.startsWith('[')
29
+ ? url.hostname.slice(1, -1)
30
+ : url.hostname ||
31
+ url.host ||
32
+ 'localhost',
33
+ hash: url.hash,
34
+ search: url.search,
35
+ pathname: url.pathname,
36
+ path: `${url.pathname || ''}${url.search || ''}`,
37
+ href: url.href
38
+ }
39
+ if (url.port !== '') {
40
+ options.port = Number(url.port)
41
+ }
42
+ if (url.username || url.password) {
43
+ options.auth = `${url.username}:${url.password}`
44
+ }
45
+ return options
46
+ }
47
+
48
+ function fromUrlString (url) {
49
+ return typeof urlToHttpOptions === 'function'
50
+ ? urlToOptions(new URL(url))
51
+ : urlParse(url)
52
+ }
53
+
22
54
  function request (data, options, callback) {
23
55
  if (!options.headers) {
24
56
  options.headers = {}
25
57
  }
26
58
 
59
+ if (options.url) {
60
+ const url = typeof options.url === 'object' ? options.url : fromUrlString(options.url)
61
+ if (url.protocol === 'unix:') {
62
+ options.socketPath = url.pathname
63
+ } else {
64
+ if (!options.path) options.path = url.path
65
+ options.protocol = url.protocol
66
+ options.hostname = url.hostname
67
+ options.port = url.port
68
+ }
69
+ }
70
+
27
71
  const isReadable = data instanceof Readable
28
72
 
29
73
  // The timeout should be kept low to avoid excessive queueing.
@@ -54,7 +98,13 @@ function request (data, options, callback) {
54
98
  if (res.statusCode >= 200 && res.statusCode <= 299) {
55
99
  callback(null, responseData, res.statusCode)
56
100
  } else {
57
- const error = new Error(`Error from the endpoint: ${res.statusCode} ${http.STATUS_CODES[res.statusCode]}`)
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]}.`
104
+ if (responseData) {
105
+ errorMessage += ` Response from the endpoint: "${responseData}"`
106
+ }
107
+ const error = new Error(errorMessage)
58
108
  error.status = res.statusCode
59
109
 
60
110
  callback(error, null, res.statusCode)
@@ -1,11 +1,15 @@
1
- const { URL } = require('url')
1
+ const { URL, format } = require('url')
2
2
 
3
3
  const { Writer } = require('./writer')
4
4
 
5
5
  class SpanStatsExporter {
6
6
  constructor (config) {
7
7
  const { hostname = '127.0.0.1', port = 8126, tags, url } = config
8
- this._url = url || new URL(`http://${hostname || 'localhost'}:${port}`)
8
+ this._url = url || new URL(format({
9
+ protocol: 'http:',
10
+ hostname: hostname || 'localhost',
11
+ port
12
+ }))
9
13
  this._writer = new Writer({ url: this._url, tags })
10
14
  }
11
15
 
@@ -9,10 +9,18 @@ const SAMPLING_PRIORITY_KEY = constants.SAMPLING_PRIORITY_KEY
9
9
  const SAMPLING_RULE_DECISION = constants.SAMPLING_RULE_DECISION
10
10
  const SAMPLING_LIMIT_DECISION = constants.SAMPLING_LIMIT_DECISION
11
11
  const SAMPLING_AGENT_DECISION = constants.SAMPLING_AGENT_DECISION
12
+ const SPAN_SAMPLING_MECHANISM = constants.SPAN_SAMPLING_MECHANISM
13
+ const SPAN_SAMPLING_RULE_RATE = constants.SPAN_SAMPLING_RULE_RATE
14
+ const SPAN_SAMPLING_MAX_PER_SECOND = constants.SPAN_SAMPLING_MAX_PER_SECOND
15
+ const SAMPLING_MECHANISM_SPAN = constants.SAMPLING_MECHANISM_SPAN
12
16
  const MEASURED = tags.MEASURED
13
17
  const ORIGIN_KEY = constants.ORIGIN_KEY
14
18
  const HOSTNAME_KEY = constants.HOSTNAME_KEY
15
19
  const TOP_LEVEL_KEY = constants.TOP_LEVEL_KEY
20
+ const PROCESS_ID = constants.PROCESS_ID
21
+ const ERROR_MESSAGE = constants.ERROR_MESSAGE
22
+ const ERROR_STACK = constants.ERROR_STACK
23
+ const ERROR_TYPE = constants.ERROR_TYPE
16
24
 
17
25
  const map = {
18
26
  'service.name': 'service',
@@ -47,6 +55,13 @@ function formatSpan (span) {
47
55
  }
48
56
  }
49
57
 
58
+ function setSingleSpanIngestionTags (span, options) {
59
+ if (!options) return
60
+ addTag({}, span.metrics, SPAN_SAMPLING_MECHANISM, SAMPLING_MECHANISM_SPAN)
61
+ addTag({}, span.metrics, SPAN_SAMPLING_RULE_RATE, options.sampleRate)
62
+ addTag({}, span.metrics, SPAN_SAMPLING_MAX_PER_SECOND, options.maxPerSecond)
63
+ }
64
+
50
65
  function extractTags (trace, span) {
51
66
  const context = span.context()
52
67
  const origin = context._trace.origin
@@ -78,9 +93,9 @@ function extractTags (trace, span) {
78
93
  extractError(trace, tags[tag])
79
94
  }
80
95
  break
81
- case 'error.type':
82
- case 'error.msg':
83
- case 'error.stack':
96
+ case ERROR_TYPE:
97
+ case ERROR_MESSAGE:
98
+ case ERROR_STACK:
84
99
  // HACK: remove when implemented in the backend
85
100
  if (context._name !== 'fs.operation') {
86
101
  trace.error = 1
@@ -92,10 +107,10 @@ function extractTags (trace, span) {
92
107
  }
93
108
  }
94
109
 
95
- if (span.tracer()._service === tags['service.name']) {
96
- addTag(trace.meta, trace.metrics, 'language', 'javascript')
97
- }
110
+ setSingleSpanIngestionTags(trace, context._sampling.spanSampling)
98
111
 
112
+ addTag(trace.meta, trace.metrics, 'language', 'javascript')
113
+ addTag(trace.meta, trace.metrics, PROCESS_ID, process.pid)
99
114
  addTag(trace.meta, trace.metrics, SAMPLING_PRIORITY_KEY, priority)
100
115
  addTag(trace.meta, trace.metrics, ORIGIN_KEY, origin)
101
116
  addTag(trace.meta, trace.metrics, HOSTNAME_KEY, hostname)
@@ -131,9 +146,9 @@ function extractError (trace, error) {
131
146
  trace.error = 1
132
147
 
133
148
  if (isError(error)) {
134
- addTag(trace.meta, trace.metrics, 'error.msg', error.message)
135
- addTag(trace.meta, trace.metrics, 'error.type', error.name)
136
- addTag(trace.meta, trace.metrics, 'error.stack', error.stack)
149
+ addTag(trace.meta, trace.metrics, ERROR_MESSAGE, error.message)
150
+ addTag(trace.meta, trace.metrics, ERROR_TYPE, error.name)
151
+ addTag(trace.meta, trace.metrics, ERROR_STACK, error.stack)
137
152
  }
138
153
  }
139
154
 
@@ -160,7 +175,7 @@ function addTag (meta, metrics, key, value, nested) {
160
175
  metrics[key] = value.toString()
161
176
  } else if (!Array.isArray(value) && !nested) {
162
177
  for (const prop in value) {
163
- if (!value.hasOwnProperty(prop)) continue
178
+ if (!hasOwn(value, prop)) continue
164
179
 
165
180
  addTag(meta, metrics, `${key}.${prop}`, value[prop], true)
166
181
  }
@@ -170,6 +185,10 @@ function addTag (meta, metrics, key, value, nested) {
170
185
  }
171
186
  }
172
187
 
188
+ function hasOwn (object, prop) {
189
+ return Object.prototype.hasOwnProperty.call(object, prop)
190
+ }
191
+
173
192
  function isNodeBuffer (obj) {
174
193
  return obj.constructor && obj.constructor.name === 'Buffer' &&
175
194
  typeof obj.readInt8 === 'function' &&
@@ -0,0 +1,72 @@
1
+ 'use strict'
2
+
3
+ const { channel } = require('../../../datadog-instrumentations/src/helpers/instrument')
4
+ const { ERROR_MESSAGE, ERROR_TYPE } = require('../constants')
5
+ const { ImpendingTimeout } = require('./runtime/errors')
6
+
7
+ const globalTracer = global._ddtrace
8
+ const tracer = globalTracer._tracer
9
+ const timeoutChannel = channel('apm:aws:lambda:timeout')
10
+ // Always crash the flushes when a message is received
11
+ // from this channel.
12
+ timeoutChannel.subscribe(_ => {
13
+ crashFlush()
14
+ })
15
+
16
+ let __lambdaTimeout
17
+
18
+ /**
19
+ * Publishes to the `apm:aws:lambda:timeout` channel when
20
+ * the AWS Lambda run time is about to end.
21
+ *
22
+ * @param {*} context AWS Lambda context object.
23
+ */
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
+ }
30
+
31
+ __lambdaTimeout = setTimeout(() => {
32
+ timeoutChannel.publish(undefined)
33
+ }, remainingTimeInMillis - 50)
34
+ }
35
+
36
+ /**
37
+ * Grabs the current span, adds an error for an impending timeout.
38
+ *
39
+ * After that, it calls `killAll` on the tracer processor
40
+ * in order to kill remaining unfinished spans.
41
+ *
42
+ * Once that is done, it finishes the last span.
43
+ */
44
+ function crashFlush () {
45
+ const activeSpan = tracer.scope().active()
46
+ const error = new ImpendingTimeout('Datadog detected an impending timeout')
47
+ activeSpan.addTags({
48
+ [ERROR_MESSAGE]: error.message,
49
+ [ERROR_TYPE]: error.name
50
+ })
51
+ tracer._processor.killAll()
52
+ activeSpan.finish()
53
+ }
54
+
55
+ /**
56
+ * Patches your AWS Lambda handler function to add some tracing support.
57
+ *
58
+ * @param {*} lambdaHandler a Lambda handler function.
59
+ */
60
+ exports.datadog = function datadog (lambdaHandler) {
61
+ return (...args) => {
62
+ const context = args[1]
63
+ const patched = lambdaHandler.apply(this, args)
64
+ checkTimeout(context)
65
+
66
+ if (patched) {
67
+ // clear the timeout as soon as a result is returned
68
+ patched.then(_ => clearTimeout(__lambdaTimeout))
69
+ }
70
+ return patched
71
+ }
72
+ }
@@ -0,0 +1,5 @@
1
+ 'use strict'
2
+
3
+ const { registerLambdaHook } = require('./runtime/ritm')
4
+
5
+ registerLambdaHook()
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ *
4
+ * Defines custom error types throwable by the runtime.
5
+ */
6
+ 'use strict'
7
+
8
+ class ExtendedError extends Error {
9
+ constructor (reason) {
10
+ super(reason)
11
+ Object.setPrototypeOf(this, new.target.prototype)
12
+ }
13
+ }
14
+
15
+ class ImpendingTimeout extends ExtendedError {}
16
+ ImpendingTimeout.prototype.name = 'Impending Timeout'
17
+
18
+ module.exports = {
19
+ ImpendingTimeout
20
+ }
@@ -0,0 +1,74 @@
1
+ 'use strict'
2
+
3
+ const path = require('path')
4
+
5
+ const { _extractModuleNameAndHandlerPath, _extractModuleRootAndHandler, _getLambdaFilePath } = require('./ritm')
6
+ const { datadog } = require('../handler')
7
+ const { addHook } = require('../../../../datadog-instrumentations/src/helpers/instrument')
8
+ const shimmer = require('../../../../datadog-shimmer')
9
+
10
+ /**
11
+ * Patches a Datadog Lambda module by calling `patchDatadogLambdaHandler`
12
+ * with the handler name `datadog`.
13
+ *
14
+ * @param {*} datadogLambdaModule node module to be patched.
15
+ * @returns a Datadog Lambda module with the `datadog` function from
16
+ * `datadog-lambda-js` patched.
17
+ */
18
+ const patchDatadogLambdaModule = (datadogLambdaModule) => {
19
+ shimmer.wrap(datadogLambdaModule, 'datadog', patchDatadogLambdaHandler)
20
+
21
+ return datadogLambdaModule
22
+ }
23
+
24
+ /**
25
+ * Patches a Datadog Lambda handler in order to do
26
+ * Datadog instrumentation by getting the Lambda handler from its
27
+ * arguments.
28
+ *
29
+ * @param {*} datadogHandler the Datadog Lambda handler to destructure.
30
+ * @returns the datadogHandler with its arguments patched.
31
+ */
32
+ function patchDatadogLambdaHandler (datadogHandler) {
33
+ return (userHandler) => {
34
+ return datadogHandler(datadog(userHandler))
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Patches a Lambda module on the given handler path.
40
+ *
41
+ * @param {string} handlerPath path of the handler to be patched.
42
+ * @returns a module with the given handler path patched.
43
+ */
44
+ const patchLambdaModule = (handlerPath) => (lambdaModule) => {
45
+ shimmer.wrap(lambdaModule, handlerPath, patchLambdaHandler)
46
+
47
+ return lambdaModule
48
+ }
49
+
50
+ /**
51
+ * Patches a Lambda handler in order to do Datadog instrumentation.
52
+ *
53
+ * @param {*} lambdaHandler the Lambda handler to be patched.
54
+ * @returns a function which patches the given Lambda handler.
55
+ */
56
+ function patchLambdaHandler (lambdaHandler) {
57
+ return datadog(lambdaHandler)
58
+ }
59
+
60
+ const lambdaTaskRoot = process.env.LAMBDA_TASK_ROOT
61
+ const originalLambdaHandler = process.env.DD_LAMBDA_HANDLER
62
+
63
+ if (originalLambdaHandler !== undefined) {
64
+ const [moduleRoot, moduleAndHandler] = _extractModuleRootAndHandler(originalLambdaHandler)
65
+ const [_module, handlerPath] = _extractModuleNameAndHandlerPath(moduleAndHandler)
66
+
67
+ const lambdaStylePath = path.resolve(lambdaTaskRoot, moduleRoot, _module)
68
+ const lambdaFilePath = _getLambdaFilePath(lambdaStylePath)
69
+
70
+ addHook({ name: lambdaFilePath }, patchLambdaModule(handlerPath))
71
+ } else {
72
+ // Instrumentation is done manually.
73
+ addHook({ name: 'datadog-lambda-js' }, patchDatadogLambdaModule)
74
+ }