dd-trace 3.42.0 → 3.44.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 (68) hide show
  1. package/index.d.ts +5 -0
  2. package/package.json +4 -4
  3. package/packages/datadog-instrumentations/src/apollo-server-core.js +41 -0
  4. package/packages/datadog-instrumentations/src/apollo-server.js +83 -0
  5. package/packages/datadog-instrumentations/src/child-process.js +4 -5
  6. package/packages/datadog-instrumentations/src/couchbase.js +5 -4
  7. package/packages/datadog-instrumentations/src/crypto.js +2 -1
  8. package/packages/datadog-instrumentations/src/dns.js +2 -1
  9. package/packages/datadog-instrumentations/src/graphql.js +18 -4
  10. package/packages/datadog-instrumentations/src/helpers/hooks.js +9 -2
  11. package/packages/datadog-instrumentations/src/helpers/instrument.js +8 -3
  12. package/packages/datadog-instrumentations/src/helpers/register.js +18 -2
  13. package/packages/datadog-instrumentations/src/http/client.js +4 -16
  14. package/packages/datadog-instrumentations/src/http/server.js +7 -4
  15. package/packages/datadog-instrumentations/src/http2/client.js +3 -1
  16. package/packages/datadog-instrumentations/src/http2/server.js +3 -1
  17. package/packages/datadog-instrumentations/src/jest.js +1 -1
  18. package/packages/datadog-instrumentations/src/net.js +10 -2
  19. package/packages/datadog-instrumentations/src/next.js +15 -5
  20. package/packages/datadog-instrumentations/src/rhea.js +15 -9
  21. package/packages/datadog-plugin-cucumber/src/index.js +34 -2
  22. package/packages/datadog-plugin-cypress/src/plugin.js +60 -8
  23. package/packages/datadog-plugin-graphql/src/resolve.js +26 -18
  24. package/packages/datadog-plugin-http/src/client.js +1 -1
  25. package/packages/datadog-plugin-jest/src/index.js +38 -4
  26. package/packages/datadog-plugin-mocha/src/index.js +32 -1
  27. package/packages/datadog-plugin-next/src/index.js +32 -6
  28. package/packages/datadog-plugin-playwright/src/index.js +17 -1
  29. package/packages/dd-trace/src/appsec/activation.js +29 -0
  30. package/packages/dd-trace/src/appsec/addresses.js +1 -0
  31. package/packages/dd-trace/src/appsec/api_security_sampler.js +48 -0
  32. package/packages/dd-trace/src/appsec/blocked_templates.js +4 -1
  33. package/packages/dd-trace/src/appsec/blocking.js +95 -43
  34. package/packages/dd-trace/src/appsec/channels.js +4 -1
  35. package/packages/dd-trace/src/appsec/graphql.js +146 -0
  36. package/packages/dd-trace/src/appsec/index.js +29 -40
  37. package/packages/dd-trace/src/appsec/remote_config/capabilities.js +6 -1
  38. package/packages/dd-trace/src/appsec/remote_config/index.js +40 -15
  39. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
  40. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +25 -13
  41. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +30 -1
  42. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +30 -1
  43. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +36 -4
  44. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-itr-configuration.js +18 -1
  45. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +26 -1
  46. package/packages/dd-trace/src/ci-visibility/telemetry.js +130 -0
  47. package/packages/dd-trace/src/config.js +104 -58
  48. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +14 -1
  49. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +14 -0
  50. package/packages/dd-trace/src/exporters/common/agent-info-exporter.js +4 -0
  51. package/packages/dd-trace/src/exporters/common/form-data.js +4 -0
  52. package/packages/dd-trace/src/opentracing/tracer.js +2 -2
  53. package/packages/dd-trace/src/plugins/ci_plugin.js +44 -8
  54. package/packages/dd-trace/src/plugins/index.js +5 -0
  55. package/packages/dd-trace/src/plugins/util/exec.js +23 -2
  56. package/packages/dd-trace/src/plugins/util/git.js +94 -19
  57. package/packages/dd-trace/src/plugins/util/user-provided-git.js +3 -2
  58. package/packages/dd-trace/src/priority_sampler.js +30 -38
  59. package/packages/dd-trace/src/profiling/exporters/agent.js +1 -0
  60. package/packages/dd-trace/src/profiling/profiler.js +7 -6
  61. package/packages/dd-trace/src/profiling/profilers/events.js +18 -13
  62. package/packages/dd-trace/src/profiling/profilers/shared.js +34 -4
  63. package/packages/dd-trace/src/profiling/profilers/space.js +2 -1
  64. package/packages/dd-trace/src/profiling/profilers/wall.js +17 -12
  65. package/packages/dd-trace/src/proxy.js +4 -0
  66. package/packages/dd-trace/src/sampling_rule.js +130 -0
  67. package/packages/dd-trace/src/span_sampler.js +6 -64
  68. package/packages/dd-trace/src/telemetry/index.js +43 -5
