dd-trace 5.91.0 → 5.92.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/package.json +2 -2
- package/packages/datadog-instrumentations/src/child_process.js +14 -8
- package/packages/datadog-instrumentations/src/graphql.js +1 -1
- package/packages/datadog-plugin-graphql/src/execute.js +2 -2
- package/packages/datadog-plugin-graphql/src/resolve.js +22 -35
- package/packages/datadog-plugin-http/src/client.js +1 -1
- package/packages/dd-trace/src/config/defaults.js +2 -0
- package/packages/dd-trace/src/config/index.js +6 -0
- package/packages/dd-trace/src/encode/agentless-json.js +4 -1
- package/packages/dd-trace/src/telemetry/send-data.js +5 -0
- package/packages/dd-trace/src/telemetry/session-propagation.js +78 -0
- package/packages/dd-trace/src/telemetry/telemetry.js +3 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dd-trace",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.92.0",
|
|
4
4
|
"description": "Datadog APM tracing client for JavaScript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"typings": "index.d.ts",
|
|
@@ -143,7 +143,7 @@
|
|
|
143
143
|
"@datadog/native-iast-taint-tracking": "4.1.0",
|
|
144
144
|
"@datadog/native-metrics": "3.1.1",
|
|
145
145
|
"@datadog/openfeature-node-server": "^1.1.0",
|
|
146
|
-
"@datadog/pprof": "5.
|
|
146
|
+
"@datadog/pprof": "5.14.0",
|
|
147
147
|
"@datadog/wasm-js-rewriter": "5.0.1",
|
|
148
148
|
"@opentelemetry/api": ">=1.0.0 <1.10.0",
|
|
149
149
|
"@opentelemetry/api-logs": "<1.0.0",
|
|
@@ -12,7 +12,7 @@ const {
|
|
|
12
12
|
const childProcessChannel = dc.tracingChannel('datadog:child_process:execution')
|
|
13
13
|
|
|
14
14
|
// ignored exec method because it calls to execFile directly
|
|
15
|
-
const execAsyncMethods = ['execFile', 'spawn']
|
|
15
|
+
const execAsyncMethods = ['execFile', 'spawn', 'fork']
|
|
16
16
|
|
|
17
17
|
const names = ['child_process', 'node:child_process']
|
|
18
18
|
|
|
@@ -97,8 +97,10 @@ function wrapChildProcessSyncMethod (returnError, shell = false) {
|
|
|
97
97
|
return childProcessMethod.apply(this, arguments)
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
const
|
|
100
|
+
const callArgs = [...arguments]
|
|
101
|
+
const childProcessInfo = normalizeArgs(callArgs, shell)
|
|
101
102
|
const context = createContextFromChildProcessInfo(childProcessInfo)
|
|
103
|
+
context.callArgs = callArgs
|
|
102
104
|
|
|
103
105
|
return childProcessChannel.start.runStores(context, () => {
|
|
104
106
|
try {
|
|
@@ -108,7 +110,7 @@ function wrapChildProcessSyncMethod (returnError, shell = false) {
|
|
|
108
110
|
return returnError(error, context)
|
|
109
111
|
}
|
|
110
112
|
|
|
111
|
-
const result = childProcessMethod.apply(this,
|
|
113
|
+
const result = childProcessMethod.apply(this, context.callArgs)
|
|
112
114
|
context.result = result
|
|
113
115
|
|
|
114
116
|
return result
|
|
@@ -131,9 +133,11 @@ function wrapChildProcessCustomPromisifyMethod (customPromisifyMethod, shell) {
|
|
|
131
133
|
return customPromisifyMethod.apply(this, arguments)
|
|
132
134
|
}
|
|
133
135
|
|
|
134
|
-
const
|
|
136
|
+
const callArgs = [...arguments]
|
|
137
|
+
const childProcessInfo = normalizeArgs(callArgs, shell)
|
|
135
138
|
|
|
136
139
|
const context = createContextFromChildProcessInfo(childProcessInfo)
|
|
140
|
+
context.callArgs = callArgs
|
|
137
141
|
|
|
138
142
|
const { start, end, asyncStart, asyncEnd, error } = childProcessChannel
|
|
139
143
|
start.publish(context)
|
|
@@ -143,7 +147,7 @@ function wrapChildProcessCustomPromisifyMethod (customPromisifyMethod, shell) {
|
|
|
143
147
|
result = Promise.reject(context.abortController.signal.reason || new Error('Aborted'))
|
|
144
148
|
} else {
|
|
145
149
|
try {
|
|
146
|
-
result = customPromisifyMethod.apply(this,
|
|
150
|
+
result = customPromisifyMethod.apply(this, context.callArgs)
|
|
147
151
|
} catch (error) {
|
|
148
152
|
context.error = error
|
|
149
153
|
error.publish(context)
|
|
@@ -181,9 +185,11 @@ function wrapChildProcessAsyncMethod (ChildProcess, shell = false) {
|
|
|
181
185
|
return childProcessMethod.apply(this, arguments)
|
|
182
186
|
}
|
|
183
187
|
|
|
184
|
-
const
|
|
188
|
+
const callArgs = [...arguments]
|
|
189
|
+
const childProcessInfo = normalizeArgs(callArgs, shell)
|
|
185
190
|
|
|
186
191
|
const context = createContextFromChildProcessInfo(childProcessInfo)
|
|
192
|
+
context.callArgs = callArgs
|
|
187
193
|
return childProcessChannel.start.runStores(context, () => {
|
|
188
194
|
let childProcess
|
|
189
195
|
if (context.abortController.signal.aborted) {
|
|
@@ -194,7 +200,7 @@ function wrapChildProcessAsyncMethod (ChildProcess, shell = false) {
|
|
|
194
200
|
const error = context.abortController.signal.reason || new Error('Aborted')
|
|
195
201
|
childProcess.emit('error', error)
|
|
196
202
|
|
|
197
|
-
const cb =
|
|
203
|
+
const cb = context.callArgs[context.callArgs.length - 1]
|
|
198
204
|
if (typeof cb === 'function') {
|
|
199
205
|
cb(error)
|
|
200
206
|
}
|
|
@@ -202,7 +208,7 @@ function wrapChildProcessAsyncMethod (ChildProcess, shell = false) {
|
|
|
202
208
|
childProcess.emit('close')
|
|
203
209
|
})
|
|
204
210
|
} else {
|
|
205
|
-
childProcess = childProcessMethod.apply(this,
|
|
211
|
+
childProcess = childProcessMethod.apply(this, context.callArgs)
|
|
206
212
|
}
|
|
207
213
|
|
|
208
214
|
if (childProcess) {
|
|
@@ -277,7 +277,7 @@ function assertField (rootCtx, info, args) {
|
|
|
277
277
|
let field = fields[pathString]
|
|
278
278
|
|
|
279
279
|
if (!field) {
|
|
280
|
-
const fieldCtx = { info, rootCtx, args }
|
|
280
|
+
const fieldCtx = { info, rootCtx, args, path, pathString }
|
|
281
281
|
startResolveCh.publish(fieldCtx)
|
|
282
282
|
field = fields[pathString] = {
|
|
283
283
|
error: null,
|
|
@@ -58,8 +58,8 @@ function addVariableTags (config, span, variableValues) {
|
|
|
58
58
|
|
|
59
59
|
if (variableValues && config.variables) {
|
|
60
60
|
const variables = config.variables(variableValues)
|
|
61
|
-
for (const param
|
|
62
|
-
tags[`graphql.variables.${param}`] =
|
|
61
|
+
for (const [param, value] of Object.entries(variables)) {
|
|
62
|
+
tags[`graphql.variables.${param}`] = value
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
@@ -10,13 +10,13 @@ class GraphQLResolvePlugin extends TracingPlugin {
|
|
|
10
10
|
static operation = 'resolve'
|
|
11
11
|
|
|
12
12
|
start (fieldCtx) {
|
|
13
|
-
const { info, rootCtx, args } = fieldCtx
|
|
13
|
+
const { info, rootCtx, args, path: pathAsArray, pathString } = fieldCtx
|
|
14
14
|
|
|
15
|
-
const path = getPath(
|
|
15
|
+
const path = getPath(this.config, pathAsArray)
|
|
16
16
|
|
|
17
17
|
// we need to get the parent span to the field if it exists for correct span parenting
|
|
18
18
|
// of nested fields
|
|
19
|
-
const parentField = getParentField(rootCtx,
|
|
19
|
+
const parentField = getParentField(rootCtx, pathString)
|
|
20
20
|
const childOf = parentField?.ctx?.currentStore?.span
|
|
21
21
|
|
|
22
22
|
fieldCtx.parent = parentField
|
|
@@ -76,9 +76,9 @@ class GraphQLResolvePlugin extends TracingPlugin {
|
|
|
76
76
|
super(...args)
|
|
77
77
|
|
|
78
78
|
this.addTraceSub('updateField', (ctx) => {
|
|
79
|
-
const { field,
|
|
79
|
+
const { field, error, path: pathAsArray } = ctx
|
|
80
80
|
|
|
81
|
-
const path = getPath(
|
|
81
|
+
const path = getPath(this.config, pathAsArray)
|
|
82
82
|
|
|
83
83
|
if (!shouldInstrument(this.config, path)) return
|
|
84
84
|
|
|
@@ -118,28 +118,14 @@ function shouldInstrument (config, path) {
|
|
|
118
118
|
return config.depth < 0 || config.depth >= depth
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
function getPath (
|
|
122
|
-
|
|
123
|
-
? withCollapse(
|
|
124
|
-
:
|
|
125
|
-
return responsePathAsArray(info && info.path)
|
|
121
|
+
function getPath (config, pathAsArray) {
|
|
122
|
+
return config.collapse
|
|
123
|
+
? withCollapse(pathAsArray)
|
|
124
|
+
: pathAsArray
|
|
126
125
|
}
|
|
127
126
|
|
|
128
|
-
function
|
|
129
|
-
|
|
130
|
-
let curr = path
|
|
131
|
-
while (curr) {
|
|
132
|
-
flattened.push(curr.key)
|
|
133
|
-
curr = curr.prev
|
|
134
|
-
}
|
|
135
|
-
return flattened.reverse()
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function withCollapse (responsePathAsArray) {
|
|
139
|
-
return function () {
|
|
140
|
-
return responsePathAsArray.apply(this, arguments)
|
|
141
|
-
.map(segment => typeof segment === 'number' ? '*' : segment)
|
|
142
|
-
}
|
|
127
|
+
function withCollapse (pathAsArray) {
|
|
128
|
+
return pathAsArray.map(segment => typeof segment === 'number' ? '*' : segment)
|
|
143
129
|
}
|
|
144
130
|
|
|
145
131
|
function getResolverInfo (info, args) {
|
|
@@ -173,19 +159,20 @@ function getResolverInfo (info, args) {
|
|
|
173
159
|
return resolverInfo
|
|
174
160
|
}
|
|
175
161
|
|
|
176
|
-
function getParentField (parentCtx,
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
162
|
+
function getParentField (parentCtx, pathToString) {
|
|
163
|
+
let current = pathToString
|
|
164
|
+
|
|
165
|
+
while (current) {
|
|
166
|
+
const lastJoin = current.lastIndexOf('.')
|
|
167
|
+
if (lastJoin === -1) break
|
|
168
|
+
|
|
169
|
+
current = current.slice(0, lastJoin)
|
|
170
|
+
const field = parentCtx.fields[current]
|
|
171
|
+
|
|
172
|
+
if (field) return field
|
|
182
173
|
}
|
|
183
174
|
|
|
184
175
|
return null
|
|
185
176
|
}
|
|
186
177
|
|
|
187
|
-
function getField (parentCtx, path) {
|
|
188
|
-
return parentCtx.fields[path.join('.')]
|
|
189
|
-
}
|
|
190
|
-
|
|
191
178
|
module.exports = GraphQLResolvePlugin
|
|
@@ -211,7 +211,7 @@ function getHeaders (config) {
|
|
|
211
211
|
if (typeof header === 'string') {
|
|
212
212
|
const separatorIndex = header.indexOf(':')
|
|
213
213
|
result.push(separatorIndex === -1
|
|
214
|
-
? [header, undefined]
|
|
214
|
+
? [header.toLowerCase(), undefined]
|
|
215
215
|
: [
|
|
216
216
|
header.slice(0, separatorIndex).toLowerCase(),
|
|
217
217
|
header.slice(separatorIndex + 1),
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const pkg = require('../pkg')
|
|
4
4
|
const { isFalse, isTrue } = require('../util')
|
|
5
|
+
const { DD_MAJOR } = require('../../../../version')
|
|
5
6
|
const { getEnvironmentVariable: getEnv } = require('./helper')
|
|
6
7
|
|
|
7
8
|
const {
|
|
@@ -117,6 +118,7 @@ const defaultsWithoutSupportedConfigurationEntry = {
|
|
|
117
118
|
// TODO: These entries should be removed. They are off by default
|
|
118
119
|
// because they rely on other configs.
|
|
119
120
|
const defaultsWithConditionalRuntimeBehavior = {
|
|
121
|
+
startupLogs: DD_MAJOR >= 6,
|
|
120
122
|
isGitUploadEnabled: false,
|
|
121
123
|
isImpactedTestsEnabled: false,
|
|
122
124
|
isIntelligentTestRunnerEnabled: false,
|
|
@@ -49,6 +49,8 @@ const VALID_PROPAGATION_BEHAVIOR_EXTRACT = new Set(['continue', 'restart', 'igno
|
|
|
49
49
|
const VALID_LOG_LEVELS = new Set(['debug', 'info', 'warn', 'error'])
|
|
50
50
|
const DEFAULT_OTLP_PORT = 4318
|
|
51
51
|
const RUNTIME_ID = uuid()
|
|
52
|
+
// eslint-disable-next-line eslint-rules/eslint-process-env -- internal propagation, not user config
|
|
53
|
+
const ROOT_SESSION_ID = process.env.DD_ROOT_JS_SESSION_ID || RUNTIME_ID
|
|
52
54
|
const NAMING_VERSIONS = new Set(['v0', 'v1'])
|
|
53
55
|
const DEFAULT_NAMING_VERSION = 'v0'
|
|
54
56
|
|
|
@@ -145,6 +147,8 @@ class Config {
|
|
|
145
147
|
'runtime-id': RUNTIME_ID,
|
|
146
148
|
})
|
|
147
149
|
|
|
150
|
+
this.rootSessionId = ROOT_SESSION_ID
|
|
151
|
+
|
|
148
152
|
if (this.isCiVisibility) {
|
|
149
153
|
tagger.add(this.tags, {
|
|
150
154
|
[ORIGIN_KEY]: 'ciapp-test',
|
|
@@ -1125,6 +1129,8 @@ class Config {
|
|
|
1125
1129
|
setBoolean(calc, 'reportHostname', true)
|
|
1126
1130
|
// Clear sampling rules - server-side sampling handles this
|
|
1127
1131
|
calc['sampler.rules'] = []
|
|
1132
|
+
// Agentless intake only accepts 64-bit trace IDs; disable 128-bit generation
|
|
1133
|
+
setBoolean(calc, 'traceId128BitGenerationEnabled', false)
|
|
1128
1134
|
}
|
|
1129
1135
|
|
|
1130
1136
|
if (this.#isCiVisibility()) {
|
|
@@ -16,6 +16,9 @@ const SOFT_LIMIT = 8 * 1024 * 1024 // 8MB
|
|
|
16
16
|
function formatSpan (span, isFirstSpan) {
|
|
17
17
|
span = normalizeSpan(truncateSpan(span, false))
|
|
18
18
|
|
|
19
|
+
// Remove _dd.p.tid (the upper 64 bits of a 128-bit trace ID) since trace_id is truncated to lower 64 bits
|
|
20
|
+
delete span.meta['_dd.p.tid']
|
|
21
|
+
|
|
19
22
|
if (span.span_events) {
|
|
20
23
|
span.meta.events = JSON.stringify(span.span_events)
|
|
21
24
|
delete span.span_events
|
|
@@ -45,7 +48,7 @@ function formatSpan (span, isFirstSpan) {
|
|
|
45
48
|
*/
|
|
46
49
|
function spanToJSON (span) {
|
|
47
50
|
const result = {
|
|
48
|
-
trace_id: span.trace_id.toString(16).toLowerCase(),
|
|
51
|
+
trace_id: span.trace_id.toString(16).toLowerCase().slice(-16),
|
|
49
52
|
span_id: span.span_id.toString(16).toLowerCase(),
|
|
50
53
|
parent_id: span.parent_id.toString(16).toLowerCase(),
|
|
51
54
|
name: span.name,
|
|
@@ -91,12 +91,17 @@ let agentTelemetry = true
|
|
|
91
91
|
* @returns {Record<string, string>}
|
|
92
92
|
*/
|
|
93
93
|
function getHeaders (config, application, reqType) {
|
|
94
|
+
const sessionId = config.tags['runtime-id']
|
|
94
95
|
const headers = {
|
|
95
96
|
'content-type': 'application/json',
|
|
96
97
|
'dd-telemetry-api-version': 'v2',
|
|
97
98
|
'dd-telemetry-request-type': reqType,
|
|
98
99
|
'dd-client-library-language': application.language_name,
|
|
99
100
|
'dd-client-library-version': application.tracer_version,
|
|
101
|
+
'dd-session-id': sessionId,
|
|
102
|
+
}
|
|
103
|
+
if (config.rootSessionId && config.rootSessionId !== sessionId) {
|
|
104
|
+
headers['dd-root-session-id'] = config.rootSessionId
|
|
100
105
|
}
|
|
101
106
|
const debug = config.telemetry && config.telemetry.debug
|
|
102
107
|
if (debug) {
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const dc = require('dc-polyfill')
|
|
4
|
+
|
|
5
|
+
const childProcessChannel = dc.tracingChannel('datadog:child_process:execution')
|
|
6
|
+
|
|
7
|
+
let subscribed = false
|
|
8
|
+
let rootSessionId
|
|
9
|
+
let runtimeId
|
|
10
|
+
|
|
11
|
+
function injectSessionEnv (existingEnv) {
|
|
12
|
+
// eslint-disable-next-line eslint-rules/eslint-process-env -- not in supported-configurations.json
|
|
13
|
+
const base = existingEnv == null ? process.env : existingEnv
|
|
14
|
+
return {
|
|
15
|
+
...base,
|
|
16
|
+
DD_ROOT_JS_SESSION_ID: rootSessionId,
|
|
17
|
+
DD_PARENT_JS_SESSION_ID: runtimeId,
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function findOptionsIndex (args, shell) {
|
|
22
|
+
if (Array.isArray(args[1])) {
|
|
23
|
+
return { index: 2, exists: args[2] != null && typeof args[2] === 'object' }
|
|
24
|
+
}
|
|
25
|
+
if (args[1] != null && typeof args[1] === 'object') {
|
|
26
|
+
return { index: 1, exists: true }
|
|
27
|
+
}
|
|
28
|
+
if (!shell && args[2] != null && typeof args[2] === 'object') {
|
|
29
|
+
return { index: 2, exists: true }
|
|
30
|
+
}
|
|
31
|
+
return { index: shell ? 1 : 2, exists: false }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function onChildProcessStart (context) {
|
|
35
|
+
if (!context.callArgs) return
|
|
36
|
+
|
|
37
|
+
const args = context.callArgs
|
|
38
|
+
const { index, exists } = findOptionsIndex(args, context.shell)
|
|
39
|
+
|
|
40
|
+
if (exists) {
|
|
41
|
+
args[index] = { ...args[index], env: injectSessionEnv(args[index].env) }
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const opts = { env: injectSessionEnv(null) }
|
|
46
|
+
|
|
47
|
+
if (!context.shell && !Array.isArray(args[1])) {
|
|
48
|
+
args.splice(1, 0, [])
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (typeof args[index] === 'function') {
|
|
52
|
+
args.splice(index, 0, opts)
|
|
53
|
+
} else {
|
|
54
|
+
args[index] = opts
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const handler = { start: onChildProcessStart }
|
|
59
|
+
|
|
60
|
+
function start (config) {
|
|
61
|
+
if (!config.telemetry?.enabled || subscribed) return
|
|
62
|
+
subscribed = true
|
|
63
|
+
|
|
64
|
+
rootSessionId = config.rootSessionId
|
|
65
|
+
runtimeId = config.tags['runtime-id']
|
|
66
|
+
|
|
67
|
+
childProcessChannel.subscribe(handler)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function stop () {
|
|
71
|
+
if (!subscribed) return
|
|
72
|
+
childProcessChannel.unsubscribe(handler)
|
|
73
|
+
subscribed = false
|
|
74
|
+
rootSessionId = undefined
|
|
75
|
+
runtimeId = undefined
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = { start, stop, _onChildProcessStart: onChildProcessStart }
|
|
@@ -12,6 +12,7 @@ const endpoints = require('./endpoints')
|
|
|
12
12
|
const { sendData } = require('./send-data')
|
|
13
13
|
const { manager: metricsManager } = require('./metrics')
|
|
14
14
|
const telemetryLogger = require('./logs')
|
|
15
|
+
const sessionPropagation = require('./session-propagation')
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* @typedef {Record<string, unknown>} TelemetryPayloadObject
|
|
@@ -370,6 +371,7 @@ function start (aConfig, thePluginManager) {
|
|
|
370
371
|
dependencies.start(config, application, host, getRetryData, updateRetryData)
|
|
371
372
|
telemetryLogger.start(config)
|
|
372
373
|
endpoints.start(config, application, host, getRetryData, updateRetryData)
|
|
374
|
+
sessionPropagation.start(config)
|
|
373
375
|
|
|
374
376
|
sendData(config, application, host, 'app-started', appStarted(config))
|
|
375
377
|
|
|
@@ -397,6 +399,7 @@ function stop () {
|
|
|
397
399
|
telemetryStopChannel.publish(getTelemetryData())
|
|
398
400
|
|
|
399
401
|
endpoints.stop()
|
|
402
|
+
sessionPropagation.stop()
|
|
400
403
|
config = undefined
|
|
401
404
|
}
|
|
402
405
|
|