dd-trace 5.104.0 → 5.106.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE-3rdparty.csv +90 -102
- package/index.d.ts +82 -3
- package/package.json +15 -15
- package/packages/datadog-core/src/storage.js +1 -1
- package/packages/datadog-instrumentations/src/aerospike.js +1 -1
- package/packages/datadog-instrumentations/src/ai.js +8 -7
- package/packages/datadog-instrumentations/src/aws-sdk.js +16 -2
- package/packages/datadog-instrumentations/src/azure-cosmos.js +7 -0
- package/packages/datadog-instrumentations/src/azure-functions.js +3 -0
- package/packages/datadog-instrumentations/src/cucumber-worker-threads.js +19 -0
- package/packages/datadog-instrumentations/src/cucumber.js +390 -157
- package/packages/datadog-instrumentations/src/dns.js +54 -18
- package/packages/datadog-instrumentations/src/fastify.js +142 -82
- package/packages/datadog-instrumentations/src/graphql.js +188 -62
- package/packages/datadog-instrumentations/src/helpers/ai-messages.js +322 -14
- package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/openai-ai-guard.js +269 -0
- package/packages/datadog-instrumentations/src/helpers/promise-instrumentor.js +42 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -3
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/azure-cosmos.js +50 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/langgraph.js +4 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +85 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +37 -236
- package/packages/datadog-instrumentations/src/hono.js +54 -3
- package/packages/datadog-instrumentations/src/http/server.js +9 -4
- package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +163 -0
- package/packages/datadog-instrumentations/src/jest.js +360 -150
- package/packages/datadog-instrumentations/src/kafkajs.js +120 -16
- package/packages/datadog-instrumentations/src/mocha/main.js +128 -17
- package/packages/datadog-instrumentations/src/nats.js +182 -0
- package/packages/datadog-instrumentations/src/nyc.js +38 -1
- package/packages/datadog-instrumentations/src/openai.js +33 -18
- package/packages/datadog-instrumentations/src/oracledb.js +6 -1
- package/packages/datadog-instrumentations/src/pino.js +17 -5
- package/packages/datadog-instrumentations/src/playwright.js +515 -292
- package/packages/datadog-instrumentations/src/router.js +76 -32
- package/packages/datadog-instrumentations/src/stripe.js +1 -1
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/tracing.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +218 -4
- package/packages/datadog-plugin-azure-cosmos/src/index.js +144 -0
- package/packages/datadog-plugin-azure-event-hubs/src/producer.js +1 -1
- package/packages/datadog-plugin-azure-functions/src/index.js +5 -2
- package/packages/datadog-plugin-azure-service-bus/src/producer.js +1 -1
- package/packages/datadog-plugin-bunyan/src/index.js +28 -0
- package/packages/datadog-plugin-cucumber/src/index.js +17 -3
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +199 -28
- package/packages/datadog-plugin-cypress/src/support.js +69 -1
- package/packages/datadog-plugin-dns/src/lookup.js +8 -6
- package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +1 -1
- package/packages/datadog-plugin-graphql/src/execute.js +2 -0
- package/packages/datadog-plugin-graphql/src/resolve.js +64 -67
- package/packages/datadog-plugin-http/src/server.js +40 -15
- package/packages/datadog-plugin-jest/src/index.js +11 -3
- package/packages/datadog-plugin-jest/src/util.js +15 -8
- package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +1 -1
- package/packages/datadog-plugin-kafkajs/src/producer.js +3 -0
- package/packages/datadog-plugin-langgraph/src/stream.js +1 -1
- package/packages/datadog-plugin-mocha/src/index.js +19 -4
- package/packages/datadog-plugin-mongodb-core/src/index.js +281 -40
- package/packages/datadog-plugin-nats/src/consumer.js +43 -0
- package/packages/datadog-plugin-nats/src/index.js +20 -0
- package/packages/datadog-plugin-nats/src/producer.js +62 -0
- package/packages/datadog-plugin-nats/src/util.js +33 -0
- package/packages/datadog-plugin-next/src/index.js +5 -3
- package/packages/datadog-plugin-openai/src/tracing.js +15 -2
- package/packages/datadog-plugin-oracledb/src/index.js +13 -2
- package/packages/datadog-plugin-pino/src/index.js +42 -0
- package/packages/datadog-plugin-playwright/src/index.js +4 -4
- package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -1
- package/packages/datadog-plugin-rhea/src/producer.js +1 -1
- package/packages/datadog-plugin-router/src/index.js +33 -44
- package/packages/datadog-plugin-selenium/src/index.js +1 -1
- package/packages/datadog-plugin-vitest/src/index.js +5 -13
- package/packages/datadog-plugin-winston/src/index.js +30 -0
- package/packages/datadog-shimmer/src/shimmer.js +33 -40
- package/packages/dd-trace/src/aiguard/index.js +1 -1
- package/packages/dd-trace/src/aiguard/sdk.js +1 -1
- package/packages/dd-trace/src/appsec/api_security_sampler.js +1 -1
- package/packages/dd-trace/src/appsec/index.js +1 -1
- package/packages/dd-trace/src/appsec/reporter.js +5 -6
- package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
- package/packages/dd-trace/src/appsec/sdk/utils.js +1 -1
- package/packages/dd-trace/src/appsec/user_tracking.js +5 -4
- package/packages/dd-trace/src/baggage.js +7 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +0 -1
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +25 -13
- package/packages/dd-trace/src/ci-visibility/test-optimization-cache.js +70 -6
- package/packages/dd-trace/src/config/generated-config-types.d.ts +6 -2
- package/packages/dd-trace/src/config/supported-configurations.json +27 -8
- package/packages/dd-trace/src/datastreams/writer.js +2 -4
- package/packages/dd-trace/src/debugger/devtools_client/condition.js +5 -8
- package/packages/dd-trace/src/encode/0.4.js +124 -108
- package/packages/dd-trace/src/encode/0.5.js +114 -26
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +31 -23
- package/packages/dd-trace/src/encode/agentless-json.js +4 -2
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +32 -13
- package/packages/dd-trace/src/encode/span-stats.js +16 -16
- package/packages/dd-trace/src/encode/tags-processors.js +16 -0
- package/packages/dd-trace/src/id.js +15 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +92 -6
- package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +43 -21
- package/packages/dd-trace/src/llmobs/plugins/genai/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +9 -7
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +0 -16
- package/packages/dd-trace/src/llmobs/span_processor.js +3 -3
- package/packages/dd-trace/src/llmobs/tagger.js +9 -1
- package/packages/dd-trace/src/llmobs/telemetry.js +1 -1
- package/packages/dd-trace/src/llmobs/util.js +66 -3
- package/packages/dd-trace/src/log/index.js +1 -1
- package/packages/dd-trace/src/msgpack/chunk.js +394 -10
- package/packages/dd-trace/src/msgpack/index.js +96 -2
- package/packages/dd-trace/src/openfeature/encoding.js +70 -0
- package/packages/dd-trace/src/openfeature/flagging_provider.js +20 -0
- package/packages/dd-trace/src/openfeature/span-enrichment-hook.js +143 -0
- package/packages/dd-trace/src/openfeature/span-enrichment.js +149 -0
- package/packages/dd-trace/src/opentelemetry/span-helpers.js +4 -3
- package/packages/dd-trace/src/opentelemetry/span.js +1 -1
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +22 -3
- package/packages/dd-trace/src/opentracing/propagation/log.js +18 -7
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +64 -77
- package/packages/dd-trace/src/opentracing/span.js +59 -19
- package/packages/dd-trace/src/opentracing/span_context.js +50 -3
- package/packages/dd-trace/src/plugins/ci_plugin.js +20 -20
- package/packages/dd-trace/src/plugins/database.js +7 -6
- package/packages/dd-trace/src/plugins/index.js +4 -0
- package/packages/dd-trace/src/plugins/log_injection.js +56 -0
- package/packages/dd-trace/src/plugins/log_plugin.js +3 -48
- package/packages/dd-trace/src/plugins/outbound.js +1 -1
- package/packages/dd-trace/src/plugins/plugin.js +15 -17
- package/packages/dd-trace/src/plugins/tracing.js +43 -5
- package/packages/dd-trace/src/plugins/util/test.js +236 -13
- package/packages/dd-trace/src/plugins/util/web.js +79 -65
- package/packages/dd-trace/src/priority_sampler.js +2 -2
- package/packages/dd-trace/src/profiling/config.js +10 -23
- package/packages/dd-trace/src/profiling/exporters/agent.js +11 -10
- package/packages/dd-trace/src/profiling/profiler.js +21 -11
- package/packages/dd-trace/src/profiling/profilers/wall.js +12 -7
- package/packages/dd-trace/src/sampling_rule.js +7 -7
- package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +10 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +8 -0
- package/packages/dd-trace/src/service-naming/source-resolver.js +46 -0
- package/packages/dd-trace/src/span_format.js +190 -58
- package/packages/dd-trace/src/spanleak.js +1 -1
- package/packages/dd-trace/src/standalone/index.js +3 -3
- package/packages/dd-trace/src/tagger.js +0 -2
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +70 -39
- package/vendor/dist/@datadog/sketches-js/LICENSE +10 -36
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/packages/dd-trace/src/msgpack/encoder.js +0 -308
- package/packages/dd-trace/src/plugins/structured_log_plugin.js +0 -9
|
@@ -8,7 +8,6 @@ const { getCompileToRegexp } = require('./path-to-regexp')
|
|
|
8
8
|
const {
|
|
9
9
|
getRouterMountPaths,
|
|
10
10
|
joinPath,
|
|
11
|
-
getLayerMatchers,
|
|
12
11
|
setLayerMatchers,
|
|
13
12
|
isAppMounted,
|
|
14
13
|
setRouterMountPath,
|
|
@@ -42,34 +41,47 @@ function createWrapRouterMethod (name, compile) {
|
|
|
42
41
|
const nextChannel = channel(`apm:${name}:middleware:next`)
|
|
43
42
|
const routeAddedChannel = channel(`apm:${name}:route:added`)
|
|
44
43
|
|
|
45
|
-
function wrapLayerHandle (layer, original) {
|
|
46
|
-
|
|
44
|
+
function wrapLayerHandle (layer, original, matchers) {
|
|
45
|
+
// Resolve `name` once at wrap time: cached on the original for any code
|
|
46
|
+
// that reads `_name`, captured in the closure so the per-call body avoids
|
|
47
|
+
// the property-lookup / `||` fallback.
|
|
48
|
+
const name = original._name || layer.name || original.name
|
|
49
|
+
original._name = name
|
|
50
|
+
|
|
51
|
+
// Wrap-time matcher analysis. The single-pattern case yields a constant
|
|
52
|
+
// route; only multi-pattern stacks need a per-request layer.path match.
|
|
53
|
+
let captureRoute
|
|
54
|
+
let needMultiMatch = false
|
|
55
|
+
if (matchers.length !== 0 && !isFastStar(layer, matchers) && !isFastSlash(layer, matchers)) {
|
|
56
|
+
if (matchers.length === 1) {
|
|
57
|
+
captureRoute = matchers[0].path
|
|
58
|
+
} else {
|
|
59
|
+
needMultiMatch = true
|
|
60
|
+
}
|
|
61
|
+
}
|
|
47
62
|
|
|
48
|
-
|
|
49
|
-
|
|
63
|
+
// Split by arity: router only ever dispatches 3-arg request handlers
|
|
64
|
+
// through `Layer.handleRequest` and 4-arg error handlers through
|
|
65
|
+
// `Layer.handleError`. Specialising lets the per-call body use named
|
|
66
|
+
// parameters and `.call`, avoiding the rest-spread Array allocation that
|
|
67
|
+
// the unified shape forced on every middleware invocation.
|
|
68
|
+
return original.length === 4
|
|
69
|
+
? shimmer.wrapFunction(original, errorHandlerLayerWrap(layer, name, captureRoute, needMultiMatch, matchers))
|
|
70
|
+
: shimmer.wrapFunction(original, requestHandlerLayerWrap(layer, name, captureRoute, needMultiMatch, matchers))
|
|
71
|
+
}
|
|
50
72
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const req = args[args.length > 3 ? 1 : 0]
|
|
55
|
-
const next = args[lastIndex]
|
|
73
|
+
function requestHandlerLayerWrap (layer, name, captureRoute, needMultiMatch, matchers) {
|
|
74
|
+
return original => function (req, res, next) {
|
|
75
|
+
if (!enterChannel.hasSubscribers) return original.call(this, req, res, next)
|
|
56
76
|
|
|
57
|
-
|
|
58
|
-
args[lastIndex] = wrapNext(req, next)
|
|
59
|
-
}
|
|
77
|
+
const wrappedNext = typeof next === 'function' ? wrapNext(req, next) : next
|
|
60
78
|
|
|
61
|
-
let route
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
} else {
|
|
68
|
-
for (const matcher of matchers) {
|
|
69
|
-
if (matcher.regex?.test(layer.path)) {
|
|
70
|
-
route = matcher.path
|
|
71
|
-
break
|
|
72
|
-
}
|
|
79
|
+
let route = captureRoute
|
|
80
|
+
if (needMultiMatch) {
|
|
81
|
+
for (const matcher of matchers) {
|
|
82
|
+
if (matcher.regex?.test(layer.path)) {
|
|
83
|
+
route = matcher.path
|
|
84
|
+
break
|
|
73
85
|
}
|
|
74
86
|
}
|
|
75
87
|
}
|
|
@@ -77,7 +89,7 @@ function createWrapRouterMethod (name, compile) {
|
|
|
77
89
|
enterChannel.publish({ name, req, route, layer })
|
|
78
90
|
|
|
79
91
|
try {
|
|
80
|
-
return original.
|
|
92
|
+
return original.call(this, req, res, wrappedNext)
|
|
81
93
|
} catch (error) {
|
|
82
94
|
errorChannel.publish({ req, error })
|
|
83
95
|
nextChannel.publish({ req })
|
|
@@ -87,15 +99,47 @@ function createWrapRouterMethod (name, compile) {
|
|
|
87
99
|
} finally {
|
|
88
100
|
exitChannel.publish({ req })
|
|
89
101
|
}
|
|
90
|
-
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function errorHandlerLayerWrap (layer, name, captureRoute, needMultiMatch, matchers) {
|
|
106
|
+
return original => function (error, req, res, next) {
|
|
107
|
+
if (!enterChannel.hasSubscribers) return original.call(this, error, req, res, next)
|
|
108
|
+
|
|
109
|
+
const wrappedNext = typeof next === 'function' ? wrapNext(req, next) : next
|
|
110
|
+
|
|
111
|
+
let route = captureRoute
|
|
112
|
+
if (needMultiMatch) {
|
|
113
|
+
for (const matcher of matchers) {
|
|
114
|
+
if (matcher.regex?.test(layer.path)) {
|
|
115
|
+
route = matcher.path
|
|
116
|
+
break
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
enterChannel.publish({ name, req, route, layer })
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
return original.call(this, error, req, res, wrappedNext)
|
|
125
|
+
} catch (caught) {
|
|
126
|
+
errorChannel.publish({ req, error: caught })
|
|
127
|
+
nextChannel.publish({ req })
|
|
128
|
+
finishChannel.publish({ req })
|
|
129
|
+
|
|
130
|
+
throw caught
|
|
131
|
+
} finally {
|
|
132
|
+
exitChannel.publish({ req })
|
|
133
|
+
}
|
|
134
|
+
}
|
|
91
135
|
}
|
|
92
136
|
|
|
93
137
|
function wrapStack (layers, matchers) {
|
|
94
138
|
for (const layer of layers) {
|
|
95
139
|
if (layer.__handle) { // express-async-errors
|
|
96
|
-
layer.__handle = wrapLayerHandle(layer, layer.__handle)
|
|
140
|
+
layer.__handle = wrapLayerHandle(layer, layer.__handle, matchers)
|
|
97
141
|
} else {
|
|
98
|
-
layer.handle = wrapLayerHandle(layer, layer.handle)
|
|
142
|
+
layer.handle = wrapLayerHandle(layer, layer.handle, matchers)
|
|
99
143
|
}
|
|
100
144
|
|
|
101
145
|
setLayerMatchers(layer, matchers)
|
|
@@ -258,15 +302,15 @@ addHook({ name: 'router', versions: ['>=2'] }, Router => {
|
|
|
258
302
|
|
|
259
303
|
shimmer.wrap(router, 'handle', function wrapHandle (originalHandle) {
|
|
260
304
|
return function wrappedHandle (req, res, next) {
|
|
261
|
-
const abortController = new AbortController()
|
|
262
|
-
|
|
263
305
|
if (queryParserReadCh.hasSubscribers && req) {
|
|
306
|
+
const abortController = new AbortController()
|
|
307
|
+
|
|
264
308
|
queryParserReadCh.publish({ req, res, query: req.query, abortController })
|
|
265
309
|
|
|
266
310
|
if (abortController.signal.aborted) return
|
|
267
311
|
}
|
|
268
312
|
|
|
269
|
-
return originalHandle.
|
|
313
|
+
return originalHandle.call(this, req, res, next)
|
|
270
314
|
}
|
|
271
315
|
})
|
|
272
316
|
|
|
@@ -97,7 +97,7 @@ function wrapStripe (Stripe) {
|
|
|
97
97
|
|
|
98
98
|
addHook({
|
|
99
99
|
name: 'stripe',
|
|
100
|
-
versions: ['9
|
|
100
|
+
versions: ['>=9 <22'],
|
|
101
101
|
}, Stripe => shimmer.wrapFunction(Stripe, wrapLegacyStripe))
|
|
102
102
|
|
|
103
103
|
addHook({
|
|
@@ -140,7 +140,7 @@ class SchemaExtractor {
|
|
|
140
140
|
return
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
if (span.context().
|
|
143
|
+
if (span.context().getTag(SCHEMA_TYPE) && operation === 'serialization') {
|
|
144
144
|
// we have already added a schema to this span, this call is an encode of nested schema types
|
|
145
145
|
return
|
|
146
146
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const BaseAwsSdkPlugin = require('../../base')
|
|
4
4
|
const { parseModelId } = require('./utils')
|
|
5
5
|
|
|
6
|
-
const enabledOperations = new Set(['invokeModel', 'invokeModelWithResponseStream'])
|
|
6
|
+
const enabledOperations = new Set(['invokeModel', 'invokeModelWithResponseStream', 'converse', 'converseStream'])
|
|
7
7
|
|
|
8
8
|
class BedrockRuntime extends BaseAwsSdkPlugin {
|
|
9
9
|
static id = 'bedrockruntime'
|
|
@@ -131,6 +131,7 @@ class Generation {
|
|
|
131
131
|
outputTokens,
|
|
132
132
|
cacheReadTokens,
|
|
133
133
|
cacheWriteTokens,
|
|
134
|
+
messages,
|
|
134
135
|
} = {}) {
|
|
135
136
|
// stringify message as it could be a single generated message as well as a list of embeddings
|
|
136
137
|
this.message = typeof message === 'string' ? message : JSON.stringify(message) || ''
|
|
@@ -143,6 +144,7 @@ class Generation {
|
|
|
143
144
|
cacheReadTokens,
|
|
144
145
|
cacheWriteTokens,
|
|
145
146
|
}
|
|
147
|
+
this.messages = messages ?? [{ content: this.message, role: this.role }]
|
|
146
148
|
}
|
|
147
149
|
}
|
|
148
150
|
|
|
@@ -401,10 +403,7 @@ function extractTextAndResponseReason (response, provider, modelName) {
|
|
|
401
403
|
message: output.message?.content[0]?.text ?? 'Unsupported content type',
|
|
402
404
|
finishReason: body.stopReason,
|
|
403
405
|
role: output.message?.role,
|
|
404
|
-
|
|
405
|
-
outputTokens: body.usage?.outputTokens,
|
|
406
|
-
cacheReadInputTokenCount: body.usage?.cacheReadInputTokenCount,
|
|
407
|
-
cacheWriteInputTokenCount: body.usage?.cacheWriteInputTokenCount,
|
|
406
|
+
...buildUsage(body.usage),
|
|
408
407
|
})
|
|
409
408
|
}
|
|
410
409
|
break
|
|
@@ -476,6 +475,216 @@ function extractTextAndResponseReason (response, provider, modelName) {
|
|
|
476
475
|
return new Generation()
|
|
477
476
|
}
|
|
478
477
|
|
|
478
|
+
/**
|
|
479
|
+
* Convert a Converse content-block array to an LLMObs message array.
|
|
480
|
+
*
|
|
481
|
+
* @param {string} role
|
|
482
|
+
* @param {Array<object>} contentBlocks
|
|
483
|
+
* @returns {{ content?: string, role: string, toolCalls?: Array, toolResults?: Array } | undefined}
|
|
484
|
+
*/
|
|
485
|
+
function extractMessagesFromConverseContent (role, contentBlocks) {
|
|
486
|
+
let content = ''
|
|
487
|
+
const toolCalls = []
|
|
488
|
+
const toolResults = []
|
|
489
|
+
|
|
490
|
+
for (const block of contentBlocks || []) {
|
|
491
|
+
if (block == null || typeof block !== 'object') continue
|
|
492
|
+
if (typeof block.text === 'string') {
|
|
493
|
+
content += block.text
|
|
494
|
+
} else if (block.toolUse) {
|
|
495
|
+
toolCalls.push(buildToolCall(block.toolUse))
|
|
496
|
+
} else if (block.toolResult) {
|
|
497
|
+
toolResults.push(buildToolResult(block.toolResult))
|
|
498
|
+
} else {
|
|
499
|
+
content += `[Unsupported content type: ${getContentBlockType(block)}]`
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
if (!content && toolCalls.length === 0 && toolResults.length === 0) return
|
|
504
|
+
|
|
505
|
+
const message = { role }
|
|
506
|
+
if (content) message.content = content
|
|
507
|
+
if (toolCalls.length > 0) message.toolCalls = toolCalls
|
|
508
|
+
if (toolResults.length > 0) message.toolResults = toolResults
|
|
509
|
+
return message
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Resolve a Converse `ContentBlock`'s member type. The block is a key-presence
|
|
514
|
+
* tagged union (no `type` discriminator), so the active member is its sole own
|
|
515
|
+
* key. For forward-compat `$unknown` members the real type is the first element
|
|
516
|
+
* of the `[name, value]` tuple.
|
|
517
|
+
*
|
|
518
|
+
* @param {object} block
|
|
519
|
+
* @returns {string}
|
|
520
|
+
*/
|
|
521
|
+
function getContentBlockType (block) {
|
|
522
|
+
const key = Object.keys(block)[0]
|
|
523
|
+
if (key === '$unknown') return block.$unknown?.[0] ?? 'unknown'
|
|
524
|
+
return key ?? 'unknown'
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// Always emit at least one output message so downstream tagging has a role to attach to.
|
|
528
|
+
function toOutputMessages (role, contentBlocks) {
|
|
529
|
+
const message = extractMessagesFromConverseContent(role, contentBlocks)
|
|
530
|
+
return message ? [message] : [{ role, content: '' }]
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
function buildToolCall ({ name, input, toolUseId }) {
|
|
534
|
+
return { name: name ?? '', arguments: input ?? {}, toolId: toolUseId ?? '', type: 'toolUse' }
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
function parseToolInput (inputStr) {
|
|
538
|
+
try {
|
|
539
|
+
return JSON.parse(inputStr)
|
|
540
|
+
} catch {
|
|
541
|
+
log.warn('Failed to parse Converse stream toolUse.input JSON; emitting empty arguments')
|
|
542
|
+
return {}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function buildToolResult ({ toolUseId, content }) {
|
|
547
|
+
const result = (content || []).map(resolveToolResultItem).join('')
|
|
548
|
+
return { name: '', result, toolId: toolUseId ?? '', type: 'tool_result' }
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
function resolveToolResultItem (item) {
|
|
552
|
+
if (typeof item.text === 'string') return item.text
|
|
553
|
+
if (item.json != null) return JSON.stringify(item.json)
|
|
554
|
+
return `[Unsupported content type(s): ${getContentBlockType(item)}]`
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
function buildUsage (usage = {}) {
|
|
558
|
+
return {
|
|
559
|
+
inputTokens: usage.inputTokens,
|
|
560
|
+
outputTokens: usage.outputTokens,
|
|
561
|
+
cacheReadTokens: usage.cacheReadInputTokens ?? usage.cacheReadInputTokenCount,
|
|
562
|
+
cacheWriteTokens: usage.cacheWriteInputTokens ?? usage.cacheWriteInputTokenCount,
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Extract tool definitions from a Converse request's `toolConfig.tools`,
|
|
568
|
+
* mapping Bedrock's `toolSpec` shape to LLMObs `ToolDefinition` shape.
|
|
569
|
+
*
|
|
570
|
+
* @param {object} params - Converse request params with optional `toolConfig.tools[].toolSpec`.
|
|
571
|
+
* @returns {Array<{ name: string, description: string, schema: object }>}
|
|
572
|
+
*/
|
|
573
|
+
function extractConverseToolDefinitions (params) {
|
|
574
|
+
const toolDefinitions = []
|
|
575
|
+
for (const tool of params.toolConfig?.tools || []) {
|
|
576
|
+
const toolSpec = tool?.toolSpec
|
|
577
|
+
if (!toolSpec?.name) continue
|
|
578
|
+
toolDefinitions.push({
|
|
579
|
+
name: toolSpec.name,
|
|
580
|
+
description: toolSpec.description ?? '',
|
|
581
|
+
schema: toolSpec.inputSchema ?? {},
|
|
582
|
+
})
|
|
583
|
+
}
|
|
584
|
+
return toolDefinitions
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Extract request metadata + rendered input messages from a Converse /
|
|
589
|
+
* ConverseStream request.
|
|
590
|
+
*
|
|
591
|
+
* @param {{ modelId?: string, messages?: Array, system?: Array, inferenceConfig?: object, toolConfig?: object }} params
|
|
592
|
+
* @returns {RequestParams}
|
|
593
|
+
*/
|
|
594
|
+
function extractRequestParamsConverse (params) {
|
|
595
|
+
const prompt = []
|
|
596
|
+
for (const block of params.system || []) {
|
|
597
|
+
if (typeof block?.text === 'string') prompt.push({ content: block.text, role: 'system' })
|
|
598
|
+
}
|
|
599
|
+
for (const msg of params.messages || []) {
|
|
600
|
+
if (msg == null || typeof msg !== 'object') continue
|
|
601
|
+
const message = extractMessagesFromConverseContent(msg.role || 'user', msg.content)
|
|
602
|
+
if (message) prompt.push(message)
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
const { temperature, topP, maxTokens, stopSequences } = params.inferenceConfig || {}
|
|
606
|
+
return new RequestParams({ prompt, temperature, topP, maxTokens, stopSequences })
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Extract output messages + usage from a non-stream Converse response.
|
|
611
|
+
*
|
|
612
|
+
* @param {{ output?: { message?: { role?: string, content?: Array } }, stopReason?: string, usage?: object }} response
|
|
613
|
+
* @returns {Generation}
|
|
614
|
+
*/
|
|
615
|
+
function extractTextAndResponseReasonConverse (response) {
|
|
616
|
+
const outputMessage = response?.output?.message
|
|
617
|
+
const role = outputMessage?.role || 'assistant'
|
|
618
|
+
|
|
619
|
+
return new Generation({
|
|
620
|
+
role,
|
|
621
|
+
finishReason: response?.stopReason || '',
|
|
622
|
+
...buildUsage(response?.usage),
|
|
623
|
+
messages: toOutputMessages(role, outputMessage?.content),
|
|
624
|
+
})
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* Aggregate Converse stream events into a single output message + usage.
|
|
629
|
+
* One messageStart / messageStop pair per response, so one message out.
|
|
630
|
+
*
|
|
631
|
+
* Stream events describe the same content-block structure as the non-stream
|
|
632
|
+
* response, spread across start/delta chunks. We reassemble those chunks
|
|
633
|
+
* into a normalized content-block array and reuse the non-stream extractor.
|
|
634
|
+
*
|
|
635
|
+
* @param {Array<object>} chunks - Ordered ConverseStreamOutput events.
|
|
636
|
+
* @returns {Generation}
|
|
637
|
+
*/
|
|
638
|
+
function extractTextAndResponseReasonConverseFromStream (chunks) {
|
|
639
|
+
let role = 'assistant'
|
|
640
|
+
let stopReason = ''
|
|
641
|
+
let usage = {}
|
|
642
|
+
const blocksByIdx = new Map()
|
|
643
|
+
|
|
644
|
+
for (const chunk of chunks || []) {
|
|
645
|
+
if (chunk.messageStart?.role) {
|
|
646
|
+
role = chunk.messageStart.role
|
|
647
|
+
} else if (chunk.messageStop?.stopReason) {
|
|
648
|
+
stopReason = chunk.messageStop.stopReason
|
|
649
|
+
} else if (chunk.metadata?.usage) {
|
|
650
|
+
usage = chunk.metadata.usage
|
|
651
|
+
} else if (chunk.contentBlockStart?.start?.toolUse) {
|
|
652
|
+
const { contentBlockIndex, start: { toolUse } } = chunk.contentBlockStart
|
|
653
|
+
blocksByIdx.set(contentBlockIndex, {
|
|
654
|
+
toolUse: { toolUseId: toolUse.toolUseId, name: toolUse.name, inputStr: '' },
|
|
655
|
+
})
|
|
656
|
+
} else if (chunk.contentBlockDelta) {
|
|
657
|
+
const { contentBlockIndex, delta } = chunk.contentBlockDelta
|
|
658
|
+
if (typeof delta?.text === 'string') {
|
|
659
|
+
const block = blocksByIdx.get(contentBlockIndex) ?? {}
|
|
660
|
+
block.text = (block.text ?? '') + delta.text
|
|
661
|
+
blocksByIdx.set(contentBlockIndex, block)
|
|
662
|
+
} else if (typeof delta?.toolUse?.input === 'string') {
|
|
663
|
+
const block = blocksByIdx.get(contentBlockIndex) ?? { toolUse: { inputStr: '' } }
|
|
664
|
+
block.toolUse ??= { inputStr: '' }
|
|
665
|
+
block.toolUse.inputStr += delta.toolUse.input
|
|
666
|
+
blocksByIdx.set(contentBlockIndex, block)
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
const contentBlocks = [...blocksByIdx.keys()].sort((a, b) => a - b).map(i => {
|
|
672
|
+
const block = blocksByIdx.get(i)
|
|
673
|
+
if (block.toolUse) {
|
|
674
|
+
const { toolUseId, name, inputStr } = block.toolUse
|
|
675
|
+
block.toolUse = { toolUseId, name, input: parseToolInput(inputStr) }
|
|
676
|
+
}
|
|
677
|
+
return block
|
|
678
|
+
})
|
|
679
|
+
|
|
680
|
+
return new Generation({
|
|
681
|
+
role,
|
|
682
|
+
finishReason: stopReason,
|
|
683
|
+
...buildUsage(usage),
|
|
684
|
+
messages: toOutputMessages(role, contentBlocks),
|
|
685
|
+
})
|
|
686
|
+
}
|
|
687
|
+
|
|
479
688
|
module.exports = {
|
|
480
689
|
Generation,
|
|
481
690
|
RequestParams,
|
|
@@ -483,5 +692,10 @@ module.exports = {
|
|
|
483
692
|
parseModelId,
|
|
484
693
|
extractRequestParams,
|
|
485
694
|
extractTextAndResponseReason,
|
|
695
|
+
extractMessagesFromConverseContent,
|
|
696
|
+
extractConverseToolDefinitions,
|
|
697
|
+
extractRequestParamsConverse,
|
|
698
|
+
extractTextAndResponseReasonConverse,
|
|
699
|
+
extractTextAndResponseReasonConverseFromStream,
|
|
486
700
|
PROVIDER,
|
|
487
701
|
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { storage } = require('../../datadog-core')
|
|
4
|
+
|
|
5
|
+
const DatabasePlugin = require('../../dd-trace/src/plugins/database')
|
|
6
|
+
|
|
7
|
+
class AzureCosmosPlugin extends DatabasePlugin {
|
|
8
|
+
static id = 'azure-cosmos'
|
|
9
|
+
// Channel prefix determines how the plugin subscribes to instrumentation events.
|
|
10
|
+
// Three patterns exist — set `static prefix` explicitly based on instrumentation type:
|
|
11
|
+
//
|
|
12
|
+
// Orchestrion: static prefix = 'tracing:orchestrion:<npm-package>:<channelName>'
|
|
13
|
+
// Shimmer + tracingChannel: static prefix = 'tracing:apm:<name>:<operation>'
|
|
14
|
+
// Shimmer + manual channels: omit prefix — defaults to `apm:${id}:${operation}`
|
|
15
|
+
static prefix = 'tracing:orchestrion:@azure/cosmos:executePlugins'
|
|
16
|
+
static peerServicePrecursors = ['db.name']
|
|
17
|
+
|
|
18
|
+
operationName () {
|
|
19
|
+
return 'cosmosdb.query'
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
asyncEnd (ctx) {
|
|
23
|
+
if (!ctx.span) return
|
|
24
|
+
const span = ctx.currentStore?.span
|
|
25
|
+
if (span) {
|
|
26
|
+
const result = ctx.result
|
|
27
|
+
if (result?.code) span.setTag('db.response.status_code', (result.code).toString())
|
|
28
|
+
if (result?.substatus) span.setTag('cosmosdb.response.sub_status_code', result.substatus)
|
|
29
|
+
span.finish()
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
error (ctx) {
|
|
34
|
+
if (!ctx.span) return
|
|
35
|
+
const span = ctx.currentStore?.span
|
|
36
|
+
if (span) {
|
|
37
|
+
const error = ctx.error
|
|
38
|
+
this.addError(error, span)
|
|
39
|
+
if (error?.code) span.setTag('db.response.status_code', (error.code).toString())
|
|
40
|
+
if (error?.substatus) span.setTag('cosmosdb.response.sub_status_code', error.substatus)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
bindStart (ctx) {
|
|
45
|
+
const requestContext = ctx.arguments[1]
|
|
46
|
+
const resource = this.getResource(requestContext)
|
|
47
|
+
const { dbName, containerName } = this.getDbInfo(requestContext)
|
|
48
|
+
const connectionMode = this.getConnectionMode(requestContext)
|
|
49
|
+
const { outHost, userAgent } = this.getHttpInfo(requestContext)
|
|
50
|
+
const pluginOn = ctx.arguments[3]
|
|
51
|
+
|
|
52
|
+
if (pluginOn != null && requestContext.operationType != null && requestContext.resourceType != null) {
|
|
53
|
+
const operationType = requestContext.operationType
|
|
54
|
+
const resourceType = requestContext.resourceType
|
|
55
|
+
// only trace operations not requests (pluginOn)
|
|
56
|
+
// trace requests only if they are read or query operations not on docs
|
|
57
|
+
// prevents doubled read spans for createIfNotExists calls
|
|
58
|
+
if (pluginOn === 'request' && ((operationType !== 'read' && operationType !== 'query') ||
|
|
59
|
+
(operationType === 'read' && resourceType !== 'docs'))) {
|
|
60
|
+
return storage('legacy').getStore()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// separately, skip tracing read requests without a path, these don't
|
|
64
|
+
// represent CRUD operations on a resource we care about
|
|
65
|
+
// not returning current store because we don't want the child http.request spans
|
|
66
|
+
// to be created
|
|
67
|
+
if (operationType === 'read' && requestContext.path === '') {
|
|
68
|
+
return { noop: true }
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const span = this.startSpan(this.operationName(), {
|
|
73
|
+
resource,
|
|
74
|
+
type: 'cosmosdb',
|
|
75
|
+
kind: 'client',
|
|
76
|
+
meta: {
|
|
77
|
+
component: 'azure_cosmos',
|
|
78
|
+
'db.system': 'cosmosdb',
|
|
79
|
+
'db.name': dbName,
|
|
80
|
+
'cosmosdb.container': containerName,
|
|
81
|
+
'cosmosdb.connection.mode': connectionMode,
|
|
82
|
+
'http.useragent': userAgent,
|
|
83
|
+
'out.host': outHost,
|
|
84
|
+
},
|
|
85
|
+
}, ctx)
|
|
86
|
+
|
|
87
|
+
ctx.span = span
|
|
88
|
+
return ctx.currentStore
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
getResource (requestContext) {
|
|
92
|
+
const path = requestContext.path
|
|
93
|
+
const parts = path.split('/')
|
|
94
|
+
let modified = false
|
|
95
|
+
for (let i = 2; i < parts.length; i += 2) {
|
|
96
|
+
if (parts[i].length > 0 && parts[i - 1] !== 'dbs' && parts[i - 1] !== 'colls') {
|
|
97
|
+
parts[i] = '?'
|
|
98
|
+
modified = true
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return `${requestContext.operationType} ${modified ? parts.join('/') : path}`
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
getDbInfo (requestContext) {
|
|
106
|
+
let dbName = null
|
|
107
|
+
let containerName = null
|
|
108
|
+
|
|
109
|
+
if (requestContext.operationType === 'create' && requestContext.resourceType === 'dbs' &&
|
|
110
|
+
requestContext.body != null && requestContext.body.id != null) {
|
|
111
|
+
dbName = requestContext.body.id
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
let resourceLink = requestContext.path
|
|
115
|
+
if (resourceLink?.length > 1 && resourceLink.startsWith('/')) {
|
|
116
|
+
resourceLink = resourceLink.slice(1)
|
|
117
|
+
const parts = resourceLink.split('/')
|
|
118
|
+
if (parts.length > 0 && parts[0].toLowerCase() === 'dbs' && parts.length >= 2) {
|
|
119
|
+
dbName = parts[1]
|
|
120
|
+
if (parts.length >= 4 && parts[2].toLowerCase() === 'colls' && parts[3] !== '') {
|
|
121
|
+
containerName = parts[3]
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return { dbName, containerName }
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
getConnectionMode (requestContext) {
|
|
130
|
+
const mode = requestContext.client?.connectionPolicy?.connectionMode
|
|
131
|
+
if (mode === 0) {
|
|
132
|
+
return 'gateway'
|
|
133
|
+
}
|
|
134
|
+
return 'direct'
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
getHttpInfo (requestContext) {
|
|
138
|
+
const outHost = requestContext.client?.cosmosClientOptions?.endpoint
|
|
139
|
+
const userAgent = requestContext.headers?.['User-Agent']
|
|
140
|
+
return { outHost, userAgent }
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
module.exports = AzureCosmosPlugin
|
|
@@ -13,6 +13,7 @@ const triggerMap = {
|
|
|
13
13
|
serviceBusQueue: 'ServiceBus',
|
|
14
14
|
serviceBusTopic: 'ServiceBus',
|
|
15
15
|
eventHub: 'EventHubs',
|
|
16
|
+
cosmosDB: 'CosmosDB',
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
class AzureFunctionsPlugin extends TracingPlugin {
|
|
@@ -53,7 +54,7 @@ class AzureFunctionsPlugin extends TracingPlugin {
|
|
|
53
54
|
)
|
|
54
55
|
|
|
55
56
|
span._integrationName = 'azure-functions'
|
|
56
|
-
span.context().
|
|
57
|
+
span.context().setTag('component', 'azure-functions')
|
|
57
58
|
span.addTags(meta)
|
|
58
59
|
webContext.span = span
|
|
59
60
|
webContext.azureFunctionCtx = ctx
|
|
@@ -127,6 +128,8 @@ function getMetaForTrigger ({ functionName, methodName, invocationContext }) {
|
|
|
127
128
|
'resource.name': `EventHubs ${functionName}`,
|
|
128
129
|
'span.kind': 'consumer',
|
|
129
130
|
}
|
|
131
|
+
} else if (triggerMap[methodName] === 'CosmosDB') {
|
|
132
|
+
meta['resource.name'] = `CosmosDB ${functionName}`
|
|
130
133
|
}
|
|
131
134
|
|
|
132
135
|
return meta
|
|
@@ -155,7 +158,7 @@ function setSpanLinks (triggerType, tracer, span, ctx) {
|
|
|
155
158
|
if (!props || Object.keys(props).length === 0) return
|
|
156
159
|
const spanContext = tracer.extract('text_map', props)
|
|
157
160
|
if (spanContext) {
|
|
158
|
-
span.addLink(spanContext)
|
|
161
|
+
span.addLink({ context: spanContext })
|
|
159
162
|
}
|
|
160
163
|
}
|
|
161
164
|
|
|
@@ -1,8 +1,36 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { buildLogHolder } = require('../../dd-trace/src/plugins/log_injection')
|
|
3
4
|
const LogPlugin = require('../../dd-trace/src/plugins/log_plugin')
|
|
4
5
|
|
|
5
6
|
class BunyanPlugin extends LogPlugin {
|
|
6
7
|
static id = 'bunyan'
|
|
8
|
+
|
|
9
|
+
constructor (...args) {
|
|
10
|
+
super(...args)
|
|
11
|
+
this.addSub('apm:bunyan:log', (arg) => this.handleLog(arg))
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Inject `dd` directly on the record bunyan hands us. bunyan builds the
|
|
16
|
+
* record inside `mkRecord` via `objCopy(log.fields)` and then copies the
|
|
17
|
+
* caller's fields onto the result, so the `rec` object that flows
|
|
18
|
+
* through `_emit` is always bunyan-owned, has `Object.prototype` for its
|
|
19
|
+
* prototype, and is never the caller's input directly. Mutating it adds
|
|
20
|
+
* `dd` for every consumer (JSON streams via `JSON.stringify(rec)`, raw
|
|
21
|
+
* streams via the record reference) without paying for a Proxy view.
|
|
22
|
+
*
|
|
23
|
+
* @param {{ message: object }} arg
|
|
24
|
+
*/
|
|
25
|
+
handleLog (arg) {
|
|
26
|
+
const rec = arg.message
|
|
27
|
+
if (rec === null || typeof rec !== 'object' || Object.hasOwn(rec, 'dd')) return
|
|
28
|
+
|
|
29
|
+
const logHolder = buildLogHolder(this.tracer)
|
|
30
|
+
if (!logHolder) return
|
|
31
|
+
|
|
32
|
+
rec.dd = logHolder.dd
|
|
33
|
+
}
|
|
7
34
|
}
|
|
35
|
+
|
|
8
36
|
module.exports = BunyanPlugin
|