dd-trace 3.0.0-pre.1 → 3.1.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 (71) hide show
  1. package/LICENSE-3rdparty.csv +2 -2
  2. package/MIGRATING.md +119 -0
  3. package/ci/init.js +0 -1
  4. package/ext/formats.js +3 -5
  5. package/index.d.ts +1 -11
  6. package/package.json +7 -7
  7. package/packages/datadog-core/src/storage/async_resource.js +19 -1
  8. package/packages/datadog-core/src/storage/index.js +1 -1
  9. package/packages/datadog-instrumentations/index.js +1 -52
  10. package/packages/datadog-instrumentations/src/connect.js +1 -1
  11. package/packages/datadog-instrumentations/src/cucumber.js +15 -0
  12. package/packages/datadog-instrumentations/src/grpc/client.js +2 -2
  13. package/packages/datadog-instrumentations/src/grpc/server.js +1 -1
  14. package/packages/datadog-instrumentations/src/hapi.js +3 -31
  15. package/packages/datadog-instrumentations/src/helpers/hooks.js +68 -0
  16. package/packages/datadog-instrumentations/src/helpers/instrument.js +5 -34
  17. package/packages/datadog-instrumentations/src/helpers/instrumentations.js +7 -0
  18. package/packages/datadog-instrumentations/src/helpers/register.js +59 -0
  19. package/packages/datadog-instrumentations/src/jest.js +33 -11
  20. package/packages/datadog-instrumentations/src/koa.js +1 -1
  21. package/packages/datadog-instrumentations/src/mocha.js +4 -1
  22. package/packages/datadog-instrumentations/src/pg.js +2 -2
  23. package/packages/datadog-instrumentations/src/restify.js +27 -5
  24. package/packages/datadog-instrumentations/src/router.js +1 -1
  25. package/packages/datadog-plugin-aws-sdk/src/base.js +1 -2
  26. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -2
  27. package/packages/datadog-plugin-cucumber/src/index.js +4 -0
  28. package/packages/datadog-plugin-jest/src/index.js +25 -4
  29. package/packages/datadog-plugin-mocha/src/index.js +2 -2
  30. package/packages/datadog-plugin-mongodb-core/src/index.js +21 -6
  31. package/packages/datadog-plugin-oracledb/src/index.js +12 -4
  32. package/packages/datadog-plugin-restify/src/index.js +7 -0
  33. package/packages/dd-trace/index.js +1 -1
  34. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +50 -0
  35. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +53 -8
  36. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +23 -24
  37. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +220 -0
  38. package/packages/dd-trace/src/config.js +13 -0
  39. package/packages/dd-trace/src/encode/0.4.js +51 -58
  40. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +13 -34
  41. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +84 -0
  42. package/packages/dd-trace/src/exporters/agent/index.js +13 -7
  43. package/packages/dd-trace/src/exporters/agent/writer.js +1 -1
  44. package/packages/dd-trace/src/{profiling/exporters → exporters/common}/form-data.js +0 -0
  45. package/packages/dd-trace/src/exporters/common/request.js +23 -11
  46. package/packages/dd-trace/src/exporters/common/writer.js +9 -6
  47. package/packages/dd-trace/src/id.js +16 -13
  48. package/packages/dd-trace/src/iitm.js +1 -1
  49. package/packages/dd-trace/src/index.js +10 -0
  50. package/packages/dd-trace/src/noop/proxy.js +77 -0
  51. package/packages/dd-trace/src/noop/scope.js +2 -6
  52. package/packages/dd-trace/src/noop/span.js +12 -12
  53. package/packages/dd-trace/src/noop/tracer.js +8 -5
  54. package/packages/dd-trace/src/opentracing/propagation/text_map.js +6 -6
  55. package/packages/dd-trace/src/opentracing/span.js +63 -49
  56. package/packages/dd-trace/src/opentracing/span_context.js +1 -5
  57. package/packages/dd-trace/src/opentracing/tracer.js +31 -36
  58. package/packages/dd-trace/src/plugin_manager.js +101 -68
  59. package/packages/dd-trace/src/plugins/index.js +57 -44
  60. package/packages/dd-trace/src/plugins/util/ci.js +34 -9
  61. package/packages/dd-trace/src/plugins/util/git.js +52 -2
  62. package/packages/dd-trace/src/plugins/util/tags.js +4 -1
  63. package/packages/dd-trace/src/plugins/util/web.js +1 -1
  64. package/packages/dd-trace/src/profiling/exporters/agent.js +1 -1
  65. package/packages/dd-trace/src/profiling/profilers/cpu.js +3 -3
  66. package/packages/dd-trace/src/proxy.js +18 -71
  67. package/packages/dd-trace/src/scope.js +1 -58
  68. package/packages/dd-trace/src/startup-log.js +8 -19
  69. package/packages/dd-trace/src/telemetry.js +2 -15
  70. package/scripts/install_plugin_modules.js +17 -26
  71. package/ci/jest/env.js +0 -38
