dd-trace 2.21.0 → 2.22.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 (74) hide show
  1. package/LICENSE-3rdparty.csv +2 -0
  2. package/index.d.ts +42 -31
  3. package/package.json +4 -2
  4. package/packages/datadog-instrumentations/src/body-parser.js +26 -0
  5. package/packages/datadog-instrumentations/src/child-process.js +30 -0
  6. package/packages/datadog-instrumentations/src/connect.js +15 -15
  7. package/packages/datadog-instrumentations/src/cucumber.js +1 -1
  8. package/packages/datadog-instrumentations/src/hapi.js +3 -3
  9. package/packages/datadog-instrumentations/src/helpers/hooks.js +5 -0
  10. package/packages/datadog-instrumentations/src/http/server.js +11 -12
  11. package/packages/datadog-instrumentations/src/jest.js +102 -42
  12. package/packages/datadog-instrumentations/src/koa.js +32 -32
  13. package/packages/datadog-instrumentations/src/mocha.js +87 -6
  14. package/packages/datadog-instrumentations/src/pg.js +1 -2
  15. package/packages/datadog-instrumentations/src/qs.js +24 -0
  16. package/packages/datadog-instrumentations/src/restify.js +6 -3
  17. package/packages/datadog-instrumentations/src/router.js +22 -22
  18. package/packages/datadog-plugin-cucumber/src/index.js +13 -32
  19. package/packages/datadog-plugin-cypress/src/plugin.js +2 -1
  20. package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +0 -1
  21. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +0 -1
  22. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +0 -1
  23. package/packages/datadog-plugin-http/src/client.js +5 -3
  24. package/packages/datadog-plugin-http/src/server.js +11 -0
  25. package/packages/datadog-plugin-http2/src/client.js +2 -0
  26. package/packages/datadog-plugin-http2/src/server.js +3 -0
  27. package/packages/datadog-plugin-jest/src/index.js +11 -131
  28. package/packages/datadog-plugin-mocha/src/index.js +33 -46
  29. package/packages/datadog-plugin-net/src/index.js +4 -0
  30. package/packages/datadog-plugin-next/src/index.js +3 -0
  31. package/packages/datadog-plugin-pg/src/index.js +5 -2
  32. package/packages/datadog-plugin-router/src/index.js +9 -1
  33. package/packages/dd-trace/src/appsec/callbacks/ddwaf.js +3 -5
  34. package/packages/dd-trace/src/appsec/gateway/engine/index.js +1 -1
  35. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +3 -1
  36. package/packages/dd-trace/src/appsec/iast/analyzers/command-injection-analyzer.js +11 -0
  37. package/packages/dd-trace/src/appsec/iast/analyzers/injection-analyzer.js +19 -0
  38. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +13 -0
  39. package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +1 -1
  40. package/packages/dd-trace/src/appsec/iast/index.js +6 -0
  41. package/packages/dd-trace/src/appsec/iast/path-line.js +8 -1
  42. package/packages/dd-trace/src/appsec/iast/taint-tracking/filter.js +16 -0
  43. package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +18 -0
  44. package/packages/dd-trace/src/appsec/iast/taint-tracking/operations.js +125 -0
  45. package/packages/dd-trace/src/appsec/iast/taint-tracking/origin-types.js +4 -0
  46. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +38 -0
  47. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +66 -0
  48. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +52 -6
  49. package/packages/dd-trace/src/appsec/index.js +8 -0
  50. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +7 -0
  51. package/packages/dd-trace/src/appsec/remote_config/index.js +34 -0
  52. package/packages/dd-trace/src/appsec/remote_config/manager.js +264 -0
  53. package/packages/dd-trace/src/{exporters → appsec/remote_config}/scheduler.js +9 -9
  54. package/packages/dd-trace/src/appsec/rule_manager.js +3 -0
  55. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +5 -7
  56. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +1 -3
  57. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +1 -3
  58. package/packages/dd-trace/src/config.js +25 -9
  59. package/packages/dd-trace/src/constants.js +6 -1
  60. package/packages/dd-trace/src/exporters/common/request.js +7 -1
  61. package/packages/dd-trace/src/format.js +12 -10
  62. package/packages/dd-trace/src/opentracing/propagation/text_map.js +1 -5
  63. package/packages/dd-trace/src/opentracing/span_context.js +9 -0
  64. package/packages/dd-trace/src/plugin_manager.js +4 -1
  65. package/packages/dd-trace/src/plugins/ci_plugin.js +132 -0
  66. package/packages/dd-trace/src/plugins/database.js +46 -0
  67. package/packages/dd-trace/src/plugins/tracing.js +2 -0
  68. package/packages/dd-trace/src/plugins/util/test.js +61 -8
  69. package/packages/dd-trace/src/plugins/util/web.js +14 -9
  70. package/packages/dd-trace/src/profiling/config.js +3 -1
  71. package/packages/dd-trace/src/profiling/index.js +2 -2
  72. package/packages/dd-trace/src/profiling/profiler.js +35 -6
  73. package/packages/dd-trace/src/proxy.js +5 -1
  74. package/packages/dd-trace/src/tracer.js +4 -3
