dd-trace 5.63.3 → 5.65.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/index.d.ts +10 -0
- package/package.json +1 -1
- package/packages/datadog-instrumentations/src/ai.js +12 -1
- package/packages/datadog-instrumentations/src/playwright.js +32 -8
- package/packages/dd-trace/src/azure_metadata.js +5 -4
- package/packages/dd-trace/src/config.js +13 -7
- package/packages/dd-trace/src/datastreams/pathway.js +1 -1
- package/packages/dd-trace/src/dogstatsd.js +17 -20
- package/packages/dd-trace/src/opentracing/tracer.js +1 -1
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +176 -126
- package/packages/dd-trace/src/supported-configurations.json +2 -1
- package/packages/dd-trace/src/telemetry/endpoints.js +124 -0
- package/packages/dd-trace/src/telemetry/telemetry.js +3 -0
package/index.d.ts
CHANGED
|
@@ -712,6 +712,16 @@ declare namespace tracer {
|
|
|
712
712
|
* @default true
|
|
713
713
|
*/
|
|
714
714
|
enabled?: boolean,
|
|
715
|
+
|
|
716
|
+
/** Whether to enable endpoint collection for API Security.
|
|
717
|
+
* @default true
|
|
718
|
+
*/
|
|
719
|
+
endpointCollectionEnabled?: boolean,
|
|
720
|
+
|
|
721
|
+
/** Maximum number of endpoints that can be serialized per message.
|
|
722
|
+
* @default 300
|
|
723
|
+
*/
|
|
724
|
+
endpointCollectionMessageLimit?: number,
|
|
715
725
|
},
|
|
716
726
|
/**
|
|
717
727
|
* Configuration for RASP
|
package/package.json
CHANGED
|
@@ -98,7 +98,18 @@ function wrapWithTracer (fn) {
|
|
|
98
98
|
return function () {
|
|
99
99
|
const options = arguments[0]
|
|
100
100
|
|
|
101
|
-
options.experimental_telemetry
|
|
101
|
+
const experimentalTelemetry = options.experimental_telemetry
|
|
102
|
+
if (experimentalTelemetry?.isEnabled === false) {
|
|
103
|
+
return fn.apply(this, arguments)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (experimentalTelemetry == null) {
|
|
107
|
+
options.experimental_telemetry = { isEnabled: true, tracer: noopTracer }
|
|
108
|
+
} else {
|
|
109
|
+
experimentalTelemetry.isEnabled = true
|
|
110
|
+
experimentalTelemetry.tracer ??= noopTracer
|
|
111
|
+
}
|
|
112
|
+
|
|
102
113
|
wrapTracer(options.experimental_telemetry.tracer)
|
|
103
114
|
|
|
104
115
|
return fn.apply(this, arguments)
|
|
@@ -148,8 +148,8 @@ function getPlaywrightConfig (playwrightRunner) {
|
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
function getRootDir (playwrightRunner) {
|
|
152
|
-
const config = getPlaywrightConfig(playwrightRunner)
|
|
151
|
+
function getRootDir (playwrightRunner, configArg) {
|
|
152
|
+
const config = configArg?.config || getPlaywrightConfig(playwrightRunner)
|
|
153
153
|
if (config.rootDir) {
|
|
154
154
|
return config.rootDir
|
|
155
155
|
}
|
|
@@ -162,8 +162,8 @@ function getRootDir (playwrightRunner) {
|
|
|
162
162
|
return process.cwd()
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
function getProjectsFromRunner (runner) {
|
|
166
|
-
const config = getPlaywrightConfig(runner)
|
|
165
|
+
function getProjectsFromRunner (runner, configArg) {
|
|
166
|
+
const config = configArg?.projects ? configArg : getPlaywrightConfig(runner)
|
|
167
167
|
return config.projects?.map((project) => {
|
|
168
168
|
if (project.project) {
|
|
169
169
|
return project.project
|
|
@@ -509,11 +509,12 @@ function dispatcherHookNew (dispatcherExport, runWrapper) {
|
|
|
509
509
|
return dispatcherExport
|
|
510
510
|
}
|
|
511
511
|
|
|
512
|
-
function
|
|
513
|
-
|
|
512
|
+
function runAllTestsWrapper (runAllTests, playwrightVersion) {
|
|
513
|
+
// Config parameter is only available from >=1.55.0
|
|
514
|
+
return async function (config) {
|
|
514
515
|
let onDone
|
|
515
516
|
|
|
516
|
-
rootDir = getRootDir(this)
|
|
517
|
+
rootDir = getRootDir(this, config)
|
|
517
518
|
|
|
518
519
|
const processArgv = process.argv.slice(2).join(' ')
|
|
519
520
|
const command = `playwright ${processArgv}`
|
|
@@ -586,7 +587,7 @@ function runnerHook (runnerExport, playwrightVersion) {
|
|
|
586
587
|
}
|
|
587
588
|
}
|
|
588
589
|
|
|
589
|
-
const projects = getProjectsFromRunner(this)
|
|
590
|
+
const projects = getProjectsFromRunner(this, config)
|
|
590
591
|
|
|
591
592
|
const shouldSetRetries = isFlakyTestRetriesEnabled &&
|
|
592
593
|
flakyTestRetriesCount > 0 &&
|
|
@@ -651,6 +652,23 @@ function runnerHook (runnerExport, playwrightVersion) {
|
|
|
651
652
|
// TODO: we can trick playwright into thinking the session passed by returning
|
|
652
653
|
// 'passed' here. We might be able to use this for both EFD and Test Management tests.
|
|
653
654
|
return runAllTestsReturn
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
function runnerHook (runnerExport, playwrightVersion) {
|
|
659
|
+
shimmer.wrap(
|
|
660
|
+
runnerExport.Runner.prototype,
|
|
661
|
+
'runAllTests',
|
|
662
|
+
runAllTests => runAllTestsWrapper(runAllTests, playwrightVersion)
|
|
663
|
+
)
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
function runnerHookNew (runnerExport, playwrightVersion) {
|
|
667
|
+
runnerExport = shimmer.wrap(runnerExport, 'runAllTestsWithConfig', function (originalGetter) {
|
|
668
|
+
const originalFunction = originalGetter.call(this)
|
|
669
|
+
return function () {
|
|
670
|
+
return runAllTestsWrapper(originalFunction, playwrightVersion)
|
|
671
|
+
}
|
|
654
672
|
})
|
|
655
673
|
|
|
656
674
|
return runnerExport
|
|
@@ -694,6 +712,12 @@ addHook({
|
|
|
694
712
|
versions: ['>=1.38.0']
|
|
695
713
|
}, runnerHook)
|
|
696
714
|
|
|
715
|
+
addHook({
|
|
716
|
+
name: 'playwright',
|
|
717
|
+
file: 'lib/runner/testRunner.js',
|
|
718
|
+
versions: ['>=1.55.0']
|
|
719
|
+
}, runnerHookNew)
|
|
720
|
+
|
|
697
721
|
addHook({
|
|
698
722
|
name: 'playwright',
|
|
699
723
|
file: 'lib/runner/dispatcher.js',
|
|
@@ -76,10 +76,11 @@ function buildMetadata () {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
function getAzureAppMetadata () {
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
// See
|
|
82
|
-
|
|
79
|
+
// WEBSITE_SITE_NAME is the unique name of the website instance within Azure App Services. Its
|
|
80
|
+
// presence is used to determine if we are running in Azure App Service
|
|
81
|
+
// See equivalent in dd-trace-dotnet:
|
|
82
|
+
// https://github.com/DataDog/dd-trace-dotnet/blob/37030168b2996e549ba23231ae732874b53a37e6/tracer/src/Datadog.Trace/Util/EnvironmentHelpers.cs#L99-L155
|
|
83
|
+
if (getEnvironmentVariable('WEBSITE_SITE_NAME') !== undefined) {
|
|
83
84
|
return buildMetadata()
|
|
84
85
|
}
|
|
85
86
|
}
|
|
@@ -323,12 +323,6 @@ class Config {
|
|
|
323
323
|
}
|
|
324
324
|
}
|
|
325
325
|
|
|
326
|
-
if (typeof options.runtimeMetrics?.gc === 'boolean') {
|
|
327
|
-
options.runtimeMetrics.gc = {
|
|
328
|
-
enabled: options.runtimeMetrics.gc
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
326
|
const DD_INSTRUMENTATION_INSTALL_ID = coalesce(
|
|
333
327
|
getEnvironmentVariable('DD_INSTRUMENTATION_INSTALL_ID'),
|
|
334
328
|
null
|
|
@@ -493,6 +487,8 @@ class Config {
|
|
|
493
487
|
defaults.apmTracingEnabled = true
|
|
494
488
|
defaults['appsec.apiSecurity.enabled'] = true
|
|
495
489
|
defaults['appsec.apiSecurity.sampleDelay'] = 30
|
|
490
|
+
defaults['appsec.apiSecurity.endpointCollectionEnabled'] = true
|
|
491
|
+
defaults['appsec.apiSecurity.endpointCollectionMessageLimit'] = 300
|
|
496
492
|
defaults['appsec.blockedTemplateGraphql'] = undefined
|
|
497
493
|
defaults['appsec.blockedTemplateHtml'] = undefined
|
|
498
494
|
defaults['appsec.blockedTemplateJson'] = undefined
|
|
@@ -690,6 +686,8 @@ class Config {
|
|
|
690
686
|
DD_AGENT_HOST,
|
|
691
687
|
DD_API_SECURITY_ENABLED,
|
|
692
688
|
DD_API_SECURITY_SAMPLE_DELAY,
|
|
689
|
+
DD_API_SECURITY_ENDPOINT_COLLECTION_ENABLED,
|
|
690
|
+
DD_API_SECURITY_ENDPOINT_COLLECTION_MESSAGE_LIMIT,
|
|
693
691
|
DD_APM_TRACING_ENABLED,
|
|
694
692
|
DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE,
|
|
695
693
|
DD_APPSEC_COLLECT_ALL_HEADERS,
|
|
@@ -846,6 +844,10 @@ class Config {
|
|
|
846
844
|
))
|
|
847
845
|
this._setBoolean(env, 'appsec.apiSecurity.enabled', DD_API_SECURITY_ENABLED && isTrue(DD_API_SECURITY_ENABLED))
|
|
848
846
|
env['appsec.apiSecurity.sampleDelay'] = maybeFloat(DD_API_SECURITY_SAMPLE_DELAY)
|
|
847
|
+
this._setBoolean(env, 'appsec.apiSecurity.endpointCollectionEnabled',
|
|
848
|
+
DD_API_SECURITY_ENDPOINT_COLLECTION_ENABLED)
|
|
849
|
+
env['appsec.apiSecurity.endpointCollectionMessageLimit'] =
|
|
850
|
+
maybeInt(DD_API_SECURITY_ENDPOINT_COLLECTION_MESSAGE_LIMIT)
|
|
849
851
|
env['appsec.blockedTemplateGraphql'] = maybeFile(DD_APPSEC_GRAPHQL_BLOCKED_TEMPLATE_JSON)
|
|
850
852
|
env['appsec.blockedTemplateHtml'] = maybeFile(DD_APPSEC_HTTP_BLOCKED_TEMPLATE_HTML)
|
|
851
853
|
this._envUnprocessed['appsec.blockedTemplateHtml'] = DD_APPSEC_HTTP_BLOCKED_TEMPLATE_HTML
|
|
@@ -1073,6 +1075,10 @@ class Config {
|
|
|
1073
1075
|
options.experimental?.appsec?.standalone && !options.experimental.appsec.standalone.enabled
|
|
1074
1076
|
))
|
|
1075
1077
|
this._setBoolean(opts, 'appsec.apiSecurity.enabled', options.appsec?.apiSecurity?.enabled)
|
|
1078
|
+
this._setBoolean(opts, 'appsec.apiSecurity.endpointCollectionEnabled',
|
|
1079
|
+
options.appsec?.apiSecurity?.endpointCollectionEnabled)
|
|
1080
|
+
opts['appsec.apiSecurity.endpointCollectionMessageLimit'] =
|
|
1081
|
+
maybeInt(options.appsec?.apiSecurity?.endpointCollectionMessageLimit)
|
|
1076
1082
|
opts['appsec.blockedTemplateGraphql'] = maybeFile(options.appsec?.blockedTemplateGraphql)
|
|
1077
1083
|
opts['appsec.blockedTemplateHtml'] = maybeFile(options.appsec?.blockedTemplateHtml)
|
|
1078
1084
|
this._optsUnprocessed['appsec.blockedTemplateHtml'] = options.appsec?.blockedTemplateHtml
|
|
@@ -1190,7 +1196,7 @@ class Config {
|
|
|
1190
1196
|
this._setBoolean(opts, 'reportHostname', options.reportHostname)
|
|
1191
1197
|
this._setBoolean(opts, 'runtimeMetrics.enabled', options.runtimeMetrics?.enabled)
|
|
1192
1198
|
this._setBoolean(opts, 'runtimeMetrics.eventLoop', options.runtimeMetrics?.eventLoop)
|
|
1193
|
-
this._setBoolean(opts, 'runtimeMetrics.gc', options.runtimeMetrics?.gc
|
|
1199
|
+
this._setBoolean(opts, 'runtimeMetrics.gc', options.runtimeMetrics?.gc)
|
|
1194
1200
|
this._setBoolean(opts, 'runtimeMetricsRuntimeId', options.runtimeMetricsRuntimeId)
|
|
1195
1201
|
this._setArray(opts, 'sampler.spanSamplingRules', reformatSpanSamplingRules(options.spanSamplingRules))
|
|
1196
1202
|
this._setUnit(opts, 'sampleRate', coalesce(options.sampleRate, options.ingestion.sampleRate))
|
|
@@ -17,7 +17,7 @@ const CONTEXT_PROPAGATION_KEY_BASE64 = 'dd-pathway-ctx-base64'
|
|
|
17
17
|
const logKeys = [CONTEXT_PROPAGATION_KEY, CONTEXT_PROPAGATION_KEY_BASE64]
|
|
18
18
|
|
|
19
19
|
function shaHash (checkpointString) {
|
|
20
|
-
const hash = crypto.createHash('
|
|
20
|
+
const hash = crypto.createHash('sha256').update(checkpointString).digest('hex').slice(0, 16)
|
|
21
21
|
return Buffer.from(hash, 'hex')
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -156,23 +156,19 @@ class DogStatsDClient {
|
|
|
156
156
|
return socket
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
-
static generateClientConfig (config
|
|
159
|
+
static generateClientConfig (config) {
|
|
160
160
|
const tags = []
|
|
161
161
|
|
|
162
162
|
if (config.tags) {
|
|
163
|
-
Object.
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
// Skip runtime-id unless enabled as cardinality may be too high
|
|
167
|
-
if (key !== 'runtime-id') return true
|
|
168
|
-
return config.runtimeMetricsRuntimeId
|
|
169
|
-
})
|
|
170
|
-
.forEach(key => {
|
|
163
|
+
for (const [key, value] of Object.entries(config.tags)) {
|
|
164
|
+
// Skip runtime-id unless enabled as cardinality may be too high
|
|
165
|
+
if (typeof value === 'string' && (key !== 'runtime-id' || config.runtimeMetricsRuntimeId)) {
|
|
171
166
|
// https://docs.datadoghq.com/tagging/#defining-tags
|
|
172
|
-
const
|
|
167
|
+
const valueStripped = value.replaceAll(/[^a-z0-9_:./-]/ig, '_')
|
|
173
168
|
|
|
174
|
-
tags.push(`${key}:${
|
|
175
|
-
}
|
|
169
|
+
tags.push(`${key}:${valueStripped}`)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
176
172
|
}
|
|
177
173
|
|
|
178
174
|
const clientConfig = {
|
|
@@ -216,7 +212,7 @@ class MetricsAggregationClient {
|
|
|
216
212
|
this._histograms = new Map()
|
|
217
213
|
}
|
|
218
214
|
|
|
219
|
-
// TODO:
|
|
215
|
+
// TODO: Aggregate with a histogram and send the buckets to the client.
|
|
220
216
|
distribution (name, value, tags) {
|
|
221
217
|
this._client.distribution(name, value, tags)
|
|
222
218
|
}
|
|
@@ -352,9 +348,10 @@ class MetricsAggregationClient {
|
|
|
352
348
|
* @implements {DogStatsD}
|
|
353
349
|
*/
|
|
354
350
|
class CustomMetrics {
|
|
351
|
+
#client
|
|
355
352
|
constructor (config) {
|
|
356
353
|
const clientConfig = DogStatsDClient.generateClientConfig(config)
|
|
357
|
-
this
|
|
354
|
+
this.#client = new MetricsAggregationClient(new DogStatsDClient(clientConfig))
|
|
358
355
|
|
|
359
356
|
const flush = this.flush.bind(this)
|
|
360
357
|
|
|
@@ -365,27 +362,27 @@ class CustomMetrics {
|
|
|
365
362
|
}
|
|
366
363
|
|
|
367
364
|
increment (stat, value = 1, tags) {
|
|
368
|
-
this.
|
|
365
|
+
this.#client.increment(stat, value, CustomMetrics.tagTranslator(tags))
|
|
369
366
|
}
|
|
370
367
|
|
|
371
368
|
decrement (stat, value = 1, tags) {
|
|
372
|
-
this.
|
|
369
|
+
this.#client.decrement(stat, value, CustomMetrics.tagTranslator(tags))
|
|
373
370
|
}
|
|
374
371
|
|
|
375
372
|
gauge (stat, value, tags) {
|
|
376
|
-
this.
|
|
373
|
+
this.#client.gauge(stat, value, CustomMetrics.tagTranslator(tags))
|
|
377
374
|
}
|
|
378
375
|
|
|
379
376
|
distribution (stat, value, tags) {
|
|
380
|
-
this.
|
|
377
|
+
this.#client.distribution(stat, value, CustomMetrics.tagTranslator(tags))
|
|
381
378
|
}
|
|
382
379
|
|
|
383
380
|
histogram (stat, value, tags) {
|
|
384
|
-
this.
|
|
381
|
+
this.#client.histogram(stat, value, CustomMetrics.tagTranslator(tags))
|
|
385
382
|
}
|
|
386
383
|
|
|
387
384
|
flush () {
|
|
388
|
-
return this.
|
|
385
|
+
return this.#client.flush()
|
|
389
386
|
}
|
|
390
387
|
|
|
391
388
|
/**
|
|
@@ -4,72 +4,90 @@
|
|
|
4
4
|
|
|
5
5
|
const v8 = require('v8')
|
|
6
6
|
const os = require('os')
|
|
7
|
+
const process = require('process')
|
|
7
8
|
const { DogStatsDClient, MetricsAggregationClient } = require('../dogstatsd')
|
|
8
9
|
const log = require('../log')
|
|
9
|
-
const { performance, PerformanceObserver } = require('perf_hooks')
|
|
10
|
+
const { performance, PerformanceObserver, monitorEventLoopDelay } = require('perf_hooks')
|
|
10
11
|
const { getEnvironmentVariable } = require('../config-helper')
|
|
11
12
|
|
|
12
|
-
const { NODE_MAJOR
|
|
13
|
+
const { NODE_MAJOR } = require('../../../../version')
|
|
14
|
+
// TODO: This environment variable may not be changed, since the agent expects a flush every ten seconds.
|
|
15
|
+
// It is only a variable for testing. Think about alternatives.
|
|
13
16
|
const DD_RUNTIME_METRICS_FLUSH_INTERVAL = getEnvironmentVariable('DD_RUNTIME_METRICS_FLUSH_INTERVAL') ?? '10000'
|
|
14
17
|
const INTERVAL = Number.parseInt(DD_RUNTIME_METRICS_FLUSH_INTERVAL, 10)
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
// See: https://github.com/nodejs/node/issues/39548
|
|
18
|
-
const hasGCObserver = NODE_MAJOR >= 18 || (NODE_MAJOR === 16 && NODE_MINOR >= 7)
|
|
19
|
+
const eventLoopDelayResolution = 4
|
|
19
20
|
|
|
20
21
|
let nativeMetrics = null
|
|
21
22
|
let gcObserver = null
|
|
22
|
-
|
|
23
|
-
let
|
|
24
|
-
let
|
|
25
|
-
let
|
|
26
|
-
let
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
23
|
+
let interval = null
|
|
24
|
+
let client = null
|
|
25
|
+
let lastTime = 0n
|
|
26
|
+
let lastCpuUsage = null
|
|
27
|
+
let eventLoopDelayObserver = null
|
|
28
|
+
|
|
29
|
+
// !!!!!!!!!!!
|
|
30
|
+
// IMPORTANT
|
|
31
|
+
// !!!!!!!!!!!
|
|
32
|
+
//
|
|
33
|
+
// ALL metrics that relate to time are handled in nanoseconds in the backend.
|
|
34
|
+
// https://github.com/DataDog/dogweb/blob/prod/integration/node/node_metadata.csv
|
|
35
|
+
|
|
36
|
+
module.exports = {
|
|
32
37
|
start (config) {
|
|
38
|
+
this.stop()
|
|
33
39
|
const clientConfig = DogStatsDClient.generateClientConfig(config)
|
|
34
|
-
const watchers = []
|
|
35
40
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
startGCObserver()
|
|
39
|
-
} else {
|
|
40
|
-
watchers.push('gc')
|
|
41
|
-
}
|
|
42
|
-
}
|
|
41
|
+
const trackEventLoop = config.runtimeMetrics.eventLoop !== false
|
|
42
|
+
const trackGc = config.runtimeMetrics.gc !== false
|
|
43
43
|
|
|
44
|
-
if (
|
|
45
|
-
|
|
44
|
+
if (trackGc) {
|
|
45
|
+
startGCObserver()
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
// Using no-gc prevents the native gc metrics from being tracked. Not
|
|
49
|
+
// passing any options means all metrics are tracked.
|
|
50
|
+
// TODO: This is a workaround. We should find a better solution.
|
|
51
|
+
const watchers = trackEventLoop ? ['loop'] : ['no-gc']
|
|
52
|
+
|
|
48
53
|
try {
|
|
49
54
|
nativeMetrics = require('@datadog/native-metrics')
|
|
50
55
|
nativeMetrics.start(...watchers)
|
|
51
|
-
} catch (
|
|
52
|
-
log.error('Error starting native metrics',
|
|
56
|
+
} catch (error) {
|
|
57
|
+
log.error('Error starting native metrics', error)
|
|
53
58
|
nativeMetrics = null
|
|
54
59
|
}
|
|
55
60
|
|
|
56
61
|
client = new MetricsAggregationClient(new DogStatsDClient(clientConfig))
|
|
57
62
|
|
|
58
|
-
|
|
63
|
+
lastTime = performance.now()
|
|
59
64
|
|
|
60
65
|
if (nativeMetrics) {
|
|
61
66
|
interval = setInterval(() => {
|
|
62
|
-
|
|
63
|
-
|
|
67
|
+
captureNativeMetrics(trackEventLoop, trackGc)
|
|
68
|
+
captureCommonMetrics(trackEventLoop)
|
|
64
69
|
client.flush()
|
|
65
70
|
}, INTERVAL)
|
|
66
71
|
} else {
|
|
67
|
-
|
|
72
|
+
lastCpuUsage = process.cpuUsage()
|
|
73
|
+
|
|
74
|
+
if (trackEventLoop) {
|
|
75
|
+
eventLoopDelayObserver = monitorEventLoopDelay({ resolution: eventLoopDelayResolution })
|
|
76
|
+
eventLoopDelayObserver.enable()
|
|
77
|
+
}
|
|
68
78
|
|
|
69
79
|
interval = setInterval(() => {
|
|
70
|
-
captureCommonMetrics()
|
|
71
80
|
captureCpuUsage()
|
|
81
|
+
captureCommonMetrics(trackEventLoop)
|
|
72
82
|
captureHeapSpace()
|
|
83
|
+
if (trackEventLoop) {
|
|
84
|
+
// Experimental: The Node.js implementation deviates from the native metrics.
|
|
85
|
+
// We normalize the metrics to the same format but the Node.js values
|
|
86
|
+
// are that way lower than they should be, while they are still nearer
|
|
87
|
+
// to the native ones that way.
|
|
88
|
+
// We use these only as fallback values.
|
|
89
|
+
captureEventLoopDelay()
|
|
90
|
+
}
|
|
73
91
|
client.flush()
|
|
74
92
|
}, INTERVAL)
|
|
75
93
|
}
|
|
@@ -78,12 +96,21 @@ const runtimeMetrics = module.exports = {
|
|
|
78
96
|
},
|
|
79
97
|
|
|
80
98
|
stop () {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
99
|
+
nativeMetrics?.stop()
|
|
100
|
+
nativeMetrics = null
|
|
84
101
|
|
|
85
102
|
clearInterval(interval)
|
|
86
|
-
|
|
103
|
+
interval = null
|
|
104
|
+
|
|
105
|
+
client = null
|
|
106
|
+
lastTime = 0n
|
|
107
|
+
lastCpuUsage = null
|
|
108
|
+
|
|
109
|
+
gcObserver?.disconnect()
|
|
110
|
+
gcObserver = null
|
|
111
|
+
|
|
112
|
+
eventLoopDelayObserver?.disable()
|
|
113
|
+
eventLoopDelayObserver = null
|
|
87
114
|
},
|
|
88
115
|
|
|
89
116
|
track (span) {
|
|
@@ -99,19 +126,19 @@ const runtimeMetrics = module.exports = {
|
|
|
99
126
|
},
|
|
100
127
|
|
|
101
128
|
boolean (name, value, tag) {
|
|
102
|
-
client
|
|
129
|
+
client?.boolean(name, value, tag)
|
|
103
130
|
},
|
|
104
131
|
|
|
105
132
|
histogram (name, value, tag) {
|
|
106
|
-
client
|
|
133
|
+
client?.histogram(name, value, tag)
|
|
107
134
|
},
|
|
108
135
|
|
|
109
136
|
count (name, count, tag, monotonic = false) {
|
|
110
|
-
client
|
|
137
|
+
client?.count(name, count, tag, monotonic)
|
|
111
138
|
},
|
|
112
139
|
|
|
113
140
|
gauge (name, value, tag) {
|
|
114
|
-
client
|
|
141
|
+
client?.gauge(name, value, tag)
|
|
115
142
|
},
|
|
116
143
|
|
|
117
144
|
increment (name, tag, monotonic) {
|
|
@@ -123,30 +150,20 @@ const runtimeMetrics = module.exports = {
|
|
|
123
150
|
}
|
|
124
151
|
}
|
|
125
152
|
|
|
126
|
-
function reset () {
|
|
127
|
-
interval = null
|
|
128
|
-
client = null
|
|
129
|
-
time = null
|
|
130
|
-
cpuUsage = null
|
|
131
|
-
nativeMetrics = null
|
|
132
|
-
gcObserver && gcObserver.disconnect()
|
|
133
|
-
gcObserver = null
|
|
134
|
-
}
|
|
135
|
-
|
|
136
153
|
function captureCpuUsage () {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const elapsedMs = elapsedTime[0] * 1000 + elapsedTime[1] / 1_000_000
|
|
146
|
-
const userPercent = 100 * elapsedUsage.user / 1000 / elapsedMs
|
|
147
|
-
const systemPercent = 100 * elapsedUsage.system / 1000 / elapsedMs
|
|
154
|
+
const currentCpuUsage = process.cpuUsage()
|
|
155
|
+
const elapsedUsageUser = currentCpuUsage.user - lastCpuUsage.user
|
|
156
|
+
const elapsedUsageSystem = currentCpuUsage.system - lastCpuUsage.system
|
|
157
|
+
|
|
158
|
+
const currentTime = performance.now() // Milliseconds with decimal places
|
|
159
|
+
const elapsedUsDividedBy100 = (currentTime - lastTime) * 10
|
|
160
|
+
const userPercent = elapsedUsageUser / elapsedUsDividedBy100
|
|
161
|
+
const systemPercent = elapsedUsageSystem / elapsedUsDividedBy100
|
|
148
162
|
const totalPercent = userPercent + systemPercent
|
|
149
163
|
|
|
164
|
+
lastTime = currentTime
|
|
165
|
+
lastCpuUsage = currentCpuUsage
|
|
166
|
+
|
|
150
167
|
client.gauge('runtime.node.cpu.system', systemPercent.toFixed(2))
|
|
151
168
|
client.gauge('runtime.node.cpu.user', userPercent.toFixed(2))
|
|
152
169
|
client.gauge('runtime.node.cpu.total', totalPercent.toFixed(2))
|
|
@@ -160,12 +177,44 @@ function captureMemoryUsage () {
|
|
|
160
177
|
client.gauge('runtime.node.mem.rss', stats.rss)
|
|
161
178
|
client.gauge('runtime.node.mem.total', os.totalmem())
|
|
162
179
|
client.gauge('runtime.node.mem.free', os.freemem())
|
|
180
|
+
client.gauge('runtime.node.mem.external', stats.external)
|
|
181
|
+
// TODO: Add arrayBuffers to the metrics. That also requires the
|
|
182
|
+
// node_metadata.csv to be updated for the website.
|
|
183
|
+
//
|
|
184
|
+
// client.gauge('runtime.node.mem.arrayBuffers', stats.arrayBuffers)
|
|
185
|
+
}
|
|
163
186
|
|
|
164
|
-
|
|
187
|
+
function captureUptime () {
|
|
188
|
+
// WARNING: lastTime must be updated in the same interval before this function is called!
|
|
189
|
+
// This is a faster `process.uptime()`.
|
|
190
|
+
client.gauge('runtime.node.process.uptime', Math.round((lastTime + 499) / 1000))
|
|
165
191
|
}
|
|
166
192
|
|
|
167
|
-
function
|
|
168
|
-
|
|
193
|
+
function captureEventLoopDelay () {
|
|
194
|
+
eventLoopDelayObserver.disable()
|
|
195
|
+
|
|
196
|
+
if (eventLoopDelayObserver.count !== 0) {
|
|
197
|
+
const minimum = eventLoopDelayResolution * 1e6
|
|
198
|
+
const avg = Math.max(eventLoopDelayObserver.mean - minimum, 0)
|
|
199
|
+
const sum = Math.round(avg * eventLoopDelayObserver.count)
|
|
200
|
+
|
|
201
|
+
if (sum !== 0) {
|
|
202
|
+
// Normalize the metrics to the same format as the native metrics.
|
|
203
|
+
const stats = {
|
|
204
|
+
min: Math.max(eventLoopDelayObserver.min - minimum, 0),
|
|
205
|
+
max: Math.max(eventLoopDelayObserver.max - minimum, 0),
|
|
206
|
+
sum,
|
|
207
|
+
total: sum,
|
|
208
|
+
avg,
|
|
209
|
+
count: eventLoopDelayObserver.count,
|
|
210
|
+
p95: Math.max(eventLoopDelayObserver.percentile(95) - minimum, 0)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
histogram('runtime.node.event_loop.delay', stats)
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
eventLoopDelayObserver = monitorEventLoopDelay({ resolution: eventLoopDelayResolution })
|
|
217
|
+
eventLoopDelayObserver.enable()
|
|
169
218
|
}
|
|
170
219
|
|
|
171
220
|
function captureHeapStats () {
|
|
@@ -176,14 +225,17 @@ function captureHeapStats () {
|
|
|
176
225
|
client.gauge('runtime.node.heap.total_physical_size', stats.total_physical_size)
|
|
177
226
|
client.gauge('runtime.node.heap.total_available_size', stats.total_available_size)
|
|
178
227
|
client.gauge('runtime.node.heap.heap_size_limit', stats.heap_size_limit)
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
228
|
+
client.gauge('runtime.node.heap.malloced_memory', stats.malloced_memory)
|
|
229
|
+
client.gauge('runtime.node.heap.peak_malloced_memory', stats.peak_malloced_memory)
|
|
230
|
+
// TODO: Add number_of_native_contexts and number_of_detached_contexts to the
|
|
231
|
+
// metrics. Those metrics allow to identify memory leaks. Adding them also
|
|
232
|
+
// requires the node_metadata.csv to be updated for the website.
|
|
233
|
+
//
|
|
234
|
+
// client.gauge('runtime.node.heap.number_of_native_contexts', stats.number_of_native_contexts)
|
|
235
|
+
// client.gauge('runtime.node.heap.number_of_detached_contexts', stats.number_of_detached_contexts)
|
|
182
236
|
}
|
|
183
237
|
|
|
184
238
|
function captureHeapSpace () {
|
|
185
|
-
if (!v8.getHeapSpaceStatistics) return
|
|
186
|
-
|
|
187
239
|
const stats = v8.getHeapSpaceStatistics()
|
|
188
240
|
|
|
189
241
|
for (let i = 0, l = stats.length; i < l; i++) {
|
|
@@ -197,55 +249,63 @@ function captureHeapSpace () {
|
|
|
197
249
|
}
|
|
198
250
|
|
|
199
251
|
/**
|
|
200
|
-
* Gathers and reports Event Loop Utilization (ELU) since last run
|
|
252
|
+
* Gathers and reports Event Loop Utilization (ELU) since last run, or from the
|
|
253
|
+
* start of the process on first run.
|
|
201
254
|
*
|
|
202
255
|
* ELU is a measure of how busy the event loop is, like running JavaScript or
|
|
203
256
|
* waiting on *Sync functions. The value is between 0 (idle) and 1 (exhausted).
|
|
204
|
-
*
|
|
205
|
-
* performance.eventLoopUtilization available in Node.js >= v14.10, >= v12.19, >= v16
|
|
206
257
|
*/
|
|
207
|
-
let
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
// if elu is undefined (first run) the measurement is from start of process
|
|
211
|
-
elu = performance.eventLoopUtilization(elu)
|
|
258
|
+
let lastElu = { idle: 0, active: 0 }
|
|
259
|
+
function captureELU () {
|
|
260
|
+
const elu = performance.eventLoopUtilization()
|
|
212
261
|
|
|
213
|
-
|
|
214
|
-
|
|
262
|
+
const idle = elu.idle - lastElu.idle
|
|
263
|
+
const active = elu.active - lastElu.active
|
|
264
|
+
const utilization = active / (idle + active)
|
|
265
|
+
|
|
266
|
+
lastElu = elu
|
|
267
|
+
|
|
268
|
+
client.gauge('runtime.node.event_loop.utilization', utilization)
|
|
215
269
|
}
|
|
216
270
|
|
|
217
|
-
function captureCommonMetrics () {
|
|
271
|
+
function captureCommonMetrics (trackEventLoop) {
|
|
218
272
|
captureMemoryUsage()
|
|
219
|
-
|
|
273
|
+
captureUptime()
|
|
220
274
|
captureHeapStats()
|
|
221
|
-
|
|
275
|
+
if (trackEventLoop) {
|
|
276
|
+
captureELU()
|
|
277
|
+
}
|
|
222
278
|
}
|
|
223
279
|
|
|
224
|
-
function captureNativeMetrics () {
|
|
280
|
+
function captureNativeMetrics (trackEventLoop, trackGc) {
|
|
225
281
|
const stats = nativeMetrics.stats()
|
|
226
282
|
const spaces = stats.heap.spaces
|
|
227
|
-
const elapsedTime = process.hrtime(time)
|
|
228
283
|
|
|
229
|
-
|
|
284
|
+
const currentTime = performance.now() // Milliseconds with decimal places
|
|
285
|
+
const elapsedUsDividedBy100 = (currentTime - lastTime) * 10
|
|
286
|
+
lastTime = currentTime
|
|
230
287
|
|
|
231
|
-
const
|
|
232
|
-
const
|
|
233
|
-
const systemPercent = 100 * stats.cpu.system / elapsedUs
|
|
288
|
+
const userPercent = stats.cpu.user / elapsedUsDividedBy100
|
|
289
|
+
const systemPercent = stats.cpu.system / elapsedUsDividedBy100
|
|
234
290
|
const totalPercent = userPercent + systemPercent
|
|
235
291
|
|
|
236
292
|
client.gauge('runtime.node.cpu.system', systemPercent.toFixed(2))
|
|
237
293
|
client.gauge('runtime.node.cpu.user', userPercent.toFixed(2))
|
|
238
294
|
client.gauge('runtime.node.cpu.total', totalPercent.toFixed(2))
|
|
239
295
|
|
|
240
|
-
|
|
296
|
+
if (trackEventLoop) {
|
|
297
|
+
histogram('runtime.node.event_loop.delay', stats.eventLoop)
|
|
298
|
+
}
|
|
241
299
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
300
|
+
if (trackGc) {
|
|
301
|
+
for (const [type, value] of Object.entries(stats.gc)) {
|
|
302
|
+
if (type === 'all') {
|
|
303
|
+
histogram('runtime.node.gc.pause', value)
|
|
304
|
+
} else {
|
|
305
|
+
histogram('runtime.node.gc.pause.by.type', value, `gc_type:${type}`)
|
|
306
|
+
}
|
|
247
307
|
}
|
|
248
|
-
}
|
|
308
|
+
}
|
|
249
309
|
|
|
250
310
|
for (let i = 0, l = spaces.length; i < l; i++) {
|
|
251
311
|
const tag = `heap_space:${spaces[i].space_name}`
|
|
@@ -258,13 +318,19 @@ function captureNativeMetrics () {
|
|
|
258
318
|
}
|
|
259
319
|
|
|
260
320
|
function histogram (name, stats, tag) {
|
|
321
|
+
if (stats.count === 0) {
|
|
322
|
+
return
|
|
323
|
+
}
|
|
261
324
|
client.gauge(`${name}.min`, stats.min, tag)
|
|
262
325
|
client.gauge(`${name}.max`, stats.max, tag)
|
|
263
326
|
client.increment(`${name}.sum`, stats.sum, tag)
|
|
264
327
|
client.increment(`${name}.total`, stats.sum, tag)
|
|
265
328
|
client.gauge(`${name}.avg`, stats.avg, tag)
|
|
266
329
|
client.increment(`${name}.count`, stats.count, tag)
|
|
267
|
-
|
|
330
|
+
if (stats.median !== undefined) {
|
|
331
|
+
// TODO: Consider adding the median to the Node.js histogram/adding stddev to native metrics.
|
|
332
|
+
client.gauge(`${name}.median`, stats.median, tag)
|
|
333
|
+
}
|
|
268
334
|
client.gauge(`${name}.95percentile`, stats.p95, tag)
|
|
269
335
|
}
|
|
270
336
|
|
|
@@ -274,43 +340,27 @@ function startGCObserver () {
|
|
|
274
340
|
gcObserver = new PerformanceObserver(list => {
|
|
275
341
|
for (const entry of list.getEntries()) {
|
|
276
342
|
const type = gcType(entry.detail?.kind || entry.kind)
|
|
343
|
+
const duration = entry.duration * 1_000_000
|
|
277
344
|
|
|
278
|
-
|
|
279
|
-
|
|
345
|
+
// These are individual metrics for each type of GC.
|
|
346
|
+
client.histogram('runtime.node.gc.pause.by.type', duration, `gc_type:${type}`)
|
|
347
|
+
client.histogram('runtime.node.gc.pause', duration)
|
|
280
348
|
}
|
|
281
349
|
})
|
|
282
350
|
|
|
283
351
|
gcObserver.observe({ type: 'gc' })
|
|
284
352
|
}
|
|
285
353
|
|
|
354
|
+
const minorGCType = NODE_MAJOR >= 22 ? 'minor_mark_sweep' : 'minor_mark_compact'
|
|
355
|
+
|
|
286
356
|
function gcType (kind) {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
}
|
|
296
|
-
} else if (NODE_MAJOR >= 18) {
|
|
297
|
-
switch (kind) {
|
|
298
|
-
case 1: return 'scavenge'
|
|
299
|
-
case 2: return 'minor_mark_compact'
|
|
300
|
-
case 4: return 'mark_sweep_compact'
|
|
301
|
-
case 8: return 'incremental_marking'
|
|
302
|
-
case 16: return 'process_weak_callbacks'
|
|
303
|
-
case 31: return 'all'
|
|
304
|
-
}
|
|
305
|
-
} else {
|
|
306
|
-
switch (kind) {
|
|
307
|
-
case 1: return 'scavenge'
|
|
308
|
-
case 2: return 'mark_sweep_compact'
|
|
309
|
-
case 4: return 'incremental_marking'
|
|
310
|
-
case 8: return 'process_weak_callbacks'
|
|
311
|
-
case 15: return 'all'
|
|
312
|
-
}
|
|
357
|
+
switch (kind) {
|
|
358
|
+
case 1: return 'scavenge'
|
|
359
|
+
case 2: return minorGCType
|
|
360
|
+
case 4: return 'mark_sweep_compact' // Deprecated, might be removed soon.
|
|
361
|
+
case 8: return 'incremental_marking'
|
|
362
|
+
case 16: return 'process_weak_callbacks'
|
|
363
|
+
case 31: return 'all'
|
|
364
|
+
default: return 'unknown'
|
|
313
365
|
}
|
|
314
|
-
|
|
315
|
-
return 'unknown'
|
|
316
366
|
}
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
"DD_API_KEY": ["A"],
|
|
9
9
|
"DD_API_SECURITY_ENABLED": ["A"],
|
|
10
10
|
"DD_API_SECURITY_SAMPLE_DELAY": ["A"],
|
|
11
|
+
"DD_API_SECURITY_ENDPOINT_COLLECTION_ENABLED": ["A"],
|
|
12
|
+
"DD_API_SECURITY_ENDPOINT_COLLECTION_MESSAGE_LIMIT": ["A"],
|
|
11
13
|
"DD_APM_FLUSH_DEADLINE_MILLISECONDS": ["A"],
|
|
12
14
|
"DD_APM_TRACING_ENABLED": ["A"],
|
|
13
15
|
"DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE": ["A"],
|
|
@@ -29,7 +31,6 @@
|
|
|
29
31
|
"DD_APPSEC_STACK_TRACE_ENABLED": ["A"],
|
|
30
32
|
"DD_APPSEC_TRACE_RATE_LIMIT": ["A"],
|
|
31
33
|
"DD_APPSEC_WAF_TIMEOUT": ["A"],
|
|
32
|
-
"DD_AZURE_APP_SERVICES": ["A"],
|
|
33
34
|
"DD_CIVISIBILITY_AGENTLESS_ENABLED": ["A"],
|
|
34
35
|
"DD_CIVISIBILITY_AGENTLESS_URL": ["A"],
|
|
35
36
|
"DD_CIVISIBILITY_AUTO_INSTRUMENTATION_PROVIDER": ["A"],
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const dc = require('dc-polyfill')
|
|
4
|
+
const { sendData } = require('./send-data')
|
|
5
|
+
|
|
6
|
+
const fastifyRouteCh = dc.channel('apm:fastify:route:added')
|
|
7
|
+
|
|
8
|
+
let config
|
|
9
|
+
let application
|
|
10
|
+
let host
|
|
11
|
+
let getRetryData
|
|
12
|
+
let updateRetryData
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Keep track of endpoints that still need to be sent.
|
|
16
|
+
* Map key is `${METHOD} ${PATH}`, value is { method, path }
|
|
17
|
+
*/
|
|
18
|
+
const pendingEndpoints = new Map()
|
|
19
|
+
let flushScheduled = false
|
|
20
|
+
let isFirstPayload = true
|
|
21
|
+
|
|
22
|
+
function endpointKey (method, path) {
|
|
23
|
+
return `${method.toUpperCase()} ${path}`
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function scheduleFlush () {
|
|
27
|
+
if (flushScheduled) return
|
|
28
|
+
flushScheduled = true
|
|
29
|
+
setImmediate(flushAndSend).unref()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function recordEndpoint (method, path) {
|
|
33
|
+
const key = endpointKey(method, path)
|
|
34
|
+
if (pendingEndpoints.has(key)) return
|
|
35
|
+
|
|
36
|
+
pendingEndpoints.set(key, { method: method.toUpperCase(), path })
|
|
37
|
+
scheduleFlush()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function onFastifyRoute (routeData) {
|
|
41
|
+
const routeOptions = routeData?.routeOptions
|
|
42
|
+
if (!routeOptions?.path) return
|
|
43
|
+
|
|
44
|
+
const methods = Array.isArray(routeOptions.method) ? routeOptions.method : [routeOptions.method]
|
|
45
|
+
|
|
46
|
+
for (const method of methods) {
|
|
47
|
+
recordEndpoint(method, routeOptions.path)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function buildEndpointObjects (endpoints) {
|
|
52
|
+
return endpoints.map(({ method, path }) => {
|
|
53
|
+
return {
|
|
54
|
+
type: 'REST',
|
|
55
|
+
method,
|
|
56
|
+
path,
|
|
57
|
+
operation_name: 'http.request',
|
|
58
|
+
resource_name: endpointKey(method, path)
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function flushAndSend () {
|
|
64
|
+
flushScheduled = false
|
|
65
|
+
if (pendingEndpoints.size === 0) return
|
|
66
|
+
|
|
67
|
+
const batchEndpoints = []
|
|
68
|
+
for (const [key, endpoint] of pendingEndpoints) {
|
|
69
|
+
batchEndpoints.push(endpoint)
|
|
70
|
+
pendingEndpoints.delete(key)
|
|
71
|
+
if (batchEndpoints.length >= config.appsec?.apiSecurity?.endpointCollectionMessageLimit) break
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const payloadObj = {
|
|
75
|
+
is_first: isFirstPayload,
|
|
76
|
+
endpoints: buildEndpointObjects(batchEndpoints)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let reqType = 'app-endpoints'
|
|
80
|
+
let payload = payloadObj
|
|
81
|
+
|
|
82
|
+
const retryData = getRetryData()
|
|
83
|
+
if (retryData) {
|
|
84
|
+
payload = [
|
|
85
|
+
{ request_type: 'app-endpoints', payload: payloadObj },
|
|
86
|
+
{ request_type: retryData.reqType, payload: retryData.payload }
|
|
87
|
+
]
|
|
88
|
+
reqType = 'message-batch'
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
sendData(config, application, host, reqType, payload, updateRetryData)
|
|
92
|
+
|
|
93
|
+
if (isFirstPayload) {
|
|
94
|
+
isFirstPayload = false
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// If more endpoints accumulated while sending, schedule another flush.
|
|
98
|
+
if (pendingEndpoints.size) scheduleFlush()
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function start (_config = {}, _application, _host, getRetryDataFunction, updateRetryDataFunction) {
|
|
102
|
+
if (!_config?.appsec?.apiSecurity?.endpointCollectionEnabled) return
|
|
103
|
+
|
|
104
|
+
config = _config
|
|
105
|
+
application = _application
|
|
106
|
+
host = _host
|
|
107
|
+
getRetryData = getRetryDataFunction
|
|
108
|
+
updateRetryData = updateRetryDataFunction
|
|
109
|
+
|
|
110
|
+
fastifyRouteCh.subscribe(onFastifyRoute)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function stop () {
|
|
114
|
+
fastifyRouteCh.unsubscribe(onFastifyRoute)
|
|
115
|
+
|
|
116
|
+
pendingEndpoints.clear()
|
|
117
|
+
flushScheduled = false
|
|
118
|
+
config = application = host = getRetryData = updateRetryData = null
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
module.exports = {
|
|
122
|
+
start,
|
|
123
|
+
stop
|
|
124
|
+
}
|
|
@@ -3,6 +3,7 @@ const tracerVersion = require('../../../../package.json').version
|
|
|
3
3
|
const dc = require('dc-polyfill')
|
|
4
4
|
const os = require('os')
|
|
5
5
|
const dependencies = require('./dependencies')
|
|
6
|
+
const endpoints = require('./endpoints')
|
|
6
7
|
const { sendData } = require('./send-data')
|
|
7
8
|
const { errors } = require('../startup-log')
|
|
8
9
|
const { manager: metricsManager } = require('./metrics')
|
|
@@ -254,6 +255,7 @@ function start (aConfig, thePluginManager) {
|
|
|
254
255
|
|
|
255
256
|
dependencies.start(config, application, host, getRetryData, updateRetryData)
|
|
256
257
|
telemetryLogger.start(config)
|
|
258
|
+
endpoints.start(config, application, host, getRetryData, updateRetryData)
|
|
257
259
|
|
|
258
260
|
sendData(config, application, host, 'app-started', appStarted(config))
|
|
259
261
|
|
|
@@ -280,6 +282,7 @@ function stop () {
|
|
|
280
282
|
|
|
281
283
|
telemetryStopChannel.publish(getTelemetryData())
|
|
282
284
|
|
|
285
|
+
endpoints.stop()
|
|
283
286
|
config = undefined
|
|
284
287
|
}
|
|
285
288
|
|