dd-trace 2.4.2 → 2.7.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 +1 -2
- package/ci/init.js +6 -0
- package/ci/jest/env.js +16 -3
- package/ext/exporters.d.ts +2 -1
- package/ext/exporters.js +2 -1
- package/index.d.ts +17 -8
- package/package.json +20 -23
- package/packages/datadog-instrumentations/index.js +14 -0
- package/packages/datadog-instrumentations/src/connect.js +111 -0
- package/packages/datadog-instrumentations/src/cypress.js +8 -0
- package/packages/datadog-instrumentations/src/express.js +27 -0
- package/packages/datadog-instrumentations/src/fastify.js +187 -0
- package/packages/datadog-instrumentations/src/find-my-way.js +30 -0
- package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +100 -0
- package/packages/datadog-instrumentations/src/http/server.js +1 -1
- package/packages/datadog-instrumentations/src/jest.js +175 -0
- package/packages/datadog-instrumentations/src/kafkajs.js +112 -0
- package/packages/datadog-instrumentations/src/knex.js +20 -0
- package/packages/datadog-instrumentations/src/koa.js +159 -0
- package/packages/datadog-instrumentations/src/limitd-client.js +21 -0
- package/packages/datadog-instrumentations/src/oracledb.js +128 -0
- package/packages/datadog-instrumentations/src/paperplane.js +77 -0
- package/packages/datadog-instrumentations/src/pg.js +2 -2
- package/packages/datadog-instrumentations/src/restify.js +58 -0
- package/packages/datadog-instrumentations/src/rhea.js +1 -1
- package/packages/datadog-instrumentations/src/router.js +177 -0
- package/packages/datadog-plugin-aws-sdk/src/helpers.js +4 -4
- package/packages/datadog-plugin-aws-sdk/src/index.js +1 -1
- package/packages/datadog-plugin-connect/src/index.js +10 -114
- package/packages/datadog-plugin-cucumber/src/index.js +16 -16
- package/packages/datadog-plugin-cypress/src/index.js +10 -5
- package/packages/datadog-plugin-cypress/src/plugin.js +18 -17
- package/packages/datadog-plugin-dns/src/index.js +12 -1
- package/packages/datadog-plugin-express/src/index.js +11 -25
- package/packages/datadog-plugin-fastify/src/index.js +17 -4
- package/packages/datadog-plugin-find-my-way/src/index.js +20 -0
- package/packages/datadog-plugin-fs/src/index.js +2 -0
- package/packages/datadog-plugin-google-cloud-pubsub/src/index.js +56 -111
- package/packages/datadog-plugin-http/src/server.js +2 -10
- package/packages/datadog-plugin-jest/src/index.js +101 -3
- package/packages/datadog-plugin-jest/src/util.js +1 -29
- package/packages/datadog-plugin-kafkajs/src/index.js +64 -90
- package/packages/datadog-plugin-koa/src/index.js +12 -164
- package/packages/datadog-plugin-mocha/src/index.js +14 -15
- package/packages/datadog-plugin-oracledb/src/index.js +34 -100
- package/packages/datadog-plugin-paperplane/src/index.js +14 -100
- package/packages/datadog-plugin-paperplane/src/logger.js +11 -0
- package/packages/datadog-plugin-paperplane/src/server.js +24 -0
- package/packages/datadog-plugin-restify/src/index.js +13 -75
- package/packages/datadog-plugin-router/src/index.js +67 -164
- package/packages/datadog-plugin-web/src/index.js +20 -0
- package/packages/dd-trace/lib/version.js +1 -1
- package/packages/dd-trace/src/appsec/callbacks/ddwaf.js +34 -12
- package/packages/dd-trace/src/appsec/index.js +7 -3
- package/packages/dd-trace/src/appsec/recommended.json +15 -5
- package/packages/dd-trace/src/appsec/reporter.js +33 -3
- package/packages/dd-trace/src/appsec/rule_manager.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +32 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +51 -0
- package/packages/dd-trace/src/config.js +33 -4
- package/packages/dd-trace/src/encode/0.4.js +0 -1
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +193 -0
- package/packages/dd-trace/src/encode/tags-processors.js +116 -0
- package/packages/dd-trace/src/exporter.js +3 -0
- package/packages/dd-trace/src/exporters/agent/index.js +1 -1
- package/packages/dd-trace/src/exporters/agent/writer.js +7 -32
- package/packages/dd-trace/src/exporters/{agent → common}/docker.js +0 -0
- package/packages/dd-trace/src/exporters/common/request.js +83 -0
- package/packages/dd-trace/src/exporters/common/writer.js +36 -0
- package/packages/dd-trace/src/exporters/{agent/scheduler.js → scheduler.js} +0 -0
- package/packages/dd-trace/src/format.js +9 -5
- package/packages/dd-trace/src/instrumenter.js +3 -0
- package/packages/dd-trace/src/pkg.js +11 -6
- package/packages/dd-trace/src/plugin_manager.js +13 -7
- package/packages/dd-trace/src/plugins/index.js +1 -2
- package/packages/dd-trace/src/plugins/log_plugin.js +8 -4
- package/packages/dd-trace/src/plugins/plugin.js +8 -0
- package/packages/dd-trace/src/plugins/util/test.js +79 -1
- package/packages/dd-trace/src/plugins/util/web.js +41 -12
- package/packages/dd-trace/src/profiling/config.js +8 -8
- package/packages/dd-trace/src/profiling/exporters/agent.js +1 -1
- package/packages/dd-trace/src/profiling/index.js +4 -4
- package/packages/dd-trace/src/profiling/profilers/{heap.js → space.js} +2 -2
- package/packages/dd-trace/src/profiling/profilers/{cpu.js → wall.js} +3 -3
- package/packages/dd-trace/src/proxy.js +2 -0
- package/packages/dd-trace/src/span_processor.js +4 -1
- package/packages/dd-trace/src/telemetry.js +187 -0
- package/scripts/install_plugin_modules.js +1 -0
- package/packages/datadog-plugin-fastify/src/fastify.js +0 -198
- package/packages/datadog-plugin-fastify/src/find-my-way.js +0 -37
- package/packages/datadog-plugin-jest/src/jest-environment.js +0 -272
- package/packages/datadog-plugin-jest/src/jest-jasmine2.js +0 -185
- package/packages/datadog-plugin-knex/src/index.js +0 -23
- package/packages/datadog-plugin-limitd-client/src/index.js +0 -30
- package/packages/dd-trace/src/exporters/agent/request.js +0 -86
- package/scripts/postpublish.js +0 -24
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "2.2",
|
|
3
3
|
"metadata": {
|
|
4
|
-
"rules_version": "1.3.
|
|
4
|
+
"rules_version": "1.3.1"
|
|
5
5
|
},
|
|
6
6
|
"rules": [
|
|
7
7
|
{
|
|
@@ -3040,7 +3040,9 @@
|
|
|
3040
3040
|
"operator": "match_regex"
|
|
3041
3041
|
}
|
|
3042
3042
|
],
|
|
3043
|
-
"transformers": [
|
|
3043
|
+
"transformers": [
|
|
3044
|
+
"keys_only"
|
|
3045
|
+
]
|
|
3044
3046
|
},
|
|
3045
3047
|
{
|
|
3046
3048
|
"id": "crs-942-360",
|
|
@@ -4097,15 +4099,23 @@
|
|
|
4097
4099
|
"parameters": {
|
|
4098
4100
|
"inputs": [
|
|
4099
4101
|
{
|
|
4100
|
-
"address": "server.request.
|
|
4102
|
+
"address": "server.request.query"
|
|
4103
|
+
},
|
|
4104
|
+
{
|
|
4105
|
+
"address": "server.request.body"
|
|
4106
|
+
},
|
|
4107
|
+
{
|
|
4108
|
+
"address": "server.request.path_params"
|
|
4101
4109
|
}
|
|
4102
4110
|
],
|
|
4103
|
-
"regex": "
|
|
4111
|
+
"regex": "^\\$(eq|ne|(l|g)te?|n?in|not|(n|x|)or|and|regex|where|expr|exists)$"
|
|
4104
4112
|
},
|
|
4105
4113
|
"operator": "match_regex"
|
|
4106
4114
|
}
|
|
4107
4115
|
],
|
|
4108
|
-
"transformers": [
|
|
4116
|
+
"transformers": [
|
|
4117
|
+
"keys_only"
|
|
4118
|
+
]
|
|
4109
4119
|
},
|
|
4110
4120
|
{
|
|
4111
4121
|
"id": "sqr-000-008",
|
|
@@ -35,6 +35,8 @@ const RESPONSE_HEADERS_PASSLIST = [
|
|
|
35
35
|
'content-type'
|
|
36
36
|
]
|
|
37
37
|
|
|
38
|
+
const metricsQueue = new Map()
|
|
39
|
+
|
|
38
40
|
function resolveHTTPRequest (context) {
|
|
39
41
|
if (!context) return {}
|
|
40
42
|
|
|
@@ -82,6 +84,24 @@ function formatHeaderName (name) {
|
|
|
82
84
|
.toLowerCase()
|
|
83
85
|
}
|
|
84
86
|
|
|
87
|
+
function reportMetrics (metrics, store) {
|
|
88
|
+
const req = store && store.get('req')
|
|
89
|
+
const topSpan = web.root(req)
|
|
90
|
+
if (!topSpan) return false
|
|
91
|
+
|
|
92
|
+
if (metrics.duration) {
|
|
93
|
+
topSpan.setTag('_dd.appsec.waf.duration', metrics.duration)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (metrics.durationExt) {
|
|
97
|
+
topSpan.setTag('_dd.appsec.waf.duration_ext', metrics.durationExt)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (metrics.rulesVersion) {
|
|
101
|
+
topSpan.setTag('_dd.appsec.event_rules.version', metrics.rulesVersion)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
85
105
|
function reportAttack (attackData, store) {
|
|
86
106
|
const req = store && store.get('req')
|
|
87
107
|
const topSpan = web.root(req)
|
|
@@ -129,9 +149,17 @@ function reportAttack (attackData, store) {
|
|
|
129
149
|
topSpan.addTags(newTags)
|
|
130
150
|
}
|
|
131
151
|
|
|
132
|
-
function
|
|
152
|
+
function finishRequest (req, context) {
|
|
133
153
|
const topSpan = web.root(req)
|
|
134
|
-
if (!topSpan
|
|
154
|
+
if (!topSpan) return false
|
|
155
|
+
|
|
156
|
+
if (metricsQueue.size) {
|
|
157
|
+
topSpan.addTags(Object.fromEntries(metricsQueue))
|
|
158
|
+
|
|
159
|
+
metricsQueue.clear()
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (!context || !topSpan.context()._tags['appsec.event']) return false
|
|
135
163
|
|
|
136
164
|
const resolvedResponse = resolveHTTPResponse(context)
|
|
137
165
|
|
|
@@ -149,11 +177,13 @@ function setRateLimit (rateLimit) {
|
|
|
149
177
|
}
|
|
150
178
|
|
|
151
179
|
module.exports = {
|
|
180
|
+
metricsQueue,
|
|
152
181
|
resolveHTTPRequest,
|
|
153
182
|
resolveHTTPResponse,
|
|
154
183
|
filterHeaders,
|
|
155
184
|
formatHeaderName,
|
|
185
|
+
reportMetrics,
|
|
156
186
|
reportAttack,
|
|
157
|
-
|
|
187
|
+
finishRequest,
|
|
158
188
|
setRateLimit
|
|
159
189
|
}
|
|
@@ -4,11 +4,11 @@ const callbacks = require('./callbacks')
|
|
|
4
4
|
|
|
5
5
|
const appliedCallbacks = new Map()
|
|
6
6
|
|
|
7
|
-
function applyRules (rules) {
|
|
7
|
+
function applyRules (rules, config) {
|
|
8
8
|
if (appliedCallbacks.has(rules)) return
|
|
9
9
|
|
|
10
10
|
// for now there is only WAF
|
|
11
|
-
const callback = new callbacks.DDWAF(rules)
|
|
11
|
+
const callback = new callbacks.DDWAF(rules, config)
|
|
12
12
|
|
|
13
13
|
appliedCallbacks.set(rules, callback)
|
|
14
14
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const URL = require('url').URL
|
|
4
|
+
const Writer = require('./writer')
|
|
5
|
+
const Scheduler = require('../../../exporters/scheduler')
|
|
6
|
+
|
|
7
|
+
class AgentlessCiVisibilityExporter {
|
|
8
|
+
constructor (config) {
|
|
9
|
+
const { flushInterval, tags, site, url } = config
|
|
10
|
+
this._url = url || new URL(`https://citestcycle-intake.${site}`)
|
|
11
|
+
this._writer = new Writer({ url: this._url, tags })
|
|
12
|
+
|
|
13
|
+
if (flushInterval > 0) {
|
|
14
|
+
this._scheduler = new Scheduler(() => this._writer.flush(), flushInterval)
|
|
15
|
+
}
|
|
16
|
+
this._scheduler && this._scheduler.start()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export (trace) {
|
|
20
|
+
this._writer.append(trace)
|
|
21
|
+
|
|
22
|
+
if (!this._scheduler) {
|
|
23
|
+
this._writer.flush()
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
flush () {
|
|
28
|
+
this._writer.flush()
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = AgentlessCiVisibilityExporter
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
const request = require('../../../exporters/common/request')
|
|
3
|
+
const log = require('../../../log')
|
|
4
|
+
|
|
5
|
+
const { AgentlessCiVisibilityEncoder } = require('../../../encode/agentless-ci-visibility')
|
|
6
|
+
const BaseWriter = require('../../../exporters/common/writer')
|
|
7
|
+
|
|
8
|
+
class Writer extends BaseWriter {
|
|
9
|
+
constructor ({ url, tags }) {
|
|
10
|
+
super(...arguments)
|
|
11
|
+
const { 'runtime-id': runtimeId, env, service } = tags
|
|
12
|
+
this._url = url
|
|
13
|
+
this._encoder = new AgentlessCiVisibilityEncoder({ runtimeId, env, service })
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
_sendPayload (data, _, done) {
|
|
17
|
+
makeRequest(data, this._url, (err, res) => {
|
|
18
|
+
if (err) {
|
|
19
|
+
log.error(err)
|
|
20
|
+
done()
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
log.debug(`Response from the intake: ${res}`)
|
|
24
|
+
done()
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function makeRequest (data, url, cb) {
|
|
30
|
+
const options = {
|
|
31
|
+
path: '/api/v2/citestcycle',
|
|
32
|
+
method: 'POST',
|
|
33
|
+
headers: {
|
|
34
|
+
'Content-Type': 'application/msgpack',
|
|
35
|
+
'dd-api-key': process.env.DATADOG_API_KEY || process.env.DD_API_KEY
|
|
36
|
+
},
|
|
37
|
+
timeout: 15000
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
options.protocol = url.protocol
|
|
41
|
+
options.hostname = url.hostname
|
|
42
|
+
options.port = url.port
|
|
43
|
+
|
|
44
|
+
log.debug(() => `Request to the intake: ${JSON.stringify(options)}`)
|
|
45
|
+
|
|
46
|
+
request(data, options, false, (err, res) => {
|
|
47
|
+
cb(err, res)
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = Writer
|
|
@@ -66,6 +66,7 @@ class Config {
|
|
|
66
66
|
process.env.DD_TRACE_URL,
|
|
67
67
|
null
|
|
68
68
|
)
|
|
69
|
+
const DD_CIVISIBILITY_AGENTLESS_URL = process.env.DD_CIVISIBILITY_AGENTLESS_URL
|
|
69
70
|
const DD_SERVICE = options.service ||
|
|
70
71
|
process.env.DD_SERVICE ||
|
|
71
72
|
process.env.DD_SERVICE_NAME ||
|
|
@@ -90,6 +91,10 @@ class Config {
|
|
|
90
91
|
process.env.DD_TRACE_STARTUP_LOGS,
|
|
91
92
|
false
|
|
92
93
|
)
|
|
94
|
+
const DD_TRACE_TELEMETRY_ENABLED = coalesce(
|
|
95
|
+
process.env.DD_TRACE_TELEMETRY_ENABLED,
|
|
96
|
+
true
|
|
97
|
+
)
|
|
93
98
|
const DD_TRACE_DEBUG = coalesce(
|
|
94
99
|
process.env.DD_TRACE_DEBUG,
|
|
95
100
|
false
|
|
@@ -145,10 +150,29 @@ class Config {
|
|
|
145
150
|
path.join(__dirname, 'appsec', 'recommended.json')
|
|
146
151
|
)
|
|
147
152
|
const DD_APPSEC_TRACE_RATE_LIMIT = coalesce(
|
|
148
|
-
appsec.rateLimit,
|
|
149
|
-
process.env.DD_APPSEC_TRACE_RATE_LIMIT,
|
|
153
|
+
parseInt(appsec.rateLimit),
|
|
154
|
+
parseInt(process.env.DD_APPSEC_TRACE_RATE_LIMIT),
|
|
150
155
|
100
|
|
151
156
|
)
|
|
157
|
+
const DD_APPSEC_WAF_TIMEOUT = coalesce(
|
|
158
|
+
parseInt(appsec.wafTimeout),
|
|
159
|
+
parseInt(process.env.DD_APPSEC_WAF_TIMEOUT),
|
|
160
|
+
5e3 // µs
|
|
161
|
+
)
|
|
162
|
+
const DD_APPSEC_OBFUSCATION_PARAMETER_KEY_REGEXP = coalesce(
|
|
163
|
+
appsec.obfuscatorKeyRegex,
|
|
164
|
+
process.env.DD_APPSEC_OBFUSCATION_PARAMETER_KEY_REGEXP,
|
|
165
|
+
`(?i)(?:p(?:ass)?w(?:or)?d|pass(?:_?phrase)?|secret|(?:api_?|private_?|public_?)key)|token|consumer_?(?:id|key|se\
|
|
166
|
+
cret)|sign(?:ed|ature)|bearer|authorization`
|
|
167
|
+
)
|
|
168
|
+
const DD_APPSEC_OBFUSCATION_PARAMETER_VALUE_REGEXP = coalesce(
|
|
169
|
+
appsec.obfuscatorValueRegex,
|
|
170
|
+
process.env.DD_APPSEC_OBFUSCATION_PARAMETER_VALUE_REGEXP,
|
|
171
|
+
`(?i)(?:p(?:ass)?w(?:or)?d|pass(?:_?phrase)?|secret|(?:api_?|private_?|public_?|access_?|secret_?)key(?:_?id)?|to\
|
|
172
|
+
ken|consumer_?(?:id|key|secret)|sign(?:ed|ature)?|auth(?:entication|orization)?)(?:\\s*=[^;]|"\\s*:\\s*"[^"]+")|bearer\
|
|
173
|
+
\\s+[a-z0-9\\._\\-]+|token:[a-z0-9]{13}|gh[opsu]_[0-9a-zA-Z]{36}|ey[I-L][\\w=-]+\\.ey[I-L][\\w=-]+(?:\\.[\\w.+\\/=-]+)?\
|
|
174
|
+
|[\\-]{5}BEGIN[a-z\\s]+PRIVATE\\sKEY[\\-]{5}[^\\-]+[\\-]{5}END[a-z\\s]+PRIVATE\\sKEY|ssh-rsa\\s*[a-z0-9\\/\\.+]{100,}`
|
|
175
|
+
)
|
|
152
176
|
|
|
153
177
|
const sampler = (options.experimental && options.experimental.sampler) || {}
|
|
154
178
|
const ingestion = options.ingestion || {}
|
|
@@ -171,7 +195,8 @@ class Config {
|
|
|
171
195
|
this.debug = isTrue(DD_TRACE_DEBUG)
|
|
172
196
|
this.logInjection = isTrue(DD_LOGS_INJECTION)
|
|
173
197
|
this.env = DD_ENV
|
|
174
|
-
this.url =
|
|
198
|
+
this.url = DD_CIVISIBILITY_AGENTLESS_URL ? new URL(DD_CIVISIBILITY_AGENTLESS_URL)
|
|
199
|
+
: getAgentUrl(DD_TRACE_AGENT_URL, options)
|
|
175
200
|
this.site = coalesce(options.site, process.env.DD_SITE, 'datadoghq.com')
|
|
176
201
|
this.hostname = DD_AGENT_HOST || (this.url && this.url.hostname)
|
|
177
202
|
this.port = String(DD_TRACE_AGENT_PORT || (this.url && this.url.port))
|
|
@@ -212,11 +237,15 @@ class Config {
|
|
|
212
237
|
}
|
|
213
238
|
this.lookup = options.lookup
|
|
214
239
|
this.startupLogs = isTrue(DD_TRACE_STARTUP_LOGS)
|
|
240
|
+
this.telemetryEnabled = isTrue(DD_TRACE_TELEMETRY_ENABLED)
|
|
215
241
|
this.protocolVersion = DD_TRACE_AGENT_PROTOCOL_VERSION
|
|
216
242
|
this.appsec = {
|
|
217
243
|
enabled: isTrue(DD_APPSEC_ENABLED),
|
|
218
244
|
rules: DD_APPSEC_RULES,
|
|
219
|
-
rateLimit: DD_APPSEC_TRACE_RATE_LIMIT
|
|
245
|
+
rateLimit: DD_APPSEC_TRACE_RATE_LIMIT,
|
|
246
|
+
wafTimeout: DD_APPSEC_WAF_TIMEOUT,
|
|
247
|
+
obfuscatorKeyRegex: DD_APPSEC_OBFUSCATION_PARAMETER_KEY_REGEXP,
|
|
248
|
+
obfuscatorValueRegex: DD_APPSEC_OBFUSCATION_PARAMETER_VALUE_REGEXP
|
|
220
249
|
}
|
|
221
250
|
|
|
222
251
|
tagger.add(this.tags, {
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
const { truncateSpan, normalizeSpan } = require('./tags-processors')
|
|
3
|
+
const Chunk = require('./chunk')
|
|
4
|
+
const { AgentEncoder } = require('./0.4')
|
|
5
|
+
|
|
6
|
+
const ENCODING_VERSION = 1
|
|
7
|
+
|
|
8
|
+
function formatSpan (span) {
|
|
9
|
+
return {
|
|
10
|
+
type: span.type === 'test' ? 'test' : 'span',
|
|
11
|
+
version: ENCODING_VERSION,
|
|
12
|
+
content: normalizeSpan(truncateSpan(span))
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
class AgentlessCiVisibilityEncoder extends AgentEncoder {
|
|
17
|
+
constructor ({ runtimeId, service, env }) {
|
|
18
|
+
super(...arguments)
|
|
19
|
+
this._events = []
|
|
20
|
+
this.runtimeId = runtimeId
|
|
21
|
+
this.service = service
|
|
22
|
+
this.env = env
|
|
23
|
+
this._traceBytes = new Chunk()
|
|
24
|
+
this._stringBytes = new Chunk()
|
|
25
|
+
this._stringCount = 0
|
|
26
|
+
this._stringMap = {}
|
|
27
|
+
|
|
28
|
+
// Used to keep track of the number of encoded events to update the
|
|
29
|
+
// length of `payload.events` when calling `makePayload`
|
|
30
|
+
this._eventCount = 0
|
|
31
|
+
|
|
32
|
+
this.reset()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
_encodeEventContent (bytes, content) {
|
|
36
|
+
this._encodeMapPrefix(bytes, content)
|
|
37
|
+
if (content.type) {
|
|
38
|
+
this._encodeString(bytes, 'type')
|
|
39
|
+
this._encodeString(bytes, content.type)
|
|
40
|
+
}
|
|
41
|
+
this._encodeString(bytes, 'trace_id')
|
|
42
|
+
this._encodeId(bytes, content.trace_id)
|
|
43
|
+
this._encodeString(bytes, 'span_id')
|
|
44
|
+
this._encodeId(bytes, content.span_id)
|
|
45
|
+
this._encodeString(bytes, 'parent_id')
|
|
46
|
+
this._encodeId(bytes, content.parent_id)
|
|
47
|
+
this._encodeString(bytes, 'name')
|
|
48
|
+
this._encodeString(bytes, content.name)
|
|
49
|
+
this._encodeString(bytes, 'resource')
|
|
50
|
+
this._encodeString(bytes, content.resource)
|
|
51
|
+
this._encodeString(bytes, 'service')
|
|
52
|
+
this._encodeString(bytes, content.service)
|
|
53
|
+
this._encodeString(bytes, 'error')
|
|
54
|
+
this._encodeNumber(bytes, content.error)
|
|
55
|
+
this._encodeString(bytes, 'start')
|
|
56
|
+
this._encodeNumber(bytes, content.start)
|
|
57
|
+
this._encodeString(bytes, 'duration')
|
|
58
|
+
this._encodeNumber(bytes, content.duration)
|
|
59
|
+
this._encodeString(bytes, 'meta')
|
|
60
|
+
this._encodeMap(bytes, content.meta)
|
|
61
|
+
this._encodeString(bytes, 'metrics')
|
|
62
|
+
this._encodeMap(bytes, content.metrics)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
_encodeEvent (bytes, event) {
|
|
66
|
+
this._encodeMapPrefix(bytes, event)
|
|
67
|
+
this._encodeString(bytes, 'type')
|
|
68
|
+
this._encodeString(bytes, event.type)
|
|
69
|
+
|
|
70
|
+
this._encodeString(bytes, 'version')
|
|
71
|
+
this._encodeNumber(bytes, event.version)
|
|
72
|
+
|
|
73
|
+
this._encodeString(bytes, 'content')
|
|
74
|
+
this._encodeEventContent(bytes, event.content)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
_encodeNumber (bytes, value) {
|
|
78
|
+
if (Math.floor(value) !== value) { // float 64
|
|
79
|
+
return this._encodeFloat(bytes, value)
|
|
80
|
+
}
|
|
81
|
+
return this._encodeLong(bytes, value)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
_encodeLong (bytes, value) {
|
|
85
|
+
const isPositive = value >= 0
|
|
86
|
+
|
|
87
|
+
const hi = isPositive ? (value / Math.pow(2, 32)) >> 0 : Math.floor(value / Math.pow(2, 32))
|
|
88
|
+
const lo = value >>> 0
|
|
89
|
+
const flag = isPositive ? 0xcf : 0xd3
|
|
90
|
+
|
|
91
|
+
const buffer = bytes.buffer
|
|
92
|
+
const offset = bytes.length
|
|
93
|
+
|
|
94
|
+
// int 64
|
|
95
|
+
bytes.reserve(9)
|
|
96
|
+
bytes.length += 9
|
|
97
|
+
|
|
98
|
+
buffer[offset] = flag
|
|
99
|
+
buffer[offset + 1] = hi >> 24
|
|
100
|
+
buffer[offset + 2] = hi >> 16
|
|
101
|
+
buffer[offset + 3] = hi >> 8
|
|
102
|
+
buffer[offset + 4] = hi
|
|
103
|
+
buffer[offset + 5] = lo >> 24
|
|
104
|
+
buffer[offset + 6] = lo >> 16
|
|
105
|
+
buffer[offset + 7] = lo >> 8
|
|
106
|
+
buffer[offset + 8] = lo
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
_encodeMapPrefix (bytes, map) {
|
|
110
|
+
const keys = Object.keys(map)
|
|
111
|
+
const buffer = bytes.buffer
|
|
112
|
+
const offset = bytes.length
|
|
113
|
+
|
|
114
|
+
bytes.reserve(5)
|
|
115
|
+
bytes.length += 5
|
|
116
|
+
buffer[offset] = 0xdf
|
|
117
|
+
buffer[offset + 1] = keys.length >> 24
|
|
118
|
+
buffer[offset + 2] = keys.length >> 16
|
|
119
|
+
buffer[offset + 3] = keys.length >> 8
|
|
120
|
+
buffer[offset + 4] = keys.length
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
_encode (bytes, trace) {
|
|
124
|
+
this._eventCount += trace.length
|
|
125
|
+
const events = trace.map(formatSpan)
|
|
126
|
+
|
|
127
|
+
for (const event of events) {
|
|
128
|
+
this._encodeEvent(bytes, event)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
makePayload () {
|
|
133
|
+
const bytes = this._traceBytes
|
|
134
|
+
const eventsOffset = this._eventsOffset
|
|
135
|
+
const eventsCount = this._eventCount
|
|
136
|
+
|
|
137
|
+
bytes.buffer[eventsOffset] = 0xdd
|
|
138
|
+
bytes.buffer[eventsOffset + 1] = eventsCount >> 24
|
|
139
|
+
bytes.buffer[eventsOffset + 2] = eventsCount >> 16
|
|
140
|
+
bytes.buffer[eventsOffset + 3] = eventsCount >> 8
|
|
141
|
+
bytes.buffer[eventsOffset + 4] = eventsCount
|
|
142
|
+
|
|
143
|
+
const traceSize = bytes.length
|
|
144
|
+
const buffer = Buffer.allocUnsafe(traceSize)
|
|
145
|
+
|
|
146
|
+
bytes.buffer.copy(buffer, 0, 0, bytes.length)
|
|
147
|
+
|
|
148
|
+
this.reset()
|
|
149
|
+
|
|
150
|
+
return buffer
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
_encodePayloadStart (bytes) {
|
|
154
|
+
// encodes the payload up to `events`. `events` will be encoded via _encode
|
|
155
|
+
const payload = {
|
|
156
|
+
version: ENCODING_VERSION,
|
|
157
|
+
metadata: {
|
|
158
|
+
'*': {
|
|
159
|
+
'language': 'javascript'
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
events: []
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (this.env) {
|
|
166
|
+
payload.metadata['*'].env = this.env
|
|
167
|
+
}
|
|
168
|
+
if (this.runtimeId) {
|
|
169
|
+
payload.metadata['*']['runtime-id'] = this.runtimeId
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
this._encodeMapPrefix(bytes, payload)
|
|
173
|
+
this._encodeString(bytes, 'version')
|
|
174
|
+
this._encodeNumber(bytes, payload.version)
|
|
175
|
+
this._encodeString(bytes, 'metadata')
|
|
176
|
+
this._encodeMapPrefix(bytes, payload.metadata)
|
|
177
|
+
this._encodeString(bytes, '*')
|
|
178
|
+
this._encodeMap(bytes, payload.metadata['*'])
|
|
179
|
+
this._encodeString(bytes, 'events')
|
|
180
|
+
// Get offset of the events list to update the length of the array when calling `makePayload`
|
|
181
|
+
this._eventsOffset = bytes.length
|
|
182
|
+
bytes.reserve(5)
|
|
183
|
+
bytes.length += 5
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
reset () {
|
|
187
|
+
this._reset()
|
|
188
|
+
this._eventCount = 0
|
|
189
|
+
this._encodePayloadStart(this._traceBytes)
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
module.exports = { AgentlessCiVisibilityEncoder }
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// From agent truncators: https://github.com/DataDog/datadog-agent/blob/main/pkg/trace/agent/truncator.go
|
|
2
|
+
|
|
3
|
+
// Values from: https://github.com/DataDog/datadog-agent/blob/main/pkg/trace/traceutil/truncate.go#L22-L27
|
|
4
|
+
// MAX_RESOURCE_NAME_LENGTH the maximum length a span resource can have
|
|
5
|
+
const MAX_RESOURCE_NAME_LENGTH = 5000
|
|
6
|
+
// MAX_META_KEY_LENGTH the maximum length of metadata key
|
|
7
|
+
const MAX_META_KEY_LENGTH = 200
|
|
8
|
+
// MAX_META_VALUE_LENGTH the maximum length of metadata value
|
|
9
|
+
const MAX_META_VALUE_LENGTH = 25000
|
|
10
|
+
// MAX_METRIC_KEY_LENGTH the maximum length of a metric name key
|
|
11
|
+
const MAX_METRIC_KEY_LENGTH = MAX_META_KEY_LENGTH
|
|
12
|
+
// MAX_METRIC_VALUE_LENGTH the maximum length of a metric name value
|
|
13
|
+
const MAX_METRIC_VALUE_LENGTH = MAX_META_VALUE_LENGTH
|
|
14
|
+
|
|
15
|
+
// From agent normalizer:
|
|
16
|
+
// https://github.com/DataDog/datadog-agent/blob/main/pkg/trace/traceutil/normalize.go
|
|
17
|
+
// DEFAULT_SPAN_NAME is the default name we assign a span if it's missing and we have no reasonable fallback
|
|
18
|
+
const DEFAULT_SPAN_NAME = 'unnamed_operation'
|
|
19
|
+
// DEFAULT_SERVICE_NAME is the default name we assign a service if it's missing and we have no reasonable fallback
|
|
20
|
+
const DEFAULT_SERVICE_NAME = 'unnamed-service'
|
|
21
|
+
// MAX_NAME_LENGTH the maximum length a name can have
|
|
22
|
+
const MAX_NAME_LENGTH = 100
|
|
23
|
+
// MAX_SERVICE_LENGTH the maximum length a service can have
|
|
24
|
+
const MAX_SERVICE_LENGTH = 100
|
|
25
|
+
// MAX_TYPE_LENGTH the maximum length a span type can have
|
|
26
|
+
const MAX_TYPE_LENGTH = 100
|
|
27
|
+
|
|
28
|
+
const fromEntries = Object.fromEntries || (entries =>
|
|
29
|
+
entries.reduce((obj, [k, v]) => Object.assign(obj, { [k]: v }), {}))
|
|
30
|
+
|
|
31
|
+
function truncateToLength (value, maxLength) {
|
|
32
|
+
if (!value) {
|
|
33
|
+
return value
|
|
34
|
+
}
|
|
35
|
+
if (value.length > maxLength) {
|
|
36
|
+
return `${value.slice(0, maxLength)}...`
|
|
37
|
+
}
|
|
38
|
+
return value
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function truncateSpan (span) {
|
|
42
|
+
return fromEntries(Object.entries(span).map(([key, value]) => {
|
|
43
|
+
switch (key) {
|
|
44
|
+
case 'resource':
|
|
45
|
+
return ['resource', truncateToLength(value, MAX_RESOURCE_NAME_LENGTH)]
|
|
46
|
+
case 'meta':
|
|
47
|
+
return ['meta', fromEntries(Object.entries(value).map(([metaKey, metaValue]) =>
|
|
48
|
+
[truncateToLength(metaKey, MAX_META_KEY_LENGTH), truncateToLength(metaValue, MAX_META_VALUE_LENGTH)]
|
|
49
|
+
))]
|
|
50
|
+
case 'metrics':
|
|
51
|
+
return ['metrics', fromEntries(Object.entries(value).map(([metricsKey, metricsValue]) =>
|
|
52
|
+
[truncateToLength(metricsKey, MAX_METRIC_KEY_LENGTH), truncateToLength(metricsValue, MAX_METRIC_VALUE_LENGTH)]
|
|
53
|
+
))]
|
|
54
|
+
default:
|
|
55
|
+
return [key, value]
|
|
56
|
+
}
|
|
57
|
+
}))
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function normalizeSpan (span) {
|
|
61
|
+
const normalizedSpan = fromEntries(Object.entries(span).map(([key, value]) => {
|
|
62
|
+
switch (key) {
|
|
63
|
+
case 'service':
|
|
64
|
+
if (!value) {
|
|
65
|
+
return [key, DEFAULT_SERVICE_NAME]
|
|
66
|
+
}
|
|
67
|
+
if (value.length > MAX_SERVICE_LENGTH) {
|
|
68
|
+
return [key, value.slice(0, MAX_SERVICE_LENGTH)]
|
|
69
|
+
}
|
|
70
|
+
break
|
|
71
|
+
case 'name':
|
|
72
|
+
if (!value) {
|
|
73
|
+
return [key, DEFAULT_SPAN_NAME]
|
|
74
|
+
}
|
|
75
|
+
if (value.length > MAX_NAME_LENGTH) {
|
|
76
|
+
return [key, value.slice(0, MAX_NAME_LENGTH)]
|
|
77
|
+
}
|
|
78
|
+
break
|
|
79
|
+
case 'resource':
|
|
80
|
+
if (!value) {
|
|
81
|
+
return [key, span.name || DEFAULT_SPAN_NAME]
|
|
82
|
+
}
|
|
83
|
+
break
|
|
84
|
+
case 'type':
|
|
85
|
+
if (!value) {
|
|
86
|
+
return [key, value]
|
|
87
|
+
}
|
|
88
|
+
if (value.length > MAX_TYPE_LENGTH) {
|
|
89
|
+
return [key, value.slice(0, MAX_TYPE_LENGTH)]
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return [key, value]
|
|
93
|
+
}))
|
|
94
|
+
if (!normalizedSpan.service) {
|
|
95
|
+
normalizedSpan.service = DEFAULT_SERVICE_NAME
|
|
96
|
+
}
|
|
97
|
+
if (!normalizedSpan.name) {
|
|
98
|
+
normalizedSpan.name = DEFAULT_SPAN_NAME
|
|
99
|
+
}
|
|
100
|
+
return normalizedSpan
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
module.exports = {
|
|
104
|
+
truncateSpan,
|
|
105
|
+
normalizeSpan,
|
|
106
|
+
MAX_META_KEY_LENGTH,
|
|
107
|
+
MAX_META_VALUE_LENGTH,
|
|
108
|
+
MAX_METRIC_KEY_LENGTH,
|
|
109
|
+
MAX_METRIC_VALUE_LENGTH,
|
|
110
|
+
MAX_NAME_LENGTH,
|
|
111
|
+
MAX_SERVICE_LENGTH,
|
|
112
|
+
MAX_TYPE_LENGTH,
|
|
113
|
+
MAX_RESOURCE_NAME_LENGTH,
|
|
114
|
+
DEFAULT_SPAN_NAME,
|
|
115
|
+
DEFAULT_SERVICE_NAME
|
|
116
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const AgentExporter = require('./exporters/agent')
|
|
4
4
|
const LogExporter = require('./exporters/log')
|
|
5
|
+
const AgentlessCiVisibilityExporter = require('./ci-visibility/exporters/agentless')
|
|
5
6
|
const exporters = require('../../../ext/exporters')
|
|
6
7
|
const fs = require('fs')
|
|
7
8
|
const constants = require('./constants')
|
|
@@ -15,6 +16,8 @@ module.exports = name => {
|
|
|
15
16
|
return LogExporter
|
|
16
17
|
case exporters.AGENT:
|
|
17
18
|
return AgentExporter
|
|
19
|
+
case exporters.DATADOG:
|
|
20
|
+
return AgentlessCiVisibilityExporter
|
|
18
21
|
default:
|
|
19
22
|
return inAWSLambda && !usingLambdaExtension ? LogExporter : AgentExporter
|
|
20
23
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const URL = require('url').URL
|
|
4
4
|
const log = require('../../log')
|
|
5
5
|
const Writer = require('./writer')
|
|
6
|
-
const Scheduler = require('
|
|
6
|
+
const Scheduler = require('../scheduler')
|
|
7
7
|
|
|
8
8
|
class AgentExporter {
|
|
9
9
|
constructor ({ url, hostname, port, flushInterval, lookup, protocolVersion }, prioritySampler) {
|