@@ -0,0 +1,264 @@
1
+ 'use strict'
2
+
3
+ const uuid = require('crypto-randomuuid')
4
+ const { EventEmitter } = require('events')
5
+ const Scheduler = require('./scheduler')
6
+ const tracerVersion = require('../../../../../package.json').version
7
+ const request = require('../../exporters/common/request')
8
+ const log = require('../../log')
9
+
10
+ const clientId = uuid()
11
+
12
+ const POLL_INTERVAL = 5e3
13
+ const DEFAULT_CAPABILITY = Buffer.alloc(1).toString('base64') // 0x00
14
+
15
+ // There MUST NOT exist separate instances of RC clients in a tracer making separate ClientGetConfigsRequest
16
+ // with their own separated Client.ClientState.
17
+ class RemoteConfigManager extends EventEmitter {
18
+ constructor (config) {
19
+ super()
20
+
21
+ this.scheduler = new Scheduler((cb) => this.poll(cb), POLL_INTERVAL)
22
+
23
+ this.requestOptions = {
24
+ url: config.url,
25
+ hostname: config.hostname,
26
+ port: config.port,
27
+ method: 'POST',
28
+ path: '/v0.7/config'
29
+ }
30
+
31
+ this.state = {
32
+ client: {
33
+ state: { // updated by `parseConfig()`
34
+ root_version: 1,
35
+ targets_version: 0,
36
+ config_states: [],
37
+ has_error: false,
38
+ error: '',
39
+ backend_client_state: ''
40
+ },
41
+ id: clientId,
42
+ products: [], // updated by `updateProducts()`
43
+ is_tracer: true,
44
+ client_tracer: {
45
+ runtime_id: config.tags['runtime-id'],
46
+ language: 'node',
47
+ tracer_version: tracerVersion,
48
+ service: config.service,
49
+ env: config.env,
50
+ app_version: config.version
51
+ },
52
+ capabilities: DEFAULT_CAPABILITY // updated by `updateCapabilities()`
53
+ },
54
+ cached_target_files: [] // updated by `parseConfig()`
55
+ }
56
+
57
+ this.appliedConfigs = new Map()
58
+ }
59
+
60
+ updateCapabilities (mask, value) {
61
+ const hex = Buffer.from(this.state.client.capabilities, 'base64').toString('hex')
62
+
63
+ let num = BigInt(`0x${hex}`)
64
+
65
+ if (value) {
66
+ num |= mask
67
+ } else {
68
+ num &= ~mask
69
+ }
70
+
71
+ let str = num.toString(16)
72
+
73
+ if (str.length % 2) str = `0${str}`
74
+
75
+ this.state.client.capabilities = Buffer.from(str, 'hex').toString('base64')
76
+ }
77
+
78
+ on (event, listener) {
79
+ super.on(event, listener)
80
+
81
+ this.state.client.products = this.eventNames()
82
+
83
+ this.scheduler.start()
84
+
85
+ return this
86
+ }
87
+
88
+ off (event, listener) {
89
+ super.off(event, listener)
90
+
91
+ this.state.client.products = this.eventNames()
92
+
93
+ if (!this.state.client.products.length) {
94
+ this.scheduler.stop()
95
+ }
96
+
97
+ return this
98
+ }
99
+
100
+ poll (cb) {
101
+ request(JSON.stringify(this.state), this.requestOptions, (err, data, statusCode) => {
102
+ // 404 means RC is disabled, ignore it
103
+ if (statusCode === 404) return cb()
104
+
105
+ if (err) {
106
+ log.error(err)
107
+ return cb()
108
+ }
109
+
110
+ // if error was just sent, reset the state
111
+ if (this.state.client.state.has_error) {
112
+ this.state.client.state.has_error = false
113
+ this.state.client.state.error = ''
114
+ }
115
+
116
+ if (data && data !== '{}') { // '{}' means the tracer is up to date
117
+ try {
118
+ this.parseConfig(JSON.parse(data))
119
+ } catch (err) {
120
+ log.error(`Could not parse remote config response: ${err}`)
121
+
122
+ this.state.client.state.has_error = true
123
+ this.state.client.state.error = err.toString()
124
+ }
125
+ }
126
+
127
+ cb()
128
+ })
129
+ }
130
+
131
+ // `client_configs` is the list of config paths to have applied
132
+ // `targets` is the signed index with metadata for config files
133
+ // `target_files` is the list of config files containing the actual config data
134
+ parseConfig ({
135
+ client_configs: clientConfigs = [],
136
+ targets,
137
+ target_files: targetFiles = []
138
+ }) {
139
+ const toUnapply = []
140
+ const toApply = []
141
+ const toModify = []
142
+
143
+ for (const appliedConfig of this.appliedConfigs.values()) {
144
+ if (!clientConfigs.includes(appliedConfig.path)) {
145
+ toUnapply.push(appliedConfig)
146
+ }
147
+ }
148
+
149
+ targets = fromBase64JSON(targets)
150
+
151
+ if (targets) {
152
+ for (const path of clientConfigs) {
153
+ const meta = targets.signed.targets[path]
154
+ if (!meta) throw new Error(`Unable to find target for path ${path}`)
155
+
156
+ const current = this.appliedConfigs.get(path)
157
+
158
+ const newConf = {}
159
+
160
+ if (current) {
161
+ if (current.hashes.sha256 === meta.hashes.sha256) continue
162
+
163
+ toModify.push(newConf)
164
+ } else {
165
+ toApply.push(newConf)
166
+ }
167
+
168
+ const file = targetFiles.find(file => file.path === path)
169
+ if (!file) throw new Error(`Unable to find file for path ${path}`)
170
+
171
+ // TODO: verify signatures
172
+ // verify length
173
+ // verify hash
174
+ // verify _type
175
+ // TODO: new Date(meta.signed.expires) ignore the Targets data if it has expired ?
176
+
177
+ const { product, id } = parseConfigPath(path)
178
+
179
+ Object.assign(newConf, {
180
+ path,
181
+ product,
182
+ id,
183
+ version: meta.custom.v,
184
+ apply_state: 1,
185
+ apply_error: '',
186
+ length: meta.length,
187
+ hashes: meta.hashes,
188
+ file: fromBase64JSON(file.raw)
189
+ })
190
+ }
191
+
192
+ this.state.client.state.targets_version = targets.signed.version
193
+ this.state.client.state.backend_client_state = targets.signed.custom.opaque_backend_state
194
+ }
195
+
196
+ if (toUnapply.length || toApply.length || toModify.length) {
197
+ this.dispatch(toUnapply, 'unapply')
198
+ this.dispatch(toApply, 'apply')
199
+ this.dispatch(toModify, 'modify')
200
+
201
+ this.state.client.state.config_states = []
202
+ this.state.cached_target_files = []
203
+
204
+ for (const conf of this.appliedConfigs.values()) {
205
+ this.state.client.state.config_states.push({
206
+ id: conf.id,
207
+ version: conf.version,
208
+ product: conf.product,
209
+ apply_state: conf.apply_state,
210
+ apply_error: conf.apply_error
211
+ })
212
+
213
+ this.state.cached_target_files.push({
214
+ path: conf.path,
215
+ length: conf.length,
216
+ hashes: Object.entries(conf.hashes).map((entry) => ({ algorithm: entry[0], hash: entry[1] }))
217
+ })
218
+ }
219
+ }
220
+ }
221
+
222
+ dispatch (list, action) {
223
+ for (const item of list) {
224
+ try {
225
+ // TODO: do we want to pass old and new config ?
226
+ this.emit(item.product, action, item.file)
227
+
228
+ item.apply_state = 2
229
+ } catch (err) {
230
+ item.apply_state = 3
231
+ item.apply_error = err.toString()
232
+ }
233
+
234
+ if (action === 'unapply') {
235
+ this.appliedConfigs.delete(item.path)
236
+ } else {
237
+ this.appliedConfigs.set(item.path, item)
238
+ }
239
+ }
240
+ }
241
+ }
242
+
243
+ function fromBase64JSON (str) {
244
+ if (!str) return null
245
+
246
+ return JSON.parse(Buffer.from(str, 'base64').toString())
247
+ }
248
+
249
+ const configPathRegex = /^(?:datadog\/\d+|employee)\/([^/]+)\/([^/]+)\/[^/]+$/
250
+
251
+ function parseConfigPath (configPath) {
252
+ const match = configPathRegex.exec(configPath)
253
+
254
+ if (!match || !match[1] || !match[2]) {
255
+ throw new Error(`Unable to parse path ${configPath}`)
256
+ }
257
+
258
+ return {
259
+ product: match[1],
260
+ id: match[2]
261
+ }
262
+ }
263
+
264
+ module.exports = RemoteConfigManager
@@ -8,21 +8,21 @@ class Scheduler {
8
8
  }
