dd-trace 5.65.0 → 5.66.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 +8 -8
- package/packages/datadog-instrumentations/src/express.js +3 -7
- package/packages/datadog-instrumentations/src/graphql.js +10 -6
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/register.js +10 -2
- package/packages/datadog-instrumentations/src/playwright.js +25 -9
- package/packages/datadog-instrumentations/src/prisma.js +8 -10
- package/packages/datadog-instrumentations/src/ws.js +136 -0
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +30 -3
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +9 -6
- package/packages/datadog-plugin-aws-sdk/src/util.js +61 -1
- package/packages/datadog-plugin-express/src/code_origin.js +11 -0
- package/packages/datadog-plugin-graphql/src/index.js +3 -0
- package/packages/datadog-plugin-ws/src/close.js +69 -0
- package/packages/datadog-plugin-ws/src/index.js +26 -0
- package/packages/datadog-plugin-ws/src/producer.js +60 -0
- package/packages/datadog-plugin-ws/src/receiver.js +70 -0
- package/packages/datadog-plugin-ws/src/server.js +79 -0
- package/packages/datadog-shimmer/src/shimmer.js +11 -2
- package/packages/dd-trace/src/appsec/blocking.js +29 -0
- package/packages/dd-trace/src/appsec/channels.js +4 -2
- package/packages/dd-trace/src/appsec/index.js +7 -2
- package/packages/dd-trace/src/appsec/rasp/fs-plugin.js +1 -0
- package/packages/dd-trace/src/appsec/rasp/index.js +25 -7
- package/packages/dd-trace/src/appsec/rasp/lfi.js +1 -1
- package/packages/dd-trace/src/appsec/rasp/utils.js +13 -2
- package/packages/dd-trace/src/config.js +12 -0
- package/packages/dd-trace/src/guardrails/index.js +11 -3
- package/packages/dd-trace/src/guardrails/telemetry.js +15 -16
- package/packages/dd-trace/src/llmobs/tagger.js +2 -1
- package/packages/dd-trace/src/log/writer.js +1 -1
- package/packages/dd-trace/src/plugin_manager.js +8 -2
- package/packages/dd-trace/src/plugins/index.js +2 -1
- package/packages/dd-trace/src/plugins/util/ip_extractor.js +48 -45
- package/packages/dd-trace/src/profiling/profilers/wall.js +9 -3
- package/packages/dd-trace/src/service-naming/schemas/v0/index.js +2 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/websocket.js +30 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/index.js +2 -1
- package/packages/dd-trace/src/service-naming/schemas/v1/websocket.js +30 -0
- package/packages/dd-trace/src/supported-configurations.json +3 -0
|
@@ -145,7 +145,10 @@ module.exports = class PluginManager {
|
|
|
145
145
|
ciVisAgentlessLogSubmissionEnabled,
|
|
146
146
|
isTestDynamicInstrumentationEnabled,
|
|
147
147
|
isServiceUserProvided,
|
|
148
|
-
middlewareTracingEnabled
|
|
148
|
+
middlewareTracingEnabled,
|
|
149
|
+
traceWebsocketMessagesEnabled,
|
|
150
|
+
traceWebsocketMessagesInheritSampling,
|
|
151
|
+
traceWebsocketMessagesSeparateTraces
|
|
149
152
|
} = this._tracerConfig
|
|
150
153
|
|
|
151
154
|
const sharedConfig = {
|
|
@@ -160,7 +163,10 @@ module.exports = class PluginManager {
|
|
|
160
163
|
ciVisibilityTestSessionName,
|
|
161
164
|
ciVisAgentlessLogSubmissionEnabled,
|
|
162
165
|
isTestDynamicInstrumentationEnabled,
|
|
163
|
-
isServiceUserProvided
|
|
166
|
+
isServiceUserProvided,
|
|
167
|
+
traceWebsocketMessagesEnabled,
|
|
168
|
+
traceWebsocketMessagesInheritSampling,
|
|
169
|
+
traceWebsocketMessagesSeparateTraces
|
|
164
170
|
}
|
|
165
171
|
|
|
166
172
|
if (logInjection !== undefined) {
|
|
@@ -102,5 +102,6 @@ module.exports = {
|
|
|
102
102
|
get sharedb () { return require('../../../datadog-plugin-sharedb/src') },
|
|
103
103
|
get tedious () { return require('../../../datadog-plugin-tedious/src') },
|
|
104
104
|
get undici () { return require('../../../datadog-plugin-undici/src') },
|
|
105
|
-
get winston () { return require('../../../datadog-plugin-winston/src') }
|
|
105
|
+
get winston () { return require('../../../datadog-plugin-winston/src') },
|
|
106
|
+
get ws () { return require('../../../datadog-plugin-ws/src') }
|
|
106
107
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const { BlockList } = require('net')
|
|
4
3
|
const net = require('net')
|
|
5
4
|
|
|
6
5
|
const FORWARED_HEADER_NAME = 'forwarded'
|
|
@@ -32,7 +31,7 @@ const privateCIDRs = [
|
|
|
32
31
|
'fd00::/8'
|
|
33
32
|
]
|
|
34
33
|
|
|
35
|
-
const privateIPMatcher = new BlockList()
|
|
34
|
+
const privateIPMatcher = new net.BlockList()
|
|
36
35
|
|
|
37
36
|
for (const cidr of privateCIDRs) {
|
|
38
37
|
const [address, prefix] = cidr.split('/')
|
|
@@ -45,7 +44,11 @@ function extractIp (config, req) {
|
|
|
45
44
|
if (config.clientIpHeader) {
|
|
46
45
|
if (!headers) return
|
|
47
46
|
|
|
48
|
-
const
|
|
47
|
+
const ipHeaderName = config.clientIpHeader
|
|
48
|
+
const header = headers[ipHeaderName]
|
|
49
|
+
if (typeof header !== 'string') return
|
|
50
|
+
|
|
51
|
+
const ip = findFirstIp(header, ipHeaderName === FORWARED_HEADER_NAME)
|
|
49
52
|
return ip.public || ip.private
|
|
50
53
|
}
|
|
51
54
|
|
|
@@ -53,12 +56,14 @@ function extractIp (config, req) {
|
|
|
53
56
|
if (headers) {
|
|
54
57
|
for (const ipHeaderName of ipHeaderList) {
|
|
55
58
|
const header = headers[ipHeaderName]
|
|
56
|
-
|
|
59
|
+
if (typeof header !== 'string') continue
|
|
60
|
+
|
|
61
|
+
const ip = findFirstIp(header, ipHeaderName === FORWARED_HEADER_NAME)
|
|
57
62
|
|
|
58
|
-
if (
|
|
59
|
-
return
|
|
60
|
-
} else if (!firstPrivateIp &&
|
|
61
|
-
firstPrivateIp =
|
|
63
|
+
if (ip.public) {
|
|
64
|
+
return ip.public
|
|
65
|
+
} else if (!firstPrivateIp && ip.private) {
|
|
66
|
+
firstPrivateIp = ip.private
|
|
62
67
|
}
|
|
63
68
|
}
|
|
64
69
|
}
|
|
@@ -66,25 +71,39 @@ function extractIp (config, req) {
|
|
|
66
71
|
return firstPrivateIp || req.socket?.remoteAddress
|
|
67
72
|
}
|
|
68
73
|
|
|
69
|
-
function
|
|
70
|
-
return !privateIPMatcher.check(ip, type === 6 ? 'ipv6' : 'ipv4')
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function findFirstIp (str) {
|
|
74
|
+
function findFirstIp (str, isForwardedHeader) {
|
|
74
75
|
const result = {}
|
|
75
76
|
if (!str) return result
|
|
76
77
|
|
|
77
78
|
const splitted = str.split(',')
|
|
78
79
|
|
|
79
|
-
for (
|
|
80
|
-
|
|
80
|
+
for (let chunk of splitted) {
|
|
81
|
+
if (isForwardedHeader) {
|
|
82
|
+
// find "for" directive
|
|
83
|
+
const forDirective = chunk.split(';').find(subchunk => subchunk.trim().toLowerCase().startsWith('for='))
|
|
84
|
+
|
|
85
|
+
// if found remove the "for=" prefix
|
|
86
|
+
// else keep going as is
|
|
87
|
+
if (forDirective) {
|
|
88
|
+
chunk = forDirective.slice(4)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
chunk = chunk.trim()
|
|
93
|
+
|
|
94
|
+
// trim potential double quotes
|
|
95
|
+
if (chunk.startsWith('"') && chunk.endsWith('"')) {
|
|
96
|
+
chunk = chunk.slice(1, -1).trim()
|
|
97
|
+
}
|
|
81
98
|
|
|
82
|
-
// TODO:
|
|
99
|
+
// TODO: when min node support is v24 we can instead use net.SocketAddress.parse()
|
|
100
|
+
chunk = cleanIp(chunk)
|
|
101
|
+
if (!chunk) continue
|
|
83
102
|
|
|
84
103
|
const type = net.isIP(chunk)
|
|
85
104
|
if (!type) continue
|
|
86
105
|
|
|
87
|
-
if (
|
|
106
|
+
if (!privateIPMatcher.check(chunk, type === 6 ? 'ipv6' : 'ipv4')) {
|
|
88
107
|
// it's public, return it immediately
|
|
89
108
|
result.public = chunk
|
|
90
109
|
return result
|
|
@@ -97,37 +116,21 @@ function findFirstIp (str) {
|
|
|
97
116
|
return result
|
|
98
117
|
}
|
|
99
118
|
|
|
100
|
-
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
if (!str) return result
|
|
107
|
-
|
|
108
|
-
const splitted = str.split(',')
|
|
109
|
-
|
|
110
|
-
for (const part of splitted) {
|
|
111
|
-
const chunk = part.trim()
|
|
112
|
-
|
|
113
|
-
for (const regex of forwardedRegexps) {
|
|
114
|
-
const ip = regex.exec(chunk)?.[1]
|
|
115
|
-
|
|
116
|
-
const type = net.isIP(ip)
|
|
117
|
-
if (!type) continue
|
|
118
|
-
|
|
119
|
-
if (isPublicIp(ip, type)) {
|
|
120
|
-
// it's public, return it immediately
|
|
121
|
-
result.public = ip
|
|
122
|
-
return result
|
|
123
|
-
}
|
|
119
|
+
function cleanIp (input) {
|
|
120
|
+
const colonIndex = input.indexOf(':')
|
|
121
|
+
if (colonIndex !== -1 && input.includes('.')) {
|
|
122
|
+
// treat it as ipv4 with port
|
|
123
|
+
return input.slice(0, colonIndex).trim()
|
|
124
|
+
}
|
|
124
125
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
const closingBracketIndex = input.indexOf(']')
|
|
127
|
+
if (closingBracketIndex !== -1 && input.startsWith('[')) {
|
|
128
|
+
// treat as ipv6 with brackets
|
|
129
|
+
return input.slice(1, closingBracketIndex).trim()
|
|
128
130
|
}
|
|
129
131
|
|
|
130
|
-
|
|
132
|
+
// no need to clean it
|
|
133
|
+
return input
|
|
131
134
|
}
|
|
132
135
|
|
|
133
136
|
module.exports = {
|
|
@@ -13,6 +13,7 @@ const {
|
|
|
13
13
|
getThreadLabels,
|
|
14
14
|
encodeProfileAsync
|
|
15
15
|
} = require('./shared')
|
|
16
|
+
const TRACE_ENDPOINT_LABEL = 'trace endpoint'
|
|
16
17
|
|
|
17
18
|
const { isWebServerSpan, endpointNameFromTags, getStartedSpans } = require('../webspan-utils')
|
|
18
19
|
|
|
@@ -251,7 +252,12 @@ class NativeWallProfiler {
|
|
|
251
252
|
this._enter()
|
|
252
253
|
this._lastSampleCount = 0
|
|
253
254
|
}
|
|
254
|
-
|
|
255
|
+
|
|
256
|
+
// Mark thread labels and trace endpoint label as good deduplication candidates
|
|
257
|
+
const lowCardinalityLabels = Object.keys(getThreadLabels())
|
|
258
|
+
lowCardinalityLabels.push(TRACE_ENDPOINT_LABEL)
|
|
259
|
+
|
|
260
|
+
const profile = this._pprof.time.stop(restart, this._generateLabels, lowCardinalityLabels)
|
|
255
261
|
|
|
256
262
|
if (restart) {
|
|
257
263
|
const v8BugDetected = this._pprof.time.v8ProfilerStuckEventLoopDetected()
|
|
@@ -312,10 +318,10 @@ class NativeWallProfiler {
|
|
|
312
318
|
labels[LOCAL_ROOT_SPAN_ID_LABEL] = rootSpanId
|
|
313
319
|
}
|
|
314
320
|
if (webTags !== undefined && Object.keys(webTags).length !== 0) {
|
|
315
|
-
labels[
|
|
321
|
+
labels[TRACE_ENDPOINT_LABEL] = endpointNameFromTags(webTags)
|
|
316
322
|
} else if (endpoint) {
|
|
317
323
|
// fallback to endpoint computed when sample was taken
|
|
318
|
-
labels[
|
|
324
|
+
labels[TRACE_ENDPOINT_LABEL] = endpoint
|
|
319
325
|
}
|
|
320
326
|
|
|
321
327
|
return labels
|
|
@@ -6,5 +6,6 @@ const storage = require('./storage')
|
|
|
6
6
|
const graphql = require('./graphql')
|
|
7
7
|
const web = require('./web')
|
|
8
8
|
const serverless = require('./serverless')
|
|
9
|
+
const websocket = require('./websocket')
|
|
9
10
|
|
|
10
|
-
module.exports = new SchemaDefinition({ messaging, storage, web, graphql, serverless })
|
|
11
|
+
module.exports = new SchemaDefinition({ messaging, storage, web, graphql, serverless, websocket })
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const websocket = {
|
|
4
|
+
request: {
|
|
5
|
+
ws: {
|
|
6
|
+
opName: () => 'web.request',
|
|
7
|
+
serviceName: ({ pluginConfig, tracerService }) => pluginConfig.service || tracerService
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
producer: {
|
|
11
|
+
ws: {
|
|
12
|
+
opName: () => 'websocket.send',
|
|
13
|
+
serviceName: ({ pluginConfig, tracerService }) => pluginConfig.service || tracerService
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
consumer: {
|
|
17
|
+
ws: {
|
|
18
|
+
opName: () => 'websocket.receive',
|
|
19
|
+
serviceName: ({ pluginConfig, tracerService }) => pluginConfig.service || tracerService
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
close: {
|
|
23
|
+
ws: {
|
|
24
|
+
opName: () => 'websocket.close',
|
|
25
|
+
serviceName: ({ pluginConfig, tracerService }) => pluginConfig.service || tracerService
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = websocket
|
|
@@ -6,5 +6,6 @@ const storage = require('./storage')
|
|
|
6
6
|
const graphql = require('./graphql')
|
|
7
7
|
const web = require('./web')
|
|
8
8
|
const serverless = require('./serverless')
|
|
9
|
+
const websocket = require('./websocket')
|
|
9
10
|
|
|
10
|
-
module.exports = new SchemaDefinition({ messaging, storage, web, graphql, serverless })
|
|
11
|
+
module.exports = new SchemaDefinition({ messaging, storage, web, graphql, serverless, websocket })
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const websocket = {
|
|
4
|
+
request: {
|
|
5
|
+
ws: {
|
|
6
|
+
opName: () => 'web.request',
|
|
7
|
+
serviceName: ({ pluginConfig, tracerService }) => pluginConfig.service || tracerService
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
producer: {
|
|
11
|
+
ws: {
|
|
12
|
+
opName: () => 'websocket.send',
|
|
13
|
+
serviceName: ({ pluginConfig, tracerService }) => pluginConfig.service || tracerService
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
consumer: {
|
|
17
|
+
ws: {
|
|
18
|
+
opName: () => 'websocket.receive',
|
|
19
|
+
serviceName: ({ pluginConfig, tracerService }) => pluginConfig.service || tracerService
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
close: {
|
|
23
|
+
ws: {
|
|
24
|
+
opName: () => 'websocket.close',
|
|
25
|
+
serviceName: ({ pluginConfig, tracerService }) => pluginConfig.service || tracerService
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = websocket
|
|
@@ -414,6 +414,9 @@
|
|
|
414
414
|
"DD_TRACE_VITEST_ENABLED": ["A"],
|
|
415
415
|
"DD_TRACE_VITEST_RUNNER_ENABLED": ["A"],
|
|
416
416
|
"DD_TRACE_VM_ENABLED": ["A"],
|
|
417
|
+
"DD_TRACE_WEBSOCKET_MESSAGES_ENABLED":["A"],
|
|
418
|
+
"DD_TRACE_WEBSOCKET_MESSAGES_INHERIT_SAMPLING": ["A"],
|
|
419
|
+
"DD_TRACE_WEBSOCKET_MESSAGES_SEPARATE_TRACES":["A"],
|
|
417
420
|
"DD_TRACE_WHEN_ENABLED": ["A"],
|
|
418
421
|
"DD_TRACE_WINSTON_ENABLED": ["A"],
|
|
419
422
|
"DD_TRACE_WORKERPOOL_ENABLED": ["A"],
|