dd-trace 6.11.0 → 6.13.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/LICENSE-3rdparty.csv +5 -0
- package/ci/test-optimization-validation/framework-adapters/cypress.js +9 -11
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +5 -4
- package/index.d.ts +143 -25
- package/package.json +26 -25
- package/packages/datadog-instrumentations/src/bunyan.js +4 -2
- package/packages/datadog-instrumentations/src/cucumber.js +4 -4
- package/packages/datadog-instrumentations/src/helpers/bunyan.js +12 -3
- package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +28 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
- package/packages/datadog-instrumentations/src/jest.js +140 -18
- package/packages/datadog-instrumentations/src/mariadb-bundle.js +1597 -0
- package/packages/datadog-instrumentations/src/mariadb.js +7 -0
- package/packages/datadog-instrumentations/src/mocha/utils.js +5 -2
- package/packages/datadog-instrumentations/src/pino.js +15 -4
- package/packages/datadog-instrumentations/src/playwright-reporter.js +149 -1
- package/packages/datadog-instrumentations/src/playwright.js +67 -53
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +13 -12
- package/packages/datadog-instrumentations/src/vitest-worker.js +11 -0
- package/packages/datadog-instrumentations/src/webdriverio.js +26 -0
- package/packages/datadog-instrumentations/src/winston.js +6 -3
- package/packages/datadog-plugin-cypress/src/support.js +1 -0
- package/packages/datadog-plugin-http/src/client.js +2 -15
- package/packages/datadog-plugin-http2/src/client.js +2 -15
- package/packages/datadog-plugin-openai-agents/src/integration.js +15 -11
- package/packages/datadog-plugin-playwright/src/index.js +13 -1
- package/packages/datadog-plugin-undici/src/index.js +2 -15
- package/packages/dd-trace/src/aiguard/evaluation.js +94 -20
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -3
- package/packages/dd-trace/src/aiguard/messages/anthropic.js +21 -9
- package/packages/dd-trace/src/aiguard/noop.js +9 -1
- package/packages/dd-trace/src/aiguard/redaction.js +183 -0
- package/packages/dd-trace/src/aiguard/sdk.js +7 -2
- package/packages/dd-trace/src/aiguard/tags.js +2 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +10 -9
- package/packages/dd-trace/src/appsec/reporter.js +2 -0
- package/packages/dd-trace/src/appsec/rule_manager.js +2 -0
- package/packages/dd-trace/src/appsec/telemetry/index.js +13 -0
- package/packages/dd-trace/src/appsec/telemetry/rasp.js +16 -0
- package/packages/dd-trace/src/appsec/telemetry/waf.js +24 -0
- package/packages/dd-trace/src/carrier.js +107 -48
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +5 -5
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +2 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +5 -3
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +51 -26
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +4 -2
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +5 -11
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +6 -4
- package/packages/dd-trace/src/ci-visibility/final-flush.js +20 -0
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +5 -3
- package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +196 -29
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -2
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +4 -2
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +4 -2
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +5 -5
- package/packages/dd-trace/src/config/generated-config-types.d.ts +6 -0
- package/packages/dd-trace/src/config/index.js +2 -1
- package/packages/dd-trace/src/config/supported-configurations.json +27 -0
- package/packages/dd-trace/src/debugger/constants.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/condition.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/index.js +3 -10
- package/packages/dd-trace/src/debugger/devtools_client/send.js +4 -3
- package/packages/dd-trace/src/debugger/index.js +4 -2
- package/packages/dd-trace/src/debugger/inspect-segment.js +154 -0
- package/packages/dd-trace/src/dogstatsd.js +45 -18
- package/packages/dd-trace/src/encode/agentless-json.js +5 -4
- package/packages/dd-trace/src/evp_proxy/constants.js +8 -0
- package/packages/dd-trace/src/evp_proxy/direct.js +64 -0
- package/packages/dd-trace/src/evp_proxy/discovery.js +133 -0
- package/packages/dd-trace/src/evp_proxy/path.js +22 -0
- package/packages/dd-trace/src/exporters/agent/index.js +22 -2
- package/packages/dd-trace/src/exporters/agent/writer.js +4 -4
- package/packages/dd-trace/src/exporters/common/request.js +5 -2
- package/packages/dd-trace/src/exporters/common/writer.js +24 -2
- package/packages/dd-trace/src/exporters/span-stats/index.js +23 -4
- package/packages/dd-trace/src/external-logger/src/index.js +4 -3
- package/packages/dd-trace/src/flush.js +102 -0
- package/packages/dd-trace/src/llmobs/experiments/client.js +10 -2
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +301 -14
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +440 -112
- package/packages/dd-trace/src/llmobs/experiments/index.js +68 -44
- package/packages/dd-trace/src/llmobs/experiments/noop.js +103 -12
- package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
- package/packages/dd-trace/src/llmobs/experiments/util.js +81 -4
- package/packages/dd-trace/src/llmobs/index.js +80 -17
- package/packages/dd-trace/src/llmobs/noop.js +2 -1
- package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +8 -4
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +6 -3
- package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +13 -6
- package/packages/dd-trace/src/llmobs/plugins/openai-agents/utils.js +5 -7
- package/packages/dd-trace/src/llmobs/writers/base.js +60 -24
- package/packages/dd-trace/src/noop/span.js +5 -0
- package/packages/dd-trace/src/openfeature/constants/constants.js +0 -18
- package/packages/dd-trace/src/openfeature/index.js +7 -4
- package/packages/dd-trace/src/openfeature/writers/base.js +153 -11
- package/packages/dd-trace/src/openfeature/writers/exposures.js +70 -15
- package/packages/dd-trace/src/openfeature/writers/util.js +97 -15
- package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +45 -3
- package/packages/dd-trace/src/opentelemetry/logs/index.js +4 -1
- package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +7 -4
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +3 -1
- package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +8 -0
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +4 -2
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +4 -2
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +25 -5
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +61 -25
- package/packages/dd-trace/src/opentelemetry/tracer.js +26 -2
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +429 -352
- package/packages/dd-trace/src/opentracing/span.js +28 -0
- package/packages/dd-trace/src/otel-thread-ctx.js +23 -5
- package/packages/dd-trace/src/plugin_manager.js +2 -0
- package/packages/dd-trace/src/plugins/util/status-validator.js +98 -0
- package/packages/dd-trace/src/plugins/util/test.js +74 -21
- package/packages/dd-trace/src/plugins/util/web.js +2 -65
- package/packages/dd-trace/src/profiling/oom.js +4 -3
- package/packages/dd-trace/src/profiling/profiler.js +7 -0
- package/packages/dd-trace/src/profiling/profilers/events.js +3 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +14 -14
- package/packages/dd-trace/src/proxy.js +36 -8
- package/packages/dd-trace/src/runtime_metrics/index.js +5 -0
- package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +5 -0
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +17 -6
- package/packages/dd-trace/src/serverless/telemetry-delivery-tracker.js +55 -0
- package/packages/dd-trace/src/serverless/vercel.js +122 -0
- package/packages/dd-trace/src/serverless.js +37 -20
- package/packages/dd-trace/src/span_stats.js +40 -3
- package/packages/dd-trace/src/telemetry/metrics.js +62 -8
- package/packages/dd-trace/src/telemetry/send-data.js +1 -0
- package/packages/dd-trace/src/tracer.js +22 -0
- package/packages/dd-trace/src/web-tags-cache.js +67 -13
- package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/https-proxy-agent/LICENSE +22 -0
- package/vendor/dist/https-proxy-agent/index.js +4 -0
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/vendor/dist/proxy-from-env/LICENSE +20 -0
- package/vendor/dist/proxy-from-env/index.js +1 -0
|
@@ -272,6 +272,7 @@ export interface GeneratedConfig {
|
|
|
272
272
|
DD_TRACE_HAPI_ENABLED: boolean;
|
|
273
273
|
DD_TRACE_HAPI_HAPI_ENABLED: boolean;
|
|
274
274
|
DD_TRACE_HONO_ENABLED: boolean;
|
|
275
|
+
DD_TRACE_HTTP_CLIENT_ERROR_STATUSES: string;
|
|
275
276
|
DD_TRACE_HTTP_ENABLED: boolean;
|
|
276
277
|
DD_TRACE_HTTP_SERVER_ERROR_STATUSES: string;
|
|
277
278
|
DD_TRACE_HTTP2_ENABLED: boolean;
|
|
@@ -418,6 +419,7 @@ export interface GeneratedConfig {
|
|
|
418
419
|
endpoint: string | undefined;
|
|
419
420
|
maxContentSize: number;
|
|
420
421
|
maxMessagesLength: number;
|
|
422
|
+
redactionEnabled: boolean;
|
|
421
423
|
timeout: number;
|
|
422
424
|
};
|
|
423
425
|
appsec: {
|
|
@@ -473,6 +475,7 @@ export interface GeneratedConfig {
|
|
|
473
475
|
agentlessEnabled: boolean | undefined;
|
|
474
476
|
DD_LLMOBS_ENABLED: boolean;
|
|
475
477
|
mlApp: string | undefined;
|
|
478
|
+
projectName: string | undefined;
|
|
476
479
|
sampleRate: number;
|
|
477
480
|
};
|
|
478
481
|
logInjection: boolean;
|
|
@@ -614,6 +617,7 @@ export interface GeneratedEnvVarConfig {
|
|
|
614
617
|
DD_AI_GUARD_ENDPOINT: string | undefined;
|
|
615
618
|
DD_AI_GUARD_MAX_CONTENT_SIZE: number;
|
|
616
619
|
DD_AI_GUARD_MAX_MESSAGES_LENGTH: number;
|
|
620
|
+
DD_AI_GUARD_REDACTION_ENABLED: boolean;
|
|
617
621
|
DD_AI_GUARD_TIMEOUT: number;
|
|
618
622
|
DD_API_KEY: string | undefined;
|
|
619
623
|
DD_API_SECURITY_DOWNSTREAM_BODY_ANALYSIS_SAMPLE_RATE: number;
|
|
@@ -758,6 +762,7 @@ export interface GeneratedEnvVarConfig {
|
|
|
758
762
|
DD_LLMOBS_AGENTLESS_ENABLED: boolean | undefined;
|
|
759
763
|
DD_LLMOBS_ENABLED: boolean;
|
|
760
764
|
DD_LLMOBS_ML_APP: string | undefined;
|
|
765
|
+
DD_LLMOBS_PROJECT_NAME: string | undefined;
|
|
761
766
|
DD_LLMOBS_SAMPLE_RATE: number;
|
|
762
767
|
DD_LOG_LEVEL: "debug" | "info" | "warn" | "error";
|
|
763
768
|
DD_LOGS_INJECTION: boolean;
|
|
@@ -970,6 +975,7 @@ export interface GeneratedEnvVarConfig {
|
|
|
970
975
|
DD_TRACE_HAPI_HAPI_ENABLED: boolean;
|
|
971
976
|
DD_TRACE_HEADER_TAGS: string[];
|
|
972
977
|
DD_TRACE_HONO_ENABLED: boolean;
|
|
978
|
+
DD_TRACE_HTTP_CLIENT_ERROR_STATUSES: string;
|
|
973
979
|
DD_TRACE_HTTP_ENABLED: boolean;
|
|
974
980
|
DD_TRACE_HTTP_SERVER_ERROR_STATUSES: string;
|
|
975
981
|
DD_TRACE_HTTP2_ENABLED: boolean;
|
|
@@ -510,7 +510,8 @@ class Config extends ConfigBase {
|
|
|
510
510
|
if (!this.llmobs.DD_LLMOBS_ENABLED &&
|
|
511
511
|
!trackedConfigOrigins.has('llmobs.DD_LLMOBS_ENABLED') &&
|
|
512
512
|
(trackedConfigOrigins.has('llmobs.agentlessEnabled') ||
|
|
513
|
-
trackedConfigOrigins.has('llmobs.mlApp')
|
|
513
|
+
trackedConfigOrigins.has('llmobs.mlApp') ||
|
|
514
|
+
trackedConfigOrigins.has('llmobs.projectName'))) {
|
|
514
515
|
setAndTrack(this, 'llmobs.DD_LLMOBS_ENABLED', true)
|
|
515
516
|
}
|
|
516
517
|
|
|
@@ -93,6 +93,16 @@
|
|
|
93
93
|
"default": "16"
|
|
94
94
|
}
|
|
95
95
|
],
|
|
96
|
+
"DD_AI_GUARD_REDACTION_ENABLED": [
|
|
97
|
+
{
|
|
98
|
+
"implementation": "A",
|
|
99
|
+
"type": "boolean",
|
|
100
|
+
"configurationNames": [
|
|
101
|
+
"experimental.aiguard.redactionEnabled"
|
|
102
|
+
],
|
|
103
|
+
"default": "true"
|
|
104
|
+
}
|
|
105
|
+
],
|
|
96
106
|
"DD_AI_GUARD_TIMEOUT": [
|
|
97
107
|
{
|
|
98
108
|
"implementation": "A",
|
|
@@ -1341,6 +1351,16 @@
|
|
|
1341
1351
|
"default": null
|
|
1342
1352
|
}
|
|
1343
1353
|
],
|
|
1354
|
+
"DD_LLMOBS_PROJECT_NAME": [
|
|
1355
|
+
{
|
|
1356
|
+
"implementation": "A",
|
|
1357
|
+
"type": "string",
|
|
1358
|
+
"configurationNames": [
|
|
1359
|
+
"llmobs.projectName"
|
|
1360
|
+
],
|
|
1361
|
+
"default": null
|
|
1362
|
+
}
|
|
1363
|
+
],
|
|
1344
1364
|
"DD_LLMOBS_SAMPLE_RATE": [
|
|
1345
1365
|
{
|
|
1346
1366
|
"implementation": "A",
|
|
@@ -2999,6 +3019,13 @@
|
|
|
2999
3019
|
"default": "true"
|
|
3000
3020
|
}
|
|
3001
3021
|
],
|
|
3022
|
+
"DD_TRACE_HTTP_CLIENT_ERROR_STATUSES": [
|
|
3023
|
+
{
|
|
3024
|
+
"implementation": "A",
|
|
3025
|
+
"type": "string",
|
|
3026
|
+
"default": "400-499"
|
|
3027
|
+
}
|
|
3028
|
+
],
|
|
3002
3029
|
"DD_TRACE_HTTP_ENABLED": [
|
|
3003
3030
|
{
|
|
3004
3031
|
"implementation": "A",
|
|
@@ -55,7 +55,7 @@ function compileSegments (segments) {
|
|
|
55
55
|
? `(() => {
|
|
56
56
|
try {
|
|
57
57
|
const result = ${compile(json)}
|
|
58
|
-
return typeof result === 'string' ? result : $
|
|
58
|
+
return typeof result === 'string' ? result : $dd_inspectSegment(result)
|
|
59
59
|
} catch (e) {
|
|
60
60
|
return { expr: ${JSON.stringify(dsl)}, message: \`\${e.name}: \${e.message}\` }
|
|
61
61
|
}
|
|
@@ -4,6 +4,7 @@ const { randomUUID } = require('crypto')
|
|
|
4
4
|
const { workerData: { probeSamplerBuffer } } = require('worker_threads')
|
|
5
5
|
const { version } = require('../../../../../package.json')
|
|
6
6
|
const processTags = require('../../process-tags')
|
|
7
|
+
const { INSPECT_SEGMENT_GLOBAL_PROPERTY } = require('../constants')
|
|
7
8
|
const {
|
|
8
9
|
MAX_SAMPLED_PROBES_PER_PAUSE,
|
|
9
10
|
SAMPLED_PROBE_COUNT_INDEX,
|
|
@@ -23,16 +24,8 @@ require('./remote_config')
|
|
|
23
24
|
|
|
24
25
|
/** @typedef {import('node:inspector').Debugger.EvaluateOnCallFrameReturnType} EvaluateOnCallFrameResult */
|
|
25
26
|
|
|
26
|
-
const templateExpressionSetupCode =
|
|
27
|
-
|
|
28
|
-
const $dd_segmentInspectOptions = {
|
|
29
|
-
depth: 0,
|
|
30
|
-
customInspect: false,
|
|
31
|
-
maxArrayLength: 3,
|
|
32
|
-
maxStringLength: 8 * 1024,
|
|
33
|
-
breakLength: Infinity
|
|
34
|
-
};
|
|
35
|
-
`
|
|
27
|
+
const templateExpressionSetupCode = 'const $dd_inspectSegment = ' +
|
|
28
|
+
`globalThis[Symbol.for('dd-trace')][${JSON.stringify(INSPECT_SEGMENT_GLOBAL_PROPERTY)}];`
|
|
36
29
|
|
|
37
30
|
// Expression to run on a call frame of the paused thread to get its active trace and span id.
|
|
38
31
|
const getDDTagsExpression = `(() => {
|
|
@@ -141,7 +141,7 @@ function setInputPath (newPath) {
|
|
|
141
141
|
* @returns {string} The serialized tags.
|
|
142
142
|
*/
|
|
143
143
|
function buildTags (tags) {
|
|
144
|
-
|
|
144
|
+
let serializedTags = ''
|
|
145
145
|
|
|
146
146
|
for (const [key, rawValue] of tags) {
|
|
147
147
|
if (rawValue === undefined) continue
|
|
@@ -151,8 +151,9 @@ function buildTags (tags) {
|
|
|
151
151
|
continue
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
serializedTags
|
|
154
|
+
if (serializedTags) serializedTags += ','
|
|
155
|
+
serializedTags += `${key}:${rawValue}`
|
|
155
156
|
}
|
|
156
157
|
|
|
157
|
-
return serializedTags
|
|
158
|
+
return serializedTags
|
|
158
159
|
}
|
|
@@ -7,7 +7,7 @@ const { Worker, MessageChannel, threadId: parentThreadId } = require('worker_thr
|
|
|
7
7
|
const log = require('../log')
|
|
8
8
|
const { fetchAgentInfo } = require('../agent/info')
|
|
9
9
|
const getDebuggerConfig = require('./config')
|
|
10
|
-
const { DEBUGGER_DIAGNOSTICS_V1, DEBUGGER_INPUT_V2 } = require('./constants')
|
|
10
|
+
const { DEBUGGER_DIAGNOSTICS_V1, DEBUGGER_INPUT_V2, INSPECT_SEGMENT_GLOBAL_PROPERTY } = require('./constants')
|
|
11
11
|
const { installProbeSampler, uninstallProbeSampler } = require('./probe_sampler')
|
|
12
12
|
|
|
13
13
|
/**
|
|
@@ -64,7 +64,9 @@ function start (config, rcInstance) {
|
|
|
64
64
|
const logChannel = new MessageChannel()
|
|
65
65
|
configChannel = new MessageChannel()
|
|
66
66
|
|
|
67
|
-
globalThis[Symbol.for('dd-trace')]
|
|
67
|
+
const debuggerGlobals = globalThis[Symbol.for('dd-trace')]
|
|
68
|
+
debuggerGlobals.utilTypes = types
|
|
69
|
+
debuggerGlobals[INSPECT_SEGMENT_GLOBAL_PROPERTY] = require('./inspect-segment')
|
|
68
70
|
|
|
69
71
|
const probeSamplerBuffer = installProbeSampler()
|
|
70
72
|
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { inspect, types } = require('node:util')
|
|
4
|
+
|
|
5
|
+
const { NODE_MAJOR } = require('../../../../version')
|
|
6
|
+
|
|
7
|
+
/** @typedef {NonNullable<ReturnType<typeof globalThis.Object.getOwnPropertyDescriptor>>} PropertyDescriptor */
|
|
8
|
+
/** @typedef {Map<unknown, unknown> | Set<unknown>} Collection */
|
|
9
|
+
|
|
10
|
+
const mapEntries = Map.prototype.entries
|
|
11
|
+
const mapSet = Map.prototype.set
|
|
12
|
+
const mapSizeGetter = Object.getOwnPropertyDescriptor(Map.prototype, 'size').get
|
|
13
|
+
const setAdd = Set.prototype.add
|
|
14
|
+
const setSizeGetter = Object.getOwnPropertyDescriptor(Set.prototype, 'size').get
|
|
15
|
+
const setValues = Set.prototype.values
|
|
16
|
+
const mapIteratorNext = Object.getPrototypeOf(mapEntries.call(new Map())).next
|
|
17
|
+
const setIteratorNext = Object.getPrototypeOf(setValues.call(new Set())).next
|
|
18
|
+
|
|
19
|
+
const maxCollectionEntries = 3
|
|
20
|
+
const maxProperties = 5
|
|
21
|
+
const segmentInspectOptions = {
|
|
22
|
+
depth: 0,
|
|
23
|
+
customInspect: false,
|
|
24
|
+
maxArrayLength: maxCollectionEntries,
|
|
25
|
+
maxStringLength: 8 * 1024,
|
|
26
|
+
breakLength: Infinity,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = inspectSegment
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Inspect a dynamic-instrumentation template value without invoking user code.
|
|
33
|
+
* Unlike collections, `util.inspect` has no option for limiting the number of object properties, so this function
|
|
34
|
+
* truncates objects before inspecting them.
|
|
35
|
+
*
|
|
36
|
+
* @param {unknown} value
|
|
37
|
+
* @returns {string}
|
|
38
|
+
*/
|
|
39
|
+
function inspectSegment (value) {
|
|
40
|
+
if (value === null || (typeof value !== 'object' && typeof value !== 'function')) {
|
|
41
|
+
return inspect(value, segmentInspectOptions)
|
|
42
|
+
}
|
|
43
|
+
if (types.isProxy(value)) return '[Proxy]'
|
|
44
|
+
if (types.isMap(value)) return inspectCollection(value, true)
|
|
45
|
+
if (types.isSet(value)) return inspectCollection(value, false)
|
|
46
|
+
if (
|
|
47
|
+
Array.isArray(value) ||
|
|
48
|
+
types.isTypedArray(value) ||
|
|
49
|
+
types.isAnyArrayBuffer(value) ||
|
|
50
|
+
types.isDataView(value) ||
|
|
51
|
+
types.isWeakMap(value) ||
|
|
52
|
+
types.isWeakSet(value) ||
|
|
53
|
+
types.isMapIterator(value) ||
|
|
54
|
+
types.isSetIterator(value)
|
|
55
|
+
) {
|
|
56
|
+
return inspect(value, segmentInspectOptions)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** @type {(string | symbol)[]} */
|
|
60
|
+
const keys = Object.keys(value)
|
|
61
|
+
let propertyCount = keys.length
|
|
62
|
+
const symbols = Object.getOwnPropertySymbols(value)
|
|
63
|
+
for (let i = 0; i < symbols.length; i++) {
|
|
64
|
+
if (Object.getOwnPropertyDescriptor(value, symbols[i])?.enumerable === true) {
|
|
65
|
+
propertyCount++
|
|
66
|
+
if (keys.length < maxProperties) keys.push(symbols[i])
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (propertyCount <= maxProperties) {
|
|
71
|
+
// TODO: Decide whether allowing util.inspect to invoke Symbol.toStringTag getters is acceptable. If it is,
|
|
72
|
+
// remove inspectionCanRunUserCode and the related omission paths.
|
|
73
|
+
if (inspectionCanRunUserCode(value)) {
|
|
74
|
+
return '[Value omitted: inspection may execute user code]'
|
|
75
|
+
}
|
|
76
|
+
return inspect(value, segmentInspectOptions)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const truncated = {}
|
|
80
|
+
for (let i = 0; i < maxProperties; i++) {
|
|
81
|
+
const descriptor = /** @type {PropertyDescriptor} */ (Object.getOwnPropertyDescriptor(value, keys[i]))
|
|
82
|
+
if (
|
|
83
|
+
(keys[i] === Symbol.toStringTag && descriptor.get !== undefined) ||
|
|
84
|
+
(descriptor.value !== value && inspectionCanRunUserCode(descriptor.value))
|
|
85
|
+
) {
|
|
86
|
+
return '[Value omitted: inspection may execute user code]'
|
|
87
|
+
}
|
|
88
|
+
if (descriptor.value === value) descriptor.value = truncated
|
|
89
|
+
Object.defineProperty(truncated, keys[i], descriptor)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const omitted = propertyCount - maxProperties
|
|
93
|
+
const inspected = inspect(truncated, segmentInspectOptions)
|
|
94
|
+
return `${inspected.slice(0, -2)}, ... ${omitted} more ${omitted === 1 ? 'property' : 'properties'} }`
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Inspect a Map or Set while bounding the number of entries on Node.js 18, where `util.inspect` does not.
|
|
99
|
+
*
|
|
100
|
+
* @param {Collection} value
|
|
101
|
+
* @param {boolean} isMap
|
|
102
|
+
* @returns {string}
|
|
103
|
+
*/
|
|
104
|
+
function inspectCollection (value, isMap) {
|
|
105
|
+
if (NODE_MAJOR !== 18) return inspect(value, segmentInspectOptions)
|
|
106
|
+
|
|
107
|
+
const size = (isMap ? mapSizeGetter : setSizeGetter).call(value)
|
|
108
|
+
if (size <= maxCollectionEntries) return inspect(value, segmentInspectOptions)
|
|
109
|
+
|
|
110
|
+
const truncated = isMap ? new Map() : new Set()
|
|
111
|
+
const iterator = (isMap ? mapEntries : setValues).call(value)
|
|
112
|
+
const iteratorNext = isMap ? mapIteratorNext : setIteratorNext
|
|
113
|
+
|
|
114
|
+
for (let i = 0; i < maxCollectionEntries; i++) {
|
|
115
|
+
const result = iteratorNext.call(iterator)
|
|
116
|
+
if (result.done) break
|
|
117
|
+
|
|
118
|
+
if (isMap) {
|
|
119
|
+
const entry = result.value
|
|
120
|
+
const key = entry[0] === value ? truncated : entry[0]
|
|
121
|
+
const entryValue = entry[1] === value ? truncated : entry[1]
|
|
122
|
+
mapSet.call(truncated, key, entryValue)
|
|
123
|
+
} else {
|
|
124
|
+
const entryValue = result.value === value ? truncated : result.value
|
|
125
|
+
setAdd.call(truncated, entryValue)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const type = isMap ? 'Map' : 'Set'
|
|
130
|
+
const inspected = inspect(truncated, segmentInspectOptions)
|
|
131
|
+
const normalized = inspected.replace(`${type}(${maxCollectionEntries})`, `${type}(${size})`)
|
|
132
|
+
const remaining = size - maxCollectionEntries
|
|
133
|
+
return `${normalized.slice(0, -2)}, ... ${remaining} more item${remaining === 1 ? '' : 's'} }`
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Determine whether inspecting a value could invoke a proxy trap or toStringTag getter.
|
|
138
|
+
*
|
|
139
|
+
* @param {unknown} value
|
|
140
|
+
* @returns {boolean}
|
|
141
|
+
*/
|
|
142
|
+
function inspectionCanRunUserCode (value) {
|
|
143
|
+
const type = typeof value
|
|
144
|
+
if (value === null || (type !== 'object' && type !== 'function')) return false
|
|
145
|
+
if (types.isProxy(value)) return true
|
|
146
|
+
|
|
147
|
+
let current = value
|
|
148
|
+
while (current !== null) {
|
|
149
|
+
if (Object.getOwnPropertyDescriptor(current, Symbol.toStringTag)?.get !== undefined) return true
|
|
150
|
+
current = Object.getPrototypeOf(current)
|
|
151
|
+
if (types.isProxy(current)) return true
|
|
152
|
+
}
|
|
153
|
+
return false
|
|
154
|
+
}
|
|
@@ -8,6 +8,8 @@ const request = require('./exporters/common/request')
|
|
|
8
8
|
const log = require('./log')
|
|
9
9
|
const Histogram = require('./histogram')
|
|
10
10
|
const { entityId } = require('./exporters/common/docker')
|
|
11
|
+
const { registerTelemetryFlusher } = require('./flush')
|
|
12
|
+
const { createServerlessDeliveryTracker } = require('./serverless')
|
|
11
13
|
|
|
12
14
|
const legacyStorage = storage('legacy')
|
|
13
15
|
|
|
@@ -25,6 +27,8 @@ const TYPE_HISTOGRAM = 'h'
|
|
|
25
27
|
class DogStatsDClient {
|
|
26
28
|
#lookup
|
|
27
29
|
#tagsPrefix
|
|
30
|
+
#serverlessDeliveryTracker
|
|
31
|
+
|
|
28
32
|
constructor (options) {
|
|
29
33
|
this.#lookup = options.lookup
|
|
30
34
|
if (options.metricsProxyUrl) {
|
|
@@ -41,6 +45,7 @@ class DogStatsDClient {
|
|
|
41
45
|
this._tags = options.tags
|
|
42
46
|
this.#tagsPrefix = this._tags?.length ? `|#${this._tags.join(',')}` : ''
|
|
43
47
|
this._queue = []
|
|
48
|
+
this.#serverlessDeliveryTracker = createServerlessDeliveryTracker()
|
|
44
49
|
this._buffer = ''
|
|
45
50
|
this._offset = 0
|
|
46
51
|
this._udp4 = this._socket('udp4')
|
|
@@ -67,23 +72,30 @@ class DogStatsDClient {
|
|
|
67
72
|
this._add(stat, value, TYPE_HISTOGRAM, tags)
|
|
68
73
|
}
|
|
69
74
|
|
|
70
|
-
flush () {
|
|
75
|
+
flush (done) {
|
|
71
76
|
const queue = this._enqueue()
|
|
72
77
|
|
|
73
|
-
if (queue.length === 0)
|
|
78
|
+
if (queue.length === 0) {
|
|
79
|
+
if (this.#serverlessDeliveryTracker) return this.#serverlessDeliveryTracker.waitForIdle(done)
|
|
80
|
+
return done?.()
|
|
81
|
+
}
|
|
74
82
|
|
|
75
83
|
log.debug('Flushing %s metrics via %s', queue.length, this._httpOptions ? 'HTTP' : 'UDP')
|
|
76
84
|
|
|
77
85
|
this._queue = []
|
|
78
86
|
|
|
79
|
-
|
|
80
|
-
this._sendHttp(queue)
|
|
81
|
-
|
|
82
|
-
|
|
87
|
+
const send = complete => {
|
|
88
|
+
if (this._httpOptions) this._sendHttp(queue, complete)
|
|
89
|
+
else this._sendUdp(queue, complete)
|
|
90
|
+
}
|
|
91
|
+
if (this.#serverlessDeliveryTracker) {
|
|
92
|
+
this.#serverlessDeliveryTracker.track(send)
|
|
93
|
+
return this.#serverlessDeliveryTracker.waitForIdle(done)
|
|
83
94
|
}
|
|
95
|
+
send(done)
|
|
84
96
|
}
|
|
85
97
|
|
|
86
|
-
_sendHttp (queue) {
|
|
98
|
+
_sendHttp (queue, done) {
|
|
87
99
|
const buffer = Buffer.concat(queue)
|
|
88
100
|
request(buffer, this._httpOptions, (err) => {
|
|
89
101
|
if (err) {
|
|
@@ -95,32 +107,46 @@ class DogStatsDClient {
|
|
|
95
107
|
// options. Either way, we can give UDP a try.
|
|
96
108
|
this._httpOptions = undefined
|
|
97
109
|
}
|
|
98
|
-
this._sendUdp(queue)
|
|
110
|
+
this._sendUdp(queue, done)
|
|
111
|
+
} else {
|
|
112
|
+
done?.()
|
|
99
113
|
}
|
|
100
114
|
})
|
|
101
115
|
}
|
|
102
116
|
|
|
103
|
-
_sendUdp (queue) {
|
|
117
|
+
_sendUdp (queue, done) {
|
|
104
118
|
// dgram resolves the local address via the instrumented dns.lookup when it
|
|
105
119
|
// binds on first send; the noop store keeps that self-traffic off the trace.
|
|
106
120
|
legacyStorage.run({ noop: true }, () => {
|
|
107
121
|
if (this._family === 0) {
|
|
108
122
|
this.#lookup(this._host, (error, address, family) => {
|
|
109
|
-
if (error)
|
|
110
|
-
|
|
123
|
+
if (error) {
|
|
124
|
+
log.error('DogStatsDClient: Host not found', error)
|
|
125
|
+
return done?.()
|
|
126
|
+
}
|
|
127
|
+
this._sendUdpFromQueue(queue, address, family, done)
|
|
111
128
|
})
|
|
112
129
|
} else {
|
|
113
|
-
this._sendUdpFromQueue(queue, this._host, this._family)
|
|
130
|
+
this._sendUdpFromQueue(queue, this._host, this._family, done)
|
|
114
131
|
}
|
|
115
132
|
})
|
|
116
133
|
}
|
|
117
134
|
|
|
118
|
-
_sendUdpFromQueue (queue, address, family) {
|
|
135
|
+
_sendUdpFromQueue (queue, address, family, done) {
|
|
119
136
|
const socket = family === 6 ? this._udp6 : this._udp4
|
|
137
|
+
let pending = queue.length
|
|
138
|
+
const complete = () => {
|
|
139
|
+
if (--pending === 0) done?.()
|
|
140
|
+
}
|
|
120
141
|
|
|
121
142
|
for (const buffer of queue) {
|
|
122
143
|
log.debug('Sending to DogStatsD: %s', buffer)
|
|
123
|
-
|
|
144
|
+
try {
|
|
145
|
+
socket.send(buffer, 0, buffer.length, this._port, address, complete)
|
|
146
|
+
} catch (error) {
|
|
147
|
+
log.error('DogStatsDClient: UDP error sending metrics', error)
|
|
148
|
+
complete()
|
|
149
|
+
}
|
|
124
150
|
}
|
|
125
151
|
}
|
|
126
152
|
|
|
@@ -212,12 +238,12 @@ class MetricsAggregationClient {
|
|
|
212
238
|
this.reset()
|
|
213
239
|
}
|
|
214
240
|
|
|
215
|
-
flush () {
|
|
241
|
+
flush (done) {
|
|
216
242
|
this._captureCounters()
|
|
217
243
|
this._captureGauges()
|
|
218
244
|
this._captureHistograms()
|
|
219
245
|
|
|
220
|
-
this._client.flush()
|
|
246
|
+
this._client.flush(done)
|
|
221
247
|
}
|
|
222
248
|
|
|
223
249
|
reset () {
|
|
@@ -370,6 +396,7 @@ class CustomMetrics {
|
|
|
370
396
|
setInterval(flush, 10 * 1000).unref?.()
|
|
371
397
|
|
|
372
398
|
globalThis[Symbol.for('dd-trace')].beforeExitHandlers.add(flush)
|
|
399
|
+
registerTelemetryFlusher(done => this.flush(done))
|
|
373
400
|
}
|
|
374
401
|
|
|
375
402
|
increment (stat, value = 1, tags) {
|
|
@@ -392,8 +419,8 @@ class CustomMetrics {
|
|
|
392
419
|
this.#client.histogram(stat, value, CustomMetrics.tagTranslator(tags))
|
|
393
420
|
}
|
|
394
421
|
|
|
395
|
-
flush () {
|
|
396
|
-
return this.#client.flush()
|
|
422
|
+
flush (done) {
|
|
423
|
+
return this.#client.flush(done)
|
|
397
424
|
}
|
|
398
425
|
|
|
399
426
|
/**
|
|
@@ -167,17 +167,18 @@ class AgentlessJSONEncoder {
|
|
|
167
167
|
const metadataPrefix = metadataJson.slice(0, -1)
|
|
168
168
|
const hasMetadata = metadataPrefix.length > 1 // more than just '{'
|
|
169
169
|
|
|
170
|
-
|
|
170
|
+
let tracesJson = ''
|
|
171
171
|
for (const spanStrings of this._traces) {
|
|
172
172
|
const spansJson = '[' + spanStrings.join(',') + ']'
|
|
173
|
+
if (tracesJson) tracesJson += ','
|
|
173
174
|
if (hasMetadata) {
|
|
174
|
-
|
|
175
|
+
tracesJson += metadataPrefix + ',"spans":' + spansJson + '}'
|
|
175
176
|
} else {
|
|
176
|
-
|
|
177
|
+
tracesJson += '{"spans":' + spansJson + '}'
|
|
177
178
|
}
|
|
178
179
|
}
|
|
179
180
|
|
|
180
|
-
const payload = '{"traces":[' +
|
|
181
|
+
const payload = '{"traces":[' + tracesJson + ']}'
|
|
181
182
|
this._reset()
|
|
182
183
|
return Buffer.from(payload, 'utf8')
|
|
183
184
|
} catch (err) {
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { format } = require('node:url')
|
|
4
|
+
|
|
5
|
+
const { HttpsProxyAgent } = require('../../../../vendor/dist/https-proxy-agent')
|
|
6
|
+
const { getProxyForUrl } = require('../../../../vendor/dist/proxy-from-env')
|
|
7
|
+
const log = require('../log')
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {object} DirectEVPRoute
|
|
11
|
+
* @property {URL} url - Direct intake URL
|
|
12
|
+
* @property {string} basePath - Direct intake base path
|
|
13
|
+
* @property {object} headers - Direct intake authentication headers
|
|
14
|
+
* @property {import('node:https').Agent} [agent] - Optional HTTPS proxy agent
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Creates an authenticated direct EVP intake route.
|
|
19
|
+
*
|
|
20
|
+
* This helper does not perform local receiver discovery.
|
|
21
|
+
*
|
|
22
|
+
* @param {import('../config/config-base')} config - Tracer configuration
|
|
23
|
+
* @param {string} intake - EVP intake subdomain
|
|
24
|
+
* @returns {DirectEVPRoute|undefined} Direct route when credentials and site are available
|
|
25
|
+
*/
|
|
26
|
+
function createDirectEVPRoute (config, intake) {
|
|
27
|
+
const apiKey = config.DD_API_KEY
|
|
28
|
+
if (!apiKey || !config.site) return
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const hostname = `${intake}.${config.site}`.toLowerCase()
|
|
32
|
+
const url = new URL(format({
|
|
33
|
+
protocol: 'https:',
|
|
34
|
+
hostname,
|
|
35
|
+
}))
|
|
36
|
+
if (
|
|
37
|
+
url.hostname !== hostname ||
|
|
38
|
+
url.username ||
|
|
39
|
+
url.password ||
|
|
40
|
+
url.port ||
|
|
41
|
+
url.pathname !== '/' ||
|
|
42
|
+
url.search ||
|
|
43
|
+
url.hash
|
|
44
|
+
) {
|
|
45
|
+
throw new Error('Invalid direct EVP intake URL')
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const proxyUrl = getProxyForUrl(url.href)
|
|
49
|
+
const agent = proxyUrl ? new HttpsProxyAgent(proxyUrl) : undefined
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
url,
|
|
53
|
+
basePath: '',
|
|
54
|
+
headers: {
|
|
55
|
+
'DD-API-KEY': apiKey,
|
|
56
|
+
},
|
|
57
|
+
...(agent && { agent }),
|
|
58
|
+
}
|
|
59
|
+
} catch (error) {
|
|
60
|
+
log.debug('Unable to configure direct EVP intake: %s', error.message)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = { createDirectEVPRoute }
|