dd-trace 5.37.0 → 5.38.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/ci/init.js +2 -2
- package/initialize.mjs +8 -4
- package/package.json +2 -2
- package/packages/datadog-instrumentations/src/aws-sdk.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/hooks.js +3 -3
- package/packages/datadog-instrumentations/src/http/client.js +5 -0
- package/packages/datadog-instrumentations/src/openai.js +114 -90
- package/packages/datadog-plugin-http/src/client.js +3 -1
- package/packages/datadog-plugin-http2/src/client.js +3 -0
- package/packages/datadog-plugin-mongodb-core/src/index.js +1 -0
- package/packages/dd-trace/src/appsec/blocked_templates.js +3 -3
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +5 -1
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +4 -1
- package/packages/dd-trace/src/config.js +3 -3
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +3 -3
- package/packages/dd-trace/src/debugger/devtools_client/source-maps.js +20 -6
- package/packages/dd-trace/src/opentracing/propagation/log.js +2 -1
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +19 -4
- package/packages/dd-trace/src/plugins/ci_plugin.js +4 -4
- package/packages/dd-trace/src/plugins/util/ci.js +32 -0
- package/packages/dd-trace/src/sampling_rule.js +8 -3
- package/packages/dd-trace/src/telemetry/index.js +2 -1
- package/packages/dd-trace/src/util.js +2 -0
- package/register.js +3 -1
package/ci/init.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
2
|
const tracer = require('../packages/dd-trace')
|
|
3
|
-
const { isTrue } = require('../packages/dd-trace/src/util')
|
|
3
|
+
const { isTrue, isFalse } = require('../packages/dd-trace/src/util')
|
|
4
4
|
const log = require('../packages/dd-trace/src/log')
|
|
5
5
|
|
|
6
6
|
const isJestWorker = !!process.env.JEST_WORKER_ID
|
|
@@ -23,7 +23,7 @@ const options = {
|
|
|
23
23
|
flushInterval: isJestWorker ? 0 : 5000
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
let shouldInit =
|
|
26
|
+
let shouldInit = !isFalse(process.env.DD_CIVISIBILITY_ENABLED)
|
|
27
27
|
|
|
28
28
|
if (isPackageManager()) {
|
|
29
29
|
log.debug('dd-trace is not initialized in a package manager.')
|
package/initialize.mjs
CHANGED
|
@@ -35,10 +35,12 @@ ${result.source}`
|
|
|
35
35
|
const [NODE_MAJOR, NODE_MINOR] = process.versions.node.split('.').map(x => +x)
|
|
36
36
|
|
|
37
37
|
const brokenLoaders = NODE_MAJOR === 18 && NODE_MINOR === 0
|
|
38
|
+
const iitmExclusions = [/langsmith/, /openai\/_shims/, /openai\/resources\/chat\/completions\/messages/]
|
|
38
39
|
|
|
39
|
-
export async function load (
|
|
40
|
-
const
|
|
41
|
-
|
|
40
|
+
export async function load (url, context, nextLoad) {
|
|
41
|
+
const iitmExclusionsMatch = iitmExclusions.some((exclusion) => exclusion.test(url))
|
|
42
|
+
const loadHook = (brokenLoaders || iitmExclusionsMatch) ? nextLoad : origLoad
|
|
43
|
+
return insertInit(await loadHook(url, context, nextLoad))
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
export const resolve = brokenLoaders ? undefined : origResolve
|
|
@@ -53,6 +55,8 @@ if (isMainThread) {
|
|
|
53
55
|
const require = Module.createRequire(import.meta.url)
|
|
54
56
|
require('./init.js')
|
|
55
57
|
if (Module.register) {
|
|
56
|
-
Module.register('./loader-hook.mjs', import.meta.url
|
|
58
|
+
Module.register('./loader-hook.mjs', import.meta.url, {
|
|
59
|
+
data: { exclude: iitmExclusions }
|
|
60
|
+
})
|
|
57
61
|
}
|
|
58
62
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dd-trace",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.38.0",
|
|
4
4
|
"description": "Datadog APM tracing client for JavaScript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"typings": "index.d.ts",
|
|
@@ -131,7 +131,7 @@
|
|
|
131
131
|
"checksum": "^1.0.0",
|
|
132
132
|
"cli-table3": "^0.6.3",
|
|
133
133
|
"dotenv": "16.3.1",
|
|
134
|
-
"esbuild": "0.
|
|
134
|
+
"esbuild": "^0.25.0",
|
|
135
135
|
"eslint": "^9.19.0",
|
|
136
136
|
"eslint-config-standard": "^17.1.0",
|
|
137
137
|
"eslint-plugin-import": "^2.31.0",
|
|
@@ -172,7 +172,7 @@ function getMessage (request, error, result) {
|
|
|
172
172
|
|
|
173
173
|
function getChannelSuffix (name) {
|
|
174
174
|
// some resource identifiers have spaces between ex: bedrock runtime
|
|
175
|
-
name = name.replaceAll(' ', '')
|
|
175
|
+
name = String(name).replaceAll(' ', '')
|
|
176
176
|
return [
|
|
177
177
|
'cloudwatchlogs',
|
|
178
178
|
'dynamodb',
|
|
@@ -19,8 +19,8 @@ module.exports = {
|
|
|
19
19
|
'@jest/test-sequencer': () => require('../jest'),
|
|
20
20
|
'@jest/transform': () => require('../jest'),
|
|
21
21
|
'@koa/router': () => require('../koa'),
|
|
22
|
-
'@langchain/core': () => require('../langchain'),
|
|
23
|
-
'@langchain/openai': () => require('../langchain'),
|
|
22
|
+
'@langchain/core': { esmFirst: true, fn: () => require('../langchain') },
|
|
23
|
+
'@langchain/openai': { esmFirst: true, fn: () => require('../langchain') },
|
|
24
24
|
'@node-redis/client': () => require('../redis'),
|
|
25
25
|
'@opensearch-project/opensearch': () => require('../opensearch'),
|
|
26
26
|
'@opentelemetry/sdk-trace-node': () => require('../otel-sdk-trace'),
|
|
@@ -100,7 +100,7 @@ module.exports = {
|
|
|
100
100
|
'node:vm': () => require('../vm'),
|
|
101
101
|
nyc: () => require('../nyc'),
|
|
102
102
|
oracledb: () => require('../oracledb'),
|
|
103
|
-
openai: () => require('../openai'),
|
|
103
|
+
openai: { esmFirst: true, fn: () => require('../openai') },
|
|
104
104
|
paperplane: () => require('../paperplane'),
|
|
105
105
|
passport: () => require('../passport'),
|
|
106
106
|
'passport-http': () => require('../passport-http'),
|
|
@@ -117,6 +117,11 @@ function patch (http, methodName) {
|
|
|
117
117
|
} catch (e) {
|
|
118
118
|
ctx.error = e
|
|
119
119
|
errorChannel.publish(ctx)
|
|
120
|
+
// if the initial request failed, ctx.req will be unset, we must close the span here
|
|
121
|
+
// fix for: https://github.com/DataDog/dd-trace-js/issues/5016
|
|
122
|
+
if (!ctx.req) {
|
|
123
|
+
finish()
|
|
124
|
+
}
|
|
120
125
|
throw e
|
|
121
126
|
} finally {
|
|
122
127
|
endChannel.publish(ctx)
|
|
@@ -8,98 +8,98 @@ const ch = dc.tracingChannel('apm:openai:request')
|
|
|
8
8
|
|
|
9
9
|
const V4_PACKAGE_SHIMS = [
|
|
10
10
|
{
|
|
11
|
-
file: 'resources/chat/completions
|
|
11
|
+
file: 'resources/chat/completions',
|
|
12
12
|
targetClass: 'Completions',
|
|
13
13
|
baseResource: 'chat.completions',
|
|
14
14
|
methods: ['create'],
|
|
15
15
|
streamedResponse: true
|
|
16
16
|
},
|
|
17
17
|
{
|
|
18
|
-
file: 'resources/completions
|
|
18
|
+
file: 'resources/completions',
|
|
19
19
|
targetClass: 'Completions',
|
|
20
20
|
baseResource: 'completions',
|
|
21
21
|
methods: ['create'],
|
|
22
22
|
streamedResponse: true
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
|
-
file: 'resources/embeddings
|
|
25
|
+
file: 'resources/embeddings',
|
|
26
26
|
targetClass: 'Embeddings',
|
|
27
27
|
baseResource: 'embeddings',
|
|
28
28
|
methods: ['create']
|
|
29
29
|
},
|
|
30
30
|
{
|
|
31
|
-
file: 'resources/files
|
|
31
|
+
file: 'resources/files',
|
|
32
32
|
targetClass: 'Files',
|
|
33
33
|
baseResource: 'files',
|
|
34
34
|
methods: ['create', 'del', 'list', 'retrieve']
|
|
35
35
|
},
|
|
36
36
|
{
|
|
37
|
-
file: 'resources/files
|
|
37
|
+
file: 'resources/files',
|
|
38
38
|
targetClass: 'Files',
|
|
39
39
|
baseResource: 'files',
|
|
40
40
|
methods: ['retrieveContent'],
|
|
41
41
|
versions: ['>=4.0.0 <4.17.1']
|
|
42
42
|
},
|
|
43
43
|
{
|
|
44
|
-
file: 'resources/files
|
|
44
|
+
file: 'resources/files',
|
|
45
45
|
targetClass: 'Files',
|
|
46
46
|
baseResource: 'files',
|
|
47
47
|
methods: ['content'], // replaced `retrieveContent` in v4.17.1
|
|
48
48
|
versions: ['>=4.17.1']
|
|
49
49
|
},
|
|
50
50
|
{
|
|
51
|
-
file: 'resources/images
|
|
51
|
+
file: 'resources/images',
|
|
52
52
|
targetClass: 'Images',
|
|
53
53
|
baseResource: 'images',
|
|
54
54
|
methods: ['createVariation', 'edit', 'generate']
|
|
55
55
|
},
|
|
56
56
|
{
|
|
57
|
-
file: 'resources/fine-tuning/jobs/jobs
|
|
57
|
+
file: 'resources/fine-tuning/jobs/jobs',
|
|
58
58
|
targetClass: 'Jobs',
|
|
59
59
|
baseResource: 'fine_tuning.jobs',
|
|
60
60
|
methods: ['cancel', 'create', 'list', 'listEvents', 'retrieve'],
|
|
61
61
|
versions: ['>=4.34.0'] // file location changed in 4.34.0
|
|
62
62
|
},
|
|
63
63
|
{
|
|
64
|
-
file: 'resources/fine-tuning/jobs
|
|
64
|
+
file: 'resources/fine-tuning/jobs',
|
|
65
65
|
targetClass: 'Jobs',
|
|
66
66
|
baseResource: 'fine_tuning.jobs',
|
|
67
67
|
methods: ['cancel', 'create', 'list', 'listEvents', 'retrieve'],
|
|
68
68
|
versions: ['>=4.1.0 <4.34.0']
|
|
69
69
|
},
|
|
70
70
|
{
|
|
71
|
-
file: 'resources/fine-tunes
|
|
71
|
+
file: 'resources/fine-tunes', // deprecated after 4.1.0
|
|
72
72
|
targetClass: 'FineTunes',
|
|
73
73
|
baseResource: 'fine-tune',
|
|
74
74
|
methods: ['cancel', 'create', 'list', 'listEvents', 'retrieve'],
|
|
75
75
|
versions: ['>=4.0.0 <4.1.0']
|
|
76
76
|
},
|
|
77
77
|
{
|
|
78
|
-
file: 'resources/models
|
|
78
|
+
file: 'resources/models',
|
|
79
79
|
targetClass: 'Models',
|
|
80
80
|
baseResource: 'models',
|
|
81
81
|
methods: ['del', 'list', 'retrieve']
|
|
82
82
|
},
|
|
83
83
|
{
|
|
84
|
-
file: 'resources/moderations
|
|
84
|
+
file: 'resources/moderations',
|
|
85
85
|
targetClass: 'Moderations',
|
|
86
86
|
baseResource: 'moderations',
|
|
87
87
|
methods: ['create']
|
|
88
88
|
},
|
|
89
89
|
{
|
|
90
|
-
file: 'resources/audio/transcriptions
|
|
90
|
+
file: 'resources/audio/transcriptions',
|
|
91
91
|
targetClass: 'Transcriptions',
|
|
92
92
|
baseResource: 'audio.transcriptions',
|
|
93
93
|
methods: ['create']
|
|
94
94
|
},
|
|
95
95
|
{
|
|
96
|
-
file: 'resources/audio/translations
|
|
96
|
+
file: 'resources/audio/translations',
|
|
97
97
|
targetClass: 'Translations',
|
|
98
98
|
baseResource: 'audio.translations',
|
|
99
99
|
methods: ['create']
|
|
100
100
|
},
|
|
101
101
|
{
|
|
102
|
-
file: 'resources/chat/completions/completions
|
|
102
|
+
file: 'resources/chat/completions/completions',
|
|
103
103
|
targetClass: 'Completions',
|
|
104
104
|
baseResource: 'chat.completions',
|
|
105
105
|
methods: ['create'],
|
|
@@ -267,93 +267,117 @@ function wrapStreamIterator (response, options, n, ctx) {
|
|
|
267
267
|
}
|
|
268
268
|
}
|
|
269
269
|
|
|
270
|
-
|
|
271
|
-
const { file, targetClass, baseResource, methods, versions, streamedResponse } = shim
|
|
272
|
-
addHook({ name: 'openai', file, versions: versions || ['>=4'] }, exports => {
|
|
273
|
-
const targetPrototype = exports[targetClass].prototype
|
|
270
|
+
const extensions = ['.js', '.mjs']
|
|
274
271
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
272
|
+
for (const extension of extensions) {
|
|
273
|
+
for (const shim of V4_PACKAGE_SHIMS) {
|
|
274
|
+
const { file, targetClass, baseResource, methods, versions, streamedResponse } = shim
|
|
275
|
+
addHook({ name: 'openai', file: file + extension, versions: versions || ['>=4'] }, exports => {
|
|
276
|
+
const targetPrototype = exports[targetClass].prototype
|
|
280
277
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
// we need to compute how many prompts we are sending in streamed cases for completions
|
|
287
|
-
// not applicable for chat completiond
|
|
288
|
-
let n
|
|
289
|
-
if (stream) {
|
|
290
|
-
n = getOption(arguments, 'n', 1)
|
|
291
|
-
const prompt = getOption(arguments, 'prompt')
|
|
292
|
-
if (Array.isArray(prompt) && typeof prompt[0] !== 'number') {
|
|
293
|
-
n *= prompt.length
|
|
278
|
+
for (const methodName of methods) {
|
|
279
|
+
shimmer.wrap(targetPrototype, methodName, methodFn => function () {
|
|
280
|
+
if (!ch.start.hasSubscribers) {
|
|
281
|
+
return methodFn.apply(this, arguments)
|
|
294
282
|
}
|
|
295
|
-
}
|
|
296
283
|
|
|
297
|
-
|
|
284
|
+
// The OpenAI library lets you set `stream: true` on the options arg to any method
|
|
285
|
+
// However, we only want to handle streamed responses in specific cases
|
|
286
|
+
// chat.completions and completions
|
|
287
|
+
const stream = streamedResponse && getOption(arguments, 'stream', false)
|
|
288
|
+
|
|
289
|
+
// we need to compute how many prompts we are sending in streamed cases for completions
|
|
290
|
+
// not applicable for chat completiond
|
|
291
|
+
let n
|
|
292
|
+
if (stream) {
|
|
293
|
+
n = getOption(arguments, 'n', 1)
|
|
294
|
+
const prompt = getOption(arguments, 'prompt')
|
|
295
|
+
if (Array.isArray(prompt) && typeof prompt[0] !== 'number') {
|
|
296
|
+
n *= prompt.length
|
|
297
|
+
}
|
|
298
|
+
}
|
|
298
299
|
|
|
299
|
-
|
|
300
|
-
methodName: `${baseResource}.${methodName}`,
|
|
301
|
-
args: arguments,
|
|
302
|
-
basePath: client.baseURL,
|
|
303
|
-
apiKey: client.apiKey
|
|
304
|
-
}
|
|
300
|
+
const client = this._client || this.client
|
|
305
301
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
return origApiPromParse.apply(this, arguments)
|
|
313
|
-
// the original response is wrapped in a promise, so we need to unwrap it
|
|
314
|
-
.then(body => Promise.all([this.responsePromise, body]))
|
|
315
|
-
.then(([{ response, options }, body]) => {
|
|
316
|
-
if (stream) {
|
|
317
|
-
if (body.iterator) {
|
|
318
|
-
shimmer.wrap(body, 'iterator', wrapStreamIterator(response, options, n, ctx))
|
|
319
|
-
} else {
|
|
320
|
-
shimmer.wrap(
|
|
321
|
-
body.response.body, Symbol.asyncIterator, wrapStreamIterator(response, options, n, ctx)
|
|
322
|
-
)
|
|
323
|
-
}
|
|
324
|
-
} else {
|
|
325
|
-
finish(ctx, {
|
|
326
|
-
headers: response.headers,
|
|
327
|
-
data: body,
|
|
328
|
-
request: {
|
|
329
|
-
path: response.url,
|
|
330
|
-
method: options.method
|
|
331
|
-
}
|
|
332
|
-
})
|
|
333
|
-
}
|
|
302
|
+
const ctx = {
|
|
303
|
+
methodName: `${baseResource}.${methodName}`,
|
|
304
|
+
args: arguments,
|
|
305
|
+
basePath: client.baseURL,
|
|
306
|
+
apiKey: client.apiKey
|
|
307
|
+
}
|
|
334
308
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
.catch(error => {
|
|
338
|
-
finish(ctx, undefined, error)
|
|
309
|
+
return ch.start.runStores(ctx, () => {
|
|
310
|
+
const apiProm = methodFn.apply(this, arguments)
|
|
339
311
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
.
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
312
|
+
if (baseResource === 'chat.completions' && typeof apiProm._thenUnwrap === 'function') {
|
|
313
|
+
// this should only ever be invoked from a client.beta.chat.completions.parse call
|
|
314
|
+
shimmer.wrap(apiProm, '_thenUnwrap', origApiPromThenUnwrap => function () {
|
|
315
|
+
// TODO(sam.brenner): I wonder if we can patch the APIPromise prototype instead, although
|
|
316
|
+
// we might not have access to everything we need...
|
|
317
|
+
|
|
318
|
+
// this is a new apipromise instance
|
|
319
|
+
const unwrappedPromise = origApiPromThenUnwrap.apply(this, arguments)
|
|
320
|
+
|
|
321
|
+
shimmer.wrap(unwrappedPromise, 'parse', origApiPromParse => function () {
|
|
322
|
+
const parsedPromise = origApiPromParse.apply(this, arguments)
|
|
323
|
+
.then(body => Promise.all([this.responsePromise, body]))
|
|
324
|
+
|
|
325
|
+
return handleUnwrappedAPIPromise(parsedPromise, ctx, stream, n)
|
|
326
|
+
})
|
|
327
|
+
|
|
328
|
+
return unwrappedPromise
|
|
346
329
|
})
|
|
347
|
-
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// wrapping `parse` avoids problematic wrapping of `then` when trying to call
|
|
333
|
+
// `withResponse` in userland code after. This way, we can return the whole `APIPromise`
|
|
334
|
+
shimmer.wrap(apiProm, 'parse', origApiPromParse => function () {
|
|
335
|
+
const parsedPromise = origApiPromParse.apply(this, arguments)
|
|
336
|
+
.then(body => Promise.all([this.responsePromise, body]))
|
|
337
|
+
|
|
338
|
+
return handleUnwrappedAPIPromise(parsedPromise, ctx, stream, n)
|
|
339
|
+
})
|
|
348
340
|
|
|
349
|
-
|
|
341
|
+
ch.end.publish(ctx)
|
|
350
342
|
|
|
351
|
-
|
|
343
|
+
return apiProm
|
|
344
|
+
})
|
|
352
345
|
})
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
}
|
|
346
|
+
}
|
|
347
|
+
return exports
|
|
348
|
+
})
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function handleUnwrappedAPIPromise (apiProm, ctx, stream, n) {
|
|
353
|
+
return apiProm
|
|
354
|
+
.then(([{ response, options }, body]) => {
|
|
355
|
+
if (stream) {
|
|
356
|
+
if (body.iterator) {
|
|
357
|
+
shimmer.wrap(body, 'iterator', wrapStreamIterator(response, options, n, ctx))
|
|
358
|
+
} else {
|
|
359
|
+
shimmer.wrap(
|
|
360
|
+
body.response.body, Symbol.asyncIterator, wrapStreamIterator(response, options, n, ctx)
|
|
361
|
+
)
|
|
362
|
+
}
|
|
363
|
+
} else {
|
|
364
|
+
finish(ctx, {
|
|
365
|
+
headers: response.headers,
|
|
366
|
+
data: body,
|
|
367
|
+
request: {
|
|
368
|
+
path: response.url,
|
|
369
|
+
method: options.method
|
|
370
|
+
}
|
|
371
|
+
})
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return body
|
|
375
|
+
})
|
|
376
|
+
.catch(error => {
|
|
377
|
+
finish(ctx, undefined, error)
|
|
378
|
+
|
|
379
|
+
throw error
|
|
380
|
+
})
|
|
357
381
|
}
|
|
358
382
|
|
|
359
383
|
function finish (ctx, response, error) {
|
|
@@ -102,7 +102,9 @@ class HttpClientPlugin extends ClientPlugin {
|
|
|
102
102
|
addResponseHeaders(res, span, this.config)
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
if (req) {
|
|
106
|
+
addRequestHeaders(req, span, this.config)
|
|
107
|
+
}
|
|
106
108
|
|
|
107
109
|
this.config.hooks.request(span, req, res)
|
|
108
110
|
|
|
@@ -73,6 +73,9 @@ class Http2ClientPlugin extends ClientPlugin {
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
bindAsyncStart ({ eventName, eventData, currentStore, parentStore }) {
|
|
76
|
+
// Plugin wasn't enabled when the request started.
|
|
77
|
+
if (!currentStore) return storage('legacy').getStore()
|
|
78
|
+
|
|
76
79
|
switch (eventName) {
|
|
77
80
|
case 'response':
|
|
78
81
|
this._onResponse(currentStore, eventData)
|
|
@@ -70,6 +70,7 @@ function getQuery (cmd) {
|
|
|
70
70
|
if (!cmd || typeof cmd !== 'object' || Array.isArray(cmd)) return
|
|
71
71
|
if (cmd.query) return sanitizeBigInt(limitDepth(cmd.query))
|
|
72
72
|
if (cmd.filter) return sanitizeBigInt(limitDepth(cmd.filter))
|
|
73
|
+
if (cmd.pipeline) return sanitizeBigInt(limitDepth(cmd.pipeline))
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
function getResource (plugin, ns, query, operationName) {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/* eslint-disable @stylistic/js/max-len */
|
|
2
2
|
'use strict'
|
|
3
3
|
|
|
4
|
-
const html =
|
|
4
|
+
const html = '<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>You\'ve been blocked</title><style>a,body,div,html,span{margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:baseline}body{background:-webkit-radial-gradient(26% 19%,circle,#fff,#f4f7f9);background:radial-gradient(circle at 26% 19%,#fff,#f4f7f9);display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-line-pack:center;align-content:center;width:100%;min-height:100vh;line-height:1;flex-direction:column}p{display:block}main{text-align:center;flex:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-ms-flex-line-pack:center;align-content:center;flex-direction:column}p{font-size:18px;line-height:normal;color:#646464;font-family:sans-serif;font-weight:400}a{color:#4842b7}footer{width:100%;text-align:center}footer p{font-size:16px}</style></head><body><main><p>Sorry, you cannot access this page. Please contact the customer service team.</p></main><footer><p>Security provided by <a href="https://www.datadoghq.com/product/security-platform/application-security-monitoring/" target="_blank">Datadog</a></p></footer></body></html>'
|
|
5
5
|
|
|
6
|
-
const json =
|
|
6
|
+
const json = '{"errors":[{"title":"You\'ve been blocked","detail":"Sorry, you cannot access this page. Please contact the customer service team. Security provided by Datadog."}]}'
|
|
7
7
|
|
|
8
|
-
const graphqlJson =
|
|
8
|
+
const graphqlJson = '{"errors":[{"message":"You\'ve been blocked","extensions":{"detail":"Sorry, you cannot perform this operation. Please contact the customer service team. Security provided by Datadog."}}]}'
|
|
9
9
|
|
|
10
10
|
module.exports = {
|
|
11
11
|
html,
|
|
@@ -69,8 +69,12 @@ class TestVisDynamicInstrumentation {
|
|
|
69
69
|
// To avoid infinite initialization loops, we're disabling DI and tracing in the worker.
|
|
70
70
|
env: {
|
|
71
71
|
...process.env,
|
|
72
|
+
DD_CIVISIBILITY_ENABLED: 0,
|
|
72
73
|
DD_TRACE_ENABLED: 0,
|
|
73
|
-
|
|
74
|
+
DD_TEST_FAILED_TEST_REPLAY_ENABLED: 0,
|
|
75
|
+
DD_CIVISIBILITY_MANUAL_API_ENABLED: 0,
|
|
76
|
+
DD_TRACING_ENABLED: 0,
|
|
77
|
+
DD_TRACE_TELEMETRY_ENABLED: 0
|
|
74
78
|
},
|
|
75
79
|
workerData: {
|
|
76
80
|
config: config.serialize(),
|
|
@@ -5,6 +5,7 @@ const FormData = require('../../../exporters/common/form-data')
|
|
|
5
5
|
const request = require('../../../exporters/common/request')
|
|
6
6
|
|
|
7
7
|
const log = require('../../../log')
|
|
8
|
+
const { isFalse } = require('../../../util')
|
|
8
9
|
const {
|
|
9
10
|
getLatestCommits,
|
|
10
11
|
getRepositoryUrl,
|
|
@@ -284,7 +285,9 @@ function sendGitMetadata (url, { isEvpProxy, evpProxyPrefix }, configRepositoryU
|
|
|
284
285
|
}
|
|
285
286
|
// Otherwise we unshallow and get commits to upload again
|
|
286
287
|
log.debug('It is shallow clone, unshallowing...')
|
|
287
|
-
|
|
288
|
+
if (!isFalse(process.env.DD_CIVISIBILITY_GIT_UNSHALLOW_ENABLED)) {
|
|
289
|
+
unshallowRepository()
|
|
290
|
+
}
|
|
288
291
|
|
|
289
292
|
// The latest commits change after unshallowing
|
|
290
293
|
latestCommits = getLatestCommits()
|
|
@@ -566,7 +566,7 @@ class Config {
|
|
|
566
566
|
this._setValue(defaults, 'telemetry.metrics', true)
|
|
567
567
|
this._setValue(defaults, 'traceEnabled', true)
|
|
568
568
|
this._setValue(defaults, 'traceId128BitGenerationEnabled', true)
|
|
569
|
-
this._setValue(defaults, 'traceId128BitLoggingEnabled',
|
|
569
|
+
this._setValue(defaults, 'traceId128BitLoggingEnabled', true)
|
|
570
570
|
this._setValue(defaults, 'tracePropagationExtractFirst', false)
|
|
571
571
|
this._setValue(defaults, 'tracePropagationStyle.inject', ['datadog', 'tracecontext', 'baggage'])
|
|
572
572
|
this._setValue(defaults, 'tracePropagationStyle.extract', ['datadog', 'tracecontext', 'baggage'])
|
|
@@ -1144,7 +1144,7 @@ class Config {
|
|
|
1144
1144
|
DD_CIVISIBILITY_FLAKY_RETRY_COUNT,
|
|
1145
1145
|
DD_TEST_SESSION_NAME,
|
|
1146
1146
|
DD_AGENTLESS_LOG_SUBMISSION_ENABLED,
|
|
1147
|
-
|
|
1147
|
+
DD_TEST_FAILED_TEST_REPLAY_ENABLED,
|
|
1148
1148
|
DD_TEST_MANAGEMENT_ENABLED,
|
|
1149
1149
|
DD_TEST_MANAGEMENT_ATTEMPT_TO_FIX_RETRIES
|
|
1150
1150
|
} = process.env
|
|
@@ -1164,7 +1164,7 @@ class Config {
|
|
|
1164
1164
|
this._setBoolean(calc, 'isManualApiEnabled', !isFalse(this._isCiVisibilityManualApiEnabled()))
|
|
1165
1165
|
this._setString(calc, 'ciVisibilityTestSessionName', DD_TEST_SESSION_NAME)
|
|
1166
1166
|
this._setBoolean(calc, 'ciVisAgentlessLogSubmissionEnabled', isTrue(DD_AGENTLESS_LOG_SUBMISSION_ENABLED))
|
|
1167
|
-
this._setBoolean(calc, 'isTestDynamicInstrumentationEnabled',
|
|
1167
|
+
this._setBoolean(calc, 'isTestDynamicInstrumentationEnabled', !isFalse(DD_TEST_FAILED_TEST_REPLAY_ENABLED))
|
|
1168
1168
|
this._setBoolean(calc, 'isServiceUserProvided', !!this._env.service)
|
|
1169
1169
|
this._setBoolean(calc, 'isTestManagementEnabled', !isFalse(DD_TEST_MANAGEMENT_ENABLED))
|
|
1170
1170
|
this._setValue(calc,
|
|
@@ -31,9 +31,9 @@ async function addBreakpoint (probe) {
|
|
|
31
31
|
probe.nsBetweenSampling = BigInt(1 / snapshotsPerSecond * 1e9)
|
|
32
32
|
probe.lastCaptureNs = 0n
|
|
33
33
|
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
//
|
|
34
|
+
// Warning: The code below relies on undocumented behavior of the inspector!
|
|
35
|
+
// It expects that `await session.post('Debugger.enable')` will wait for all loaded scripts to be emitted as
|
|
36
|
+
// `Debugger.scriptParsed` events. If this ever changes, we will have a race condition!
|
|
37
37
|
const script = findScriptFromPartialPath(file)
|
|
38
38
|
if (!script) throw new Error(`No loaded script found for ${file} (probe: ${probe.id}, version: ${probe.version})`)
|
|
39
39
|
const { url, scriptId, sourceMapURL, source } = script
|
|
@@ -7,6 +7,7 @@ const { SourceMapConsumer } = require('source-map')
|
|
|
7
7
|
|
|
8
8
|
const cache = new Map()
|
|
9
9
|
let cacheTimer = null
|
|
10
|
+
let cacheTimerLastSet = 0
|
|
10
11
|
|
|
11
12
|
const self = module.exports = {
|
|
12
13
|
async loadSourceMap (dir, url) {
|
|
@@ -33,13 +34,26 @@ const self = module.exports = {
|
|
|
33
34
|
}
|
|
34
35
|
}
|
|
35
36
|
|
|
37
|
+
// TODO: Remove if-statement around `setTimeout` below once it's safe to do so.
|
|
38
|
+
//
|
|
39
|
+
// This is a workaround for, what seems like a bug in Node.js core, that seems to trigger when, among other things, a
|
|
40
|
+
// lot of timers are being created very rapidly. This makes the call to `setTimeout` throw an error from within
|
|
41
|
+
// `AsyncLocalStorage._propagate` with the following error message:
|
|
42
|
+
//
|
|
43
|
+
// TypeError: Cannot read properties of undefined (reading 'Symbol(kResourceStore)')
|
|
44
|
+
//
|
|
45
|
+
// Source: https://github.com/nodejs/node/blob/v18.20.6/lib/async_hooks.js#L312
|
|
36
46
|
function cacheIt (key, value) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
47
|
+
const now = Date.now()
|
|
48
|
+
if (now > cacheTimerLastSet + 1_000) {
|
|
49
|
+
clearTimeout(cacheTimer)
|
|
50
|
+
cacheTimer = setTimeout(function () {
|
|
51
|
+
// Optimize for app boot, where a lot of reads might happen
|
|
52
|
+
// Clear cache a few seconds after it was last used
|
|
53
|
+
cache.clear()
|
|
54
|
+
}, 10_000).unref()
|
|
55
|
+
cacheTimerLastSet = now
|
|
56
|
+
}
|
|
43
57
|
cache.set(key, value)
|
|
44
58
|
return value
|
|
45
59
|
}
|
|
@@ -14,7 +14,8 @@ class LogPropagator {
|
|
|
14
14
|
carrier.dd = {}
|
|
15
15
|
|
|
16
16
|
if (spanContext) {
|
|
17
|
-
if (this._config.
|
|
17
|
+
if (this._config.traceId128BitGenerationEnabled &&
|
|
18
|
+
this._config.traceId128BitLoggingEnabled && spanContext._trace.tags['_dd.p.tid']) {
|
|
18
19
|
carrier.dd.trace_id = spanContext.toTraceId(true)
|
|
19
20
|
} else {
|
|
20
21
|
carrier.dd.trace_id = spanContext.toTraceId()
|
|
@@ -46,6 +46,7 @@ const tracestateTagKeyFilter = /[^\x21-\x2b\x2d-\x3c\x3e-\x7e]/g
|
|
|
46
46
|
const tracestateTagValueFilter = /[^\x20-\x2b\x2d-\x3a\x3c-\x7d]/g
|
|
47
47
|
const invalidSegment = /^0+$/
|
|
48
48
|
const zeroTraceId = '0000000000000000'
|
|
49
|
+
const hex16 = /^[0-9A-Fa-f]{16}$/
|
|
49
50
|
|
|
50
51
|
class TextMapPropagator {
|
|
51
52
|
constructor (config) {
|
|
@@ -482,10 +483,20 @@ class TextMapPropagator {
|
|
|
482
483
|
}
|
|
483
484
|
break
|
|
484
485
|
}
|
|
485
|
-
default:
|
|
486
|
+
default: {
|
|
486
487
|
if (!key.startsWith('t.')) continue
|
|
487
|
-
|
|
488
|
-
|
|
488
|
+
const subKey = key.slice(2) // e.g. t.tid -> tid
|
|
489
|
+
const transformedValue = value.replace(/[\x7e]/gm, '=')
|
|
490
|
+
|
|
491
|
+
// If subkey is tid then do nothing because trace header tid should always be preserved
|
|
492
|
+
if (subKey === 'tid') {
|
|
493
|
+
if (!hex16.test(value) || spanContext._trace.tags['_dd.p.tid'] !== transformedValue) {
|
|
494
|
+
log.error(`Invalid trace id ${value} in tracestate, skipping`)
|
|
495
|
+
}
|
|
496
|
+
continue
|
|
497
|
+
}
|
|
498
|
+
spanContext._trace.tags[`_dd.p.${subKey}`] = transformedValue
|
|
499
|
+
}
|
|
489
500
|
}
|
|
490
501
|
}
|
|
491
502
|
})
|
|
@@ -645,7 +656,11 @@ class TextMapPropagator {
|
|
|
645
656
|
log.error('Trace tags from carrier are invalid, skipping extraction.')
|
|
646
657
|
return
|
|
647
658
|
}
|
|
648
|
-
|
|
659
|
+
// Check if value is a valid 16 character lower-case hexadecimal encoded number as per spec
|
|
660
|
+
if (key === '_dd.p.tid' && !(hex16.test(value))) {
|
|
661
|
+
log.error(`Invalid _dd.p.tid tag ${value}, skipping`)
|
|
662
|
+
continue
|
|
663
|
+
}
|
|
649
664
|
tags[key] = value
|
|
650
665
|
}
|
|
651
666
|
|
|
@@ -202,15 +202,15 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
202
202
|
configure (config, shouldGetEnvironmentData = true) {
|
|
203
203
|
super.configure(config)
|
|
204
204
|
|
|
205
|
+
if (!shouldGetEnvironmentData) {
|
|
206
|
+
return
|
|
207
|
+
}
|
|
208
|
+
|
|
205
209
|
if (config.isTestDynamicInstrumentationEnabled && !this.di) {
|
|
206
210
|
const testVisibilityDynamicInstrumentation = require('../ci-visibility/dynamic-instrumentation')
|
|
207
211
|
this.di = testVisibilityDynamicInstrumentation
|
|
208
212
|
}
|
|
209
213
|
|
|
210
|
-
if (!shouldGetEnvironmentData) {
|
|
211
|
-
return
|
|
212
|
-
}
|
|
213
|
-
|
|
214
214
|
this.testEnvironmentMetadata = getTestEnvironmentMetadata(this.constructor.id, this.config)
|
|
215
215
|
|
|
216
216
|
const {
|
|
@@ -628,6 +628,38 @@ module.exports = {
|
|
|
628
628
|
}
|
|
629
629
|
}
|
|
630
630
|
|
|
631
|
+
if (env.DRONE && env.CI) {
|
|
632
|
+
const {
|
|
633
|
+
DRONE_BUILD_NUMBER,
|
|
634
|
+
DRONE_BUILD_LINK,
|
|
635
|
+
DRONE_STEP_NAME,
|
|
636
|
+
DRONE_STAGE_NAME,
|
|
637
|
+
DRONE_WORKSPACE,
|
|
638
|
+
DRONE_GIT_HTTP_URL,
|
|
639
|
+
DRONE_COMMIT_SHA,
|
|
640
|
+
DRONE_BRANCH,
|
|
641
|
+
DRONE_TAG,
|
|
642
|
+
DRONE_COMMIT_AUTHOR_NAME,
|
|
643
|
+
DRONE_COMMIT_AUTHOR_EMAIL,
|
|
644
|
+
DRONE_COMMIT_MESSAGE
|
|
645
|
+
} = env
|
|
646
|
+
tags = {
|
|
647
|
+
[CI_PROVIDER_NAME]: 'drone',
|
|
648
|
+
[CI_PIPELINE_NUMBER]: DRONE_BUILD_NUMBER,
|
|
649
|
+
[CI_PIPELINE_URL]: DRONE_BUILD_LINK,
|
|
650
|
+
[CI_JOB_NAME]: DRONE_STEP_NAME,
|
|
651
|
+
[CI_STAGE_NAME]: DRONE_STAGE_NAME,
|
|
652
|
+
[CI_WORKSPACE_PATH]: DRONE_WORKSPACE,
|
|
653
|
+
[GIT_REPOSITORY_URL]: DRONE_GIT_HTTP_URL,
|
|
654
|
+
[GIT_COMMIT_SHA]: DRONE_COMMIT_SHA,
|
|
655
|
+
[GIT_BRANCH]: DRONE_BRANCH,
|
|
656
|
+
[GIT_TAG]: DRONE_TAG,
|
|
657
|
+
[GIT_COMMIT_AUTHOR_NAME]: DRONE_COMMIT_AUTHOR_NAME,
|
|
658
|
+
[GIT_COMMIT_AUTHOR_EMAIL]: DRONE_COMMIT_AUTHOR_EMAIL,
|
|
659
|
+
[GIT_COMMIT_MESSAGE]: DRONE_COMMIT_MESSAGE
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
631
663
|
normalizeTag(tags, CI_WORKSPACE_PATH, resolveTilde)
|
|
632
664
|
normalizeTag(tags, GIT_REPOSITORY_URL, filterSensitiveInfoFromRepository)
|
|
633
665
|
normalizeTag(tags, GIT_BRANCH, normalizeRef)
|
|
@@ -41,10 +41,9 @@ function matcher (pattern, locator) {
|
|
|
41
41
|
return new RegExpMatcher(pattern, locator)
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
if (typeof pattern === 'string' && pattern !== '*') {
|
|
44
|
+
if (typeof pattern === 'string' && pattern !== '*' && pattern !== '**' && pattern !== '***') {
|
|
45
45
|
return new GlobMatcher(pattern, locator)
|
|
46
46
|
}
|
|
47
|
-
|
|
48
47
|
return new AlwaysMatcher()
|
|
49
48
|
}
|
|
50
49
|
|
|
@@ -63,6 +62,12 @@ function serviceLocator (span) {
|
|
|
63
62
|
span.tracer()._service
|
|
64
63
|
}
|
|
65
64
|
|
|
65
|
+
function resourceLocator (span) {
|
|
66
|
+
const { _tags: tags } = span.context()
|
|
67
|
+
return tags.resource ||
|
|
68
|
+
tags['resource.name']
|
|
69
|
+
}
|
|
70
|
+
|
|
66
71
|
class SamplingRule {
|
|
67
72
|
constructor ({ name, service, resource, tags, sampleRate = 1.0, provenance = undefined, maxPerSecond } = {}) {
|
|
68
73
|
this.matchers = []
|
|
@@ -74,7 +79,7 @@ class SamplingRule {
|
|
|
74
79
|
this.matchers.push(matcher(service, serviceLocator))
|
|
75
80
|
}
|
|
76
81
|
if (resource) {
|
|
77
|
-
this.matchers.push(matcher(resource,
|
|
82
|
+
this.matchers.push(matcher(resource, resourceLocator))
|
|
78
83
|
}
|
|
79
84
|
for (const [key, value] of Object.entries(tags || {})) {
|
|
80
85
|
this.matchers.push(matcher(value, makeTagLocator(key)))
|
|
@@ -327,7 +327,8 @@ function updateConfig (changes, config) {
|
|
|
327
327
|
service: 'DD_SERVICE',
|
|
328
328
|
clientIpHeader: 'DD_TRACE_CLIENT_IP_HEADER',
|
|
329
329
|
'grpc.client.error.statuses': 'DD_GRPC_CLIENT_ERROR_STATUSES',
|
|
330
|
-
'grpc.server.error.statuses': 'DD_GRPC_SERVER_ERROR_STATUSES'
|
|
330
|
+
'grpc.server.error.statuses': 'DD_GRPC_SERVER_ERROR_STATUSES',
|
|
331
|
+
traceId128BitLoggingEnabled: 'DD_TRACE_128_BIT_TRACEID_LOGGING_ENABLED'
|
|
331
332
|
}
|
|
332
333
|
|
|
333
334
|
const namesNeedFormatting = new Set(['DD_TAGS', 'peerServiceMapping', 'serviceMapping'])
|
|
@@ -26,6 +26,8 @@ function isError (value) {
|
|
|
26
26
|
function globMatch (pattern, subject) {
|
|
27
27
|
if (typeof pattern === 'string') pattern = pattern.toLowerCase()
|
|
28
28
|
if (typeof subject === 'string') subject = subject.toLowerCase()
|
|
29
|
+
if (typeof subject === 'number' && Number.isInteger(subject)) subject = String(subject)
|
|
30
|
+
|
|
29
31
|
let px = 0 // [p]attern inde[x]
|
|
30
32
|
let sx = 0 // [s]ubject inde[x]
|
|
31
33
|
let nextPx = 0
|
package/register.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
const { register } = require('node:module')
|
|
2
2
|
const { pathToFileURL } = require('node:url')
|
|
3
3
|
|
|
4
|
-
register('./loader-hook.mjs', pathToFileURL(__filename)
|
|
4
|
+
register('./loader-hook.mjs', pathToFileURL(__filename), {
|
|
5
|
+
data: { exclude: [/langsmith/, /openai\/_shims/, /openai\/resources\/chat\/completions\/messages/] }
|
|
6
|
+
})
|