dd-trace 3.12.1 → 3.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/LICENSE-3rdparty.csv +1 -0
  2. package/README.md +5 -5
  3. package/ci/init.js +3 -1
  4. package/index.d.ts +100 -1
  5. package/package.json +5 -4
  6. package/packages/datadog-instrumentations/src/aws-sdk.js +86 -0
  7. package/packages/datadog-instrumentations/src/cucumber.js +74 -15
  8. package/packages/datadog-instrumentations/src/cypress.js +1 -1
  9. package/packages/datadog-instrumentations/src/fs.js +358 -0
  10. package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
  11. package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
  12. package/packages/datadog-instrumentations/src/jest.js +24 -23
  13. package/packages/datadog-instrumentations/src/ldapjs.js +12 -2
  14. package/packages/datadog-instrumentations/src/mocha.js +10 -7
  15. package/packages/datadog-instrumentations/src/mongoose.js +1 -1
  16. package/packages/datadog-instrumentations/src/mysql.js +7 -1
  17. package/packages/datadog-instrumentations/src/mysql2.js +7 -1
  18. package/packages/datadog-instrumentations/src/next.js +2 -1
  19. package/packages/datadog-instrumentations/src/playwright.js +263 -0
  20. package/packages/datadog-plugin-aws-sdk/src/base.js +12 -5
  21. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +2 -2
  22. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +29 -24
  23. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +31 -16
  24. package/packages/datadog-plugin-cucumber/src/index.js +42 -11
  25. package/packages/datadog-plugin-cypress/src/plugin.js +129 -4
  26. package/packages/datadog-plugin-cypress/src/support.js +5 -0
  27. package/packages/datadog-plugin-fs/src/index.js +45 -0
  28. package/packages/datadog-plugin-hapi/src/index.js +5 -1
  29. package/packages/datadog-plugin-http/src/server.js +1 -1
  30. package/packages/datadog-plugin-http2/src/server.js +1 -1
  31. package/packages/datadog-plugin-jest/src/index.js +40 -70
  32. package/packages/datadog-plugin-mocha/src/index.js +44 -64
  33. package/packages/datadog-plugin-mysql/src/index.js +8 -7
  34. package/packages/datadog-plugin-playwright/src/index.js +112 -0
  35. package/packages/datadog-shimmer/src/shimmer.js +28 -11
  36. package/packages/dd-trace/src/appsec/addresses.js +3 -1
  37. package/packages/dd-trace/src/appsec/blocking.js +35 -9
  38. package/packages/dd-trace/src/appsec/callbacks/ddwaf.js +1 -1
  39. package/packages/dd-trace/src/appsec/iast/analyzers/analyzers.js +1 -0
  40. package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +60 -0
  41. package/packages/dd-trace/src/appsec/iast/iast-context.js +6 -2
  42. package/packages/dd-trace/src/appsec/iast/index.js +3 -2
  43. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +5 -2
  44. package/packages/dd-trace/src/appsec/index.js +5 -5
  45. package/packages/dd-trace/src/appsec/recommended.json +320 -184
  46. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -1
  47. package/packages/dd-trace/src/appsec/remote_config/index.js +3 -0
  48. package/packages/dd-trace/src/appsec/reporter.js +14 -14
  49. package/packages/dd-trace/src/appsec/sdk/index.js +41 -0
  50. package/packages/dd-trace/src/appsec/sdk/noop.js +17 -0
  51. package/packages/dd-trace/src/appsec/sdk/set_user.js +30 -0
  52. package/packages/dd-trace/src/appsec/sdk/track_event.js +74 -0
  53. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +73 -0
  54. package/packages/dd-trace/src/appsec/sdk/utils.js +10 -0
  55. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +1 -5
  56. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +1 -5
  57. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +48 -11
  58. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +7 -1
  59. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +4 -2
  60. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +5 -3
  61. package/packages/dd-trace/src/config.js +63 -7
  62. package/packages/dd-trace/src/encode/0.4.js +1 -1
  63. package/packages/dd-trace/src/encode/0.5.js +1 -1
  64. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +44 -4
  65. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +52 -37
  66. package/packages/dd-trace/src/encode/tags-processors.js +3 -2
  67. package/packages/dd-trace/src/exporters/common/request.js +10 -3
  68. package/packages/dd-trace/src/lambda/handler.js +5 -6
  69. package/packages/dd-trace/src/log/channels.js +47 -0
  70. package/packages/dd-trace/src/log/index.js +79 -0
  71. package/packages/dd-trace/src/log/writer.js +124 -0
  72. package/packages/dd-trace/src/metrics.js +18 -0
  73. package/packages/dd-trace/src/noop/proxy.js +5 -2
  74. package/packages/dd-trace/src/opentracing/propagation/text_map.js +188 -36
  75. package/packages/dd-trace/src/opentracing/propagation/tracestate.js +99 -0
  76. package/packages/dd-trace/src/opentracing/span.js +2 -1
  77. package/packages/dd-trace/src/opentracing/span_context.js +6 -3
  78. package/packages/dd-trace/src/plugins/ci_plugin.js +72 -12
  79. package/packages/dd-trace/src/plugins/index.js +2 -0
  80. package/packages/dd-trace/src/plugins/util/ci.js +13 -21
  81. package/packages/dd-trace/src/plugins/util/exec.js +2 -2
  82. package/packages/dd-trace/src/plugins/util/git.js +16 -1
  83. package/packages/dd-trace/src/{appsec → plugins/util}/ip_extractor.js +1 -1
  84. package/packages/dd-trace/src/plugins/util/test.js +53 -10
  85. package/packages/dd-trace/src/plugins/util/user-provided-git.js +2 -7
  86. package/packages/dd-trace/src/plugins/util/web.js +11 -0
  87. package/packages/dd-trace/src/profiler.js +3 -0
  88. package/packages/dd-trace/src/profiling/config.js +8 -3
  89. package/packages/dd-trace/src/profiling/exporters/file.js +13 -2
  90. package/packages/dd-trace/src/profiling/profiler.js +23 -6
  91. package/packages/dd-trace/src/profiling/profilers/wall.js +1 -0
  92. package/packages/dd-trace/src/proxy.js +2 -0
  93. package/packages/dd-trace/src/span_processor.js +1 -1
  94. package/packages/dd-trace/src/span_sampler.js +68 -52
  95. package/packages/dd-trace/src/startup-log.js +3 -6
  96. package/packages/dd-trace/src/telemetry/index.js +23 -2
  97. package/packages/dd-trace/src/telemetry/send-data.js +4 -1
  98. package/packages/dd-trace/src/tracer.js +0 -16
  99. package/scripts/check-proposal-labels.js +71 -0
  100. package/packages/dd-trace/src/log.js +0 -143
  101. /package/packages/dd-trace/src/{appsec → plugins/util}/ip_blocklist.js +0 -0
