dd-trace 5.104.0 → 5.106.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 +90 -102
- package/index.d.ts +82 -3
- package/package.json +15 -15
- package/packages/datadog-core/src/storage.js +1 -1
- package/packages/datadog-instrumentations/src/aerospike.js +1 -1
- package/packages/datadog-instrumentations/src/ai.js +8 -7
- package/packages/datadog-instrumentations/src/aws-sdk.js +16 -2
- package/packages/datadog-instrumentations/src/azure-cosmos.js +7 -0
- package/packages/datadog-instrumentations/src/azure-functions.js +3 -0
- package/packages/datadog-instrumentations/src/cucumber-worker-threads.js +19 -0
- package/packages/datadog-instrumentations/src/cucumber.js +390 -157
- package/packages/datadog-instrumentations/src/dns.js +54 -18
- package/packages/datadog-instrumentations/src/fastify.js +142 -82
- package/packages/datadog-instrumentations/src/graphql.js +188 -62
- package/packages/datadog-instrumentations/src/helpers/ai-messages.js +322 -14
- package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/openai-ai-guard.js +269 -0
- package/packages/datadog-instrumentations/src/helpers/promise-instrumentor.js +42 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -3
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/azure-cosmos.js +50 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/langgraph.js +4 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +85 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +37 -236
- package/packages/datadog-instrumentations/src/hono.js +54 -3
- package/packages/datadog-instrumentations/src/http/server.js +9 -4
- package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +163 -0
- package/packages/datadog-instrumentations/src/jest.js +360 -150
- package/packages/datadog-instrumentations/src/kafkajs.js +120 -16
- package/packages/datadog-instrumentations/src/mocha/main.js +128 -17
- package/packages/datadog-instrumentations/src/nats.js +182 -0
- package/packages/datadog-instrumentations/src/nyc.js +38 -1
- package/packages/datadog-instrumentations/src/openai.js +33 -18
- package/packages/datadog-instrumentations/src/oracledb.js +6 -1
- package/packages/datadog-instrumentations/src/pino.js +17 -5
- package/packages/datadog-instrumentations/src/playwright.js +515 -292
- package/packages/datadog-instrumentations/src/router.js +76 -32
- package/packages/datadog-instrumentations/src/stripe.js +1 -1
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/tracing.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +218 -4
- package/packages/datadog-plugin-azure-cosmos/src/index.js +144 -0
- package/packages/datadog-plugin-azure-event-hubs/src/producer.js +1 -1
- package/packages/datadog-plugin-azure-functions/src/index.js +5 -2
- package/packages/datadog-plugin-azure-service-bus/src/producer.js +1 -1
- package/packages/datadog-plugin-bunyan/src/index.js +28 -0
- package/packages/datadog-plugin-cucumber/src/index.js +17 -3
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +199 -28
- package/packages/datadog-plugin-cypress/src/support.js +69 -1
- package/packages/datadog-plugin-dns/src/lookup.js +8 -6
- package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +1 -1
- package/packages/datadog-plugin-graphql/src/execute.js +2 -0
- package/packages/datadog-plugin-graphql/src/resolve.js +64 -67
- package/packages/datadog-plugin-http/src/server.js +40 -15
- package/packages/datadog-plugin-jest/src/index.js +11 -3
- package/packages/datadog-plugin-jest/src/util.js +15 -8
- package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +1 -1
- package/packages/datadog-plugin-kafkajs/src/producer.js +3 -0
- package/packages/datadog-plugin-langgraph/src/stream.js +1 -1
- package/packages/datadog-plugin-mocha/src/index.js +19 -4
- package/packages/datadog-plugin-mongodb-core/src/index.js +281 -40
- package/packages/datadog-plugin-nats/src/consumer.js +43 -0
- package/packages/datadog-plugin-nats/src/index.js +20 -0
- package/packages/datadog-plugin-nats/src/producer.js +62 -0
- package/packages/datadog-plugin-nats/src/util.js +33 -0
- package/packages/datadog-plugin-next/src/index.js +5 -3
- package/packages/datadog-plugin-openai/src/tracing.js +15 -2
- package/packages/datadog-plugin-oracledb/src/index.js +13 -2
- package/packages/datadog-plugin-pino/src/index.js +42 -0
- package/packages/datadog-plugin-playwright/src/index.js +4 -4
- package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -1
- package/packages/datadog-plugin-rhea/src/producer.js +1 -1
- package/packages/datadog-plugin-router/src/index.js +33 -44
- package/packages/datadog-plugin-selenium/src/index.js +1 -1
- package/packages/datadog-plugin-vitest/src/index.js +5 -13
- package/packages/datadog-plugin-winston/src/index.js +30 -0
- package/packages/datadog-shimmer/src/shimmer.js +33 -40
- package/packages/dd-trace/src/aiguard/index.js +1 -1
- package/packages/dd-trace/src/aiguard/sdk.js +1 -1
- package/packages/dd-trace/src/appsec/api_security_sampler.js +1 -1
- package/packages/dd-trace/src/appsec/index.js +1 -1
- package/packages/dd-trace/src/appsec/reporter.js +5 -6
- package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
- package/packages/dd-trace/src/appsec/sdk/utils.js +1 -1
- package/packages/dd-trace/src/appsec/user_tracking.js +5 -4
- package/packages/dd-trace/src/baggage.js +7 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +0 -1
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +25 -13
- package/packages/dd-trace/src/ci-visibility/test-optimization-cache.js +70 -6
- package/packages/dd-trace/src/config/generated-config-types.d.ts +6 -2
- package/packages/dd-trace/src/config/supported-configurations.json +27 -8
- package/packages/dd-trace/src/datastreams/writer.js +2 -4
- package/packages/dd-trace/src/debugger/devtools_client/condition.js +5 -8
- package/packages/dd-trace/src/encode/0.4.js +124 -108
- package/packages/dd-trace/src/encode/0.5.js +114 -26
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +31 -23
- package/packages/dd-trace/src/encode/agentless-json.js +4 -2
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +32 -13
- package/packages/dd-trace/src/encode/span-stats.js +16 -16
- package/packages/dd-trace/src/encode/tags-processors.js +16 -0
- package/packages/dd-trace/src/id.js +15 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +92 -6
- package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +43 -21
- package/packages/dd-trace/src/llmobs/plugins/genai/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +9 -7
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +0 -16
- package/packages/dd-trace/src/llmobs/span_processor.js +3 -3
- package/packages/dd-trace/src/llmobs/tagger.js +9 -1
- package/packages/dd-trace/src/llmobs/telemetry.js +1 -1
- package/packages/dd-trace/src/llmobs/util.js +66 -3
- package/packages/dd-trace/src/log/index.js +1 -1
- package/packages/dd-trace/src/msgpack/chunk.js +394 -10
- package/packages/dd-trace/src/msgpack/index.js +96 -2
- package/packages/dd-trace/src/openfeature/encoding.js +70 -0
- package/packages/dd-trace/src/openfeature/flagging_provider.js +20 -0
- package/packages/dd-trace/src/openfeature/span-enrichment-hook.js +143 -0
- package/packages/dd-trace/src/openfeature/span-enrichment.js +149 -0
- package/packages/dd-trace/src/opentelemetry/span-helpers.js +4 -3
- package/packages/dd-trace/src/opentelemetry/span.js +1 -1
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +22 -3
- package/packages/dd-trace/src/opentracing/propagation/log.js +18 -7
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +64 -77
- package/packages/dd-trace/src/opentracing/span.js +59 -19
- package/packages/dd-trace/src/opentracing/span_context.js +50 -3
- package/packages/dd-trace/src/plugins/ci_plugin.js +20 -20
- package/packages/dd-trace/src/plugins/database.js +7 -6
- package/packages/dd-trace/src/plugins/index.js +4 -0
- package/packages/dd-trace/src/plugins/log_injection.js +56 -0
- package/packages/dd-trace/src/plugins/log_plugin.js +3 -48
- package/packages/dd-trace/src/plugins/outbound.js +1 -1
- package/packages/dd-trace/src/plugins/plugin.js +15 -17
- package/packages/dd-trace/src/plugins/tracing.js +43 -5
- package/packages/dd-trace/src/plugins/util/test.js +236 -13
- package/packages/dd-trace/src/plugins/util/web.js +79 -65
- package/packages/dd-trace/src/priority_sampler.js +2 -2
- package/packages/dd-trace/src/profiling/config.js +10 -23
- package/packages/dd-trace/src/profiling/exporters/agent.js +11 -10
- package/packages/dd-trace/src/profiling/profiler.js +21 -11
- package/packages/dd-trace/src/profiling/profilers/wall.js +12 -7
- package/packages/dd-trace/src/sampling_rule.js +7 -7
- package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +10 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +8 -0
- package/packages/dd-trace/src/service-naming/source-resolver.js +46 -0
- package/packages/dd-trace/src/span_format.js +190 -58
- package/packages/dd-trace/src/spanleak.js +1 -1
- package/packages/dd-trace/src/standalone/index.js +3 -3
- package/packages/dd-trace/src/tagger.js +0 -2
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +70 -39
- package/vendor/dist/@datadog/sketches-js/LICENSE +10 -36
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/packages/dd-trace/src/msgpack/encoder.js +0 -308
- package/packages/dd-trace/src/plugins/structured_log_plugin.js +0 -9
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { isMap, isRegExp } = require('node:util').types
|
|
4
|
+
|
|
3
5
|
const DatabasePlugin = require('../../dd-trace/src/plugins/database')
|
|
4
6
|
|
|
5
7
|
class MongodbCorePlugin extends DatabasePlugin {
|
|
@@ -138,62 +140,301 @@ function truncate (input) {
|
|
|
138
140
|
return input.length > MAX_QUERY_LENGTH ? input.slice(0, MAX_QUERY_LENGTH) : input
|
|
139
141
|
}
|
|
140
142
|
|
|
141
|
-
//
|
|
142
|
-
//
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
143
|
+
// Depth doubles as the cycle bound: a cycle pushes past MAX_DEPTH and bails,
|
|
144
|
+
// after which the slow path catches it via its ancestor stack.
|
|
145
|
+
/** @param {unknown} input */
|
|
146
|
+
function canStringifyDirect (input) {
|
|
147
|
+
if (input === null ||
|
|
148
|
+
typeof input !== 'object' ||
|
|
149
|
+
ArrayBuffer.isView(input) ||
|
|
150
|
+
input._bsontype !== undefined ||
|
|
151
|
+
isRegExp(input) ||
|
|
152
|
+
isMap(input) ||
|
|
153
|
+
typeof input.toJSON === 'function') {
|
|
154
|
+
return false
|
|
155
|
+
}
|
|
156
|
+
return canStringifyDirectWalk(input, 1)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* @param {Record<string, unknown> | unknown[]} value
|
|
161
|
+
* @param {number} depth
|
|
162
|
+
*/
|
|
163
|
+
function canStringifyDirectWalk (value, depth) {
|
|
164
|
+
if (depth > MAX_DEPTH) return false
|
|
165
|
+
const children = Array.isArray(value) ? value : Object.values(value)
|
|
166
|
+
for (const child of children) {
|
|
167
|
+
if (child === null ||
|
|
168
|
+
typeof child === 'string' ||
|
|
169
|
+
typeof child === 'number' ||
|
|
170
|
+
typeof child === 'boolean') {
|
|
171
|
+
continue
|
|
172
|
+
}
|
|
173
|
+
if (typeof child !== 'object' ||
|
|
174
|
+
ArrayBuffer.isView(child) ||
|
|
175
|
+
child._bsontype !== undefined ||
|
|
176
|
+
isRegExp(child) ||
|
|
177
|
+
isMap(child) ||
|
|
178
|
+
typeof child.toJSON === 'function') {
|
|
179
|
+
return false
|
|
180
|
+
}
|
|
181
|
+
if (!canStringifyDirectWalk(child, depth + 1)) return false
|
|
182
|
+
}
|
|
183
|
+
return true
|
|
184
|
+
}
|
|
185
|
+
|
|
152
186
|
/**
|
|
153
187
|
* @param {Record<string, unknown> | unknown[]} input
|
|
154
188
|
* @param {'none' | 'types' | 'redact'} mode
|
|
155
189
|
*/
|
|
156
190
|
function sanitiseAndStringify (input, mode) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
191
|
+
if (mode === 'none') {
|
|
192
|
+
if (canStringifyDirect(input)) return JSON.stringify(input)
|
|
193
|
+
return buildNone(input, [])
|
|
194
|
+
}
|
|
195
|
+
if (mode === 'redact') return buildRedact(input, [])
|
|
196
|
+
return buildTypes(input, [])
|
|
197
|
+
}
|
|
165
198
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
199
|
+
const REDACT_LEAF = '"?"'
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* @param {RegExp} value
|
|
203
|
+
* @returns {string}
|
|
204
|
+
*/
|
|
205
|
+
function stringifyRegExp (value) {
|
|
206
|
+
return `{"$regex":${JSON.stringify(value.source)},"$options":${JSON.stringify(value.flags)}}`
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* @param {Record<string, unknown> | unknown[]} value
|
|
211
|
+
* @param {object[]} ancestors
|
|
212
|
+
* @returns {string | undefined}
|
|
213
|
+
*/
|
|
214
|
+
function buildNone (value, ancestors) {
|
|
215
|
+
// ArrayBuffer views (Buffer, every TypedArray, DataView) and Binary BSON
|
|
216
|
+
// wrappers redact at the leaf; the walker neither recurses into the bytes
|
|
217
|
+
// nor invokes any custom conversion.
|
|
218
|
+
const bsontype = value._bsontype
|
|
219
|
+
if (ArrayBuffer.isView(value) || bsontype === 'Binary' ||
|
|
220
|
+
ancestors.length >= MAX_DEPTH || ancestors.includes(value)) {
|
|
221
|
+
return REDACT_LEAF
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (isRegExp(value)) return stringifyRegExp(value)
|
|
225
|
+
|
|
226
|
+
// Mirror JSON.stringify's contract: when `toJSON` is present, walk its
|
|
227
|
+
// result (wrappers like Timestamp / Decimal128 expand to a small object,
|
|
228
|
+
// ObjectId / Date flatten to a primitive).
|
|
229
|
+
if (typeof value.toJSON === 'function') {
|
|
230
|
+
const json = value.toJSON()
|
|
231
|
+
if (json === value) return REDACT_LEAF
|
|
232
|
+
// JSON.stringify keeps a null result as null (an invalid Date's toJSON
|
|
233
|
+
// returns null); only function / symbol / undefined results drop the key.
|
|
234
|
+
if (json === null) return 'null'
|
|
235
|
+
if (typeof json !== 'object') return classifyLeafForNone(json)
|
|
236
|
+
// A wrapper that exposes binary state through toJSON (Buffer-backed
|
|
237
|
+
// class with WeakMap state, etc.) returns a TypedArray here. Re-screen
|
|
238
|
+
// before the per-key walk would expand it element by element.
|
|
239
|
+
if (ArrayBuffer.isView(json) || json._bsontype === 'Binary') return REDACT_LEAF
|
|
240
|
+
value = json
|
|
241
|
+
} else if (bsontype !== undefined) {
|
|
242
|
+
return REDACT_LEAF
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// The driver serializes a Map via its entries; mirror that as a document so
|
|
246
|
+
// the tag matches the wire shape.
|
|
247
|
+
if (isMap(value)) value = Object.fromEntries(value)
|
|
248
|
+
|
|
249
|
+
ancestors.push(value)
|
|
250
|
+
|
|
251
|
+
let result
|
|
252
|
+
if (Array.isArray(value)) {
|
|
253
|
+
result = '['
|
|
254
|
+
let sep = ''
|
|
255
|
+
for (let i = 0; i < value.length; i++) {
|
|
256
|
+
// JSON.stringify renders unsupported leaves (function, symbol, undefined) as null in arrays.
|
|
257
|
+
result += sep + (classifyForNone(value[i], ancestors) ?? 'null')
|
|
258
|
+
sep = ','
|
|
172
259
|
}
|
|
260
|
+
result += ']'
|
|
261
|
+
} else {
|
|
262
|
+
result = '{'
|
|
263
|
+
let sep = ''
|
|
264
|
+
for (const key of Object.keys(value)) {
|
|
265
|
+
const childResult = classifyForNone(value[key], ancestors)
|
|
266
|
+
if (childResult === undefined) continue
|
|
267
|
+
result += sep + JSON.stringify(key) + ':' + childResult
|
|
268
|
+
sep = ','
|
|
269
|
+
}
|
|
270
|
+
result += '}'
|
|
271
|
+
}
|
|
272
|
+
ancestors.pop()
|
|
273
|
+
return result
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* @param {unknown} child
|
|
278
|
+
* @param {object[]} ancestors
|
|
279
|
+
* @returns {string | undefined}
|
|
280
|
+
*/
|
|
281
|
+
function classifyForNone (child, ancestors) {
|
|
282
|
+
if (typeof child !== 'object') return classifyLeafForNone(child)
|
|
283
|
+
if (child === null) return 'null'
|
|
284
|
+
return buildNone(child, ancestors)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* @param {unknown} leaf
|
|
289
|
+
* @returns {string | undefined}
|
|
290
|
+
*/
|
|
291
|
+
function classifyLeafForNone (leaf) {
|
|
292
|
+
// Implicit `undefined` for function / symbol / undefined matches the
|
|
293
|
+
// contract callers rely on: JSON.stringify drops those property values
|
|
294
|
+
// inside objects and writes `null` in arrays.
|
|
295
|
+
switch (typeof leaf) {
|
|
296
|
+
case 'string': return JSON.stringify(leaf)
|
|
297
|
+
case 'number': return Number.isFinite(leaf) ? String(leaf) : 'null'
|
|
298
|
+
case 'boolean': return leaf ? 'true' : 'false'
|
|
299
|
+
case 'bigint': return `"${String(leaf)}"`
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* @param {Record<string, unknown> | unknown[]} value
|
|
305
|
+
* @param {object[]} ancestors
|
|
306
|
+
*/
|
|
307
|
+
function buildRedact (value, ancestors) {
|
|
308
|
+
const bsontype = value._bsontype
|
|
309
|
+
if (ArrayBuffer.isView(value) || bsontype === 'Binary' || isRegExp(value) ||
|
|
310
|
+
ancestors.length >= MAX_DEPTH || ancestors.includes(value)) {
|
|
311
|
+
return REDACT_LEAF
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Mirror JSON.stringify: when `toJSON` is present, walk its result (which
|
|
315
|
+
// wrappers like Timestamp / Decimal128 expand to `{$timestamp: "..."}` etc).
|
|
316
|
+
// A primitive, null, or self-reference collapses to the sentinel — master's
|
|
317
|
+
// `value === original` short-circuit.
|
|
318
|
+
if (typeof value.toJSON === 'function') {
|
|
319
|
+
const json = value.toJSON()
|
|
320
|
+
if (typeof json !== 'object' || json === null || json === value) return REDACT_LEAF
|
|
321
|
+
// Re-screen: toJSON can return a TypedArray or Binary BSON wrapper.
|
|
322
|
+
if (ArrayBuffer.isView(json) || json._bsontype === 'Binary') return REDACT_LEAF
|
|
323
|
+
value = json
|
|
324
|
+
} else if (bsontype !== undefined) {
|
|
325
|
+
return REDACT_LEAF
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (isMap(value)) value = Object.fromEntries(value)
|
|
329
|
+
|
|
330
|
+
ancestors.push(value)
|
|
173
331
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
332
|
+
let result
|
|
333
|
+
if (Array.isArray(value)) {
|
|
334
|
+
result = '['
|
|
335
|
+
let sep = ''
|
|
336
|
+
for (let i = 0; i < value.length; i++) {
|
|
337
|
+
result += sep + classifyForRedact(value[i], ancestors)
|
|
338
|
+
sep = ','
|
|
178
339
|
}
|
|
340
|
+
result += ']'
|
|
341
|
+
} else {
|
|
342
|
+
result = '{'
|
|
343
|
+
let sep = ''
|
|
344
|
+
for (const key of Object.keys(value)) {
|
|
345
|
+
result += sep + JSON.stringify(key) + ':' + classifyForRedact(value[key], ancestors)
|
|
346
|
+
sep = ','
|
|
347
|
+
}
|
|
348
|
+
result += '}'
|
|
349
|
+
}
|
|
350
|
+
ancestors.pop()
|
|
351
|
+
return result
|
|
352
|
+
}
|
|
179
353
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
354
|
+
/**
|
|
355
|
+
* @param {unknown} child
|
|
356
|
+
* @param {object[]} ancestors
|
|
357
|
+
*/
|
|
358
|
+
function classifyForRedact (child, ancestors) {
|
|
359
|
+
if (typeof child !== 'object' || child === null) return REDACT_LEAF
|
|
360
|
+
return buildRedact(child, ancestors)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const TYPE_OBJECT = '"object"'
|
|
364
|
+
const TYPE_NULL = '"null"'
|
|
365
|
+
const TYPE_BY_TYPEOF = {
|
|
366
|
+
string: '"string"',
|
|
367
|
+
number: '"number"',
|
|
368
|
+
boolean: '"boolean"',
|
|
369
|
+
bigint: '"bigint"',
|
|
370
|
+
undefined: '"undefined"',
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* @param {Record<string, unknown> | unknown[]} value
|
|
375
|
+
* @param {object[]} ancestors
|
|
376
|
+
*/
|
|
377
|
+
function buildTypes (value, ancestors) {
|
|
378
|
+
const bsontype = value._bsontype
|
|
379
|
+
if (ArrayBuffer.isView(value) || bsontype === 'Binary' || isRegExp(value) ||
|
|
380
|
+
ancestors.length >= MAX_DEPTH || ancestors.includes(value)) {
|
|
381
|
+
return TYPE_OBJECT
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (typeof value.toJSON === 'function') {
|
|
385
|
+
const json = value.toJSON()
|
|
386
|
+
if (typeof json !== 'object' ||
|
|
387
|
+
json === null ||
|
|
388
|
+
json === value ||
|
|
389
|
+
ArrayBuffer.isView(json) ||
|
|
390
|
+
json._bsontype === 'Binary') {
|
|
391
|
+
return TYPE_OBJECT
|
|
183
392
|
}
|
|
184
|
-
|
|
393
|
+
value = json
|
|
394
|
+
} else if (bsontype !== undefined) {
|
|
395
|
+
return TYPE_OBJECT
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (isMap(value)) value = Object.fromEntries(value)
|
|
185
399
|
|
|
186
|
-
|
|
187
|
-
|
|
400
|
+
ancestors.push(value)
|
|
401
|
+
|
|
402
|
+
let result
|
|
403
|
+
if (Array.isArray(value)) {
|
|
404
|
+
result = '['
|
|
405
|
+
let sep = ''
|
|
406
|
+
for (let i = 0; i < value.length; i++) {
|
|
407
|
+
// JSON.stringify renders unsupported leaves (function, symbol) as null in arrays.
|
|
408
|
+
result += sep + (classifyForTypes(value[i], ancestors) ?? 'null')
|
|
409
|
+
sep = ','
|
|
410
|
+
}
|
|
411
|
+
result += ']'
|
|
412
|
+
} else {
|
|
413
|
+
result = '{'
|
|
414
|
+
let sep = ''
|
|
415
|
+
for (const key of Object.keys(value)) {
|
|
416
|
+
const childResult = classifyForTypes(value[key], ancestors)
|
|
417
|
+
if (childResult === undefined) continue
|
|
418
|
+
result += sep + JSON.stringify(key) + ':' + childResult
|
|
419
|
+
sep = ','
|
|
420
|
+
}
|
|
421
|
+
result += '}'
|
|
422
|
+
}
|
|
423
|
+
ancestors.pop()
|
|
424
|
+
return result
|
|
188
425
|
}
|
|
189
426
|
|
|
190
427
|
/**
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
* @param {unknown} value
|
|
195
|
-
* @returns {'none' | 'types' | 'redact'}
|
|
428
|
+
* @param {unknown} child
|
|
429
|
+
* @param {object[]} ancestors
|
|
196
430
|
*/
|
|
431
|
+
function classifyForTypes (child, ancestors) {
|
|
432
|
+
if (typeof child !== 'object') return TYPE_BY_TYPEOF[typeof child]
|
|
433
|
+
if (child === null) return TYPE_NULL
|
|
434
|
+
return buildTypes(child, ancestors)
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/** @param {unknown} value */
|
|
197
438
|
function normaliseObfuscateQuery (value) {
|
|
198
439
|
if (value === 'types' || value === 'redact') return value
|
|
199
440
|
return 'none'
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { TEXT_MAP } = require('../../../ext/formats')
|
|
4
|
+
const ConsumerPlugin = require('../../dd-trace/src/plugins/consumer')
|
|
5
|
+
const { headersToTextMap } = require('./util')
|
|
6
|
+
|
|
7
|
+
const MESSAGING_DESTINATION_KEY = 'messaging.destination.name'
|
|
8
|
+
|
|
9
|
+
class NatsConsumerPlugin extends ConsumerPlugin {
|
|
10
|
+
static id = 'nats'
|
|
11
|
+
static operation = 'consume'
|
|
12
|
+
|
|
13
|
+
bindStart (ctx) {
|
|
14
|
+
const { subject: filter, message } = ctx
|
|
15
|
+
// For wildcard subscriptions (e.g. `orders.*`), `filter` is the subscription
|
|
16
|
+
// pattern but `message.subject` is the actual delivered subject. Prefer the
|
|
17
|
+
// delivered one for resource/destination so spans aren't all collapsed under
|
|
18
|
+
// the wildcard pattern. Fall back to the filter if the message is missing it.
|
|
19
|
+
const subject = typeof message?.subject === 'string' ? message.subject : filter
|
|
20
|
+
const carrier = headersToTextMap(message?.headers)
|
|
21
|
+
const childOf = carrier ? this.tracer.extract(TEXT_MAP, carrier) : null
|
|
22
|
+
|
|
23
|
+
const meta = {
|
|
24
|
+
component: 'nats',
|
|
25
|
+
'nats.subject': subject,
|
|
26
|
+
[MESSAGING_DESTINATION_KEY]: subject,
|
|
27
|
+
}
|
|
28
|
+
if (filter && filter !== subject) {
|
|
29
|
+
meta['nats.subscription.subject'] = filter
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
this.startSpan({
|
|
33
|
+
childOf,
|
|
34
|
+
resource: subject,
|
|
35
|
+
type: 'worker',
|
|
36
|
+
meta,
|
|
37
|
+
}, ctx)
|
|
38
|
+
|
|
39
|
+
return ctx.currentStore
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = NatsConsumerPlugin
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const CompositePlugin = require('../../dd-trace/src/plugins/composite')
|
|
4
|
+
const ProducerPlugin = require('./producer')
|
|
5
|
+
const ConsumerPlugin = require('./consumer')
|
|
6
|
+
|
|
7
|
+
class NatsPlugin extends CompositePlugin {
|
|
8
|
+
static id = 'nats'
|
|
9
|
+
// Disabled by default — users must opt in via DD_TRACE_NATS_ENABLED=true
|
|
10
|
+
// or `tracer.use('nats')`. Matches the feature parity dashboard policy.
|
|
11
|
+
static experimental = true
|
|
12
|
+
static get plugins () {
|
|
13
|
+
return {
|
|
14
|
+
producer: ProducerPlugin,
|
|
15
|
+
consumer: ConsumerPlugin,
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
module.exports = NatsPlugin
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { TEXT_MAP } = require('../../../ext/formats')
|
|
4
|
+
const { CLIENT_PORT_KEY } = require('../../dd-trace/src/constants')
|
|
5
|
+
const ProducerPlugin = require('../../dd-trace/src/plugins/producer')
|
|
6
|
+
const { getOperationName } = require('./util')
|
|
7
|
+
|
|
8
|
+
const MESSAGING_DESTINATION_KEY = 'messaging.destination.name'
|
|
9
|
+
|
|
10
|
+
class NatsProducerPlugin extends ProducerPlugin {
|
|
11
|
+
static id = 'nats'
|
|
12
|
+
static operation = 'publish'
|
|
13
|
+
static peerServicePrecursors = [MESSAGING_DESTINATION_KEY]
|
|
14
|
+
|
|
15
|
+
bindStart (ctx) {
|
|
16
|
+
const { subject, options, connection, type, createHeaders } = ctx
|
|
17
|
+
const server = connection?.protocol?.servers?.getCurrent?.() ??
|
|
18
|
+
connection?.protocol?.servers?.getCurrentServer?.()
|
|
19
|
+
const operation = getOperationName(type)
|
|
20
|
+
|
|
21
|
+
const span = this.startSpan({
|
|
22
|
+
resource: subject,
|
|
23
|
+
meta: {
|
|
24
|
+
component: 'nats',
|
|
25
|
+
'nats.subject': subject,
|
|
26
|
+
'nats.operation': operation,
|
|
27
|
+
[MESSAGING_DESTINATION_KEY]: subject,
|
|
28
|
+
'out.host': server?.hostname,
|
|
29
|
+
},
|
|
30
|
+
}, ctx)
|
|
31
|
+
|
|
32
|
+
if (server?.port) {
|
|
33
|
+
span.setTag(CLIENT_PORT_KEY, server.port)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (this.serverSupportsHeaders(connection)) {
|
|
37
|
+
let headers = options.headers
|
|
38
|
+
if (!headers && typeof createHeaders === 'function') {
|
|
39
|
+
headers = createHeaders()
|
|
40
|
+
options.headers = headers
|
|
41
|
+
}
|
|
42
|
+
if (headers && typeof headers.set === 'function') {
|
|
43
|
+
const carrier = {}
|
|
44
|
+
this.tracer.inject(span, TEXT_MAP, carrier)
|
|
45
|
+
for (const key of Object.keys(carrier)) {
|
|
46
|
+
headers.set(key, carrier[key])
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return ctx.currentStore
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
serverSupportsHeaders (connection) {
|
|
55
|
+
const info = connection?.protocol?.info
|
|
56
|
+
// If info isn't available yet (e.g. publish before INFO), assume supported — modern NATS does.
|
|
57
|
+
if (!info) return true
|
|
58
|
+
return info.headers !== false
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = NatsProducerPlugin
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
function headersToTextMap (msgHdrs) {
|
|
4
|
+
if (!msgHdrs || typeof msgHdrs[Symbol.iterator] !== 'function') return null
|
|
5
|
+
const textMap = {}
|
|
6
|
+
for (const [key, values] of msgHdrs) {
|
|
7
|
+
if (!Array.isArray(values) || values.length === 0) continue
|
|
8
|
+
// Trace headers are single-valued (injected via `set`, not `append`), so
|
|
9
|
+
// the first element is always the authoritative value.
|
|
10
|
+
textMap[key] = values[0]
|
|
11
|
+
}
|
|
12
|
+
return textMap
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getOperationName (type) {
|
|
16
|
+
switch (type) {
|
|
17
|
+
case 'publish':
|
|
18
|
+
return 'publish'
|
|
19
|
+
case 'request':
|
|
20
|
+
case 'requestMany':
|
|
21
|
+
return 'request'
|
|
22
|
+
default:
|
|
23
|
+
// Surface unrecognized operations explicitly rather than silently
|
|
24
|
+
// collapsing them into 'publish' — if NATS adds a new outbound API,
|
|
25
|
+
// this lets us see it in traces and fix the mapping deliberately.
|
|
26
|
+
return 'unknown'
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = {
|
|
31
|
+
headersToTextMap,
|
|
32
|
+
getOperationName,
|
|
33
|
+
}
|
|
@@ -33,11 +33,13 @@ class NextPlugin extends ServerPlugin {
|
|
|
33
33
|
'span.type': 'web',
|
|
34
34
|
'span.kind': 'server',
|
|
35
35
|
'http.method': req.method,
|
|
36
|
-
...(serviceSource === undefined ?
|
|
36
|
+
...(serviceSource === undefined ? undefined : { [SVC_SRC_KEY]: serviceSource }),
|
|
37
37
|
},
|
|
38
38
|
integrationName: this.constructor.id,
|
|
39
39
|
})
|
|
40
40
|
|
|
41
|
+
this.stampIntegrationService(span, serviceName)
|
|
42
|
+
|
|
41
43
|
analyticsSampler.sample(span, this.config.measured, true)
|
|
42
44
|
|
|
43
45
|
return { ...store, span, req }
|
|
@@ -60,7 +62,7 @@ class NextPlugin extends ServerPlugin {
|
|
|
60
62
|
if (!store) return
|
|
61
63
|
|
|
62
64
|
const span = store.span
|
|
63
|
-
const error = span.context().
|
|
65
|
+
const error = span.context().getTag('error')
|
|
64
66
|
const requestError = req.error || nextRequest.error
|
|
65
67
|
|
|
66
68
|
if (requestError) {
|
|
@@ -97,7 +99,7 @@ class NextPlugin extends ServerPlugin {
|
|
|
97
99
|
if (!req) return
|
|
98
100
|
|
|
99
101
|
// Only use error page names if there's not already a name
|
|
100
|
-
const current = span.context().
|
|
102
|
+
const current = span.context().getTag('next.page')
|
|
101
103
|
const isErrorPage = errorPages.has(page)
|
|
102
104
|
|
|
103
105
|
if (current && isErrorPage) {
|
|
@@ -8,6 +8,7 @@ const Sampler = require('../../dd-trace/src/sampler')
|
|
|
8
8
|
const { MEASURED } = require('../../../ext/tags')
|
|
9
9
|
|
|
10
10
|
const { DD_MAJOR } = require('../../../version')
|
|
11
|
+
const { ERROR_TYPE } = require('../../dd-trace/src/constants')
|
|
11
12
|
const {
|
|
12
13
|
convertBuffersToObjects,
|
|
13
14
|
constructCompletionResponseFromStreamedChunks,
|
|
@@ -157,7 +158,7 @@ class OpenAiTracingPlugin extends TracingPlugin {
|
|
|
157
158
|
const span = store?.span
|
|
158
159
|
if (!span) return
|
|
159
160
|
|
|
160
|
-
const error = !!span.context().
|
|
161
|
+
const error = !!span.context().getTag('error')
|
|
161
162
|
|
|
162
163
|
let headers, body, method, path
|
|
163
164
|
if (!error) {
|
|
@@ -171,7 +172,7 @@ class OpenAiTracingPlugin extends TracingPlugin {
|
|
|
171
172
|
headers = Object.fromEntries(headers)
|
|
172
173
|
}
|
|
173
174
|
|
|
174
|
-
const resource = span.
|
|
175
|
+
const resource = span.context().getTag('resource.name')
|
|
175
176
|
const normalizedMethodName = store.normalizedMethodName
|
|
176
177
|
|
|
177
178
|
body = coerceResponseBody(body, normalizedMethodName)
|
|
@@ -211,6 +212,18 @@ class OpenAiTracingPlugin extends TracingPlugin {
|
|
|
211
212
|
this.sendMetrics(headers, body, endpoint, span._duration, error, tags)
|
|
212
213
|
}
|
|
213
214
|
|
|
215
|
+
error (ctx) {
|
|
216
|
+
const span = ctx.currentStore?.span
|
|
217
|
+
if (!span) return
|
|
218
|
+
|
|
219
|
+
super.error(ctx) // add normal error tag
|
|
220
|
+
|
|
221
|
+
const errorType = ctx.error?.type
|
|
222
|
+
if (errorType) {
|
|
223
|
+
span.setTag(ERROR_TYPE, errorType)
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
214
227
|
sendMetrics (headers, body, endpoint, duration, error, spanTags) {
|
|
215
228
|
const tags = [`error:${Number(!!error)}`]
|
|
216
229
|
if (error) {
|
|
@@ -24,19 +24,30 @@ class OracledbPlugin extends DatabasePlugin {
|
|
|
24
24
|
dbInstance ??= dbInfo.dbInstance
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
// oracledb >= 6.4 accepts `execute({ statement, values })` (sql-template-tag form)
|
|
28
|
+
// in addition to a plain SQL string. Extract the SQL text either way so we can tag
|
|
29
|
+
// the resource and inject DBM into the statement, then re-wrap if needed to keep
|
|
30
|
+
// the caller's binds.
|
|
31
|
+
const sql = query?.statement ?? query
|
|
32
|
+
|
|
33
|
+
const span = this.startSpan(this.operationName(), {
|
|
28
34
|
service,
|
|
29
|
-
resource:
|
|
35
|
+
resource: sql,
|
|
30
36
|
type: 'sql',
|
|
31
37
|
kind: 'client',
|
|
32
38
|
meta: {
|
|
33
39
|
'db.user': this.config.user,
|
|
34
40
|
'db.instance': dbInstance,
|
|
41
|
+
'db.name': dbInstance,
|
|
35
42
|
'db.hostname': hostname,
|
|
43
|
+
'out.host': hostname,
|
|
36
44
|
[CLIENT_PORT_KEY]: port,
|
|
37
45
|
},
|
|
38
46
|
}, ctx)
|
|
39
47
|
|
|
48
|
+
const injected = this.injectDbmQuery(span, sql, service.name)
|
|
49
|
+
ctx.injected = query?.statement ? { ...query, statement: injected } : injected
|
|
50
|
+
|
|
40
51
|
return ctx.currentStore
|
|
41
52
|
}
|
|
42
53
|
}
|
|
@@ -1,9 +1,51 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { buildLogHolder, messageProxy } = require('../../dd-trace/src/plugins/log_injection')
|
|
3
4
|
const LogPlugin = require('../../dd-trace/src/plugins/log_plugin')
|
|
4
5
|
|
|
5
6
|
class PinoPlugin extends LogPlugin {
|
|
6
7
|
static id = 'pino'
|
|
8
|
+
|
|
9
|
+
constructor (...args) {
|
|
10
|
+
super(...args)
|
|
11
|
+
this.addSub('apm:pino:log:json', (payload) => this.handleJsonLine(payload))
|
|
12
|
+
this.addSub('apm:pino:log', (arg) => this.handlePrettyMessage(arg))
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Splice `,"dd":<json>` into the JSON line pino has already produced.
|
|
17
|
+
* The caller-owned message object is never observed -- user Proxies and
|
|
18
|
+
* custom serialisers see nothing because there is no mutation to see.
|
|
19
|
+
*
|
|
20
|
+
* @param {{ line: string }} payload
|
|
21
|
+
*/
|
|
22
|
+
handleJsonLine (payload) {
|
|
23
|
+
const logHolder = buildLogHolder(this.tracer)
|
|
24
|
+
if (!logHolder) return
|
|
25
|
+
|
|
26
|
+
const line = payload.line
|
|
27
|
+
const lastClose = line.lastIndexOf('}')
|
|
28
|
+
if (lastClose < 1) return
|
|
29
|
+
|
|
30
|
+
const ddJson = JSON.stringify(logHolder.dd)
|
|
31
|
+
const sep = line.charCodeAt(lastClose - 1) === 0x7B ? '' : ','
|
|
32
|
+
payload.line = line.slice(0, lastClose) + sep + '"dd":' + ddJson + line.slice(lastClose)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* `pino-pretty` (bundled with pino 5/7, separate package on >=8) reads
|
|
37
|
+
* the original message object rather than the JSON line, so the splice
|
|
38
|
+
* above is invisible to it. Wrap the message in a Proxy that exposes a
|
|
39
|
+
* virtual `dd` field for the prettifier to pick up.
|
|
40
|
+
*
|
|
41
|
+
* @param {{ message: object }} arg
|
|
42
|
+
*/
|
|
43
|
+
handlePrettyMessage (arg) {
|
|
44
|
+
const logHolder = buildLogHolder(this.tracer)
|
|
45
|
+
if (!logHolder) return
|
|
46
|
+
|
|
47
|
+
arg.message = messageProxy(arg.message, logHolder)
|
|
48
|
+
}
|
|
7
49
|
}
|
|
8
50
|
|
|
9
51
|
module.exports = PinoPlugin
|