@@ -0,0 +1,220 @@
1
+
2
+ const fs = require('fs')
3
+ const https = require('https')
4
+ const path = require('path')
5
+
6
+ const FormData = require('../../../exporters/common/form-data')
7
+
8
+ const log = require('../../../log')
9
+ const {
10
+ getLatestCommits,
11
+ getRepositoryUrl,
12
+ generatePackFilesForCommits,
13
+ getCommitsToUpload
14
+ } = require('../../../plugins/util/git')
15
+
16
+ const isValidSha = (sha) => /[0-9a-f]{40}/.test(sha)
17
+
18
+ function sanitizeCommits (commits) {
19
+ return commits.map(({ id: commitSha, type }) => {
20
+ if (type !== 'commit') {
21
+ throw new Error('Invalid commit response')
22
+ }
23
+ const sanitizedCommit = commitSha.replace(/[^0-9a-f]+/g, '')
24
+ if (sanitizedCommit !== commitSha || !isValidSha(sanitizedCommit)) {
25
+ throw new Error('Invalid commit format')
26
+ }
27
+ return sanitizedCommit
28
+ })
29
+ }
30
+
31
+ function getCommonRequestOptions (url) {
32
+ return {
33
+ method: 'POST',
34
+ headers: {
35
+ 'dd-api-key': process.env.DATADOG_API_KEY || process.env.DD_API_KEY
36
+ },
37
+ timeout: 15000,
38
+ protocol: url.protocol,
39
+ hostname: url.hostname,
40
+ port: url.port
41
+ }
42
+ }
43
+
44
+ /**
45
+ * This function posts the SHAs of the commits of the last month
46
+ * The response are the commits for which the backend already has information
47
+ * This response is used to know which commits can be ignored from there on
48
+ */
49
+ function getCommitsToExclude ({ url, repositoryUrl }, callback) {
50
+ const latestCommits = getLatestCommits()
51
+ const [headCommit] = latestCommits
52
+
53
+ const commonOptions = getCommonRequestOptions(url)
54
+
55
+ const options = {
56
+ ...commonOptions,
57
+ headers: {
58
+ ...commonOptions.headers,
59
+ 'Content-Type': 'application/json'
60
+ },
61
+ path: '/api/v2/git/repository/search_commits'
62
+ }
63
+
64
+ const localCommitData = JSON.stringify({
65
+ meta: {
66
+ repository_url: repositoryUrl
67
+ },
68
+ data: latestCommits.map(commit => ({
69
+ id: commit,
70
+ type: 'commit'
71
+ }))
72
+ })
73
+
74
+ const request = https.request(options, (res) => {
75
+ let responseData = ''
76
+
77
+ res.on('data', chunk => { responseData += chunk })
78
+ res.on('end', () => {
79
+ if (res.statusCode === 200) {
80
+ let commitsToExclude
81
+ try {
82
+ commitsToExclude = sanitizeCommits(JSON.parse(responseData).data)
83
+ } catch (e) {
84
+ callback(new Error(`Can't parse response: ${e.message}`))
85
+ return
86
+ }
87
+ callback(null, commitsToExclude, headCommit)
88
+ } else {
89
+ const error = new Error(`Error getting commits: ${res.statusCode} ${res.statusMessage}`)
90
+ callback(error)
91
+ }
92
+ })
93
+ })
94
+
95
+ request.write(localCommitData)
96
+ request.on('error', callback)
97
+
98
+ request.end()
99
+
100
+ return request
101
+ }
102
+
103
+ /**
104
+ * This function uploads a git packfile
105
+ */
106
+ function uploadPackFile ({ url, packFileToUpload, repositoryUrl, headCommit }, callback) {
107
+ const form = new FormData()
108
+
109
+ const pushedSha = JSON.stringify({
110
+ data: {
111
+ id: headCommit,
112
+ type: 'commit'
113
+ },
114
+ meta: {
115
+ repository_url: repositoryUrl
116
+ }
117
+ })
118
+
119
+ form.append('pushedSha', pushedSha, { contentType: 'application/json' })
120
+
121
+ try {
122
+ const packFileContent = fs.readFileSync(packFileToUpload)
123
+ // The original filename includes a random prefix, so we remove it here
124
+ const [, filename] = path.basename(packFileToUpload).split('-')
125
+ form.append('packfile', packFileContent, {
126
+ filename,
127
+ contentType: 'application/octet-stream'
128
+ })
129
+ } catch (e) {
130
+ callback(new Error(`Error reading packfile: ${packFileToUpload}`))
131
+ return
132
+ }
133
+
134
+ const commonOptions = getCommonRequestOptions(url)
135
+
136
+ const options = {
137
+ ...commonOptions,
138
+ path: '/api/v2/git/repository/packfile',
139
+ headers: {
140
+ ...commonOptions.headers,
141
+ ...form.getHeaders()
142
+ }
143
+ }
144
+
145
+ const req = https.request(options, res => {
146
+ res.on('data', () => {})
147
+ res.on('end', () => {
148
+ if (res.statusCode === 204) {
149
+ callback(null)
150
+ } else {
151
+ const error = new Error(`Error uploading packfiles: ${res.statusCode} ${res.statusMessage}`)
152
+ error.status = res.statusCode
153
+
154
+ callback(error)
155
+ }
156
+ })
157
+ })
158
+
159
+ req.on('error', err => {
160
+ callback(err)
161
+ })
162
+ form.pipe(req)
163
+ }
164
+
165
+ /**
166
+ * This function uploads git metadata to CI Visibility's backend.
167
+ */
168
+ function sendGitMetadata (site, callback) {
169
+ const url = new URL(`https://api.${site}`)
170
+
171
+ const repositoryUrl = getRepositoryUrl()
172
+
173
+ getCommitsToExclude({ url, repositoryUrl }, (err, commitsToExclude, headCommit) => {
174
+ if (err) {
175
+ callback(err)
176
+ return
177
+ }
178
+ const commitsToUpload = getCommitsToUpload(commitsToExclude)
179
+
180
+ if (!commitsToUpload.length) {
181
+ log.debug('No commits to upload')
182
+ callback(null)
183
+ return
184
+ }
185
+
186
+ const packFilesToUpload = generatePackFilesForCommits(commitsToUpload)
187
+
188
+ let packFileIndex = 0
189
+ // This uploads packfiles sequentially
190
+ const uploadPackFileCallback = (err) => {
191
+ if (err || packFileIndex === packFilesToUpload.length) {
192
+ callback(err)
193
+ return
194
+ }
195
+ return uploadPackFile(
196
+ {
197
+ packFileToUpload: packFilesToUpload[packFileIndex++],
198
+ url,
199
+ repositoryUrl,
200
+ headCommit
201
+ },
202
+ uploadPackFileCallback
203
+ )
204
+ }
205
+
206
+ uploadPackFile(
207
+ {
208
+ url,
209
+ packFileToUpload: packFilesToUpload[packFileIndex++],
210
+ repositoryUrl,
211
+ headCommit
212
+ },
213
+ uploadPackFileCallback
214
+ )
215
+ })
216
+ }
217
+
218
+ module.exports = {
219
+ sendGitMetadata
220
+ }
@@ -70,6 +70,12 @@ class Config {
70
70
  null
71
71
  )
