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.
- package/LICENSE-3rdparty.csv +2 -2
- package/MIGRATING.md +119 -0
- package/ci/init.js +0 -1
- package/ext/formats.js +3 -5
- package/index.d.ts +1 -11
- package/package.json +7 -7
- package/packages/datadog-core/src/storage/async_resource.js +19 -1
- package/packages/datadog-core/src/storage/index.js +1 -1
- package/packages/datadog-instrumentations/index.js +1 -52
- package/packages/datadog-instrumentations/src/connect.js +1 -1
- package/packages/datadog-instrumentations/src/cucumber.js +15 -0
- package/packages/datadog-instrumentations/src/grpc/client.js +2 -2
- package/packages/datadog-instrumentations/src/grpc/server.js +1 -1
- package/packages/datadog-instrumentations/src/hapi.js +3 -31
- package/packages/datadog-instrumentations/src/helpers/hooks.js +68 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +5 -34
- package/packages/datadog-instrumentations/src/helpers/instrumentations.js +7 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +59 -0
- package/packages/datadog-instrumentations/src/jest.js +33 -11
- package/packages/datadog-instrumentations/src/koa.js +1 -1
- package/packages/datadog-instrumentations/src/mocha.js +4 -1
- package/packages/datadog-instrumentations/src/pg.js +2 -2
- package/packages/datadog-instrumentations/src/restify.js +27 -5
- package/packages/datadog-instrumentations/src/router.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/base.js +1 -2
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -2
- package/packages/datadog-plugin-cucumber/src/index.js +4 -0
- package/packages/datadog-plugin-jest/src/index.js +25 -4
- package/packages/datadog-plugin-mocha/src/index.js +2 -2
- package/packages/datadog-plugin-mongodb-core/src/index.js +21 -6
- package/packages/datadog-plugin-oracledb/src/index.js +12 -4
- package/packages/datadog-plugin-restify/src/index.js +7 -0
- package/packages/dd-trace/index.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +50 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +53 -8
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +23 -24
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +220 -0
- package/packages/dd-trace/src/config.js +13 -0
- package/packages/dd-trace/src/encode/0.4.js +51 -58
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +13 -34
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +84 -0
- package/packages/dd-trace/src/exporters/agent/index.js +13 -7
- package/packages/dd-trace/src/exporters/agent/writer.js +1 -1
- package/packages/dd-trace/src/{profiling/exporters → exporters/common}/form-data.js +0 -0
- package/packages/dd-trace/src/exporters/common/request.js +23 -11
- package/packages/dd-trace/src/exporters/common/writer.js +9 -6
- package/packages/dd-trace/src/id.js +16 -13
- package/packages/dd-trace/src/iitm.js +1 -1
- package/packages/dd-trace/src/index.js +10 -0
- package/packages/dd-trace/src/noop/proxy.js +77 -0
- package/packages/dd-trace/src/noop/scope.js +2 -6
- package/packages/dd-trace/src/noop/span.js +12 -12
- package/packages/dd-trace/src/noop/tracer.js +8 -5
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +6 -6
- package/packages/dd-trace/src/opentracing/span.js +63 -49
- package/packages/dd-trace/src/opentracing/span_context.js +1 -5
- package/packages/dd-trace/src/opentracing/tracer.js +31 -36
- package/packages/dd-trace/src/plugin_manager.js +101 -68
- package/packages/dd-trace/src/plugins/index.js +57 -44
- package/packages/dd-trace/src/plugins/util/ci.js +34 -9
- package/packages/dd-trace/src/plugins/util/git.js +52 -2
- package/packages/dd-trace/src/plugins/util/tags.js +4 -1
- package/packages/dd-trace/src/plugins/util/web.js +1 -1
- package/packages/dd-trace/src/profiling/exporters/agent.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/cpu.js +3 -3
- package/packages/dd-trace/src/proxy.js +18 -71
- package/packages/dd-trace/src/scope.js +1 -58
- package/packages/dd-trace/src/startup-log.js +8 -19
- package/packages/dd-trace/src/telemetry.js +2 -15
- package/scripts/install_plugin_modules.js +17 -26
- 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 >
|
|
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
|
-
|
|
130
|
-
const
|
|
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
|
|
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
|
-
|
|
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
|
|
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(
|
|
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,
|
|
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 (
|
|
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
|
-
|
|
14
|
-
|
|
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
|
-
|
|
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,
|
|
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({
|
|
File without changes
|