@@ -1,38 +1,63 @@
1
1
  'use strict'
2
2
 
3
+ const Activation = require('../activation')
4
+
3
5
  const RemoteConfigManager = require('./manager')
4
6
  const RemoteConfigCapabilities = require('./capabilities')
7
+ const apiSecuritySampler = require('../api_security_sampler')
5
8
 
6
9
  let rc
7
10
 
8
11
  function enable (config) {
9
12
  rc = new RemoteConfigManager(config)
13
+ rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_CUSTOM_TAGS, true)
14
+ rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_HTTP_HEADER_TAGS, true)
15
+ rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_LOGS_INJECTION, true)
16
+ rc.updateCapabilities(RemoteConfigCapabilities.APM_TRACING_SAMPLE_RATE, true)
17
+
18
+ const activation = Activation.fromConfig(config)
10
19
 
11
- if (config.appsec.enabled === undefined) { // only activate ASM_FEATURES when conf is not set locally
12
- rc.updateCapabilities(RemoteConfigCapabilities.ASM_ACTIVATION, true)
20
+ if (activation !== Activation.DISABLED) {
21
+ if (activation === Activation.ONECLICK) {
22
+ rc.updateCapabilities(RemoteConfigCapabilities.ASM_ACTIVATION, true)
23
+ }
13
24
 
14
- rc.on('ASM_FEATURES', (action, conf) => {
15
- if (conf && conf.asm && typeof conf.asm.enabled === 'boolean') {
16
- let shouldEnable
25
+ if (config.appsec.apiSecurity?.enabled) {
26
+ rc.updateCapabilities(RemoteConfigCapabilities.ASM_API_SECURITY_SAMPLE_RATE, true)
27
+ }
17
28
 
18
- if (action === 'apply' || action === 'modify') {
19
- shouldEnable = conf.asm.enabled // take control
20
- } else {
21
- shouldEnable = config.appsec.enabled // give back control to local config
22
- }
29
+ rc.on('ASM_FEATURES', (action, rcConfig) => {
30
+ if (!rcConfig) return
23
31
 
24
- if (shouldEnable) {
25
- require('..').enable(config)
26
- } else {
27
- require('..').disable()
28
- }
32
+ if (activation === Activation.ONECLICK) {
33
+ enableOrDisableAppsec(action, rcConfig, config)
29
34
  }
35
+
36
+ apiSecuritySampler.setRequestSampling(rcConfig.api_security?.request_sample_rate)
30
37
  })
31
38
  }
32
39
 
33
40
  return rc
34
41
  }
35
42
 
43
+ function enableOrDisableAppsec (action, rcConfig, config) {
44
+ if (typeof rcConfig.asm?.enabled === 'boolean') {
45
+ let shouldEnable
46
+
47
+ if (action === 'apply' || action === 'modify') {
48
+ shouldEnable = rcConfig.asm.enabled // take control
49
+ } else {
50
+ shouldEnable = config.appsec.enabled // give back control to local config
51
+ }
52
+
53
+ if (shouldEnable) {
54
+ require('..').enable(config)
55
+ } else {
56
+ require('..').disable()
57
+ }
58
+ }
59
+ }
60
+
36
61
  function enableWafUpdate (appsecConfig) {
37
62
  if (rc && appsecConfig && !appsecConfig.customRulesProvided) {
38
63
  // dirty require to make startup faster for serverless
@@ -9,7 +9,7 @@ const { setUserTags } = require('./set_user')
9
9
  const log = require('../../log')
10
10
 
11
11
  function isUserBlocked (user) {
12
- const actions = waf.run({ [USER_ID]: user.id })
12
+ const actions = waf.run({ persistent: { [USER_ID]: user.id } })
13
13
 
14
14
  if (!actions) return false
15
15
 
@@ -18,30 +18,42 @@ class WAFContextWrapper {
18
18
  this.addressesToSkip = new Set()
19
19
  }
20
20
 
21
- run (params) {
21
+ run ({ persistent, ephemeral }) {
22
+ const payload = {}
23
+ let payloadHasData = false
22
24
  const inputs = {}
23
- let someInputAdded = false
24
25
  const newAddressesToSkip = new Set(this.addressesToSkip)
25
26
 
26
- // TODO: possible optimizaion: only send params that haven't already been sent with same value to this wafContext
27
- for (const key of Object.keys(params)) {
28
- // TODO: requiredAddresses is no longer used due to processor addresses are not included in the list. Check on
29
- // future versions when the actual addresses are included in the 'loaded' section inside diagnostics.
30
- if (!this.addressesToSkip.has(key)) {
31
- inputs[key] = params[key]
32
- if (preventDuplicateAddresses.has(key)) {
33
- newAddressesToSkip.add(key)
27
+ if (persistent && typeof persistent === 'object') {
28
+ // TODO: possible optimization: only send params that haven't already been sent with same value to this wafContext
29
+ for (const key of Object.keys(persistent)) {
30
+ // TODO: requiredAddresses is no longer used due to processor addresses are not included in the list. Check on
31
+ // future versions when the actual addresses are included in the 'loaded' section inside diagnostics.
32
+ if (!this.addressesToSkip.has(key)) {
33
+ inputs[key] = persistent[key]
34
+ if (preventDuplicateAddresses.has(key)) {
35
+ newAddressesToSkip.add(key)
36
+ }
34
37
  }
35
- someInputAdded = true
36
38
  }
37
39
  }
38
40
 
39
- if (!someInputAdded) return
41
+ if (Object.keys(inputs).length) {
42
+ payload['persistent'] = inputs
43
+ payloadHasData = true
44
+ }
45
+
46
+ if (ephemeral && Object.keys(ephemeral).length) {
47
+ payload['ephemeral'] = ephemeral
48
+ payloadHasData = true
49
+ }
50
+
51
+ if (!payloadHasData) return
40
52
 
41
53
  try {
42
54
  const start = process.hrtime.bigint()
43
55
 
44
- const result = this.ddwafContext.run(inputs, this.wafTimeout)
56
+ const result = this.ddwafContext.run(payload, this.wafTimeout)
45
57
 
46
58
  const end = process.hrtime.bigint()
47
59
 
@@ -5,6 +5,16 @@ const { safeJSONStringify } = require('../../../exporters/common/util')
5
5
 
6
6
  const { CoverageCIVisibilityEncoder } = require('../../../encode/coverage-ci-visibility')
7
7
  const BaseWriter = require('../../../exporters/common/writer')
8
+ const {
9
+ incrementCountMetric,
10
+ distributionMetric,
11
+ TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS,
12
+ TELEMETRY_ENDPOINT_PAYLOAD_BYTES,
13
+ TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_MS,
14
+ TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_ERRORS,
15
+ TELEMETRY_ENDPOINT_PAYLOAD_DROPPED,
16
+ getErrorTypeFromStatusCode
17
+ } = require('../../../ci-visibility/telemetry')
8
18
 
9
19
  class Writer extends BaseWriter {
10
20
  constructor ({ url, evpProxyPrefix = '' }) {
@@ -34,8 +44,27 @@ class Writer extends BaseWriter {
34
44
 
35
45
  log.debug(() => `Request to the intake: ${safeJSONStringify(options)}`)
36
46
 
37
- request(form, options, (err, res) => {
47
+ const startRequestTime = Date.now()
48
+
49
+ incrementCountMetric(TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS, { endpoint: 'code_coverage' })
50
+ distributionMetric(TELEMETRY_ENDPOINT_PAYLOAD_BYTES, { endpoint: 'code_coverage' }, form.size())
51
+
52
+ request(form, options, (err, res, statusCode) => {
53
+ distributionMetric(
54
+ TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_MS,
55
+ { endpoint: 'code_coverage' },
56
+ Date.now() - startRequestTime
57
+ )
38
58
  if (err) {
59
+ const errorType = getErrorTypeFromStatusCode(statusCode)
60
+ incrementCountMetric(
61
+ TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_ERRORS,
62
+ { endpoint: 'code_coverage', errorType }
63
+ )
64
+ incrementCountMetric(
65
+ TELEMETRY_ENDPOINT_PAYLOAD_DROPPED,
66
+ { endpoint: 'code_coverage' }
67
+ )
39
68
  log.error(err)
40
69
  done()
41
70
  return
@@ -5,6 +5,16 @@ const log = require('../../../log')
5
5
 
6
6
  const { AgentlessCiVisibilityEncoder } = require('../../../encode/agentless-ci-visibility')
7
7
  const BaseWriter = require('../../../exporters/common/writer')
8
+ const {
9
+ incrementCountMetric,
10
+ distributionMetric,
11
+ TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS,
12
+ TELEMETRY_ENDPOINT_PAYLOAD_BYTES,
13
+ TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_MS,
14
+ TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_ERRORS,
15
+ TELEMETRY_ENDPOINT_PAYLOAD_DROPPED,
16
+ getErrorTypeFromStatusCode
17
+ } = require('../../../ci-visibility/telemetry')
8
18
 
9
19
  class Writer extends BaseWriter {
10
20
  constructor ({ url, tags, evpProxyPrefix = '' }) {
@@ -35,8 +45,27 @@ class Writer extends BaseWriter {
35
45
 
36
46
  log.debug(() => `Request to the intake: ${safeJSONStringify(options)}`)
37
47
 
38
- request(data, options, (err, res) => {
48
+ const startRequestTime = Date.now()
49
+
50
+ incrementCountMetric(TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS, { endpoint: 'test_cycle' })
51
+ distributionMetric(TELEMETRY_ENDPOINT_PAYLOAD_BYTES, { endpoint: 'test_cycle' }, data.length)
52
+
53
+ request(data, options, (err, res, statusCode) => {
54
+ distributionMetric(
55
+ TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_MS,
56
+ { endpoint: 'test_cycle' },
57
+ Date.now() - startRequestTime
58
+ )
39
59
  if (err) {
60
+ const errorType = getErrorTypeFromStatusCode(statusCode)
61
+ incrementCountMetric(
62
+ TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_ERRORS,
63
+ { endpoint: 'test_cycle', errorType }
64
+ )
65
+ incrementCountMetric(
66
+ TELEMETRY_ENDPOINT_PAYLOAD_DROPPED,
67
+ { endpoint: 'test_cycle' }
68
+ )
40
69
  log.error(err)
41
70
  done()
42
71
  return
@@ -15,6 +15,20 @@ const {
15
15
  unshallowRepository
16
16
  } = require('../../../plugins/util/git')
17
17
 
18
+ const {
19
+ incrementCountMetric,
20
+ distributionMetric,
21
+ TELEMETRY_GIT_REQUESTS_SEARCH_COMMITS,
22
+ TELEMETRY_GIT_REQUESTS_SEARCH_COMMITS_MS,
23
+ TELEMETRY_GIT_REQUESTS_SEARCH_COMMITS_ERRORS,
24
+ TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES_NUM,
25
+ TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES,
26
+ TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES_MS,
27
+ TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES_ERRORS,
28
+ TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES_BYTES,
29
+ getErrorTypeFromStatusCode
30
+ } = require('../../../ci-visibility/telemetry')
31
+
18
32
  const isValidSha1 = (sha) => /^[0-9a-f]{40}$/.test(sha)
19
33
  const isValidSha256 = (sha) => /^[0-9a-f]{64}$/.test(sha)
20
34
 
@@ -74,8 +88,13 @@ function getCommitsToUpload ({ url, repositoryUrl, latestCommits, isEvpProxy },
74
88
  }))
75
89
  })
76
90
 
77
- request(localCommitData, options, (err, response) => {
91
+ incrementCountMetric(TELEMETRY_GIT_REQUESTS_SEARCH_COMMITS)
92
+ const startTime = Date.now()
93
+ request(localCommitData, options, (err, response, statusCode) => {
94
+ distributionMetric(TELEMETRY_GIT_REQUESTS_SEARCH_COMMITS_MS, {}, Date.now() - startTime)
78
95
  if (err) {
96
+ const errorType = getErrorTypeFromStatusCode(statusCode)
97
+ incrementCountMetric(TELEMETRY_GIT_REQUESTS_SEARCH_COMMITS_ERRORS, { errorType })
79
98
  const error = new Error(`Error fetching commits to exclude: ${err.message}`)
80
99
  return callback(error)
81
100
  }
@@ -83,6 +102,7 @@ function getCommitsToUpload ({ url, repositoryUrl, latestCommits, isEvpProxy },
83
102
  try {
84
103
  alreadySeenCommits = validateCommits(JSON.parse(response).data)
85
104
  } catch (e) {
105
+ incrementCountMetric(TELEMETRY_GIT_REQUESTS_SEARCH_COMMITS_ERRORS, { errorType: 'network' })
86
106
  return callback(new Error(`Can't parse commits to exclude response: ${e.message}`))
87
107
  }
88
108
  log.debug(`There are ${alreadySeenCommits.length} commits to exclude.`)
@@ -147,12 +167,20 @@ function uploadPackFile ({ url, isEvpProxy, packFileToUpload, repositoryUrl, hea
147
167
  delete options.headers['dd-api-key']
148
168
  }
149
169
 
170
+ incrementCountMetric(TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES)
171
+
172
+ const uploadSize = form.size()
173
+
174
+ const startTime = Date.now()
150
175
  request(form, options, (err, _, statusCode) => {
176
+ distributionMetric(TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES_MS, {}, Date.now() - startTime)
151
177
  if (err) {
178
+ const errorType = getErrorTypeFromStatusCode(statusCode)
179
+ incrementCountMetric(TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES_ERRORS, { errorType })
152
180
  const error = new Error(`Could not upload packfiles: status code ${statusCode}: ${err.message}`)
153
- return callback(error)
181
+ return callback(error, uploadSize)
154
182
  }
155
- callback(null)
183
+ callback(null, uploadSize)
156
184
  })
157
185
  }
158
186
 
@@ -173,10 +201,14 @@ function generateAndUploadPackFiles ({
173
201
  return callback(new Error('Failed to generate packfiles'))
174
202
  }
175
203
 
204
+ distributionMetric(TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES_NUM, {}, packFilesToUpload.length)
176
205
  let packFileIndex = 0
206
+ let totalUploadedBytes = 0
177
207
  // This uploads packfiles sequentially
178
- const uploadPackFileCallback = (err) => {
208
+ const uploadPackFileCallback = (err, byteLength) => {
209
+ totalUploadedBytes += byteLength
179
210
  if (err || packFileIndex === packFilesToUpload.length) {
211
+ distributionMetric(TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES_BYTES, {}, totalUploadedBytes)
180
212
  return callback(err)
181
213
  }
182
214
  return uploadPackFile(
@@ -1,6 +1,15 @@
1
1
  const request = require('../../exporters/common/request')
2
2
  const id = require('../../id')
3
3
  const log = require('../../log')
4
+ const {
5
+ incrementCountMetric,
6
+ distributionMetric,
7
+ TELEMETRY_GIT_REQUESTS_SETTINGS,
8
+ TELEMETRY_GIT_REQUESTS_SETTINGS_MS,
9
+ TELEMETRY_GIT_REQUESTS_SETTINGS_ERRORS,
10
+ TELEMETRY_GIT_REQUESTS_SETTINGS_RESPONSE,
11
+ getErrorTypeFromStatusCode
12
+ } = require('../../ci-visibility/telemetry')
4
13
 
5
14
  function getItrConfiguration ({
6
15
  url,
@@ -62,8 +71,14 @@ function getItrConfiguration ({
62
71
  }
63
72
  })
64
73
 
65
- request(data, options, (err, res) => {
74
+ incrementCountMetric(TELEMETRY_GIT_REQUESTS_SETTINGS)
75
+
76
+ const startTime = Date.now()
77
+ request(data, options, (err, res, statusCode) => {
78
+ distributionMetric(TELEMETRY_GIT_REQUESTS_SETTINGS_MS, {}, Date.now() - startTime)
66
79
  if (err) {
80
+ const errorType = getErrorTypeFromStatusCode(statusCode)
81
+ incrementCountMetric(TELEMETRY_GIT_REQUESTS_SETTINGS_ERRORS, { errorType })
67
82
  done(err)
68
83
  } else {
69
84
  try {
@@ -91,6 +106,8 @@ function getItrConfiguration ({
91
106
  log.debug(() => 'Dangerously set test skipping to true')
92
107
  }
93
108
 
109
+ incrementCountMetric(TELEMETRY_GIT_REQUESTS_SETTINGS_RESPONSE, settings)
110
+
94
111
  done(null, settings)
95
112
  } catch (err) {
96
113
  done(err)
@@ -1,5 +1,16 @@
1
1
  const request = require('../../exporters/common/request')
2
2
  const log = require('../../log')
3
+ const {
4
+ incrementCountMetric,
5
+ distributionMetric,
6
+ TELEMETRY_ITR_SKIPPABLE_TESTS,
7
+ TELEMETRY_ITR_SKIPPABLE_TESTS_MS,
8
+ TELEMETRY_ITR_SKIPPABLE_TESTS_ERRORS,
9
+ TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_SUITES,
10
+ TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_TESTS,
11
+ TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_BYTES,
12
+ getErrorTypeFromStatusCode
13
+ } = require('../../ci-visibility/telemetry')
3
14
 
4
15
  function getSkippableSuites ({
5
16
  url,
@@ -59,8 +70,15 @@ function getSkippableSuites ({
59
70
  }
60
71
  })
61
72
 
62
- request(data, options, (err, res) => {
73
+ incrementCountMetric(TELEMETRY_ITR_SKIPPABLE_TESTS)
74
+
75
+ const startTime = Date.now()
76
+
77
+ request(data, options, (err, res, statusCode) => {
78
+ distributionMetric(TELEMETRY_ITR_SKIPPABLE_TESTS_MS, {}, Date.now() - startTime)
63
79
  if (err) {
80
+ const errorType = getErrorTypeFromStatusCode(statusCode)
81
+ incrementCountMetric(TELEMETRY_ITR_SKIPPABLE_TESTS_ERRORS, { errorType })
64
82
  done(err)
65
83
  } else {
66
84
  let skippableSuites = []
@@ -74,6 +92,13 @@ function getSkippableSuites ({
74
92
  }
75
93
  return { suite, name }
76
94
  })
95
+ incrementCountMetric(
96
+ testLevel === 'test'
97
+ ? TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_TESTS : TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_SUITES,
98
+ {},
99
+ skippableSuites.length
100
+ )
101
+ distributionMetric(TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_BYTES, {}, res.length)
77
102
  log.debug(() => `Number of received skippable ${testLevel}s: ${skippableSuites.length}`)
78
103
  done(null, skippableSuites)
79
104
  } catch (err) {
@@ -0,0 +1,130 @@
1
+ const telemetryMetrics = require('../telemetry/metrics')
2
+
3
+ const ciVisibilityMetrics = telemetryMetrics.manager.namespace('civisibility')
4
+
5
+ const formattedTags = {
6
+ testLevel: 'event_type',
7
+ testFramework: 'test_framework',
8
+ errorType: 'error_type',
9
+ exitCode: 'exit_code',
10
+ isCodeCoverageEnabled: 'coverage_enabled',
11
+ isSuitesSkippingEnabled: 'itrskip_enabled',
12
+ hasCodeOwners: 'has_code_owners',
13
+ isUnsupportedCIProvider: 'is_unsupported_ci'
14
+ }
15
+
16
+ // Transform tags dictionary to array of strings.
17
+ // If tag value is true, then only tag key is added to the array.
18
+ function formatMetricTags (tagsDictionary) {
19
+ return Object.keys(tagsDictionary).reduce((acc, tagKey) => {
20
+ const formattedTagKey = formattedTags[tagKey] || tagKey
21
+ if (tagsDictionary[tagKey] === true) {
22
+ acc.push(formattedTagKey)
23
+ } else if (tagsDictionary[tagKey] !== undefined && tagsDictionary[tagKey] !== null) {
24
+ acc.push(`${formattedTagKey}:${tagsDictionary[tagKey]}`)
25
+ }
26
+ return acc
27
+ }, [])
28
+ }
29
+
30
+ function incrementCountMetric (name, tags = {}, value = 1) {
31
+ ciVisibilityMetrics.count(name, formatMetricTags(tags)).inc(value)
32
+ }
33
+
34
+ function distributionMetric (name, tags, measure) {
35
+ ciVisibilityMetrics.distribution(name, formatMetricTags(tags)).track(measure)
36
+ }
37
+
38
+ // CI Visibility telemetry events
39
+ const TELEMETRY_EVENT_CREATED = 'event_created'
40
+ const TELEMETRY_EVENT_FINISHED = 'event_finished'
41
+ const TELEMETRY_CODE_COVERAGE_STARTED = 'code_coverage_started'
42
+ const TELEMETRY_CODE_COVERAGE_FINISHED = 'code_coverage_finished'
43
+ const TELEMETRY_ITR_SKIPPED = 'itr_skipped'
44
+ const TELEMETRY_ITR_UNSKIPPABLE = 'itr_unskippable'
45
+ const TELEMETRY_ITR_FORCED_TO_RUN = 'itr_forced_run'
46
+ const TELEMETRY_CODE_COVERAGE_EMPTY = 'code_coverage.is_empty'
47
+ const TELEMETRY_CODE_COVERAGE_NUM_FILES = 'code_coverage.files'
48
+ const TELEMETRY_EVENTS_ENQUEUED_FOR_SERIALIZATION = 'events_enqueued_for_serialization'
49
+ const TELEMETRY_ENDPOINT_PAYLOAD_SERIALIZATION_MS = 'endpoint_payload.events_serialization_ms'
50
+ const TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS = 'endpoint_payload.requests'
51
+ const TELEMETRY_ENDPOINT_PAYLOAD_BYTES = 'endpoint_payload.bytes'
52
+ const TELEMETRY_ENDPOINT_PAYLOAD_EVENTS_COUNT = 'endpoint_payload.events_count'
53
+ const TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_MS = 'endpoint_payload.requests_ms'
54
+ const TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_ERRORS = 'endpoint_payload.requests_errors'
55
+ const TELEMETRY_ENDPOINT_PAYLOAD_DROPPED = 'endpoint_payload.dropped'
56
+ const TELEMETRY_GIT_COMMAND = 'git.command'
57
+ const TELEMETRY_GIT_COMMAND_MS = 'git.command_ms'
58
+ const TELEMETRY_GIT_COMMAND_ERRORS = 'git.command_errors'
59
+ const TELEMETRY_GIT_REQUESTS_SEARCH_COMMITS = 'git_requests.search_commits'
60
+ const TELEMETRY_GIT_REQUESTS_SEARCH_COMMITS_MS = 'git_requests.search_commits_ms'
61
+ const TELEMETRY_GIT_REQUESTS_SEARCH_COMMITS_ERRORS = 'git_requests.search_commits_errors'
62
+ const TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES = 'git_requests.objects_pack'
63
+ const TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES_MS = 'git_requests.objects_pack_ms'
64
+ const TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES_ERRORS = 'git_requests.objects_pack_errors'
65
+ const TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES_NUM = 'git_requests.objects_pack_files'
66
+ const TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES_BYTES = 'git_requests.objects_pack_bytes'
67
+ const TELEMETRY_GIT_REQUESTS_SETTINGS = 'git_requests.settings'
68
+ const TELEMETRY_GIT_REQUESTS_SETTINGS_MS = 'git_requests.settings_ms'
69
+ const TELEMETRY_GIT_REQUESTS_SETTINGS_ERRORS = 'git_requests.settings_errors'
70
+ const TELEMETRY_GIT_REQUESTS_SETTINGS_RESPONSE = 'git_requests.settings_response'
71
+ const TELEMETRY_ITR_SKIPPABLE_TESTS = 'itr_skippable_tests.request'
72
+ const TELEMETRY_ITR_SKIPPABLE_TESTS_MS = 'itr_skippable_tests.request_ms'
73
+ const TELEMETRY_ITR_SKIPPABLE_TESTS_ERRORS = 'itr_skippable_tests.request_errors'
74
+ const TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_SUITES = 'itr_skippable_tests.response_suites'
75
+ const TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_TESTS = 'itr_skippable_tests.response_tests'
76
+ const TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_BYTES = 'itr_skippable_tests.response_bytes'
77
+
78
+ function getErrorTypeFromStatusCode (statusCode) {
79
+ if (statusCode >= 400 && statusCode < 500) {
80
+ return 'status_code_4xx_response'
81
+ }
82
+ if (statusCode >= 500) {
83
+ return 'status_code_5xx_response'
84
+ }
85
+ return 'network'
86
+ }
87
+
88
+ module.exports = {
89
+ incrementCountMetric,
90
+ distributionMetric,
91
+ TELEMETRY_EVENT_CREATED,
92
+ TELEMETRY_EVENT_FINISHED,
93
+ TELEMETRY_CODE_COVERAGE_STARTED,
94
+ TELEMETRY_CODE_COVERAGE_FINISHED,
95
+ TELEMETRY_ITR_SKIPPED,
96
+ TELEMETRY_ITR_UNSKIPPABLE,
97
+ TELEMETRY_ITR_FORCED_TO_RUN,
98
+ TELEMETRY_CODE_COVERAGE_EMPTY,
99
+ TELEMETRY_CODE_COVERAGE_NUM_FILES,
100
+ TELEMETRY_EVENTS_ENQUEUED_FOR_SERIALIZATION,
101
+ TELEMETRY_ENDPOINT_PAYLOAD_SERIALIZATION_MS,
102
+ TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS,
103
+ TELEMETRY_ENDPOINT_PAYLOAD_BYTES,
104
+ TELEMETRY_ENDPOINT_PAYLOAD_EVENTS_COUNT,
105
+ TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_MS,
106
+ TELEMETRY_ENDPOINT_PAYLOAD_REQUESTS_ERRORS,
107
+ TELEMETRY_ENDPOINT_PAYLOAD_DROPPED,
108
+ TELEMETRY_GIT_COMMAND,
109
+ TELEMETRY_GIT_COMMAND_MS,
110
+ TELEMETRY_GIT_COMMAND_ERRORS,
111
+ TELEMETRY_GIT_REQUESTS_SEARCH_COMMITS,
112
+ TELEMETRY_GIT_REQUESTS_SEARCH_COMMITS_MS,
113
+ TELEMETRY_GIT_REQUESTS_SEARCH_COMMITS_ERRORS,
114
+ TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES_NUM,
115
+ TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES_BYTES,
116
+ TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES,
117
+ TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES_MS,
118
+ TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES_ERRORS,
119
+ TELEMETRY_GIT_REQUESTS_SETTINGS,
120
+ TELEMETRY_GIT_REQUESTS_SETTINGS_MS,
121
+ TELEMETRY_GIT_REQUESTS_SETTINGS_ERRORS,
122
+ TELEMETRY_GIT_REQUESTS_SETTINGS_RESPONSE,
123
+ TELEMETRY_ITR_SKIPPABLE_TESTS,
124
+ TELEMETRY_ITR_SKIPPABLE_TESTS_MS,
125
+ TELEMETRY_ITR_SKIPPABLE_TESTS_ERRORS,
126
+ TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_SUITES,
127
+ TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_TESTS,
128
+ TELEMETRY_ITR_SKIPPABLE_TESTS_RESPONSE_BYTES,
129
+ getErrorTypeFromStatusCode
130
+ }