72
72
  const DD_CIVISIBILITY_AGENTLESS_URL = process.env.DD_CIVISIBILITY_AGENTLESS_URL
73
+
74
+ const DD_CIVISIBILITY_ITR_ENABLED = coalesce(
75
+ process.env.DD_CIVISIBILITY_ITR_ENABLED,
76
+ false
77
+ )
78
+
73
79
  const DD_SERVICE = options.service ||
74
80
  process.env.DD_SERVICE ||
75
81
  process.env.DD_SERVICE_NAME ||
@@ -181,6 +187,11 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
181
187
  |[\\-]{5}BEGIN[a-z\\s]+PRIVATE\\sKEY[\\-]{5}[^\\-]+[\\-]{5}END[a-z\\s]+PRIVATE\\sKEY|ssh-rsa\\s*[a-z0-9\\/\\.+]{100,}`
182
188
  )
183
189
 
190
+ const DD_CIVISIBILITY_GIT_UPLOAD_ENABLED = coalesce(
191
+ process.env.DD_CIVISIBILITY_GIT_UPLOAD_ENABLED,
192
+ false
193
+ )
194
+
184
195
  const sampler = (options.experimental && options.experimental.sampler) || {}
185
196
  const ingestion = options.ingestion || {}
186
197
  const dogstatsd = coalesce(options.dogstatsd, {})
@@ -256,6 +267,8 @@ ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)
256
267
  obfuscatorKeyRegex: DD_APPSEC_OBFUSCATION_PARAMETER_KEY_REGEXP,
257
268
  obfuscatorValueRegex: DD_APPSEC_OBFUSCATION_PARAMETER_VALUE_REGEXP
258
269
  }
270
+ this.isGitUploadEnabled = isTrue(DD_CIVISIBILITY_GIT_UPLOAD_ENABLED)
271
+ this.isIntelligentTestRunnerEnabled = isTrue(DD_CIVISIBILITY_ITR_ENABLED)
259
272
 
260
273
  tagger.add(this.tags, {
261
274
  service: this.service,
@@ -13,7 +13,8 @@ float64Array[0] = -1
13
13
  const bigEndian = uInt8Float64Array[7] === 0
14
14
 
15
15
  class AgentEncoder {
16
- constructor (writer) {
16
+ constructor (writer, limit = SOFT_LIMIT) {
17
+ this._limit = limit
17
18
  this._traceBytes = new Chunk()
18
19
  this._stringBytes = new Chunk()
19
20
  this._writer = writer
@@ -41,7 +42,8 @@ class AgentEncoder {
41
42
  })
42
43
 
43
44
  // we can go over the soft limit since the agent has a 50MB hard limit
44
- if (this._traceBytes.length > SOFT_LIMIT || this._stringBytes.length > SOFT_LIMIT) {
45
+ if (this._traceBytes.length > this._limit || this._stringBytes.length > this._limit) {
46
+ log.debug('Buffer went over soft limit, flushing')
45
47
  this._writer.flush()
46
48
  }
47
49
  }
@@ -113,29 +115,37 @@ class AgentEncoder {
113
115
 
114
116
  _encodeArrayPrefix (bytes, value) {
115
117
  const length = value.length
116
- const buffer = bytes.buffer
117
118
  const offset = bytes.length
118
119
 
119
120
  bytes.reserve(5)
120
121
  bytes.length += 5
121
122
 
122
- buffer[offset] = 0xdd
123
- buffer[offset + 1] = length >> 24
124
- buffer[offset + 2] = length >> 16
125
- buffer[offset + 3] = length >> 8
126
- buffer[offset + 4] = length
123
+ bytes.buffer[offset] = 0xdd
124
+ bytes.buffer[offset + 1] = length >> 24
125
+ bytes.buffer[offset + 2] = length >> 16
126
+ bytes.buffer[offset + 3] = length >> 8
127
+ bytes.buffer[offset + 4] = length
127
128
  }
128
129
 
129
- _encodeByte (bytes, value) {
130
- const buffer = bytes.buffer
130
+ _encodeMapPrefix (bytes, keysLength) {
131
+ const offset = bytes.length
131
132
 
133
+ bytes.reserve(5)
134
+ bytes.length += 5
135
+ bytes.buffer[offset] = 0xdf
136
+ bytes.buffer[offset + 1] = keysLength >> 24
137
+ bytes.buffer[offset + 2] = keysLength >> 16
138
+ bytes.buffer[offset + 3] = keysLength >> 8
139
+ bytes.buffer[offset + 4] = keysLength
140
+ }
141
+
142
+ _encodeByte (bytes, value) {
132
143
  bytes.reserve(1)
133
144
 
134
- buffer[bytes.length++] = value
145
+ bytes.buffer[bytes.length++] = value
135
146
  }
136
147
 
137
148
  _encodeId (bytes, id) {
138
- const buffer = bytes.buffer
139
149
  const offset = bytes.length
140
150
 
141
151
  bytes.reserve(9)
@@ -143,33 +153,31 @@ class AgentEncoder {
143
153
 
144
154
  id = id.toArray()
145
155
 
146
- buffer[offset] = 0xcf
147
- buffer[offset + 1] = id[0]
148
- buffer[offset + 2] = id[1]
149
- buffer[offset + 3] = id[2]
150
- buffer[offset + 4] = id[3]
151
- buffer[offset + 5] = id[4]
152
- buffer[offset + 6] = id[5]
153
- buffer[offset + 7] = id[6]
154
- buffer[offset + 8] = id[7]
156
+ bytes.buffer[offset] = 0xcf
157
+ bytes.buffer[offset + 1] = id[0]
158
+ bytes.buffer[offset + 2] = id[1]
159
+ bytes.buffer[offset + 3] = id[2]
160
+ bytes.buffer[offset + 4] = id[3]
161
+ bytes.buffer[offset + 5] = id[4]
162
+ bytes.buffer[offset + 6] = id[5]
163
+ bytes.buffer[offset + 7] = id[6]
164
+ bytes.buffer[offset + 8] = id[7]
155
165
  }
156
166
 
157
167
  _encodeInteger (bytes, value) {
158
- const buffer = bytes.buffer
159
168
  const offset = bytes.length
160
169
 
161
170
  bytes.reserve(5)
162
171
  bytes.length += 5
163
172
 
164
- buffer[offset] = 0xce
165
- buffer[offset + 1] = value >> 24
166
- buffer[offset + 2] = value >> 16
167
- buffer[offset + 3] = value >> 8
168
- buffer[offset + 4] = value
173
+ bytes.buffer[offset] = 0xce
174
+ bytes.buffer[offset + 1] = value >> 24
175
+ bytes.buffer[offset + 2] = value >> 16
176
+ bytes.buffer[offset + 3] = value >> 8
177
+ bytes.buffer[offset + 4] = value
169
178
  }
170
179
 
171
180
  _encodeLong (bytes, value) {
172
- const buffer = bytes.buffer
173
181
  const offset = bytes.length
174
182
  const hi = (value / Math.pow(2, 32)) >> 0
175
183
  const lo = value >>> 0
@@ -177,40 +185,27 @@ class AgentEncoder {
177
185
  bytes.reserve(9)
178
186
  bytes.length += 9
179
187
 
180
- buffer[offset] = 0xcf
181
- buffer[offset + 1] = hi >> 24
182
- buffer[offset + 2] = hi >> 16
183
- buffer[offset + 3] = hi >> 8
184
- buffer[offset + 4] = hi
185
- buffer[offset + 5] = lo >> 24
186
- buffer[offset + 6] = lo >> 16
187
- buffer[offset + 7] = lo >> 8
188
- buffer[offset + 8] = lo
188
+ bytes.buffer[offset] = 0xcf
189
+ bytes.buffer[offset + 1] = hi >> 24
190
+ bytes.buffer[offset + 2] = hi >> 16
191
+ bytes.buffer[offset + 3] = hi >> 8
192
+ bytes.buffer[offset + 4] = hi
193
+ bytes.buffer[offset + 5] = lo >> 24
194
+ bytes.buffer[offset + 6] = lo >> 16
195
+ bytes.buffer[offset + 7] = lo >> 8
196
+ bytes.buffer[offset + 8] = lo
189
197
  }
190
198
 
191
199
  _encodeMap (bytes, value) {
192
200
  const keys = Object.keys(value)
193
- const buffer = bytes.buffer
194
- const offset = bytes.length
195
-
196
- bytes.reserve(5)
197
- bytes.length += 5
201
+ const validKeys = keys.filter(key => typeof value[key] === 'string' || typeof value[key] === 'number')
198
202
 
199
- let length = 0
200
-
201
- for (const key of keys) {
202
- if (typeof value[key] !== 'string' && typeof value[key] !== 'number') return
203
- length++
203
+ this._encodeMapPrefix(bytes, validKeys.length)
204
204
 
205
+ for (const key of validKeys) {
205
206
  this._encodeString(bytes, key)
206
207
  this._encodeValue(bytes, value[key])
207
208
  }
208
-
209
- buffer[offset] = 0xdf
210
- buffer[offset + 1] = length >> 24
211
- buffer[offset + 2] = length >> 16
212
- buffer[offset + 3] = length >> 8
213
- buffer[offset + 4] = length
214
209
  }
215
210
 
216
211
  _encodeValue (bytes, value) {
@@ -237,21 +232,19 @@ class AgentEncoder {
237
232
  _encodeFloat (bytes, value) {
238
233
  float64Array[0] = value
239
234
 
240
- const buffer = bytes.buffer
241
235
  const offset = bytes.length
242
-
243
236
  bytes.reserve(9)
244
237
  bytes.length += 9
245
238
 
246
- buffer[offset] = 0xcb
239
+ bytes.buffer[offset] = 0xcb
247
240
 
248
241
  if (bigEndian) {
249
242
  for (let i = 0; i <= 7; i++) {
250
- buffer[offset + i + 1] = uInt8Float64Array[i]
243
+ bytes.buffer[offset + i + 1] = uInt8Float64Array[i]
251
244
  }
252
245
  } else {
253
246
  for (let i = 7; i >= 0; i--) {
254
- buffer[bytes.length - i - 1] = uInt8Float64Array[i]
247
+ bytes.buffer[bytes.length - i - 1] = uInt8Float64Array[i]
255
248
  }
256
249
  }
257
250
  }
@@ -1,10 +1,8 @@
1
1
  'use strict'
2
2
  const { truncateSpan, normalizeSpan } = require('./tags-processors')
3
- const Chunk = require('./chunk')
4
3
  const { AgentEncoder } = require('./0.4')
5
4
  const { version: ddTraceVersion } = require('../../../../package.json')
6
5
  const id = require('../../../dd-trace/src/id')
7
-
8
6
  const ENCODING_VERSION = 1
9
7
 
10
8
  const ALLOWED_CONTENT_TYPES = ['test_session_end', 'test_suite_end', 'test']
@@ -12,7 +10,7 @@ const ALLOWED_CONTENT_TYPES = ['test_session_end', 'test_suite_end', 'test']
12
10
  const TEST_SUITE_KEYS_LENGTH = 11
13
11
  const TEST_SESSION_KEYS_LENGTH = 10
14
12
 
15
- const CHUNK_SIZE = 4 * 1024 * 1024 // 4MB
13
+ const INTAKE_SOFT_LIMIT = 2 * 1024 * 1024 // 2MB
16
14
 
17
15
  function formatSpan (span) {
18
16
  let encodingVersion = ENCODING_VERSION
@@ -27,16 +25,11 @@ function formatSpan (span) {
27
25
  }
28
26
 
29
27
  class AgentlessCiVisibilityEncoder extends AgentEncoder {
30
- constructor ({ runtimeId, service, env }) {
31
- super(...arguments)
32
- this._events = []
28
+ constructor (writer, { runtimeId, service, env }) {
29
+ super(writer, INTAKE_SOFT_LIMIT)
33
30
  this.runtimeId = runtimeId
34
31
  this.service = service
35
32
  this.env = env
36
- this._traceBytes = new Chunk(CHUNK_SIZE)
37
- this._stringBytes = new Chunk(CHUNK_SIZE)
38
- this._stringCount = 0
39
- this._stringMap = {}
40
33
 
41
34
  // Used to keep track of the number of encoded events to update the
42
35
  // length of `payload.events` when calling `makePayload`
@@ -186,35 +179,21 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
186
179
  const lo = value >>> 0
187
180
  const flag = isPositive ? 0xcf : 0xd3
188
181
 
189
- const buffer = bytes.buffer
190
182
  const offset = bytes.length
191
183
 
192
184
  // int 64
193
185
  bytes.reserve(9)
194
186
  bytes.length += 9
195
187
 
196
- buffer[offset] = flag
197
- buffer[offset + 1] = hi >> 24
198
- buffer[offset + 2] = hi >> 16
199
- buffer[offset + 3] = hi >> 8
200
- buffer[offset + 4] = hi
201
- buffer[offset + 5] = lo >> 24
202
- buffer[offset + 6] = lo >> 16
203
- buffer[offset + 7] = lo >> 8
204
- buffer[offset + 8] = lo
205
- }
206
-
207
- _encodeMapPrefix (bytes, keysLength) {
208
- const buffer = bytes.buffer
209
- const offset = bytes.length
210
-
211
- bytes.reserve(5)
212
- bytes.length += 5
213
- buffer[offset] = 0xdf
214
- buffer[offset + 1] = keysLength >> 24
215
- buffer[offset + 2] = keysLength >> 16
216
- buffer[offset + 3] = keysLength >> 8
217
- buffer[offset + 4] = keysLength
188
+ bytes.buffer[offset] = flag
189
+ bytes.buffer[offset + 1] = hi >> 24
190
+ bytes.buffer[offset + 2] = hi >> 16
191
+ bytes.buffer[offset + 3] = hi >> 8
192
+ bytes.buffer[offset + 4] = hi
193
+ bytes.buffer[offset + 5] = lo >> 24
194
+ bytes.buffer[offset + 6] = lo >> 16
195
+ bytes.buffer[offset + 7] = lo >> 8
196
+ bytes.buffer[offset + 8] = lo
218
197
  }
219
198
 
220
199
  _encode (bytes, trace) {
@@ -240,7 +219,7 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
240
219
  const traceSize = bytes.length
241
220
  const buffer = Buffer.allocUnsafe(traceSize)
242
221
 
243
- bytes.buffer.copy(buffer, 0, 0, bytes.length)
222
+ bytes.buffer.copy(buffer, 0, 0, traceSize)
244
223
 
245
224
  this.reset()
246
225
 
@@ -0,0 +1,84 @@
1
+ 'use strict'
2
+ const { AgentEncoder } = require('./0.4')
3
+ const Chunk = require('./chunk')
4
+
5
+ const FormData = require('../exporters/common/form-data')
6
+
7
+ const COVERAGE_PAYLOAD_VERSION = 1
8
+ const COVERAGE_KEYS_LENGTH = 4
9
+ const MAXIMUM_NUM_COVERAGE_FILES = 100
10
+
11
+ class CoverageCIVisibilityEncoder extends AgentEncoder {
12
+ constructor () {
13
+ super(...arguments)
14
+ this.codeCoverageBuffers = []
15
+ this._coverageBytes = new Chunk()
16
+ this.reset()
17
+ }
18
+
19
+ count () {
20
+ return this.codeCoverageBuffers.length
21
+ }
22
+
23
+ encode (coverage) {
24
+ const bytes = this._coverageBytes
25
+ const coverageBuffer = this.encodeCodeCoverage(bytes, coverage)
26
+ this.codeCoverageBuffers.push(coverageBuffer)
27
+ this.reset()
28
+ }
29
+
30
+ encodeCodeCoverage (bytes, coverage) {
31
+ this._encodeMapPrefix(bytes, COVERAGE_KEYS_LENGTH)
32
+ this._encodeString(bytes, 'version')
33
+ this._encodeInteger(bytes, COVERAGE_PAYLOAD_VERSION)
34
+ this._encodeString(bytes, 'trace_id')
35
+ this._encodeId(bytes, coverage.traceId)
36
+ this._encodeString(bytes, 'span_id')
37
+ this._encodeId(bytes, coverage.spanId)
38
+ this._encodeString(bytes, 'files')
39
+ this._encodeArrayPrefix(bytes, coverage.files)
40
+ for (const filename of coverage.files) {
41
+ this._encodeMapPrefix(bytes, 1)
42
+ this._encodeString(bytes, 'filename')
43
+ this._encodeString(bytes, filename)
44
+ }
45
+ const traceSize = bytes.length
46
+ const buffer = Buffer.allocUnsafe(traceSize)
47
+
48
+ bytes.buffer.copy(buffer, 0, 0, bytes.length)
49
+
50
+ return buffer
51
+ }
52
+
53
+ reset () {
54
+ this._reset()
55
+ if (this._coverageBytes) {
56
+ this._coverageBytes.length = 0
57
+ }
58
+ }
59
+
60
+ makePayload () {
61
+ const form = new FormData()
62
+
63
+ let coverageFileIndex = 1
64
+
65
+ for (const coverageBuffer of this.codeCoverageBuffers.slice(0, MAXIMUM_NUM_COVERAGE_FILES)) {
66
+ const coverageFilename = `coverage${coverageFileIndex++}`
67
+ form.append(
68
+ coverageFilename,
69
+ coverageBuffer,
70
+ {
71
+ filename: `${coverageFilename}.msgpack`,
72
+ contentType: 'application/msgpack'
73
+ }
74
+ )
75
+ }
76
+ // 'event' is a backend requirement
77
+ form.append('event', JSON.stringify({}), { filename: 'event.json', contentType: 'application/json' })
78
+ this.codeCoverageBuffers = this.codeCoverageBuffers.slice(MAXIMUM_NUM_COVERAGE_FILES)
79
+
80
+ return form
81
+ }
82
+ }
83
+
84
+ module.exports = { CoverageCIVisibilityEncoder }
@@ -3,17 +3,16 @@
3
3
  const URL = require('url').URL
4
4
  const log = require('../../log')
5
5
  const Writer = require('./writer')
6
- const Scheduler = require('../scheduler')
7
6
 
8
7
  class AgentExporter {
9
- constructor ({ url, hostname, port, flushInterval, lookup, protocolVersion }, prioritySampler) {
8
+ constructor (config, prioritySampler) {
9
+ this._config = config
10
+ const { url, hostname, port, lookup, protocolVersion } = config
10
11
  this._url = url || new URL(`http://${hostname || 'localhost'}:${port}`)