@@ -122,7 +122,7 @@ module.exports = {
122
122
  [CI_ENV_VARS]: JSON.stringify({ DD_CUSTOM_TRACE_ID })
123
123
  }
124
124
 
125
- const isTag = JENKINS_GIT_BRANCH && JENKINS_GIT_BRANCH.includes('tags')
125
+ const isTag = JENKINS_GIT_BRANCH && JENKINS_GIT_BRANCH.includes('tags/')
126
126
  const refKey = isTag ? GIT_TAG : GIT_BRANCH
127
127
  const ref = normalizeRef(JENKINS_GIT_BRANCH)
128
128
 
@@ -216,7 +216,8 @@ module.exports = {
216
216
  [GIT_REPOSITORY_URL]: CIRCLE_REPOSITORY_URL,
217
217
  [CI_JOB_URL]: CIRCLE_BUILD_URL,
218
218
  [CI_WORKSPACE_PATH]: CIRCLE_WORKING_DIRECTORY,
219
- [CIRCLE_TAG ? GIT_TAG : GIT_BRANCH]: CIRCLE_TAG || CIRCLE_BRANCH,
219
+ [GIT_TAG]: CIRCLE_TAG,
220
+ [GIT_BRANCH]: CIRCLE_BRANCH,
220
221
  [CI_ENV_VARS]: JSON.stringify({
221
222
  CIRCLE_WORKFLOW_ID,
222
223
  CIRCLE_BUILD_NUM
@@ -249,7 +250,7 @@ module.exports = {
249
250
  const jobUrl = `${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/commit/${GITHUB_SHA}/checks`
250
251
 
251
252
  const ref = GITHUB_HEAD_REF || GITHUB_REF || ''
252
- const refKey = ref.includes('tags') ? GIT_TAG : GIT_BRANCH
253
+ const refKey = ref.includes('tags/') ? GIT_TAG : GIT_BRANCH
253
254
 
254
255
  tags = {
255
256
  [CI_PIPELINE_ID]: GITHUB_RUN_ID,
@@ -305,13 +306,12 @@ module.exports = {
305
306
  }
306
307
 
307
308
  if (APPVEYOR_REPO_PROVIDER === 'github') {
308
- const refKey = APPVEYOR_REPO_TAG_NAME ? GIT_TAG : GIT_BRANCH
309
- const ref = APPVEYOR_REPO_TAG_NAME || APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH || APPVEYOR_REPO_BRANCH
310
309
  tags = {
311
310
  ...tags,
312
311
  [GIT_REPOSITORY_URL]: `https://github.com/${APPVEYOR_REPO_NAME}.git`,
313
312
  [GIT_COMMIT_SHA]: APPVEYOR_REPO_COMMIT,
314
- [refKey]: ref
313
+ [GIT_TAG]: APPVEYOR_REPO_TAG_NAME,
314
+ [GIT_BRANCH]: APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH || APPVEYOR_REPO_BRANCH
315
315
  }
316
316
  }
317
317
  }
@@ -340,7 +340,7 @@ module.exports = {
340
340
  } = env
341
341
 
342
342
  const ref = SYSTEM_PULLREQUEST_SOURCEBRANCH || BUILD_SOURCEBRANCH || BUILD_SOURCEBRANCHNAME
343
- const refKey = (ref || '').includes('tags') ? GIT_TAG : GIT_BRANCH
343
+ const refKey = (ref || '').includes('tags/') ? GIT_TAG : GIT_BRANCH
344
344
 
345
345
  tags = {
346
346
  [CI_PROVIDER_NAME]: 'azurepipelines',
@@ -419,10 +419,6 @@ module.exports = {
419
419
  BITRISE_GIT_MESSAGE
420
420
  } = env
421
421
 
422
- const isTag = !!BITRISE_GIT_TAG
423
- const refKey = isTag ? GIT_TAG : GIT_BRANCH
424
- const ref = BITRISE_GIT_TAG || BITRISEIO_GIT_BRANCH_DEST || BITRISE_GIT_BRANCH
425
-
426
422
  tags = {
427
423
  [CI_PROVIDER_NAME]: 'bitrise',
428
424
  [CI_PIPELINE_ID]: BITRISE_BUILD_SLUG,
@@ -432,7 +428,8 @@ module.exports = {
432
428
  [GIT_COMMIT_SHA]: BITRISE_GIT_COMMIT || GIT_CLONE_COMMIT_HASH,
433
429
  [GIT_REPOSITORY_URL]: BITRISE_GIT_REPOSITORY_URL,
434
430
  [CI_WORKSPACE_PATH]: BITRISE_SOURCE_DIR,
435
- [refKey]: ref,
431
+ [GIT_TAG]: BITRISE_GIT_TAG,
432
+ [GIT_BRANCH]: BITRISEIO_GIT_BRANCH_DEST || BITRISE_GIT_BRANCH,
436
433
  [GIT_COMMIT_MESSAGE]: BITRISE_GIT_MESSAGE
437
434
  }
438
435
  }
@@ -454,9 +451,6 @@ module.exports = {
454
451
  BUILDKITE_MESSAGE
455
452
  } = env
456
453
 
457
- const ref = BUILDKITE_TAG || BUILDKITE_BRANCH
458
- const refKey = BUILDKITE_TAG ? GIT_TAG : GIT_BRANCH
459
-
460
454
  tags = {
461
455
  [CI_PROVIDER_NAME]: 'buildkite',
462
456
  [CI_PIPELINE_ID]: BUILDKITE_BUILD_ID,
@@ -467,7 +461,8 @@ module.exports = {
467
461
  [GIT_COMMIT_SHA]: BUILDKITE_COMMIT,
468
462
  [CI_WORKSPACE_PATH]: BUILDKITE_BUILD_CHECKOUT_PATH,
469
463
  [GIT_REPOSITORY_URL]: BUILDKITE_REPO,
470
- [refKey]: ref,
464
+ [GIT_TAG]: BUILDKITE_TAG,
465
+ [GIT_BRANCH]: BUILDKITE_BRANCH,
471
466
  [GIT_COMMIT_AUTHOR_NAME]: BUILDKITE_BUILD_AUTHOR,
472
467
  [GIT_COMMIT_AUTHOR_EMAIL]: BUILDKITE_BUILD_AUTHOR_EMAIL,
473
468
  [GIT_COMMIT_MESSAGE]: BUILDKITE_MESSAGE,
@@ -493,10 +488,6 @@ module.exports = {
493
488
  TRAVIS_COMMIT_MESSAGE
494
489
  } = env
495
490
 
496
- const isTag = !!TRAVIS_TAG
497
- const ref = TRAVIS_TAG || TRAVIS_PULL_REQUEST_BRANCH || TRAVIS_BRANCH
498
- const refKey = isTag ? GIT_TAG : GIT_BRANCH
499
-
500
491
  tags = {
501
492
  [CI_PROVIDER_NAME]: 'travisci',
502
493
  [CI_JOB_URL]: TRAVIS_JOB_WEB_URL,
@@ -507,7 +498,8 @@ module.exports = {
507
498
  [GIT_COMMIT_SHA]: TRAVIS_COMMIT,
508
499
  [GIT_REPOSITORY_URL]: `https://github.com/${TRAVIS_REPO_SLUG}.git`,
509
500
  [CI_WORKSPACE_PATH]: TRAVIS_BUILD_DIR,
510
- [refKey]: ref,
501
+ [GIT_TAG]: TRAVIS_TAG,
502
+ [GIT_BRANCH]: TRAVIS_PULL_REQUEST_BRANCH || TRAVIS_BRANCH,
511
503
  [GIT_COMMIT_MESSAGE]: TRAVIS_COMMIT_MESSAGE
512
504
  }
513
505
  }
@@ -1,8 +1,8 @@
1
- const { execSync } = require('child_process')
1
+ const cp = require('child_process')
2
2
 
3
3
  const sanitizedExec = (cmd, options = {}) => {
4
4
  try {
5
- return execSync(cmd, options).toString().replace(/(\r\n|\n|\r)/gm, '')
5
+ return cp.execSync(cmd, options).toString().replace(/(\r\n|\n|\r)/gm, '')
6
6
  } catch (e) {
7
7
  return ''
8
8
  }
@@ -21,6 +21,19 @@ const {
21
21
 
22
22
  const GIT_REV_LIST_MAX_BUFFER = 8 * 1024 * 1024 // 8MB
23
23
 
24
+ function isShallowRepository () {
25
+ return sanitizedExec('git rev-parse --is-shallow-repository', { stdio: 'pipe' }) === 'true'
26
+ }
27
+
28
+ function unshallowRepository () {
29
+ try {
30
+ execSync('git config remote.origin.partialclonefilter "blob:none"', { stdio: 'pipe' })
31
+ execSync('git fetch --shallow-since="1 month ago" --update-shallow --refetch', { stdio: 'pipe' })
32
+ } catch (err) {
33
+ log.error(err)
34
+ }
35
+ }
36
+
24
37
  function getRepositoryUrl () {
25
38
  return sanitizedExec('git config --get remote.origin.url', { stdio: 'pipe' })
26
39
  }
@@ -146,5 +159,7 @@ module.exports = {
146
159
  getRepositoryUrl,
147
160
  generatePackFilesForCommits,
148
161
  getCommitsToUpload,
149
- GIT_REV_LIST_MAX_BUFFER
162
+ GIT_REV_LIST_MAX_BUFFER,
163
+ isShallowRepository,
164
+ unshallowRepository
150
165
  }
@@ -2,7 +2,7 @@
2
2
 
3
3
  const BlockList = require('./ip_blocklist')
4
4
  const net = require('net')
5
- const log = require('../log')
5
+ const log = require('../../log')
6
6
 
7
7
  const ipHeaderList = [
8
8
  'x-forwarded-for',
@@ -38,7 +38,9 @@ const TEST_CODE_OWNERS = 'test.codeowners'
38
38
  const TEST_SOURCE_FILE = 'test.source.file'
39
39
  const LIBRARY_VERSION = 'library_version'
40
40
  const TEST_COMMAND = 'test.command'
41
+ const TEST_BUNDLE = 'test.bundle'
41
42
  const TEST_SESSION_ID = 'test_session_id'
43
+ const TEST_MODULE_ID = 'test_module_id'
42
44
  const TEST_SUITE_ID = 'test_suite_id'
43
45
 
44
46
  const CI_APP_ORIGIN = 'ciapp-test'
@@ -48,6 +50,8 @@ const JEST_TEST_RUNNER = 'test.jest.test_runner'
48
50
  const TEST_ITR_TESTS_SKIPPED = '_dd.ci.itr.tests_skipped'
49
51
  const TEST_SESSION_ITR_SKIPPING_ENABLED = 'test_session.itr.tests_skipping.enabled'
50
52
  const TEST_SESSION_CODE_COVERAGE_ENABLED = 'test_session.code_coverage.enabled'
53
+ const TEST_MODULE_ITR_SKIPPING_ENABLED = 'test_module.itr.tests_skipping.enabled'
54
+ const TEST_MODULE_CODE_COVERAGE_ENABLED = 'test_module.code_coverage.enabled'
51
55
 
52
56
  const TEST_CODE_COVERAGE_LINES_TOTAL = 'test.codecov_lines_total'
53
57
 
@@ -75,14 +79,20 @@ module.exports = {
75
79
  getCodeOwnersForFilename,
76
80
  getTestCommonTags,
77
81
  getTestSessionCommonTags,
82
+ getTestModuleCommonTags,
78
83
  getTestSuiteCommonTags,
79
84
  TEST_COMMAND,
80
85
  TEST_SESSION_ID,
86
+ TEST_MODULE_ID,
81
87
  TEST_SUITE_ID,
82
88
  TEST_ITR_TESTS_SKIPPED,
89
+ TEST_BUNDLE,
83
90
  TEST_SESSION_ITR_SKIPPING_ENABLED,
84
91
  TEST_SESSION_CODE_COVERAGE_ENABLED,
92
+ TEST_MODULE_ITR_SKIPPING_ENABLED,
93
+ TEST_MODULE_CODE_COVERAGE_ENABLED,
85
94
  TEST_CODE_COVERAGE_LINES_TOTAL,
95
+ addIntelligentTestRunnerSpanTags,
86
96
  getCoveredFilenamesFromCoverage,
87
97
  resetCoverage,
88
98
  mergeCoverage,
@@ -242,26 +252,59 @@ function getCodeOwnersForFilename (filename, entries) {
242
252
  return null
243
253
  }
244
254
 
245
- function getTestSessionCommonTags (command, version) {
255
+ function getTestLevelCommonTags (command, testFrameworkVersion) {
256
+ return {
257
+ [TEST_FRAMEWORK_VERSION]: testFrameworkVersion,
258
+ [LIBRARY_VERSION]: ddTraceVersion,
259
+ [TEST_COMMAND]: command,
260
+ [TEST_TYPE]: 'test'
261
+ }
262
+ }
263
+
264
+ function getTestSessionCommonTags (command, testFrameworkVersion) {
246
265
  return {
247
266
  [SPAN_TYPE]: 'test_session_end',
248
- [TEST_TYPE]: 'test',
249
267
  [RESOURCE_NAME]: `test_session.${command}`,
250
- [TEST_FRAMEWORK_VERSION]: version,
251
- [LIBRARY_VERSION]: ddTraceVersion,
252
- [TEST_COMMAND]: command
268
+ ...getTestLevelCommonTags(command, testFrameworkVersion)
269
+ }
270
+ }
271
+
272
+ function getTestModuleCommonTags (command, testFrameworkVersion) {
273
+ return {
274
+ [SPAN_TYPE]: 'test_module_end',
275
+ [RESOURCE_NAME]: `test_module.${command}`,
276
+ [TEST_BUNDLE]: command,
277
+ ...getTestLevelCommonTags(command, testFrameworkVersion)
253
278
  }
254
279
  }
255
280
 
256
- function getTestSuiteCommonTags (command, version, testSuite) {
281
+ function getTestSuiteCommonTags (command, testFrameworkVersion, testSuite) {
257
282
  return {
258
283
  [SPAN_TYPE]: 'test_suite_end',
259
- [TEST_TYPE]: 'test',
260
284
  [RESOURCE_NAME]: `test_suite.${testSuite}`,
261
- [TEST_FRAMEWORK_VERSION]: version,
262
- [LIBRARY_VERSION]: ddTraceVersion,
285
+ [TEST_BUNDLE]: command,
263
286
  [TEST_SUITE]: testSuite,
264
- [TEST_COMMAND]: command
287
+ ...getTestLevelCommonTags(command, testFrameworkVersion)
288
+ }
289
+ }
290
+
291
+ function addIntelligentTestRunnerSpanTags (
292
+ testSessionSpan,
293
+ testModuleSpan,
294
+ { isSuitesSkipped, isSuitesSkippingEnabled, isCodeCoverageEnabled, testCodeCoverageLinesTotal }
295
+ ) {
296
+ testSessionSpan.setTag(TEST_ITR_TESTS_SKIPPED, isSuitesSkipped ? 'true' : 'false')
297
+ testSessionSpan.setTag(TEST_SESSION_ITR_SKIPPING_ENABLED, isSuitesSkippingEnabled ? 'true' : 'false')
298
+ testSessionSpan.setTag(TEST_SESSION_CODE_COVERAGE_ENABLED, isCodeCoverageEnabled ? 'true' : 'false')
299
+
300
+ testModuleSpan.setTag(TEST_ITR_TESTS_SKIPPED, isSuitesSkipped ? 'true' : 'false')
301
+ testModuleSpan.setTag(TEST_MODULE_ITR_SKIPPING_ENABLED, isSuitesSkippingEnabled ? 'true' : 'false')
302
+ testModuleSpan.setTag(TEST_MODULE_CODE_COVERAGE_ENABLED, isCodeCoverageEnabled ? 'true' : 'false')
303
+
304
+ // If suites have been skipped we don't want to report the total coverage, as it will be wrong
305
+ if (testCodeCoverageLinesTotal !== undefined && !isSuitesSkipped) {
306
+ testSessionSpan.setTag(TEST_CODE_COVERAGE_LINES_TOTAL, testCodeCoverageLinesTotal)
307
+ testModuleSpan.setTag(TEST_CODE_COVERAGE_LINES_TOTAL, testCodeCoverageLinesTotal)
265
308
  }
266
309
  }
267
310
 
@@ -54,16 +54,11 @@ function getUserProviderGitMetadata () {
54
54
  DD_GIT_COMMIT_AUTHOR_DATE
55
55
  } = process.env
56
56
 
57
- let branch = normalizeRef(DD_GIT_BRANCH)
57
+ const branch = normalizeRef(DD_GIT_BRANCH)
58
58
  let tag = normalizeRef(DD_GIT_TAG)
59
59
 
60
- if (DD_GIT_TAG) {
61
- branch = undefined
62
- }
63
-
64
- // if DD_GIT_BRANCH is a tag, we associate its value to TAG instead of BRANCH
60
+ // if DD_GIT_BRANCH is a tag, we associate its value to TAG too
65
61
  if ((DD_GIT_BRANCH || '').includes('origin/tags') || (DD_GIT_BRANCH || '').includes('refs/heads/tags')) {
66
- branch = undefined
67
62
  tag = normalizeRef(DD_GIT_BRANCH)
68
63
  }
69
64
 
@@ -8,6 +8,7 @@ const tags = require('../../../../../ext/tags')
8
8
  const types = require('../../../../../ext/types')
9
9
  const kinds = require('../../../../../ext/kinds')
10
10
  const urlFilter = require('./urlfilter')
11
+ const { extractIp } = require('./ip_extractor')
11
12
  const { ERROR_MESSAGE, ERROR_TYPE, ERROR_STACK } = require('../../constants')
12
13
 
13
14
  const WEB = types.WEB
@@ -24,6 +25,7 @@ const HTTP_ROUTE = tags.HTTP_ROUTE
24
25
  const HTTP_REQUEST_HEADERS = tags.HTTP_REQUEST_HEADERS
25
26
  const HTTP_RESPONSE_HEADERS = tags.HTTP_RESPONSE_HEADERS
26
27
  const HTTP_USERAGENT = tags.HTTP_USERAGENT
28
+ const HTTP_CLIENT_IP = tags.HTTP_CLIENT_IP
27
29
  const MANUAL_DROP = tags.MANUAL_DROP
28
30
 
29
31
  const HTTP2_HEADER_AUTHORITY = ':authority'
@@ -432,6 +434,15 @@ function addRequestTags (context) {
432
434
  [HTTP_USERAGENT]: req.headers['user-agent']
433
435
  })
434
436
 
437
+ // if client ip has already been set by appsec, no need to run it again
438
+ if (config.clientIpEnabled && !span.context()._tags.hasOwnProperty(HTTP_CLIENT_IP)) {
439
+ const clientIp = extractIp(config, req)
440
+
441
+ if (clientIp) {
442
+ span.setTag(HTTP_CLIENT_IP, clientIp)
443
+ }
444
+ }
445
+
435
446
  addHeaders(context)
436
447
  }
437
448
 
@@ -3,6 +3,9 @@
3
3
  const log = require('./log')
4
4
  const { profiler } = require('./profiling')
5
5
 
6
+ // Stop profiler upon exit in order to collect and export the current profile
7
+ process.once('beforeExit', () => { profiler.stop() })
8
+
6
9
  module.exports = {
7
10
  start: config => {
8
11
  const { service, version, env, url, hostname, port, tags } = config
@@ -23,7 +23,9 @@ const {
23
23
  DD_AGENT_HOST,
24
24
  DD_TRACE_AGENT_PORT,
25
25
  DD_PROFILING_UPLOAD_TIMEOUT,
26
- DD_PROFILING_SOURCE_MAP
26
+ DD_PROFILING_SOURCE_MAP,
27
+ DD_PROFILING_UPLOAD_PERIOD,
28
+ DD_PROFILING_PPROF_PREFIX
27
29
  } = process.env
28
30
 
29
31
  class Config {
@@ -35,13 +37,15 @@ class Config {
35
37
  const version = coalesce(options.version, DD_VERSION)
36
38
  const functionname = process.env.AWS_LAMBDA_FUNCTION_NAME
37
39
  // Must be longer than one minute so pad with five seconds
38
- const flushInterval = coalesce(options.interval, 65 * 1000)
40
+ const flushInterval = coalesce(options.interval, Number(DD_PROFILING_UPLOAD_PERIOD) * 1000, 65 * 1000)
39
41
  const uploadTimeout = coalesce(options.uploadTimeout,
40
- DD_PROFILING_UPLOAD_TIMEOUT, 60 * 1000)
42
+ Number(DD_PROFILING_UPLOAD_TIMEOUT), 60 * 1000)
41
43
  const sourceMap = coalesce(options.sourceMap,
42
44
  DD_PROFILING_SOURCE_MAP, true)
43
45
  const endpointCollection = coalesce(options.endpointCollection,
44
46
  DD_PROFILING_ENDPOINT_COLLECTION_ENABLED, false)
47
+ const pprofPrefix = coalesce(options.pprofPrefix,
48
+ DD_PROFILING_PPROF_PREFIX)
45
49
 
46
50
  this.enabled = String(enabled) !== 'false'
47
51
  this.service = service
@@ -60,6 +64,7 @@ class Config {
60
64
  this.uploadTimeout = uploadTimeout
61
65
  this.sourceMap = sourceMap
62
66
  this.endpointCollection = endpointCollection
67
+ this.pprofPrefix = pprofPrefix
63
68
 
64
69
  const hostname = coalesce(options.hostname, DD_AGENT_HOST) || 'localhost'
65
70
  const port = coalesce(options.port, DD_TRACE_AGENT_PORT) || 8126
@@ -4,11 +4,22 @@ const fs = require('fs')
4
4
  const { promisify } = require('util')
5
5
  const writeFile = promisify(fs.writeFile)
6
6
 
7
+ function formatDateTime (t) {
8
+ const pad = (n) => String(n).padStart(2, '0')
9
+ return `${t.getUTCFullYear()}${pad(t.getUTCMonth() + 1)}${pad(t.getUTCDate())}` +
10
+ `T${pad(t.getUTCHours())}${pad(t.getUTCMinutes())}${pad(t.getUTCSeconds())}Z`
11
+ }
12
+
7
13
  class FileExporter {
8
- export ({ profiles }) {
14
+ constructor ({ pprofPrefix } = {}) {
15
+ this._pprofPrefix = pprofPrefix || ''
16
+ }
17
+
18
+ export ({ profiles, end }) {
9
19
  const types = Object.keys(profiles)
20
+ const dateStr = formatDateTime(end)
10
21
  const tasks = types.map(type => {
11
- return writeFile(`${type}.pb.gz`, profiles[type])
22
+ return writeFile(`${this._pprofPrefix}${type}_${dateStr}.pprof`, profiles[type])
12
23
  })
13
24
 
14
25
  return Promise.all(tasks)
@@ -56,7 +56,7 @@ class Profiler extends EventEmitter {
56
56
  this._capture(this._timeoutInterval)
57
57
  } catch (e) {
58
58
  this._logger.error(e)
59
- this.stop()
59
+ this._stop()
60
60
  }
61
61
  }
62
62
 
@@ -64,7 +64,16 @@ class Profiler extends EventEmitter {
64
64
  this._timeoutInterval = this._config.flushInterval
65
65
  }
66
66
 
67
- stop () {
67
+ async stop () {
68
+ if (!this._enabled) return
69
+
70
+ // collect and export current profiles
71
+ // once collect returns, profilers can be safely stopped
72
+ this._collect()
73
+ this._stop()
74
+ }
75
+
76
+ _stop () {
68
77
  if (!this._enabled) return
69
78
 
70
79
  this._enabled = false
@@ -92,16 +101,24 @@ class Profiler extends EventEmitter {
92
101
  }
93
102
 
94
103
  async _collect () {
104
+ if (!this._enabled) return
105
+
95
106
  const start = this._lastStart
96
107
  const end = new Date()
97
- const profiles = {}
108
+ const profiles = []
109
+ const encodedProfiles = {}
98
110
 
99
111
  try {
112
+ // collect profiles synchronously so that profilers can be safely stopped asynchronously
100
113
  for (const profiler of this._config.profilers) {
101
114
  const profile = profiler.profile()
102
115
  if (!profile) continue
116
+ profiles.push({ profiler, profile })
117
+ }
103
118
 
104
- profiles[profiler.type] = await profiler.encode(profile)
119
+ // encode and export asynchronously
120
+ for (const { profiler, profile } of profiles) {
121
+ encodedProfiles[profiler.type] = await profiler.encode(profile)
105
122
  this._logger.debug(() => {
106
123
  const profileJson = JSON.stringify(profile, (key, value) => {
107
124
  return typeof value === 'bigint' ? value.toString() : value
@@ -111,11 +128,11 @@ class Profiler extends EventEmitter {
111
128
  }
112
129
 
113
130
  this._capture(this._timeoutInterval)
114
- await this._submit(profiles, start, end)
131
+ await this._submit(encodedProfiles, start, end)
115
132
  this._logger.debug('Submitted profiles')
116
133
  } catch (err) {
117
134
  this._logger.error(err)
118
- this.stop()
135
+ this._stop()
119
136
  }
120
137
  }
121
138
 
@@ -35,6 +35,7 @@ class NativeWallProfiler {
35
35
  stop () {
36
36
  if (!this._stop) return
37
37
  this._stop()
38
+ this._stop = undefined
38
39
  }
39
40
 
40
41
  _record () {
@@ -8,6 +8,7 @@ const { setStartupLogPluginManager } = require('./startup-log')
8
8
  const telemetry = require('./telemetry')
9
9
  const PluginManager = require('./plugin_manager')
10
10
  const remoteConfig = require('./appsec/remote_config')
11
+ const AppsecSdk = require('./appsec/sdk')
11
12
 
12
13
  class Tracer extends NoopProxy {
13
14
  constructor () {
@@ -50,6 +51,7 @@ class Tracer extends NoopProxy {
50
51
  }
51
52
 
52
53
  this._tracer = new DatadogTracer(config)
54
+ this.appsec = new AppsecSdk(this._tracer, config)
53
55
 
54
56
  if (config.iast.enabled) {
55
57
  require('./appsec/iast').enable(config, this._tracer)
@@ -17,7 +17,7 @@ class SpanProcessor {
17
17
  this._killAll = false
18
18
 
19
19
  this._stats = new SpanStatsProcessor(config)
20
- this._spanSampler = new SpanSampler(config)
20
+ this._spanSampler = new SpanSampler(config.sampler)
21
21
  }
22
22
 
23
23
  process (span) {
@@ -2,76 +2,92 @@
2
2
  const { globMatch } = require('../src/util')
3
3
  const { USER_KEEP, AUTO_KEEP } = require('../../../ext').priority
4
4
  const RateLimiter = require('./rate_limiter')
5
+ const Sampler = require('./sampler')
5
6
 
6
- class SpanSampler {
7
- constructor ({ spanSamplingRules = [] }) {
8
- this._rules = spanSamplingRules
9
- this._limiters = {}
7
+ class SpanSamplingRule {
8
+ constructor ({ service, name, sampleRate = 1.0, maxPerSecond } = {}) {
9
+ this.service = service
10
+ this.name = name
11
+
12
+ this._sampler = new Sampler(sampleRate)
13
+ this._limiter = undefined
14
+
15
+ if (Number.isFinite(maxPerSecond)) {
16
+ this._limiter = new RateLimiter(maxPerSecond)
17
+ }
10
18
  }
11
19
 
12
- sample (spanContext) {
13
- const decision = spanContext._sampling.priority
14
- if (decision === USER_KEEP || decision === AUTO_KEEP) return
20
+ get sampleRate () {
21
+ return this._sampler.rate()
22
+ }
15
23
 
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
24
+ get maxPerSecond () {
25
+ return this._limiter && this._limiter._rateLimit
26
+ }
22
27
 
23
- const sampleRate = getSampleRate(rule.sampleRate)
24
- const maxPerSecond = getMaxPerSecond(rule.maxPerSecond)
25
- const sampled = sample(sampleRate)
26
- if (!sampled) continue
28
+ static from (config) {
29
+ return new SpanSamplingRule(config)
30
+ }
27
31
 
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
- }
32
+ match (service, name) {
33
+ if (this.service && !globMatch(this.service, service)) {
34
+ return false
36
35
  }
37
- }
38
- }
39
36
 
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
37
+ if (this.name && !globMatch(this.name, name)) {
38
+ return false
46
39
  }
40
+
41
+ return true
47
42
  }
48
- }
49
43
 
50
- function getLimiter (list, key, maxPerSecond) {
51
- if (typeof list[key] === 'undefined') {
52
- list[key] = new RateLimiter(maxPerSecond)
44
+ sample () {
45
+ if (!this._sampler.isSampled()) {
46
+ return false
47
+ }
48
+
49
+ if (this._limiter) {
50
+ return this._limiter.isAllowed()
51
+ }
52
+
53
+ return true
53
54
  }
54
- return list[key]
55
55
  }
56
56
 
57
- function sample (sampleRate) {
58
- return Math.random() < sampleRate
59
- }
57
+ class SpanSampler {
58
+ constructor ({ spanSamplingRules = [] } = {}) {
59
+ this._rules = spanSamplingRules.map(SpanSamplingRule.from)
60
+ }
60
61
 
61
- function getService (service) {
62
- return service || '*'
63
- }
62
+ findRule (service, name) {
63
+ for (const rule of this._rules) {
64
+ if (rule.match(service, name)) {
65
+ return rule
66
+ }
67
+ }
68
+ }
64
69
 
65
- function getName (name) {
66
- return name || '*'
67
- }
70
+ sample (spanContext) {
71
+ const decision = spanContext._sampling.priority
72
+ if (decision === USER_KEEP || decision === AUTO_KEEP) return
68
73
 
69
- function getSampleRate (sampleRate) {
70
- return sampleRate || 1.0
71
- }
74
+ const { started } = spanContext._trace
75
+ for (const span of started) {
76
+ const tags = span.context()._tags || {}
77
+ const name = span._name
78
+ const service = tags.service ||
79
+ tags['service.name'] ||
80
+ span.tracer()._service
72
81
 
73
- function getMaxPerSecond (maxPerSecond) {
74
- return maxPerSecond || Infinity
82
+ const rule = this.findRule(service, name)
83
+ if (rule && rule.sample()) {
84
+ span.context()._sampling.spanSampling = {
85
+ sampleRate: rule.sampleRate,
86
+ maxPerSecond: rule.maxPerSecond
87
+ }
88
+ }
89
+ }
90
+ }
75
91
  }
76
92
 
77
93
  module.exports = SpanSampler
@@ -1,14 +1,11 @@
1
1
  'use strict'
2
2
 
3
- const mainLogger = require('./log')
3
+ const { info, warn } = require('./log/writer')
4
4
 
5
5
  const os = require('os')
6
6
  const { inspect } = require('util')
7
7
  const tracerVersion = require('../../../package.json').version
8
8
 
9
- const logger = Object.create(mainLogger)
10
- logger._enabled = true
11
-
12
9
  let config
13
10
  let pluginManager
14
11
  let samplingRules = []
@@ -89,9 +86,9 @@ function startupLog ({ agentError } = {}) {
89
86
  // out.service_mapping
90
87
  // out.service_mapping_error
91
88
 
92
- logger.info('DATADOG TRACER CONFIGURATION - ' + out)
89
+ info('DATADOG TRACER CONFIGURATION - ' + out)
93
90
  if (agentError) {
94
- logger.warn('DATADOG TRACER DIAGNOSTIC - Agent Error: ' + agentError.message)
91
+ warn('DATADOG TRACER DIAGNOSTIC - Agent Error: ' + agentError.message)
95
92
  }
96
93
 
97
94
  config = undefined