dd-trace 5.101.0 → 5.102.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/package.json +9 -7
- package/packages/datadog-instrumentations/src/aerospike.js +2 -2
- package/packages/datadog-instrumentations/src/ai.js +8 -8
- package/packages/datadog-instrumentations/src/amqplib.js +6 -7
- package/packages/datadog-instrumentations/src/anthropic.js +10 -10
- package/packages/datadog-instrumentations/src/apollo-server-core.js +3 -3
- package/packages/datadog-instrumentations/src/apollo-server.js +5 -5
- package/packages/datadog-instrumentations/src/avsc.js +6 -6
- package/packages/datadog-instrumentations/src/aws-sdk.js +151 -67
- package/packages/datadog-instrumentations/src/azure-durable-functions.js +8 -8
- package/packages/datadog-instrumentations/src/bluebird.js +2 -2
- package/packages/datadog-instrumentations/src/body-parser.js +2 -2
- package/packages/datadog-instrumentations/src/cassandra-driver.js +7 -7
- package/packages/datadog-instrumentations/src/child_process.js +12 -12
- package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +9 -9
- package/packages/datadog-instrumentations/src/connect.js +7 -7
- package/packages/datadog-instrumentations/src/cookie-parser.js +4 -4
- package/packages/datadog-instrumentations/src/cookie.js +2 -2
- package/packages/datadog-instrumentations/src/couchbase.js +16 -30
- package/packages/datadog-instrumentations/src/crypto.js +4 -4
- package/packages/datadog-instrumentations/src/cucumber.js +77 -16
- package/packages/datadog-instrumentations/src/dns.js +0 -3
- package/packages/datadog-instrumentations/src/elasticsearch.js +8 -11
- package/packages/datadog-instrumentations/src/express-mongo-sanitize.js +6 -6
- package/packages/datadog-instrumentations/src/express-session.js +4 -4
- package/packages/datadog-instrumentations/src/express.js +10 -11
- package/packages/datadog-instrumentations/src/fastify.js +2 -2
- package/packages/datadog-instrumentations/src/fs.js +14 -14
- package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +5 -7
- package/packages/datadog-instrumentations/src/google-genai.js +4 -4
- package/packages/datadog-instrumentations/src/grpc/server.js +2 -2
- package/packages/datadog-instrumentations/src/hapi.js +2 -2
- package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +8 -8
- package/packages/datadog-instrumentations/src/helpers/promise.js +2 -2
- package/packages/datadog-instrumentations/src/hono.js +2 -2
- package/packages/datadog-instrumentations/src/http/client.js +6 -6
- package/packages/datadog-instrumentations/src/http/server.js +9 -9
- package/packages/datadog-instrumentations/src/jest.js +31 -31
- package/packages/datadog-instrumentations/src/kafkajs.js +9 -9
- package/packages/datadog-instrumentations/src/knex.js +17 -17
- package/packages/datadog-instrumentations/src/koa.js +12 -12
- package/packages/datadog-instrumentations/src/ldapjs.js +5 -5
- package/packages/datadog-instrumentations/src/light-my-request.js +2 -2
- package/packages/datadog-instrumentations/src/limitd-client.js +4 -4
- package/packages/datadog-instrumentations/src/lodash.js +4 -4
- package/packages/datadog-instrumentations/src/mariadb.js +13 -13
- package/packages/datadog-instrumentations/src/memcached.js +2 -2
- package/packages/datadog-instrumentations/src/microgateway-core.js +2 -2
- package/packages/datadog-instrumentations/src/mocha/common.js +3 -3
- package/packages/datadog-instrumentations/src/mocha/main.js +12 -10
- package/packages/datadog-instrumentations/src/mocha/utils.js +133 -16
- package/packages/datadog-instrumentations/src/mocha/worker.js +7 -5
- package/packages/datadog-instrumentations/src/mongodb-core.js +9 -22
- package/packages/datadog-instrumentations/src/mongodb.js +5 -5
- package/packages/datadog-instrumentations/src/mongoose.js +21 -21
- package/packages/datadog-instrumentations/src/mquery.js +5 -5
- package/packages/datadog-instrumentations/src/multer.js +4 -4
- package/packages/datadog-instrumentations/src/mysql.js +16 -16
- package/packages/datadog-instrumentations/src/mysql2.js +4 -4
- package/packages/datadog-instrumentations/src/net.js +14 -8
- package/packages/datadog-instrumentations/src/nyc.js +5 -5
- package/packages/datadog-instrumentations/src/openai.js +19 -19
- package/packages/datadog-instrumentations/src/oracledb.js +6 -6
- package/packages/datadog-instrumentations/src/passport-utils.js +5 -5
- package/packages/datadog-instrumentations/src/pg.js +15 -15
- package/packages/datadog-instrumentations/src/pino.js +6 -10
- package/packages/datadog-instrumentations/src/playwright.js +20 -15
- package/packages/datadog-instrumentations/src/protobufjs.js +16 -16
- package/packages/datadog-instrumentations/src/redis.js +1 -2
- package/packages/datadog-instrumentations/src/restify.js +2 -2
- package/packages/datadog-instrumentations/src/router.js +12 -12
- package/packages/datadog-instrumentations/src/stripe.js +12 -12
- package/packages/datadog-instrumentations/src/vitest.js +107 -26
- package/packages/datadog-instrumentations/src/winston.js +4 -4
- package/packages/datadog-instrumentations/src/ws.js +7 -7
- package/packages/datadog-plugin-aws-sdk/src/base.js +52 -4
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +19 -12
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +45 -35
- package/packages/datadog-plugin-aws-sdk/src/services/lambda.js +33 -22
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +12 -13
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +73 -54
- package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +19 -17
- package/packages/datadog-plugin-aws-sdk/src/util.js +22 -0
- package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +6 -6
- package/packages/datadog-plugin-cucumber/src/index.js +4 -0
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +1 -4
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +1 -5
- package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +3 -1
- package/packages/datadog-plugin-http/src/client.js +1 -5
- package/packages/datadog-plugin-jest/src/util.js +1 -2
- package/packages/datadog-plugin-mocha/src/index.js +4 -0
- package/packages/datadog-plugin-mongodb-core/src/index.js +2 -1
- package/packages/datadog-plugin-openai/src/tracing.js +12 -23
- package/packages/datadog-plugin-playwright/src/index.js +1 -1
- package/packages/datadog-plugin-vitest/src/index.js +8 -1
- package/packages/datadog-shimmer/src/shimmer.js +7 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-password-rules.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secret-rules.js +81 -81
- package/packages/dd-trace/src/appsec/iast/security-controls/index.js +2 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugins/kafka.js +2 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +2 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +2 -2
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +2 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +1 -3
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +83 -48
- package/packages/dd-trace/src/appsec/index.js +21 -24
- package/packages/dd-trace/src/appsec/reporter.js +3 -1
- package/packages/dd-trace/src/appsec/rule_manager.js +4 -2
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +31 -16
- package/packages/dd-trace/src/config/git_properties.js +2 -2
- package/packages/dd-trace/src/datastreams/index.js +2 -1
- package/packages/dd-trace/src/datastreams/processor.js +1 -2
- package/packages/dd-trace/src/debugger/devtools_client/snapshot-pruner.js +1 -0
- package/packages/dd-trace/src/encode/0.4.js +757 -232
- package/packages/dd-trace/src/encode/0.5.js +13 -7
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -2
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +6 -3
- package/packages/dd-trace/src/llmobs/sdk.js +24 -26
- package/packages/dd-trace/src/llmobs/span_processor.js +25 -5
- package/packages/dd-trace/src/llmobs/util.js +1 -0
- package/packages/dd-trace/src/msgpack/chunk.js +6 -3
- package/packages/dd-trace/src/openfeature/noop.js +40 -36
- package/packages/dd-trace/src/openfeature/writers/exposures.js +33 -52
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +1 -2
- package/packages/dd-trace/src/opentelemetry/tracer.js +0 -22
- package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +2 -11
- package/packages/dd-trace/src/plugins/util/ci.js +1 -1
- package/packages/dd-trace/src/plugins/util/git-cache.js +3 -5
- package/packages/dd-trace/src/plugins/util/test.js +19 -7
- package/packages/dd-trace/src/plugins/util/url.js +1 -3
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +1 -1
- package/packages/dd-trace/src/plugins/util/web.js +5 -7
- package/packages/dd-trace/src/profiling/profilers/events.js +3 -23
- package/packages/dd-trace/src/profiling/profilers/wall.js +4 -5
- package/packages/dd-trace/src/runtime_metrics/index.js +2 -2
- package/packages/dd-trace/src/scope.js +3 -10
- package/packages/dd-trace/src/serverless.js +1 -4
- package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +7 -1
- package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +4 -0
- package/packages/dd-trace/src/tracer.js +7 -7
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
const { DsmPathwayCodec, getSizeOrZero } = require('../../../dd-trace/src/datastreams')
|
|
3
3
|
const log = require('../../../dd-trace/src/log')
|
|
4
4
|
const BaseAwsSdkPlugin = require('../base')
|
|
5
|
+
const { isEmpty } = require('../util')
|
|
6
|
+
|
|
7
|
+
function recordDataAsString (data) {
|
|
8
|
+
return Buffer.isBuffer(data) ? data.toString('utf8') : Buffer.from(data).toString('utf8')
|
|
9
|
+
}
|
|
5
10
|
|
|
6
11
|
class Kinesis extends BaseAwsSdkPlugin {
|
|
7
12
|
static id = 'kinesis'
|
|
@@ -90,14 +95,14 @@ class Kinesis extends BaseAwsSdkPlugin {
|
|
|
90
95
|
const record = response.Records[0]
|
|
91
96
|
|
|
92
97
|
try {
|
|
93
|
-
const decodedData = JSON.parse(
|
|
98
|
+
const decodedData = JSON.parse(recordDataAsString(record.Data))
|
|
94
99
|
|
|
95
100
|
return {
|
|
96
101
|
maybeChildOf: this.tracer.extract('text_map', decodedData._datadog),
|
|
97
102
|
parsedAttributes: decodedData._datadog,
|
|
98
103
|
}
|
|
99
|
-
} catch (
|
|
100
|
-
log.error('Kinesis error extracting response',
|
|
104
|
+
} catch (error) {
|
|
105
|
+
log.error('Kinesis error extracting response', error)
|
|
101
106
|
}
|
|
102
107
|
}
|
|
103
108
|
|
|
@@ -107,27 +112,32 @@ class Kinesis extends BaseAwsSdkPlugin {
|
|
|
107
112
|
if (operation !== 'getRecords') return
|
|
108
113
|
if (!response || !response.Records || !response.Records[0]) return
|
|
109
114
|
|
|
110
|
-
//
|
|
115
|
+
// Only attribute payloadSize to the span when there is a single record.
|
|
111
116
|
span = response.Records.length > 1 ? null : span
|
|
112
117
|
|
|
118
|
+
const tags = streamName
|
|
119
|
+
? ['direction:in', `topic:${streamName}`, 'type:kinesis']
|
|
120
|
+
: ['direction:in', 'type:kinesis']
|
|
121
|
+
|
|
113
122
|
for (const record of response.Records) {
|
|
114
|
-
|
|
123
|
+
let parsedAttributes
|
|
124
|
+
try {
|
|
125
|
+
parsedAttributes = JSON.parse(recordDataAsString(record.Data))
|
|
126
|
+
} catch {
|
|
127
|
+
// Non-JSON record. Skip DSM context for this entry; the
|
|
128
|
+
// checkpoint payload size below is still reported.
|
|
129
|
+
}
|
|
115
130
|
|
|
116
131
|
const payloadSize = getSizeOrZero(record.Data)
|
|
117
132
|
if (parsedAttributes?._datadog) {
|
|
118
133
|
this.tracer.decodeDataStreamsContext(parsedAttributes._datadog)
|
|
119
134
|
}
|
|
120
|
-
|
|
121
|
-
? ['direction:in', `topic:${streamName}`, 'type:kinesis']
|
|
122
|
-
: ['direction:in', 'type:kinesis']
|
|
123
|
-
this.tracer
|
|
124
|
-
.setCheckpoint(tags, span, payloadSize)
|
|
135
|
+
this.tracer.setCheckpoint(tags, span, payloadSize)
|
|
125
136
|
}
|
|
126
137
|
}
|
|
127
138
|
|
|
128
|
-
// AWS-SDK
|
|
129
|
-
//
|
|
130
|
-
// This method handles both
|
|
139
|
+
// AWS-SDK base64-encodes kinesis payloads but also accepts an already
|
|
140
|
+
// base64-encoded payload; both shapes land here.
|
|
131
141
|
_tryParse (body) {
|
|
132
142
|
try {
|
|
133
143
|
return JSON.parse(body)
|
|
@@ -135,7 +145,7 @@ class Kinesis extends BaseAwsSdkPlugin {
|
|
|
135
145
|
log.info('Not JSON string. Trying Base64 encoded JSON string')
|
|
136
146
|
}
|
|
137
147
|
try {
|
|
138
|
-
return JSON.parse(Buffer.from(body, 'base64').toString('ascii')
|
|
148
|
+
return JSON.parse(Buffer.from(body, 'base64').toString('ascii'))
|
|
139
149
|
} catch {
|
|
140
150
|
return null
|
|
141
151
|
}
|
|
@@ -179,36 +189,36 @@ class Kinesis extends BaseAwsSdkPlugin {
|
|
|
179
189
|
}
|
|
180
190
|
|
|
181
191
|
const ddInfo = {}
|
|
182
|
-
//
|
|
183
|
-
if (injectTraceContext) {
|
|
192
|
+
// For now we only inject to the first message; batches may change later.
|
|
193
|
+
if (injectTraceContext) {
|
|
194
|
+
this.tracer.inject(span, 'text_map', ddInfo)
|
|
195
|
+
}
|
|
184
196
|
|
|
185
|
-
// set DSM hash if enabled
|
|
186
197
|
if (this.config.dsmEnabled) {
|
|
187
198
|
parsedData._datadog = ddInfo
|
|
188
|
-
const dataStreamsContext = this.setDSMCheckpoint(span,
|
|
189
|
-
|
|
199
|
+
const dataStreamsContext = this.setDSMCheckpoint(span, params, stream)
|
|
200
|
+
if (dataStreamsContext) {
|
|
201
|
+
DsmPathwayCodec.encode(dataStreamsContext, ddInfo)
|
|
202
|
+
}
|
|
190
203
|
}
|
|
191
204
|
|
|
192
|
-
if (
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
}
|
|
202
|
-
params.Data = finalData
|
|
205
|
+
if (isEmpty(ddInfo)) return
|
|
206
|
+
|
|
207
|
+
parsedData._datadog = ddInfo
|
|
208
|
+
const serialized = JSON.stringify(parsedData)
|
|
209
|
+
const byteSize = Buffer.byteLength(serialized, 'utf8')
|
|
210
|
+
// Kinesis max payload size is 1 MiB; bail if our context push tipped us over.
|
|
211
|
+
if (byteSize >= 1_048_576) {
|
|
212
|
+
log.info('Payload size too large to pass context')
|
|
213
|
+
return
|
|
203
214
|
}
|
|
215
|
+
params.Data = Buffer.from(serialized, 'utf8')
|
|
204
216
|
}
|
|
205
217
|
|
|
206
|
-
setDSMCheckpoint (span,
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
const dataStreamsContext = this.tracer
|
|
218
|
+
setDSMCheckpoint (span, params, stream) {
|
|
219
|
+
const payloadSize = getSizeOrZero(params.Data)
|
|
220
|
+
return this.tracer
|
|
210
221
|
.setCheckpoint(['direction:out', `topic:${stream}`, 'type:kinesis'], span, payloadSize)
|
|
211
|
-
return dataStreamsContext
|
|
212
222
|
}
|
|
213
223
|
}
|
|
214
224
|
|
|
@@ -18,33 +18,44 @@ class Lambda extends BaseAwsSdkPlugin {
|
|
|
18
18
|
|
|
19
19
|
requestInject (span, request) {
|
|
20
20
|
const operation = request.operation
|
|
21
|
-
if (operation
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
21
|
+
if (operation !== 'invoke') return
|
|
22
|
+
|
|
23
|
+
if (!request.params) {
|
|
24
|
+
request.params = {}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const isSyncInvocation = !request.params.InvocationType ||
|
|
28
|
+
request.params.InvocationType === 'RequestResponse'
|
|
29
|
+
if (!isSyncInvocation) return
|
|
30
|
+
|
|
31
|
+
const injected = {}
|
|
32
|
+
this.tracer.inject(span, 'text_map', injected)
|
|
25
33
|
|
|
26
|
-
|
|
27
|
-
|
|
34
|
+
let newContextJson
|
|
35
|
+
if (request.params.ClientContext) {
|
|
36
|
+
const clientContextJson = Buffer.from(request.params.ClientContext, 'base64').toString('utf8')
|
|
28
37
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
const newContextBase64 = Buffer.from(JSON.stringify(clientContext)).toString('base64')
|
|
42
|
-
request.params.ClientContext = newContextBase64
|
|
43
|
-
} catch (err) {
|
|
44
|
-
log.error('Lambda error injecting request', err)
|
|
38
|
+
// The two throwing surfaces here are the inline `JSON.parse` and the
|
|
39
|
+
// slow path inside `injectFieldIntoJsonObject`. Tighten the catch
|
|
40
|
+
// around the JSON ops so the rest of the inject stays optimisable.
|
|
41
|
+
try {
|
|
42
|
+
if (clientContextJson.includes('"custom"')) {
|
|
43
|
+
// Existing customer keys under `custom` survive the round-trip.
|
|
44
|
+
const clientContext = JSON.parse(clientContextJson)
|
|
45
|
+
if (!clientContext.custom) clientContext.custom = {}
|
|
46
|
+
Object.assign(clientContext.custom, injected)
|
|
47
|
+
newContextJson = JSON.stringify(clientContext)
|
|
48
|
+
} else {
|
|
49
|
+
newContextJson = BaseAwsSdkPlugin.injectFieldIntoJsonObject(clientContextJson, 'custom', injected)
|
|
45
50
|
}
|
|
51
|
+
} catch (error) {
|
|
52
|
+
log.error('Lambda error injecting request', error)
|
|
53
|
+
return
|
|
46
54
|
}
|
|
55
|
+
} else {
|
|
56
|
+
newContextJson = `{"custom":${JSON.stringify(injected)}}`
|
|
47
57
|
}
|
|
58
|
+
request.params.ClientContext = Buffer.from(newContextJson).toString('base64')
|
|
48
59
|
}
|
|
49
60
|
|
|
50
61
|
operationFromRequest (request) {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
const { DsmPathwayCodec, getHeadersSize } = require('../../../dd-trace/src/datastreams')
|
|
3
3
|
const log = require('../../../dd-trace/src/log')
|
|
4
4
|
const BaseAwsSdkPlugin = require('../base')
|
|
5
|
+
const { isEmpty } = require('../util')
|
|
5
6
|
|
|
6
7
|
class Sns extends BaseAwsSdkPlugin {
|
|
7
8
|
static id = 'sns'
|
|
@@ -14,12 +15,10 @@ class Sns extends BaseAwsSdkPlugin {
|
|
|
14
15
|
if (!params.TopicArn && !(response.data && response.data.TopicArn)) return {}
|
|
15
16
|
const TopicArn = params.TopicArn || response.data.TopicArn
|
|
16
17
|
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
// Get the topic name from the last part of the ARN
|
|
22
|
-
const topicName = arnParts.at(-1)
|
|
18
|
+
// Get the topic name from the last `:`-delimited segment of the ARN
|
|
19
|
+
// (e.g. 'my-topic' in 'arn:aws:sns:us-east-1:123456789012:my-topic')
|
|
20
|
+
// without allocating an intermediate parts array.
|
|
21
|
+
const topicName = TopicArn.slice(TopicArn.lastIndexOf(':') + 1)
|
|
23
22
|
|
|
24
23
|
return {
|
|
25
24
|
'resource.name': `${operation} ${params.TopicArn || response.data.TopicArn}`,
|
|
@@ -103,12 +102,14 @@ class Sns extends BaseAwsSdkPlugin {
|
|
|
103
102
|
DsmPathwayCodec.encode(dataStreamsContext, ddInfo)
|
|
104
103
|
}
|
|
105
104
|
|
|
106
|
-
if (
|
|
105
|
+
if (isEmpty(ddInfo)) {
|
|
106
|
+
if (params.MessageAttributes._datadog) {
|
|
107
|
+
// let's avoid adding any additional information to payload if we failed to inject
|
|
108
|
+
delete params.MessageAttributes._datadog
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
107
111
|
// BINARY types are automatically base64 encoded
|
|
108
112
|
params.MessageAttributes._datadog.BinaryValue = Buffer.from(JSON.stringify(ddInfo))
|
|
109
|
-
} else if (params.MessageAttributes._datadog) {
|
|
110
|
-
// let's avoid adding any additional information to payload if we failed to inject
|
|
111
|
-
delete params.MessageAttributes._datadog
|
|
112
113
|
}
|
|
113
114
|
}
|
|
114
115
|
|
|
@@ -116,9 +117,7 @@ class Sns extends BaseAwsSdkPlugin {
|
|
|
116
117
|
// only set a checkpoint if publishing to a topic
|
|
117
118
|
if (topicArn) {
|
|
118
119
|
const payloadSize = getHeadersSize(params)
|
|
119
|
-
|
|
120
|
-
.setCheckpoint(['direction:out', `topic:${topicArn}`, 'type:sns'], span, payloadSize)
|
|
121
|
-
return dataStreamsContext
|
|
120
|
+
return this.tracer.setCheckpoint(['direction:out', `topic:${topicArn}`, 'type:sns'], span, payloadSize)
|
|
122
121
|
}
|
|
123
122
|
}
|
|
124
123
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const log = require('../../../dd-trace/src/log')
|
|
4
4
|
const BaseAwsSdkPlugin = require('../base')
|
|
5
5
|
const { DsmPathwayCodec, getHeadersSize } = require('../../../dd-trace/src/datastreams')
|
|
6
|
-
const { extractQueueMetadata } = require('../util')
|
|
6
|
+
const { extractQueueMetadata, isEmpty } = require('../util')
|
|
7
7
|
|
|
8
8
|
class Sqs extends BaseAwsSdkPlugin {
|
|
9
9
|
static id = 'sqs'
|
|
@@ -23,25 +23,32 @@ class Sqs extends BaseAwsSdkPlugin {
|
|
|
23
23
|
|
|
24
24
|
let store = this._parentMap.get(request)
|
|
25
25
|
let span
|
|
26
|
-
let parsedMessageAttributes
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
26
|
+
let parsedMessageAttributes
|
|
27
|
+
let parsedFirstBody
|
|
28
|
+
let firstBodyChecked = false
|
|
29
|
+
if (contextExtraction !== undefined) {
|
|
30
|
+
parsedFirstBody = contextExtraction.parsedBody
|
|
31
|
+
firstBodyChecked = contextExtraction.bodyChecked === true
|
|
32
|
+
if (contextExtraction.datadogContext !== undefined) {
|
|
33
|
+
ctx.needsFinish = true
|
|
34
|
+
const options = {
|
|
35
|
+
childOf: contextExtraction.datadogContext,
|
|
36
|
+
meta: {
|
|
37
|
+
...this.requestTags.get(request),
|
|
38
|
+
'span.kind': 'server',
|
|
39
|
+
},
|
|
40
|
+
integrationName: 'aws-sdk',
|
|
41
|
+
}
|
|
42
|
+
parsedMessageAttributes = contextExtraction.parsedAttributes
|
|
43
|
+
span = this.startSpan('aws.response', options, ctx)
|
|
44
|
+
store = ctx.currentStore
|
|
36
45
|
}
|
|
37
|
-
parsedMessageAttributes = contextExtraction.parsedAttributes
|
|
38
|
-
span = this.startSpan('aws.response', options, ctx)
|
|
39
|
-
store = ctx.currentStore
|
|
40
46
|
}
|
|
41
47
|
|
|
42
|
-
//
|
|
48
|
+
// Extract DSM context after, as we might not have a parent-child but may have a DSM context.
|
|
43
49
|
this.responseExtractDSMContext(
|
|
44
|
-
request.operation, request.params, response, span
|
|
50
|
+
request.operation, request.params, response, span ?? null,
|
|
51
|
+
{ parsedAttributes: parsedMessageAttributes, parsedFirstBody, firstBodyChecked }
|
|
45
52
|
)
|
|
46
53
|
|
|
47
54
|
return store
|
|
@@ -128,21 +135,23 @@ class Sqs extends BaseAwsSdkPlugin {
|
|
|
128
135
|
if (!response || !response.Messages || !response.Messages[0]) return
|
|
129
136
|
|
|
130
137
|
let message = response.Messages[0]
|
|
138
|
+
let parsedBody
|
|
131
139
|
|
|
132
140
|
if (message.Body) {
|
|
133
141
|
try {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
// SNS to SQS
|
|
137
|
-
if (body.Type === 'Notification') {
|
|
138
|
-
message = body
|
|
139
|
-
}
|
|
142
|
+
parsedBody = JSON.parse(message.Body)
|
|
140
143
|
} catch {
|
|
141
144
|
// SQS to SQS
|
|
142
145
|
}
|
|
146
|
+
// SNS to SQS
|
|
147
|
+
if (parsedBody?.Type === 'Notification') {
|
|
148
|
+
message = parsedBody
|
|
149
|
+
}
|
|
143
150
|
}
|
|
144
151
|
|
|
145
|
-
if (!message.MessageAttributes || !message.MessageAttributes._datadog)
|
|
152
|
+
if (!message.MessageAttributes || !message.MessageAttributes._datadog) {
|
|
153
|
+
return { parsedBody, bodyChecked: true }
|
|
154
|
+
}
|
|
146
155
|
|
|
147
156
|
const datadogAttribute = message.MessageAttributes._datadog
|
|
148
157
|
|
|
@@ -151,8 +160,12 @@ class Sqs extends BaseAwsSdkPlugin {
|
|
|
151
160
|
return {
|
|
152
161
|
datadogContext: this.tracer.extract('text_map', parsedAttributes),
|
|
153
162
|
parsedAttributes,
|
|
163
|
+
parsedBody,
|
|
164
|
+
bodyChecked: true,
|
|
154
165
|
}
|
|
155
166
|
}
|
|
167
|
+
|
|
168
|
+
return { parsedBody, bodyChecked: true }
|
|
156
169
|
}
|
|
157
170
|
|
|
158
171
|
parseDatadogAttributes (attributes) {
|
|
@@ -164,36 +177,43 @@ class Sqs extends BaseAwsSdkPlugin {
|
|
|
164
177
|
const buffer = Buffer.from(attributes.Value ?? attributes.BinaryValue, 'base64')
|
|
165
178
|
return JSON.parse(buffer)
|
|
166
179
|
}
|
|
167
|
-
} catch (
|
|
168
|
-
log.error('Sqs error parsing DD attributes',
|
|
180
|
+
} catch (error) {
|
|
181
|
+
log.error('Sqs error parsing DD attributes', error)
|
|
169
182
|
}
|
|
170
183
|
}
|
|
171
184
|
|
|
172
185
|
responseExtractDSMContext (operation, params, response, span, kwargs = {}) {
|
|
173
186
|
let { parsedAttributes } = kwargs
|
|
187
|
+
const { parsedFirstBody, firstBodyChecked } = kwargs
|
|
174
188
|
if (!this.config.dsmEnabled) return
|
|
175
189
|
if (operation !== 'receiveMessage') return
|
|
176
190
|
if (!response || !response.Messages || !response.Messages[0]) return
|
|
177
191
|
|
|
178
|
-
//
|
|
192
|
+
// Only attribute payloadSize to the span when there is a single message.
|
|
179
193
|
span = response.Messages.length > 1 ? null : span
|
|
180
194
|
|
|
181
|
-
for
|
|
182
|
-
|
|
195
|
+
// QueueUrl is the same for the whole receive batch.
|
|
196
|
+
const queue = params.QueueUrl.slice(params.QueueUrl.lastIndexOf('/') + 1)
|
|
197
|
+
|
|
198
|
+
for (let i = 0; i < response.Messages.length; i++) {
|
|
199
|
+
let message = response.Messages[i]
|
|
183
200
|
if (!parsedAttributes) {
|
|
184
|
-
|
|
201
|
+
let body
|
|
202
|
+
// responseExtract already parsed message[0]; reuse that result instead of re-parsing.
|
|
203
|
+
if (i === 0 && firstBodyChecked) {
|
|
204
|
+
body = parsedFirstBody
|
|
205
|
+
} else if (message.Body) {
|
|
185
206
|
try {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
// SNS to SQS
|
|
189
|
-
if (body.Type === 'Notification') {
|
|
190
|
-
message = body
|
|
191
|
-
}
|
|
207
|
+
body = JSON.parse(message.Body)
|
|
192
208
|
} catch {
|
|
193
209
|
// SQS to SQS
|
|
194
210
|
}
|
|
195
211
|
}
|
|
196
|
-
|
|
212
|
+
// SNS to SQS
|
|
213
|
+
if (body?.Type === 'Notification') {
|
|
214
|
+
message = body
|
|
215
|
+
}
|
|
216
|
+
if (message.MessageAttributes && message.MessageAttributes._datadog) {
|
|
197
217
|
parsedAttributes = this.parseDatadogAttributes(message.MessageAttributes._datadog)
|
|
198
218
|
}
|
|
199
219
|
}
|
|
@@ -201,7 +221,6 @@ class Sqs extends BaseAwsSdkPlugin {
|
|
|
201
221
|
Body: message.Body,
|
|
202
222
|
MessageAttributes: message.MessageAttributes,
|
|
203
223
|
})
|
|
204
|
-
const queue = params.QueueUrl.split('/').pop()
|
|
205
224
|
if (parsedAttributes) {
|
|
206
225
|
this.tracer.decodeDataStreamsContext(parsedAttributes)
|
|
207
226
|
}
|
|
@@ -253,34 +272,35 @@ class Sqs extends BaseAwsSdkPlugin {
|
|
|
253
272
|
// TODO: add test when the test suite is fixed
|
|
254
273
|
return
|
|
255
274
|
}
|
|
275
|
+
|
|
256
276
|
const ddInfo = {}
|
|
257
|
-
//
|
|
277
|
+
// For now we only inject to the first message; batches may change later.
|
|
258
278
|
if (injectTraceContext) {
|
|
259
279
|
this.tracer.inject(span, 'text_map', ddInfo)
|
|
260
|
-
params.MessageAttributes._datadog = {
|
|
261
|
-
DataType: 'String',
|
|
262
|
-
StringValue: JSON.stringify(ddInfo),
|
|
263
|
-
}
|
|
264
280
|
}
|
|
265
281
|
|
|
266
282
|
if (this.config.dsmEnabled) {
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
283
|
+
// Attach `_datadog` before measuring so the DSM payload size metric
|
|
284
|
+
// matches the on-wire payload, then update with the encoded context.
|
|
285
|
+
params.MessageAttributes._datadog = {
|
|
286
|
+
DataType: 'String',
|
|
287
|
+
StringValue: JSON.stringify(ddInfo),
|
|
272
288
|
}
|
|
273
|
-
|
|
274
289
|
const dataStreamsContext = this.setDSMCheckpoint(span, params, queueUrl)
|
|
275
290
|
if (dataStreamsContext) {
|
|
276
291
|
DsmPathwayCodec.encode(dataStreamsContext, ddInfo)
|
|
277
292
|
params.MessageAttributes._datadog.StringValue = JSON.stringify(ddInfo)
|
|
293
|
+
} else if (isEmpty(ddInfo)) {
|
|
294
|
+
delete params.MessageAttributes._datadog
|
|
278
295
|
}
|
|
296
|
+
return
|
|
279
297
|
}
|
|
280
298
|
|
|
281
|
-
if (
|
|
282
|
-
|
|
283
|
-
|
|
299
|
+
if (isEmpty(ddInfo)) return
|
|
300
|
+
|
|
301
|
+
params.MessageAttributes._datadog = {
|
|
302
|
+
DataType: 'String',
|
|
303
|
+
StringValue: JSON.stringify(ddInfo),
|
|
284
304
|
}
|
|
285
305
|
}
|
|
286
306
|
|
|
@@ -289,10 +309,9 @@ class Sqs extends BaseAwsSdkPlugin {
|
|
|
289
309
|
Body: params.MessageBody,
|
|
290
310
|
MessageAttributes: params.MessageAttributes,
|
|
291
311
|
})
|
|
292
|
-
const queue = queueUrl.
|
|
293
|
-
|
|
312
|
+
const queue = queueUrl.slice(queueUrl.lastIndexOf('/') + 1)
|
|
313
|
+
return this.tracer
|
|
294
314
|
.setCheckpoint(['direction:out', `topic:${queue}`, 'type:sqs'], span, payloadSize)
|
|
295
|
-
return dataStreamsContext
|
|
296
315
|
}
|
|
297
316
|
}
|
|
298
317
|
|
|
@@ -38,25 +38,27 @@ class Stepfunctions extends BaseAwsSdkPlugin {
|
|
|
38
38
|
|
|
39
39
|
requestInject (span, request) {
|
|
40
40
|
const operation = request.operation
|
|
41
|
-
if (operation
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
if ((operation !== 'startExecution' && operation !== 'startSyncExecution') || !request.params?.input) return
|
|
42
|
+
|
|
43
|
+
const input = request.params.input
|
|
44
|
+
if (typeof input !== 'string' || input.length < 2) return
|
|
45
|
+
|
|
46
|
+
// Skip non-object payloads up front to avoid a `JSON.parse` round-trip.
|
|
47
|
+
// `trimEnd` is the identity on payloads with no trailing whitespace;
|
|
48
|
+
// for the rare whitespace-suffixed object the slow path inside
|
|
49
|
+
// `injectFieldIntoJsonObject` handles the parse + restringify.
|
|
50
|
+
const trimmed = input.trimEnd()
|
|
51
|
+
if (trimmed.length < 2 || trimmed.charCodeAt(trimmed.length - 1) !== 0x7D) return
|
|
45
52
|
|
|
46
|
-
|
|
53
|
+
const injected = {}
|
|
54
|
+
this.tracer.inject(span, 'text_map', injected)
|
|
47
55
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const newInput = JSON.stringify(inputObj)
|
|
55
|
-
request.params.input = newInput
|
|
56
|
-
}
|
|
57
|
-
} catch {
|
|
58
|
-
log.info('Unable to treat input as JSON')
|
|
59
|
-
}
|
|
56
|
+
// `injectFieldIntoJsonObject` is the only throwing call path
|
|
57
|
+
// (`JSON.parse` slow path for non-trivial JSON shapes).
|
|
58
|
+
try {
|
|
59
|
+
request.params.input = BaseAwsSdkPlugin.injectFieldIntoJsonObject(input, '_datadog', injected)
|
|
60
|
+
} catch {
|
|
61
|
+
log.info('Unable to treat input as JSON')
|
|
60
62
|
}
|
|
61
63
|
}
|
|
62
64
|
}
|
|
@@ -143,9 +143,31 @@ const extractQueueMetadata = queueURL => {
|
|
|
143
143
|
return { queueName, arn }
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Returns true when `obj` has no own enumerable properties. The
|
|
148
|
+
* `for-in` loop with an early return is the only allocation-free shape
|
|
149
|
+
* for this check; benchmarks pin it as 1.3-1.4x faster than
|
|
150
|
+
* `Object.keys(obj).length === 0` across small / medium / large
|
|
151
|
+
* objects, and this is the hot path on every AWS messaging send.
|
|
152
|
+
*
|
|
153
|
+
* Callers in this package only pass plain objects they construct
|
|
154
|
+
* locally, so prototype-enumerable keys are not a concern here. Do not
|
|
155
|
+
* reuse this helper on caller-supplied objects without revisiting that
|
|
156
|
+
* assumption.
|
|
157
|
+
*
|
|
158
|
+
* @param {object} obj
|
|
159
|
+
* @returns {boolean}
|
|
160
|
+
*/
|
|
161
|
+
const isEmpty = obj => {
|
|
162
|
+
// eslint-disable-next-line no-unreachable-loop
|
|
163
|
+
for (const _ in obj) return false
|
|
164
|
+
return true
|
|
165
|
+
}
|
|
166
|
+
|
|
146
167
|
module.exports = {
|
|
147
168
|
generatePointerHash,
|
|
148
169
|
encodeValue,
|
|
149
170
|
extractPrimaryKeys,
|
|
150
171
|
extractQueueMetadata,
|
|
172
|
+
isEmpty,
|
|
151
173
|
}
|
|
@@ -5,7 +5,7 @@ const shellParser = require('../../../vendor/dist/shell-quote').parse
|
|
|
5
5
|
const ALLOWED_ENV_VARIABLES = new Set(['LD_PRELOAD', 'LD_LIBRARY_PATH', 'PATH'])
|
|
6
6
|
const PROCESS_DENYLIST = new Set(['md5'])
|
|
7
7
|
|
|
8
|
-
const VARNAMES_REGEX = /\$(
|
|
8
|
+
const VARNAMES_REGEX = /\$(\w*)(?:[^\w]|$)/gmi
|
|
9
9
|
// eslint-disable-next-line @stylistic/max-len
|
|
10
10
|
const PARAM_PATTERN = '^-{0,2}(?:p(?:ass(?:w(?:or)?d)?)?|address|api[-_]?key|e?mail|secret(?:[-_]?key)?|a(?:ccess|uth)[-_]?token|mysql_pwd|credentials|(?:stripe)?token)$'
|
|
11
11
|
const regexParam = new RegExp(PARAM_PATTERN, 'i')
|
|
@@ -104,13 +104,13 @@ function scrubChildProcessCmd (expression) {
|
|
|
104
104
|
result.push(token)
|
|
105
105
|
|
|
106
106
|
if (PROCESS_DENYLIST.has(token)) {
|
|
107
|
-
for (
|
|
108
|
-
const
|
|
107
|
+
for (let i = index + 1; i < expressionTokens.length; i++) {
|
|
108
|
+
const innerToken = expressionTokens[i]
|
|
109
109
|
|
|
110
|
-
if (
|
|
111
|
-
result.push(
|
|
110
|
+
if (innerToken.op) {
|
|
111
|
+
result.push(innerToken.op)
|
|
112
112
|
} else {
|
|
113
|
-
expressionTokens[
|
|
113
|
+
expressionTokens[i] = REDACTED
|
|
114
114
|
result.push(REDACTED)
|
|
115
115
|
}
|
|
116
116
|
}
|
|
@@ -308,6 +308,7 @@ class CucumberPlugin extends CiPlugin {
|
|
|
308
308
|
isDisabled,
|
|
309
309
|
isQuarantined,
|
|
310
310
|
isModified,
|
|
311
|
+
earlyFlakeAbortReason,
|
|
311
312
|
finalStatus,
|
|
312
313
|
}) => {
|
|
313
314
|
const statusTag = isStep ? 'step.status' : TEST_STATUS
|
|
@@ -317,6 +318,9 @@ class CucumberPlugin extends CiPlugin {
|
|
|
317
318
|
if (finalStatus) {
|
|
318
319
|
span.setTag(TEST_FINAL_STATUS, finalStatus)
|
|
319
320
|
}
|
|
321
|
+
if (earlyFlakeAbortReason) {
|
|
322
|
+
span.setTag(TEST_EARLY_FLAKE_ABORT_REASON, earlyFlakeAbortReason)
|
|
323
|
+
}
|
|
320
324
|
|
|
321
325
|
if (isNew) {
|
|
322
326
|
span.setTag(TEST_IS_NEW, 'true')
|
|
@@ -1054,10 +1054,7 @@ class CypressPlugin {
|
|
|
1054
1054
|
rumFlushWaitMillis: this.rumFlushWaitMillis,
|
|
1055
1055
|
}
|
|
1056
1056
|
|
|
1057
|
-
|
|
1058
|
-
return suitePayload
|
|
1059
|
-
}
|
|
1060
|
-
this.testSuiteSpan = this.getTestSuiteSpan({ testSuite, testSuiteAbsolutePath })
|
|
1057
|
+
this.testSuiteSpan ||= this.getTestSuiteSpan({ testSuite, testSuiteAbsolutePath })
|
|
1061
1058
|
return suitePayload
|
|
1062
1059
|
},
|
|
1063
1060
|
'dd:beforeEach': (test) => {
|
|
@@ -122,8 +122,6 @@ class GoogleCloudPubsubConsumerPlugin extends ConsumerPlugin {
|
|
|
122
122
|
|
|
123
123
|
const topicName = topic?.slice(topic.lastIndexOf('/') + 1) ??
|
|
124
124
|
subscription.name.slice(subscription.name.lastIndexOf('/') + 1)
|
|
125
|
-
const baseService = this.tracer._service || 'unknown'
|
|
126
|
-
const serviceName = this.config.service || { name: `${baseService}-pubsub`, source: baseService }
|
|
127
125
|
|
|
128
126
|
const meta = {
|
|
129
127
|
'gcloud.project_id': subscription.pubsub.projectId,
|
|
@@ -133,8 +131,6 @@ class GoogleCloudPubsubConsumerPlugin extends ConsumerPlugin {
|
|
|
133
131
|
'pubsub.subscription': subscription.name,
|
|
134
132
|
'pubsub.subscription_type': 'pull',
|
|
135
133
|
'messaging.operation': 'receive',
|
|
136
|
-
base_service: baseService,
|
|
137
|
-
service_override_type: 'custom',
|
|
138
134
|
}
|
|
139
135
|
|
|
140
136
|
if (batchRequestTraceId && batchRequestSpanId) {
|
|
@@ -168,7 +164,7 @@ class GoogleCloudPubsubConsumerPlugin extends ConsumerPlugin {
|
|
|
168
164
|
childOf,
|
|
169
165
|
resource: `Message from ${topicName}`,
|
|
170
166
|
type: 'worker',
|
|
171
|
-
service: serviceName,
|
|
167
|
+
service: this.config.service || this.serviceName(),
|
|
172
168
|
meta,
|
|
173
169
|
metrics,
|
|
174
170
|
}, ctx)
|