9
9
 
10
10
  start () {
11
- this._timer = setInterval(this._callback, this._interval)
12
- this._timer.unref && this._timer.unref()
11
+ if (this._timer) return
13
12
 
14
- process.once('beforeExit', this._callback)
13
+ this.runAfterDelay(0)
15
14
  }
16
15
 
17
- stop () {
18
- clearInterval(this._timer)
16
+ runAfterDelay (interval = this._interval) {
17
+ this._timer = setTimeout(this._callback, interval, () => this.runAfterDelay())
19
18
 
20
- process.removeListener('beforeExit', this._callback)
19
+ this._timer.unref && this._timer.unref()
21
20
  }
22
21
 
23
- reset () {
24
- this.stop()
25
- this.start()
22
+ stop () {
23
+ clearTimeout(this._timer)
24
+
25
+ this._timer = null
26
26
  }
27
27
  }
28
28
 
@@ -1,6 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const callbacks = require('./callbacks')
4
+ const Gateway = require('./gateway/engine')
4
5
 
5
6
  const appliedCallbacks = new Map()
6
7
 
@@ -14,6 +15,8 @@ function applyRules (rules, config) {
14
15
  }
15
16
 
16
17
  function clearAllRules () {
18
+ Gateway.manager.clear()
19
+
17
20
  for (const [key, callback] of appliedCallbacks) {
18
21
  callback.clear()
19
22
 
@@ -35,9 +35,7 @@ function getCommonRequestOptions (url) {
35
35
  'dd-api-key': process.env.DATADOG_API_KEY || process.env.DD_API_KEY
36
36
  },
37
37
  timeout: 15000,
38
- protocol: url.protocol,
39
- hostname: url.hostname,
40
- port: url.port
38
+ url
41
39
  }
42
40
  }
43
41
 
@@ -71,16 +69,16 @@ function getCommitsToExclude ({ url, repositoryUrl }, callback) {
71
69
  }))
