dd-trace 5.103.0 → 5.105.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 +107 -6
- package/package.json +18 -17
- 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 +15 -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/cassandra-driver.js +5 -2
- package/packages/datadog-instrumentations/src/cucumber.js +181 -35
- package/packages/datadog-instrumentations/src/dns.js +54 -18
- package/packages/datadog-instrumentations/src/elasticsearch.js +4 -4
- package/packages/datadog-instrumentations/src/fastify.js +142 -82
- package/packages/datadog-instrumentations/src/graphql.js +188 -67
- package/packages/datadog-instrumentations/src/grpc/client.js +48 -32
- package/packages/datadog-instrumentations/src/helpers/ai-messages.js +322 -14
- package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +1 -1
- 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/kafka.js +17 -0
- 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/compiler.js +3 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +19 -6
- 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 +31 -229
- package/packages/datadog-instrumentations/src/hono.js +54 -3
- package/packages/datadog-instrumentations/src/http/client.js +2 -2
- package/packages/datadog-instrumentations/src/http/server.js +9 -4
- package/packages/datadog-instrumentations/src/ioredis.js +3 -3
- package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +163 -0
- package/packages/datadog-instrumentations/src/jest.js +390 -183
- package/packages/datadog-instrumentations/src/kafkajs.js +140 -17
- 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 +399 -107
- package/packages/datadog-instrumentations/src/mocha/utils.js +48 -8
- package/packages/datadog-instrumentations/src/mongodb-core.js +1 -1
- 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/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/pg.js +1 -1
- package/packages/datadog-instrumentations/src/pino.js +17 -5
- package/packages/datadog-instrumentations/src/playwright.js +537 -297
- package/packages/datadog-instrumentations/src/router.js +80 -34
- package/packages/datadog-instrumentations/src/stripe.js +1 -1
- package/packages/datadog-instrumentations/src/vitest.js +246 -149
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +1 -1
- 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 +223 -45
- package/packages/datadog-plugin-cypress/src/support.js +69 -1
- package/packages/datadog-plugin-dns/src/lookup.js +8 -6
- package/packages/datadog-plugin-elasticsearch/src/index.js +28 -8
- 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-graphql/src/utils.js +4 -1
- 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 +35 -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 +311 -35
- 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-redis/src/index.js +37 -2
- 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-undici/src/index.js +19 -0
- package/packages/datadog-plugin-vitest/src/index.js +24 -20
- package/packages/datadog-plugin-winston/src/index.js +30 -0
- package/packages/datadog-shimmer/src/shimmer.js +49 -21
- 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/blocking.js +2 -2
- package/packages/dd-trace/src/appsec/index.js +11 -4
- package/packages/dd-trace/src/appsec/reporter.js +24 -11
- 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/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/ci-visibility/test-optimization-cache.js +70 -6
- package/packages/dd-trace/src/config/generated-config-types.d.ts +7 -2
- package/packages/dd-trace/src/config/supported-configurations.json +36 -8
- package/packages/dd-trace/src/crashtracking/crashtracker.js +15 -3
- package/packages/dd-trace/src/datastreams/context.js +4 -2
- 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 +57 -42
- 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/exporters/common/agents.js +3 -1
- package/packages/dd-trace/src/exporters/common/request.js +3 -1
- package/packages/dd-trace/src/id.js +17 -4
- package/packages/dd-trace/src/lambda/handler.js +2 -4
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -1
- 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 +10 -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/log/writer.js +3 -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/noop/span.js +3 -1
- 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/openfeature/writers/exposures.js +51 -20
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -1
- 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/opentracing/propagation/log.js +18 -7
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +62 -67
- package/packages/dd-trace/src/opentracing/span.js +59 -19
- package/packages/dd-trace/src/opentracing/span_context.js +49 -0
- package/packages/dd-trace/src/plugins/apollo.js +3 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +23 -33
- 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 -46
- 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 +48 -8
- package/packages/dd-trace/src/plugins/util/git.js +3 -1
- package/packages/dd-trace/src/plugins/util/test.js +318 -13
- package/packages/dd-trace/src/plugins/util/web.js +89 -64
- package/packages/dd-trace/src/priority_sampler.js +2 -2
- package/packages/dd-trace/src/profiling/profiler.js +2 -2
- package/packages/dd-trace/src/profiling/profilers/wall.js +10 -4
- package/packages/dd-trace/src/sampling_rule.js +7 -7
- package/packages/dd-trace/src/scope.js +7 -5
- package/packages/dd-trace/src/service-naming/extra-services.js +14 -0
- 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
- 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
|
@@ -24,6 +24,40 @@ const suppressedTestFailures = new Map()
|
|
|
24
24
|
// to a cross-origin URL, safeGetRum() handles the access error.
|
|
25
25
|
let originalWindow
|
|
26
26
|
|
|
27
|
+
let currentTestCommands = []
|
|
28
|
+
const commandStartTimes = new Map()
|
|
29
|
+
const INTERNAL_CYPRESS_COMMANDS = new Set(['wrap', 'then', 'noop'])
|
|
30
|
+
|
|
31
|
+
Cypress.on('command:start', (command) => {
|
|
32
|
+
commandStartTimes.set(command.get('id'), { startTime: Date.now(), name: command.get('name') })
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
Cypress.on('command:end', (command) => {
|
|
36
|
+
const id = command.get('id')
|
|
37
|
+
const entry = commandStartTimes.get(id)
|
|
38
|
+
commandStartTimes.delete(id)
|
|
39
|
+
|
|
40
|
+
const name = command.get('name')
|
|
41
|
+
const args = command.get('args')
|
|
42
|
+
if (name === 'task' && args && typeof args[0] === 'string' && args[0].startsWith('dd:')) {
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
if (INTERNAL_CYPRESS_COMMANDS.has(name)) {
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
if (entry == null) {
|
|
49
|
+
return
|
|
50
|
+
}
|
|
51
|
+
const err = command.get('err')
|
|
52
|
+
currentTestCommands.push({
|
|
53
|
+
name,
|
|
54
|
+
startTime: entry.startTime,
|
|
55
|
+
endTime: Date.now(),
|
|
56
|
+
// Serialize the error to a plain object so it survives cy.task JSON transport.
|
|
57
|
+
error: err ? { message: err.message, stack: err.stack, name: err.name } : null,
|
|
58
|
+
})
|
|
59
|
+
})
|
|
60
|
+
|
|
27
61
|
// If the test is using multi domain with cy.origin, trying to access
|
|
28
62
|
// window properties will result in a cross origin error.
|
|
29
63
|
function safeGetRum (window) {
|
|
@@ -56,6 +90,29 @@ function getTestProperties (testName) {
|
|
|
56
90
|
// By not re-throwing the error, Cypress marks the test as passed
|
|
57
91
|
// This allows quarantined tests to run but not affect the exit code
|
|
58
92
|
Cypress.on('fail', (err, runnable) => {
|
|
93
|
+
// For commands that time out, command:end may never fire.
|
|
94
|
+
// Finalize any in-flight commands so their step spans carry the error.
|
|
95
|
+
const hadInFlightCommands = commandStartTimes.size > 0
|
|
96
|
+
for (const [, { startTime, name }] of commandStartTimes) {
|
|
97
|
+
if (INTERNAL_CYPRESS_COMMANDS.has(name)) continue
|
|
98
|
+
currentTestCommands.push({
|
|
99
|
+
name,
|
|
100
|
+
startTime,
|
|
101
|
+
endTime: Date.now(),
|
|
102
|
+
error: { message: err.message, stack: err.stack, name: err.name },
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
commandStartTimes.clear()
|
|
106
|
+
|
|
107
|
+
// If command:end fired for all commands (none in-flight) but the last command
|
|
108
|
+
// has no error, it means command:end fired before the error was attached to it.
|
|
109
|
+
if (!hadInFlightCommands && currentTestCommands.length > 0) {
|
|
110
|
+
const lastCommand = currentTestCommands[currentTestCommands.length - 1]
|
|
111
|
+
if (!lastCommand.error) {
|
|
112
|
+
lastCommand.error = { message: err.message, stack: err.stack, name: err.name }
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
59
116
|
if (!isTestManagementEnabled) {
|
|
60
117
|
throw err
|
|
61
118
|
}
|
|
@@ -169,6 +226,9 @@ beforeEach(function () {
|
|
|
169
226
|
retryReasonsByTestName.delete(testName)
|
|
170
227
|
}
|
|
171
228
|
|
|
229
|
+
currentTestCommands = []
|
|
230
|
+
commandStartTimes.clear()
|
|
231
|
+
|
|
172
232
|
cy.on('window:load', (win) => {
|
|
173
233
|
originalWindow = win
|
|
174
234
|
})
|
|
@@ -212,6 +272,11 @@ beforeEach(function () {
|
|
|
212
272
|
if (shouldSkip) {
|
|
213
273
|
this.skip()
|
|
214
274
|
}
|
|
275
|
+
}).then(() => {
|
|
276
|
+
// Clear any commands accumulated during DD-owned setup (e.g. setCookie, RUM restart)
|
|
277
|
+
// so they are not reported as user test steps.
|
|
278
|
+
currentTestCommands = []
|
|
279
|
+
commandStartTimes.clear()
|
|
215
280
|
})
|
|
216
281
|
})
|
|
217
282
|
|
|
@@ -289,6 +354,9 @@ afterEach(function () {
|
|
|
289
354
|
testInfo.testSourceStack = invocationDetails.stack
|
|
290
355
|
} catch {}
|
|
291
356
|
|
|
357
|
+
// Snapshot before any DD-owned Cypress commands so they are not reported as test steps.
|
|
358
|
+
const commandsToReport = [...currentTestCommands]
|
|
359
|
+
|
|
292
360
|
const rum = safeGetRum(originalWindow)
|
|
293
361
|
if (rum) {
|
|
294
362
|
testInfo.isRUMActive = true
|
|
@@ -310,5 +378,5 @@ afterEach(function () {
|
|
|
310
378
|
suppressedTestFailures.delete(testName)
|
|
311
379
|
}
|
|
312
380
|
|
|
313
|
-
cy.task('dd:afterEach', { test: testInfo, coverage })
|
|
381
|
+
cy.task('dd:afterEach', { test: testInfo, coverage, commands: commandsToReport })
|
|
314
382
|
})
|
|
@@ -23,22 +23,24 @@ class DNSLookupPlugin extends ClientPlugin {
|
|
|
23
23
|
return ctx.currentStore
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
finish (ctx) {
|
|
27
27
|
const span = ctx.currentStore.span
|
|
28
28
|
const result = ctx.result
|
|
29
29
|
|
|
30
30
|
if (Array.isArray(result)) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
: [result]
|
|
34
|
-
|
|
31
|
+
// `lookup(..., { all: true })` or `dns.promises.lookup(..., { all: true })`.
|
|
32
|
+
const addresses = result.map(entry => entry.address).sort()
|
|
35
33
|
span.setTag('dns.address', addresses[0])
|
|
36
34
|
span.setTag('dns.addresses', addresses.join(','))
|
|
35
|
+
} else if (result && typeof result === 'object') {
|
|
36
|
+
// `dns.promises.lookup(...)` resolves to `{ address, family }`; the callback variant
|
|
37
|
+
// passes the address as a string.
|
|
38
|
+
span.setTag('dns.address', result.address)
|
|
37
39
|
} else {
|
|
38
40
|
span.setTag('dns.address', result)
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
|
|
43
|
+
super.finish(ctx)
|
|
42
44
|
}
|
|
43
45
|
}
|
|
44
46
|
|
|
@@ -5,23 +5,43 @@ const DatabasePlugin = require('../../dd-trace/src/plugins/database')
|
|
|
5
5
|
class ElasticsearchPlugin extends DatabasePlugin {
|
|
6
6
|
static id = 'elasticsearch'
|
|
7
7
|
|
|
8
|
+
#urlTag
|
|
9
|
+
#methodTag
|
|
10
|
+
#bodyTag
|
|
11
|
+
#paramsTag
|
|
12
|
+
|
|
13
|
+
constructor (...args) {
|
|
14
|
+
super(...args)
|
|
15
|
+
|
|
16
|
+
// Per-instance because `system` differs on the OpenSearchPlugin subclass.
|
|
17
|
+
const { system } = this
|
|
18
|
+
this.#urlTag = `${system}.url`
|
|
19
|
+
this.#methodTag = `${system}.method`
|
|
20
|
+
this.#bodyTag = `${system}.body`
|
|
21
|
+
this.#paramsTag = `${system}.params`
|
|
22
|
+
}
|
|
23
|
+
|
|
8
24
|
bindStart (ctx) {
|
|
9
25
|
const { params } = ctx
|
|
10
26
|
|
|
11
|
-
const
|
|
27
|
+
const meta = {
|
|
28
|
+
'db.type': this.system,
|
|
29
|
+
[this.#urlTag]: params.path,
|
|
30
|
+
[this.#methodTag]: params.method,
|
|
31
|
+
[this.#bodyTag]: getBody(params.body || params.bulkBody),
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const queryString = params.querystring || params.query
|
|
35
|
+
if (queryString) {
|
|
36
|
+
meta[this.#paramsTag] = JSON.stringify(queryString)
|
|
37
|
+
}
|
|
12
38
|
|
|
13
39
|
this.startSpan(this.operationName(), {
|
|
14
40
|
service: this.serviceName({ pluginConfig: this.config }),
|
|
15
41
|
resource: `${params.method} ${quantizePath(params.path)}`,
|
|
16
42
|
type: 'elasticsearch',
|
|
17
43
|
kind: 'client',
|
|
18
|
-
meta
|
|
19
|
-
'db.type': this.system,
|
|
20
|
-
[`${this.system}.url`]: params.path,
|
|
21
|
-
[`${this.system}.method`]: params.method,
|
|
22
|
-
[`${this.system}.body`]: body,
|
|
23
|
-
[`${this.system}.params`]: JSON.stringify(params.querystring || params.query),
|
|
24
|
-
},
|
|
44
|
+
meta,
|
|
25
45
|
}, ctx)
|
|
26
46
|
|
|
27
47
|
return ctx.currentStore
|
|
@@ -181,7 +181,7 @@ class GoogleCloudPubsubPushSubscriptionPlugin extends TracingPlugin {
|
|
|
181
181
|
|
|
182
182
|
if (linkContext) {
|
|
183
183
|
if (span.addLink) {
|
|
184
|
-
span.addLink(linkContext, {})
|
|
184
|
+
span.addLink({ context: linkContext, attributes: {} })
|
|
185
185
|
} else {
|
|
186
186
|
span._links ??= []
|
|
187
187
|
span._links.push({ context: linkContext, attributes: {} })
|
|
@@ -17,6 +17,8 @@ class GraphQLExecutePlugin extends TracingPlugin {
|
|
|
17
17
|
const document = args.document
|
|
18
18
|
const source = this.config.source && document && docSource
|
|
19
19
|
|
|
20
|
+
ctx.collapse = this.config.collapse
|
|
21
|
+
|
|
20
22
|
const span = this.startSpan(this.operationName(), {
|
|
21
23
|
service: this.config.service || this.serviceName(),
|
|
22
24
|
resource: getSignature(document, name, type, this.config.signature),
|
|
@@ -3,59 +3,61 @@
|
|
|
3
3
|
const dc = require('dc-polyfill')
|
|
4
4
|
const TracingPlugin = require('../../dd-trace/src/plugins/tracing')
|
|
5
5
|
|
|
6
|
-
const collapsedPathSym = Symbol('collapsedPaths')
|
|
7
|
-
|
|
8
6
|
class GraphQLResolvePlugin extends TracingPlugin {
|
|
9
7
|
static id = 'graphql'
|
|
10
8
|
static operation = 'resolve'
|
|
11
9
|
|
|
10
|
+
/**
|
|
11
|
+
* @param {{
|
|
12
|
+
* rootCtx: {
|
|
13
|
+
* source?: string,
|
|
14
|
+
* collapse: boolean,
|
|
15
|
+
* collapsedFields?: Map<string, { ctx: object }>,
|
|
16
|
+
* },
|
|
17
|
+
* args: Record<string, unknown>,
|
|
18
|
+
* path: { prev: object | undefined, key: string | number },
|
|
19
|
+
* pathString: string,
|
|
20
|
+
* fieldName: string,
|
|
21
|
+
* returnType: { name: string },
|
|
22
|
+
* fieldNode: { loc?: { start: number, end: number }, arguments?: object[], directives?: object[] } | undefined,
|
|
23
|
+
* variableValues: Record<string, unknown> | undefined,
|
|
24
|
+
* }} fieldCtx
|
|
25
|
+
*/
|
|
12
26
|
start (fieldCtx) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
// we need to get the parent span to the field if it exists for correct span parenting
|
|
16
|
-
// of nested fields
|
|
17
|
-
const parentField = getParentField(rootCtx, pathString)
|
|
18
|
-
const childOf = parentField?.ctx?.currentStore?.span
|
|
27
|
+
if (!shouldInstrument(this.config, fieldCtx.path)) return
|
|
19
28
|
|
|
20
|
-
|
|
29
|
+
const { rootCtx, args, path, pathString, fieldName, returnType, fieldNode, variableValues } = fieldCtx
|
|
21
30
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
31
|
+
// Siblings 2..N of a collapsed list share the first sibling's span, so
|
|
32
|
+
// skip span creation here. updateField still fires on the shared ctx and
|
|
33
|
+
// advances the shared span's finishTime.
|
|
34
|
+
if (rootCtx.collapse && rootCtx.collapsedFields?.has(pathString)) return
|
|
26
35
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
if (!rootCtx[collapsedPathSym]) {
|
|
31
|
-
rootCtx[collapsedPathSym] = Object.create(null)
|
|
32
|
-
} else if (rootCtx[collapsedPathSym][computedPathString]) {
|
|
33
|
-
return
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
rootCtx[collapsedPathSym][computedPathString] = true
|
|
37
|
-
}
|
|
36
|
+
const parentField = getParentField(rootCtx, path)
|
|
37
|
+
const childOf = parentField?.ctx?.currentStore?.span
|
|
38
38
|
|
|
39
39
|
const document = rootCtx.source
|
|
40
|
-
const fieldNode = info.fieldNodes[0]
|
|
41
40
|
const loc = this.config.source && document && fieldNode && fieldNode.loc
|
|
42
41
|
const source = loc && document.slice(loc.start, loc.end)
|
|
43
42
|
|
|
43
|
+
let namedReturnType = returnType
|
|
44
|
+
while (namedReturnType.ofType) namedReturnType = namedReturnType.ofType
|
|
45
|
+
|
|
44
46
|
const span = this.startSpan('graphql.resolve', {
|
|
45
47
|
service: this.config.service,
|
|
46
|
-
resource: `${
|
|
48
|
+
resource: `${fieldName}:${returnType}`,
|
|
47
49
|
childOf,
|
|
48
50
|
type: 'graphql',
|
|
49
51
|
meta: {
|
|
50
|
-
'graphql.field.name':
|
|
51
|
-
'graphql.field.path':
|
|
52
|
-
'graphql.field.type':
|
|
52
|
+
'graphql.field.name': fieldName,
|
|
53
|
+
'graphql.field.path': pathString,
|
|
54
|
+
'graphql.field.type': namedReturnType.name,
|
|
53
55
|
'graphql.source': source,
|
|
54
56
|
},
|
|
55
57
|
}, fieldCtx)
|
|
56
58
|
|
|
57
59
|
if (fieldNode && this.config.variables && fieldNode.arguments) {
|
|
58
|
-
const variables = this.config.variables(
|
|
60
|
+
const variables = this.config.variables(variableValues)
|
|
59
61
|
|
|
60
62
|
for (const arg of fieldNode.arguments) {
|
|
61
63
|
if (arg.value?.name && arg.value.kind === 'Variable' && variables[arg.value.name.value]) {
|
|
@@ -66,7 +68,7 @@ class GraphQLResolvePlugin extends TracingPlugin {
|
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
if (this.resolverStartCh.hasSubscribers) {
|
|
69
|
-
this.resolverStartCh.publish({ ctx: rootCtx, resolverInfo: getResolverInfo(
|
|
71
|
+
this.resolverStartCh.publish({ ctx: rootCtx, resolverInfo: getResolverInfo(fieldNode, fieldName, args) })
|
|
70
72
|
}
|
|
71
73
|
|
|
72
74
|
return fieldCtx.currentStore
|
|
@@ -76,11 +78,11 @@ class GraphQLResolvePlugin extends TracingPlugin {
|
|
|
76
78
|
super(...args)
|
|
77
79
|
|
|
78
80
|
this.addTraceSub('updateField', (ctx) => {
|
|
79
|
-
|
|
81
|
+
// start short-circuited on the depth gate, so there is no span to advance.
|
|
82
|
+
if (ctx.currentStore === undefined) return
|
|
80
83
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const span = ctx?.currentStore?.span || this.activeSpan
|
|
84
|
+
const { field, error } = ctx
|
|
85
|
+
const span = ctx.currentStore.span
|
|
84
86
|
field.finishTime = span._getTime ? span._getTime() : 0
|
|
85
87
|
field.error = field.error || error
|
|
86
88
|
})
|
|
@@ -105,38 +107,38 @@ class GraphQLResolvePlugin extends TracingPlugin {
|
|
|
105
107
|
|
|
106
108
|
// helpers
|
|
107
109
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
+
/**
|
|
111
|
+
* @param {{ depth: number, collapse: boolean }} config
|
|
112
|
+
* @param {{ prev: object | undefined, key: string | number }} path
|
|
113
|
+
*/
|
|
114
|
+
function shouldInstrument (config, path) {
|
|
115
|
+
const depth = config.depth
|
|
116
|
+
if (depth < 0) return true
|
|
110
117
|
|
|
111
|
-
let
|
|
118
|
+
let count = 0
|
|
112
119
|
if (config.collapse) {
|
|
113
|
-
|
|
120
|
+
for (let curr = path; curr; curr = curr.prev) count += 1
|
|
114
121
|
} else {
|
|
115
|
-
for (
|
|
116
|
-
if (typeof
|
|
122
|
+
for (let curr = path; curr; curr = curr.prev) {
|
|
123
|
+
if (typeof curr.key === 'string') count += 1
|
|
117
124
|
}
|
|
118
125
|
}
|
|
119
|
-
|
|
120
|
-
return config.depth >= depth
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function buildCollapsedPathString (pathAsArray) {
|
|
124
|
-
let result = ''
|
|
125
|
-
for (const segment of pathAsArray) {
|
|
126
|
-
if (result.length > 0) result += '.'
|
|
127
|
-
result += typeof segment === 'number' ? '*' : segment
|
|
128
|
-
}
|
|
129
|
-
return result
|
|
126
|
+
return depth >= count
|
|
130
127
|
}
|
|
131
128
|
|
|
132
|
-
|
|
129
|
+
/**
|
|
130
|
+
* @param {object | undefined} fieldNode
|
|
131
|
+
* @param {string} fieldName
|
|
132
|
+
* @param {Record<string, unknown> | undefined} args
|
|
133
|
+
*/
|
|
134
|
+
function getResolverInfo (fieldNode, fieldName, args) {
|
|
133
135
|
let resolverVars
|
|
134
136
|
|
|
135
137
|
if (args && Object.keys(args).length > 0) {
|
|
136
138
|
resolverVars = { ...args }
|
|
137
139
|
}
|
|
138
140
|
|
|
139
|
-
const directives =
|
|
141
|
+
const directives = fieldNode?.directives
|
|
140
142
|
if (Array.isArray(directives)) {
|
|
141
143
|
for (const directive of directives) {
|
|
142
144
|
if (directive.arguments.length === 0) continue
|
|
@@ -151,23 +153,18 @@ function getResolverInfo (info, args) {
|
|
|
151
153
|
}
|
|
152
154
|
}
|
|
153
155
|
|
|
154
|
-
return resolverVars === undefined ? null : { [
|
|
156
|
+
return resolverVars === undefined ? null : { [fieldName]: resolverVars }
|
|
155
157
|
}
|
|
156
158
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
current = current.slice(0, lastJoin)
|
|
165
|
-
const field = parentCtx.fields[current]
|
|
166
|
-
|
|
159
|
+
/**
|
|
160
|
+
* @param {{ fields: Map<object, { error: unknown, ctx: object }> }} rootCtx
|
|
161
|
+
* @param {{ prev: object | undefined }} path
|
|
162
|
+
*/
|
|
163
|
+
function getParentField (rootCtx, path) {
|
|
164
|
+
for (let curr = path.prev; curr; curr = curr.prev) {
|
|
165
|
+
const field = rootCtx.fields.get(curr)
|
|
167
166
|
if (field) return field
|
|
168
167
|
}
|
|
169
|
-
|
|
170
|
-
return null
|
|
171
168
|
}
|
|
172
169
|
|
|
173
170
|
module.exports = GraphQLResolvePlugin
|
|
@@ -7,7 +7,10 @@ function extractErrorIntoSpanEvent (config, span, exc) {
|
|
|
7
7
|
attributes.type = exc.name
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
// graphql-js validation errors carry a lazy `.stack` accessor; reading it
|
|
11
|
+
// here is the only consumer in the pipeline and pays full V8 symbolisation.
|
|
12
|
+
const isValidationOnly = exc.locations && !exc.path && !exc.originalError?.stack
|
|
13
|
+
if (!isValidationOnly && exc.stack) {
|
|
11
14
|
attributes.stacktrace = exc.stack
|
|
12
15
|
}
|
|
13
16
|
|
|
@@ -14,30 +14,35 @@ class HttpServerPlugin extends ServerPlugin {
|
|
|
14
14
|
|
|
15
15
|
static prefix = 'apm:http:server:request'
|
|
16
16
|
|
|
17
|
+
/** @type {string | undefined} */
|
|
18
|
+
#operationName
|
|
19
|
+
|
|
20
|
+
/** @type {object | undefined} */
|
|
21
|
+
#startConfig
|
|
22
|
+
|
|
23
|
+
/** @type {string | undefined} */
|
|
24
|
+
#serviceSource
|
|
25
|
+
|
|
17
26
|
constructor (...args) {
|
|
18
27
|
super(...args)
|
|
19
28
|
this.addTraceSub('exit', message => this.exit(message))
|
|
20
29
|
}
|
|
21
30
|
|
|
22
|
-
start (
|
|
31
|
+
start (ctx) {
|
|
32
|
+
const { req, res } = ctx
|
|
23
33
|
let store = legacyStorage.getStore()
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
? 'opt.plugin'
|
|
28
|
-
: (service === this.tracer._service ? undefined : schemaServiceSource)
|
|
34
|
+
if (this.#startConfig === undefined) {
|
|
35
|
+
this.#refreshStartCache()
|
|
36
|
+
}
|
|
29
37
|
const span = web.startSpan(
|
|
30
38
|
this.tracer,
|
|
31
|
-
|
|
32
|
-
...this.config,
|
|
33
|
-
service,
|
|
34
|
-
},
|
|
39
|
+
this.#startConfig,
|
|
35
40
|
req,
|
|
36
41
|
res,
|
|
37
|
-
this
|
|
42
|
+
this.#operationName
|
|
38
43
|
)
|
|
39
|
-
if (serviceSource !== undefined) {
|
|
40
|
-
span.setTag(SVC_SRC_KEY, serviceSource)
|
|
44
|
+
if (this.#serviceSource !== undefined) {
|
|
45
|
+
span.setTag(SVC_SRC_KEY, this.#serviceSource)
|
|
41
46
|
}
|
|
42
47
|
span.setTag(COMPONENT, this.constructor.id)
|
|
43
48
|
span._integrationName = this.constructor.id
|
|
@@ -60,7 +65,10 @@ class HttpServerPlugin extends ServerPlugin {
|
|
|
60
65
|
}
|
|
61
66
|
|
|
62
67
|
if (appsecActive) {
|
|
63
|
-
|
|
68
|
+
// Reuse the ctx allocated by the HTTP server instrumentation rather
|
|
69
|
+
// than a fresh `{ req, res, abortController }` per request; the AppSec
|
|
70
|
+
// subscriber only reads from the message.
|
|
71
|
+
incomingHttpRequestStart.publish(ctx)
|
|
64
72
|
}
|
|
65
73
|
}
|
|
66
74
|
|
|
@@ -93,7 +101,24 @@ class HttpServerPlugin extends ServerPlugin {
|
|
|
93
101
|
}
|
|
94
102
|
|
|
95
103
|
configure (config) {
|
|
96
|
-
|
|
104
|
+
const result = super.configure(web.normalizeConfig(config))
|
|
105
|
+
// Invalidate the start-cache; the next `start` refills it. Resolving
|
|
106
|
+
// service / operation eagerly here would pin nomenclature lookups to
|
|
107
|
+
// the order plugins and tracer initialise.
|
|
108
|
+
this.#startConfig = undefined
|
|
109
|
+
return result
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
#refreshStartCache () {
|
|
113
|
+
const { name: schemaServiceName, source: schemaServiceSource } = this.serviceName()
|
|
114
|
+
const tracerService = this.tracer._service
|
|
115
|
+
const configService = this.config.service
|
|
116
|
+
const service = configService || schemaServiceName
|
|
117
|
+
this.#serviceSource = (configService && service !== tracerService)
|
|
118
|
+
? 'opt.plugin'
|
|
119
|
+
: (service === tracerService ? undefined : schemaServiceSource)
|
|
120
|
+
this.#operationName = this.operationName()
|
|
121
|
+
this.#startConfig = { ...this.config, service }
|
|
97
122
|
}
|
|
98
123
|
}
|
|
99
124
|
|
|
@@ -16,7 +16,6 @@ const {
|
|
|
16
16
|
getTestSuiteCommonTags,
|
|
17
17
|
addIntelligentTestRunnerSpanTags,
|
|
18
18
|
TEST_PARAMETERS,
|
|
19
|
-
TEST_COMMAND,
|
|
20
19
|
TEST_FRAMEWORK_VERSION,
|
|
21
20
|
TEST_SOURCE_START,
|
|
22
21
|
TEST_ITR_UNSKIPPABLE,
|
|
@@ -115,7 +114,9 @@ class JestPlugin extends CiPlugin {
|
|
|
115
114
|
isSuitesSkipped,
|
|
116
115
|
isSuitesSkippingEnabled,
|
|
117
116
|
isCodeCoverageEnabled,
|
|
117
|
+
isCoverageReportUploadEnabled,
|
|
118
118
|
testCodeCoverageLinesTotal,
|
|
119
|
+
testSessionCoverageFiles,
|
|
119
120
|
numSkippedSuites,
|
|
120
121
|
hasUnskippableSuites,
|
|
121
122
|
hasForcedToRunSuites,
|
|
@@ -149,6 +150,13 @@ class JestPlugin extends CiPlugin {
|
|
|
149
150
|
}
|
|
150
151
|
)
|
|
151
152
|
|
|
153
|
+
if (testSessionCoverageFiles?.length && isCoverageReportUploadEnabled) {
|
|
154
|
+
this.tracer._exporter.exportCoverage({
|
|
155
|
+
sessionId: this.testSessionSpan.context()._traceId,
|
|
156
|
+
files: testSessionCoverageFiles,
|
|
157
|
+
})
|
|
158
|
+
}
|
|
159
|
+
|
|
152
160
|
if (isEarlyFlakeDetectionEnabled) {
|
|
153
161
|
this.testSessionSpan.setTag(TEST_EARLY_FLAKE_ENABLED, 'true')
|
|
154
162
|
}
|
|
@@ -194,7 +202,7 @@ class JestPlugin extends CiPlugin {
|
|
|
194
202
|
for (const config of configs) {
|
|
195
203
|
config._ddTestSessionId = this.testSessionSpan.context().toTraceId()
|
|
196
204
|
config._ddTestModuleId = this.testModuleSpan.context().toSpanId()
|
|
197
|
-
config._ddTestCommand = this.
|
|
205
|
+
config._ddTestCommand = this.command
|
|
198
206
|
config._ddRequestErrorTags = this.getSessionRequestErrorTags()
|
|
199
207
|
config._ddItrCorrelationId = this.itrCorrelationId
|
|
200
208
|
config._ddIsEarlyFlakeDetectionEnabled = !!this.libraryConfig?.isEarlyFlakeDetectionEnabled
|
|
@@ -596,7 +604,7 @@ class JestPlugin extends CiPlugin {
|
|
|
596
604
|
extraTags[TEST_HAS_DYNAMIC_NAME] = 'true'
|
|
597
605
|
}
|
|
598
606
|
const testSuiteSpan = this.testSuiteSpanPerTestSuiteAbsolutePath.get(testSuiteAbsolutePath) || this.testSuiteSpan
|
|
599
|
-
const skippingEnabled = testSuiteSpan?.context()
|
|
607
|
+
const skippingEnabled = testSuiteSpan?.context()?.getTag?.(TEST_ITR_SKIPPING_ENABLED)
|
|
600
608
|
if (skippingEnabled !== undefined) {
|
|
601
609
|
extraTags[TEST_ITR_SKIPPING_ENABLED] = skippingEnabled
|
|
602
610
|
}
|
|
@@ -41,11 +41,16 @@ function getFormattedJestTestParameters (testParameters) {
|
|
|
41
41
|
return formattedParameters
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
//
|
|
45
|
-
//
|
|
44
|
+
// @fast-check/jest appends a random seed to the reported test name. A test name that keeps changing
|
|
45
|
+
// breaks some Test Optimization features, so normalize this narrow suffix regardless of import style.
|
|
46
46
|
const SEED_SUFFIX_RE = /\s*\(with seed=-?\d+\)\s*$/i
|
|
47
|
+
|
|
48
|
+
function removeSeedSuffixFromTestName (testName) {
|
|
49
|
+
return testName.replace(SEED_SUFFIX_RE, '')
|
|
50
|
+
}
|
|
51
|
+
|
|
47
52
|
// https://github.com/facebook/jest/blob/3e38157ad5f23fb7d24669d24fae8ded06a7ab75/packages/jest-circus/src/utils.ts#L396
|
|
48
|
-
function
|
|
53
|
+
function getRawJestTestName (test) {
|
|
49
54
|
const titles = []
|
|
50
55
|
let parent = test
|
|
51
56
|
do {
|
|
@@ -54,11 +59,11 @@ function getJestTestName (test, shouldStripSeed = false) {
|
|
|
54
59
|
|
|
55
60
|
titles.shift() // remove TOP_DESCRIBE_BLOCK_NAME
|
|
56
61
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
return
|
|
62
|
+
return titles.join(' ')
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function getJestTestName (test) {
|
|
66
|
+
return removeSeedSuffixFromTestName(getRawJestTestName(test))
|
|
62
67
|
}
|
|
63
68
|
|
|
64
69
|
const globalDocblockRegExp = /^\s*(\/\*\*?(.|\r?\n)*?\*\/)/
|
|
@@ -170,6 +175,8 @@ module.exports = {
|
|
|
170
175
|
SEED_SUFFIX_RE,
|
|
171
176
|
getFormattedJestTestParameters,
|
|
172
177
|
getJestTestName,
|
|
178
|
+
getRawJestTestName,
|
|
173
179
|
getJestSuitesToRun,
|
|
174
180
|
isMarkedAsUnskippable,
|
|
181
|
+
removeSeedSuffixFromTestName,
|
|
175
182
|
}
|
|
@@ -91,6 +91,41 @@ 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
|
+
// sendBatch hands the same multi-topic response to every per-topic
|
|
105
|
+
// ctx; the span only owns its own topic's entries.
|
|
106
|
+
if (entry.topicName !== ctx.topic) continue
|
|
107
|
+
const offsetAsLong = entry.offset ?? entry.baseOffset
|
|
108
|
+
if (entry.partition === undefined || offsetAsLong === undefined) continue
|
|
109
|
+
// Kafka offsets are 64-bit; coercing to Number loses precision past
|
|
110
|
+
// 2^53. Keep them as strings so the tag matches the exact offset on
|
|
111
|
+
// long-lived/high-throughput topics.
|
|
112
|
+
offsets.push({ partition: entry.partition, start_offset: String(offsetAsLong) })
|
|
113
|
+
}
|
|
114
|
+
if (offsets.length > 0) {
|
|
115
|
+
offsets.sort((a, b) => a.partition - b.partition)
|
|
116
|
+
span.setTag('kafka.messages.offsets', JSON.stringify(offsets))
|
|
117
|
+
}
|
|
118
|
+
// Single-message send: the one entry's partition/offset describes the
|
|
119
|
+
// exact record. Also expose them as flat tags for easy filtering.
|
|
120
|
+
if (offsets.length === 1 && ctx.messages?.length === 1) {
|
|
121
|
+
span.setTag('kafka.partition', offsets[0].partition)
|
|
122
|
+
// Set as a string meta tag (not a metric) to preserve full 64-bit precision.
|
|
123
|
+
span.setTag('kafka.message.offset', offsets[0].start_offset)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
super.finish(ctx)
|
|
127
|
+
}
|
|
128
|
+
|
|
94
129
|
bindStart (ctx) {
|
|
95
130
|
const { topic, messages, bootstrapServers, clusterId, disableHeaderInjection } = ctx
|
|
96
131
|
const span = this.startSpan({
|
|
@@ -20,7 +20,7 @@ class PregelStreamPlugin extends TracingPlugin {
|
|
|
20
20
|
}
|
|
21
21
|
class NextStreamPlugin extends TracingPlugin {
|
|
22
22
|
static id = 'langgraph_stream_next'
|
|
23
|
-
static prefix = 'tracing:orchestrion:@langchain/langgraph:
|
|
23
|
+
static prefix = 'tracing:orchestrion:@langchain/langgraph:Pregel_stream:next'
|
|
24
24
|
|
|
25
25
|
bindStart (ctx) {
|
|
26
26
|
return ctx.currentStore
|