dd-trace 5.28.0 → 5.29.1
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/LICENSE-3rdparty.csv +8 -2
- package/ci/init.js +16 -0
- package/index.d.ts +31 -13
- package/init.js +4 -68
- package/loader-hook.mjs +4 -0
- package/package.json +16 -11
- package/packages/datadog-core/src/storage.js +39 -2
- package/packages/datadog-instrumentations/src/aerospike.js +1 -1
- package/packages/datadog-instrumentations/src/cucumber.js +29 -3
- package/packages/datadog-instrumentations/src/express.js +38 -4
- package/packages/datadog-instrumentations/src/helpers/bundler-register.js +3 -3
- package/packages/datadog-instrumentations/src/helpers/hooks.js +0 -1
- package/packages/datadog-instrumentations/src/helpers/register.js +3 -4
- package/packages/datadog-instrumentations/src/http/client.js +1 -1
- package/packages/datadog-instrumentations/src/jest.js +27 -8
- package/packages/datadog-instrumentations/src/mocha/utils.js +2 -1
- package/packages/datadog-instrumentations/src/mysql2.js +13 -8
- package/packages/datadog-instrumentations/src/next.js +7 -4
- package/packages/datadog-instrumentations/src/passport-http.js +2 -14
- package/packages/datadog-instrumentations/src/passport-local.js +2 -14
- package/packages/datadog-instrumentations/src/passport-utils.js +43 -19
- package/packages/datadog-instrumentations/src/pg.js +6 -6
- package/packages/datadog-instrumentations/src/playwright.js +17 -4
- package/packages/datadog-instrumentations/src/router.js +97 -1
- package/packages/datadog-instrumentations/src/sequelize.js +9 -4
- package/packages/datadog-instrumentations/src/url.js +4 -0
- package/packages/datadog-instrumentations/src/vitest.js +27 -2
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +8 -3
- package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +154 -0
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/lambda.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/s3.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/util.js +92 -0
- package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +1 -1
- package/packages/datadog-plugin-cucumber/src/index.js +39 -4
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +3 -3
- package/packages/datadog-plugin-grpc/src/client.js +2 -2
- package/packages/datadog-plugin-grpc/src/util.js +1 -1
- package/packages/datadog-plugin-jest/src/index.js +39 -4
- package/packages/datadog-plugin-mocha/src/index.js +36 -2
- package/packages/datadog-plugin-oracledb/src/index.js +1 -1
- package/packages/datadog-plugin-vitest/src/index.js +34 -2
- package/packages/datadog-shimmer/src/shimmer.js +8 -4
- package/packages/dd-trace/src/appsec/addresses.js +3 -0
- package/packages/dd-trace/src/appsec/blocked_templates.js +1 -1
- package/packages/dd-trace/src/appsec/channels.js +1 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/code-injection-analyzer.js +4 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-password-rules.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secret-rules.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secrets-rules.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/injection-analyzer.js +10 -3
- package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +4 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/template-injection-analyzer.js +4 -0
- package/packages/dd-trace/src/appsec/iast/iast-plugin.js +6 -19
- package/packages/dd-trace/src/appsec/iast/taint-tracking/index.js +3 -3
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +64 -3
- package/packages/dd-trace/src/appsec/iast/taint-tracking/source-types.js +2 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-regex.js +2 -2
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +32 -37
- package/packages/dd-trace/src/appsec/index.js +16 -10
- package/packages/dd-trace/src/appsec/remote_config/capabilities.js +1 -0
- package/packages/dd-trace/src/appsec/remote_config/index.js +25 -1
- package/packages/dd-trace/src/appsec/reporter.js +3 -1
- package/packages/dd-trace/src/appsec/sdk/track_event.js +32 -19
- package/packages/dd-trace/src/appsec/telemetry.js +10 -0
- package/packages/dd-trace/src/appsec/user_tracking.js +168 -0
- package/packages/dd-trace/src/azure_metadata.js +4 -4
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +5 -4
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +39 -3
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +29 -9
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -2
- package/packages/dd-trace/src/config.js +24 -32
- package/packages/dd-trace/src/constants.js +1 -0
- package/packages/dd-trace/src/crashtracking/crashtracker.js +3 -2
- package/packages/dd-trace/src/datastreams/processor.js +4 -6
- package/packages/dd-trace/src/datastreams/writer.js +6 -5
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +80 -0
- package/packages/dd-trace/src/debugger/devtools_client/config.js +3 -1
- package/packages/dd-trace/src/debugger/devtools_client/defaults.js +6 -0
- package/packages/dd-trace/src/debugger/devtools_client/index.js +63 -8
- package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +10 -67
- package/packages/dd-trace/src/debugger/devtools_client/send.js +2 -1
- package/packages/dd-trace/src/debugger/devtools_client/state.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/status.js +4 -4
- package/packages/dd-trace/src/debugger/index.js +14 -10
- package/packages/dd-trace/src/dogstatsd.js +2 -2
- package/packages/dd-trace/src/encode/0.4.js +23 -78
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +0 -32
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +1 -2
- package/packages/dd-trace/src/encode/span-stats.js +0 -30
- package/packages/dd-trace/src/exporters/agent/writer.js +3 -3
- package/packages/dd-trace/src/exporters/common/request.js +1 -1
- package/packages/dd-trace/src/exporters/span-stats/writer.js +1 -1
- package/packages/dd-trace/src/flare/index.js +1 -1
- package/packages/dd-trace/src/guardrails/index.js +64 -0
- package/packages/dd-trace/src/guardrails/log.js +32 -0
- package/packages/dd-trace/src/guardrails/telemetry.js +78 -0
- package/packages/dd-trace/src/guardrails/util.js +10 -0
- package/packages/dd-trace/src/lambda/runtime/ritm.js +2 -2
- package/packages/dd-trace/src/llmobs/storage.js +2 -3
- package/packages/dd-trace/src/llmobs/writers/base.js +2 -2
- package/packages/dd-trace/src/log/channels.js +9 -2
- package/packages/dd-trace/src/log/index.js +11 -1
- package/packages/dd-trace/src/log/writer.js +14 -3
- package/packages/dd-trace/src/{encode → msgpack}/chunk.js +8 -5
- package/packages/dd-trace/src/msgpack/encoder.js +309 -0
- package/packages/dd-trace/src/msgpack/index.js +6 -0
- package/packages/dd-trace/src/opentelemetry/context_manager.js +2 -2
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +12 -9
- package/packages/dd-trace/src/opentracing/span.js +1 -1
- package/packages/dd-trace/src/opentracing/tracer.js +2 -2
- package/packages/dd-trace/src/plugin_manager.js +4 -2
- package/packages/dd-trace/src/plugins/ci_plugin.js +47 -4
- package/packages/dd-trace/src/plugins/plugin.js +1 -1
- package/packages/dd-trace/src/plugins/tracing.js +1 -1
- package/packages/dd-trace/src/plugins/util/git.js +7 -7
- package/packages/dd-trace/src/plugins/util/test.js +36 -3
- package/packages/dd-trace/src/plugins/util/web.js +2 -2
- package/packages/dd-trace/src/priority_sampler.js +11 -1
- package/packages/dd-trace/src/profiling/config.js +3 -0
- package/packages/dd-trace/src/profiling/exporters/agent.js +9 -68
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +76 -0
- package/packages/dd-trace/src/profiling/exporters/file.js +8 -4
- package/packages/dd-trace/src/profiling/profiler.js +62 -10
- package/packages/dd-trace/src/profiling/profilers/event_plugins/event.js +22 -12
- package/packages/dd-trace/src/profiling/profilers/events.js +47 -8
- package/packages/dd-trace/src/profiling/profilers/wall.js +2 -17
- package/packages/dd-trace/src/profiling/webspan-utils.js +23 -0
- package/packages/dd-trace/src/proxy.js +7 -2
- package/packages/dd-trace/src/runtime_metrics.js +107 -4
- package/packages/dd-trace/src/serverless.js +1 -1
- package/packages/dd-trace/src/span_processor.js +10 -10
- package/packages/dd-trace/src/tagger.js +1 -1
- package/packages/dd-trace/src/telemetry/index.js +1 -0
- package/packages/dd-trace/src/telemetry/logs/index.js +2 -2
- package/packages/dd-trace/src/telemetry/logs/log-collector.js +10 -2
- package/packages/dd-trace/src/telemetry/send-data.js +2 -2
- package/packages/dd-trace/src/util.js +5 -16
- package/packages/datadog-instrumentations/src/qs.js +0 -24
- package/packages/dd-trace/src/appsec/passport.js +0 -110
- package/packages/dd-trace/src/telemetry/init-telemetry.js +0 -75
|
@@ -254,10 +254,10 @@ class NodeApiEventSource {
|
|
|
254
254
|
}
|
|
255
255
|
|
|
256
256
|
class DatadogInstrumentationEventSource {
|
|
257
|
-
constructor (eventHandler) {
|
|
257
|
+
constructor (eventHandler, eventFilter) {
|
|
258
258
|
this.plugins = ['dns_lookup', 'dns_lookupservice', 'dns_resolve', 'dns_reverse', 'net'].map(m => {
|
|
259
259
|
const Plugin = require(`./event_plugins/${m}`)
|
|
260
|
-
return new Plugin(eventHandler)
|
|
260
|
+
return new Plugin(eventHandler, eventFilter)
|
|
261
261
|
})
|
|
262
262
|
|
|
263
263
|
this.started = false
|
|
@@ -292,29 +292,68 @@ class CompositeEventSource {
|
|
|
292
292
|
}
|
|
293
293
|
}
|
|
294
294
|
|
|
295
|
+
function createPossionProcessSamplingFilter (samplingIntervalMillis) {
|
|
296
|
+
let nextSamplingInstant = performance.now()
|
|
297
|
+
let currentSamplingInstant = 0
|
|
298
|
+
setNextSamplingInstant()
|
|
299
|
+
|
|
300
|
+
return event => {
|
|
301
|
+
const endTime = event.startTime + event.duration
|
|
302
|
+
while (endTime >= nextSamplingInstant) {
|
|
303
|
+
setNextSamplingInstant()
|
|
304
|
+
}
|
|
305
|
+
// An event is sampled if it started before, and ended on or after a sampling instant. The above
|
|
306
|
+
// while loop will ensure that the ending invariant is always true for the current sampling
|
|
307
|
+
// instant so we don't have to test for it below. Across calls, the invariant also holds as long
|
|
308
|
+
// as the events arrive in endTime order. This is true for events coming from
|
|
309
|
+
// DatadogInstrumentationEventSource; they will be ordered by endTime by virtue of this method
|
|
310
|
+
// being invoked synchronously with the plugins' finish() handler which evaluates
|
|
311
|
+
// performance.now(). OTOH, events coming from NodeAPIEventSource (GC in typical setup) might be
|
|
312
|
+
// somewhat delayed as they are queued by Node, so they can arrive out of order with regard to
|
|
313
|
+
// events coming from the non-queued source. By omitting the endTime check, we will pass through
|
|
314
|
+
// some short events that started and ended before the current sampling instant. OTOH, if we
|
|
315
|
+
// were to check for this.currentSamplingInstant <= endTime, we would discard some long events
|
|
316
|
+
// that also ended before the current sampling instant. We'd rather err on the side of including
|
|
317
|
+
// some short events than excluding some long events.
|
|
318
|
+
return event.startTime < currentSamplingInstant
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function setNextSamplingInstant () {
|
|
322
|
+
currentSamplingInstant = nextSamplingInstant
|
|
323
|
+
nextSamplingInstant -= Math.log(1 - Math.random()) * samplingIntervalMillis
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
295
327
|
/**
|
|
296
328
|
* This class generates pprof files with timeline events. It combines an event
|
|
297
|
-
* source with an event serializer.
|
|
329
|
+
* source with a sampling event filter and an event serializer.
|
|
298
330
|
*/
|
|
299
331
|
class EventsProfiler {
|
|
300
332
|
constructor (options = {}) {
|
|
301
333
|
this.type = 'events'
|
|
302
334
|
this.eventSerializer = new EventSerializer()
|
|
303
335
|
|
|
304
|
-
const eventHandler = event =>
|
|
305
|
-
|
|
336
|
+
const eventHandler = event => this.eventSerializer.addEvent(event)
|
|
337
|
+
const eventFilter = options.timelineSamplingEnabled
|
|
338
|
+
// options.samplingInterval comes in microseconds, we need millis
|
|
339
|
+
? createPossionProcessSamplingFilter((options.samplingInterval ?? 1e6 / 99) / 1000)
|
|
340
|
+
: _ => true
|
|
341
|
+
const filteringEventHandler = event => {
|
|
342
|
+
if (eventFilter(event)) {
|
|
343
|
+
eventHandler(event)
|
|
344
|
+
}
|
|
306
345
|
}
|
|
307
346
|
|
|
308
347
|
if (options.codeHotspotsEnabled) {
|
|
309
348
|
// Use Datadog instrumentation to collect events with span IDs. Still use
|
|
310
349
|
// Node API for GC events.
|
|
311
350
|
this.eventSource = new CompositeEventSource([
|
|
312
|
-
new DatadogInstrumentationEventSource(eventHandler),
|
|
313
|
-
new NodeApiEventSource(
|
|
351
|
+
new DatadogInstrumentationEventSource(eventHandler, eventFilter),
|
|
352
|
+
new NodeApiEventSource(filteringEventHandler, ['gc'])
|
|
314
353
|
])
|
|
315
354
|
} else {
|
|
316
355
|
// Use Node API instrumentation to collect events without span IDs
|
|
317
|
-
this.eventSource = new NodeApiEventSource(
|
|
356
|
+
this.eventSource = new NodeApiEventSource(filteringEventHandler)
|
|
318
357
|
}
|
|
319
358
|
}
|
|
320
359
|
|
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
const { storage } = require('../../../../datadog-core')
|
|
4
4
|
|
|
5
5
|
const dc = require('dc-polyfill')
|
|
6
|
-
const { HTTP_METHOD, HTTP_ROUTE, RESOURCE_NAME, SPAN_TYPE } = require('../../../../../ext/tags')
|
|
7
|
-
const { WEB } = require('../../../../../ext/types')
|
|
8
6
|
const runtimeMetrics = require('../../runtime_metrics')
|
|
9
7
|
const telemetryMetrics = require('../../telemetry/metrics')
|
|
10
8
|
const {
|
|
@@ -15,6 +13,8 @@ const {
|
|
|
15
13
|
getThreadLabels
|
|
16
14
|
} = require('./shared')
|
|
17
15
|
|
|
16
|
+
const { isWebServerSpan, endpointNameFromTags, getStartedSpans } = require('../webspan-utils')
|
|
17
|
+
|
|
18
18
|
const beforeCh = dc.channel('dd-trace:storage:before')
|
|
19
19
|
const enterCh = dc.channel('dd-trace:storage:enter')
|
|
20
20
|
const spanFinishCh = dc.channel('dd-trace:span:finish')
|
|
@@ -29,21 +29,6 @@ function getActiveSpan () {
|
|
|
29
29
|
return store && store.span
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
function getStartedSpans (context) {
|
|
33
|
-
return context._trace.started
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function isWebServerSpan (tags) {
|
|
37
|
-
return tags[SPAN_TYPE] === WEB
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function endpointNameFromTags (tags) {
|
|
41
|
-
return tags[RESOURCE_NAME] || [
|
|
42
|
-
tags[HTTP_METHOD],
|
|
43
|
-
tags[HTTP_ROUTE]
|
|
44
|
-
].filter(v => v).join(' ')
|
|
45
|
-
}
|
|
46
|
-
|
|
47
32
|
let channelsActivated = false
|
|
48
33
|
function ensureChannelsActivated () {
|
|
49
34
|
if (channelsActivated) return
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const { HTTP_METHOD, HTTP_ROUTE, RESOURCE_NAME, SPAN_TYPE } = require('../../../../ext/tags')
|
|
2
|
+
const { WEB } = require('../../../../ext/types')
|
|
3
|
+
|
|
4
|
+
function isWebServerSpan (tags) {
|
|
5
|
+
return tags[SPAN_TYPE] === WEB
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function endpointNameFromTags (tags) {
|
|
9
|
+
return tags[RESOURCE_NAME] || [
|
|
10
|
+
tags[HTTP_METHOD],
|
|
11
|
+
tags[HTTP_ROUTE]
|
|
12
|
+
].filter(v => v).join(' ')
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getStartedSpans (context) {
|
|
16
|
+
return context._trace.started
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = {
|
|
20
|
+
isWebServerSpan,
|
|
21
|
+
endpointNameFromTags,
|
|
22
|
+
getStartedSpans
|
|
23
|
+
}
|
|
@@ -181,8 +181,13 @@ class Tracer extends NoopProxy {
|
|
|
181
181
|
)
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
|
+
|
|
185
|
+
if (config.isTestDynamicInstrumentationEnabled) {
|
|
186
|
+
const testVisibilityDynamicInstrumentation = require('./ci-visibility/dynamic-instrumentation')
|
|
187
|
+
testVisibilityDynamicInstrumentation.start()
|
|
188
|
+
}
|
|
184
189
|
} catch (e) {
|
|
185
|
-
log.error(e)
|
|
190
|
+
log.error('Error initialising tracer', e)
|
|
186
191
|
}
|
|
187
192
|
|
|
188
193
|
return this
|
|
@@ -193,7 +198,7 @@ class Tracer extends NoopProxy {
|
|
|
193
198
|
try {
|
|
194
199
|
return require('./profiler').start(config)
|
|
195
200
|
} catch (e) {
|
|
196
|
-
log.error(e)
|
|
201
|
+
log.error('Error starting profiler', e)
|
|
197
202
|
}
|
|
198
203
|
}
|
|
199
204
|
|
|
@@ -7,11 +7,19 @@ const os = require('os')
|
|
|
7
7
|
const { DogStatsDClient } = require('./dogstatsd')
|
|
8
8
|
const log = require('./log')
|
|
9
9
|
const Histogram = require('./histogram')
|
|
10
|
-
const { performance } = require('perf_hooks')
|
|
10
|
+
const { performance, PerformanceObserver } = require('perf_hooks')
|
|
11
11
|
|
|
12
|
+
const { NODE_MAJOR, NODE_MINOR } = require('../../../version')
|
|
12
13
|
const INTERVAL = 10 * 1000
|
|
13
14
|
|
|
15
|
+
// Node >=16 has PerformanceObserver with `gc` type, but <16.7 had a critical bug.
|
|
16
|
+
// See: https://github.com/nodejs/node/issues/39548
|
|
17
|
+
const hasGCObserver = NODE_MAJOR >= 18 || (NODE_MAJOR === 16 && NODE_MINOR >= 7)
|
|
18
|
+
const hasGCProfiler = NODE_MAJOR >= 20 || (NODE_MAJOR === 18 && NODE_MINOR >= 15)
|
|
19
|
+
|
|
14
20
|
let nativeMetrics = null
|
|
21
|
+
let gcObserver = null
|
|
22
|
+
let gcProfiler = null
|
|
15
23
|
|
|
16
24
|
let interval
|
|
17
25
|
let client
|
|
@@ -24,15 +32,20 @@ let elu
|
|
|
24
32
|
|
|
25
33
|
reset()
|
|
26
34
|
|
|
27
|
-
module.exports = {
|
|
35
|
+
const runtimeMetrics = module.exports = {
|
|
28
36
|
start (config) {
|
|
29
37
|
const clientConfig = DogStatsDClient.generateClientConfig(config)
|
|
30
38
|
|
|
31
39
|
try {
|
|
32
40
|
nativeMetrics = require('@datadog/native-metrics')
|
|
33
|
-
|
|
41
|
+
|
|
42
|
+
if (hasGCObserver) {
|
|
43
|
+
nativeMetrics.start('loop') // Only add event loop watcher and not GC.
|
|
44
|
+
} else {
|
|
45
|
+
nativeMetrics.start()
|
|
46
|
+
}
|
|
34
47
|
} catch (e) {
|
|
35
|
-
log.error(e)
|
|
48
|
+
log.error('Error starting native metrics', e)
|
|
36
49
|
nativeMetrics = null
|
|
37
50
|
}
|
|
38
51
|
|
|
@@ -40,6 +53,9 @@ module.exports = {
|
|
|
40
53
|
|
|
41
54
|
time = process.hrtime()
|
|
42
55
|
|
|
56
|
+
startGCObserver()
|
|
57
|
+
startGCProfiler()
|
|
58
|
+
|
|
43
59
|
if (nativeMetrics) {
|
|
44
60
|
interval = setInterval(() => {
|
|
45
61
|
captureCommonMetrics()
|
|
@@ -138,6 +154,10 @@ function reset () {
|
|
|
138
154
|
counters = {}
|
|
139
155
|
histograms = {}
|
|
140
156
|
nativeMetrics = null
|
|
157
|
+
gcObserver && gcObserver.disconnect()
|
|
158
|
+
gcObserver = null
|
|
159
|
+
gcProfiler && gcProfiler.stop()
|
|
160
|
+
gcProfiler = null
|
|
141
161
|
}
|
|
142
162
|
|
|
143
163
|
function captureCpuUsage () {
|
|
@@ -202,6 +222,29 @@ function captureHeapSpace () {
|
|
|
202
222
|
client.gauge('runtime.node.heap.physical_size.by.space', stats[i].physical_space_size, tags)
|
|
203
223
|
}
|
|
204
224
|
}
|
|
225
|
+
function captureGCMetrics () {
|
|
226
|
+
if (!gcProfiler) return
|
|
227
|
+
|
|
228
|
+
const profile = gcProfiler.stop()
|
|
229
|
+
const pauseAll = new Histogram()
|
|
230
|
+
const pause = {}
|
|
231
|
+
|
|
232
|
+
for (const stat of profile.statistics) {
|
|
233
|
+
const type = stat.gcType.replace(/([a-z])([A-Z])/g, '$1_$2').toLowerCase()
|
|
234
|
+
|
|
235
|
+
pause[type] = pause[type] || new Histogram()
|
|
236
|
+
pause[type].record(stat.cost)
|
|
237
|
+
pauseAll.record(stat.cost)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
histogram('runtime.node.gc.pause', pauseAll)
|
|
241
|
+
|
|
242
|
+
for (const type in pause) {
|
|
243
|
+
histogram('runtime.node.gc.pause.by.type', pause[type], [`gc_type:${type}`])
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
gcProfiler.start()
|
|
247
|
+
}
|
|
205
248
|
|
|
206
249
|
function captureGauges () {
|
|
207
250
|
Object.keys(gauges).forEach(name => {
|
|
@@ -256,6 +299,7 @@ function captureCommonMetrics () {
|
|
|
256
299
|
captureCounters()
|
|
257
300
|
captureHistograms()
|
|
258
301
|
captureELU()
|
|
302
|
+
captureGCMetrics()
|
|
259
303
|
}
|
|
260
304
|
|
|
261
305
|
function captureNativeMetrics () {
|
|
@@ -297,6 +341,11 @@ function captureNativeMetrics () {
|
|
|
297
341
|
function histogram (name, stats, tags) {
|
|
298
342
|
tags = [].concat(tags)
|
|
299
343
|
|
|
344
|
+
// Stats can contain garbage data when a value was never recorded.
|
|
345
|
+
if (stats.count === 0) {
|
|
346
|
+
stats = { max: 0, min: 0, sum: 0, avg: 0, median: 0, p95: 0, count: 0 }
|
|
347
|
+
}
|
|
348
|
+
|
|
300
349
|
client.gauge(`${name}.min`, stats.min, tags)
|
|
301
350
|
client.gauge(`${name}.max`, stats.max, tags)
|
|
302
351
|
client.increment(`${name}.sum`, stats.sum, tags)
|
|
@@ -306,3 +355,57 @@ function histogram (name, stats, tags) {
|
|
|
306
355
|
client.gauge(`${name}.median`, stats.median, tags)
|
|
307
356
|
client.gauge(`${name}.95percentile`, stats.p95, tags)
|
|
308
357
|
}
|
|
358
|
+
|
|
359
|
+
function startGCObserver () {
|
|
360
|
+
if (gcObserver || hasGCProfiler || !hasGCObserver) return
|
|
361
|
+
|
|
362
|
+
gcObserver = new PerformanceObserver(list => {
|
|
363
|
+
for (const entry of list.getEntries()) {
|
|
364
|
+
const type = gcType(entry.detail?.kind || entry.kind)
|
|
365
|
+
|
|
366
|
+
runtimeMetrics.histogram('runtime.node.gc.pause.by.type', entry.duration, `gc_type:${type}`)
|
|
367
|
+
runtimeMetrics.histogram('runtime.node.gc.pause', entry.duration)
|
|
368
|
+
}
|
|
369
|
+
})
|
|
370
|
+
|
|
371
|
+
gcObserver.observe({ type: 'gc' })
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
function startGCProfiler () {
|
|
375
|
+
if (gcProfiler || !hasGCProfiler) return
|
|
376
|
+
|
|
377
|
+
gcProfiler = new v8.GCProfiler()
|
|
378
|
+
gcProfiler.start()
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function gcType (kind) {
|
|
382
|
+
if (NODE_MAJOR >= 22) {
|
|
383
|
+
switch (kind) {
|
|
384
|
+
case 1: return 'scavenge'
|
|
385
|
+
case 2: return 'minor_mark_sweep'
|
|
386
|
+
case 4: return 'mark_sweep_compact' // Deprecated, might be removed soon.
|
|
387
|
+
case 8: return 'incremental_marking'
|
|
388
|
+
case 16: return 'process_weak_callbacks'
|
|
389
|
+
case 31: return 'all'
|
|
390
|
+
}
|
|
391
|
+
} else if (NODE_MAJOR >= 18) {
|
|
392
|
+
switch (kind) {
|
|
393
|
+
case 1: return 'scavenge'
|
|
394
|
+
case 2: return 'minor_mark_compact'
|
|
395
|
+
case 4: return 'mark_sweep_compact'
|
|
396
|
+
case 8: return 'incremental_marking'
|
|
397
|
+
case 16: return 'process_weak_callbacks'
|
|
398
|
+
case 31: return 'all'
|
|
399
|
+
}
|
|
400
|
+
} else {
|
|
401
|
+
switch (kind) {
|
|
402
|
+
case 1: return 'scavenge'
|
|
403
|
+
case 2: return 'mark_sweep_compact'
|
|
404
|
+
case 4: return 'incremental_marking'
|
|
405
|
+
case 8: return 'process_weak_callbacks'
|
|
406
|
+
case 15: return 'all'
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
return 'unknown'
|
|
411
|
+
}
|
|
@@ -23,7 +23,7 @@ function maybeStartServerlessMiniAgent (config) {
|
|
|
23
23
|
try {
|
|
24
24
|
require('child_process').spawn(rustBinaryPath, { stdio: 'inherit' })
|
|
25
25
|
} catch (err) {
|
|
26
|
-
log.error(
|
|
26
|
+
log.error('Error spawning mini agent process: %s', err.message)
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
@@ -87,22 +87,22 @@ class SpanProcessor {
|
|
|
87
87
|
const id = context.toSpanId()
|
|
88
88
|
|
|
89
89
|
if (finished.has(span)) {
|
|
90
|
-
log.error(
|
|
90
|
+
log.error('Span was already finished in the same trace: %s', span)
|
|
91
91
|
} else {
|
|
92
92
|
finished.add(span)
|
|
93
93
|
|
|
94
94
|
if (finishedIds.has(id)) {
|
|
95
|
-
log.error(
|
|
95
|
+
log.error('Another span with the same ID was already finished in the same trace: %s', span)
|
|
96
96
|
} else {
|
|
97
97
|
finishedIds.add(id)
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
if (context._trace !== trace) {
|
|
101
|
-
log.error(
|
|
101
|
+
log.error('A span was finished in the wrong trace: %s', span)
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
if (finishedSpans.has(span)) {
|
|
105
|
-
log.error(
|
|
105
|
+
log.error('Span was already finished in a different trace: %s', span)
|
|
106
106
|
} else {
|
|
107
107
|
finishedSpans.add(span)
|
|
108
108
|
}
|
|
@@ -114,35 +114,35 @@ class SpanProcessor {
|
|
|
114
114
|
const id = context.toSpanId()
|
|
115
115
|
|
|
116
116
|
if (started.has(span)) {
|
|
117
|
-
log.error(
|
|
117
|
+
log.error('Span was already started in the same trace: %s', span)
|
|
118
118
|
} else {
|
|
119
119
|
started.add(span)
|
|
120
120
|
|
|
121
121
|
if (startedIds.has(id)) {
|
|
122
|
-
log.error(
|
|
122
|
+
log.error('Another span with the same ID was already started in the same trace: %s', span)
|
|
123
123
|
} else {
|
|
124
124
|
startedIds.add(id)
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
if (context._trace !== trace) {
|
|
128
|
-
log.error(
|
|
128
|
+
log.error('A span was started in the wrong trace: %s', span)
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
if (startedSpans.has(span)) {
|
|
132
|
-
log.error(
|
|
132
|
+
log.error('Span was already started in a different trace: %s', span)
|
|
133
133
|
} else {
|
|
134
134
|
startedSpans.add(span)
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
if (!finished.has(span)) {
|
|
139
|
-
log.error(
|
|
139
|
+
log.error('Span started in one trace but was finished in another trace: %s', span)
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
for (const span of trace.finished) {
|
|
144
144
|
if (!started.has(span)) {
|
|
145
|
-
log.error(
|
|
145
|
+
log.error('Span finished in one trace but was started in another trace: %s', span)
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
148
|
}
|
|
@@ -137,6 +137,7 @@ function appClosing () {
|
|
|
137
137
|
sendData(config, application, host, reqType, payload)
|
|
138
138
|
// We flush before shutting down.
|
|
139
139
|
metricsManager.send(config, application, host)
|
|
140
|
+
telemetryLogger.send(config, application, host)
|
|
140
141
|
}
|
|
141
142
|
|
|
142
143
|
function onBeforeExit () {
|
|
@@ -40,6 +40,7 @@ function onErrorLog (msg) {
|
|
|
40
40
|
|
|
41
41
|
const telLog = {
|
|
42
42
|
level: 'ERROR',
|
|
43
|
+
count: 1,
|
|
43
44
|
|
|
44
45
|
// existing log.error(err) without message will be reported as 'Generic Error'
|
|
45
46
|
message: message ?? 'Generic Error'
|
|
@@ -47,8 +48,7 @@ function onErrorLog (msg) {
|
|
|
47
48
|
|
|
48
49
|
if (cause) {
|
|
49
50
|
telLog.stack_trace = cause.stack
|
|
50
|
-
|
|
51
|
-
telLog.message = `${errorType}: ${telLog.message}`
|
|
51
|
+
telLog.errorType = cause.constructor.name
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
onLog(telLog)
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const log = require('../../log')
|
|
4
4
|
const { calculateDDBasePath } = require('../../util')
|
|
5
5
|
|
|
6
|
-
const logs = new Map()
|
|
6
|
+
const logs = new Map() // hash -> log
|
|
7
7
|
|
|
8
8
|
// NOTE: Is this a reasonable number?
|
|
9
9
|
let maxEntries = 10000
|
|
@@ -47,8 +47,14 @@ function sanitize (logEntry) {
|
|
|
47
47
|
.filter((line, index) => (isDDCode && index < firstIndex) || line.includes(ddBasePath))
|
|
48
48
|
.map(line => line.replace(ddBasePath, ''))
|
|
49
49
|
|
|
50
|
+
if (!isDDCode && logEntry.errorType && stackLines.length) {
|
|
51
|
+
stackLines = [`${logEntry.errorType}: redacted`, ...stackLines]
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
delete logEntry.errorType
|
|
55
|
+
|
|
50
56
|
logEntry.stack_trace = stackLines.join(EOL)
|
|
51
|
-
if (logEntry.stack_trace === '' && !logEntry.message) {
|
|
57
|
+
if (logEntry.stack_trace === '' && (!logEntry.message || logEntry.message === 'Generic Error')) {
|
|
52
58
|
// If entire stack was removed and there is no message we'd rather not log it at all.
|
|
53
59
|
return null
|
|
54
60
|
}
|
|
@@ -75,6 +81,8 @@ const logCollector = {
|
|
|
75
81
|
if (!logs.has(hash)) {
|
|
76
82
|
logs.set(hash, logEntry)
|
|
77
83
|
return true
|
|
84
|
+
} else {
|
|
85
|
+
logs.get(hash).count++
|
|
78
86
|
}
|
|
79
87
|
} catch (e) {
|
|
80
88
|
log.error('Unable to add log to logCollector: %s', e.message)
|
|
@@ -57,7 +57,7 @@ function sendData (config, application, host, reqType, payload = {}, cb = () =>
|
|
|
57
57
|
try {
|
|
58
58
|
url = url || new URL(getAgentlessTelemetryEndpoint(config.site))
|
|
59
59
|
} catch (err) {
|
|
60
|
-
log.error(err)
|
|
60
|
+
log.error('Telemetry endpoint url is invalid', err)
|
|
61
61
|
// No point to do the request if the URL is invalid
|
|
62
62
|
return cb(err, { payload, reqType })
|
|
63
63
|
}
|
|
@@ -100,7 +100,7 @@ function sendData (config, application, host, reqType, payload = {}, cb = () =>
|
|
|
100
100
|
path: '/api/v2/apmtelemetry'
|
|
101
101
|
}
|
|
102
102
|
if (backendUrl) {
|
|
103
|
-
request(data, backendOptions, (error) => { log.error(error) })
|
|
103
|
+
request(data, backendOptions, (error) => { log.error('Error sending telemetry data', error) })
|
|
104
104
|
} else {
|
|
105
105
|
log.error('Invalid Telemetry URL')
|
|
106
106
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const crypto = require('crypto')
|
|
4
3
|
const path = require('path')
|
|
5
4
|
|
|
6
5
|
function isTrue (str) {
|
|
@@ -25,6 +24,8 @@ function isError (value) {
|
|
|
25
24
|
|
|
26
25
|
// Matches a glob pattern to a given subject string
|
|
27
26
|
function globMatch (pattern, subject) {
|
|
27
|
+
if (typeof pattern === 'string') pattern = pattern.toLowerCase()
|
|
28
|
+
if (typeof subject === 'string') subject = subject.toLowerCase()
|
|
28
29
|
let px = 0 // [p]attern inde[x]
|
|
29
30
|
let sx = 0 // [s]ubject inde[x]
|
|
30
31
|
let nextPx = 0
|
|
@@ -64,6 +65,8 @@ function globMatch (pattern, subject) {
|
|
|
64
65
|
return true
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
// TODO: this adds stack traces relative to packages/
|
|
69
|
+
// shouldn't paths be relative to the root of dd-trace?
|
|
67
70
|
function calculateDDBasePath (dirname) {
|
|
68
71
|
const dirSteps = dirname.split(path.sep)
|
|
69
72
|
const packagesIndex = dirSteps.lastIndexOf('packages')
|
|
@@ -74,25 +77,11 @@ function hasOwn (object, prop) {
|
|
|
74
77
|
return Object.prototype.hasOwnProperty.call(object, prop)
|
|
75
78
|
}
|
|
76
79
|
|
|
77
|
-
/**
|
|
78
|
-
* Generates a unique hash from an array of strings by joining them with | before hashing.
|
|
79
|
-
* Used to uniquely identify AWS requests for span pointers.
|
|
80
|
-
* @param {string[]} components - Array of strings to hash
|
|
81
|
-
* @returns {string} A 32-character hash uniquely identifying the components
|
|
82
|
-
*/
|
|
83
|
-
function generatePointerHash (components) {
|
|
84
|
-
// If passing S3's ETag as a component, make sure any quotes have already been removed!
|
|
85
|
-
const dataToHash = components.join('|')
|
|
86
|
-
const hash = crypto.createHash('sha256').update(dataToHash).digest('hex')
|
|
87
|
-
return hash.substring(0, 32)
|
|
88
|
-
}
|
|
89
|
-
|
|
90
80
|
module.exports = {
|
|
91
81
|
isTrue,
|
|
92
82
|
isFalse,
|
|
93
83
|
isError,
|
|
94
84
|
globMatch,
|
|
95
85
|
calculateDDBasePath,
|
|
96
|
-
hasOwn
|
|
97
|
-
generatePointerHash
|
|
86
|
+
hasOwn
|
|
98
87
|
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const { addHook, channel } = require('./helpers/instrument')
|
|
4
|
-
const shimmer = require('../../datadog-shimmer')
|
|
5
|
-
|
|
6
|
-
const qsParseCh = channel('datadog:qs:parse:finish')
|
|
7
|
-
|
|
8
|
-
function wrapParse (originalParse) {
|
|
9
|
-
return function () {
|
|
10
|
-
const qsParsedObj = originalParse.apply(this, arguments)
|
|
11
|
-
if (qsParseCh.hasSubscribers && qsParsedObj) {
|
|
12
|
-
qsParseCh.publish({ qs: qsParsedObj })
|
|
13
|
-
}
|
|
14
|
-
return qsParsedObj
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
addHook({
|
|
19
|
-
name: 'qs',
|
|
20
|
-
versions: ['>=1']
|
|
21
|
-
}, qs => {
|
|
22
|
-
shimmer.wrap(qs, 'parse', wrapParse)
|
|
23
|
-
return qs
|
|
24
|
-
})
|