dd-trace 5.102.0 → 5.104.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/ext/exporters.js +1 -0
- package/index.d.ts +25 -3
- package/package.json +15 -13
- package/packages/datadog-esbuild/src/utils.js +2 -2
- package/packages/datadog-instrumentations/src/ai.js +1 -1
- package/packages/datadog-instrumentations/src/aws-sdk.js +2 -2
- package/packages/datadog-instrumentations/src/cassandra-driver.js +5 -2
- package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +32 -15
- package/packages/datadog-instrumentations/src/couchbase.js +69 -220
- package/packages/datadog-instrumentations/src/cucumber.js +104 -31
- package/packages/datadog-instrumentations/src/elasticsearch.js +4 -4
- package/packages/datadog-instrumentations/src/electron/preload.js +42 -0
- package/packages/datadog-instrumentations/src/electron.js +240 -0
- package/packages/datadog-instrumentations/src/fetch.js +5 -5
- package/packages/datadog-instrumentations/src/graphql.js +13 -17
- package/packages/datadog-instrumentations/src/grpc/client.js +48 -32
- package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +2 -2
- package/packages/datadog-instrumentations/src/helpers/hook.js +4 -1
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +2 -2
- package/packages/datadog-instrumentations/src/helpers/kafka.js +58 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +3 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +19 -5
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +14 -13
- package/packages/datadog-instrumentations/src/http/client.js +2 -2
- package/packages/datadog-instrumentations/src/ioredis.js +18 -14
- package/packages/datadog-instrumentations/src/jest.js +382 -84
- package/packages/datadog-instrumentations/src/kafkajs.js +184 -174
- package/packages/datadog-instrumentations/src/mariadb.js +1 -1
- package/packages/datadog-instrumentations/src/memcached.js +2 -1
- package/packages/datadog-instrumentations/src/mocha/main.js +309 -56
- package/packages/datadog-instrumentations/src/mocha/utils.js +48 -8
- package/packages/datadog-instrumentations/src/mongodb-core.js +34 -9
- package/packages/datadog-instrumentations/src/mongoose.js +10 -12
- package/packages/datadog-instrumentations/src/mysql.js +2 -2
- package/packages/datadog-instrumentations/src/mysql2.js +1 -1
- package/packages/datadog-instrumentations/src/pg.js +25 -11
- package/packages/datadog-instrumentations/src/playwright.js +449 -60
- package/packages/datadog-instrumentations/src/redis.js +19 -10
- package/packages/datadog-instrumentations/src/router.js +4 -2
- package/packages/datadog-instrumentations/src/vitest.js +246 -149
- package/packages/datadog-plugin-apollo/src/gateway/request.js +1 -21
- package/packages/datadog-plugin-aws-sdk/src/base.js +18 -24
- package/packages/datadog-plugin-aws-sdk/src/services/cloudwatchlogs.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/lambda.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/s3.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +2 -2
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +1 -1
- package/packages/datadog-plugin-couchbase/src/index.js +58 -52
- package/packages/datadog-plugin-cucumber/src/index.js +1 -0
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +239 -40
- package/packages/datadog-plugin-cypress/src/support.js +13 -1
- package/packages/datadog-plugin-elasticsearch/src/index.js +28 -8
- package/packages/datadog-plugin-electron/src/index.js +17 -0
- package/packages/datadog-plugin-electron/src/ipc.js +143 -0
- package/packages/datadog-plugin-electron/src/net.js +82 -0
- package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +27 -18
- package/packages/datadog-plugin-graphql/src/execute.js +6 -28
- package/packages/datadog-plugin-graphql/src/resolve.js +30 -35
- package/packages/datadog-plugin-graphql/src/tools/signature.js +32 -7
- package/packages/datadog-plugin-graphql/src/tools/transforms.js +118 -100
- package/packages/datadog-plugin-graphql/src/utils.js +33 -1
- package/packages/datadog-plugin-grpc/src/client.js +6 -7
- package/packages/datadog-plugin-grpc/src/util.js +57 -22
- package/packages/datadog-plugin-http/src/client.js +2 -2
- package/packages/datadog-plugin-jest/src/index.js +92 -50
- package/packages/datadog-plugin-kafkajs/src/producer.js +32 -0
- package/packages/datadog-plugin-mocha/src/index.js +1 -0
- package/packages/datadog-plugin-mongodb-core/src/index.js +70 -69
- package/packages/datadog-plugin-mysql/src/index.js +1 -1
- package/packages/datadog-plugin-openai/src/services.js +2 -1
- package/packages/datadog-plugin-pg/src/index.js +3 -3
- package/packages/datadog-plugin-playwright/src/index.js +4 -0
- package/packages/datadog-plugin-redis/src/index.js +54 -24
- package/packages/datadog-plugin-undici/src/index.js +19 -0
- package/packages/datadog-plugin-vitest/src/index.js +19 -7
- package/packages/datadog-shimmer/src/shimmer.js +35 -0
- package/packages/dd-trace/src/aiguard/index.js +3 -1
- package/packages/dd-trace/src/aiguard/sdk.js +36 -30
- package/packages/dd-trace/src/aiguard/tags.js +20 -11
- package/packages/dd-trace/src/appsec/blocking.js +2 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +2 -2
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +1 -1
- package/packages/dd-trace/src/appsec/index.js +10 -3
- package/packages/dd-trace/src/appsec/reporter.js +19 -5
- package/packages/dd-trace/src/azure_metadata.js +17 -6
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +4 -4
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -2
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +6 -4
- package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +1 -1
- package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -1
- package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +5 -3
- package/packages/dd-trace/src/config/defaults.js +3 -14
- package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -1
- package/packages/dd-trace/src/config/helper.js +4 -0
- package/packages/dd-trace/src/config/index.js +2 -2
- package/packages/dd-trace/src/config/major-overrides.js +98 -0
- package/packages/dd-trace/src/config/parsers.js +7 -1
- package/packages/dd-trace/src/config/supported-configurations.json +60 -38
- package/packages/dd-trace/src/crashtracking/crashtracker.js +15 -3
- package/packages/dd-trace/src/datastreams/checkpointer.js +2 -2
- package/packages/dd-trace/src/datastreams/context.js +4 -2
- package/packages/dd-trace/src/datastreams/manager.js +1 -1
- package/packages/dd-trace/src/datastreams/processor.js +2 -2
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +2 -2
- package/packages/dd-trace/src/debugger/devtools_client/source-maps.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/state.js +2 -1
- package/packages/dd-trace/src/debugger/index.js +7 -7
- package/packages/dd-trace/src/dogstatsd.js +2 -2
- package/packages/dd-trace/src/encode/0.4.js +45 -54
- package/packages/dd-trace/src/encode/0.5.js +34 -3
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +26 -19
- package/packages/dd-trace/src/encode/agentless-json.js +1 -1
- package/packages/dd-trace/src/exporter.js +2 -0
- package/packages/dd-trace/src/exporters/agent/index.js +2 -1
- package/packages/dd-trace/src/exporters/agentless/index.js +3 -2
- package/packages/dd-trace/src/exporters/agentless/writer.js +2 -2
- package/packages/dd-trace/src/exporters/common/agents.js +3 -1
- package/packages/dd-trace/src/exporters/common/buffering-exporter.js +2 -1
- package/packages/dd-trace/src/exporters/common/request.js +4 -2
- package/packages/dd-trace/src/exporters/electron/index.js +49 -0
- package/packages/dd-trace/src/external-logger/src/index.js +2 -1
- package/packages/dd-trace/src/git_metadata.js +10 -8
- package/packages/dd-trace/src/id.js +17 -4
- package/packages/dd-trace/src/lambda/handler-paths.js +52 -0
- package/packages/dd-trace/src/lambda/handler.js +2 -4
- package/packages/dd-trace/src/lambda/index.js +62 -14
- package/packages/dd-trace/src/lambda/runtime/patch.js +21 -46
- package/packages/dd-trace/src/llmobs/index.js +13 -2
- package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +45 -15
- package/packages/dd-trace/src/llmobs/sdk.js +10 -0
- package/packages/dd-trace/src/llmobs/writers/base.js +2 -1
- package/packages/dd-trace/src/log/writer.js +3 -1
- package/packages/dd-trace/src/noop/span.js +3 -1
- package/packages/dd-trace/src/openfeature/writers/base.js +2 -1
- package/packages/dd-trace/src/openfeature/writers/exposures.js +51 -20
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +3 -2
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +20 -9
- package/packages/dd-trace/src/payload-tagging/config/index.js +2 -2
- package/packages/dd-trace/src/plugins/apollo.js +3 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +52 -17
- package/packages/dd-trace/src/plugins/database.js +54 -12
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/log_plugin.js +3 -1
- package/packages/dd-trace/src/plugins/plugin.js +2 -4
- package/packages/dd-trace/src/plugins/tracing.js +5 -3
- package/packages/dd-trace/src/plugins/util/ci.js +8 -8
- package/packages/dd-trace/src/plugins/util/git-cache.js +20 -18
- package/packages/dd-trace/src/plugins/util/git.js +3 -1
- package/packages/dd-trace/src/plugins/util/stacktrace.js +2 -2
- package/packages/dd-trace/src/plugins/util/test.js +119 -5
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +17 -15
- package/packages/dd-trace/src/plugins/util/web.js +11 -0
- package/packages/dd-trace/src/priority_sampler.js +1 -1
- package/packages/dd-trace/src/profiling/profiler.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/wall.js +1 -1
- package/packages/dd-trace/src/profiling/ssi-heuristics.js +1 -1
- package/packages/dd-trace/src/rate_limiter.js +1 -1
- package/packages/dd-trace/src/remote_config/scheduler.js +1 -1
- package/packages/dd-trace/src/ritm.js +2 -1
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +5 -8
- package/packages/dd-trace/src/scope.js +7 -5
- package/packages/dd-trace/src/serverless.js +5 -2
- package/packages/dd-trace/src/service-naming/extra-services.js +14 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +20 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/web.js +4 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +20 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/web.js +4 -0
- package/packages/dd-trace/src/span_stats.js +1 -1
- package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
- package/packages/dd-trace/src/telemetry/endpoints.js +1 -1
- package/packages/dd-trace/src/telemetry/telemetry.js +2 -2
- package/packages/dd-trace/src/lambda/runtime/ritm.js +0 -133
- package/vendor/dist/opentracing/LICENSE +0 -201
- package/vendor/dist/opentracing/binary_carrier.d.ts +0 -11
- package/vendor/dist/opentracing/constants.d.ts +0 -61
- package/vendor/dist/opentracing/examples/demo/demo.d.ts +0 -2
- package/vendor/dist/opentracing/ext/tags.d.ts +0 -90
- package/vendor/dist/opentracing/functions.d.ts +0 -20
- package/vendor/dist/opentracing/global_tracer.d.ts +0 -14
- package/vendor/dist/opentracing/index.d.ts +0 -12
- package/vendor/dist/opentracing/index.js +0 -1
- package/vendor/dist/opentracing/mock_tracer/index.d.ts +0 -5
- package/vendor/dist/opentracing/mock_tracer/mock_context.d.ts +0 -13
- package/vendor/dist/opentracing/mock_tracer/mock_report.d.ts +0 -16
- package/vendor/dist/opentracing/mock_tracer/mock_span.d.ts +0 -50
- package/vendor/dist/opentracing/mock_tracer/mock_tracer.d.ts +0 -26
- package/vendor/dist/opentracing/noop.d.ts +0 -8
- package/vendor/dist/opentracing/reference.d.ts +0 -33
- package/vendor/dist/opentracing/span.d.ts +0 -147
- package/vendor/dist/opentracing/span_context.d.ts +0 -26
- package/vendor/dist/opentracing/test/api_compatibility.d.ts +0 -16
- package/vendor/dist/opentracing/test/mocktracer_implemenation.d.ts +0 -3
- package/vendor/dist/opentracing/test/noop_implementation.d.ts +0 -4
- package/vendor/dist/opentracing/test/opentracing_api.d.ts +0 -3
- package/vendor/dist/opentracing/test/unittest.d.ts +0 -2
- package/vendor/dist/opentracing/tracer.d.ts +0 -127
|
@@ -91,6 +91,38 @@ class KafkajsProducerPlugin extends ProducerPlugin {
|
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
+
finish (ctx) {
|
|
95
|
+
const span = ctx?.currentStore?.span
|
|
96
|
+
const result = ctx?.result
|
|
97
|
+
if (span && Array.isArray(result) && result.length > 0) {
|
|
98
|
+
// The broker response is one entry per (topic, partition). Each entry
|
|
99
|
+
// carries a `baseOffset` — the offset assigned to the first record sent
|
|
100
|
+
// to that partition. We don't know per-partition record counts from the
|
|
101
|
+
// response, only the starting offset.
|
|
102
|
+
const offsets = []
|
|
103
|
+
for (const entry of result) {
|
|
104
|
+
const offsetAsLong = entry.offset ?? entry.baseOffset
|
|
105
|
+
if (entry.partition === undefined || offsetAsLong === undefined) continue
|
|
106
|
+
// Kafka offsets are 64-bit; coercing to Number loses precision past
|
|
107
|
+
// 2^53. Keep them as strings so the tag matches the exact offset on
|
|
108
|
+
// long-lived/high-throughput topics.
|
|
109
|
+
offsets.push({ partition: entry.partition, start_offset: String(offsetAsLong) })
|
|
110
|
+
}
|
|
111
|
+
if (offsets.length > 0) {
|
|
112
|
+
offsets.sort((a, b) => a.partition - b.partition)
|
|
113
|
+
span.setTag('kafka.messages.offsets', JSON.stringify(offsets))
|
|
114
|
+
}
|
|
115
|
+
// Single-message send: the one entry's partition/offset describes the
|
|
116
|
+
// exact record. Also expose them as flat tags for easy filtering.
|
|
117
|
+
if (offsets.length === 1 && ctx.messages?.length === 1) {
|
|
118
|
+
span.setTag('kafka.partition', offsets[0].partition)
|
|
119
|
+
// Set as a string meta tag (not a metric) to preserve full 64-bit precision.
|
|
120
|
+
span.setTag('kafka.message.offset', offsets[0].start_offset)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
super.finish(ctx)
|
|
124
|
+
}
|
|
125
|
+
|
|
94
126
|
bindStart (ctx) {
|
|
95
127
|
const { topic, messages, bootstrapServers, clusterId, disableHeaderInjection } = ctx
|
|
96
128
|
const span = this.startSpan({
|
|
@@ -20,6 +20,9 @@ class MongodbCorePlugin extends DatabasePlugin {
|
|
|
20
20
|
|
|
21
21
|
this.config.heartbeatEnabled = config.heartbeatEnabled ??
|
|
22
22
|
this._tracerConfig.DD_TRACE_MONGODB_HEARTBEAT_ENABLED
|
|
23
|
+
this.config.obfuscateQuery = normaliseObfuscateQuery(
|
|
24
|
+
config.obfuscateQuery ?? this._tracerConfig.DD_TRACE_MONGODB_OBFUSCATE_QUERY
|
|
25
|
+
)
|
|
23
26
|
}
|
|
24
27
|
|
|
25
28
|
bindStart (ctx) {
|
|
@@ -28,7 +31,7 @@ class MongodbCorePlugin extends DatabasePlugin {
|
|
|
28
31
|
if (!this.config.heartbeatEnabled && isHeartbeat(ops, this.config)) {
|
|
29
32
|
return
|
|
30
33
|
}
|
|
31
|
-
const query = getQuery(ops)
|
|
34
|
+
const query = getQuery(ops, this.config.obfuscateQuery)
|
|
32
35
|
const resource = truncate(getResource(this, ns, query, name))
|
|
33
36
|
const serviceResult = this.serviceName({ pluginConfig: this.config })
|
|
34
37
|
const span = this.startSpan(this.operationName(), {
|
|
@@ -90,9 +93,8 @@ class MongodbCorePlugin extends DatabasePlugin {
|
|
|
90
93
|
}
|
|
91
94
|
}
|
|
92
95
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
+
const MAX_DEPTH = 10
|
|
97
|
+
const MAX_QUERY_LENGTH = 10_000
|
|
96
98
|
|
|
97
99
|
function extractQuery (statements) {
|
|
98
100
|
if (statements.length === 1 && statements[0].q) return statements[0].q
|
|
@@ -100,22 +102,26 @@ function extractQuery (statements) {
|
|
|
100
102
|
const extractedQueries = []
|
|
101
103
|
for (let i = 0; i < statements.length; i++) {
|
|
102
104
|
if (statements[i].q) {
|
|
103
|
-
extractedQueries.push(
|
|
105
|
+
extractedQueries.push(statements[i].q)
|
|
104
106
|
}
|
|
105
107
|
}
|
|
106
108
|
|
|
107
109
|
return extractedQueries
|
|
108
110
|
}
|
|
109
111
|
|
|
110
|
-
|
|
112
|
+
/**
|
|
113
|
+
* @param {Record<string, unknown> | unknown[] | undefined} cmd
|
|
114
|
+
* @param {'none' | 'types' | 'redact'} mode
|
|
115
|
+
*/
|
|
116
|
+
function getQuery (cmd, mode) {
|
|
111
117
|
if (!cmd || (typeof cmd !== 'object' && !Array.isArray(cmd))) return
|
|
112
118
|
|
|
113
|
-
if (Array.isArray(cmd)) return
|
|
114
|
-
if (cmd.query) return
|
|
115
|
-
if (cmd.filter) return
|
|
116
|
-
if (cmd.pipeline) return
|
|
117
|
-
if (cmd.deletes) return
|
|
118
|
-
if (cmd.updates) return
|
|
119
|
+
if (Array.isArray(cmd)) return sanitiseAndStringify(extractQuery(cmd), mode)
|
|
120
|
+
if (cmd.query) return sanitiseAndStringify(cmd.query, mode)
|
|
121
|
+
if (cmd.filter) return sanitiseAndStringify(cmd.filter, mode)
|
|
122
|
+
if (cmd.pipeline) return sanitiseAndStringify(cmd.pipeline, mode)
|
|
123
|
+
if (cmd.deletes) return sanitiseAndStringify(extractQuery(cmd.deletes), mode)
|
|
124
|
+
if (cmd.updates) return sanitiseAndStringify(extractQuery(cmd.updates), mode)
|
|
119
125
|
}
|
|
120
126
|
|
|
121
127
|
function getResource (plugin, ns, query, operationName) {
|
|
@@ -129,73 +135,68 @@ function getResource (plugin, ns, query, operationName) {
|
|
|
129
135
|
}
|
|
130
136
|
|
|
131
137
|
function truncate (input) {
|
|
132
|
-
return input.slice(0,
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function shouldSimplify (input) {
|
|
136
|
-
return !isObject(input) || typeof input.toJSON === 'function'
|
|
138
|
+
return input.length > MAX_QUERY_LENGTH ? input.slice(0, MAX_QUERY_LENGTH) : input
|
|
137
139
|
}
|
|
138
140
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
for (const key of Object.keys(input)) {
|
|
164
|
-
let child = input[key]
|
|
165
|
-
if (typeof child === 'function') continue
|
|
166
|
-
|
|
167
|
-
if (isBSON(child)) {
|
|
168
|
-
child = typeof child.toJSON === 'function' ? child.toJSON() : '?'
|
|
169
|
-
}
|
|
141
|
+
// Single-pass sanitisation. The replacer:
|
|
142
|
+
// - skips functions and coerces bigint to its decimal string,
|
|
143
|
+
// - collapses Buffer / BSON Binary / BSON types without toJSON (MinKey, MaxKey) to a sentinel,
|
|
144
|
+
// - lets JSON.stringify call toJSON on other BSON types (ObjectId, Long, Decimal128, Date, Timestamp, ...)
|
|
145
|
+
// so the result lands here as a primitive or plain object,
|
|
146
|
+
// - tracks depth via an ancestor stack so cycles and depth >= MAX_DEPTH collapse to the sentinel,
|
|
147
|
+
// - in `redact` mode, replaces every primitive leaf (including null) with '?',
|
|
148
|
+
// - in `types` mode, replaces every primitive leaf with the typeof of the *original* value (so a
|
|
149
|
+
// BSON Date that flattens to a string still reports as 'object'), and 'null' for null.
|
|
150
|
+
// Keys, operator names, and array / pipeline shape are preserved in both modes so the resulting
|
|
151
|
+
// JSON is still a usable query signature.
|
|
152
|
+
/**
|
|
153
|
+
* @param {Record<string, unknown> | unknown[]} input
|
|
154
|
+
* @param {'none' | 'types' | 'redact'} mode
|
|
155
|
+
*/
|
|
156
|
+
function sanitiseAndStringify (input, mode) {
|
|
157
|
+
const ancestors = []
|
|
158
|
+
return JSON.stringify(input, function (key, value) {
|
|
159
|
+
if (typeof value === 'function') return
|
|
160
|
+
if (typeof value === 'bigint') {
|
|
161
|
+
if (mode === 'redact') return '?'
|
|
162
|
+
if (mode === 'types') return 'bigint'
|
|
163
|
+
return value.toString()
|
|
164
|
+
}
|
|
170
165
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
output[key] = {}
|
|
177
|
-
queue.push({
|
|
178
|
-
input: child,
|
|
179
|
-
output: output[key],
|
|
180
|
-
depth: nextDepth,
|
|
181
|
-
})
|
|
166
|
+
const original = key === '' ? value : this[key]
|
|
167
|
+
if (typeof original === 'object' && original !== null) {
|
|
168
|
+
const bsontype = original._bsontype
|
|
169
|
+
if (Buffer.isBuffer(original) || (bsontype !== undefined && (bsontype === 'Binary' || value === original))) {
|
|
170
|
+
return mode === 'types' ? 'object' : '?'
|
|
182
171
|
}
|
|
183
172
|
}
|
|
184
|
-
}
|
|
185
173
|
|
|
186
|
-
|
|
187
|
-
|
|
174
|
+
if (value === null || typeof value !== 'object') {
|
|
175
|
+
if (key === '' || mode === 'none') return value
|
|
176
|
+
if (mode === 'redact') return '?'
|
|
177
|
+
return original === null ? 'null' : typeof original
|
|
178
|
+
}
|
|
188
179
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
180
|
+
while (ancestors.length > 0 && ancestors.at(-1) !== this) ancestors.pop()
|
|
181
|
+
if (ancestors.length >= MAX_DEPTH || ancestors.includes(value)) {
|
|
182
|
+
return mode === 'types' ? 'object' : '?'
|
|
183
|
+
}
|
|
184
|
+
ancestors.push(value)
|
|
192
185
|
|
|
193
|
-
|
|
194
|
-
|
|
186
|
+
return value
|
|
187
|
+
})
|
|
195
188
|
}
|
|
196
189
|
|
|
197
|
-
|
|
198
|
-
|
|
190
|
+
/**
|
|
191
|
+
* Coerce the plugin-config and env values for `obfuscateQuery` to one of the three canonical modes.
|
|
192
|
+
* Anything outside the enum — including `undefined` — falls back to `'none'`.
|
|
193
|
+
*
|
|
194
|
+
* @param {unknown} value
|
|
195
|
+
* @returns {'none' | 'types' | 'redact'}
|
|
196
|
+
*/
|
|
197
|
+
function normaliseObfuscateQuery (value) {
|
|
198
|
+
if (value === 'types' || value === 'redact') return value
|
|
199
|
+
return 'none'
|
|
199
200
|
}
|
|
200
201
|
|
|
201
202
|
function isHeartbeat (ops, config) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { storage } = require('../../datadog-core')
|
|
4
|
-
const CLIENT_PORT_KEY = require('../../dd-trace/src/constants')
|
|
4
|
+
const { CLIENT_PORT_KEY } = require('../../dd-trace/src/constants')
|
|
5
5
|
const DatabasePlugin = require('../../dd-trace/src/plugins/database')
|
|
6
6
|
|
|
7
7
|
class MySQLPlugin extends DatabasePlugin {
|
|
@@ -9,9 +9,9 @@ class PGPlugin extends DatabasePlugin {
|
|
|
9
9
|
static system = 'postgres'
|
|
10
10
|
|
|
11
11
|
bindStart (ctx) {
|
|
12
|
-
const { params = {}, query, processId, stream } = ctx
|
|
12
|
+
const { params = {}, query, originalText, processId, stream } = ctx
|
|
13
13
|
const service = this.serviceName({ pluginConfig: this.config, params })
|
|
14
|
-
const originalStatement = this.maybeTruncate(
|
|
14
|
+
const originalStatement = this.maybeTruncate(originalText)
|
|
15
15
|
|
|
16
16
|
const span = this.startSpan(this.operationName(), {
|
|
17
17
|
service,
|
|
@@ -32,7 +32,7 @@ class PGPlugin extends DatabasePlugin {
|
|
|
32
32
|
span.setTag('db.stream', 1)
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
ctx.injected = this.injectDbmQuery(span, originalText, service.name, !!query.name)
|
|
36
36
|
|
|
37
37
|
return ctx.currentStore
|
|
38
38
|
}
|
|
@@ -321,6 +321,7 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
321
321
|
isAtrRetry,
|
|
322
322
|
isModified,
|
|
323
323
|
finalStatus,
|
|
324
|
+
earlyFlakeAbortReason,
|
|
324
325
|
onDone,
|
|
325
326
|
}) => {
|
|
326
327
|
if (!span) return
|
|
@@ -384,6 +385,9 @@ class PlaywrightPlugin extends CiPlugin {
|
|
|
384
385
|
if (finalStatus) {
|
|
385
386
|
span.setTag(TEST_FINAL_STATUS, finalStatus)
|
|
386
387
|
}
|
|
388
|
+
if (earlyFlakeAbortReason) {
|
|
389
|
+
span.setTag(TEST_EARLY_FLAKE_ABORT_REASON, earlyFlakeAbortReason)
|
|
390
|
+
}
|
|
387
391
|
for (const step of steps) {
|
|
388
392
|
const stepStartTime = step.startTime.getTime()
|
|
389
393
|
const stepSpan = this.tracer.startSpan('playwright.step', {
|
|
@@ -4,17 +4,31 @@ const { CLIENT_PORT_KEY } = require('../../dd-trace/src/constants')
|
|
|
4
4
|
const CachePlugin = require('../../dd-trace/src/plugins/cache')
|
|
5
5
|
const urlFilter = require('../../dd-trace/src/plugins/util/urlfilter')
|
|
6
6
|
|
|
7
|
+
const MAX_ARG_LENGTH = 100
|
|
8
|
+
const MAX_COMMAND_LENGTH = 1000
|
|
9
|
+
|
|
7
10
|
class RedisPlugin extends CachePlugin {
|
|
8
11
|
static id = 'redis'
|
|
9
12
|
static system = 'redis'
|
|
10
13
|
|
|
14
|
+
/** @type {string} */
|
|
15
|
+
#rawCommandKey
|
|
16
|
+
/** @type {Map<string | undefined, { name: string, source: string | undefined }>} */
|
|
17
|
+
#serviceByConnection = new Map()
|
|
18
|
+
// `nomenclature.config` identity at last cache fill. `withNamingSchema` swaps it without
|
|
19
|
+
// running this plugin's `configure`, so identity drives invalidation in `bindStart`.
|
|
20
|
+
/** @type {object | undefined} */
|
|
21
|
+
#lastNomenclatureConfig
|
|
22
|
+
/** @type {string | undefined} */
|
|
23
|
+
#cachedOperationName
|
|
24
|
+
|
|
11
25
|
constructor (...args) {
|
|
12
26
|
super(...args)
|
|
13
27
|
this._spanType = 'redis'
|
|
14
28
|
}
|
|
15
29
|
|
|
16
30
|
bindStart (ctx) {
|
|
17
|
-
const { db, command, args, connectionOptions, connectionName } = ctx
|
|
31
|
+
const { db, command, args, argsStartIndex, connectionOptions, connectionName } = ctx
|
|
18
32
|
|
|
19
33
|
const resource = command
|
|
20
34
|
const normalizedCommand = command.toUpperCase()
|
|
@@ -22,14 +36,27 @@ class RedisPlugin extends CachePlugin {
|
|
|
22
36
|
return { noop: true }
|
|
23
37
|
}
|
|
24
38
|
|
|
39
|
+
const nomConfig = this._tracer._nomenclature.config
|
|
40
|
+
if (this.#lastNomenclatureConfig !== nomConfig) {
|
|
41
|
+
this.#lastNomenclatureConfig = nomConfig
|
|
42
|
+
this.#cachedOperationName = undefined
|
|
43
|
+
this.#serviceByConnection.clear()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let service = this.#serviceByConnection.get(connectionName)
|
|
47
|
+
if (service === undefined) {
|
|
48
|
+
service = this.serviceName({ pluginConfig: this.config, system: this.system, connectionName })
|
|
49
|
+
this.#serviceByConnection.set(connectionName, service)
|
|
50
|
+
}
|
|
51
|
+
|
|
25
52
|
this.startSpan({
|
|
26
53
|
resource,
|
|
27
|
-
service
|
|
54
|
+
service,
|
|
28
55
|
type: this._spanType,
|
|
29
56
|
meta: {
|
|
30
57
|
'db.type': this._spanType,
|
|
31
58
|
'db.name': db || '0',
|
|
32
|
-
[
|
|
59
|
+
[this.#rawCommandKey]: formatCommand(normalizedCommand, args, argsStartIndex),
|
|
33
60
|
'out.host': connectionOptions.host,
|
|
34
61
|
[CLIENT_PORT_KEY]: connectionOptions.port,
|
|
35
62
|
},
|
|
@@ -38,41 +65,44 @@ class RedisPlugin extends CachePlugin {
|
|
|
38
65
|
return ctx.currentStore
|
|
39
66
|
}
|
|
40
67
|
|
|
68
|
+
operationName () {
|
|
69
|
+
this.#cachedOperationName ??= super.operationName()
|
|
70
|
+
return this.#cachedOperationName
|
|
71
|
+
}
|
|
72
|
+
|
|
41
73
|
configure (config) {
|
|
42
74
|
super.configure(normalizeConfig(config))
|
|
75
|
+
// Subclasses (iovalkey) overwrite `_spanType` in their constructor, before any `configure`,
|
|
76
|
+
// so reading it here is stable.
|
|
77
|
+
this.#rawCommandKey = `${this._spanType}.raw_command`
|
|
78
|
+
this.#lastNomenclatureConfig = undefined
|
|
79
|
+
this.#cachedOperationName = undefined
|
|
80
|
+
this.#serviceByConnection.clear()
|
|
43
81
|
}
|
|
44
82
|
}
|
|
45
83
|
|
|
46
|
-
function formatCommand (command, args) {
|
|
84
|
+
function formatCommand (command, args, argsStartIndex = 0) {
|
|
47
85
|
if (!args || command === 'AUTH') return command
|
|
48
86
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
87
|
+
let result = command
|
|
88
|
+
for (let i = argsStartIndex, l = args.length; i < l; i++) {
|
|
89
|
+
const arg = args[i]
|
|
90
|
+
if (typeof arg === 'function') continue
|
|
53
91
|
|
|
54
|
-
|
|
92
|
+
result = `${result} ${formatArg(arg)}`
|
|
93
|
+
if (result.length > MAX_COMMAND_LENGTH) return result.slice(0, MAX_COMMAND_LENGTH - 3) + '...'
|
|
55
94
|
}
|
|
56
95
|
|
|
57
|
-
return
|
|
96
|
+
return result
|
|
58
97
|
}
|
|
59
98
|
|
|
60
99
|
function formatArg (arg) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
case 'number':
|
|
64
|
-
return trim(String(arg), 100)
|
|
65
|
-
default:
|
|
66
|
-
return '?'
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function trim (str, maxlen) {
|
|
71
|
-
if (str.length > maxlen) {
|
|
72
|
-
str = str.slice(0, maxlen - 3) + '...'
|
|
100
|
+
if (typeof arg === 'string') {
|
|
101
|
+
return arg.length > MAX_ARG_LENGTH ? arg.slice(0, MAX_ARG_LENGTH - 3) + '...' : arg
|
|
73
102
|
}
|
|
74
|
-
|
|
75
|
-
return
|
|
103
|
+
// Number stringification is bounded (~23 chars max), so it never hits MAX_ARG_LENGTH.
|
|
104
|
+
if (typeof arg === 'number') return String(arg)
|
|
105
|
+
return '?'
|
|
76
106
|
}
|
|
77
107
|
|
|
78
108
|
function normalizeConfig (config) {
|
|
@@ -27,6 +27,7 @@ class UndiciPlugin extends HttpClientPlugin {
|
|
|
27
27
|
// Subscribe to native undici diagnostic channels for undici >= 4.7.0
|
|
28
28
|
// These channels fire for ALL undici requests (fetch, request, stream, etc.)
|
|
29
29
|
this.addSub('undici:request:create', this.#onNativeRequestCreate.bind(this))
|
|
30
|
+
this.addSub('undici:request:bodySent', this.#onNativeRequestBodySent.bind(this))
|
|
30
31
|
this.addSub('undici:request:headers', this.#onNativeRequestHeaders.bind(this))
|
|
31
32
|
this.addSub('undici:request:trailers', this.#onNativeRequestTrailers.bind(this))
|
|
32
33
|
this.addSub('undici:request:error', this.#onNativeRequestError.bind(this))
|
|
@@ -109,12 +110,30 @@ class UndiciPlugin extends HttpClientPlugin {
|
|
|
109
110
|
span,
|
|
110
111
|
store,
|
|
111
112
|
uri,
|
|
113
|
+
method,
|
|
112
114
|
})
|
|
113
115
|
|
|
114
116
|
// Enter the span context
|
|
115
117
|
storage('legacy').enterWith({ ...store, span })
|
|
116
118
|
}
|
|
117
119
|
|
|
120
|
+
#onNativeRequestBodySent ({ request }) {
|
|
121
|
+
const ctx = requestContexts.get(request)
|
|
122
|
+
if (!ctx || ctx.method !== 'CONNECT') return
|
|
123
|
+
|
|
124
|
+
const { span, store } = ctx
|
|
125
|
+
|
|
126
|
+
this.config.hooks.request(span, null, null)
|
|
127
|
+
|
|
128
|
+
span.finish()
|
|
129
|
+
|
|
130
|
+
requestContexts.delete(request)
|
|
131
|
+
|
|
132
|
+
if (store) {
|
|
133
|
+
storage('legacy').enterWith(store)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
118
137
|
#onNativeRequestHeaders ({ request, response }) {
|
|
119
138
|
const ctx = requestContexts.get(request)
|
|
120
139
|
if (!ctx) return
|
|
@@ -56,6 +56,16 @@ class VitestPlugin extends CiPlugin {
|
|
|
56
56
|
|
|
57
57
|
this.taskToFinishTime = new WeakMap()
|
|
58
58
|
|
|
59
|
+
this.addSub('ci:vitest:session:configuration', ({ onDone }) => {
|
|
60
|
+
const testSessionSpanContext = this.testSessionSpan?.context()
|
|
61
|
+
const testModuleSpanContext = this.testModuleSpan?.context()
|
|
62
|
+
onDone({
|
|
63
|
+
testSessionId: testSessionSpanContext?.toTraceId(),
|
|
64
|
+
testModuleId: testModuleSpanContext?.toSpanId(),
|
|
65
|
+
testCommand: this.command,
|
|
66
|
+
})
|
|
67
|
+
})
|
|
68
|
+
|
|
59
69
|
this.addSub('ci:vitest:test:is-new', ({ knownTests, testSuiteAbsolutePath, testName, onDone }) => {
|
|
60
70
|
// if for whatever reason the worker does not receive valid known tests, we don't consider it as new
|
|
61
71
|
if (!knownTests.vitest) {
|
|
@@ -299,14 +309,16 @@ class VitestPlugin extends CiPlugin {
|
|
|
299
309
|
this.addBind('ci:vitest:test-suite:start', (ctx) => {
|
|
300
310
|
const { testSuiteAbsolutePath, frameworkVersion } = ctx
|
|
301
311
|
|
|
302
|
-
|
|
303
|
-
|
|
312
|
+
const testCommand = ctx.testCommand || 'vitest run'
|
|
313
|
+
const { testSessionId, testModuleId } = ctx
|
|
314
|
+
this.command = testCommand
|
|
304
315
|
this.frameworkVersion = frameworkVersion
|
|
305
|
-
const testSessionSpanContext =
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
316
|
+
const testSessionSpanContext = testSessionId && testModuleId
|
|
317
|
+
? this.tracer.extract('text_map', {
|
|
318
|
+
'x-datadog-trace-id': testSessionId,
|
|
319
|
+
'x-datadog-parent-id': testModuleId,
|
|
320
|
+
})
|
|
321
|
+
: undefined
|
|
310
322
|
|
|
311
323
|
const trimmedCommand = DD_MAJOR < 6 ? this.command : 'vitest run'
|
|
312
324
|
// test suites run in a different process, so they also need to init the metadata dictionary
|
|
@@ -82,6 +82,40 @@ function wrapFunction (original, wrapper) {
|
|
|
82
82
|
return wrapped
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Lean variant of `wrapFunction` for the case where the wrapped value is a
|
|
87
|
+
* user-supplied callback that the user cannot reasonably introspect beyond
|
|
88
|
+
* `name` and `length`, and the wrapper closure is fully controlled by us.
|
|
89
|
+
*
|
|
90
|
+
* Compared to `wrapFunction`, this skips the prototype copy, the
|
|
91
|
+
* `assertNotClass` guard, and the `Reflect.ownKeys` descriptor-copy loop.
|
|
92
|
+
* Only `name` and `length` are preserved, and only when the wrapper's
|
|
93
|
+
* autogenerated values differ -- a wrapper whose closure already has the
|
|
94
|
+
* right arity / name pays no overhead.
|
|
95
|
+
*
|
|
96
|
+
* Use `wrapFunction` instead when any of the following is true: the wrapped
|
|
97
|
+
* function needs to keep its prototype, has custom own properties the caller
|
|
98
|
+
* may read, or is `new`-ed.
|
|
99
|
+
*
|
|
100
|
+
* @param {Function} original - User-supplied callback being wrapped.
|
|
101
|
+
* @param {(original: Function) => Function} wrapper - Factory that receives
|
|
102
|
+
* `original` and returns the wrapper closure.
|
|
103
|
+
* @returns {Function} The wrapper closure with `name` and `length` preserved.
|
|
104
|
+
*/
|
|
105
|
+
function wrapCallback (original, wrapper) {
|
|
106
|
+
if (typeof original !== 'function') {
|
|
107
|
+
return original
|
|
108
|
+
}
|
|
109
|
+
const wrapped = wrapper(original)
|
|
110
|
+
if (wrapped.name !== original.name) {
|
|
111
|
+
Object.defineProperty(wrapped, 'name', { value: original.name, configurable: true })
|
|
112
|
+
}
|
|
113
|
+
if (wrapped.length !== original.length) {
|
|
114
|
+
Object.defineProperty(wrapped, 'length', { value: original.length, configurable: true })
|
|
115
|
+
}
|
|
116
|
+
return wrapped
|
|
117
|
+
}
|
|
118
|
+
|
|
85
119
|
/**
|
|
86
120
|
* Wraps a method of an object with a wrapper function.
|
|
87
121
|
*
|
|
@@ -280,6 +314,7 @@ function assertNotClass (target) {
|
|
|
280
314
|
|
|
281
315
|
module.exports = {
|
|
282
316
|
wrap,
|
|
317
|
+
wrapCallback,
|
|
283
318
|
wrapFunction,
|
|
284
319
|
massWrap,
|
|
285
320
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const log = require('../log')
|
|
4
4
|
const { incomingHttpRequestStart, aiguardChannel } = require('./channels')
|
|
5
5
|
const AIGuard = require('./sdk')
|
|
6
|
+
const { SOURCE_AUTO, INTEGRATION_NONE } = require('./tags')
|
|
6
7
|
|
|
7
8
|
let isEnabled = false
|
|
8
9
|
let aiguard
|
|
@@ -51,7 +52,8 @@ function onEvaluate (ctx) {
|
|
|
51
52
|
return
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
|
|
55
|
+
const opts = { block, source: SOURCE_AUTO, integration: ctx.integration || INTEGRATION_NONE }
|
|
56
|
+
aiguard.evaluate(ctx.messages, opts)
|
|
55
57
|
.then(() => {
|
|
56
58
|
ctx.resolve()
|
|
57
59
|
})
|