dd-trace 5.124.0 → 5.125.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 +3 -1
- package/ci/init.js +8 -3
- package/ci/test-optimization-validation/ci-package-scripts.js +2 -2
- package/ci/test-optimization-validation/ci-remediation.js +6 -6
- package/ci/test-optimization-validation/command-blocker.js +7 -5
- package/ci/test-optimization-validation/framework-adapters/cucumber.js +1 -1
- package/ci/test-optimization-validation/framework-adapters/vitest.js +3 -3
- package/ci/test-optimization-validation/package-check.js +1 -1
- package/ci/test-optimization-validation/report-writer.js +7 -5
- package/ci/vitest-no-worker-init-runner-setup.mjs +8 -0
- package/ci/vitest-no-worker-init-setup.mjs +13 -13
- package/ci/vitest-no-worker-init-v5-setup.mjs +4 -0
- package/package.json +13 -10
- package/packages/datadog-esbuild/index.js +3 -1
- package/packages/datadog-instrumentations/src/cypress-config.js +19 -17
- package/packages/datadog-instrumentations/src/fs.js +24 -20
- package/packages/datadog-instrumentations/src/google-cloud-vertexai.js +2 -2
- package/packages/datadog-instrumentations/src/graphql.js +16 -16
- package/packages/datadog-instrumentations/src/helpers/graphql-jit-runtime.js +504 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +265 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +1 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +95 -30
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +0 -25
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +83 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +85 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mercurius.js +30 -12
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +13 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +251 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +11 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +562 -27
- package/packages/datadog-instrumentations/src/jest.js +25 -20
- package/packages/datadog-instrumentations/src/knex.js +76 -0
- package/packages/datadog-instrumentations/src/mariadb.js +6 -0
- package/packages/datadog-instrumentations/src/mocha/common.js +59 -2
- package/packages/datadog-instrumentations/src/mocha/main.js +25 -15
- package/packages/datadog-instrumentations/src/mocha/utils.js +2 -1
- package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +98 -0
- package/packages/datadog-instrumentations/src/mocha/worker.js +12 -8
- package/packages/datadog-instrumentations/src/net.js +14 -17
- package/packages/datadog-instrumentations/src/next.js +212 -62
- package/packages/datadog-instrumentations/src/openai.js +42 -40
- package/packages/datadog-instrumentations/src/pg.js +15 -0
- package/packages/datadog-instrumentations/src/playwright.js +109 -13
- package/packages/datadog-instrumentations/src/rum-browser-scripts.js +40 -0
- package/packages/datadog-instrumentations/src/sequelize.js +94 -2
- package/packages/datadog-instrumentations/src/undici.js +53 -4
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +37 -3
- package/packages/datadog-instrumentations/src/vitest-main.js +92 -9
- package/packages/datadog-instrumentations/src/vitest-worker.js +71 -14
- package/packages/datadog-instrumentations/src/webdriverio.js +965 -6
- package/packages/datadog-plugin-amqp10/src/consumer.js +3 -1
- package/packages/datadog-plugin-amqp10/src/producer.js +3 -1
- package/packages/datadog-plugin-amqp10/src/util.js +2 -1
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +6 -1
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +16 -5
- package/packages/datadog-plugin-cassandra-driver/src/index.js +8 -3
- package/packages/datadog-plugin-cucumber/src/index.js +0 -2
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +262 -48
- package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +4 -1
- package/packages/datadog-plugin-graphql/src/execute.js +998 -196
- package/packages/datadog-plugin-graphql/src/index.js +27 -7
- package/packages/datadog-plugin-graphql/src/jit.js +136 -0
- package/packages/datadog-plugin-graphql/src/request.js +18 -27
- package/packages/datadog-plugin-graphql/src/resolve-error.js +108 -0
- package/packages/datadog-plugin-graphql/src/tools/signature.js +1 -2
- package/packages/datadog-plugin-graphql/src/tools/transforms.js +45 -3
- package/packages/datadog-plugin-graphql/src/utils.js +208 -120
- package/packages/datadog-plugin-graphql/src/validate.js +10 -23
- package/packages/datadog-plugin-jest/src/index.js +0 -6
- package/packages/datadog-plugin-langchain/src/tracing.js +7 -0
- package/packages/datadog-plugin-mocha/src/index.js +403 -75
- package/packages/datadog-plugin-mongodb-core/src/query.js +6 -0
- package/packages/datadog-plugin-mysql/src/index.js +41 -15
- package/packages/datadog-plugin-net/src/tcp.js +3 -1
- package/packages/datadog-plugin-next/src/index.js +63 -29
- package/packages/datadog-plugin-playwright/src/index.js +167 -27
- package/packages/datadog-plugin-undici/src/index.js +27 -41
- package/packages/datadog-plugin-vitest/src/index.js +6 -12
- package/packages/dd-trace/src/aiguard/evaluation.js +2 -1
- package/packages/dd-trace/src/aiguard/messages/anthropic.js +8 -1
- package/packages/dd-trace/src/aiguard/messages/openai.js +8 -1
- package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +20 -6
- package/packages/dd-trace/src/appsec/api_security/index.js +23 -6
- package/packages/dd-trace/src/appsec/api_security/sampler.js +99 -40
- package/packages/dd-trace/src/appsec/graphql.js +6 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +3 -3
- package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +2 -1
- package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +8 -2
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +11 -36
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/lambda.js +44 -26
- package/packages/dd-trace/src/ci-visibility/encode/json-encoder.js +32 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +8 -8
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +6 -4
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +20 -4
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +5 -173
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +7 -6
- package/packages/dd-trace/src/ci-visibility/exporters/agents.js +54 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/writer.js +1 -2
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +394 -225
- package/packages/dd-trace/src/ci-visibility/exporters/limits.js +9 -0
- package/packages/dd-trace/src/ci-visibility/exporters/request.js +323 -31
- package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +42 -0
- package/packages/dd-trace/src/ci-visibility/final-flush.js +2 -1
- package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +57 -18
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +0 -3
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +164 -71
- package/packages/dd-trace/src/ci-visibility/requests/video-request.js +176 -0
- package/packages/dd-trace/src/ci-visibility/telemetry.js +10 -7
- package/packages/dd-trace/src/ci-visibility/test-video.js +61 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -0
- package/packages/dd-trace/src/config/index.js +73 -13
- package/packages/dd-trace/src/config/parsers.js +11 -1
- package/packages/dd-trace/src/config/stable.js +3 -3
- package/packages/dd-trace/src/config/supported-configurations.json +20 -2
- package/packages/dd-trace/src/crashtracking/crashtracker.js +82 -18
- package/packages/dd-trace/src/debugger/config.js +12 -1
- package/packages/dd-trace/src/debugger/constants.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +2 -1
- package/packages/dd-trace/src/debugger/devtools_client/request-options.js +33 -0
- package/packages/dd-trace/src/debugger/devtools_client/send.js +7 -40
- package/packages/dd-trace/src/debugger/devtools_client/status.js +11 -6
- package/packages/dd-trace/src/debugger/devtools_client/tags.js +41 -0
- package/packages/dd-trace/src/debugger/index.js +23 -3
- package/packages/dd-trace/src/dogstatsd.js +510 -76
- package/packages/dd-trace/src/encode/0.4-cross-payload.js +170 -0
- package/packages/dd-trace/src/encode/0.4.js +5 -5
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +9 -8
- package/packages/dd-trace/src/evp_proxy/direct.js +27 -23
- package/packages/dd-trace/src/exporters/agent/index.js +2 -1
- package/packages/dd-trace/src/exporters/agent/writer.js +24 -7
- package/packages/dd-trace/src/exporters/agentless/index.js +7 -9
- package/packages/dd-trace/src/exporters/agentless/writer.js +100 -118
- package/packages/dd-trace/src/exporters/common/agents.js +28 -6
- package/packages/dd-trace/src/exporters/common/buffering-exporter.js +2 -2
- package/packages/dd-trace/src/exporters/common/final-flush-request-tracker.js +191 -0
- package/packages/dd-trace/src/exporters/common/request.js +31 -11
- package/packages/dd-trace/src/exporters/common/url.js +40 -1
- package/packages/dd-trace/src/exporters/common/writer.js +9 -7
- package/packages/dd-trace/src/flush.js +6 -3
- package/packages/dd-trace/src/id.js +14 -0
- package/packages/dd-trace/src/index.js +5 -2
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +2 -1
- package/packages/dd-trace/src/llmobs/index.js +32 -32
- package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +22 -71
- package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +23 -13
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +7 -0
- package/packages/dd-trace/src/llmobs/util.js +0 -33
- package/packages/dd-trace/src/llmobs/writers/base.js +49 -32
- package/packages/dd-trace/src/log/index.js +7 -1
- package/packages/dd-trace/src/{ci-visibility/log-submission → log-submission}/log-submission-plugin.js +48 -19
- package/packages/dd-trace/src/openfeature/writers/base.js +14 -12
- package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
- package/packages/dd-trace/src/opentelemetry/logs/index.js +5 -24
- package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +14 -4
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +15 -22
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +14 -4
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +10 -0
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +19 -4
- package/packages/dd-trace/src/opentelemetry/resource-attributes.js +55 -0
- package/packages/dd-trace/src/opentelemetry/trace/index.js +6 -1
- package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +10 -0
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +93 -18
- package/packages/dd-trace/src/payload-tagging/index.js +2 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +1 -6
- package/packages/dd-trace/src/plugins/tracing.js +12 -10
- package/packages/dd-trace/src/plugins/util/ci.js +3 -1
- package/packages/dd-trace/src/plugins/util/inferred_proxy.js +16 -15
- package/packages/dd-trace/src/plugins/util/test.js +83 -20
- package/packages/dd-trace/src/plugins/util/url.js +2 -2
- package/packages/dd-trace/src/plugins/util/web.js +4 -9
- package/packages/dd-trace/src/profiling/config.js +1 -0
- package/packages/dd-trace/src/profiling/oom.js +7 -2
- package/packages/dd-trace/src/profiling/profiler.js +23 -13
- package/packages/dd-trace/src/profiling/profilers/space.js +38 -11
- package/packages/dd-trace/src/profiling/webspan-utils.js +7 -4
- package/packages/dd-trace/src/proxy.js +75 -5
- package/packages/dd-trace/src/remote_config/index.js +319 -101
- package/packages/dd-trace/src/remote_config/scheduler.js +37 -12
- package/packages/dd-trace/src/runtime_metrics/client.js +37 -8
- package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +5 -1
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +5 -1
- package/packages/dd-trace/src/sampling_rule.js +36 -9
- package/packages/dd-trace/src/serverless/telemetry-delivery-tracker.js +4 -8
- package/packages/dd-trace/src/serverless/vercel.js +53 -20
- package/packages/dd-trace/src/serverless.js +10 -3
- package/packages/dd-trace/src/service-naming/schemas/util.js +1 -1
- package/packages/dd-trace/src/span_stats.js +24 -28
- package/packages/dd-trace/src/startup-log.js +3 -2
- package/packages/dd-trace/src/telemetry/agentless-url.js +28 -0
- package/packages/dd-trace/src/telemetry/send-data.js +18 -16
- package/packages/dd-trace/src/telemetry/session-propagation.js +3 -2
- package/packages/dd-trace/src/tracer.js +4 -5
- package/packages/dd-trace/src/tracer_metadata.js +11 -3
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/packages/datadog-instrumentations/src/playwright-browser-scripts.js +0 -27
- package/packages/dd-trace/src/encode/agentless-json.js +0 -210
|
@@ -2,7 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
const dgram = require('dgram')
|
|
4
4
|
const isIP = require('net').isIP
|
|
5
|
+
const { performance } = require('node:perf_hooks')
|
|
5
6
|
|
|
7
|
+
const { channel } = require('dc-polyfill')
|
|
8
|
+
|
|
9
|
+
const tracerVersion = require('../../../package.json').version
|
|
6
10
|
const { storage } = require('../../datadog-core')
|
|
7
11
|
const request = require('./exporters/common/request')
|
|
8
12
|
const log = require('./log')
|
|
@@ -14,42 +18,170 @@ const { createServerlessDeliveryTracker } = require('./serverless')
|
|
|
14
18
|
const legacyStorage = storage('legacy')
|
|
15
19
|
|
|
16
20
|
const MAX_BUFFER_SIZE = 1024 // limit from the agent
|
|
21
|
+
const TELEMETRY_INTERVAL = 10_000
|
|
17
22
|
|
|
18
23
|
const TYPE_COUNTER = 'c'
|
|
19
24
|
const TYPE_GAUGE = 'g'
|
|
20
25
|
const TYPE_DISTRIBUTION = 'd'
|
|
21
26
|
const TYPE_HISTOGRAM = 'h'
|
|
22
27
|
|
|
28
|
+
const TYPE_COUNTER_INDEX = 0
|
|
29
|
+
const TYPE_GAUGE_INDEX = 1
|
|
30
|
+
const TYPE_DISTRIBUTION_INDEX = 2
|
|
31
|
+
const TYPE_HISTOGRAM_INDEX = 3
|
|
32
|
+
const TYPE_LABELS = ['count', 'gauge', 'distribution', 'histogram']
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @typedef {'c'|'g'|'d'|'h'} MetricType
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @typedef {object} DogStatsDBufferState
|
|
40
|
+
* @property {string} message
|
|
41
|
+
* @property {number} offset
|
|
42
|
+
* @property {Buffer[]} queue
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @typedef {object} DogStatsDClientOptions
|
|
47
|
+
* @property {string} host
|
|
48
|
+
* @property {number} port
|
|
49
|
+
* @property {string[]} tags
|
|
50
|
+
* @property {typeof import('node:dns').lookup} lookup
|
|
51
|
+
* @property {URL|string} [metricsProxyUrl]
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @typedef {object} DogStatsDTelemetryState
|
|
56
|
+
* @property {number[]} aggregatedContextsByType
|
|
57
|
+
* @property {number} bytesDropped
|
|
58
|
+
* @property {number} bytesSent
|
|
59
|
+
* @property {number[]} metricsByType
|
|
60
|
+
* @property {number} nextFlush
|
|
61
|
+
* @property {number} packetsDropped
|
|
62
|
+
* @property {number} packetsSent
|
|
63
|
+
* @property {DogStatsDBufferState} payload
|
|
64
|
+
*/
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @typedef {object} MetricNode
|
|
68
|
+
* @property {Map<string, MetricNode>} nodes
|
|
69
|
+
* @property {number} submissions
|
|
70
|
+
* @property {number|Histogram|null} value
|
|
71
|
+
*/
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @callback CaptureMetric
|
|
75
|
+
* @param {MetricNode} node
|
|
76
|
+
* @param {string} name
|
|
77
|
+
* @param {string[]} tags
|
|
78
|
+
* @returns {void}
|
|
79
|
+
*/
|
|
80
|
+
|
|
81
|
+
const identityRefreshChannel = channel('datadog:identity:refresh')
|
|
82
|
+
|
|
23
83
|
/**
|
|
24
84
|
* @import { DogStatsD } from "../../../index.d.ts"
|
|
25
85
|
* @implements {DogStatsD}
|
|
26
86
|
*/
|
|
27
87
|
class DogStatsDClient {
|
|
88
|
+
#family
|
|
89
|
+
#host
|
|
90
|
+
#httpOptions
|
|
28
91
|
#lookup
|
|
29
|
-
#
|
|
92
|
+
#metrics = { message: '', offset: 0, queue: [] }
|
|
93
|
+
#port
|
|
30
94
|
#serverlessDeliveryTracker
|
|
95
|
+
#tagsPrefix
|
|
96
|
+
#telemetryHttpTagsPrefix
|
|
97
|
+
#telemetryUdpTagsPrefix
|
|
98
|
+
#udp4
|
|
99
|
+
#udp6
|
|
100
|
+
|
|
101
|
+
/** @type {DogStatsDTelemetryState|undefined} */
|
|
102
|
+
telemetry
|
|
31
103
|
|
|
32
|
-
|
|
104
|
+
/**
|
|
105
|
+
* @param {DogStatsDClientOptions} options - DogStatsD transport options
|
|
106
|
+
* @param {boolean} [telemetryEnabled] - Whether to collect client telemetry
|
|
107
|
+
*/
|
|
108
|
+
constructor (options, telemetryEnabled = false) {
|
|
33
109
|
this.#lookup = options.lookup
|
|
34
110
|
if (options.metricsProxyUrl) {
|
|
35
|
-
this
|
|
111
|
+
this.#httpOptions = {
|
|
36
112
|
method: 'POST',
|
|
113
|
+
retry: false,
|
|
37
114
|
url: options.metricsProxyUrl.toString(),
|
|
38
115
|
path: '/dogstatsd/v2/proxy',
|
|
39
116
|
}
|
|
40
117
|
}
|
|
41
118
|
|
|
42
|
-
this
|
|
43
|
-
this
|
|
44
|
-
this
|
|
45
|
-
this._tags = options.tags
|
|
46
|
-
this.#tagsPrefix = this._tags?.length ? `|#${this._tags.join(',')}` : ''
|
|
47
|
-
this._queue = []
|
|
119
|
+
this.#family = isIP(options.host)
|
|
120
|
+
this.#host = options.host
|
|
121
|
+
this.#port = options.port
|
|
48
122
|
this.#serverlessDeliveryTracker = createServerlessDeliveryTracker()
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
123
|
+
|
|
124
|
+
if (telemetryEnabled) {
|
|
125
|
+
this.telemetry = {
|
|
126
|
+
aggregatedContextsByType: [0, 0, 0, 0],
|
|
127
|
+
bytesDropped: 0,
|
|
128
|
+
bytesSent: 0,
|
|
129
|
+
metricsByType: [0, 0, 0, 0],
|
|
130
|
+
nextFlush: performance.now() + TELEMETRY_INTERVAL,
|
|
131
|
+
packetsDropped: 0,
|
|
132
|
+
packetsSent: 0,
|
|
133
|
+
payload: { message: '', offset: 0, queue: [] },
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
this.#updateTagPrefixes(options.tags.length ? `|#${options.tags.join(',')}` : '')
|
|
138
|
+
|
|
139
|
+
this.#udp4 = this._socket('udp4')
|
|
140
|
+
this.#udp6 = this._socket('udp6')
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Recomputes the cached tags and tag-prefix (mirrors the constructor) after a `config.tags`
|
|
145
|
+
* change, e.g. a MicroVM clone resume.
|
|
146
|
+
*
|
|
147
|
+
* Buffered lines have the old prefix baked in, and on a clone resume they were produced during
|
|
148
|
+
* the image build, so every clone holds the same bytes — flushing them would submit one identical
|
|
149
|
+
* copy per clone. Dropping is right here for that reason only: for a tag change on a live process
|
|
150
|
+
* the buffer holds unique data whose old tags are still correct, so that case wants a flush
|
|
151
|
+
* before the swap.
|
|
152
|
+
*
|
|
153
|
+
* @param {string[]} tags - DogStatsD-formatted tags (e.g. `['key:value']`)
|
|
154
|
+
*/
|
|
155
|
+
updateTags (tags) {
|
|
156
|
+
const tagsPrefix = tags.length ? `|#${tags.join(',')}` : ''
|
|
157
|
+
if (tagsPrefix === this.#tagsPrefix) return
|
|
158
|
+
|
|
159
|
+
this.#updateTagPrefixes(tagsPrefix)
|
|
160
|
+
this.#metrics.queue = []
|
|
161
|
+
this.#metrics.message = ''
|
|
162
|
+
this.#metrics.offset = 0
|
|
163
|
+
|
|
164
|
+
const payload = this.telemetry?.payload
|
|
165
|
+
if (payload) {
|
|
166
|
+
payload.queue = []
|
|
167
|
+
payload.message = ''
|
|
168
|
+
payload.offset = 0
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Updates the prefixes used for application metrics and client telemetry.
|
|
174
|
+
* @param {string} tagsPrefix - Serialized global DogStatsD tags
|
|
175
|
+
*/
|
|
176
|
+
#updateTagPrefixes (tagsPrefix) {
|
|
177
|
+
this.#tagsPrefix = tagsPrefix
|
|
178
|
+
|
|
179
|
+
if (!this.telemetry) return
|
|
180
|
+
|
|
181
|
+
const separator = tagsPrefix ? ',' : '|#'
|
|
182
|
+
const prefix = `${tagsPrefix}${separator}client:nodejs,client_version:${tracerVersion},client_transport:`
|
|
183
|
+
this.#telemetryHttpTagsPrefix = `${prefix}http`
|
|
184
|
+
this.#telemetryUdpTagsPrefix = `${prefix}udp`
|
|
53
185
|
}
|
|
54
186
|
|
|
55
187
|
increment (stat, value, tags) {
|
|
@@ -72,68 +204,218 @@ class DogStatsDClient {
|
|
|
72
204
|
this._add(stat, value, TYPE_HISTOGRAM, tags)
|
|
73
205
|
}
|
|
74
206
|
|
|
75
|
-
|
|
76
|
-
|
|
207
|
+
/**
|
|
208
|
+
* @param {boolean|(() => void)} [forceTelemetry] - Whether to ignore the telemetry interval, or completion callback
|
|
209
|
+
* @param {() => void} [done] - Called after serverless deliveries complete
|
|
210
|
+
* @returns {void}
|
|
211
|
+
*/
|
|
212
|
+
flush (forceTelemetry = false, done) {
|
|
213
|
+
if (typeof forceTelemetry === 'function') {
|
|
214
|
+
done = forceTelemetry
|
|
215
|
+
forceTelemetry = false
|
|
216
|
+
}
|
|
77
217
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
218
|
+
let complete
|
|
219
|
+
let track
|
|
220
|
+
if (done && !this.#serverlessDeliveryTracker) {
|
|
221
|
+
let pending = 1
|
|
222
|
+
complete = () => {
|
|
223
|
+
if (--pending === 0) done()
|
|
224
|
+
}
|
|
225
|
+
track = send => {
|
|
226
|
+
pending++
|
|
227
|
+
send(complete)
|
|
228
|
+
}
|
|
81
229
|
}
|
|
82
230
|
|
|
83
|
-
|
|
231
|
+
this.#flush(this.#metrics, true, track)
|
|
232
|
+
if (this.telemetry) this.#flushTelemetry(forceTelemetry, track)
|
|
84
233
|
|
|
85
|
-
this
|
|
234
|
+
if (this.#serverlessDeliveryTracker) return this.#serverlessDeliveryTracker.waitForIdle(done)
|
|
235
|
+
complete?.()
|
|
236
|
+
}
|
|
86
237
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
238
|
+
/**
|
|
239
|
+
* @param {boolean} force - Whether to ignore the telemetry interval
|
|
240
|
+
* @param {((send: (done?: () => void) => void) => void)} [track] - Tracks a non-serverless delivery
|
|
241
|
+
* @returns {void}
|
|
242
|
+
*/
|
|
243
|
+
#flushTelemetry (force, track) {
|
|
244
|
+
const telemetry = this.telemetry
|
|
245
|
+
|
|
246
|
+
const now = performance.now()
|
|
247
|
+
|
|
248
|
+
if (!force && now < telemetry.nextFlush) return
|
|
249
|
+
|
|
250
|
+
telemetry.nextFlush = now + TELEMETRY_INTERVAL
|
|
251
|
+
|
|
252
|
+
let aggregatedContexts = 0
|
|
253
|
+
let metrics = 0
|
|
254
|
+
for (let index = 0; index < TYPE_LABELS.length; index++) {
|
|
255
|
+
aggregatedContexts += telemetry.aggregatedContextsByType[index]
|
|
256
|
+
metrics += telemetry.metricsByType[index]
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
this.#addTelemetry('datadog.dogstatsd.client.metrics', metrics)
|
|
260
|
+
this.#addTelemetry('datadog.dogstatsd.client.aggregated_context', aggregatedContexts)
|
|
261
|
+
for (let index = 0; index < TYPE_LABELS.length; index++) {
|
|
262
|
+
this.#addTelemetry(
|
|
263
|
+
'datadog.dogstatsd.client.metrics_by_type',
|
|
264
|
+
telemetry.metricsByType[index],
|
|
265
|
+
TYPE_LABELS[index]
|
|
266
|
+
)
|
|
267
|
+
this.#addTelemetry(
|
|
268
|
+
'datadog.dogstatsd.client.aggregated_context_by_type',
|
|
269
|
+
telemetry.aggregatedContextsByType[index],
|
|
270
|
+
TYPE_LABELS[index]
|
|
271
|
+
)
|
|
90
272
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
273
|
+
this.#addTelemetry('datadog.dogstatsd.client.bytes_sent', telemetry.bytesSent)
|
|
274
|
+
this.#addTelemetry('datadog.dogstatsd.client.bytes_dropped', telemetry.bytesDropped)
|
|
275
|
+
this.#addTelemetry('datadog.dogstatsd.client.packets_sent', telemetry.packetsSent)
|
|
276
|
+
this.#addTelemetry('datadog.dogstatsd.client.packets_dropped', telemetry.packetsDropped)
|
|
277
|
+
|
|
278
|
+
telemetry.bytesSent = 0
|
|
279
|
+
telemetry.bytesDropped = 0
|
|
280
|
+
telemetry.packetsSent = 0
|
|
281
|
+
telemetry.packetsDropped = 0
|
|
282
|
+
for (let index = 0; index < TYPE_LABELS.length; index++) {
|
|
283
|
+
telemetry.aggregatedContextsByType[index] = 0
|
|
284
|
+
telemetry.metricsByType[index] = 0
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
this.#flush(telemetry.payload, false, track)
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* @param {DogStatsDBufferState} state - Payload state to flush
|
|
292
|
+
* @param {boolean} recordTelemetry - Whether to record the transport outcome
|
|
293
|
+
* @param {((send: (done?: () => void) => void) => void)} [track] - Tracks a non-serverless delivery
|
|
294
|
+
* @returns {void}
|
|
295
|
+
*/
|
|
296
|
+
#flush (state, recordTelemetry, track) {
|
|
297
|
+
const queue = this._enqueue(state)
|
|
298
|
+
|
|
299
|
+
if (queue.length === 0) return
|
|
300
|
+
|
|
301
|
+
log.debug('Flushing %s metrics via %s', queue.length, this.#httpOptions ? 'HTTP' : 'UDP')
|
|
302
|
+
|
|
303
|
+
state.queue = []
|
|
304
|
+
|
|
305
|
+
const send = complete => {
|
|
306
|
+
if (this.#httpOptions) this._sendHttp(queue, recordTelemetry, complete)
|
|
307
|
+
else this._sendUdp(queue, recordTelemetry, complete)
|
|
94
308
|
}
|
|
95
|
-
|
|
309
|
+
|
|
310
|
+
if (this.#serverlessDeliveryTracker) return this.#serverlessDeliveryTracker.track(send)
|
|
311
|
+
if (track) return track(send)
|
|
312
|
+
send()
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* @param {number} bytes - Number of bytes sent
|
|
317
|
+
* @returns {void}
|
|
318
|
+
*/
|
|
319
|
+
#recordSent (bytes) {
|
|
320
|
+
const telemetry = this.telemetry
|
|
321
|
+
|
|
322
|
+
telemetry.bytesSent += bytes
|
|
323
|
+
telemetry.packetsSent++
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* @param {number} bytes - Number of bytes dropped
|
|
328
|
+
* @param {number} [packets] - Number of packets dropped
|
|
329
|
+
* @returns {void}
|
|
330
|
+
*/
|
|
331
|
+
#recordDropped (bytes, packets = 1) {
|
|
332
|
+
const telemetry = this.telemetry
|
|
333
|
+
|
|
334
|
+
telemetry.bytesDropped += bytes
|
|
335
|
+
telemetry.packetsDropped += packets
|
|
96
336
|
}
|
|
97
337
|
|
|
98
|
-
|
|
338
|
+
/**
|
|
339
|
+
* Send metrics to the agent via HTTP
|
|
340
|
+
*
|
|
341
|
+
* @param {Buffer[]} queue - The metrics to send
|
|
342
|
+
* @param {boolean} recordTelemetry - Whether to record the transport outcome
|
|
343
|
+
* @param {() => void} [done] - Called after delivery completes
|
|
344
|
+
* @returns {void}
|
|
345
|
+
* @memberof DogStatsDClient
|
|
346
|
+
*/
|
|
347
|
+
_sendHttp (queue, recordTelemetry, done) {
|
|
99
348
|
const buffer = Buffer.concat(queue)
|
|
100
|
-
request(buffer, this
|
|
101
|
-
if (
|
|
102
|
-
|
|
103
|
-
|
|
349
|
+
request(buffer, this.#httpOptions, (error, _result, _statusCode, _headers, dropped) => {
|
|
350
|
+
if (dropped) {
|
|
351
|
+
if (recordTelemetry && this.telemetry) {
|
|
352
|
+
this.#recordDropped(buffer.length)
|
|
353
|
+
}
|
|
354
|
+
done?.()
|
|
355
|
+
} else if (error) {
|
|
356
|
+
log.error('DogStatsDClient: HTTP error from agent: %s', error.message, error)
|
|
357
|
+
if (error.status === 404) {
|
|
104
358
|
// Inside this if-block, we have connectivity to the agent, but
|
|
105
359
|
// we're not getting a 200 from the proxy endpoint. If it's a 404,
|
|
106
360
|
// then we know we'll never have the endpoint, so just clear out the
|
|
107
361
|
// options. Either way, we can give UDP a try.
|
|
108
|
-
this
|
|
362
|
+
this.#httpOptions = undefined
|
|
109
363
|
}
|
|
110
|
-
this._sendUdp(queue, done)
|
|
364
|
+
this._sendUdp(queue, recordTelemetry, done)
|
|
111
365
|
} else {
|
|
366
|
+
if (recordTelemetry && this.telemetry) this.#recordSent(buffer.length)
|
|
112
367
|
done?.()
|
|
113
368
|
}
|
|
114
369
|
})
|
|
115
370
|
}
|
|
116
371
|
|
|
117
|
-
|
|
372
|
+
/**
|
|
373
|
+
* Send metrics to the agent via UDP
|
|
374
|
+
*
|
|
375
|
+
* @param {Buffer[]} queue - The metrics to send
|
|
376
|
+
* @param {boolean} recordTelemetry - Whether to record the transport outcome
|
|
377
|
+
* @param {() => void} [done] - Called after delivery completes
|
|
378
|
+
* @returns {void}
|
|
379
|
+
* @memberof DogStatsDClient
|
|
380
|
+
*/
|
|
381
|
+
_sendUdp (queue, recordTelemetry, done) {
|
|
118
382
|
// dgram resolves the local address via the instrumented dns.lookup when it
|
|
119
383
|
// binds on first send; the noop store keeps that self-traffic off the trace.
|
|
120
384
|
legacyStorage.run({ noop: true }, () => {
|
|
121
|
-
if (this
|
|
122
|
-
this.#lookup(this
|
|
385
|
+
if (this.#family === 0) {
|
|
386
|
+
this.#lookup(this.#host, (error, address, family) => {
|
|
123
387
|
if (error) {
|
|
388
|
+
if (recordTelemetry && this.telemetry) {
|
|
389
|
+
let bytes = 0
|
|
390
|
+
for (const buffer of queue) {
|
|
391
|
+
bytes += buffer.length
|
|
392
|
+
}
|
|
393
|
+
this.#recordDropped(bytes, queue.length)
|
|
394
|
+
}
|
|
124
395
|
log.error('DogStatsDClient: Host not found', error)
|
|
125
396
|
return done?.()
|
|
126
397
|
}
|
|
127
|
-
this._sendUdpFromQueue(queue, address, family, done)
|
|
398
|
+
this._sendUdpFromQueue(queue, address, family, recordTelemetry, done)
|
|
128
399
|
})
|
|
129
400
|
} else {
|
|
130
|
-
this._sendUdpFromQueue(queue, this
|
|
401
|
+
this._sendUdpFromQueue(queue, this.#host, this.#family, recordTelemetry, done)
|
|
131
402
|
}
|
|
132
403
|
})
|
|
133
404
|
}
|
|
134
405
|
|
|
135
|
-
|
|
136
|
-
|
|
406
|
+
/**
|
|
407
|
+
* Send metrics to the agent via UDP from queue
|
|
408
|
+
*
|
|
409
|
+
* @param {Buffer[]} queue - The metrics to send
|
|
410
|
+
* @param {string} address - The address to send the metrics to
|
|
411
|
+
* @param {number} family - The family of the address
|
|
412
|
+
* @param {boolean} recordTelemetry - Whether to record the transport outcome
|
|
413
|
+
* @param {() => void} [done] - Called after every packet completes
|
|
414
|
+
* @returns {void}
|
|
415
|
+
* @memberof DogStatsDClient
|
|
416
|
+
*/
|
|
417
|
+
_sendUdpFromQueue (queue, address, family, recordTelemetry, done) {
|
|
418
|
+
const socket = family === 6 ? this.#udp6 : this.#udp4
|
|
137
419
|
let pending = queue.length
|
|
138
420
|
const complete = () => {
|
|
139
421
|
if (--pending === 0) done?.()
|
|
@@ -141,8 +423,24 @@ class DogStatsDClient {
|
|
|
141
423
|
|
|
142
424
|
for (const buffer of queue) {
|
|
143
425
|
log.debug('Sending to DogStatsD: %s', buffer)
|
|
426
|
+
|
|
427
|
+
if (!this.telemetry && !done) {
|
|
428
|
+
socket.send(buffer, 0, buffer.length, this.#port, address)
|
|
429
|
+
continue
|
|
430
|
+
}
|
|
431
|
+
|
|
144
432
|
try {
|
|
145
|
-
socket.send(buffer, 0, buffer.length, this
|
|
433
|
+
socket.send(buffer, 0, buffer.length, this.#port, address, (error) => {
|
|
434
|
+
if (error) {
|
|
435
|
+
if (recordTelemetry && this.telemetry) {
|
|
436
|
+
this.#recordDropped(buffer.length)
|
|
437
|
+
}
|
|
438
|
+
log.error('DogStatsDClient: UDP error', error)
|
|
439
|
+
} else if (recordTelemetry && this.telemetry) {
|
|
440
|
+
this.#recordSent(buffer.length)
|
|
441
|
+
}
|
|
442
|
+
complete()
|
|
443
|
+
})
|
|
146
444
|
} catch (error) {
|
|
147
445
|
log.error('DogStatsDClient: UDP error sending metrics', error)
|
|
148
446
|
complete()
|
|
@@ -150,43 +448,81 @@ class DogStatsDClient {
|
|
|
150
448
|
}
|
|
151
449
|
}
|
|
152
450
|
|
|
153
|
-
|
|
451
|
+
/**
|
|
452
|
+
* Add a metric to the queue
|
|
453
|
+
*
|
|
454
|
+
* @param {string} stat - The metric name
|
|
455
|
+
* @param {number} value - The metric value
|
|
456
|
+
* @param {string} type - The metric type
|
|
457
|
+
* @param {string[]} tags - The metric tags
|
|
458
|
+
* @param {string} [tagsPrefix] - Serialized global tags
|
|
459
|
+
* @param {DogStatsDBufferState} [state] - Payload state receiving the metric
|
|
460
|
+
* @returns {void}
|
|
461
|
+
* @memberof DogStatsDClient
|
|
462
|
+
*/
|
|
463
|
+
_add (stat, value, type, tags, tagsPrefix = this.#tagsPrefix, state = this.#metrics) {
|
|
154
464
|
let message = `${stat}:${value}|${type}`
|
|
155
465
|
|
|
156
466
|
if (tags?.length) {
|
|
157
|
-
message +=
|
|
158
|
-
? `${
|
|
467
|
+
message += tagsPrefix
|
|
468
|
+
? `${tagsPrefix},${tags.join(',')}`
|
|
159
469
|
: `|#${tags.join(',')}`
|
|
160
470
|
} else {
|
|
161
|
-
message +=
|
|
471
|
+
message += tagsPrefix
|
|
162
472
|
}
|
|
163
473
|
|
|
164
474
|
if (entityId) {
|
|
165
475
|
message += `|c:${entityId}`
|
|
166
476
|
}
|
|
167
477
|
|
|
168
|
-
this._write(`${message}\n
|
|
478
|
+
this._write(`${message}\n`, state)
|
|
169
479
|
}
|
|
170
480
|
|
|
171
|
-
|
|
172
|
-
|
|
481
|
+
/**
|
|
482
|
+
* @param {string} stat - Telemetry metric name
|
|
483
|
+
* @param {number} value - Telemetry metric value
|
|
484
|
+
* @param {string} [typeLabel] - Submitted or aggregated metric type
|
|
485
|
+
* @returns {void}
|
|
486
|
+
*/
|
|
487
|
+
#addTelemetry (stat, value, typeLabel) {
|
|
488
|
+
const tags = typeLabel === undefined ? undefined : [`metrics_type:${typeLabel}`]
|
|
489
|
+
const tagsPrefix = this.#httpOptions ? this.#telemetryHttpTagsPrefix : this.#telemetryUdpTagsPrefix
|
|
173
490
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
491
|
+
this._add(stat, value, TYPE_COUNTER, tags, tagsPrefix, this.telemetry.payload)
|
|
492
|
+
}
|
|
177
493
|
|
|
178
|
-
|
|
179
|
-
|
|
494
|
+
/**
|
|
495
|
+
* Write a message to the queue
|
|
496
|
+
*
|
|
497
|
+
* @param {string} message - The message to write
|
|
498
|
+
* @param {DogStatsDBufferState} [state] - Payload state receiving the message
|
|
499
|
+
* @returns {void}
|
|
500
|
+
* @memberof DogStatsDClient
|
|
501
|
+
*/
|
|
502
|
+
_write (message, state = this.#metrics) {
|
|
503
|
+
const offset = Buffer.byteLength(message)
|
|
504
|
+
if (state.offset + offset > MAX_BUFFER_SIZE) {
|
|
505
|
+
this._enqueue(state)
|
|
506
|
+
}
|
|
507
|
+
state.offset += offset
|
|
508
|
+
state.message += message
|
|
180
509
|
}
|
|
181
510
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
511
|
+
/**
|
|
512
|
+
* Enqueue a message to the queue
|
|
513
|
+
*
|
|
514
|
+
* @param {DogStatsDBufferState} [state] - Payload state to enqueue
|
|
515
|
+
* @returns {Buffer[]} Queued payloads
|
|
516
|
+
* @memberof DogStatsDClient
|
|
517
|
+
*/
|
|
518
|
+
_enqueue (state = this.#metrics) {
|
|
519
|
+
if (state.offset > 0) {
|
|
520
|
+
state.queue.push(Buffer.from(state.message))
|
|
521
|
+
state.message = ''
|
|
522
|
+
state.offset = 0
|
|
187
523
|
}
|
|
188
524
|
|
|
189
|
-
return
|
|
525
|
+
return state.queue
|
|
190
526
|
}
|
|
191
527
|
|
|
192
528
|
_socket (type) {
|
|
@@ -231,19 +567,65 @@ class DogStatsDClient {
|
|
|
231
567
|
}
|
|
232
568
|
}
|
|
233
569
|
|
|
570
|
+
/**
|
|
571
|
+
* @param {DogStatsDClientOptions} options - DogStatsD transport options
|
|
572
|
+
* @returns {MetricsAggregationClient} Aggregating client with shared telemetry
|
|
573
|
+
*/
|
|
574
|
+
function createMetricsAggregationClient (options) {
|
|
575
|
+
const client = new DogStatsDClient(options, true)
|
|
576
|
+
|
|
577
|
+
return new MetricsAggregationClient(client)
|
|
578
|
+
}
|
|
579
|
+
|
|
234
580
|
class MetricsAggregationClient {
|
|
581
|
+
#metricsByType
|
|
582
|
+
|
|
583
|
+
/** @type {DogStatsDTelemetryState|undefined} */
|
|
584
|
+
#telemetry
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* @param {DogStatsDClient} client - DogStatsD transport client
|
|
588
|
+
*/
|
|
235
589
|
constructor (client) {
|
|
236
590
|
this._client = client
|
|
591
|
+
this.#telemetry = client.telemetry
|
|
592
|
+
this.#metricsByType = this.#telemetry?.metricsByType ?? [0, 0, 0, 0]
|
|
237
593
|
|
|
238
594
|
this.reset()
|
|
239
595
|
}
|
|
240
596
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
597
|
+
/**
|
|
598
|
+
* Recomputes the wrapped client's cached tags (e.g. after a MicroVM clone resume).
|
|
599
|
+
* @param {string[]} tags - DogStatsD-formatted tags (e.g. `['key:value']`)
|
|
600
|
+
*/
|
|
601
|
+
updateTags (tags) {
|
|
602
|
+
this._client.updateTags(tags)
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* @param {boolean|(() => void)} [forceTelemetry] - Whether to ignore the telemetry interval, or completion callback
|
|
607
|
+
* @param {() => void} [done] - Called after serverless deliveries complete
|
|
608
|
+
* @returns {void}
|
|
609
|
+
*/
|
|
610
|
+
flush (forceTelemetry = false, done) {
|
|
611
|
+
if (typeof forceTelemetry === 'function') {
|
|
612
|
+
done = forceTelemetry
|
|
613
|
+
forceTelemetry = false
|
|
614
|
+
}
|
|
245
615
|
|
|
246
|
-
this.
|
|
616
|
+
const counters = this._captureCounters()
|
|
617
|
+
const gauges = this._captureGauges()
|
|
618
|
+
const histograms = this._captureHistograms()
|
|
619
|
+
const telemetry = this.#telemetry
|
|
620
|
+
|
|
621
|
+
if (telemetry) {
|
|
622
|
+
telemetry.aggregatedContextsByType[TYPE_COUNTER_INDEX] += counters
|
|
623
|
+
telemetry.aggregatedContextsByType[TYPE_GAUGE_INDEX] += gauges
|
|
624
|
+
telemetry.aggregatedContextsByType[TYPE_HISTOGRAM_INDEX] += histograms
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
if (forceTelemetry) this._client.flush(true, done)
|
|
628
|
+
else this._client.flush(done)
|
|
247
629
|
}
|
|
248
630
|
|
|
249
631
|
reset () {
|
|
@@ -255,6 +637,7 @@ class MetricsAggregationClient {
|
|
|
255
637
|
// TODO: Aggregate with a histogram and send the buckets to the client.
|
|
256
638
|
distribution (name, value, tags) {
|
|
257
639
|
this._client.distribution(name, value, tags)
|
|
640
|
+
this.#metricsByType[TYPE_DISTRIBUTION_INDEX]++
|
|
258
641
|
}
|
|
259
642
|
|
|
260
643
|
boolean (name, value, tags) {
|
|
@@ -297,24 +680,45 @@ class MetricsAggregationClient {
|
|
|
297
680
|
this.count(name, -count, tags)
|
|
298
681
|
}
|
|
299
682
|
|
|
683
|
+
/**
|
|
684
|
+
* @returns {number} Number of gauge contexts flushed
|
|
685
|
+
*/
|
|
300
686
|
_captureGauges () {
|
|
301
|
-
|
|
687
|
+
let metrics = 0
|
|
688
|
+
const contexts = this._captureTree(this._gauges, (node, name, tags) => {
|
|
689
|
+
metrics += node.submissions
|
|
302
690
|
this._client.gauge(name, node.value, tags)
|
|
303
691
|
})
|
|
304
692
|
|
|
693
|
+
this.#metricsByType[TYPE_GAUGE_INDEX] += metrics
|
|
305
694
|
this._gauges.clear()
|
|
695
|
+
|
|
696
|
+
return contexts
|
|
306
697
|
}
|
|
307
698
|
|
|
699
|
+
/**
|
|
700
|
+
* @returns {number} Number of counter contexts flushed
|
|
701
|
+
*/
|
|
308
702
|
_captureCounters () {
|
|
309
|
-
|
|
703
|
+
let metrics = 0
|
|
704
|
+
const contexts = this._captureTree(this._counters, (node, name, tags) => {
|
|
705
|
+
metrics += node.submissions
|
|
310
706
|
this._client.increment(name, node.value, tags)
|
|
311
707
|
})
|
|
312
708
|
|
|
709
|
+
this.#metricsByType[TYPE_COUNTER_INDEX] += metrics
|
|
313
710
|
this._counters.clear()
|
|
711
|
+
|
|
712
|
+
return contexts
|
|
314
713
|
}
|
|
315
714
|
|
|
715
|
+
/**
|
|
716
|
+
* @returns {number} Number of histogram contexts flushed
|
|
717
|
+
*/
|
|
316
718
|
_captureHistograms () {
|
|
317
|
-
|
|
719
|
+
let metrics = 0
|
|
720
|
+
const contexts = this._captureTree(this._histograms, (node, name, tags) => {
|
|
721
|
+
metrics += node.submissions
|
|
318
722
|
const stats = node.value
|
|
319
723
|
|
|
320
724
|
this._client.gauge(`${name}.min`, stats.min, tags)
|
|
@@ -327,25 +731,49 @@ class MetricsAggregationClient {
|
|
|
327
731
|
this._client.gauge(`${name}.95percentile`, stats.p95, tags)
|
|
328
732
|
})
|
|
329
733
|
|
|
734
|
+
this.#metricsByType[TYPE_HISTOGRAM_INDEX] += metrics
|
|
330
735
|
this._histograms.clear()
|
|
736
|
+
|
|
737
|
+
return contexts
|
|
331
738
|
}
|
|
332
739
|
|
|
740
|
+
/**
|
|
741
|
+
* @param {Map<string, MetricNode>} tree - Metric context tree
|
|
742
|
+
* @param {CaptureMetric} fn - Called for every context with submissions
|
|
743
|
+
* @returns {number} Number of contexts with submissions
|
|
744
|
+
*/
|
|
333
745
|
_captureTree (tree, fn) {
|
|
746
|
+
let contexts = 0
|
|
747
|
+
|
|
334
748
|
for (const [name, root] of tree) {
|
|
335
|
-
this._captureNode(root, name, [], fn)
|
|
749
|
+
contexts += this._captureNode(root, name, [], fn)
|
|
336
750
|
}
|
|
751
|
+
|
|
752
|
+
return contexts
|
|
337
753
|
}
|
|
338
754
|
|
|
755
|
+
/**
|
|
756
|
+
* @param {MetricNode} node - Current metric context node
|
|
757
|
+
* @param {string} name - Metric name
|
|
758
|
+
* @param {string[]} tags - Current metric tags
|
|
759
|
+
* @param {CaptureMetric} fn - Called for every context with submissions
|
|
760
|
+
* @returns {number} Number of contexts with submissions
|
|
761
|
+
*/
|
|
339
762
|
_captureNode (node, name, tags, fn) {
|
|
340
|
-
|
|
763
|
+
let contexts = 0
|
|
764
|
+
|
|
765
|
+
if (node.submissions !== 0) {
|
|
341
766
|
fn(node, name, tags)
|
|
767
|
+
contexts++
|
|
342
768
|
}
|
|
343
769
|
|
|
344
770
|
for (const [tag, next] of node.nodes) {
|
|
345
771
|
tags.push(tag)
|
|
346
|
-
this._captureNode(next, name, tags, fn)
|
|
772
|
+
contexts += this._captureNode(next, name, tags, fn)
|
|
347
773
|
tags.pop()
|
|
348
774
|
}
|
|
775
|
+
|
|
776
|
+
return contexts
|
|
349
777
|
}
|
|
350
778
|
|
|
351
779
|
_ensureTree (tree, name, tags = [], value) {
|
|
@@ -359,7 +787,7 @@ class MetricsAggregationClient {
|
|
|
359
787
|
node = this._ensureNode(node.nodes, tag, value)
|
|
360
788
|
}
|
|
361
789
|
|
|
362
|
-
node.
|
|
790
|
+
node.submissions++
|
|
363
791
|
|
|
364
792
|
return node
|
|
365
793
|
}
|
|
@@ -368,7 +796,7 @@ class MetricsAggregationClient {
|
|
|
368
796
|
let node = container.get(key)
|
|
369
797
|
|
|
370
798
|
if (!node) {
|
|
371
|
-
node = { nodes: new Map(),
|
|
799
|
+
node = { nodes: new Map(), submissions: 0, value }
|
|
372
800
|
|
|
373
801
|
if (typeof key === 'string') {
|
|
374
802
|
container.set(key, node)
|
|
@@ -388,14 +816,19 @@ class CustomMetrics {
|
|
|
388
816
|
#client
|
|
389
817
|
constructor (config) {
|
|
390
818
|
const clientConfig = DogStatsDClient.generateClientConfig(config)
|
|
391
|
-
this.#client =
|
|
819
|
+
this.#client = createMetricsAggregationClient(clientConfig)
|
|
820
|
+
|
|
821
|
+
// CustomMetrics has process-lifetime flush handlers and no stop hook, so this shares that lifetime.
|
|
822
|
+
identityRefreshChannel.subscribe(() => {
|
|
823
|
+
this.#client.updateTags(DogStatsDClient.generateClientConfig(config).tags)
|
|
824
|
+
})
|
|
392
825
|
|
|
393
826
|
const flush = this.flush.bind(this)
|
|
394
827
|
|
|
395
828
|
// TODO(bengl) this magic number should be configurable
|
|
396
829
|
setInterval(flush, 10 * 1000).unref?.()
|
|
397
830
|
|
|
398
|
-
globalThis[Symbol.for('dd-trace')].beforeExitHandlers.add(flush)
|
|
831
|
+
globalThis[Symbol.for('dd-trace')].beforeExitHandlers.add(() => this.#client.flush(true))
|
|
399
832
|
registerTelemetryFlusher(done => this.flush(done))
|
|
400
833
|
}
|
|
401
834
|
|
|
@@ -420,7 +853,7 @@ class CustomMetrics {
|
|
|
420
853
|
}
|
|
421
854
|
|
|
422
855
|
flush (done) {
|
|
423
|
-
return this.#client.flush(done)
|
|
856
|
+
return this.#client.flush(false, done)
|
|
424
857
|
}
|
|
425
858
|
|
|
426
859
|
/**
|
|
@@ -446,4 +879,5 @@ module.exports = {
|
|
|
446
879
|
DogStatsDClient,
|
|
447
880
|
CustomMetrics,
|
|
448
881
|
MetricsAggregationClient,
|
|
882
|
+
createMetricsAggregationClient,
|
|
449
883
|
}
|