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
|
@@ -503,13 +503,38 @@ function getTestFinishInfo (test, status, config, error) {
|
|
|
503
503
|
}
|
|
504
504
|
}
|
|
505
505
|
|
|
506
|
-
function getOnTestEndHandler (config) {
|
|
506
|
+
function getOnTestEndHandler (config, finalAttemptHandlers) {
|
|
507
507
|
return async function (test) {
|
|
508
508
|
if (test._ddShouldSkipEfdRetry) {
|
|
509
509
|
return
|
|
510
510
|
}
|
|
511
511
|
const ctx = getTestContext(test)
|
|
512
512
|
const status = getTestStatus(test)
|
|
513
|
+
const shouldFinishTest = ctx && (!getAfterEachHooks(test).length || (test._ddIsDisabled && !test._ddIsAttemptToFix))
|
|
514
|
+
let testFinishInfo
|
|
515
|
+
let isFinalAttempt = false
|
|
516
|
+
|
|
517
|
+
// If there are afterEach to be run, we don't finish the test yet.
|
|
518
|
+
// Disabled tests (marked pending by us) are finished immediately without waiting for afterEach hooks.
|
|
519
|
+
// In older mocha versions, pending tests don't run afterEach hooks, so we can't rely on
|
|
520
|
+
// getOnHookEndHandler to finish the test. This mirrors Jest's approach where the skip handler
|
|
521
|
+
// directly sets finalStatus without waiting for hooks
|
|
522
|
+
if (!ctx && test.isPending()) {
|
|
523
|
+
test._ddIsFinalAttempt = true
|
|
524
|
+
isFinalAttempt = true
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
if (shouldFinishTest) {
|
|
528
|
+
testFinishInfo = getTestFinishInfo(test, status, config, ctx.err || test.err)
|
|
529
|
+
if (testFinishInfo.finalStatus !== undefined) {
|
|
530
|
+
test._ddIsFinalAttempt = true
|
|
531
|
+
isFinalAttempt = true
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
if (isFinalAttempt) {
|
|
536
|
+
finalAttemptHandlers?.onStart?.(test)
|
|
537
|
+
}
|
|
513
538
|
|
|
514
539
|
// After finishing it might take a bit for the snapshot to be handled.
|
|
515
540
|
// This means that tests retried with DI are BREAKPOINT_HIT_GRACE_PERIOD_MS slower at least.
|
|
@@ -521,13 +546,7 @@ function getOnTestEndHandler (config) {
|
|
|
521
546
|
})
|
|
522
547
|
}
|
|
523
548
|
|
|
524
|
-
|
|
525
|
-
// Disabled tests (marked pending by us) are finished immediately without waiting for afterEach hooks.
|
|
526
|
-
// In older mocha versions, pending tests don't run afterEach hooks, so we can't rely on
|
|
527
|
-
// getOnHookEndHandler to finish the test. This mirrors Jest's approach where the skip handler
|
|
528
|
-
// directly sets finalStatus without waiting for hooks
|
|
529
|
-
if (ctx && (!getAfterEachHooks(test).length || (test._ddIsDisabled && !test._ddIsAttemptToFix))) {
|
|
530
|
-
const testFinishInfo = getTestFinishInfo(test, status, config, ctx.err || test.err)
|
|
549
|
+
if (shouldFinishTest) {
|
|
531
550
|
testFinishCh.publish({
|
|
532
551
|
status,
|
|
533
552
|
hasBeenRetried: isMochaRetry(test),
|
|
@@ -536,6 +555,10 @@ function getOnTestEndHandler (config) {
|
|
|
536
555
|
...ctx.currentStore,
|
|
537
556
|
})
|
|
538
557
|
}
|
|
558
|
+
|
|
559
|
+
if (isFinalAttempt) {
|
|
560
|
+
finalAttemptHandlers?.onFinish?.(test)
|
|
561
|
+
}
|
|
539
562
|
}
|
|
540
563
|
}
|
|
541
564
|
|
|
@@ -552,6 +575,9 @@ function getOnHookEndHandler (config) {
|
|
|
552
575
|
// skip to avoid double-publishing
|
|
553
576
|
if (ctx && (!test._ddIsDisabled || test._ddIsAttemptToFix)) {
|
|
554
577
|
const testFinishInfo = getTestFinishInfo(test, status, config, ctx.err || test.err)
|
|
578
|
+
if (testFinishInfo.finalStatus !== undefined) {
|
|
579
|
+
test._ddIsFinalAttempt = true
|
|
580
|
+
}
|
|
555
581
|
testFinishCh.publish({
|
|
556
582
|
status,
|
|
557
583
|
hasBeenRetried: isMochaRetry(test),
|
|
@@ -583,6 +609,20 @@ function getOnFailHandler (isMain, config) {
|
|
|
583
609
|
testContext.err = err
|
|
584
610
|
errorCh.runStores(testContext, () => {})
|
|
585
611
|
const testFinishInfo = getTestFinishInfo(test, 'fail', config, err)
|
|
612
|
+
// ATR never retries hook failures: this.retries(N) is set in runnableWrapper
|
|
613
|
+
// which only runs when the test function executes — hooks bypass that path,
|
|
614
|
+
// so _retries stays at -1 and getIsLastRetry returns false, leaving finalStatus
|
|
615
|
+
// undefined. We must also mark the attempt final when no clone-based retry
|
|
616
|
+
// mechanism (EFD original, EFD clone, ATF) has queued further attempts.
|
|
617
|
+
const noCloneRetries = !test._ddIsEfdRetry &&
|
|
618
|
+
!((test._ddIsNew || test._ddIsModified) && config.isEarlyFlakeDetectionEnabled) &&
|
|
619
|
+
!test._ddIsAttemptToFix
|
|
620
|
+
if (testFinishInfo.finalStatus !== undefined || noCloneRetries) {
|
|
621
|
+
test._ddIsFinalAttempt = true
|
|
622
|
+
}
|
|
623
|
+
// test.state is never set to 'failed' for hook failures (Mocha marks the hook,
|
|
624
|
+
// not the test). Flag it so finishRootSuiteForFile can compute the correct status.
|
|
625
|
+
test._ddHookFailed = true
|
|
586
626
|
testFinishCh.publish({
|
|
587
627
|
status: 'fail',
|
|
588
628
|
hasBeenRetried: isMochaRetry(test),
|
|
@@ -174,7 +174,7 @@ function instrument (operation, command, instance, args, server, ns, ops, option
|
|
|
174
174
|
name,
|
|
175
175
|
}
|
|
176
176
|
return startCh.runStores(ctx, () => {
|
|
177
|
-
args[index] = shimmer.
|
|
177
|
+
args[index] = shimmer.wrapCallback(callback, callback => function (err, res) {
|
|
178
178
|
if (err) {
|
|
179
179
|
ctx.error = err
|
|
180
180
|
errorCh.publish(ctx)
|
|
@@ -125,21 +125,19 @@ addHook({
|
|
|
125
125
|
const resolve = args[0]
|
|
126
126
|
const reject = args[1]
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
if (resolve) {
|
|
128
|
+
if (typeof resolve === 'function') {
|
|
129
|
+
args[0] = shimmer.wrapCallback(resolve, resolve => function wrappedResolve (...args) {
|
|
130
|
+
finishCh.publish(ctx)
|
|
132
131
|
return resolve.apply(this, args)
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
args[1] = shimmer.wrapFunction(reject, reject => function wrappedReject (...args) {
|
|
137
|
-
finishCh.publish(ctx)
|
|
132
|
+
})
|
|
133
|
+
}
|
|
138
134
|
|
|
139
|
-
|
|
135
|
+
if (typeof reject === 'function') {
|
|
136
|
+
args[1] = shimmer.wrapCallback(reject, reject => function wrappedReject (...args) {
|
|
137
|
+
finishCh.publish(ctx)
|
|
140
138
|
return reject.apply(this, args)
|
|
141
|
-
}
|
|
142
|
-
}
|
|
139
|
+
})
|
|
140
|
+
}
|
|
143
141
|
|
|
144
142
|
return originalThen.apply(this, args)
|
|
145
143
|
}
|
|
@@ -29,7 +29,7 @@ addHook({ name: 'mysql', file: 'lib/Connection.js', versions: ['>=2'] }, Connect
|
|
|
29
29
|
|
|
30
30
|
if (res._callback) {
|
|
31
31
|
const cb = res._callback
|
|
32
|
-
res._callback = shimmer.
|
|
32
|
+
res._callback = shimmer.wrapCallback(cb, cb => function (error, result) {
|
|
33
33
|
if (error) {
|
|
34
34
|
ctx.error = error
|
|
35
35
|
errorCh.publish(ctx)
|
|
@@ -86,7 +86,7 @@ addHook({ name: 'mysql', file: 'lib/Pool.js', versions: ['>=2'] }, Pool => {
|
|
|
86
86
|
return startPoolQueryCh.runStores(ctx, () => {
|
|
87
87
|
const cb = args[args.length - 1]
|
|
88
88
|
if (typeof cb === 'function') {
|
|
89
|
-
args[args.length - 1] = shimmer.
|
|
89
|
+
args[args.length - 1] = shimmer.wrapCallback(cb, cb => function (...args) {
|
|
90
90
|
return finishPoolQueryCh.runStores(ctx, cb, this, ...args)
|
|
91
91
|
})
|
|
92
92
|
}
|
|
@@ -176,7 +176,7 @@ function wrapConnection (Connection, version) {
|
|
|
176
176
|
if (typeof this.onResult === 'function') {
|
|
177
177
|
const onResult = this.onResult
|
|
178
178
|
|
|
179
|
-
this.onResult = shimmer.
|
|
179
|
+
this.onResult = shimmer.wrapCallback(onResult, onResult => function (error) {
|
|
180
180
|
if (error) {
|
|
181
181
|
ctx.error = error
|
|
182
182
|
errorCh.publish(ctx)
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
// Shimmer required: NATS consumer paths need argument modification — the user's
|
|
4
|
+
// `opts.callback` is wrapped before being handed to SubscriptionImpl, and the
|
|
5
|
+
// returned subscription's async iterator is wrapped so iterator-style consumers
|
|
6
|
+
// get receive events. Orchestrion can only wrap method calls, not arguments
|
|
7
|
+
// or returned iterables.
|
|
8
|
+
|
|
9
|
+
const shimmer = require('../../datadog-shimmer')
|
|
10
|
+
const { addHook, channel } = require('./helpers/instrument')
|
|
11
|
+
|
|
12
|
+
const publishStartCh = channel('apm:nats:publish:start')
|
|
13
|
+
const publishFinishCh = channel('apm:nats:publish:finish')
|
|
14
|
+
const publishErrorCh = channel('apm:nats:publish:error')
|
|
15
|
+
|
|
16
|
+
const consumeStartCh = channel('apm:nats:consume:start')
|
|
17
|
+
const consumeFinishCh = channel('apm:nats:consume:finish')
|
|
18
|
+
const consumeErrorCh = channel('apm:nats:consume:error')
|
|
19
|
+
|
|
20
|
+
// Tracks connections that are currently inside a `request`/`requestMany` call
|
|
21
|
+
// so the nested `this.publish(...)` they issue short-circuits without creating
|
|
22
|
+
// a second producer span (the outer request wrap already created one and
|
|
23
|
+
// injected headers — the inner publish would double-count it). A WeakSet avoids
|
|
24
|
+
// changing the shape of the user's connection object.
|
|
25
|
+
const requestsInFlight = new WeakSet()
|
|
26
|
+
|
|
27
|
+
// Captured from the `lib/headers.js` hook below. The nats-core package always
|
|
28
|
+
// imports `./headers` from `lib/nats.js`, so by the time we wrap `publish` the
|
|
29
|
+
// reference is set. No defensive checks needed at call sites.
|
|
30
|
+
let createHeaders
|
|
31
|
+
|
|
32
|
+
addHook({ name: '@nats-io/nats-core', versions: ['>=3.0.0'], file: 'lib/headers.js' }, exports => {
|
|
33
|
+
createHeaders = exports.headers
|
|
34
|
+
return exports
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
// transport-node re-exports nats-core internals — the passthrough hook ensures
|
|
38
|
+
// the package name is registered so `withVersions('nats', '@nats-io/transport-node', ...)`
|
|
39
|
+
// can resolve it in plugin tests.
|
|
40
|
+
addHook({ name: '@nats-io/transport-node', versions: ['>=3.0.0'] }, exports => exports)
|
|
41
|
+
|
|
42
|
+
function wrapSyncProducer (original, type) {
|
|
43
|
+
return function (subject, data, options) {
|
|
44
|
+
if (!publishStartCh.hasSubscribers) {
|
|
45
|
+
return original.apply(this, arguments)
|
|
46
|
+
}
|
|
47
|
+
const opts = { ...options }
|
|
48
|
+
const ctx = { type, subject, data, options: opts, connection: this, createHeaders }
|
|
49
|
+
return publishStartCh.runStores(ctx, () => {
|
|
50
|
+
try {
|
|
51
|
+
return original.call(this, subject, data, opts)
|
|
52
|
+
} catch (err) {
|
|
53
|
+
ctx.error = err
|
|
54
|
+
publishErrorCh.publish(ctx)
|
|
55
|
+
throw err
|
|
56
|
+
} finally {
|
|
57
|
+
publishFinishCh.publish(ctx)
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// publish is also wrapped by `wrapSyncProducer`, but request/requestMany call
|
|
64
|
+
// `this.publish(...)` internally. Set a marker on the connection so the inner
|
|
65
|
+
// publish wrap short-circuits — see `wrapPublish`.
|
|
66
|
+
function wrapAsyncProducer (original, type) {
|
|
67
|
+
return function (subject, data, options) {
|
|
68
|
+
if (!publishStartCh.hasSubscribers) {
|
|
69
|
+
return original.apply(this, arguments)
|
|
70
|
+
}
|
|
71
|
+
const opts = { ...options }
|
|
72
|
+
const ctx = { type, subject, data, options: opts, connection: this, createHeaders }
|
|
73
|
+
return publishStartCh.runStores(ctx, () => {
|
|
74
|
+
requestsInFlight.add(this)
|
|
75
|
+
let promise
|
|
76
|
+
try {
|
|
77
|
+
// `request`/`requestMany` never throw synchronously — they wrap their own
|
|
78
|
+
// input validation in a try/catch that returns `Promise.reject`.
|
|
79
|
+
promise = original.call(this, subject, data, opts)
|
|
80
|
+
} finally {
|
|
81
|
+
// The nested `this.publish(...)` runs during the synchronous body of
|
|
82
|
+
// request/requestMany, so clearing the marker as soon as the call
|
|
83
|
+
// returns is sufficient — the promise resolution happens later.
|
|
84
|
+
requestsInFlight.delete(this)
|
|
85
|
+
}
|
|
86
|
+
return Promise.resolve(promise).then(
|
|
87
|
+
result => {
|
|
88
|
+
ctx.result = result
|
|
89
|
+
publishFinishCh.publish(ctx)
|
|
90
|
+
return result
|
|
91
|
+
},
|
|
92
|
+
err => {
|
|
93
|
+
ctx.error = err
|
|
94
|
+
publishErrorCh.publish(ctx)
|
|
95
|
+
publishFinishCh.publish(ctx)
|
|
96
|
+
throw err
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function wrapPublish (original) {
|
|
104
|
+
const wrapped = wrapSyncProducer(original, 'publish')
|
|
105
|
+
return function (subject, data, options) {
|
|
106
|
+
// Called from inside request/requestMany — the outer wrap already produced
|
|
107
|
+
// a span and injected headers; running the inner wrap would double-count.
|
|
108
|
+
if (requestsInFlight.has(this)) {
|
|
109
|
+
return original.apply(this, arguments)
|
|
110
|
+
}
|
|
111
|
+
return wrapped.apply(this, arguments)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function wrapSubscribeCallback (userCallback, subject, connection) {
|
|
116
|
+
return function (err, message) {
|
|
117
|
+
if (!message || err) {
|
|
118
|
+
return userCallback.call(this, err, message)
|
|
119
|
+
}
|
|
120
|
+
const ctx = { subject, message, connection }
|
|
121
|
+
return consumeStartCh.runStores(ctx, () => {
|
|
122
|
+
try {
|
|
123
|
+
return userCallback.call(this, err, message)
|
|
124
|
+
} catch (e) {
|
|
125
|
+
ctx.error = e
|
|
126
|
+
consumeErrorCh.publish(ctx)
|
|
127
|
+
throw e
|
|
128
|
+
} finally {
|
|
129
|
+
consumeFinishCh.publish(ctx)
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Iterator-style consumers don't expose a delivery callback we can wrap, so
|
|
136
|
+
// the consume span represents the moment of receipt only — it starts and
|
|
137
|
+
// finishes before the value is yielded to user code, and the user's loop
|
|
138
|
+
// body is not parented under the span.
|
|
139
|
+
function wrapAsyncIteratorFactory (asyncIterator, subject, connection) {
|
|
140
|
+
return function () {
|
|
141
|
+
const iterator = asyncIterator.apply(this, arguments)
|
|
142
|
+
iterator.next = shimmer.wrapCallback(iterator.next, next => function () {
|
|
143
|
+
return next.apply(this, arguments).then(result => {
|
|
144
|
+
if (result && !result.done && result.value) {
|
|
145
|
+
const ctx = { subject, message: result.value, connection }
|
|
146
|
+
consumeStartCh.runStores(ctx, () => {
|
|
147
|
+
consumeFinishCh.publish(ctx)
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
return result
|
|
151
|
+
})
|
|
152
|
+
})
|
|
153
|
+
return iterator
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
addHook({ name: '@nats-io/nats-core', versions: ['>=3.0.0'], file: 'lib/nats.js' }, exports => {
|
|
158
|
+
const proto = exports.NatsConnectionImpl.prototype
|
|
159
|
+
|
|
160
|
+
shimmer.wrap(proto, 'publish', wrapPublish)
|
|
161
|
+
shimmer.wrap(proto, 'request', request => wrapAsyncProducer(request, 'request'))
|
|
162
|
+
shimmer.wrap(proto, 'requestMany', requestMany => wrapAsyncProducer(requestMany, 'requestMany'))
|
|
163
|
+
|
|
164
|
+
shimmer.wrap(proto, 'subscribe', subscribe => function (subject, opts) {
|
|
165
|
+
if (!consumeStartCh.hasSubscribers) {
|
|
166
|
+
return subscribe.apply(this, arguments)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const userOpts = opts ?? {}
|
|
170
|
+
if (typeof userOpts.callback === 'function') {
|
|
171
|
+
arguments[1] = { ...userOpts, callback: wrapSubscribeCallback(userOpts.callback, subject, this) }
|
|
172
|
+
return subscribe.apply(this, arguments)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const sub = subscribe.apply(this, arguments)
|
|
176
|
+
shimmer.wrap(sub, Symbol.asyncIterator, asyncIterator =>
|
|
177
|
+
wrapAsyncIteratorFactory(asyncIterator, subject, this))
|
|
178
|
+
return sub
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
return exports
|
|
182
|
+
})
|
|
@@ -2,7 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
const shimmer = require('../../datadog-shimmer')
|
|
4
4
|
const { getEnvironmentVariable } = require('../../dd-trace/src/config/helper')
|
|
5
|
-
const {
|
|
5
|
+
const {
|
|
6
|
+
readCoverageBackfillFromCache,
|
|
7
|
+
readCoverageBackfillRootDirFromCache,
|
|
8
|
+
setupSettingsCachePath,
|
|
9
|
+
} = require('../../dd-trace/src/ci-visibility/test-optimization-cache')
|
|
10
|
+
const { applySkippedCoverageToCoverage } = require('../../dd-trace/src/plugins/util/test')
|
|
6
11
|
const { addHook, channel } = require('./helpers/instrument')
|
|
7
12
|
|
|
8
13
|
const codeCoverageWrapCh = channel('ci:nyc:wrap')
|
|
@@ -16,6 +21,38 @@ addHook({
|
|
|
16
21
|
// when dd-trace fetches library configuration
|
|
17
22
|
setupSettingsCachePath()
|
|
18
23
|
|
|
24
|
+
if (nycPackage.prototype.getCoverageMapFromAllCoverageFiles) {
|
|
25
|
+
// Some test frameworks receive skipped-suite coverage in the test process, but nyc merges reports later in the nyc
|
|
26
|
+
// process. Reuse the settings cache path as the process handoff so nyc can backfill skipped files before reporting.
|
|
27
|
+
shimmer.wrap(
|
|
28
|
+
nycPackage.prototype,
|
|
29
|
+
'getCoverageMapFromAllCoverageFiles',
|
|
30
|
+
getCoverageMapFromAllCoverageFiles => function (...args) {
|
|
31
|
+
const coverageMap = getCoverageMapFromAllCoverageFiles.apply(this, args)
|
|
32
|
+
const applyCoverageBackfill = (resolvedCoverageMap) => {
|
|
33
|
+
try {
|
|
34
|
+
if (!resolvedCoverageMap) {
|
|
35
|
+
return resolvedCoverageMap
|
|
36
|
+
}
|
|
37
|
+
applySkippedCoverageToCoverage(
|
|
38
|
+
resolvedCoverageMap,
|
|
39
|
+
readCoverageBackfillFromCache(),
|
|
40
|
+
readCoverageBackfillRootDirFromCache() || this.cwd
|
|
41
|
+
)
|
|
42
|
+
} catch {
|
|
43
|
+
// Do not break nyc's report generation if the cached backfill is stale or malformed.
|
|
44
|
+
}
|
|
45
|
+
return resolvedCoverageMap
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (coverageMap && typeof coverageMap.then === 'function') {
|
|
49
|
+
return coverageMap.then(applyCoverageBackfill)
|
|
50
|
+
}
|
|
51
|
+
return applyCoverageBackfill(coverageMap)
|
|
52
|
+
}
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
|
|
19
56
|
// `wrap` is an async function
|
|
20
57
|
shimmer.wrap(nycPackage.prototype, 'wrap', wrap => function (...args) {
|
|
21
58
|
// Only relevant if the config `all` is set to true (for untested code coverage)
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const dc = require('dc-polyfill')
|
|
4
4
|
const shimmer = require('../../datadog-shimmer')
|
|
5
5
|
const { addHook } = require('./helpers/instrument')
|
|
6
|
+
const aiGuard = require('./helpers/openai-ai-guard')
|
|
6
7
|
|
|
7
8
|
const ch = dc.tracingChannel('apm:openai:request')
|
|
8
9
|
const onStreamedChunkCh = dc.channel('apm:openai:request:chunk')
|
|
@@ -216,15 +217,20 @@ for (const extension of extensions) {
|
|
|
216
217
|
|
|
217
218
|
for (const methodName of methods) {
|
|
218
219
|
shimmer.wrap(targetPrototype, methodName, methodFn => function (...args) {
|
|
219
|
-
if (!ch.start.hasSubscribers) {
|
|
220
|
+
if (!ch.start.hasSubscribers && !aiGuard.hasSubscribers()) {
|
|
220
221
|
return methodFn.apply(this, args)
|
|
221
222
|
}
|
|
222
|
-
|
|
223
223
|
// The OpenAI library lets you set `stream: true` on the options arg to any method
|
|
224
224
|
// However, we only want to handle streamed responses in specific cases
|
|
225
225
|
// chat.completions and completions
|
|
226
226
|
const stream = streamedResponse && getOption(args, 'stream', false)
|
|
227
227
|
|
|
228
|
+
const guard = aiGuard.createGuard(baseResource, args[0], stream)
|
|
229
|
+
|
|
230
|
+
if (!ch.start.hasSubscribers && !guard) {
|
|
231
|
+
return methodFn.apply(this, args)
|
|
232
|
+
}
|
|
233
|
+
|
|
228
234
|
const client = this._client || this.client
|
|
229
235
|
|
|
230
236
|
const ctx = {
|
|
@@ -249,7 +255,7 @@ for (const extension of extensions) {
|
|
|
249
255
|
const parsedPromise = origApiPromParse.apply(this, args)
|
|
250
256
|
.then(body => Promise.all([this.responsePromise, body]))
|
|
251
257
|
|
|
252
|
-
return handleUnwrappedAPIPromise(parsedPromise, ctx, stream)
|
|
258
|
+
return handleUnwrappedAPIPromise(parsedPromise, ctx, stream, guard)
|
|
253
259
|
})
|
|
254
260
|
|
|
255
261
|
return unwrappedPromise
|
|
@@ -262,9 +268,11 @@ for (const extension of extensions) {
|
|
|
262
268
|
const parsedPromise = origApiPromParse.apply(this, args)
|
|
263
269
|
.then(body => Promise.all([this.responsePromise, body]))
|
|
264
270
|
|
|
265
|
-
return handleUnwrappedAPIPromise(parsedPromise, ctx, stream)
|
|
271
|
+
return handleUnwrappedAPIPromise(parsedPromise, ctx, stream, guard)
|
|
266
272
|
})
|
|
267
273
|
|
|
274
|
+
if (guard) aiGuard.wrapAsResponse(apiProm, guard)
|
|
275
|
+
|
|
268
276
|
ch.end.publish(ctx)
|
|
269
277
|
|
|
270
278
|
return apiProm
|
|
@@ -276,8 +284,10 @@ for (const extension of extensions) {
|
|
|
276
284
|
}
|
|
277
285
|
}
|
|
278
286
|
|
|
279
|
-
function handleUnwrappedAPIPromise (apiProm, ctx, stream) {
|
|
280
|
-
|
|
287
|
+
function handleUnwrappedAPIPromise (apiProm, ctx, stream, guard) {
|
|
288
|
+
const guardedApiProm = guard ? aiGuard.gateParse(apiProm, guard) : apiProm
|
|
289
|
+
|
|
290
|
+
return guardedApiProm
|
|
281
291
|
.then(([{ response, options }, body]) => {
|
|
282
292
|
if (stream) {
|
|
283
293
|
if (body.iterator) {
|
|
@@ -287,22 +297,27 @@ function handleUnwrappedAPIPromise (apiProm, ctx, stream) {
|
|
|
287
297
|
body.response.body, Symbol.asyncIterator, wrapStreamIterator(response, options, ctx)
|
|
288
298
|
)
|
|
289
299
|
}
|
|
290
|
-
|
|
291
|
-
finish(ctx, {
|
|
292
|
-
headers: response.headers,
|
|
293
|
-
data: body,
|
|
294
|
-
request: {
|
|
295
|
-
path: response.url,
|
|
296
|
-
method: options.method,
|
|
297
|
-
},
|
|
298
|
-
})
|
|
300
|
+
return body
|
|
299
301
|
}
|
|
300
302
|
|
|
301
|
-
|
|
303
|
+
finish(ctx, {
|
|
304
|
+
headers: response.headers,
|
|
305
|
+
data: body,
|
|
306
|
+
request: {
|
|
307
|
+
path: response.url,
|
|
308
|
+
method: options.method,
|
|
309
|
+
},
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
if (!guard) return body
|
|
313
|
+
|
|
314
|
+
return aiGuard.evaluateOutput(guard, body).then(() => body)
|
|
302
315
|
})
|
|
303
316
|
.catch(error => {
|
|
304
|
-
finish(
|
|
305
|
-
|
|
317
|
+
// ctx.result is set inside finish(); if absent, finish never ran (sync throw in
|
|
318
|
+
// success branch, before-model block, or openai error) — record the error now.
|
|
319
|
+
// If finish already ran successfully (after-model block), don't double-publish.
|
|
320
|
+
if (!ctx.result) finish(ctx, undefined, error)
|
|
306
321
|
throw error
|
|
307
322
|
})
|
|
308
323
|
}
|
|
@@ -22,7 +22,7 @@ function finish (ctx) {
|
|
|
22
22
|
|
|
23
23
|
addHook({ name: 'oracledb', versions: ['>=5'], file: 'lib/oracledb.js' }, oracledb => {
|
|
24
24
|
shimmer.wrap(oracledb.Connection.prototype, 'execute', execute => {
|
|
25
|
-
return function wrappedExecute (dbQuery
|
|
25
|
+
return function wrappedExecute (dbQuery) {
|
|
26
26
|
if (!startChannel.hasSubscribers) {
|
|
27
27
|
return execute.apply(this, arguments)
|
|
28
28
|
}
|
|
@@ -72,6 +72,11 @@ addHook({ name: 'oracledb', versions: ['>=5'], file: 'lib/oracledb.js' }, oracle
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
return startChannel.runStores(ctx, () => {
|
|
75
|
+
// bindStart is skipped when tracing is suppressed (legacy store is `noop`),
|
|
76
|
+
// leaving ctx.injected unset — do not overwrite the caller's SQL argument.
|
|
77
|
+
if (ctx.injected !== undefined) {
|
|
78
|
+
arguments[0] = ctx.injected
|
|
79
|
+
}
|
|
75
80
|
try {
|
|
76
81
|
let result = execute.apply(this, arguments)
|
|
77
82
|
|
|
@@ -193,7 +193,7 @@ function wrapPoolQuery (query) {
|
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
if (typeof cb === 'function') {
|
|
196
|
-
args[args.length - 1] = shimmer.
|
|
196
|
+
args[args.length - 1] = shimmer.wrapCallback(cb, cb => function (...args) {
|
|
197
197
|
finish(ctx)
|
|
198
198
|
return cb.apply(this, args)
|
|
199
199
|
})
|
|
@@ -6,7 +6,16 @@ const {
|
|
|
6
6
|
addHook,
|
|
7
7
|
} = require('./helpers/instrument')
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* @param {string} symbol
|
|
11
|
+
* @param {(original: Function) => Function} wrapper
|
|
12
|
+
* @param {Function} pino
|
|
13
|
+
*/
|
|
9
14
|
function wrapPino (symbol, wrapper, pino) {
|
|
15
|
+
/**
|
|
16
|
+
* @param {unknown[]} args
|
|
17
|
+
* @returns {unknown}
|
|
18
|
+
*/
|
|
10
19
|
return function pinoWithTrace (...args) {
|
|
11
20
|
const instance = pino.apply(this, args)
|
|
12
21
|
|
|
@@ -22,15 +31,18 @@ function wrapPino (symbol, wrapper, pino) {
|
|
|
22
31
|
}
|
|
23
32
|
|
|
24
33
|
function wrapAsJson (asJson) {
|
|
25
|
-
const
|
|
34
|
+
const jsonCh = channel('apm:pino:log:json')
|
|
26
35
|
return function asJsonWithTrace (obj, msg, num, time) {
|
|
27
36
|
obj = arguments[0] = obj || {}
|
|
28
37
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
38
|
+
// Caller-provided `dd` wins -- skip the splice so a bespoke `dd` survives.
|
|
39
|
+
if (!jsonCh.hasSubscribers || Object.hasOwn(obj, 'dd')) {
|
|
40
|
+
return asJson.apply(this, arguments)
|
|
41
|
+
}
|
|
32
42
|
|
|
33
|
-
|
|
43
|
+
const payload = { line: asJson.apply(this, arguments) }
|
|
44
|
+
jsonCh.publish(payload)
|
|
45
|
+
return payload.line
|
|
34
46
|
}
|
|
35
47
|
}
|
|
36
48
|
|