dd-trace 5.100.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/index.d.ts +14 -0
- package/package.json +11 -9
- 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/cypress.js +5 -3
- 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 +26 -9
- package/packages/datadog-instrumentations/src/http/server.js +9 -9
- package/packages/datadog-instrumentations/src/jest.js +93 -63
- 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 +7 -4
- package/packages/datadog-instrumentations/src/mocha/main.js +37 -14
- package/packages/datadog-instrumentations/src/mocha/utils.js +133 -16
- package/packages/datadog-instrumentations/src/mocha/worker.js +12 -7
- 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/otel-sdk-trace.js +11 -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-bullmq/src/consumer.js +2 -2
- package/packages/datadog-plugin-bullmq/src/producer.js +14 -20
- 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 +18 -4
- package/packages/datadog-plugin-cypress/src/plugin.js +5 -14
- 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-kafkajs/src/consumer.js +2 -9
- package/packages/datadog-plugin-kafkajs/src/producer.js +2 -8
- 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 +7 -2
- 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/ci-visibility/lage.js +2 -1
- package/packages/dd-trace/src/ci-visibility/requests/request.js +11 -33
- package/packages/dd-trace/src/config/config-types.d.ts +0 -2
- package/packages/dd-trace/src/config/git_properties.js +2 -2
- package/packages/dd-trace/src/config/index.js +1 -55
- package/packages/dd-trace/src/datastreams/checkpointer.js +4 -10
- package/packages/dd-trace/src/datastreams/encoding.js +39 -28
- package/packages/dd-trace/src/datastreams/index.js +2 -1
- package/packages/dd-trace/src/datastreams/pathway.js +29 -26
- package/packages/dd-trace/src/datastreams/processor.js +18 -17
- package/packages/dd-trace/src/datastreams/size.js +6 -2
- package/packages/dd-trace/src/debugger/config.js +5 -2
- package/packages/dd-trace/src/debugger/devtools_client/index.js +2 -5
- package/packages/dd-trace/src/debugger/devtools_client/send.js +2 -1
- package/packages/dd-trace/src/debugger/devtools_client/snapshot-pruner.js +1 -0
- package/packages/dd-trace/src/dogstatsd.js +10 -7
- package/packages/dd-trace/src/encode/0.4.js +759 -234
- package/packages/dd-trace/src/encode/0.5.js +15 -9
- package/packages/dd-trace/src/encode/agentless-json.js +2 -2
- package/packages/dd-trace/src/encode/tags-processors.js +2 -27
- package/packages/dd-trace/src/exporters/common/request.js +22 -11
- package/packages/dd-trace/src/exporters/common/retry.js +104 -0
- package/packages/dd-trace/src/git_metadata.js +66 -0
- package/packages/dd-trace/src/git_metadata_tagger.js +13 -5
- package/packages/dd-trace/src/id.js +15 -26
- package/packages/dd-trace/src/llmobs/constants/tags.js +2 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -2
- package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +27 -16
- package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +3 -0
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +33 -13
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +20 -50
- package/packages/dd-trace/src/llmobs/sdk.js +29 -27
- package/packages/dd-trace/src/llmobs/span_processor.js +52 -6
- package/packages/dd-trace/src/llmobs/tagger.js +42 -0
- package/packages/dd-trace/src/llmobs/telemetry.js +29 -0
- package/packages/dd-trace/src/llmobs/util.js +81 -5
- 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/active-span-proxy.js +42 -0
- package/packages/dd-trace/src/opentelemetry/bridge-span-base.js +106 -0
- package/packages/dd-trace/src/opentelemetry/context_manager.js +11 -2
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +1 -2
- package/packages/dd-trace/src/opentelemetry/span-helpers.js +188 -50
- package/packages/dd-trace/src/opentelemetry/span.js +42 -80
- package/packages/dd-trace/src/opentelemetry/tracer.js +0 -22
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +65 -27
- package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +2 -11
- package/packages/dd-trace/src/opentracing/propagation/tracestate.js +58 -22
- package/packages/dd-trace/src/opentracing/span.js +56 -48
- package/packages/dd-trace/src/opentracing/span_context.js +1 -0
- 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/priority_sampler.js +6 -4
- package/packages/dd-trace/src/profiling/config.js +5 -4
- 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/remote_config/index.js +5 -3
- 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/span_format.js +52 -5
- package/packages/dd-trace/src/span_processor.js +0 -4
- package/packages/dd-trace/src/spanleak.js +0 -1
- package/packages/dd-trace/src/tracer.js +7 -7
- package/packages/dd-trace/src/util.js +17 -0
|
@@ -8,10 +8,51 @@ const { tagsFromRequest, tagsFromResponse } = require('../../dd-trace/src/payloa
|
|
|
8
8
|
const { getValueFromEnvSources } = require('../../dd-trace/src/config/helper')
|
|
9
9
|
const { IS_SERVERLESS } = require('../../dd-trace/src/serverless')
|
|
10
10
|
|
|
11
|
+
const RESPONSE_SKIP_KEYS = new Set(['request', 'requestId', 'error', '$metadata'])
|
|
12
|
+
|
|
11
13
|
class BaseAwsSdkPlugin extends ClientPlugin {
|
|
12
14
|
static id = 'aws'
|
|
13
15
|
static isPayloadReporter = false
|
|
14
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Append `"<key>": <JSON.stringify(value)>` to a JSON-encoded object
|
|
19
|
+
* payload without re-parsing when possible.
|
|
20
|
+
*
|
|
21
|
+
* Fast path: `payload` is `{}` (returns `{"<key>":<json>}`) or ends with
|
|
22
|
+
* `}` preceded by a non-whitespace, non-`{` byte and does not contain
|
|
23
|
+
* `"<key>"` anywhere. The new field is spliced in before the trailing
|
|
24
|
+
* brace.
|
|
25
|
+
*
|
|
26
|
+
* Slow path falls back to `JSON.parse` + assign + `JSON.stringify` so the
|
|
27
|
+
* result still matches the previous round-trip when the payload has
|
|
28
|
+
* whitespace before the trailing `}`, is not a JSON object, or already
|
|
29
|
+
* contains `key`. The slow path replaces an existing `key` rather than
|
|
30
|
+
* merging — callers that need to preserve nested fields under `key` must
|
|
31
|
+
* read and merge before calling.
|
|
32
|
+
*
|
|
33
|
+
* @param {string} payload
|
|
34
|
+
* @param {string} key Top-level key to insert. Must be a simple
|
|
35
|
+
* identifier that does not need JSON escaping.
|
|
36
|
+
* @param {object} value Value to inject; will be `JSON.stringify`'d.
|
|
37
|
+
* @returns {string}
|
|
38
|
+
*/
|
|
39
|
+
static injectFieldIntoJsonObject (payload, key, value) {
|
|
40
|
+
const last = payload.length - 1
|
|
41
|
+
if (last >= 1 && payload[last] === '}') {
|
|
42
|
+
if (last === 1) {
|
|
43
|
+
return `{"${key}":${JSON.stringify(value)}}`
|
|
44
|
+
}
|
|
45
|
+
const before = payload.charCodeAt(last - 1)
|
|
46
|
+
const isWhitespace = before === 0x20 || before === 0x09 || before === 0x0A || before === 0x0D
|
|
47
|
+
if (!isWhitespace && before !== 0x7B && !payload.includes(`"${key}"`)) {
|
|
48
|
+
return `${payload.slice(0, last)},"${key}":${JSON.stringify(value)}}`
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const obj = JSON.parse(payload)
|
|
52
|
+
obj[key] = value
|
|
53
|
+
return JSON.stringify(obj)
|
|
54
|
+
}
|
|
55
|
+
|
|
15
56
|
get serviceIdentifier () {
|
|
16
57
|
const id = this.constructor.id.toLowerCase()
|
|
17
58
|
Object.defineProperty(this, 'serviceIdentifier', {
|
|
@@ -224,12 +265,19 @@ class BaseAwsSdkPlugin extends ClientPlugin {
|
|
|
224
265
|
}
|
|
225
266
|
|
|
226
267
|
extractResponseBody (response) {
|
|
227
|
-
if (
|
|
268
|
+
if (Object.hasOwn(response, 'data')) {
|
|
228
269
|
return response.data
|
|
229
270
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
271
|
+
// `{ ...response }` followed by `delete body.X` allocates a copy and then
|
|
272
|
+
// pushes the copy into V8 dictionary mode for every SDK response. Filter
|
|
273
|
+
// on build instead -- ~2.3x faster on the typical 4-of-8-keys shape.
|
|
274
|
+
const body = {}
|
|
275
|
+
for (const key of Object.keys(response)) {
|
|
276
|
+
if (!RESPONSE_SKIP_KEYS.has(key)) {
|
|
277
|
+
body[key] = response[key]
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return body
|
|
233
281
|
}
|
|
234
282
|
|
|
235
283
|
generateTags () {
|
|
@@ -34,20 +34,27 @@ class EventBridge extends BaseAwsSdkPlugin {
|
|
|
34
34
|
request.params.Entries &&
|
|
35
35
|
request.params.Entries.length > 0 &&
|
|
36
36
|
request.params.Entries[0].Detail) {
|
|
37
|
+
const injected = {}
|
|
38
|
+
this.tracer.inject(span, 'text_map', injected)
|
|
39
|
+
|
|
40
|
+
// Only `injectFieldIntoJsonObject` can throw (the slow path
|
|
41
|
+
// `JSON.parse` for non-`{...}` payloads). Tighten the catch around
|
|
42
|
+
// it so the rest of the body stays in V8's optimisable surface.
|
|
43
|
+
let finalData
|
|
37
44
|
try {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
log.error('EventBridge error injecting request', e)
|
|
45
|
+
finalData = BaseAwsSdkPlugin.injectFieldIntoJsonObject(
|
|
46
|
+
request.params.Entries[0].Detail, '_datadog', injected
|
|
47
|
+
)
|
|
48
|
+
} catch (error) {
|
|
49
|
+
log.error('EventBridge error injecting request', error)
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (Buffer.byteLength(finalData) >= 1024 * 256) {
|
|
54
|
+
log.info('Payload size too large to pass context')
|
|
55
|
+
return
|
|
50
56
|
}
|
|
57
|
+
request.params.Entries[0].Detail = finalData
|
|
51
58
|
}
|
|
52
59
|
}
|
|
53
60
|
}
|
|
@@ -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
|
}
|