11
12
  this._writer = new Writer({ url: this._url, prioritySampler, lookup, protocolVersion })
12
13
 
13
- if (flushInterval > 0) {
14
- this._scheduler = new Scheduler(() => this._writer.flush(), flushInterval)
15
- }
16
- this._scheduler && this._scheduler.start()
14
+ this._timer = undefined
15
+ process.once('beforeExit', () => this._writer.flush())
17
16
  }
18
17
 
19
18
  setUrl (url) {
@@ -29,8 +28,15 @@ class AgentExporter {
29
28
  export (spans) {
30
29
  this._writer.append(spans)
31
30
 
32
- if (!this._scheduler) {
31
+ const { flushInterval } = this._config
32
+
33
+ if (flushInterval === 0) {
33
34
  this._writer.flush()
35
+ } else if (flushInterval > 0 && !this._timer) {
36
+ this._timer = setTimeout(() => {
37
+ this._writer.flush()
38
+ this._timer = clearTimeout(this._timer)
39
+ }, flushInterval).unref()
34
40
  }
35
41
  }
36
42
  }
@@ -99,7 +99,7 @@ function makeRequest (version, data, count, url, lookup, needsStartupLog, cb) {
99
99
 
100
100
  log.debug(() => `Request to the agent: ${JSON.stringify(options)}`)
101
101
 
102
- request(data, options, true, (err, res, status) => {
102
+ request(data, options, (err, res, status) => {
103
103
  if (needsStartupLog) {
104
104
  // Note that logging will only happen once, regardless of how many times this is called.
105
105
  startupLog({