72
70
  })
73
71
 
74
- request(localCommitData, options, (err, response, statusCode) => {
72
+ request(localCommitData, options, (err, response) => {
75
73
  if (err) {
76
- const error = new Error(`search_commits returned an error: status code ${statusCode}`)
74
+ const error = new Error(`Error fetching commits to exclude: ${err.message}`)
77
75
  return callback(error)
78
76
  }
79
77
  let commitsToExclude
80
78
  try {
81
79
  commitsToExclude = sanitizeCommits(JSON.parse(response).data)
82
80
  } catch (e) {
83
- return callback(new Error(`Can't parse search_commits response: ${e.message}`))
81
+ return callback(new Error(`Can't parse commits to exclude response: ${e.message}`))
84
82
  }
85
83
  callback(null, commitsToExclude, headCommit)
86
84
  })
@@ -129,7 +127,7 @@ function uploadPackFile ({ url, packFileToUpload, repositoryUrl, headCommit }, c
129
127
  }
130
128
  request(form, options, (err, _, statusCode) => {
131
129
  if (err) {
132
- const error = new Error(`Could not upload packfiles: status code ${statusCode}`)
130
+ const error = new Error(`Could not upload packfiles: status code ${statusCode}: ${err.message}`)
133
131
  return callback(error)
134
132
  }
135
133
  callback(null)
@@ -36,9 +36,7 @@ function getItrConfiguration ({
36
36
  'dd-application-key': appKey,
37
37
  'Content-Type': 'application/json'
38
38
  },
39
- protocol: intakeUrl.protocol,
40
- hostname: intakeUrl.hostname,
41
- port: intakeUrl.port
39
+ url: intakeUrl
42
40
  }
43
41
 
44
42
  const data = JSON.stringify({
@@ -34,9 +34,7 @@ function getSkippableSuites ({
34
34
  'Content-Type': 'application/json'
35
35
  },
36
36
  timeout: 15000,
37
- protocol: intakeUrl.protocol,
38
- hostname: intakeUrl.hostname,
39
- port: intakeUrl.port
37
+ url: intakeUrl
40
38
  }
41
39
 
42
40
  const data = JSON.stringify({
@@ -92,6 +92,11 @@ class Config {
92
92
  process.env.DD_RUNTIME_METRICS_ENABLED,
93
93
  false
94
94
  )
95
+ const DD_DBM_PROPAGATION_MODE = coalesce(
96
+ options.dbmPropagationMode,
97
+ process.env.DD_DBM_PROPAGATION_MODE,
98
+ 'disabled'
99
+ )
95
100
  const DD_AGENT_HOST = coalesce(
96
101
  options.hostname,
97
102
  process.env.DD_AGENT_HOST,
@@ -110,6 +115,7 @@ class Config {
110
115
  null
111
116
  )
112
117
  const DD_CIVISIBILITY_AGENTLESS_URL = process.env.DD_CIVISIBILITY_AGENTLESS_URL
118
+ const DD_CIVISIBILITY_AGENTLESS_ENABLED = process.env.DD_CIVISIBILITY_AGENTLESS_ENABLED
113
119
 
114
120
  const DD_CIVISIBILITY_ITR_ENABLED = coalesce(
115
121
  process.env.DD_CIVISIBILITY_ITR_ENABLED,
@@ -198,16 +204,21 @@ class Config {
198
204
  false
199
205
  )
200
206
 
201
- let appsec = options.appsec || (options.experimental && options.experimental.appsec)
207
+ let appsec = options.appsec != null ? options.appsec : options.experimental && options.experimental.appsec
208
+
209
+ if (typeof appsec === 'boolean') {
210
+ appsec = {
211
+ enabled: appsec
212
+ }
213
+ } else if (appsec == null) {
214
+ appsec = {}
215
+ }
202
216
 
203
217
  const DD_APPSEC_ENABLED = coalesce(
204
- appsec && (appsec === true || appsec.enabled === true), // TODO: remove when enabled by default
205
- process.env.DD_APPSEC_ENABLED,
206
- false
218
+ appsec.enabled,
219
+ process.env.DD_APPSEC_ENABLED && isTrue(process.env.DD_APPSEC_ENABLED)
207
220
  )
208
221
 
209
- appsec = appsec || {}
210
-
211
222
  const DD_APPSEC_RULES = coalesce(
212
223
  appsec.rules,
213
224
  process.env.DD_APPSEC_RULES,
@@ -307,6 +318,7 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
307
318
  const defaultFlushInterval = inAWSLambda ? 0 : 2000
308
319
 
309
320
  this.tracing = !isFalse(DD_TRACING_ENABLED)
321
+ this.dbmPropagationMode = DD_DBM_PROPAGATION_MODE
310
322
  this.logInjection = isTrue(DD_LOGS_INJECTION)
311
323
  this.env = DD_ENV
312
324
  this.url = DD_CIVISIBILITY_AGENTLESS_URL ? new URL(DD_CIVISIBILITY_AGENTLESS_URL)
@@ -353,7 +365,7 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
353
365
  this.protocolVersion = DD_TRACE_AGENT_PROTOCOL_VERSION
354
366
  this.tagsHeaderMaxLength = parseInt(DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH)
355
367
  this.appsec = {
356
- enabled: isTrue(DD_APPSEC_ENABLED),
368
+ enabled: DD_APPSEC_ENABLED,
357
369
  rules: DD_APPSEC_RULES,
358
370
  rateLimit: DD_APPSEC_TRACE_RATE_LIMIT,
359
371
  wafTimeout: DD_APPSEC_WAF_TIMEOUT,
@@ -366,8 +378,12 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
366
378
  maxConcurrentRequests: DD_IAST_MAX_CONCURRENT_REQUESTS,
367
379
  maxContextOperations: DD_IAST_MAX_CONTEXT_OPERATIONS
368
380
  }
369
- this.isGitUploadEnabled = isTrue(DD_CIVISIBILITY_GIT_UPLOAD_ENABLED)
370
- this.isIntelligentTestRunnerEnabled = isTrue(DD_CIVISIBILITY_ITR_ENABLED)
381
+
382
+ const isCiVisibilityAgentlessEnabled = isTrue(DD_CIVISIBILITY_AGENTLESS_ENABLED)
383
+ this.isIntelligentTestRunnerEnabled = isCiVisibilityAgentlessEnabled && isTrue(DD_CIVISIBILITY_ITR_ENABLED)
384
+ this.isGitUploadEnabled = this.isIntelligentTestRunnerEnabled ||
385
+ (isCiVisibilityAgentlessEnabled && isTrue(DD_CIVISIBILITY_GIT_UPLOAD_ENABLED))
386
+
371
387
  this.stats = {
372
388
  enabled: isTrue(DD_TRACE_STATS_COMPUTATION_ENABLED)
373
389
  }
@@ -19,5 +19,10 @@ module.exports = {
19
19
  SPAN_SAMPLING_RULE_RATE: '_dd.span_sampling.rule_rate',
20
20
  SPAN_SAMPLING_MAX_PER_SECOND: '_dd.span_sampling.max_per_second',
21
21
  DATADOG_LAMBDA_EXTENSION_PATH: '/opt/extensions/datadog-agent',
22
- DECISION_MAKER_KEY: '_dd.p.dm'
22
+ DECISION_MAKER_KEY: '_dd.p.dm',
23
+ PROCESS_ID: 'process_id',
24
+ ERROR_TYPE: 'error.type',
25
+ ERROR_MESSAGE: 'error.message',
26
+ ERROR_STACK: 'error.stack',
27
+ COMPONENT: 'component'
23
28
  }
@@ -66,7 +66,13 @@ function request (data, options, callback) {
66
66
  if (res.statusCode >= 200 && res.statusCode <= 299) {
67
67
  callback(null, responseData, res.statusCode)
68
68
  } else {
69
- const error = new Error(`Error from the endpoint: ${res.statusCode} ${http.STATUS_CODES[res.statusCode]}`)
69
+ const fullUrl = `${options.url || options.hostname || `localhost:${options.port}`}${options.path}`
70
+ // eslint-disable-next-line
71
+ let errorMessage = `Error from ${fullUrl}: ${res.statusCode} ${http.STATUS_CODES[res.statusCode]}.`
72
+ if (responseData) {
73
+ errorMessage += ` Response from the endpoint: "${responseData}"`
74
+ }
75
+ const error = new Error(errorMessage)
70
76
  error.status = res.statusCode
71
77
 
72
78
  callback(error, null, res.statusCode)
@@ -17,6 +17,10 @@ const MEASURED = tags.MEASURED
17
17
  const ORIGIN_KEY = constants.ORIGIN_KEY
18
18
  const HOSTNAME_KEY = constants.HOSTNAME_KEY
19
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
20
24
 
21
25
  const map = {
22
26
  'service.name': 'service',
@@ -89,9 +93,9 @@ function extractTags (trace, span) {
89
93
  extractError(trace, tags[tag])
90
94
  }
91
95
  break
92
- case 'error.type':
93
- case 'error.msg':
94
- case 'error.stack':
96
+ case ERROR_TYPE:
97
+ case ERROR_MESSAGE:
98
+ case ERROR_STACK:
95
99
  // HACK: remove when implemented in the backend
96
100
  if (context._name !== 'fs.operation') {
97
101
  trace.error = 1
@@ -103,12 +107,10 @@ function extractTags (trace, span) {
103
107
  }
104
108
  }
105
109
 
106
- if (span.tracer()._service === tags['service.name']) {
107
- addTag(trace.meta, trace.metrics, 'language', 'javascript')
108
- }
109
-
110
110
  setSingleSpanIngestionTags(trace, context._sampling.spanSampling)
111
111
 
112
+ addTag(trace.meta, trace.metrics, 'language', 'javascript')
113
+ addTag(trace.meta, trace.metrics, PROCESS_ID, process.pid)
112
114
  addTag(trace.meta, trace.metrics, SAMPLING_PRIORITY_KEY, priority)
113
115
  addTag(trace.meta, trace.metrics, ORIGIN_KEY, origin)
114
116
  addTag(trace.meta, trace.metrics, HOSTNAME_KEY, hostname)
@@ -144,9 +146,9 @@ function extractError (trace, error) {
144
146
  trace.error = 1
145
147
 
146
148
  if (isError(error)) {
147
- addTag(trace.meta, trace.metrics, 'error.msg', error.message)
148
- addTag(trace.meta, trace.metrics, 'error.type', error.name)
149
- 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)
150
152
  }
151
153
  }
152
154
 
@@ -130,11 +130,7 @@ class TextMapPropagator {
130
130
 
131
131
  _injectTraceparent (spanContext, carrier) {
132
132
  if (!this._config.experimental.traceparent) return
133
-
134
- const sampling = spanContext._sampling.priority >= AUTO_KEEP ? '01' : '00'
135
- const traceId = spanContext._traceId.toString(16).padStart(32, '0')
136
- const spanId = spanContext._spanId.toString(16).padStart(16, '0')
137
- carrier[traceparentKey] = `01-${traceId}-${spanId}-${sampling}`
133
+ carrier[traceparentKey] = spanContext.toTraceparent()
138
134
  }
139
135
 
140
136
  _extractSpanContext (carrier) {
@@ -1,5 +1,7 @@
1
1
  'use strict'
2
2
 
3
+ const { AUTO_KEEP } = require('../../../../ext/priority')
4
+
3
5
  class DatadogSpanContext {
4
6
  constructor (props) {
5
7
  props = props || {}
@@ -27,6 +29,13 @@ class DatadogSpanContext {
27
29
  toSpanId () {
28
30
  return this._spanId.toString(10)
29
31
  }
32
+
33
+ toTraceparent () {
34
+ const sampling = this._sampling.priority >= AUTO_KEEP ? '01' : '00'
35
+ const traceId = this._traceId.toString(16).padStart(32, '0')
36
+ const spanId = this._spanId.toString(16).padStart(16, '0')
37
+ return `01-${traceId}-${spanId}-${sampling}`
38
+ }
30
39
  }
31
40
 
32
41
  module.exports = DatadogSpanContext
@@ -125,7 +125,8 @@ module.exports = class PluginManager {
125
125
  site,
126
126
  experimental,
127
127
  queryStringObfuscation,
128
- url
128
+ url,
129
+ dbmPropagationMode
129
130
  } = this._tracerConfig
130
131
 
131
132
  const sharedConfig = {}
@@ -152,6 +153,8 @@ module.exports = class PluginManager {
152
153
 
153
154
  sharedConfig.isIntelligentTestRunnerEnabled = isIntelligentTestRunnerEnabled
154
155
 
156
+ sharedConfig.dbmPropagationMode = dbmPropagationMode
157
+
155
158
  if (serviceMapping && serviceMapping[name]) {
156
159
  sharedConfig.service = serviceMapping[name]
157
160
  }