dd-trace 5.66.0 → 5.68.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 +0 -3
- package/README.md +0 -2
- package/ci/init.js +52 -54
- package/ext/exporters.d.ts +2 -1
- package/ext/exporters.js +2 -1
- package/index.d.ts +85 -2
- package/initialize.mjs +1 -1
- package/package.json +8 -11
- package/packages/datadog-esbuild/index.js +56 -0
- package/packages/datadog-instrumentations/src/aws-sdk.js +42 -4
- package/packages/datadog-instrumentations/src/azure-functions.js +1 -1
- package/packages/datadog-instrumentations/src/azure-service-bus.js +1 -1
- package/packages/datadog-instrumentations/src/cassandra-driver.js +2 -2
- package/packages/datadog-instrumentations/src/connect.js +6 -2
- package/packages/datadog-instrumentations/src/cucumber.js +31 -6
- package/packages/datadog-instrumentations/src/express.js +5 -6
- package/packages/datadog-instrumentations/src/fastify.js +3 -3
- package/packages/datadog-instrumentations/src/helpers/hook.js +28 -15
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +11 -2
- package/packages/datadog-instrumentations/src/helpers/register.js +10 -3
- package/packages/datadog-instrumentations/src/http2/client.js +1 -0
- package/packages/datadog-instrumentations/src/http2/server.js +0 -1
- package/packages/datadog-instrumentations/src/ioredis.js +12 -1
- package/packages/datadog-instrumentations/src/jest.js +48 -36
- package/packages/datadog-instrumentations/src/limitd-client.js +2 -1
- package/packages/datadog-instrumentations/src/mocha/main.js +15 -7
- package/packages/datadog-instrumentations/src/mocha/utils.js +3 -0
- package/packages/datadog-instrumentations/src/mongoose.js +2 -1
- package/packages/datadog-instrumentations/src/oracledb.js +19 -13
- package/packages/datadog-instrumentations/src/pg.js +9 -5
- package/packages/datadog-instrumentations/src/pino.js +18 -6
- package/packages/datadog-instrumentations/src/playwright.js +15 -1
- package/packages/datadog-instrumentations/src/sequelize.js +1 -1
- package/packages/datadog-instrumentations/src/vitest.js +155 -62
- package/packages/datadog-plugin-ai/src/tracing.js +3 -3
- package/packages/datadog-plugin-aws-sdk/src/base.js +23 -8
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/tracing.js +2 -2
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +101 -2
- package/packages/datadog-plugin-aws-sdk/src/util.js +1 -1
- package/packages/datadog-plugin-cucumber/src/index.js +4 -56
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +6 -2
- package/packages/datadog-plugin-cypress/src/support.js +4 -0
- package/packages/datadog-plugin-express/src/code_origin.js +2 -2
- package/packages/datadog-plugin-fastify/src/code_origin.js +1 -2
- package/packages/datadog-plugin-jest/src/index.js +0 -21
- package/packages/datadog-plugin-mocha/src/index.js +3 -57
- package/packages/datadog-plugin-mongodb-core/src/index.js +20 -7
- package/packages/datadog-plugin-playwright/src/index.js +11 -5
- package/packages/datadog-plugin-vitest/src/index.js +5 -1
- package/packages/datadog-plugin-ws/src/close.js +1 -1
- package/packages/datadog-plugin-ws/src/producer.js +6 -1
- package/packages/datadog-plugin-ws/src/receiver.js +6 -1
- package/packages/dd-trace/src/appsec/iast/security-controls/parser.js +1 -1
- package/packages/dd-trace/src/appsec/telemetry/waf.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -4
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +11 -3
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +10 -1
- package/packages/dd-trace/src/config.js +69 -304
- package/packages/dd-trace/src/config_defaults.js +186 -0
- package/packages/dd-trace/src/crashtracking/crashtracker.js +2 -1
- package/packages/dd-trace/src/datastreams/fnv.js +2 -2
- package/packages/dd-trace/src/datastreams/writer.js +3 -2
- package/packages/dd-trace/src/debugger/devtools_client/config.js +2 -1
- package/packages/dd-trace/src/dogstatsd.js +4 -3
- package/packages/dd-trace/src/encode/0.4.js +1 -5
- package/packages/dd-trace/src/exporter.js +1 -0
- package/packages/dd-trace/src/exporters/agent/index.js +3 -2
- package/packages/dd-trace/src/exporters/agent/writer.js +1 -1
- package/packages/dd-trace/src/exporters/common/agent-info-exporter.js +3 -2
- package/packages/dd-trace/src/exporters/common/request.js +2 -1
- package/packages/dd-trace/src/exporters/span-stats/index.js +3 -2
- package/packages/dd-trace/src/llmobs/constants/tags.js +2 -0
- package/packages/dd-trace/src/llmobs/index.js +7 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/index.js +4 -3
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +12 -1
- package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +40 -13
- package/packages/dd-trace/src/llmobs/plugins/openai.js +7 -1
- package/packages/dd-trace/src/llmobs/sdk.js +28 -0
- package/packages/dd-trace/src/llmobs/span_processor.js +124 -28
- package/packages/dd-trace/src/llmobs/tagger.js +8 -0
- package/packages/dd-trace/src/llmobs/telemetry.js +9 -2
- package/packages/dd-trace/src/log/index.js +28 -17
- package/packages/dd-trace/src/log/log.js +29 -5
- package/packages/dd-trace/src/log/writer.js +5 -5
- package/packages/dd-trace/src/noop/span.js +1 -0
- package/packages/dd-trace/src/opentelemetry/span.js +14 -3
- package/packages/dd-trace/src/opentracing/span.js +18 -4
- package/packages/dd-trace/src/plugin_manager.js +20 -2
- package/packages/dd-trace/src/plugins/ci_plugin.js +97 -3
- package/packages/dd-trace/src/plugins/index.js +2 -0
- package/packages/dd-trace/src/plugins/util/git-cache.js +129 -0
- package/packages/dd-trace/src/plugins/util/git.js +40 -26
- package/packages/dd-trace/src/plugins/util/test.js +37 -27
- package/packages/dd-trace/src/plugins/util/web.js +1 -1
- package/packages/dd-trace/src/profiler.js +4 -1
- package/packages/dd-trace/src/profiling/config.js +73 -42
- package/packages/dd-trace/src/profiling/profiler.js +3 -1
- package/packages/dd-trace/src/profiling/profilers/events.js +3 -8
- package/packages/dd-trace/src/profiling/profilers/space.js +1 -0
- package/packages/dd-trace/src/profiling/profilers/wall.js +196 -117
- package/packages/dd-trace/src/remote_config/capabilities.js +5 -0
- package/packages/dd-trace/src/remote_config/manager.js +3 -2
- package/packages/dd-trace/src/startup-log.js +2 -1
- package/packages/dd-trace/src/supported-configurations.json +3 -0
- package/packages/dd-trace/src/telemetry/logs/index.js +2 -2
- package/register.js +1 -1
|
@@ -23,9 +23,16 @@ const LLMObsTagger = require('./tagger')
|
|
|
23
23
|
const { channel } = require('dc-polyfill')
|
|
24
24
|
const evalMetricAppendCh = channel('llmobs:eval-metric:append')
|
|
25
25
|
const flushCh = channel('llmobs:writers:flush')
|
|
26
|
+
const registerUserSpanProcessorCh = channel('llmobs:register-processor')
|
|
26
27
|
const NoopLLMObs = require('./noop')
|
|
27
28
|
|
|
28
29
|
class LLMObs extends NoopLLMObs {
|
|
30
|
+
/**
|
|
31
|
+
* flag representing if a user span processor has been registered
|
|
32
|
+
* @type {boolean}
|
|
33
|
+
*/
|
|
34
|
+
#hasUserSpanProcessor = false
|
|
35
|
+
|
|
29
36
|
constructor (tracer, llmobsModule, config) {
|
|
30
37
|
super(tracer)
|
|
31
38
|
|
|
@@ -309,6 +316,27 @@ class LLMObs extends NoopLLMObs {
|
|
|
309
316
|
}
|
|
310
317
|
}
|
|
311
318
|
|
|
319
|
+
registerProcessor (processor) {
|
|
320
|
+
if (!this.enabled) return
|
|
321
|
+
|
|
322
|
+
if (this.#hasUserSpanProcessor) {
|
|
323
|
+
throw new Error(
|
|
324
|
+
'[LLMObs] Only one user span processor can be registered. ' +
|
|
325
|
+
'To register a new processor, deregister the existing processor first using `llmobs.deregisterProcessor()`.'
|
|
326
|
+
)
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
this.#hasUserSpanProcessor = true
|
|
330
|
+
registerUserSpanProcessorCh.publish(processor)
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
deregisterProcessor () {
|
|
334
|
+
if (!this.enabled) return
|
|
335
|
+
|
|
336
|
+
this.#hasUserSpanProcessor = false
|
|
337
|
+
registerUserSpanProcessorCh.publish(null)
|
|
338
|
+
}
|
|
339
|
+
|
|
312
340
|
submitEvaluation (llmobsSpanContext, options = {}) {
|
|
313
341
|
if (!this.enabled) return
|
|
314
342
|
|
|
@@ -34,25 +34,55 @@ const LLMObsTagger = require('./tagger')
|
|
|
34
34
|
const tracerVersion = require('../../../../package.json').version
|
|
35
35
|
const logger = require('../log')
|
|
36
36
|
|
|
37
|
+
const util = require('node:util')
|
|
38
|
+
|
|
39
|
+
class LLMObservabilitySpan {
|
|
40
|
+
constructor () {
|
|
41
|
+
this.input = []
|
|
42
|
+
this.output = []
|
|
43
|
+
|
|
44
|
+
this._tags = {}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
getTag (key) {
|
|
48
|
+
return this._tags[key]
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
37
52
|
class LLMObsSpanProcessor {
|
|
53
|
+
/** @type {import('../config')} */
|
|
54
|
+
#config
|
|
55
|
+
|
|
56
|
+
/** @type {((span: LLMObservabilitySpan) => LLMObservabilitySpan | null) | null} */
|
|
57
|
+
#userSpanProcessor
|
|
58
|
+
|
|
59
|
+
/** @type {import('./writers/spans')} */
|
|
60
|
+
#writer
|
|
61
|
+
|
|
38
62
|
constructor (config) {
|
|
39
|
-
this
|
|
63
|
+
this.#config = config
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
setUserSpanProcessor (userSpanProcessor) {
|
|
67
|
+
this.#userSpanProcessor = userSpanProcessor
|
|
40
68
|
}
|
|
41
69
|
|
|
42
70
|
setWriter (writer) {
|
|
43
|
-
this
|
|
71
|
+
this.#writer = writer
|
|
44
72
|
}
|
|
45
73
|
|
|
46
74
|
// TODO: instead of relying on the tagger's weakmap registry, can we use some namespaced storage correlation?
|
|
47
75
|
process ({ span }) {
|
|
48
|
-
if (!this.
|
|
76
|
+
if (!this.#config.llmobs.enabled) return
|
|
49
77
|
// if the span is not in our private tagger map, it is not an llmobs span
|
|
50
78
|
if (!LLMObsTagger.tagMap.has(span)) return
|
|
51
79
|
|
|
52
80
|
try {
|
|
53
81
|
const formattedEvent = this.format(span)
|
|
54
82
|
telemetry.incrementLLMObsSpanFinishedCount(span)
|
|
55
|
-
|
|
83
|
+
if (formattedEvent == null) return
|
|
84
|
+
|
|
85
|
+
this.#writer.append(formattedEvent)
|
|
56
86
|
} catch (e) {
|
|
57
87
|
// this should be a rare case
|
|
58
88
|
// we protect against unserializable properties in the format function, and in
|
|
@@ -65,6 +95,9 @@ class LLMObsSpanProcessor {
|
|
|
65
95
|
}
|
|
66
96
|
|
|
67
97
|
format (span) {
|
|
98
|
+
const llmObsSpan = new LLMObservabilitySpan()
|
|
99
|
+
let inputType, outputType
|
|
100
|
+
|
|
68
101
|
const spanTags = span.context()._tags
|
|
69
102
|
const mlObsTags = LLMObsTagger.tagMap.get(span)
|
|
70
103
|
|
|
@@ -78,26 +111,29 @@ class LLMObsSpanProcessor {
|
|
|
78
111
|
meta.model_name = mlObsTags[MODEL_NAME] || 'custom'
|
|
79
112
|
meta.model_provider = (mlObsTags[MODEL_PROVIDER] || 'custom').toLowerCase()
|
|
80
113
|
}
|
|
114
|
+
|
|
81
115
|
if (mlObsTags[METADATA]) {
|
|
82
|
-
this
|
|
116
|
+
this.#addObject(mlObsTags[METADATA], meta.metadata = {})
|
|
83
117
|
}
|
|
118
|
+
|
|
84
119
|
if (spanKind === 'llm' && mlObsTags[INPUT_MESSAGES]) {
|
|
85
|
-
input
|
|
86
|
-
|
|
87
|
-
if (mlObsTags[
|
|
88
|
-
input.value = mlObsTags[INPUT_VALUE]
|
|
89
|
-
}
|
|
90
|
-
if (spanKind === 'llm' && mlObsTags[OUTPUT_MESSAGES]) {
|
|
91
|
-
output.messages = mlObsTags[OUTPUT_MESSAGES]
|
|
92
|
-
}
|
|
93
|
-
if (spanKind === 'embedding' && mlObsTags[INPUT_DOCUMENTS]) {
|
|
120
|
+
llmObsSpan.input = mlObsTags[INPUT_MESSAGES]
|
|
121
|
+
inputType = 'messages'
|
|
122
|
+
} else if (spanKind === 'embedding' && mlObsTags[INPUT_DOCUMENTS]) {
|
|
94
123
|
input.documents = mlObsTags[INPUT_DOCUMENTS]
|
|
124
|
+
} else if (mlObsTags[INPUT_VALUE]) {
|
|
125
|
+
llmObsSpan.input = [{ role: '', content: mlObsTags[INPUT_VALUE] }]
|
|
126
|
+
inputType = 'value'
|
|
95
127
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
128
|
+
|
|
129
|
+
if (spanKind === 'llm' && mlObsTags[OUTPUT_MESSAGES]) {
|
|
130
|
+
llmObsSpan.output = mlObsTags[OUTPUT_MESSAGES]
|
|
131
|
+
outputType = 'messages'
|
|
132
|
+
} else if (spanKind === 'retrieval' && mlObsTags[OUTPUT_DOCUMENTS]) {
|
|
100
133
|
output.documents = mlObsTags[OUTPUT_DOCUMENTS]
|
|
134
|
+
} else if (mlObsTags[OUTPUT_VALUE]) {
|
|
135
|
+
llmObsSpan.output = [{ role: '', content: mlObsTags[OUTPUT_VALUE] }]
|
|
136
|
+
outputType = 'value'
|
|
101
137
|
}
|
|
102
138
|
|
|
103
139
|
const error = spanTags.error || spanTags[ERROR_TYPE]
|
|
@@ -107,9 +143,6 @@ class LLMObsSpanProcessor {
|
|
|
107
143
|
meta[ERROR_STACK] = spanTags[ERROR_STACK] || error.stack
|
|
108
144
|
}
|
|
109
145
|
|
|
110
|
-
if (input) meta.input = input
|
|
111
|
-
if (output) meta.output = output
|
|
112
|
-
|
|
113
146
|
const metrics = mlObsTags[METRICS] || {}
|
|
114
147
|
|
|
115
148
|
const mlApp = mlObsTags[ML_APP]
|
|
@@ -118,12 +151,37 @@ class LLMObsSpanProcessor {
|
|
|
118
151
|
|
|
119
152
|
const name = mlObsTags[NAME] || span._name
|
|
120
153
|
|
|
154
|
+
const tags = this.#getTags(span, mlApp, sessionId, error)
|
|
155
|
+
llmObsSpan._tags = tags
|
|
156
|
+
|
|
157
|
+
const processedSpan = this.#runProcessor(llmObsSpan)
|
|
158
|
+
if (processedSpan === null) return null
|
|
159
|
+
|
|
160
|
+
if (processedSpan.input) {
|
|
161
|
+
if (inputType === 'messages') {
|
|
162
|
+
input.messages = processedSpan.input
|
|
163
|
+
} else if (inputType === 'value') {
|
|
164
|
+
input.value = processedSpan.input[0].content
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (processedSpan.output) {
|
|
169
|
+
if (outputType === 'messages') {
|
|
170
|
+
output.messages = processedSpan.output
|
|
171
|
+
} else if (outputType === 'value') {
|
|
172
|
+
output.value = processedSpan.output[0].content
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (input) meta.input = input
|
|
177
|
+
if (output) meta.output = output
|
|
178
|
+
|
|
121
179
|
const llmObsSpanEvent = {
|
|
122
180
|
trace_id: span.context().toTraceId(true),
|
|
123
181
|
span_id: span.context().toSpanId(),
|
|
124
182
|
parent_id: parentId,
|
|
125
183
|
name,
|
|
126
|
-
tags: this
|
|
184
|
+
tags: this.#objectTagsToStringArrayTags(tags),
|
|
127
185
|
start_ns: Math.round(span._startTime * 1e6),
|
|
128
186
|
duration: Math.round(span._duration * 1e6),
|
|
129
187
|
status: error ? 'error' : 'ok',
|
|
@@ -144,7 +202,7 @@ class LLMObsSpanProcessor {
|
|
|
144
202
|
// However, we want to protect against circular references or BigInts (unserializable)
|
|
145
203
|
// This function can be reused for other fields if needed
|
|
146
204
|
// Messages, Documents, and Metrics are safeguarded in `llmobs/tagger.js`
|
|
147
|
-
|
|
205
|
+
#addObject (obj, carrier) {
|
|
148
206
|
const seenObjects = new WeakSet()
|
|
149
207
|
seenObjects.add(obj) // capture root object
|
|
150
208
|
|
|
@@ -176,12 +234,12 @@ class LLMObsSpanProcessor {
|
|
|
176
234
|
add(obj, carrier)
|
|
177
235
|
}
|
|
178
236
|
|
|
179
|
-
|
|
237
|
+
#getTags (span, mlApp, sessionId, error) {
|
|
180
238
|
let tags = {
|
|
181
|
-
...this.
|
|
182
|
-
version: this.
|
|
183
|
-
env: this.
|
|
184
|
-
service: this.
|
|
239
|
+
...this.#config.parsedDdTags,
|
|
240
|
+
version: this.#config.version,
|
|
241
|
+
env: this.#config.env,
|
|
242
|
+
service: this.#config.service,
|
|
185
243
|
source: 'integration',
|
|
186
244
|
ml_app: mlApp,
|
|
187
245
|
'ddtrace.version': tracerVersion,
|
|
@@ -191,13 +249,51 @@ class LLMObsSpanProcessor {
|
|
|
191
249
|
|
|
192
250
|
const errType = span.context()._tags[ERROR_TYPE] || error?.name
|
|
193
251
|
if (errType) tags.error_type = errType
|
|
252
|
+
|
|
194
253
|
if (sessionId) tags.session_id = sessionId
|
|
254
|
+
|
|
195
255
|
const integration = LLMObsTagger.tagMap.get(span)?.[INTEGRATION]
|
|
196
256
|
if (integration) tags.integration = integration
|
|
257
|
+
|
|
197
258
|
const existingTags = LLMObsTagger.tagMap.get(span)?.[TAGS] || {}
|
|
198
259
|
if (existingTags) tags = { ...tags, ...existingTags }
|
|
260
|
+
|
|
261
|
+
return tags
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
#objectTagsToStringArrayTags (tags) {
|
|
199
265
|
return Object.entries(tags).map(([key, value]) => `${key}:${value ?? ''}`)
|
|
200
266
|
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Runs the user span processor, emitting telemetry and adding some guardrails against invalid return types
|
|
270
|
+
* @param {LLMObservabilitySpan} span
|
|
271
|
+
* @returns {LLMObservabilitySpan | null}
|
|
272
|
+
*/
|
|
273
|
+
#runProcessor (span) {
|
|
274
|
+
const processor = this.#userSpanProcessor
|
|
275
|
+
if (!processor) return span
|
|
276
|
+
|
|
277
|
+
let error = false
|
|
278
|
+
|
|
279
|
+
try {
|
|
280
|
+
const processedLLMObsSpan = processor(span)
|
|
281
|
+
if (processedLLMObsSpan === null) return null
|
|
282
|
+
|
|
283
|
+
if (!(processedLLMObsSpan instanceof LLMObservabilitySpan)) {
|
|
284
|
+
error = true
|
|
285
|
+
logger.warn('User span processor must return an instance of an LLMObservabilitySpan or null, dropping span.')
|
|
286
|
+
return null
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return processedLLMObsSpan
|
|
290
|
+
} catch (e) {
|
|
291
|
+
logger.error(`[LLMObs] Error in LLMObs span processor (${util.inspect(processor)}): ${util.inspect(e)}`)
|
|
292
|
+
error = true
|
|
293
|
+
} finally {
|
|
294
|
+
telemetry.recordLLMObsUserProcessorCalled(error)
|
|
295
|
+
}
|
|
296
|
+
}
|
|
201
297
|
}
|
|
202
298
|
|
|
203
299
|
module.exports = LLMObsSpanProcessor
|
|
@@ -20,6 +20,8 @@ const {
|
|
|
20
20
|
NAME,
|
|
21
21
|
PROPAGATED_PARENT_ID_KEY,
|
|
22
22
|
ROOT_PARENT_ID,
|
|
23
|
+
CACHE_READ_INPUT_TOKENS_METRIC_KEY,
|
|
24
|
+
CACHE_WRITE_INPUT_TOKENS_METRIC_KEY,
|
|
23
25
|
INPUT_TOKENS_METRIC_KEY,
|
|
24
26
|
OUTPUT_TOKENS_METRIC_KEY,
|
|
25
27
|
TOTAL_TOKENS_METRIC_KEY,
|
|
@@ -144,6 +146,12 @@ class LLMObsTagger {
|
|
|
144
146
|
case 'totalTokens':
|
|
145
147
|
processedKey = TOTAL_TOKENS_METRIC_KEY
|
|
146
148
|
break
|
|
149
|
+
case 'cacheReadTokens':
|
|
150
|
+
processedKey = CACHE_READ_INPUT_TOKENS_METRIC_KEY
|
|
151
|
+
break
|
|
152
|
+
case 'cacheWriteTokens':
|
|
153
|
+
processedKey = CACHE_WRITE_INPUT_TOKENS_METRIC_KEY
|
|
154
|
+
break
|
|
147
155
|
}
|
|
148
156
|
|
|
149
157
|
if (typeof value === 'number') {
|
|
@@ -85,7 +85,8 @@ function recordLLMObsEnabled (startTime, config, value = 1) {
|
|
|
85
85
|
error: 0,
|
|
86
86
|
agentless: Number(config.llmobs.agentlessEnabled),
|
|
87
87
|
site: config.site,
|
|
88
|
-
auto: Number(autoEnabled)
|
|
88
|
+
auto: Number(autoEnabled),
|
|
89
|
+
ml_app: config.llmobs.mlApp
|
|
89
90
|
}
|
|
90
91
|
llmobsMetrics.count('product_enabled', tags).inc(value)
|
|
91
92
|
llmobsMetrics.distribution('init_time', tags).track(initTimeMs)
|
|
@@ -154,6 +155,11 @@ function recordSubmitEvaluation (options, err, value = 1) {
|
|
|
154
155
|
llmobsMetrics.count('evals_submitted', tags).inc(value)
|
|
155
156
|
}
|
|
156
157
|
|
|
158
|
+
function recordLLMObsUserProcessorCalled (error, value = 1) {
|
|
159
|
+
const tags = { error: error ? 1 : 0 }
|
|
160
|
+
llmobsMetrics.count('user_processor_called', tags).inc(value)
|
|
161
|
+
}
|
|
162
|
+
|
|
157
163
|
module.exports = {
|
|
158
164
|
recordLLMObsEnabled,
|
|
159
165
|
incrementLLMObsSpanStartCount,
|
|
@@ -164,5 +170,6 @@ module.exports = {
|
|
|
164
170
|
recordLLMObsAnnotate,
|
|
165
171
|
recordUserFlush,
|
|
166
172
|
recordExportSpan,
|
|
167
|
-
recordSubmitEvaluation
|
|
173
|
+
recordSubmitEvaluation,
|
|
174
|
+
recordLLMObsUserProcessorCalled
|
|
168
175
|
}
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
'use strict'
|
|
2
|
-
|
|
3
|
-
const coalesce = require('koalas')
|
|
4
2
|
const { inspect } = require('util')
|
|
5
3
|
const { isTrue } = require('../util')
|
|
6
4
|
const { traceChannel, debugChannel, infoChannel, warnChannel, errorChannel } = require('./channels')
|
|
7
5
|
const logWriter = require('./writer')
|
|
8
|
-
const { Log } = require('./log')
|
|
6
|
+
const { Log, LogConfig, NoTransmitError } = require('./log')
|
|
9
7
|
const { memoize } = require('./utils')
|
|
10
8
|
const { getEnvironmentVariable } = require('../config-helper')
|
|
11
9
|
|
|
@@ -15,7 +13,14 @@ const config = {
|
|
|
15
13
|
logLevel: 'debug'
|
|
16
14
|
}
|
|
17
15
|
|
|
16
|
+
// in most places where we know we want to mute a log we use log.error() directly
|
|
17
|
+
const NO_TRANSMIT = new LogConfig(false)
|
|
18
|
+
|
|
18
19
|
const log = {
|
|
20
|
+
LogConfig,
|
|
21
|
+
NO_TRANSMIT,
|
|
22
|
+
NoTransmitError,
|
|
23
|
+
|
|
19
24
|
/**
|
|
20
25
|
* @returns Read-only version of logging config. To modify config, call `log.use` and `log.toggle`
|
|
21
26
|
*/
|
|
@@ -92,18 +97,26 @@ const log = {
|
|
|
92
97
|
return this
|
|
93
98
|
},
|
|
94
99
|
|
|
100
|
+
errorWithoutTelemetry (...args) {
|
|
101
|
+
args.push(NO_TRANSMIT)
|
|
102
|
+
if (errorChannel.hasSubscribers) {
|
|
103
|
+
errorChannel.publish(Log.parse(...args))
|
|
104
|
+
}
|
|
105
|
+
return this
|
|
106
|
+
},
|
|
107
|
+
|
|
95
108
|
deprecate (code, message) {
|
|
96
109
|
return this._deprecate(code, message)
|
|
97
110
|
},
|
|
98
111
|
|
|
99
112
|
isEnabled (fleetStableConfigValue, localStableConfigValue) {
|
|
100
|
-
return isTrue(
|
|
101
|
-
fleetStableConfigValue
|
|
102
|
-
getEnvironmentVariable('DD_TRACE_DEBUG')
|
|
103
|
-
getEnvironmentVariable('OTEL_LOG_LEVEL') === 'debug' ||
|
|
104
|
-
localStableConfigValue
|
|
105
|
-
config.enabled
|
|
106
|
-
)
|
|
113
|
+
return isTrue(
|
|
114
|
+
(fleetStableConfigValue ??
|
|
115
|
+
getEnvironmentVariable('DD_TRACE_DEBUG') ??
|
|
116
|
+
getEnvironmentVariable('OTEL_LOG_LEVEL') === 'debug') ||
|
|
117
|
+
(localStableConfigValue ??
|
|
118
|
+
config.enabled)
|
|
119
|
+
)
|
|
107
120
|
},
|
|
108
121
|
|
|
109
122
|
getLogLevel (
|
|
@@ -111,14 +124,12 @@ const log = {
|
|
|
111
124
|
fleetStableConfigValue,
|
|
112
125
|
localStableConfigValue
|
|
113
126
|
) {
|
|
114
|
-
return
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
getEnvironmentVariable('
|
|
118
|
-
|
|
119
|
-
localStableConfigValue,
|
|
127
|
+
return optionsValue ??
|
|
128
|
+
fleetStableConfigValue ??
|
|
129
|
+
getEnvironmentVariable('DD_TRACE_LOG_LEVEL') ??
|
|
130
|
+
getEnvironmentVariable('OTEL_LOG_LEVEL') ??
|
|
131
|
+
localStableConfigValue ??
|
|
120
132
|
config.logLevel
|
|
121
|
-
)
|
|
122
133
|
}
|
|
123
134
|
}
|
|
124
135
|
|
|
@@ -2,12 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
const { format } = require('util')
|
|
4
4
|
|
|
5
|
+
// other times we produce an Error in a central location and log it several other places
|
|
6
|
+
class NoTransmitError extends Error {}
|
|
7
|
+
|
|
5
8
|
class Log {
|
|
6
|
-
constructor (message, args, cause, delegate) {
|
|
9
|
+
constructor (message, args, cause, delegate, sendViaTelemetry = true) {
|
|
7
10
|
this.message = message
|
|
8
11
|
this.args = args
|
|
9
12
|
this.cause = cause
|
|
10
13
|
this.delegate = delegate
|
|
14
|
+
this.sendViaTelemetry = sendViaTelemetry
|
|
11
15
|
}
|
|
12
16
|
|
|
13
17
|
get formatted () {
|
|
@@ -22,10 +26,18 @@ class Log {
|
|
|
22
26
|
|
|
23
27
|
static parse (...args) {
|
|
24
28
|
let message, cause, delegate
|
|
29
|
+
let sendViaTelemetry = true
|
|
30
|
+
|
|
31
|
+
const maybeLogConfig = args.at(-1)
|
|
32
|
+
if (maybeLogConfig instanceof LogConfig) {
|
|
33
|
+
args.pop()
|
|
34
|
+
sendViaTelemetry = maybeLogConfig.transmit
|
|
35
|
+
}
|
|
25
36
|
|
|
26
|
-
const
|
|
27
|
-
if (
|
|
37
|
+
const maybeError = args.at(-1)
|
|
38
|
+
if (maybeError && typeof maybeError === 'object' && maybeError.stack) { // maybeError instanceof Error?
|
|
28
39
|
cause = args.pop()
|
|
40
|
+
if (cause instanceof NoTransmitError) sendViaTelemetry = false
|
|
29
41
|
}
|
|
30
42
|
|
|
31
43
|
const firstArg = args.shift()
|
|
@@ -43,10 +55,22 @@ class Log {
|
|
|
43
55
|
message = String(firstArg)
|
|
44
56
|
}
|
|
45
57
|
|
|
46
|
-
return new Log(message, args, cause, delegate)
|
|
58
|
+
return new Log(message, args, cause, delegate, sendViaTelemetry)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Pass instances of this class to logger methods when fine-grain control is needed
|
|
64
|
+
* @property {boolean} transmit - Whether to send the log via telemetry.
|
|
65
|
+
*/
|
|
66
|
+
class LogConfig {
|
|
67
|
+
constructor (transmit = true) {
|
|
68
|
+
this.transmit = transmit
|
|
47
69
|
}
|
|
48
70
|
}
|
|
49
71
|
|
|
50
72
|
module.exports = {
|
|
51
|
-
Log
|
|
73
|
+
Log,
|
|
74
|
+
LogConfig,
|
|
75
|
+
NoTransmitError,
|
|
52
76
|
}
|
|
@@ -75,12 +75,12 @@ function onError (err) {
|
|
|
75
75
|
// TODO: replace it with Error(message, { cause }) when cause has broad support
|
|
76
76
|
if (formatted) {
|
|
77
77
|
withNoop(() => {
|
|
78
|
-
const
|
|
78
|
+
const stackTraceLimitBackup = Error.stackTraceLimit
|
|
79
79
|
Error.stackTraceLimit = 0
|
|
80
|
-
const
|
|
81
|
-
Error.stackTraceLimit =
|
|
82
|
-
Error.captureStackTrace(
|
|
83
|
-
logger.error(
|
|
80
|
+
const newError = new Error(formatted)
|
|
81
|
+
Error.stackTraceLimit = stackTraceLimitBackup
|
|
82
|
+
Error.captureStackTrace(newError, stackTraceLimitFunction)
|
|
83
|
+
logger.error(newError)
|
|
84
84
|
})
|
|
85
85
|
}
|
|
86
86
|
if (cause) withNoop(() => logger.error(cause))
|
|
@@ -22,6 +22,7 @@ class NoopSpan {
|
|
|
22
22
|
setTag (key, value) { return this }
|
|
23
23
|
addTags (keyValueMap) { return this }
|
|
24
24
|
addLink (link) { return this }
|
|
25
|
+
addLinks (links) { return this }
|
|
25
26
|
addSpanPointer (ptrKind, ptrDir, ptrHash) { return this }
|
|
26
27
|
log () { return this }
|
|
27
28
|
logEvent () {}
|
|
@@ -211,10 +211,21 @@ class Span {
|
|
|
211
211
|
return this
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
-
addLink (
|
|
215
|
-
//
|
|
214
|
+
addLink (link, attrs) {
|
|
215
|
+
// TODO: Remove this once we remove addLink(context, attrs) in v6.0.0
|
|
216
|
+
if (link instanceof SpanContext) {
|
|
217
|
+
link = { context: link, attributes: attrs ?? {} }
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const { context, attributes } = link
|
|
221
|
+
// Extract dd context
|
|
216
222
|
const ddSpanContext = context._ddContext
|
|
217
|
-
this._ddSpan.addLink(ddSpanContext, attributes)
|
|
223
|
+
this._ddSpan.addLink({ context: ddSpanContext, attributes })
|
|
224
|
+
return this
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
addLinks (links) {
|
|
228
|
+
links.forEach(link => this.addLink(link))
|
|
218
229
|
return this
|
|
219
230
|
}
|
|
220
231
|
|
|
@@ -86,8 +86,10 @@ class DatadogSpan {
|
|
|
86
86
|
|
|
87
87
|
this._startTime = fields.startTime || this._getTime()
|
|
88
88
|
|
|
89
|
-
this._links =
|
|
90
|
-
|
|
89
|
+
this._links = fields.links?.map(link => ({
|
|
90
|
+
context: link.context._ddContext ?? link.context,
|
|
91
|
+
attributes: this._sanitizeAttributes(link.attributes)
|
|
92
|
+
})) ?? []
|
|
91
93
|
|
|
92
94
|
if (DD_TRACE_EXPERIMENTAL_SPAN_COUNTS && finishedRegistry) {
|
|
93
95
|
runtimeMetrics.increment('runtime.node.spans.unfinished')
|
|
@@ -196,13 +198,25 @@ class DatadogSpan {
|
|
|
196
198
|
|
|
197
199
|
logEvent () {}
|
|
198
200
|
|
|
199
|
-
addLink (
|
|
201
|
+
addLink (link, attrs) {
|
|
202
|
+
// TODO: Remove this once we remove addLink(context, attrs) in v6.0.0
|
|
203
|
+
if (link instanceof SpanContext) {
|
|
204
|
+
link = { context: link, attributes: attrs ?? {} }
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const { context, attributes } = link
|
|
208
|
+
|
|
200
209
|
this._links.push({
|
|
201
210
|
context: context._ddContext ?? context,
|
|
202
211
|
attributes: this._sanitizeAttributes(attributes)
|
|
203
212
|
})
|
|
204
213
|
}
|
|
205
214
|
|
|
215
|
+
addLinks (links) {
|
|
216
|
+
links.forEach(link => this.addLink(link))
|
|
217
|
+
return this
|
|
218
|
+
}
|
|
219
|
+
|
|
206
220
|
addSpanPointer (ptrKind, ptrDir, ptrHash) {
|
|
207
221
|
const zeroContext = new SpanContext({
|
|
208
222
|
traceId: id('0'),
|
|
@@ -214,7 +228,7 @@ class DatadogSpan {
|
|
|
214
228
|
'ptr.hash': ptrHash,
|
|
215
229
|
'link.kind': 'span-pointer'
|
|
216
230
|
}
|
|
217
|
-
this.addLink(zeroContext, attributes)
|
|
231
|
+
this.addLink({ context: zeroContext, attributes })
|
|
218
232
|
}
|
|
219
233
|
|
|
220
234
|
addEvent (name, attributesOrStartTime, startTime) {
|
|
@@ -6,6 +6,15 @@ const plugins = require('./plugins')
|
|
|
6
6
|
const log = require('./log')
|
|
7
7
|
const { getEnvironmentVariable } = require('../../dd-trace/src/config-helper')
|
|
8
8
|
|
|
9
|
+
// Test optimization plugins that should only be enabled when isCiVisibility is true
|
|
10
|
+
const TEST_OPTIMIZATION_PLUGINS = new Set([
|
|
11
|
+
'jest',
|
|
12
|
+
'vitest',
|
|
13
|
+
'cucumber',
|
|
14
|
+
'mocha',
|
|
15
|
+
'playwright'
|
|
16
|
+
])
|
|
17
|
+
|
|
9
18
|
const loadChannel = channel('dd-trace:instrumentation:load')
|
|
10
19
|
|
|
11
20
|
// instrument everything that needs Plugin System V2 instrumentation
|
|
@@ -74,6 +83,13 @@ module.exports = class PluginManager {
|
|
|
74
83
|
|
|
75
84
|
if (!Plugin) return
|
|
76
85
|
if (!this._tracerConfig) return // TODO: don't wait for tracer to be initialized
|
|
86
|
+
|
|
87
|
+
// Check if this is a Test Optimization plugin and Test Optimization is not enabled
|
|
88
|
+
if (TEST_OPTIMIZATION_PLUGINS.has(name) && !this._tracerConfig.isCiVisibility) {
|
|
89
|
+
log.debug('Plugin "%s" is not initialized because Test Optimization mode is not enabled.', name)
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
|
|
77
93
|
if (!this._pluginsByName[name]) {
|
|
78
94
|
this._pluginsByName[name] = new Plugin(this._tracer, this._tracerConfig)
|
|
79
95
|
}
|
|
@@ -148,7 +164,8 @@ module.exports = class PluginManager {
|
|
|
148
164
|
middlewareTracingEnabled,
|
|
149
165
|
traceWebsocketMessagesEnabled,
|
|
150
166
|
traceWebsocketMessagesInheritSampling,
|
|
151
|
-
traceWebsocketMessagesSeparateTraces
|
|
167
|
+
traceWebsocketMessagesSeparateTraces,
|
|
168
|
+
experimental
|
|
152
169
|
} = this._tracerConfig
|
|
153
170
|
|
|
154
171
|
const sharedConfig = {
|
|
@@ -166,7 +183,8 @@ module.exports = class PluginManager {
|
|
|
166
183
|
isServiceUserProvided,
|
|
167
184
|
traceWebsocketMessagesEnabled,
|
|
168
185
|
traceWebsocketMessagesInheritSampling,
|
|
169
|
-
traceWebsocketMessagesSeparateTraces
|
|
186
|
+
traceWebsocketMessagesSeparateTraces,
|
|
187
|
+
experimental
|
|
170
188
|
}
|
|
171
189
|
|
|
172
190
|
if (logInjection !== undefined) {
|