dd-trace 4.45.0 → 4.47.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 -0
- package/index.d.ts +20 -8
- package/package.json +11 -5
- package/packages/datadog-instrumentations/src/aerospike.js +1 -1
- package/packages/datadog-instrumentations/src/apollo-server.js +1 -1
- package/packages/datadog-instrumentations/src/aws-sdk.js +4 -4
- package/packages/datadog-instrumentations/src/body-parser.js +4 -4
- package/packages/datadog-instrumentations/src/cassandra-driver.js +2 -2
- package/packages/datadog-instrumentations/src/child_process.js +2 -2
- package/packages/datadog-instrumentations/src/connect.js +4 -4
- package/packages/datadog-instrumentations/src/cookie-parser.js +4 -4
- package/packages/datadog-instrumentations/src/couchbase.js +12 -12
- package/packages/datadog-instrumentations/src/cucumber.js +294 -56
- package/packages/datadog-instrumentations/src/dns.js +10 -10
- package/packages/datadog-instrumentations/src/elasticsearch.js +4 -4
- package/packages/datadog-instrumentations/src/express-mongo-sanitize.js +3 -3
- package/packages/datadog-instrumentations/src/express.js +4 -4
- package/packages/datadog-instrumentations/src/fastify.js +6 -6
- package/packages/datadog-instrumentations/src/fetch.js +1 -1
- package/packages/datadog-instrumentations/src/find-my-way.js +2 -2
- package/packages/datadog-instrumentations/src/fs.js +2 -2
- package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +2 -2
- package/packages/datadog-instrumentations/src/grpc/client.js +4 -6
- package/packages/datadog-instrumentations/src/grpc/server.js +2 -2
- package/packages/datadog-instrumentations/src/hapi.js +10 -13
- package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
- package/packages/datadog-instrumentations/src/http/client.js +3 -3
- package/packages/datadog-instrumentations/src/jest.js +8 -5
- package/packages/datadog-instrumentations/src/kafkajs.js +67 -31
- package/packages/datadog-instrumentations/src/knex.js +2 -2
- package/packages/datadog-instrumentations/src/koa.js +5 -5
- package/packages/datadog-instrumentations/src/ldapjs.js +1 -1
- package/packages/datadog-instrumentations/src/mariadb.js +8 -8
- package/packages/datadog-instrumentations/src/memcached.js +2 -2
- package/packages/datadog-instrumentations/src/microgateway-core.js +7 -5
- package/packages/datadog-instrumentations/src/mocha/common.js +1 -1
- package/packages/datadog-instrumentations/src/mocha/main.js +139 -53
- package/packages/datadog-instrumentations/src/mocha/utils.js +37 -18
- package/packages/datadog-instrumentations/src/mocha/worker.js +29 -1
- package/packages/datadog-instrumentations/src/mocha.js +4 -0
- package/packages/datadog-instrumentations/src/moleculer/server.js +2 -2
- package/packages/datadog-instrumentations/src/mongodb-core.js +7 -7
- package/packages/datadog-instrumentations/src/mongoose.js +5 -6
- package/packages/datadog-instrumentations/src/mysql.js +3 -3
- package/packages/datadog-instrumentations/src/mysql2.js +6 -6
- package/packages/datadog-instrumentations/src/net.js +2 -2
- package/packages/datadog-instrumentations/src/next.js +5 -5
- package/packages/datadog-instrumentations/src/openai.js +62 -71
- package/packages/datadog-instrumentations/src/oracledb.js +8 -8
- package/packages/datadog-instrumentations/src/passport-http.js +1 -1
- package/packages/datadog-instrumentations/src/passport-local.js +1 -1
- package/packages/datadog-instrumentations/src/passport-utils.js +1 -1
- package/packages/datadog-instrumentations/src/pg.js +60 -5
- package/packages/datadog-instrumentations/src/pino.js +4 -4
- package/packages/datadog-instrumentations/src/playwright.js +6 -4
- package/packages/datadog-instrumentations/src/redis.js +2 -2
- package/packages/datadog-instrumentations/src/restify.js +4 -4
- package/packages/datadog-instrumentations/src/rhea.js +4 -4
- package/packages/datadog-instrumentations/src/router.js +5 -5
- package/packages/datadog-instrumentations/src/sharedb.js +2 -2
- package/packages/datadog-instrumentations/src/vitest.js +188 -12
- package/packages/datadog-instrumentations/src/winston.js +2 -3
- package/packages/datadog-plugin-amqplib/src/consumer.js +1 -3
- package/packages/datadog-plugin-aws-sdk/src/base.js +33 -0
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +2 -0
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -1
- package/packages/datadog-plugin-cucumber/src/index.js +24 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +39 -10
- package/packages/datadog-plugin-cypress/src/support.js +4 -1
- package/packages/datadog-plugin-hapi/src/index.js +2 -2
- package/packages/datadog-plugin-http/src/client.js +1 -42
- package/packages/datadog-plugin-http2/src/client.js +1 -26
- package/packages/datadog-plugin-jest/src/index.js +18 -1
- package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +20 -0
- package/packages/datadog-plugin-kafkajs/src/consumer.js +1 -2
- package/packages/datadog-plugin-kafkajs/src/index.js +3 -1
- package/packages/datadog-plugin-mocha/src/index.js +18 -0
- package/packages/datadog-plugin-openai/src/index.js +85 -65
- package/packages/datadog-plugin-playwright/src/index.js +9 -0
- package/packages/datadog-plugin-rhea/src/consumer.js +1 -3
- package/packages/datadog-plugin-vitest/src/index.js +68 -3
- package/packages/datadog-shimmer/src/shimmer.js +144 -10
- package/packages/dd-trace/src/appsec/addresses.js +3 -1
- package/packages/dd-trace/src/appsec/blocking.js +23 -17
- package/packages/dd-trace/src/appsec/channels.js +4 -2
- package/packages/dd-trace/src/appsec/graphql.js +3 -1
- package/packages/dd-trace/src/appsec/iast/iast-log.js +2 -1
- package/packages/dd-trace/src/appsec/rasp/index.js +103 -0
- package/packages/dd-trace/src/appsec/rasp/sql_injection.js +86 -0
- package/packages/dd-trace/src/appsec/rasp/ssrf.js +37 -0
- package/packages/dd-trace/src/appsec/rasp/utils.js +63 -0
- package/packages/dd-trace/src/appsec/remote_config/capabilities.js +2 -0
- package/packages/dd-trace/src/appsec/remote_config/index.js +16 -7
- package/packages/dd-trace/src/appsec/remote_config/manager.js +93 -52
- package/packages/dd-trace/src/appsec/rule_manager.js +8 -0
- package/packages/dd-trace/src/appsec/telemetry.js +3 -3
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +33 -14
- package/packages/dd-trace/src/appsec/waf/waf_manager.js +2 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +4 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +15 -1
- package/packages/dd-trace/src/config.js +100 -40
- package/packages/dd-trace/src/constants.js +11 -1
- package/packages/dd-trace/src/data_streams_context.js +3 -0
- package/packages/dd-trace/src/datastreams/fnv.js +23 -0
- package/packages/dd-trace/src/datastreams/pathway.js +12 -5
- package/packages/dd-trace/src/datastreams/processor.js +35 -0
- package/packages/dd-trace/src/datastreams/schemas/schema.js +8 -0
- package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +125 -0
- package/packages/dd-trace/src/datastreams/schemas/schema_sampler.js +29 -0
- package/packages/dd-trace/src/debugger/devtools_client/config.js +24 -0
- package/packages/dd-trace/src/debugger/devtools_client/index.js +57 -0
- package/packages/dd-trace/src/debugger/devtools_client/inspector_promises_polyfill.js +23 -0
- package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +164 -0
- package/packages/dd-trace/src/debugger/devtools_client/send.js +28 -0
- package/packages/dd-trace/src/debugger/devtools_client/session.js +7 -0
- package/packages/dd-trace/src/debugger/devtools_client/state.js +47 -0
- package/packages/dd-trace/src/debugger/devtools_client/status.js +109 -0
- package/packages/dd-trace/src/debugger/index.js +92 -0
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +29 -2
- package/packages/dd-trace/src/exporters/common/request.js +1 -1
- package/packages/dd-trace/src/lambda/handler.js +1 -0
- package/packages/dd-trace/src/lambda/index.js +12 -1
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +1 -6
- package/packages/dd-trace/src/payload-tagging/config/aws.json +30 -0
- package/packages/dd-trace/src/payload-tagging/config/index.js +30 -0
- package/packages/dd-trace/src/payload-tagging/index.js +93 -0
- package/packages/dd-trace/src/payload-tagging/tagging.js +83 -0
- package/packages/dd-trace/src/plugin_manager.js +11 -10
- package/packages/dd-trace/src/plugins/ci_plugin.js +33 -8
- package/packages/dd-trace/src/plugins/util/env.js +5 -2
- package/packages/dd-trace/src/plugins/util/test.js +24 -4
- package/packages/dd-trace/src/profiler.js +15 -5
- package/packages/dd-trace/src/profiling/config.js +7 -4
- package/packages/dd-trace/src/profiling/exporter_cli.js +13 -1
- package/packages/dd-trace/src/profiling/exporters/agent.js +8 -2
- package/packages/dd-trace/src/profiling/profiler.js +0 -9
- package/packages/dd-trace/src/profiling/profilers/event_plugins/dns.js +13 -0
- package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_lookup.js +16 -0
- package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_lookupservice.js +16 -0
- package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_resolve.js +24 -0
- package/packages/dd-trace/src/profiling/profilers/event_plugins/dns_reverse.js +16 -0
- package/packages/dd-trace/src/profiling/profilers/event_plugins/event.js +48 -0
- package/packages/dd-trace/src/profiling/profilers/event_plugins/net.js +24 -0
- package/packages/dd-trace/src/profiling/profilers/events.js +108 -32
- package/packages/dd-trace/src/profiling/profilers/shared.js +5 -0
- package/packages/dd-trace/src/profiling/profilers/wall.js +9 -3
- package/packages/dd-trace/src/profiling/ssi-heuristics.js +59 -60
- package/packages/dd-trace/src/proxy.js +31 -24
- package/packages/dd-trace/src/span_stats.js +4 -2
- package/packages/dd-trace/src/telemetry/index.js +23 -6
- package/packages/dd-trace/src/telemetry/logs/index.js +20 -0
- package/packages/dd-trace/src/appsec/rasp.js +0 -176
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const aws = require('./aws.json')
|
|
2
|
+
const sdks = { aws }
|
|
3
|
+
|
|
4
|
+
function getSDKRules (sdk, requestInput, responseInput) {
|
|
5
|
+
return Object.fromEntries(
|
|
6
|
+
Object.entries(sdk).map(([service, serviceRules]) => {
|
|
7
|
+
return [
|
|
8
|
+
service,
|
|
9
|
+
{
|
|
10
|
+
request: serviceRules.request.concat(requestInput || []),
|
|
11
|
+
response: serviceRules.response.concat(responseInput || []),
|
|
12
|
+
expand: serviceRules.expand || []
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
})
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function appendRules (requestInput, responseInput) {
|
|
20
|
+
return Object.fromEntries(
|
|
21
|
+
Object.entries(sdks).map(([name, sdk]) => {
|
|
22
|
+
return [
|
|
23
|
+
name,
|
|
24
|
+
getSDKRules(sdk, requestInput, responseInput)
|
|
25
|
+
]
|
|
26
|
+
})
|
|
27
|
+
)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = { appendRules }
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
const rfdc = require('rfdc')({ proto: false, circles: false })
|
|
2
|
+
|
|
3
|
+
const {
|
|
4
|
+
PAYLOAD_TAG_REQUEST_PREFIX,
|
|
5
|
+
PAYLOAD_TAG_RESPONSE_PREFIX
|
|
6
|
+
} = require('../constants')
|
|
7
|
+
|
|
8
|
+
const jsonpath = require('jsonpath-plus').JSONPath
|
|
9
|
+
|
|
10
|
+
const { tagsFromObject } = require('./tagging')
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Given an identified value, attempt to parse it as JSON if relevant
|
|
14
|
+
*
|
|
15
|
+
* @param {any} value
|
|
16
|
+
* @returns {any} the parsed object if parsing was successful, the input if not
|
|
17
|
+
*/
|
|
18
|
+
function maybeJSONParseValue (value) {
|
|
19
|
+
if (typeof value !== 'string' || value[0] !== '{') {
|
|
20
|
+
return value
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
return JSON.parse(value)
|
|
25
|
+
} catch (e) {
|
|
26
|
+
return value
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Apply expansion to all expansion JSONPath queries
|
|
32
|
+
*
|
|
33
|
+
* @param {Object} object
|
|
34
|
+
* @param {[String]} expansionRules list of JSONPath queries
|
|
35
|
+
*/
|
|
36
|
+
function expand (object, expansionRules) {
|
|
37
|
+
for (const rule of expansionRules) {
|
|
38
|
+
jsonpath(rule, object, (value, _type, desc) => {
|
|
39
|
+
desc.parent[desc.parentProperty] = maybeJSONParseValue(value)
|
|
40
|
+
})
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Apply redaction to all redaction JSONPath queries
|
|
46
|
+
*
|
|
47
|
+
* @param {Object} object
|
|
48
|
+
* @param {[String]} redactionRules
|
|
49
|
+
*/
|
|
50
|
+
function redact (object, redactionRules) {
|
|
51
|
+
for (const rule of redactionRules) {
|
|
52
|
+
jsonpath(rule, object, (_value, _type, desc) => {
|
|
53
|
+
desc.parent[desc.parentProperty] = 'redacted'
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Generate a map of tag names to tag values by performing:
|
|
60
|
+
* 1. Attempting to parse identified fields as JSON
|
|
61
|
+
* 2. Redacting fields identified by redaction rules
|
|
62
|
+
* 3. Flattening the resulting object, producing as many tag name/tag value pairs
|
|
63
|
+
* as there are leaf values in the object
|
|
64
|
+
* This function performs side-effects on a _copy_ of the input object.
|
|
65
|
+
*
|
|
66
|
+
* @param {Object} config sdk configuration for the service
|
|
67
|
+
* @param {[String]} config.expand expansion rules for the service
|
|
68
|
+
* @param {[String]} config.request redaction rules for the request
|
|
69
|
+
* @param {[String]} config.response redaction rules for the response
|
|
70
|
+
* @param {Object} object the input object to generate tags from
|
|
71
|
+
* @param {Object} opts tag generation options
|
|
72
|
+
* @param {String} opts.prefix prefix for all generated tags
|
|
73
|
+
* @param {number} opts.maxDepth maximum depth to traverse the object
|
|
74
|
+
* @returns
|
|
75
|
+
*/
|
|
76
|
+
function computeTags (config, object, opts) {
|
|
77
|
+
const payload = rfdc(object)
|
|
78
|
+
const redactionRules = opts.prefix === PAYLOAD_TAG_REQUEST_PREFIX ? config.request : config.response
|
|
79
|
+
const expansionRules = config.expand
|
|
80
|
+
expand(payload, expansionRules)
|
|
81
|
+
redact(payload, redactionRules)
|
|
82
|
+
return tagsFromObject(payload, opts)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function tagsFromRequest (config, object, opts) {
|
|
86
|
+
return computeTags(config, object, { ...opts, prefix: PAYLOAD_TAG_REQUEST_PREFIX })
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function tagsFromResponse (config, object, opts) {
|
|
90
|
+
return computeTags(config, object, { ...opts, prefix: PAYLOAD_TAG_RESPONSE_PREFIX })
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
module.exports = { computeTags, tagsFromRequest, tagsFromResponse }
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
const { PAYLOAD_TAGGING_MAX_TAGS } = require('../constants')
|
|
2
|
+
|
|
3
|
+
const redactedKeys = [
|
|
4
|
+
'authorization', 'x-authorization', 'password', 'token'
|
|
5
|
+
]
|
|
6
|
+
const truncated = 'truncated'
|
|
7
|
+
const redacted = 'redacted'
|
|
8
|
+
|
|
9
|
+
function escapeKey (key) {
|
|
10
|
+
return key.replaceAll('.', '\\.')
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Compute normalized payload tags from any given object.
|
|
15
|
+
*
|
|
16
|
+
* @param {object} object
|
|
17
|
+
* @param {import('./mask').Mask} mask
|
|
18
|
+
* @param {number} maxDepth
|
|
19
|
+
* @param {string} prefix
|
|
20
|
+
* @returns
|
|
21
|
+
*/
|
|
22
|
+
function tagsFromObject (object, opts) {
|
|
23
|
+
const { maxDepth, prefix } = opts
|
|
24
|
+
|
|
25
|
+
let tagCount = 0
|
|
26
|
+
let abort = false
|
|
27
|
+
const result = {}
|
|
28
|
+
|
|
29
|
+
function tagRec (prefix, object, depth = 0) {
|
|
30
|
+
// Off by one: _dd.payload_tags_trimmed counts as 1 tag
|
|
31
|
+
if (abort) { return }
|
|
32
|
+
|
|
33
|
+
if (tagCount >= PAYLOAD_TAGGING_MAX_TAGS - 1) {
|
|
34
|
+
abort = true
|
|
35
|
+
result['_dd.payload_tags_incomplete'] = true
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (depth >= maxDepth && typeof object === 'object') {
|
|
40
|
+
tagCount += 1
|
|
41
|
+
result[prefix] = truncated
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (object === undefined) {
|
|
46
|
+
tagCount += 1
|
|
47
|
+
result[prefix] = 'undefined'
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (object === null) {
|
|
52
|
+
tagCount += 1
|
|
53
|
+
result[prefix] = 'null'
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (['number', 'boolean'].includes(typeof object) || Buffer.isBuffer(object)) {
|
|
58
|
+
tagCount += 1
|
|
59
|
+
result[prefix] = object.toString().substring(0, 5000)
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (typeof object === 'string') {
|
|
64
|
+
tagCount += 1
|
|
65
|
+
result[prefix] = object.substring(0, 5000)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (typeof object === 'object') {
|
|
69
|
+
for (const [key, value] of Object.entries(object)) {
|
|
70
|
+
if (redactedKeys.includes(key.toLowerCase())) {
|
|
71
|
+
tagCount += 1
|
|
72
|
+
result[`${prefix}.${escapeKey(key)}`] = redacted
|
|
73
|
+
} else {
|
|
74
|
+
tagRec(`${prefix}.${escapeKey(key)}`, value, depth + 1)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
tagRec(prefix, object)
|
|
80
|
+
return result
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
module.exports = { tagsFromObject }
|
|
@@ -136,10 +136,19 @@ module.exports = class PluginManager {
|
|
|
136
136
|
dbmPropagationMode,
|
|
137
137
|
dsmEnabled,
|
|
138
138
|
clientIpEnabled,
|
|
139
|
-
memcachedCommandEnabled
|
|
139
|
+
memcachedCommandEnabled,
|
|
140
|
+
ciVisibilityTestSessionName
|
|
140
141
|
} = this._tracerConfig
|
|
141
142
|
|
|
142
|
-
const sharedConfig = {
|
|
143
|
+
const sharedConfig = {
|
|
144
|
+
dbmPropagationMode,
|
|
145
|
+
dsmEnabled,
|
|
146
|
+
memcachedCommandEnabled,
|
|
147
|
+
site,
|
|
148
|
+
url,
|
|
149
|
+
headers: headerTags || [],
|
|
150
|
+
ciVisibilityTestSessionName
|
|
151
|
+
}
|
|
143
152
|
|
|
144
153
|
if (logInjection !== undefined) {
|
|
145
154
|
sharedConfig.logInjection = logInjection
|
|
@@ -149,10 +158,6 @@ module.exports = class PluginManager {
|
|
|
149
158
|
sharedConfig.queryStringObfuscation = queryStringObfuscation
|
|
150
159
|
}
|
|
151
160
|
|
|
152
|
-
sharedConfig.dbmPropagationMode = dbmPropagationMode
|
|
153
|
-
sharedConfig.dsmEnabled = dsmEnabled
|
|
154
|
-
sharedConfig.memcachedCommandEnabled = memcachedCommandEnabled
|
|
155
|
-
|
|
156
161
|
if (serviceMapping && serviceMapping[name]) {
|
|
157
162
|
sharedConfig.service = serviceMapping[name]
|
|
158
163
|
}
|
|
@@ -161,10 +166,6 @@ module.exports = class PluginManager {
|
|
|
161
166
|
sharedConfig.clientIpEnabled = clientIpEnabled
|
|
162
167
|
}
|
|
163
168
|
|
|
164
|
-
sharedConfig.site = site
|
|
165
|
-
sharedConfig.url = url
|
|
166
|
-
sharedConfig.headers = headerTags || []
|
|
167
|
-
|
|
168
169
|
return sharedConfig
|
|
169
170
|
}
|
|
170
171
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const {
|
|
2
2
|
getTestEnvironmentMetadata,
|
|
3
|
+
getTestSessionName,
|
|
3
4
|
getCodeOwnersFileEntries,
|
|
4
5
|
getTestParentSpan,
|
|
5
6
|
getTestCommonTags,
|
|
@@ -13,11 +14,14 @@ const {
|
|
|
13
14
|
TEST_SESSION_ID,
|
|
14
15
|
TEST_COMMAND,
|
|
15
16
|
TEST_MODULE,
|
|
17
|
+
TEST_SESSION_NAME,
|
|
16
18
|
getTestSuiteCommonTags,
|
|
17
19
|
TEST_STATUS,
|
|
18
20
|
TEST_SKIPPED_BY_ITR,
|
|
19
21
|
ITR_CORRELATION_ID,
|
|
20
|
-
TEST_SOURCE_FILE
|
|
22
|
+
TEST_SOURCE_FILE,
|
|
23
|
+
TEST_LEVEL_EVENT_TYPES,
|
|
24
|
+
TEST_SUITE
|
|
21
25
|
} = require('./util/test')
|
|
22
26
|
const Plugin = require('./plugin')
|
|
23
27
|
const { COMPONENT } = require('../constants')
|
|
@@ -75,6 +79,19 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
75
79
|
// only for playwright
|
|
76
80
|
this.rootDir = rootDir
|
|
77
81
|
|
|
82
|
+
const testSessionName = getTestSessionName(this.config, this.command, this.testEnvironmentMetadata)
|
|
83
|
+
|
|
84
|
+
const metadataTags = {}
|
|
85
|
+
for (const testLevel of TEST_LEVEL_EVENT_TYPES) {
|
|
86
|
+
metadataTags[testLevel] = {
|
|
87
|
+
[TEST_SESSION_NAME]: testSessionName
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// tracer might not be initialized correctly
|
|
91
|
+
if (this.tracer._exporter.setMetadataTags) {
|
|
92
|
+
this.tracer._exporter.setMetadataTags(metadataTags)
|
|
93
|
+
}
|
|
94
|
+
|
|
78
95
|
this.testSessionSpan = this.tracer.startSpan(`${this.constructor.id}.test_session`, {
|
|
79
96
|
childOf,
|
|
80
97
|
tags: {
|
|
@@ -97,6 +114,7 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
97
114
|
if (this.constructor.id === 'vitest') {
|
|
98
115
|
process.env.DD_CIVISIBILITY_TEST_SESSION_ID = this.testSessionSpan.context().toTraceId()
|
|
99
116
|
process.env.DD_CIVISIBILITY_TEST_MODULE_ID = this.testModuleSpan.context().toSpanId()
|
|
117
|
+
process.env.DD_CIVISIBILITY_TEST_COMMAND = this.command
|
|
100
118
|
}
|
|
101
119
|
|
|
102
120
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_CREATED, 'module')
|
|
@@ -194,6 +212,19 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
194
212
|
}
|
|
195
213
|
}
|
|
196
214
|
|
|
215
|
+
getCodeOwners (tags) {
|
|
216
|
+
const {
|
|
217
|
+
[TEST_SOURCE_FILE]: testSourceFile,
|
|
218
|
+
[TEST_SUITE]: testSuite
|
|
219
|
+
} = tags
|
|
220
|
+
// We'll try with the test source file if available (it could be different from the test suite)
|
|
221
|
+
let codeOwners = getCodeOwnersForFilename(testSourceFile, this.codeOwnersEntries)
|
|
222
|
+
if (!codeOwners) {
|
|
223
|
+
codeOwners = getCodeOwnersForFilename(testSuite, this.codeOwnersEntries)
|
|
224
|
+
}
|
|
225
|
+
return codeOwners
|
|
226
|
+
}
|
|
227
|
+
|
|
197
228
|
startTestSpan (testName, testSuite, testSuiteSpan, extraTags = {}) {
|
|
198
229
|
const childOf = getTestParentSpan(this.tracer)
|
|
199
230
|
|
|
@@ -208,13 +239,7 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
208
239
|
...extraTags
|
|
209
240
|
}
|
|
210
241
|
|
|
211
|
-
const
|
|
212
|
-
// We'll try with the test source file if available (it could be different from the test suite)
|
|
213
|
-
let codeOwners = getCodeOwnersForFilename(testSourceFile, this.codeOwnersEntries)
|
|
214
|
-
if (!codeOwners) {
|
|
215
|
-
codeOwners = getCodeOwnersForFilename(testSuite, this.codeOwnersEntries)
|
|
216
|
-
}
|
|
217
|
-
|
|
242
|
+
const codeOwners = this.getCodeOwners(testTags)
|
|
218
243
|
if (codeOwners) {
|
|
219
244
|
testTags[TEST_CODE_OWNERS] = codeOwners
|
|
220
245
|
}
|
|
@@ -5,6 +5,7 @@ const OS_VERSION = 'os.version'
|
|
|
5
5
|
const OS_ARCHITECTURE = 'os.architecture'
|
|
6
6
|
const RUNTIME_NAME = 'runtime.name'
|
|
7
7
|
const RUNTIME_VERSION = 'runtime.version'
|
|
8
|
+
const DD_HOST_CPU_COUNT = '_dd.host.vcpu_count'
|
|
8
9
|
|
|
9
10
|
function getRuntimeAndOSMetadata () {
|
|
10
11
|
return {
|
|
@@ -12,7 +13,8 @@ function getRuntimeAndOSMetadata () {
|
|
|
12
13
|
[OS_ARCHITECTURE]: process.arch,
|
|
13
14
|
[OS_PLATFORM]: process.platform,
|
|
14
15
|
[RUNTIME_NAME]: 'node',
|
|
15
|
-
[OS_VERSION]: os.release()
|
|
16
|
+
[OS_VERSION]: os.release(),
|
|
17
|
+
[DD_HOST_CPU_COUNT]: os.cpus().length
|
|
16
18
|
}
|
|
17
19
|
}
|
|
18
20
|
|
|
@@ -22,5 +24,6 @@ module.exports = {
|
|
|
22
24
|
OS_VERSION,
|
|
23
25
|
OS_ARCHITECTURE,
|
|
24
26
|
RUNTIME_NAME,
|
|
25
|
-
RUNTIME_VERSION
|
|
27
|
+
RUNTIME_VERSION,
|
|
28
|
+
DD_HOST_CPU_COUNT
|
|
26
29
|
}
|
|
@@ -19,7 +19,8 @@ const {
|
|
|
19
19
|
GIT_COMMIT_AUTHOR_NAME,
|
|
20
20
|
GIT_COMMIT_MESSAGE,
|
|
21
21
|
CI_WORKSPACE_PATH,
|
|
22
|
-
CI_PIPELINE_URL
|
|
22
|
+
CI_PIPELINE_URL,
|
|
23
|
+
CI_JOB_NAME
|
|
23
24
|
} = require('./tags')
|
|
24
25
|
const id = require('../../id')
|
|
25
26
|
|
|
@@ -28,6 +29,9 @@ const { SAMPLING_RULE_DECISION } = require('../../constants')
|
|
|
28
29
|
const { AUTO_KEEP } = require('../../../../../ext/priority')
|
|
29
30
|
const { version: ddTraceVersion } = require('../../../../../package.json')
|
|
30
31
|
|
|
32
|
+
// session tags
|
|
33
|
+
const TEST_SESSION_NAME = 'test_session.name'
|
|
34
|
+
|
|
31
35
|
const TEST_FRAMEWORK = 'test.framework'
|
|
32
36
|
const TEST_FRAMEWORK_VERSION = 'test.framework_version'
|
|
33
37
|
const TEST_TYPE = 'test.type'
|
|
@@ -95,11 +99,16 @@ const MOCHA_WORKER_TRACE_PAYLOAD_CODE = 80
|
|
|
95
99
|
const EFD_STRING = "Retried by Datadog's Early Flake Detection"
|
|
96
100
|
const EFD_TEST_NAME_REGEX = new RegExp(EFD_STRING + ' \\(#\\d+\\): ', 'g')
|
|
97
101
|
|
|
98
|
-
|
|
99
|
-
|
|
102
|
+
const TEST_LEVEL_EVENT_TYPES = [
|
|
103
|
+
'test',
|
|
104
|
+
'test_suite_end',
|
|
105
|
+
'test_module_end',
|
|
106
|
+
'test_session_end'
|
|
107
|
+
]
|
|
100
108
|
|
|
101
109
|
module.exports = {
|
|
102
110
|
TEST_CODE_OWNERS,
|
|
111
|
+
TEST_SESSION_NAME,
|
|
103
112
|
TEST_FRAMEWORK,
|
|
104
113
|
TEST_FRAMEWORK_VERSION,
|
|
105
114
|
JEST_TEST_RUNNER,
|
|
@@ -171,7 +180,8 @@ module.exports = {
|
|
|
171
180
|
TEST_BROWSER_DRIVER_VERSION,
|
|
172
181
|
TEST_BROWSER_NAME,
|
|
173
182
|
TEST_BROWSER_VERSION,
|
|
174
|
-
|
|
183
|
+
getTestSessionName,
|
|
184
|
+
TEST_LEVEL_EVENT_TYPES
|
|
175
185
|
}
|
|
176
186
|
|
|
177
187
|
// Returns pkg manager and its version, separated by '-', e.g. npm-8.15.0 or yarn-1.22.19
|
|
@@ -619,3 +629,13 @@ function getIsFaultyEarlyFlakeDetection (projectSuites, testsBySuiteName, faulty
|
|
|
619
629
|
newSuitesPercentage > faultyThresholdPercentage
|
|
620
630
|
)
|
|
621
631
|
}
|
|
632
|
+
|
|
633
|
+
function getTestSessionName (config, testCommand, envTags) {
|
|
634
|
+
if (config.ciVisibilityTestSessionName) {
|
|
635
|
+
return config.ciVisibilityTestSessionName
|
|
636
|
+
}
|
|
637
|
+
if (envTags[CI_JOB_NAME]) {
|
|
638
|
+
return `${envTags[CI_JOB_NAME]}-${testCommand}`
|
|
639
|
+
}
|
|
640
|
+
return testCommand
|
|
641
|
+
}
|
|
@@ -8,8 +8,8 @@ process.once('beforeExit', () => { profiler.stop() })
|
|
|
8
8
|
|
|
9
9
|
module.exports = {
|
|
10
10
|
start: config => {
|
|
11
|
-
const { service, version, env, url, hostname, port, tags, repositoryUrl, commitSHA } = config
|
|
12
|
-
const { enabled, sourceMap, exporters
|
|
11
|
+
const { service, version, env, url, hostname, port, tags, repositoryUrl, commitSHA, injectionEnabled } = config
|
|
12
|
+
const { enabled, sourceMap, exporters } = config.profiling
|
|
13
13
|
const logger = {
|
|
14
14
|
debug: (message) => log.debug(message),
|
|
15
15
|
info: (message) => log.info(message),
|
|
@@ -17,9 +17,17 @@ module.exports = {
|
|
|
17
17
|
error: (message) => log.error(message)
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
const libraryInjected = injectionEnabled.length > 0
|
|
21
|
+
let activation
|
|
22
|
+
if (enabled === 'auto') {
|
|
23
|
+
activation = 'auto'
|
|
24
|
+
} else if (enabled === 'true') {
|
|
25
|
+
activation = 'manual'
|
|
26
|
+
} else if (injectionEnabled.includes('profiler')) {
|
|
27
|
+
activation = 'injection'
|
|
28
|
+
} // else activation = undefined
|
|
29
|
+
|
|
20
30
|
return profiler.start({
|
|
21
|
-
enabled,
|
|
22
|
-
heuristicsEnabled,
|
|
23
31
|
service,
|
|
24
32
|
version,
|
|
25
33
|
env,
|
|
@@ -31,7 +39,9 @@ module.exports = {
|
|
|
31
39
|
port,
|
|
32
40
|
tags,
|
|
33
41
|
repositoryUrl,
|
|
34
|
-
commitSHA
|
|
42
|
+
commitSHA,
|
|
43
|
+
libraryInjected,
|
|
44
|
+
activation
|
|
35
45
|
})
|
|
36
46
|
},
|
|
37
47
|
|
|
@@ -23,7 +23,6 @@ class Config {
|
|
|
23
23
|
DD_PROFILING_CODEHOTSPOTS_ENABLED,
|
|
24
24
|
DD_PROFILING_CPU_ENABLED,
|
|
25
25
|
DD_PROFILING_DEBUG_SOURCE_MAPS,
|
|
26
|
-
DD_PROFILING_ENABLED,
|
|
27
26
|
DD_PROFILING_ENDPOINT_COLLECTION_ENABLED,
|
|
28
27
|
DD_PROFILING_EXPERIMENTAL_CODEHOTSPOTS_ENABLED,
|
|
29
28
|
DD_PROFILING_EXPERIMENTAL_CPU_ENABLED,
|
|
@@ -49,7 +48,6 @@ class Config {
|
|
|
49
48
|
DD_VERSION
|
|
50
49
|
} = process.env
|
|
51
50
|
|
|
52
|
-
const enabled = isTrue(coalesce(options.enabled, DD_PROFILING_ENABLED, true))
|
|
53
51
|
const env = coalesce(options.env, DD_ENV)
|
|
54
52
|
const service = options.service || DD_SERVICE || 'node'
|
|
55
53
|
const host = os.hostname()
|
|
@@ -64,8 +62,6 @@ class Config {
|
|
|
64
62
|
const pprofPrefix = coalesce(options.pprofPrefix,
|
|
65
63
|
DD_PROFILING_PPROF_PREFIX, '')
|
|
66
64
|
|
|
67
|
-
this.enabled = enabled
|
|
68
|
-
this.heuristicsEnabled = options.heuristicsEnabled
|
|
69
65
|
this.service = service
|
|
70
66
|
this.env = env
|
|
71
67
|
this.host = host
|
|
@@ -101,6 +97,11 @@ class Config {
|
|
|
101
97
|
const samplingContextsAvailable = process.platform !== 'win32'
|
|
102
98
|
function checkOptionAllowed (option, description, condition) {
|
|
103
99
|
if (option && !condition) {
|
|
100
|
+
// injection hardening: all of these can only happen if user explicitly
|
|
101
|
+
// sets an environment variable to its non-default value on the platform.
|
|
102
|
+
// In practical terms, it'd require someone explicitly turning on OOM
|
|
103
|
+
// monitoring, code hotspots, endpoint profiling, or CPU profiling on
|
|
104
|
+
// Windows, where it is not supported.
|
|
104
105
|
throw new Error(`${description} not supported on ${process.platform}.`)
|
|
105
106
|
}
|
|
106
107
|
}
|
|
@@ -129,6 +130,8 @@ class Config {
|
|
|
129
130
|
port
|
|
130
131
|
})))
|
|
131
132
|
|
|
133
|
+
this.libraryInjected = options.libraryInjected
|
|
134
|
+
this.activation = options.activation
|
|
132
135
|
this.exporters = ensureExporters(options.exporters || [
|
|
133
136
|
new AgentExporter(this)
|
|
134
137
|
], this)
|
|
@@ -16,10 +16,22 @@ function exporterFromURL (url) {
|
|
|
16
16
|
if (url.protocol === 'file:') {
|
|
17
17
|
return new FileExporter({ pprofPrefix: fileURLToPath(url) })
|
|
18
18
|
} else {
|
|
19
|
+
const injectionEnabled = (process.env.DD_INJECTION_ENABLED || '').split(',')
|
|
20
|
+
const libraryInjected = injectionEnabled.length > 0
|
|
21
|
+
const profilingEnabled = (process.env.DD_PROFILING_ENABLED || '').toLowerCase()
|
|
22
|
+
const activation = ['true', '1'].includes(profilingEnabled)
|
|
23
|
+
? 'manual'
|
|
24
|
+
: profilingEnabled === 'auto'
|
|
25
|
+
? 'auto'
|
|
26
|
+
: injectionEnabled.includes('profiling')
|
|
27
|
+
? 'injection'
|
|
28
|
+
: 'unknown'
|
|
19
29
|
return new AgentExporter({
|
|
20
30
|
url,
|
|
21
31
|
logger,
|
|
22
|
-
uploadTimeout: timeoutMs
|
|
32
|
+
uploadTimeout: timeoutMs,
|
|
33
|
+
libraryInjected,
|
|
34
|
+
activation
|
|
23
35
|
})
|
|
24
36
|
}
|
|
25
37
|
}
|
|
@@ -53,7 +53,7 @@ function computeRetries (uploadTimeout) {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
class AgentExporter {
|
|
56
|
-
constructor ({ url, logger, uploadTimeout, env, host, service, version } = {}) {
|
|
56
|
+
constructor ({ url, logger, uploadTimeout, env, host, service, version, libraryInjected, activation } = {}) {
|
|
57
57
|
this._url = url
|
|
58
58
|
this._logger = logger
|
|
59
59
|
|
|
@@ -65,6 +65,8 @@ class AgentExporter {
|
|
|
65
65
|
this._host = host
|
|
66
66
|
this._service = service
|
|
67
67
|
this._appVersion = version
|
|
68
|
+
this._libraryInjected = !!libraryInjected
|
|
69
|
+
this._activation = activation || 'unknown'
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
export ({ profiles, start, end, tags }) {
|
|
@@ -105,6 +107,10 @@ class AgentExporter {
|
|
|
105
107
|
kernel_version: os.version()
|
|
106
108
|
},
|
|
107
109
|
profiler: {
|
|
110
|
+
activation: this._activation,
|
|
111
|
+
ssi: {
|
|
112
|
+
mechanism: this._libraryInjected ? 'injected_agent' : 'none'
|
|
113
|
+
},
|
|
108
114
|
version
|
|
109
115
|
},
|
|
110
116
|
runtime: {
|
|
@@ -193,7 +199,7 @@ class AgentExporter {
|
|
|
193
199
|
this._logger.error(`Error from the agent: ${err.message}`)
|
|
194
200
|
return
|
|
195
201
|
} else if (err) {
|
|
196
|
-
reject(
|
|
202
|
+
reject(err)
|
|
197
203
|
return
|
|
198
204
|
}
|
|
199
205
|
|
|
@@ -5,7 +5,6 @@ const { Config } = require('./config')
|
|
|
5
5
|
const { snapshotKinds } = require('./constants')
|
|
6
6
|
const { threadNamePrefix } = require('./profilers/shared')
|
|
7
7
|
const dc = require('dc-polyfill')
|
|
8
|
-
const telemetryLog = dc.channel('datadog:telemetry:log')
|
|
9
8
|
|
|
10
9
|
const profileSubmittedChannel = dc.channel('datadog:profiling:profile-submitted')
|
|
11
10
|
|
|
@@ -20,13 +19,6 @@ function logError (logger, err) {
|
|
|
20
19
|
if (logger) {
|
|
21
20
|
logger.error(err)
|
|
22
21
|
}
|
|
23
|
-
if (telemetryLog.hasSubscribers) {
|
|
24
|
-
telemetryLog.publish({
|
|
25
|
-
message: err.message,
|
|
26
|
-
level: 'ERROR',
|
|
27
|
-
stack_trace: err.stack
|
|
28
|
-
})
|
|
29
|
-
}
|
|
30
22
|
}
|
|
31
23
|
|
|
32
24
|
class Profiler extends EventEmitter {
|
|
@@ -55,7 +47,6 @@ class Profiler extends EventEmitter {
|
|
|
55
47
|
if (this._enabled) return true
|
|
56
48
|
|
|
57
49
|
const config = this._config = new Config(options)
|
|
58
|
-
if (!config.enabled && !config.heuristicsEnabled) return false
|
|
59
50
|
|
|
60
51
|
this._logger = config.logger
|
|
61
52
|
this._enabled = true
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const DNSPlugin = require('./dns')
|
|
2
|
+
|
|
3
|
+
class DNSLookupPlugin extends DNSPlugin {
|
|
4
|
+
static get operation () {
|
|
5
|
+
return 'lookup'
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
extendEvent (event, startEvent) {
|
|
9
|
+
event.name = 'lookup'
|
|
10
|
+
event.detail = { hostname: startEvent[0] }
|
|
11
|
+
|
|
12
|
+
return event
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module.exports = DNSLookupPlugin
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const DNSPlugin = require('./dns')
|
|
2
|
+
|
|
3
|
+
class DNSLookupServicePlugin extends DNSPlugin {
|
|
4
|
+
static get operation () {
|
|
5
|
+
return 'lookup_service'
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
extendEvent (event, startEvent) {
|
|
9
|
+
event.name = 'lookupService'
|
|
10
|
+
event.detail = { host: startEvent[0], port: startEvent[1] }
|
|
11
|
+
|
|
12
|
+
return event
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module.exports = DNSLookupServicePlugin
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const DNSPlugin = require('./dns')
|
|
2
|
+
|
|
3
|
+
const queryNames = new Map()
|
|
4
|
+
|
|
5
|
+
class DNSResolvePlugin extends DNSPlugin {
|
|
6
|
+
static get operation () {
|
|
7
|
+
return 'resolve'
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
extendEvent (event, startEvent) {
|
|
11
|
+
const rrtype = startEvent[1]
|
|
12
|
+
let name = queryNames.get(rrtype)
|
|
13
|
+
if (!name) {
|
|
14
|
+
name = `query${rrtype}`
|
|
15
|
+
queryNames.set(rrtype, name)
|
|
16
|
+
}
|
|
17
|
+
event.name = name
|
|
18
|
+
event.detail = { host: startEvent[0] }
|
|
19
|
+
|
|
20
|
+
return event
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = DNSResolvePlugin
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const DNSPlugin = require('./dns')
|
|
2
|
+
|
|
3
|
+
class DNSReversePlugin extends DNSPlugin {
|
|
4
|
+
static get operation () {
|
|
5
|
+
return 'reverse'
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
extendEvent (event, startEvent) {
|
|
9
|
+
event.name = 'getHostByAddr'
|
|
10
|
+
event.detail = { host: startEvent[0] }
|
|
11
|
+
|
|
12
|
+
return event
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module.exports = DNSReversePlugin
|