dd-trace 5.0.0-pre-0a30f33 → 5.0.0-pre-cd5fbdb
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/README.md +4 -9
- package/index.d.ts +43 -0
- package/package.json +2 -2
- package/packages/datadog-esbuild/index.js +20 -6
- package/packages/datadog-instrumentations/src/jest.js +1 -1
- package/packages/datadog-instrumentations/src/kafkajs.js +3 -4
- package/packages/datadog-instrumentations/src/mocha.js +3 -0
- package/packages/datadog-instrumentations/src/next.js +86 -2
- package/packages/datadog-instrumentations/src/openai.js +1 -1
- package/packages/datadog-plugin-cypress/src/plugin.js +32 -5
- package/packages/datadog-plugin-graphql/src/index.js +3 -3
- package/packages/datadog-plugin-mongodb-core/src/index.js +6 -2
- package/packages/datadog-plugin-next/src/index.js +4 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +12 -1
- package/packages/dd-trace/src/dogstatsd.js +65 -5
- package/packages/dd-trace/src/exporters/agent/writer.js +9 -9
- package/packages/dd-trace/src/exporters/common/request.js +13 -4
- package/packages/dd-trace/src/opentracing/span.js +13 -13
- package/packages/dd-trace/src/opentracing/tracer.js +3 -3
- package/packages/dd-trace/src/plugins/util/test.js +2 -2
- package/packages/dd-trace/src/profiling/config.js +3 -1
- package/packages/dd-trace/src/profiling/profilers/wall.js +23 -7
- package/packages/dd-trace/src/proxy.js +23 -2
- package/packages/dd-trace/src/telemetry/dependencies.js +15 -0
- package/packages/dd-trace/src/telemetry/index.js +11 -1
- /package/packages/dd-trace/src/{metrics.js → runtime_metrics.js} +0 -0
package/README.md
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/dd-trace)
|
|
4
4
|
[](https://www.npmjs.com/package/dd-trace/v/latest-node12)
|
|
5
|
-
[](https://www.npmjs.com/package/dd-trace/v/latest-node12)
|
|
6
|
-
[](https://www.npmjs.com/package/dd-trace/v/dev)
|
|
7
5
|
[](https://codecov.io/gh/DataDog/dd-trace-js)
|
|
8
6
|
|
|
9
7
|
<img align="right" src="https://user-images.githubusercontent.com/551402/208212084-1d0c07e2-4135-4c61-b2da-8f2fddbc66ed.png" alt="Bits the dog JavaScript" width="200px"/>
|
|
@@ -28,12 +26,12 @@ Most of the documentation for `dd-trace` is available on these webpages:
|
|
|
28
26
|
| Release Line | Latest Version | Node.js | Status |Initial Release | End of Life |
|
|
29
27
|
| :---: | :---: | :---: | :---: | :---: | :---: |
|
|
30
28
|
| [`v1`](https://github.com/DataDog/dd-trace-js/tree/v1.x) |  | `>= v12` | **End of Life** | 2021-07-13 | 2022-02-25 |
|
|
31
|
-
| [`v2`](https://github.com/DataDog/dd-trace-js/tree/v2.x) |  | `>= v12` | **
|
|
29
|
+
| [`v2`](https://github.com/DataDog/dd-trace-js/tree/v2.x) |  | `>= v12` | **End of Life** | 2022-01-28 | 2023-08-15 |
|
|
32
30
|
| [`v3`](https://github.com/DataDog/dd-trace-js/tree/v3.x) |  | `>= v14` | **Maintenance** | 2022-08-15 | 2024-05-15 |
|
|
33
31
|
| [`v4`](https://github.com/DataDog/dd-trace-js/tree/v4.x) |  | `>= v16` | **Current** | 2023-05-12 | Unknown |
|
|
34
32
|
|
|
35
|
-
We currently maintain
|
|
36
|
-
Features and bug fixes that are merged are released to the `v4` line and, if appropriate, also the `
|
|
33
|
+
We currently maintain two release lines, namely `v3` and `v4`.
|
|
34
|
+
Features and bug fixes that are merged are released to the `v4` line and, if appropriate, also the `v3` line.
|
|
37
35
|
|
|
38
36
|
For any new projects it is recommended to use the `v4` release line:
|
|
39
37
|
|
|
@@ -42,15 +40,12 @@ $ npm install dd-trace
|
|
|
42
40
|
$ yarn add dd-trace
|
|
43
41
|
```
|
|
44
42
|
|
|
45
|
-
However, existing projects that already use the `
|
|
43
|
+
However, existing projects that already use the `v3` release line, or projects that need to support EOL versions of Node.js, may continue to use these release lines.
|
|
46
44
|
This is done by specifying the version when installing the package.
|
|
47
|
-
Note that we also publish to npm using a `latest-node12` and `latest-node14` tag that can also be used for install:
|
|
48
45
|
|
|
49
46
|
```sh
|
|
50
47
|
$ npm install dd-trace@3
|
|
51
48
|
$ yarn add dd-trace@3
|
|
52
|
-
$ npm install dd-trace@latest-node14
|
|
53
|
-
$ yarn add dd-trace@latest-node14
|
|
54
49
|
```
|
|
55
50
|
|
|
56
51
|
Any backwards-breaking functionality that is introduced into the library will result in an increase of the major version of the library and therefore a new release line.
|
package/index.d.ts
CHANGED
|
@@ -121,6 +121,8 @@ export declare interface Tracer extends opentracing.Tracer {
|
|
|
121
121
|
appsec: Appsec;
|
|
122
122
|
|
|
123
123
|
TracerProvider: opentelemetry.TracerProvider;
|
|
124
|
+
|
|
125
|
+
dogstatsd: DogStatsD;
|
|
124
126
|
}
|
|
125
127
|
|
|
126
128
|
export declare interface TraceOptions extends Analyzable {
|
|
@@ -642,6 +644,47 @@ export declare interface User {
|
|
|
642
644
|
[key: string]: string | undefined
|
|
643
645
|
}
|
|
644
646
|
|
|
647
|
+
export declare interface DogStatsD {
|
|
648
|
+
/**
|
|
649
|
+
* Increments a metric by the specified value, optionally specifying tags.
|
|
650
|
+
* @param {string} stat The dot-separated metric name.
|
|
651
|
+
* @param {number} value The amount to increment the stat by.
|
|
652
|
+
* @param {[tag:string]:string|number} tags Tags to pass along, such as `[ 'foo:bar' ]`. Values are combined with config.tags.
|
|
653
|
+
*/
|
|
654
|
+
increment(stat: string, value?: number, tags?: { [tag: string]: string|number }): void
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* Decrements a metric by the specified value, optionally specifying tags.
|
|
658
|
+
* @param {string} stat The dot-separated metric name.
|
|
659
|
+
* @param {number} value The amount to decrement the stat by.
|
|
660
|
+
* @param {[tag:string]:string|number} tags Tags to pass along, such as `[ 'foo:bar' ]`. Values are combined with config.tags.
|
|
661
|
+
*/
|
|
662
|
+
decrement(stat: string, value?: number, tags?: { [tag: string]: string|number }): void
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* Sets a distribution value, optionally specifying tags.
|
|
666
|
+
* @param {string} stat The dot-separated metric name.
|
|
667
|
+
* @param {number} value The amount to increment the stat by.
|
|
668
|
+
* @param {[tag:string]:string|number} tags Tags to pass along, such as `[ 'foo:bar' ]`. Values are combined with config.tags.
|
|
669
|
+
*/
|
|
670
|
+
distribution(stat: string, value?: number, tags?: { [tag: string]: string|number }): void
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* Sets a gauge value, optionally specifying tags.
|
|
674
|
+
* @param {string} stat The dot-separated metric name.
|
|
675
|
+
* @param {number} value The amount to increment the stat by.
|
|
676
|
+
* @param {[tag:string]:string|number} tags Tags to pass along, such as `[ 'foo:bar' ]`. Values are combined with config.tags.
|
|
677
|
+
*/
|
|
678
|
+
gauge(stat: string, value?: number, tags?: { [tag: string]: string|number }): void
|
|
679
|
+
|
|
680
|
+
/**
|
|
681
|
+
* Forces any unsent metrics to be sent
|
|
682
|
+
*
|
|
683
|
+
* @beta This method is experimental and could be removed in future versions.
|
|
684
|
+
*/
|
|
685
|
+
flush(): void
|
|
686
|
+
}
|
|
687
|
+
|
|
645
688
|
export declare interface Appsec {
|
|
646
689
|
/**
|
|
647
690
|
* Links a successful login event to the current trace. Will link the passed user to the current trace with Appsec.setUser() internally.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dd-trace",
|
|
3
|
-
"version": "5.0.0-pre-
|
|
3
|
+
"version": "5.0.0-pre-cd5fbdb",
|
|
4
4
|
"description": "Datadog APM tracing client for JavaScript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"typings": "index.d.ts",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"@datadog/native-iast-rewriter": "2.0.1",
|
|
72
72
|
"@datadog/native-iast-taint-tracking": "1.5.0",
|
|
73
73
|
"@datadog/native-metrics": "^2.0.0",
|
|
74
|
-
"@datadog/pprof": "3.
|
|
74
|
+
"@datadog/pprof": "3.2.0",
|
|
75
75
|
"@datadog/sketches-js": "^2.1.0",
|
|
76
76
|
"@opentelemetry/api": "^1.0.0",
|
|
77
77
|
"@opentelemetry/core": "^1.14.0",
|
|
@@ -53,10 +53,25 @@ module.exports.setup = function (build) {
|
|
|
53
53
|
const externalModules = new Set(build.initialOptions.external || [])
|
|
54
54
|
build.onResolve({ filter: /.*/ }, args => {
|
|
55
55
|
if (externalModules.has(args.path)) {
|
|
56
|
+
// Internal Node.js packages will still be instrumented via require()
|
|
56
57
|
if (DEBUG) console.log(`EXTERNAL: ${args.path}`)
|
|
57
58
|
return
|
|
58
59
|
}
|
|
59
60
|
|
|
61
|
+
// TODO: Should this also check for namespace === 'file'?
|
|
62
|
+
if (args.path.startsWith('.') && !args.importer.includes('node_modules/')) {
|
|
63
|
+
// This is local application code, not an instrumented package
|
|
64
|
+
if (DEBUG) console.log(`LOCAL: ${args.path}`)
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// TODO: Should this also check for namespace === 'file'?
|
|
69
|
+
if (args.path.startsWith('@') && !args.importer.includes('node_modules/')) {
|
|
70
|
+
// This is the Next.js convention for loading local files
|
|
71
|
+
if (DEBUG) console.log(`@LOCAL: ${args.path}`)
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
|
|
60
75
|
let fullPathToModule
|
|
61
76
|
try {
|
|
62
77
|
fullPathToModule = dotFriendlyResolve(args.path, args.resolveDir)
|
|
@@ -65,12 +80,11 @@ module.exports.setup = function (build) {
|
|
|
65
80
|
return
|
|
66
81
|
}
|
|
67
82
|
const extracted = extractPackageAndModulePath(fullPathToModule)
|
|
68
|
-
const packageName = args.path
|
|
69
83
|
|
|
70
84
|
const internal = builtins.has(args.path)
|
|
71
85
|
|
|
72
86
|
if (args.namespace === 'file' && (
|
|
73
|
-
modulesOfInterest.has(
|
|
87
|
+
modulesOfInterest.has(args.path) || modulesOfInterest.has(`${extracted.pkg}/${extracted.path}`))
|
|
74
88
|
) {
|
|
75
89
|
// The file namespace is used when requiring files from disk in userland
|
|
76
90
|
|
|
@@ -90,7 +104,7 @@ module.exports.setup = function (build) {
|
|
|
90
104
|
|
|
91
105
|
const packageJson = require(pathToPackageJson)
|
|
92
106
|
|
|
93
|
-
if (DEBUG) console.log(`RESOLVE: ${
|
|
107
|
+
if (DEBUG) console.log(`RESOLVE: ${args.path}@${packageJson.version}`)
|
|
94
108
|
|
|
95
109
|
// https://esbuild.github.io/plugins/#on-resolve-arguments
|
|
96
110
|
return {
|
|
@@ -101,17 +115,17 @@ module.exports.setup = function (build) {
|
|
|
101
115
|
pkg: extracted.pkg,
|
|
102
116
|
path: extracted.path,
|
|
103
117
|
full: fullPathToModule,
|
|
104
|
-
raw:
|
|
118
|
+
raw: args.path,
|
|
105
119
|
internal
|
|
106
120
|
}
|
|
107
121
|
}
|
|
108
122
|
} else if (args.namespace === NAMESPACE) {
|
|
109
123
|
// The datadog namespace is used when requiring files that are injected during the onLoad stage
|
|
110
124
|
|
|
111
|
-
if (builtins.has(
|
|
125
|
+
if (builtins.has(args.path)) return
|
|
112
126
|
|
|
113
127
|
return {
|
|
114
|
-
path: require.resolve(
|
|
128
|
+
path: require.resolve(args.path, { paths: [ args.resolveDir ] }),
|
|
115
129
|
namespace: 'file'
|
|
116
130
|
}
|
|
117
131
|
}
|
|
@@ -105,7 +105,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
105
105
|
await super.handleTestEvent(event, state)
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
const setNameToParams = (name, params) => { this.nameToParams[name] = params }
|
|
108
|
+
const setNameToParams = (name, params) => { this.nameToParams[name] = [...params] }
|
|
109
109
|
|
|
110
110
|
if (event.name === 'setup') {
|
|
111
111
|
if (this.global.test) {
|
|
@@ -15,15 +15,14 @@ const consumerStartCh = channel('apm:kafkajs:consume:start')
|
|
|
15
15
|
const consumerFinishCh = channel('apm:kafkajs:consume:finish')
|
|
16
16
|
const consumerErrorCh = channel('apm:kafkajs:consume:error')
|
|
17
17
|
|
|
18
|
-
addHook({ name: 'kafkajs', versions: ['>=1.4'] }, (
|
|
19
|
-
class Kafka extends
|
|
18
|
+
addHook({ name: 'kafkajs', file: 'src/index.js', versions: ['>=1.4'] }, (BaseKafka) => {
|
|
19
|
+
class Kafka extends BaseKafka {
|
|
20
20
|
constructor (options) {
|
|
21
21
|
super(options)
|
|
22
22
|
this._brokers = (options.brokers && typeof options.brokers !== 'function')
|
|
23
23
|
? options.brokers.join(',') : undefined
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
-
obj.Kafka = Kafka
|
|
27
26
|
|
|
28
27
|
shimmer.wrap(Kafka.prototype, 'producer', createProducer => function () {
|
|
29
28
|
const producer = createProducer.apply(this, arguments)
|
|
@@ -117,5 +116,5 @@ addHook({ name: 'kafkajs', versions: ['>=1.4'] }, (obj) => {
|
|
|
117
116
|
}
|
|
118
117
|
return consumer
|
|
119
118
|
})
|
|
120
|
-
return
|
|
119
|
+
return Kafka
|
|
121
120
|
})
|
|
@@ -441,6 +441,9 @@ addHook({
|
|
|
441
441
|
file: 'lib/cli/run-helpers.js'
|
|
442
442
|
}, (run) => {
|
|
443
443
|
shimmer.wrap(run, 'runMocha', runMocha => async function () {
|
|
444
|
+
if (!testStartCh.hasSubscribers) {
|
|
445
|
+
return runMocha.apply(this, arguments)
|
|
446
|
+
}
|
|
444
447
|
const mocha = arguments[0]
|
|
445
448
|
/**
|
|
446
449
|
* This attaches `run` to the global context, which we'll call after
|
|
@@ -12,6 +12,7 @@ const errorChannel = channel('apm:next:request:error')
|
|
|
12
12
|
const pageLoadChannel = channel('apm:next:page:load')
|
|
13
13
|
|
|
14
14
|
const requests = new WeakSet()
|
|
15
|
+
const requestToNextjsPagePath = new WeakMap()
|
|
15
16
|
|
|
16
17
|
function wrapHandleRequest (handleRequest) {
|
|
17
18
|
return function (req, res, pathname, query) {
|
|
@@ -132,6 +133,57 @@ function instrument (req, res, handler) {
|
|
|
132
133
|
})
|
|
133
134
|
}
|
|
134
135
|
|
|
136
|
+
function wrapSetupServerWorker (setupServerWorker) {
|
|
137
|
+
return function (requestHandler) {
|
|
138
|
+
arguments[0] = shimmer.wrap(requestHandler, wrapRequestHandler(requestHandler))
|
|
139
|
+
return setupServerWorker.apply(this, arguments)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function wrapInitialize (initialize) {
|
|
144
|
+
return async function () {
|
|
145
|
+
const result = await initialize.apply(this, arguments)
|
|
146
|
+
if (Array.isArray(result)) {
|
|
147
|
+
const requestHandler = result[0]
|
|
148
|
+
result[0] = shimmer.wrap(requestHandler, wrapRequestHandler(requestHandler))
|
|
149
|
+
}
|
|
150
|
+
return result
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function wrapRequestHandler (requestHandler) {
|
|
155
|
+
return function (req, res) {
|
|
156
|
+
return instrument(req, res, async () => {
|
|
157
|
+
const result = await requestHandler.apply(this, arguments) // apply here first to get page path association
|
|
158
|
+
|
|
159
|
+
const page = requestToNextjsPagePath.get(req)
|
|
160
|
+
if (page && pageLoadChannel.hasSubscribers) pageLoadChannel.publish({ page })
|
|
161
|
+
|
|
162
|
+
return result
|
|
163
|
+
})
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// these two functions make sure we get path groups for routes in standalone,
|
|
168
|
+
// as it doesn't route through `next-server`/`base-server`
|
|
169
|
+
function wrapGetResolveRoutes (getResolveRoutes) {
|
|
170
|
+
return function () {
|
|
171
|
+
const result = getResolveRoutes.apply(this, arguments)
|
|
172
|
+
return shimmer.wrap(result, wrapResolveRoutes(result))
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function wrapResolveRoutes (resolveRoutes) {
|
|
177
|
+
return async function (req) {
|
|
178
|
+
const result = await resolveRoutes.apply(this, arguments)
|
|
179
|
+
if (result && result.matchedOutput) {
|
|
180
|
+
const path = result.matchedOutput.itemPath
|
|
181
|
+
requestToNextjsPagePath.set(req, path)
|
|
182
|
+
}
|
|
183
|
+
return result
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
135
187
|
function finish (ctx, result, err) {
|
|
136
188
|
if (err) {
|
|
137
189
|
ctx.error = err
|
|
@@ -147,11 +199,27 @@ function finish (ctx, result, err) {
|
|
|
147
199
|
return result
|
|
148
200
|
}
|
|
149
201
|
|
|
202
|
+
addHook({
|
|
203
|
+
name: 'next',
|
|
204
|
+
versions: ['>=13.4.13'],
|
|
205
|
+
file: 'dist/server/lib/router-utils/resolve-routes.js'
|
|
206
|
+
}, resolveRoutesModule => shimmer.wrap(resolveRoutesModule, 'getResolveRoutes', wrapGetResolveRoutes))
|
|
207
|
+
|
|
208
|
+
addHook({
|
|
209
|
+
name: 'next',
|
|
210
|
+
versions: ['13.4.13'],
|
|
211
|
+
file: 'dist/server/lib/setup-server-worker.js'
|
|
212
|
+
}, setupServerWorker => shimmer.wrap(setupServerWorker, 'initializeServerWorker', wrapSetupServerWorker))
|
|
213
|
+
|
|
214
|
+
addHook({
|
|
215
|
+
name: 'next',
|
|
216
|
+
versions: ['>=13.4.15'],
|
|
217
|
+
file: 'dist/server/lib/router-server.js'
|
|
218
|
+
}, routerServer => shimmer.wrap(routerServer, 'initialize', wrapInitialize))
|
|
219
|
+
|
|
150
220
|
addHook({ name: 'next', versions: ['>=13.2'], file: 'dist/server/next-server.js' }, nextServer => {
|
|
151
221
|
const Server = nextServer.default
|
|
152
222
|
|
|
153
|
-
shimmer.wrap(Server.prototype, 'handleRequest', wrapHandleRequest)
|
|
154
|
-
shimmer.wrap(Server.prototype, 'handleApiRequest', wrapHandleApiRequestWithMatch)
|
|
155
223
|
shimmer.wrap(Server.prototype, 'renderToResponse', wrapRenderToResponse)
|
|
156
224
|
shimmer.wrap(Server.prototype, 'renderErrorToResponse', wrapRenderErrorToResponse)
|
|
157
225
|
shimmer.wrap(Server.prototype, 'findPageComponents', wrapFindPageComponents)
|
|
@@ -159,6 +227,22 @@ addHook({ name: 'next', versions: ['>=13.2'], file: 'dist/server/next-server.js'
|
|
|
159
227
|
return nextServer
|
|
160
228
|
})
|
|
161
229
|
|
|
230
|
+
// these functions wrapped in all versions above 13.2 except:
|
|
231
|
+
// 13.4.13 due to tests failing when these functions are wrapped
|
|
232
|
+
// 13.4.14 due to it not being in the NPM registry/officially released
|
|
233
|
+
addHook({
|
|
234
|
+
name: 'next',
|
|
235
|
+
versions: ['>=13.2 <13.4.13', '>=13.4.15'],
|
|
236
|
+
file: 'dist/server/next-server.js'
|
|
237
|
+
}, nextServer => {
|
|
238
|
+
const Server = nextServer.default
|
|
239
|
+
|
|
240
|
+
shimmer.wrap(Server.prototype, 'handleRequest', wrapHandleRequest)
|
|
241
|
+
shimmer.wrap(Server.prototype, 'handleApiRequest', wrapHandleApiRequestWithMatch)
|
|
242
|
+
|
|
243
|
+
return nextServer
|
|
244
|
+
})
|
|
245
|
+
|
|
162
246
|
addHook({ name: 'next', versions: ['>=11.1 <13.2'], file: 'dist/server/next-server.js' }, nextServer => {
|
|
163
247
|
const Server = nextServer.default
|
|
164
248
|
|
|
@@ -10,7 +10,7 @@ const startCh = channel('apm:openai:request:start')
|
|
|
10
10
|
const finishCh = channel('apm:openai:request:finish')
|
|
11
11
|
const errorCh = channel('apm:openai:request:error')
|
|
12
12
|
|
|
13
|
-
addHook({ name: 'openai', file: 'dist/api.js', versions: ['>=3.0.0'] }, exports => {
|
|
13
|
+
addHook({ name: 'openai', file: 'dist/api.js', versions: ['>=3.0.0 <4'] }, exports => {
|
|
14
14
|
const methodNames = Object.getOwnPropertyNames(exports.OpenAIApi.prototype)
|
|
15
15
|
methodNames.shift() // remove leading 'constructor' method
|
|
16
16
|
|
|
@@ -25,6 +25,7 @@ const {
|
|
|
25
25
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
26
26
|
const { ORIGIN_KEY, COMPONENT } = require('../../dd-trace/src/constants')
|
|
27
27
|
const log = require('../../dd-trace/src/log')
|
|
28
|
+
const NoopTracer = require('../../dd-trace/src/noop/tracer')
|
|
28
29
|
|
|
29
30
|
const TEST_FRAMEWORK_NAME = 'cypress'
|
|
30
31
|
|
|
@@ -119,10 +120,32 @@ function getSkippableTests (isSuitesSkippingEnabled, tracer, testConfiguration)
|
|
|
119
120
|
})
|
|
120
121
|
}
|
|
121
122
|
|
|
123
|
+
const noopTask = {
|
|
124
|
+
'dd:testSuiteStart': () => {
|
|
125
|
+
return null
|
|
126
|
+
},
|
|
127
|
+
'dd:beforeEach': () => {
|
|
128
|
+
return {}
|
|
129
|
+
},
|
|
130
|
+
'dd:afterEach': () => {
|
|
131
|
+
return null
|
|
132
|
+
},
|
|
133
|
+
'dd:addTags': () => {
|
|
134
|
+
return null
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
122
138
|
module.exports = (on, config) => {
|
|
123
139
|
let isTestsSkipped = false
|
|
124
140
|
const skippedTests = []
|
|
125
141
|
const tracer = require('../../dd-trace')
|
|
142
|
+
|
|
143
|
+
// The tracer was not init correctly for whatever reason (such as invalid DD_SITE)
|
|
144
|
+
if (tracer._tracer instanceof NoopTracer) {
|
|
145
|
+
// We still need to register these tasks or the support file will fail
|
|
146
|
+
return on('task', noopTask)
|
|
147
|
+
}
|
|
148
|
+
|
|
126
149
|
const testEnvironmentMetadata = getTestEnvironmentMetadata(TEST_FRAMEWORK_NAME)
|
|
127
150
|
|
|
128
151
|
const {
|
|
@@ -328,12 +351,16 @@ module.exports = (on, config) => {
|
|
|
328
351
|
}
|
|
329
352
|
|
|
330
353
|
return new Promise(resolve => {
|
|
331
|
-
|
|
332
|
-
|
|
354
|
+
const exporter = tracer._tracer._exporter
|
|
355
|
+
if (!exporter) {
|
|
356
|
+
return resolve(null)
|
|
357
|
+
}
|
|
358
|
+
if (exporter.flush) {
|
|
359
|
+
exporter.flush(() => {
|
|
333
360
|
resolve(null)
|
|
334
361
|
})
|
|
335
|
-
} else {
|
|
336
|
-
|
|
362
|
+
} else if (exporter._writer) {
|
|
363
|
+
exporter._writer.flush(() => {
|
|
337
364
|
resolve(null)
|
|
338
365
|
})
|
|
339
366
|
}
|
|
@@ -375,7 +402,7 @@ module.exports = (on, config) => {
|
|
|
375
402
|
'dd:afterEach': ({ test, coverage }) => {
|
|
376
403
|
const { state, error, isRUMActive, testSourceLine, testSuite, testName } = test
|
|
377
404
|
if (activeSpan) {
|
|
378
|
-
if (coverage && tracer._tracer._exporter
|
|
405
|
+
if (coverage && isCodeCoverageEnabled && tracer._tracer._exporter && tracer._tracer._exporter.exportCoverage) {
|
|
379
406
|
const coverageFiles = getCoveredFilenamesFromCoverage(coverage)
|
|
380
407
|
const relativeCoverageFiles = coverageFiles.map(file => getTestSuitePath(file, rootDir))
|
|
381
408
|
const { _traceId, _spanId } = testSuiteSpan.context()
|
|
@@ -56,9 +56,9 @@ function getVariablesFilter (config) {
|
|
|
56
56
|
|
|
57
57
|
function getHooks (config) {
|
|
58
58
|
const noop = () => { }
|
|
59
|
-
const execute =
|
|
60
|
-
const parse =
|
|
61
|
-
const validate =
|
|
59
|
+
const execute = config.hooks?.execute || noop
|
|
60
|
+
const parse = config.hooks?.parse || noop
|
|
61
|
+
const validate = config.hooks?.validate || noop
|
|
62
62
|
|
|
63
63
|
return { execute, parse, validate }
|
|
64
64
|
}
|
|
@@ -36,10 +36,14 @@ class MongodbCorePlugin extends DatabasePlugin {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
function sanitizeBigInt (data) {
|
|
40
|
+
return JSON.stringify(data, (_key, value) => typeof value === 'bigint' ? value.toString() : value)
|
|
41
|
+
}
|
|
42
|
+
|
|
39
43
|
function getQuery (cmd) {
|
|
40
44
|
if (!cmd || typeof cmd !== 'object' || Array.isArray(cmd)) return
|
|
41
|
-
if (cmd.query) return
|
|
42
|
-
if (cmd.filter) return
|
|
45
|
+
if (cmd.query) return sanitizeBigInt(limitDepth(cmd.query))
|
|
46
|
+
if (cmd.filter) return sanitizeBigInt(limitDepth(cmd.filter))
|
|
43
47
|
}
|
|
44
48
|
|
|
45
49
|
function getResource (plugin, ns, query, operationName) {
|
|
@@ -4,6 +4,7 @@ const ServerPlugin = require('../../dd-trace/src/plugins/server')
|
|
|
4
4
|
const { storage } = require('../../datadog-core')
|
|
5
5
|
const analyticsSampler = require('../../dd-trace/src/analytics_sampler')
|
|
6
6
|
const { COMPONENT } = require('../../dd-trace/src/constants')
|
|
7
|
+
const web = require('../../dd-trace/src/plugins/util/web')
|
|
7
8
|
|
|
8
9
|
class NextPlugin extends ServerPlugin {
|
|
9
10
|
static get id () {
|
|
@@ -49,6 +50,7 @@ class NextPlugin extends ServerPlugin {
|
|
|
49
50
|
|
|
50
51
|
const span = store.span
|
|
51
52
|
const error = span.context()._tags['error']
|
|
53
|
+
const page = span.context()._tags['next.page']
|
|
52
54
|
|
|
53
55
|
if (!this.config.validateStatus(res.statusCode) && !error) {
|
|
54
56
|
span.setTag('error', true)
|
|
@@ -58,6 +60,8 @@ class NextPlugin extends ServerPlugin {
|
|
|
58
60
|
'http.status_code': res.statusCode
|
|
59
61
|
})
|
|
60
62
|
|
|
63
|
+
if (page) web.setRoute(req, page)
|
|
64
|
+
|
|
61
65
|
this.config.hooks.request(span, req, res)
|
|
62
66
|
|
|
63
67
|
span.finish()
|
|
@@ -76,7 +76,18 @@ class SensitiveHandler {
|
|
|
76
76
|
while (nextSensitive != null && contains(nextTainted, nextSensitive)) {
|
|
77
77
|
const redactionStart = nextSensitive.start - nextTainted.start
|
|
78
78
|
const redactionEnd = nextSensitive.end - nextTainted.start
|
|
79
|
-
|
|
79
|
+
if (redactionStart === redactionEnd) {
|
|
80
|
+
this.writeRedactedValuePart(valueParts, 0)
|
|
81
|
+
} else {
|
|
82
|
+
this.redactSource(
|
|
83
|
+
sources,
|
|
84
|
+
redactedSources,
|
|
85
|
+
redactedSourcesContext,
|
|
86
|
+
sourceIndex,
|
|
87
|
+
redactionStart,
|
|
88
|
+
redactionEnd
|
|
89
|
+
)
|
|
90
|
+
}
|
|
80
91
|
nextSensitive = sensitive.shift()
|
|
81
92
|
}
|
|
82
93
|
|
|
@@ -35,14 +35,14 @@ class DogStatsDClient {
|
|
|
35
35
|
this._udp6 = this._socket('udp6')
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
gauge (stat, value, tags) {
|
|
39
|
-
this._add(stat, value, TYPE_GAUGE, tags)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
38
|
increment (stat, value, tags) {
|
|
43
39
|
this._add(stat, value, TYPE_COUNTER, tags)
|
|
44
40
|
}
|
|
45
41
|
|
|
42
|
+
gauge (stat, value, tags) {
|
|
43
|
+
this._add(stat, value, TYPE_GAUGE, tags)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
46
|
distribution (stat, value, tags) {
|
|
47
47
|
this._add(stat, value, TYPE_DISTRIBUTION, tags)
|
|
48
48
|
}
|
|
@@ -153,7 +153,67 @@ class NoopDogStatsDClient {
|
|
|
153
153
|
flush () { }
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
// This is a simplified user-facing proxy to the underlying DogStatsDClient instance
|
|
157
|
+
class CustomMetrics {
|
|
158
|
+
constructor (options) {
|
|
159
|
+
this.dogstatsd = new DogStatsDClient(options)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
increment (stat, value = 1, tags) {
|
|
163
|
+
return this.dogstatsd.increment(
|
|
164
|
+
stat,
|
|
165
|
+
value,
|
|
166
|
+
CustomMetrics.tagTranslator(tags)
|
|
167
|
+
)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
decrement (stat, value = 1, tags) {
|
|
171
|
+
return this.dogstatsd.increment(
|
|
172
|
+
stat,
|
|
173
|
+
value * -1,
|
|
174
|
+
CustomMetrics.tagTranslator(tags)
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
gauge (stat, value, tags) {
|
|
179
|
+
return this.dogstatsd.gauge(
|
|
180
|
+
stat,
|
|
181
|
+
value,
|
|
182
|
+
CustomMetrics.tagTranslator(tags)
|
|
183
|
+
)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
distribution (stat, value, tags) {
|
|
187
|
+
return this.dogstatsd.distribution(
|
|
188
|
+
stat,
|
|
189
|
+
value,
|
|
190
|
+
CustomMetrics.tagTranslator(tags)
|
|
191
|
+
)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
flush () {
|
|
195
|
+
return this.dogstatsd.flush()
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Exposing { tagName: 'tagValue' } to the end user
|
|
200
|
+
* These are translated into [ 'tagName:tagValue' ] for internal use
|
|
201
|
+
*/
|
|
202
|
+
static tagTranslator (objTags) {
|
|
203
|
+
const arrTags = []
|
|
204
|
+
|
|
205
|
+
if (!objTags) return arrTags
|
|
206
|
+
|
|
207
|
+
for (const [key, value] of Object.entries(objTags)) {
|
|
208
|
+
arrTags.push(`${key}:${value}`)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return arrTags
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
156
215
|
module.exports = {
|
|
157
216
|
DogStatsDClient,
|
|
158
|
-
NoopDogStatsDClient
|
|
217
|
+
NoopDogStatsDClient,
|
|
218
|
+
CustomMetrics
|
|
159
219
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const request = require('../common/request')
|
|
4
4
|
const { startupLog } = require('../../startup-log')
|
|
5
|
-
const
|
|
5
|
+
const runtimeMetrics = require('../../runtime_metrics')
|
|
6
6
|
const log = require('../../log')
|
|
7
7
|
const tracerVersion = require('../../../../../package.json').version
|
|
8
8
|
const BaseWriter = require('../common/writer')
|
|
@@ -22,19 +22,19 @@ class Writer extends BaseWriter {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
_sendPayload (data, count, done) {
|
|
25
|
-
|
|
25
|
+
runtimeMetrics.increment(`${METRIC_PREFIX}.requests`, true)
|
|
26
26
|
|
|
27
27
|
const { _headers, _lookup, _protocolVersion, _url } = this
|
|
28
28
|
makeRequest(_protocolVersion, data, count, _url, _headers, _lookup, true, (err, res, status) => {
|
|
29
29
|
if (status) {
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
runtimeMetrics.increment(`${METRIC_PREFIX}.responses`, true)
|
|
31
|
+
runtimeMetrics.increment(`${METRIC_PREFIX}.responses.by.status`, `status:${status}`, true)
|
|
32
32
|
} else if (err) {
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
runtimeMetrics.increment(`${METRIC_PREFIX}.errors`, true)
|
|
34
|
+
runtimeMetrics.increment(`${METRIC_PREFIX}.errors.by.name`, `name:${err.name}`, true)
|
|
35
35
|
|
|
36
36
|
if (err.code) {
|
|
37
|
-
|
|
37
|
+
runtimeMetrics.increment(`${METRIC_PREFIX}.errors.by.code`, `code:${err.code}`, true)
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -53,8 +53,8 @@ class Writer extends BaseWriter {
|
|
|
53
53
|
} catch (e) {
|
|
54
54
|
log.error(e)
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
runtimeMetrics.increment(`${METRIC_PREFIX}.errors`, true)
|
|
57
|
+
runtimeMetrics.increment(`${METRIC_PREFIX}.errors.by.name`, `name:${e.name}`, true)
|
|
58
58
|
}
|
|
59
59
|
done()
|
|
60
60
|
})
|
|
@@ -42,10 +42,19 @@ function urlToOptions (url) {
|
|
|
42
42
|
return options
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
function fromUrlString (
|
|
46
|
-
|
|
47
|
-
? urlToOptions(new URL(
|
|
48
|
-
: urlParse(
|
|
45
|
+
function fromUrlString (urlString) {
|
|
46
|
+
const url = typeof urlToHttpOptions === 'function'
|
|
47
|
+
? urlToOptions(new URL(urlString))
|
|
48
|
+
: urlParse(urlString)
|
|
49
|
+
|
|
50
|
+
// Add the 'hostname' back if we're using named pipes
|
|
51
|
+
if (url.protocol === 'unix:' && url.host === '.') {
|
|
52
|
+
const udsPath = urlString.replace(/^unix:/, '')
|
|
53
|
+
url.path = udsPath
|
|
54
|
+
url.pathname = udsPath
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return url
|
|
49
58
|
}
|
|
50
59
|
|
|
51
60
|
function request (data, options, callback) {
|
|
@@ -8,7 +8,7 @@ const semver = require('semver')
|
|
|
8
8
|
const SpanContext = require('./span_context')
|
|
9
9
|
const id = require('../id')
|
|
10
10
|
const tagger = require('../tagger')
|
|
11
|
-
const
|
|
11
|
+
const runtimeMetrics = require('../runtime_metrics')
|
|
12
12
|
const log = require('../log')
|
|
13
13
|
const { storage } = require('../../../datadog-core')
|
|
14
14
|
const telemetryMetrics = require('../telemetry/metrics')
|
|
@@ -79,11 +79,11 @@ class DatadogSpan {
|
|
|
79
79
|
this._startTime = fields.startTime || this._getTime()
|
|
80
80
|
|
|
81
81
|
if (DD_TRACE_EXPERIMENTAL_SPAN_COUNTS && finishedRegistry) {
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
runtimeMetrics.increment('runtime.node.spans.unfinished')
|
|
83
|
+
runtimeMetrics.increment('runtime.node.spans.unfinished.by.name', `span_name:${operationName}`)
|
|
84
84
|
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
runtimeMetrics.increment('runtime.node.spans.open') // unfinished for real
|
|
86
|
+
runtimeMetrics.increment('runtime.node.spans.open.by.name', `span_name:${operationName}`)
|
|
87
87
|
|
|
88
88
|
unfinishedRegistry.register(this, operationName, this)
|
|
89
89
|
}
|
|
@@ -159,13 +159,13 @@ class DatadogSpan {
|
|
|
159
159
|
getIntegrationCounter('span_finished', this._integrationName).inc()
|
|
160
160
|
|
|
161
161
|
if (DD_TRACE_EXPERIMENTAL_SPAN_COUNTS && finishedRegistry) {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
162
|
+
runtimeMetrics.decrement('runtime.node.spans.unfinished')
|
|
163
|
+
runtimeMetrics.decrement('runtime.node.spans.unfinished.by.name', `span_name:${this._name}`)
|
|
164
|
+
runtimeMetrics.increment('runtime.node.spans.finished')
|
|
165
|
+
runtimeMetrics.increment('runtime.node.spans.finished.by.name', `span_name:${this._name}`)
|
|
166
166
|
|
|
167
|
-
|
|
168
|
-
|
|
167
|
+
runtimeMetrics.decrement('runtime.node.spans.open') // unfinished for real
|
|
168
|
+
runtimeMetrics.decrement('runtime.node.spans.open.by.name', `span_name:${this._name}`)
|
|
169
169
|
|
|
170
170
|
unfinishedRegistry.unregister(this)
|
|
171
171
|
finishedRegistry.register(this, this._name)
|
|
@@ -243,8 +243,8 @@ function createRegistry (type) {
|
|
|
243
243
|
if (!semver.satisfies(process.version, '>=14.6')) return
|
|
244
244
|
|
|
245
245
|
return new global.FinalizationRegistry(name => {
|
|
246
|
-
|
|
247
|
-
|
|
246
|
+
runtimeMetrics.decrement(`runtime.node.spans.${type}`)
|
|
247
|
+
runtimeMetrics.decrement(`runtime.node.spans.${type}.by.name`, [`span_name:${name}`])
|
|
248
248
|
})
|
|
249
249
|
}
|
|
250
250
|
|
|
@@ -11,7 +11,7 @@ const LogPropagator = require('./propagation/log')
|
|
|
11
11
|
const formats = require('../../../../ext/formats')
|
|
12
12
|
|
|
13
13
|
const log = require('../log')
|
|
14
|
-
const
|
|
14
|
+
const runtimeMetrics = require('../runtime_metrics')
|
|
15
15
|
const getExporter = require('../exporter')
|
|
16
16
|
const SpanContext = require('./span_context')
|
|
17
17
|
|
|
@@ -82,7 +82,7 @@ class DatadogTracer {
|
|
|
82
82
|
this._propagators[format].inject(spanContext, carrier)
|
|
83
83
|
} catch (e) {
|
|
84
84
|
log.error(e)
|
|
85
|
-
|
|
85
|
+
runtimeMetrics.increment('datadog.tracer.node.inject.errors', true)
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
@@ -91,7 +91,7 @@ class DatadogTracer {
|
|
|
91
91
|
return this._propagators[format].extract(carrier)
|
|
92
92
|
} catch (e) {
|
|
93
93
|
log.error(e)
|
|
94
|
-
|
|
94
|
+
runtimeMetrics.increment('datadog.tracer.node.extract.errors', true)
|
|
95
95
|
return null
|
|
96
96
|
}
|
|
97
97
|
}
|
|
@@ -139,13 +139,13 @@ function removeInvalidMetadata (metadata) {
|
|
|
139
139
|
return Object.keys(metadata).reduce((filteredTags, tag) => {
|
|
140
140
|
if (tag === GIT_REPOSITORY_URL) {
|
|
141
141
|
if (!validateGitRepositoryUrl(metadata[GIT_REPOSITORY_URL])) {
|
|
142
|
-
log.error(
|
|
142
|
+
log.error(`Repository URL is not a valid repository URL: ${metadata[GIT_REPOSITORY_URL]}.`)
|
|
143
143
|
return filteredTags
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
146
|
if (tag === GIT_COMMIT_SHA) {
|
|
147
147
|
if (!validateGitCommitSha(metadata[GIT_COMMIT_SHA])) {
|
|
148
|
-
log.error(
|
|
148
|
+
log.error(`Git commit SHA must be a full-length git SHA: ${metadata[GIT_COMMIT_SHA]}.`)
|
|
149
149
|
return filteredTags
|
|
150
150
|
}
|
|
151
151
|
}
|
|
@@ -31,6 +31,7 @@ class Config {
|
|
|
31
31
|
DD_PROFILING_UPLOAD_PERIOD,
|
|
32
32
|
DD_PROFILING_PPROF_PREFIX,
|
|
33
33
|
DD_PROFILING_HEAP_ENABLED,
|
|
34
|
+
DD_PROFILING_V8_PROFILER_BUG_WORKAROUND,
|
|
34
35
|
DD_PROFILING_WALLTIME_ENABLED,
|
|
35
36
|
DD_PROFILING_EXPERIMENTAL_OOM_MONITORING_ENABLED,
|
|
36
37
|
DD_PROFILING_EXPERIMENTAL_OOM_HEAP_LIMIT_EXTENSION_SIZE,
|
|
@@ -76,7 +77,8 @@ class Config {
|
|
|
76
77
|
this.debugSourceMaps = isTrue(coalesce(options.debugSourceMaps, DD_PROFILING_DEBUG_SOURCE_MAPS, false))
|
|
77
78
|
this.endpointCollectionEnabled = endpointCollectionEnabled
|
|
78
79
|
this.pprofPrefix = pprofPrefix
|
|
79
|
-
|
|
80
|
+
this.v8ProfilerBugWorkaroundEnabled = isTrue(coalesce(options.v8ProfilerBugWorkaround,
|
|
81
|
+
DD_PROFILING_V8_PROFILER_BUG_WORKAROUND, true))
|
|
80
82
|
const hostname = coalesce(options.hostname, DD_AGENT_HOST) || 'localhost'
|
|
81
83
|
const port = coalesce(options.port, DD_TRACE_AGENT_PORT) || 8126
|
|
82
84
|
this.url = new URL(coalesce(options.url, DD_TRACE_AGENT_URL, format({
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
const { storage } = require('../../../../datadog-core')
|
|
4
4
|
|
|
5
5
|
const dc = require('../../../../diagnostics_channel')
|
|
6
|
+
const { HTTP_METHOD, HTTP_ROUTE, RESOURCE_NAME, SPAN_TYPE } = require('../../../../../ext/tags')
|
|
7
|
+
const { WEB } = require('../../../../../ext/types')
|
|
8
|
+
const runtimeMetrics = require('../../runtime_metrics')
|
|
6
9
|
|
|
7
10
|
const beforeCh = dc.channel('dd-trace:storage:before')
|
|
8
11
|
const enterCh = dc.channel('dd-trace:storage:enter')
|
|
@@ -41,13 +44,13 @@ function getSpanContextTags (span) {
|
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
function isWebServerSpan (tags) {
|
|
44
|
-
return tags[
|
|
47
|
+
return tags[SPAN_TYPE] === WEB
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
function endpointNameFromTags (tags) {
|
|
48
|
-
return tags[
|
|
49
|
-
tags[
|
|
50
|
-
tags[
|
|
51
|
+
return tags[RESOURCE_NAME] || [
|
|
52
|
+
tags[HTTP_METHOD],
|
|
53
|
+
tags[HTTP_ROUTE]
|
|
51
54
|
].filter(v => v).join(' ')
|
|
52
55
|
}
|
|
53
56
|
|
|
@@ -81,6 +84,7 @@ class NativeWallProfiler {
|
|
|
81
84
|
this._flushIntervalMillis = options.flushInterval || 60 * 1e3 // 60 seconds
|
|
82
85
|
this._codeHotspotsEnabled = !!options.codeHotspotsEnabled
|
|
83
86
|
this._endpointCollectionEnabled = !!options.endpointCollectionEnabled
|
|
87
|
+
this._v8ProfilerBugWorkaroundEnabled = !!options.v8ProfilerBugWorkaroundEnabled
|
|
84
88
|
this._mapper = undefined
|
|
85
89
|
this._pprof = undefined
|
|
86
90
|
|
|
@@ -99,7 +103,7 @@ class NativeWallProfiler {
|
|
|
99
103
|
|
|
100
104
|
if (this._codeHotspotsEnabled && !this._emittedFFMessage && this._logger) {
|
|
101
105
|
this._logger.debug(
|
|
102
|
-
`Wall profiler: Enable
|
|
106
|
+
`Wall profiler: Enable trace_show_breakdown_profiling_for_node feature flag to see code hotspots.`)
|
|
103
107
|
this._emittedFFMessage = true
|
|
104
108
|
}
|
|
105
109
|
|
|
@@ -120,7 +124,8 @@ class NativeWallProfiler {
|
|
|
120
124
|
durationMillis: this._flushIntervalMillis,
|
|
121
125
|
sourceMapper: this._mapper,
|
|
122
126
|
withContexts: this._codeHotspotsEnabled,
|
|
123
|
-
lineNumbers: false
|
|
127
|
+
lineNumbers: false,
|
|
128
|
+
workaroundV8Bug: this._v8ProfilerBugWorkaroundEnabled
|
|
124
129
|
})
|
|
125
130
|
|
|
126
131
|
if (this._codeHotspotsEnabled) {
|
|
@@ -170,7 +175,18 @@ class NativeWallProfiler {
|
|
|
170
175
|
this._enter()
|
|
171
176
|
this._lastSampleCount = 0
|
|
172
177
|
}
|
|
173
|
-
|
|
178
|
+
const profile = this._pprof.time.stop(restart, this._codeHotspotsEnabled ? generateLabels : undefined)
|
|
179
|
+
if (restart) {
|
|
180
|
+
const v8BugDetected = this._pprof.time.v8ProfilerStuckEventLoopDetected()
|
|
181
|
+
if (v8BugDetected === 1) {
|
|
182
|
+
this._logger?.warn('Wall profiler: possible v8 profiler stuck event loop detected.')
|
|
183
|
+
runtimeMetrics.increment('runtime.node.profiler.v8_cpu_profiler_maybe_stuck_event_loop', undefined, true)
|
|
184
|
+
} else if (v8BugDetected === 2) {
|
|
185
|
+
this._logger?.warn('Wall profiler: v8 profiler stuck event loop detected.')
|
|
186
|
+
runtimeMetrics.increment('runtime.node.profiler.v8_cpu_profiler_stuck_event_loop', undefined, true)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return profile
|
|
174
190
|
}
|
|
175
191
|
|
|
176
192
|
profile () {
|
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
const NoopProxy = require('./noop/proxy')
|
|
3
3
|
const DatadogTracer = require('./tracer')
|
|
4
4
|
const Config = require('./config')
|
|
5
|
-
const
|
|
5
|
+
const runtimeMetrics = require('./runtime_metrics')
|
|
6
6
|
const log = require('./log')
|
|
7
7
|
const { setStartupLogPluginManager } = require('./startup-log')
|
|
8
8
|
const telemetry = require('./telemetry')
|
|
9
9
|
const PluginManager = require('./plugin_manager')
|
|
10
10
|
const remoteConfig = require('./appsec/remote_config')
|
|
11
11
|
const AppsecSdk = require('./appsec/sdk')
|
|
12
|
+
const dogstatsd = require('./dogstatsd')
|
|
12
13
|
|
|
13
14
|
class Tracer extends NoopProxy {
|
|
14
15
|
constructor () {
|
|
@@ -16,6 +17,7 @@ class Tracer extends NoopProxy {
|
|
|
16
17
|
|
|
17
18
|
this._initialized = false
|
|
18
19
|
this._pluginManager = new PluginManager(this)
|
|
20
|
+
this.dogstatsd = new dogstatsd.NoopDogStatsDClient()
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
init (options) {
|
|
@@ -26,6 +28,25 @@ class Tracer extends NoopProxy {
|
|
|
26
28
|
try {
|
|
27
29
|
const config = new Config(options) // TODO: support dynamic code config
|
|
28
30
|
|
|
31
|
+
if (config.dogstatsd) {
|
|
32
|
+
// Custom Metrics
|
|
33
|
+
this.dogstatsd = new dogstatsd.CustomMetrics({
|
|
34
|
+
host: config.dogstatsd.hostname,
|
|
35
|
+
port: config.dogstatsd.port,
|
|
36
|
+
tags: [
|
|
37
|
+
// these are the Runtime Metrics default tags
|
|
38
|
+
// Python also uses these as default Custom Metrics tags
|
|
39
|
+
`service:${config.tags.service}`,
|
|
40
|
+
`env:${config.tags.env}`,
|
|
41
|
+
`version:${config.tags.version}`
|
|
42
|
+
]
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
setInterval(() => {
|
|
46
|
+
this.dogstatsd.flush()
|
|
47
|
+
}, 10 * 1000).unref()
|
|
48
|
+
}
|
|
49
|
+
|
|
29
50
|
if (config.remoteConfig.enabled && !config.isCiVisibility) {
|
|
30
51
|
const rc = remoteConfig.enable(config)
|
|
31
52
|
|
|
@@ -58,7 +79,7 @@ class Tracer extends NoopProxy {
|
|
|
58
79
|
}
|
|
59
80
|
|
|
60
81
|
if (config.runtimeMetrics) {
|
|
61
|
-
|
|
82
|
+
runtimeMetrics.start(config)
|
|
62
83
|
}
|
|
63
84
|
|
|
64
85
|
if (config.tracing) {
|
|
@@ -15,6 +15,7 @@ const FILE_URI_START = `file://`
|
|
|
15
15
|
const moduleLoadStartChannel = dc.channel('dd-trace:moduleLoadStart')
|
|
16
16
|
|
|
17
17
|
let immediate, config, application, host
|
|
18
|
+
let isFirstModule = true
|
|
18
19
|
|
|
19
20
|
function waitAndSend (config, application, host) {
|
|
20
21
|
if (!immediate) {
|
|
@@ -36,7 +37,21 @@ function waitAndSend (config, application, host) {
|
|
|
36
37
|
}
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
function loadAllTheLoadedModules () {
|
|
41
|
+
if (require.cache) {
|
|
42
|
+
const filenames = Object.keys(require.cache)
|
|
43
|
+
filenames.forEach(filename => {
|
|
44
|
+
onModuleLoad({ filename })
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
39
49
|
function onModuleLoad (data) {
|
|
50
|
+
if (isFirstModule) {
|
|
51
|
+
isFirstModule = false
|
|
52
|
+
loadAllTheLoadedModules()
|
|
53
|
+
}
|
|
54
|
+
|
|
40
55
|
if (data) {
|
|
41
56
|
let filename = data.filename
|
|
42
57
|
if (filename && filename.startsWith(FILE_URI_START)) {
|
|
@@ -17,6 +17,7 @@ let pluginManager
|
|
|
17
17
|
let application
|
|
18
18
|
let host
|
|
19
19
|
let interval
|
|
20
|
+
let heartbeatTimeout
|
|
20
21
|
let heartbeatInterval
|
|
21
22
|
const sentIntegrations = new Set()
|
|
22
23
|
|
|
@@ -110,6 +111,14 @@ function getTelemetryData () {
|
|
|
110
111
|
return { config, application, host, heartbeatInterval }
|
|
111
112
|
}
|
|
112
113
|
|
|
114
|
+
function heartbeat (config, application, host) {
|
|
115
|
+
heartbeatTimeout = setTimeout(() => {
|
|
116
|
+
sendData(config, application, host, 'app-heartbeat')
|
|
117
|
+
heartbeat(config, application, host)
|
|
118
|
+
}, heartbeatInterval).unref()
|
|
119
|
+
return heartbeatTimeout
|
|
120
|
+
}
|
|
121
|
+
|
|
113
122
|
function start (aConfig, thePluginManager) {
|
|
114
123
|
if (!aConfig.telemetry.enabled) {
|
|
115
124
|
return
|
|
@@ -122,9 +131,9 @@ function start (aConfig, thePluginManager) {
|
|
|
122
131
|
|
|
123
132
|
dependencies.start(config, application, host)
|
|
124
133
|
sendData(config, application, host, 'app-started', appStarted())
|
|
134
|
+
heartbeat(config, application, host)
|
|
125
135
|
interval = setInterval(() => {
|
|
126
136
|
metricsManager.send(config, application, host)
|
|
127
|
-
sendData(config, application, host, 'app-heartbeat')
|
|
128
137
|
}, heartbeatInterval)
|
|
129
138
|
interval.unref()
|
|
130
139
|
process.on('beforeExit', onBeforeExit)
|
|
@@ -137,6 +146,7 @@ function stop () {
|
|
|
137
146
|
return
|
|
138
147
|
}
|
|
139
148
|
clearInterval(interval)
|
|
149
|
+
clearTimeout(heartbeatTimeout)
|
|
140
150
|
process.removeListener('beforeExit', onBeforeExit)
|
|
141
151
|
|
|
142
152
|
telemetryStopChannel.publish(getTelemetryData())
|
|
File without changes
|