dd-trace 6.12.0 → 6.14.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/cypress.js +9 -11
- 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/test-optimization-validation/scenarios/ci-wiring.js +5 -4
- 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/index.d.ts +127 -26
- package/package.json +22 -19
- package/packages/datadog-esbuild/index.js +3 -1
- package/packages/datadog-instrumentations/src/bunyan.js +4 -2
- 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/bunyan.js +12 -3
- 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 +266 -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 +279 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +12 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +562 -27
- package/packages/datadog-instrumentations/src/jest.js +165 -38
- package/packages/datadog-instrumentations/src/knex.js +76 -0
- package/packages/datadog-instrumentations/src/mariadb-bundle.js +1597 -0
- package/packages/datadog-instrumentations/src/mariadb.js +13 -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/pino.js +15 -4
- package/packages/datadog-instrumentations/src/playwright.js +137 -27
- 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 +44 -7
- package/packages/datadog-instrumentations/src/vitest-main.js +92 -9
- package/packages/datadog-instrumentations/src/vitest-worker.js +82 -14
- package/packages/datadog-instrumentations/src/webdriverio.js +986 -1
- package/packages/datadog-instrumentations/src/winston.js +6 -3
- 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-cypress/src/support.js +1 -0
- 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-http/src/client.js +2 -15
- package/packages/datadog-plugin-http2/src/client.js +2 -15
- 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-openai-agents/src/integration.js +15 -11
- package/packages/datadog-plugin-playwright/src/index.js +176 -28
- package/packages/datadog-plugin-undici/src/index.js +29 -56
- package/packages/datadog-plugin-vitest/src/index.js +6 -12
- package/packages/dd-trace/src/aiguard/evaluation.js +96 -21
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -3
- package/packages/dd-trace/src/aiguard/messages/anthropic.js +29 -10
- 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/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/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/iast/vulnerabilities-formatter/utils.js +10 -9
- package/packages/dd-trace/src/appsec/lambda.js +44 -26
- package/packages/dd-trace/src/appsec/rule_manager.js +2 -0
- 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 +7 -150
- 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 +397 -234
- 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 +21 -0
- 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 +10 -0
- package/packages/dd-trace/src/config/index.js +75 -14
- 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 +47 -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 -39
- 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 +532 -71
- 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 +24 -3
- package/packages/dd-trace/src/exporters/agent/writer.js +28 -11
- 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 +31 -7
- 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 +105 -0
- 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/client.js +4 -0
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +52 -2
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +420 -105
- package/packages/dd-trace/src/llmobs/experiments/index.js +54 -42
- package/packages/dd-trace/src/llmobs/experiments/noop.js +39 -8
- package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
- package/packages/dd-trace/src/llmobs/experiments/util.js +21 -0
- package/packages/dd-trace/src/llmobs/index.js +111 -48
- package/packages/dd-trace/src/llmobs/noop.js +2 -1
- 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/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/langchain/index.js +7 -0
- 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/util.js +0 -33
- package/packages/dd-trace/src/llmobs/writers/base.js +106 -53
- package/packages/dd-trace/src/log/index.js +7 -1
- package/packages/dd-trace/src/log-submission/log-submission-plugin.js +249 -0
- package/packages/dd-trace/src/noop/span.js +5 -0
- package/packages/dd-trace/src/openfeature/writers/base.js +14 -12
- package/packages/dd-trace/src/openfeature/writers/exposures.js +8 -1
- package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
- package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +45 -3
- package/packages/dd-trace/src/opentelemetry/logs/index.js +9 -25
- package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +7 -4
- package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +14 -4
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +18 -23
- package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +8 -0
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +18 -6
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +14 -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/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/opentelemetry/tracer.js +26 -2
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +494 -351
- 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/payload-tagging/index.js +2 -1
- package/packages/dd-trace/src/plugin_manager.js +2 -0
- 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/status-validator.js +98 -0
- package/packages/dd-trace/src/plugins/util/test.js +157 -41
- package/packages/dd-trace/src/plugins/util/url.js +2 -2
- package/packages/dd-trace/src/plugins/util/web.js +6 -74
- package/packages/dd-trace/src/profiling/config.js +1 -0
- package/packages/dd-trace/src/profiling/oom.js +11 -5
- package/packages/dd-trace/src/profiling/profiler.js +30 -13
- package/packages/dd-trace/src/profiling/profilers/events.js +3 -3
- package/packages/dd-trace/src/profiling/profilers/space.js +38 -11
- package/packages/dd-trace/src/profiling/profilers/wall.js +14 -14
- package/packages/dd-trace/src/profiling/webspan-utils.js +7 -4
- package/packages/dd-trace/src/proxy.js +108 -10
- 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/index.js +5 -0
- package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +10 -1
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +22 -7
- package/packages/dd-trace/src/sampling_rule.js +36 -9
- package/packages/dd-trace/src/serverless/telemetry-delivery-tracker.js +51 -0
- package/packages/dd-trace/src/serverless/vercel.js +155 -0
- package/packages/dd-trace/src/serverless.js +45 -21
- package/packages/dd-trace/src/service-naming/schemas/util.js +1 -1
- package/packages/dd-trace/src/span_stats.js +64 -31
- 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 +26 -5
- package/packages/dd-trace/src/tracer_metadata.js +11 -3
- 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/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/ci-visibility/log-submission/log-submission-plugin.js +0 -53
- package/packages/dd-trace/src/encode/agentless-json.js +0 -209
|
@@ -2,49 +2,186 @@
|
|
|
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')
|
|
9
13
|
const Histogram = require('./histogram')
|
|
10
14
|
const { entityId } = require('./exporters/common/docker')
|
|
15
|
+
const { registerTelemetryFlusher } = require('./flush')
|
|
16
|
+
const { createServerlessDeliveryTracker } = require('./serverless')
|
|
11
17
|
|
|
12
18
|
const legacyStorage = storage('legacy')
|
|
13
19
|
|
|
14
20
|
const MAX_BUFFER_SIZE = 1024 // limit from the agent
|
|
21
|
+
const TELEMETRY_INTERVAL = 10_000
|
|
15
22
|
|
|
16
23
|
const TYPE_COUNTER = 'c'
|
|
17
24
|
const TYPE_GAUGE = 'g'
|
|
18
25
|
const TYPE_DISTRIBUTION = 'd'
|
|
19
26
|
const TYPE_HISTOGRAM = 'h'
|
|
20
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
|
+
|
|
21
83
|
/**
|
|
22
84
|
* @import { DogStatsD } from "../../../index.d.ts"
|
|
23
85
|
* @implements {DogStatsD}
|
|
24
86
|
*/
|
|
25
87
|
class DogStatsDClient {
|
|
88
|
+
#family
|
|
89
|
+
#host
|
|
90
|
+
#httpOptions
|
|
26
91
|
#lookup
|
|
92
|
+
#metrics = { message: '', offset: 0, queue: [] }
|
|
93
|
+
#port
|
|
94
|
+
#serverlessDeliveryTracker
|
|
27
95
|
#tagsPrefix
|
|
28
|
-
|
|
96
|
+
#telemetryHttpTagsPrefix
|
|
97
|
+
#telemetryUdpTagsPrefix
|
|
98
|
+
#udp4
|
|
99
|
+
#udp6
|
|
100
|
+
|
|
101
|
+
/** @type {DogStatsDTelemetryState|undefined} */
|
|
102
|
+
telemetry
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @param {DogStatsDClientOptions} options - DogStatsD transport options
|
|
106
|
+
* @param {boolean} [telemetryEnabled] - Whether to collect client telemetry
|
|
107
|
+
*/
|
|
108
|
+
constructor (options, telemetryEnabled = false) {
|
|
29
109
|
this.#lookup = options.lookup
|
|
30
110
|
if (options.metricsProxyUrl) {
|
|
31
|
-
this
|
|
111
|
+
this.#httpOptions = {
|
|
32
112
|
method: 'POST',
|
|
113
|
+
retry: false,
|
|
33
114
|
url: options.metricsProxyUrl.toString(),
|
|
34
115
|
path: '/dogstatsd/v2/proxy',
|
|
35
116
|
}
|
|
36
117
|
}
|
|
37
118
|
|
|
38
|
-
this
|
|
39
|
-
this
|
|
40
|
-
this
|
|
41
|
-
this
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
119
|
+
this.#family = isIP(options.host)
|
|
120
|
+
this.#host = options.host
|
|
121
|
+
this.#port = options.port
|
|
122
|
+
this.#serverlessDeliveryTracker = createServerlessDeliveryTracker()
|
|
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`
|
|
48
185
|
}
|
|
49
186
|
|
|
50
187
|
increment (stat, value, tags) {
|
|
@@ -67,100 +204,325 @@ class DogStatsDClient {
|
|
|
67
204
|
this._add(stat, value, TYPE_HISTOGRAM, tags)
|
|
68
205
|
}
|
|
69
206
|
|
|
70
|
-
|
|
71
|
-
|
|
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
|
+
}
|
|
217
|
+
|
|
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
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
this.#flush(this.#metrics, true, track)
|
|
232
|
+
if (this.telemetry) this.#flushTelemetry(forceTelemetry, track)
|
|
233
|
+
|
|
234
|
+
if (this.#serverlessDeliveryTracker) return this.#serverlessDeliveryTracker.waitForIdle(done)
|
|
235
|
+
complete?.()
|
|
236
|
+
}
|
|
237
|
+
|
|
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
|
+
)
|
|
272
|
+
}
|
|
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)
|
|
72
298
|
|
|
73
299
|
if (queue.length === 0) return
|
|
74
300
|
|
|
75
|
-
log.debug('Flushing %s metrics via %s', queue.length, this
|
|
301
|
+
log.debug('Flushing %s metrics via %s', queue.length, this.#httpOptions ? 'HTTP' : 'UDP')
|
|
76
302
|
|
|
77
|
-
|
|
303
|
+
state.queue = []
|
|
78
304
|
|
|
79
|
-
|
|
80
|
-
this._sendHttp(queue)
|
|
81
|
-
|
|
82
|
-
this._sendUdp(queue)
|
|
305
|
+
const send = complete => {
|
|
306
|
+
if (this.#httpOptions) this._sendHttp(queue, recordTelemetry, complete)
|
|
307
|
+
else this._sendUdp(queue, recordTelemetry, complete)
|
|
83
308
|
}
|
|
309
|
+
|
|
310
|
+
if (this.#serverlessDeliveryTracker) return this.#serverlessDeliveryTracker.track(send)
|
|
311
|
+
if (track) return track(send)
|
|
312
|
+
send()
|
|
84
313
|
}
|
|
85
314
|
|
|
86
|
-
|
|
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
|
|
336
|
+
}
|
|
337
|
+
|
|
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) {
|
|
87
348
|
const buffer = Buffer.concat(queue)
|
|
88
|
-
request(buffer, this
|
|
89
|
-
if (
|
|
90
|
-
|
|
91
|
-
|
|
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) {
|
|
92
358
|
// Inside this if-block, we have connectivity to the agent, but
|
|
93
359
|
// we're not getting a 200 from the proxy endpoint. If it's a 404,
|
|
94
360
|
// then we know we'll never have the endpoint, so just clear out the
|
|
95
361
|
// options. Either way, we can give UDP a try.
|
|
96
|
-
this
|
|
362
|
+
this.#httpOptions = undefined
|
|
97
363
|
}
|
|
98
|
-
this._sendUdp(queue)
|
|
364
|
+
this._sendUdp(queue, recordTelemetry, done)
|
|
365
|
+
} else {
|
|
366
|
+
if (recordTelemetry && this.telemetry) this.#recordSent(buffer.length)
|
|
367
|
+
done?.()
|
|
99
368
|
}
|
|
100
369
|
})
|
|
101
370
|
}
|
|
102
371
|
|
|
103
|
-
|
|
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) {
|
|
104
382
|
// dgram resolves the local address via the instrumented dns.lookup when it
|
|
105
383
|
// binds on first send; the noop store keeps that self-traffic off the trace.
|
|
106
384
|
legacyStorage.run({ noop: true }, () => {
|
|
107
|
-
if (this
|
|
108
|
-
this.#lookup(this
|
|
109
|
-
if (error)
|
|
110
|
-
|
|
385
|
+
if (this.#family === 0) {
|
|
386
|
+
this.#lookup(this.#host, (error, address, family) => {
|
|
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
|
+
}
|
|
395
|
+
log.error('DogStatsDClient: Host not found', error)
|
|
396
|
+
return done?.()
|
|
397
|
+
}
|
|
398
|
+
this._sendUdpFromQueue(queue, address, family, recordTelemetry, done)
|
|
111
399
|
})
|
|
112
400
|
} else {
|
|
113
|
-
this._sendUdpFromQueue(queue, this
|
|
401
|
+
this._sendUdpFromQueue(queue, this.#host, this.#family, recordTelemetry, done)
|
|
114
402
|
}
|
|
115
403
|
})
|
|
116
404
|
}
|
|
117
405
|
|
|
118
|
-
|
|
119
|
-
|
|
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
|
|
419
|
+
let pending = queue.length
|
|
420
|
+
const complete = () => {
|
|
421
|
+
if (--pending === 0) done?.()
|
|
422
|
+
}
|
|
120
423
|
|
|
121
424
|
for (const buffer of queue) {
|
|
122
425
|
log.debug('Sending to DogStatsD: %s', buffer)
|
|
123
|
-
|
|
426
|
+
|
|
427
|
+
if (!this.telemetry && !done) {
|
|
428
|
+
socket.send(buffer, 0, buffer.length, this.#port, address)
|
|
429
|
+
continue
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
try {
|
|
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
|
+
})
|
|
444
|
+
} catch (error) {
|
|
445
|
+
log.error('DogStatsDClient: UDP error sending metrics', error)
|
|
446
|
+
complete()
|
|
447
|
+
}
|
|
124
448
|
}
|
|
125
449
|
}
|
|
126
450
|
|
|
127
|
-
|
|
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) {
|
|
128
464
|
let message = `${stat}:${value}|${type}`
|
|
129
465
|
|
|
130
466
|
if (tags?.length) {
|
|
131
|
-
message +=
|
|
132
|
-
? `${
|
|
467
|
+
message += tagsPrefix
|
|
468
|
+
? `${tagsPrefix},${tags.join(',')}`
|
|
133
469
|
: `|#${tags.join(',')}`
|
|
134
470
|
} else {
|
|
135
|
-
message +=
|
|
471
|
+
message += tagsPrefix
|
|
136
472
|
}
|
|
137
473
|
|
|
138
474
|
if (entityId) {
|
|
139
475
|
message += `|c:${entityId}`
|
|
140
476
|
}
|
|
141
477
|
|
|
142
|
-
this._write(`${message}\n
|
|
478
|
+
this._write(`${message}\n`, state)
|
|
143
479
|
}
|
|
144
480
|
|
|
145
|
-
|
|
146
|
-
|
|
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
|
|
147
490
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
491
|
+
this._add(stat, value, TYPE_COUNTER, tags, tagsPrefix, this.telemetry.payload)
|
|
492
|
+
}
|
|
151
493
|
|
|
152
|
-
|
|
153
|
-
|
|
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
|
|
154
509
|
}
|
|
155
510
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
|
161
523
|
}
|
|
162
524
|
|
|
163
|
-
return
|
|
525
|
+
return state.queue
|
|
164
526
|
}
|
|
165
527
|
|
|
166
528
|
_socket (type) {
|
|
@@ -205,19 +567,65 @@ class DogStatsDClient {
|
|
|
205
567
|
}
|
|
206
568
|
}
|
|
207
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
|
+
|
|
208
580
|
class MetricsAggregationClient {
|
|
581
|
+
#metricsByType
|
|
582
|
+
|
|
583
|
+
/** @type {DogStatsDTelemetryState|undefined} */
|
|
584
|
+
#telemetry
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* @param {DogStatsDClient} client - DogStatsD transport client
|
|
588
|
+
*/
|
|
209
589
|
constructor (client) {
|
|
210
590
|
this._client = client
|
|
591
|
+
this.#telemetry = client.telemetry
|
|
592
|
+
this.#metricsByType = this.#telemetry?.metricsByType ?? [0, 0, 0, 0]
|
|
211
593
|
|
|
212
594
|
this.reset()
|
|
213
595
|
}
|
|
214
596
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
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
|
+
}
|
|
615
|
+
|
|
616
|
+
const counters = this._captureCounters()
|
|
617
|
+
const gauges = this._captureGauges()
|
|
618
|
+
const histograms = this._captureHistograms()
|
|
619
|
+
const telemetry = this.#telemetry
|
|
219
620
|
|
|
220
|
-
|
|
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)
|
|
221
629
|
}
|
|
222
630
|
|
|
223
631
|
reset () {
|
|
@@ -229,6 +637,7 @@ class MetricsAggregationClient {
|
|
|
229
637
|
// TODO: Aggregate with a histogram and send the buckets to the client.
|
|
230
638
|
distribution (name, value, tags) {
|
|
231
639
|
this._client.distribution(name, value, tags)
|
|
640
|
+
this.#metricsByType[TYPE_DISTRIBUTION_INDEX]++
|
|
232
641
|
}
|
|
233
642
|
|
|
234
643
|
boolean (name, value, tags) {
|
|
@@ -271,24 +680,45 @@ class MetricsAggregationClient {
|
|
|
271
680
|
this.count(name, -count, tags)
|
|
272
681
|
}
|
|
273
682
|
|
|
683
|
+
/**
|
|
684
|
+
* @returns {number} Number of gauge contexts flushed
|
|
685
|
+
*/
|
|
274
686
|
_captureGauges () {
|
|
275
|
-
|
|
687
|
+
let metrics = 0
|
|
688
|
+
const contexts = this._captureTree(this._gauges, (node, name, tags) => {
|
|
689
|
+
metrics += node.submissions
|
|
276
690
|
this._client.gauge(name, node.value, tags)
|
|
277
691
|
})
|
|
278
692
|
|
|
693
|
+
this.#metricsByType[TYPE_GAUGE_INDEX] += metrics
|
|
279
694
|
this._gauges.clear()
|
|
695
|
+
|
|
696
|
+
return contexts
|
|
280
697
|
}
|
|
281
698
|
|
|
699
|
+
/**
|
|
700
|
+
* @returns {number} Number of counter contexts flushed
|
|
701
|
+
*/
|
|
282
702
|
_captureCounters () {
|
|
283
|
-
|
|
703
|
+
let metrics = 0
|
|
704
|
+
const contexts = this._captureTree(this._counters, (node, name, tags) => {
|
|
705
|
+
metrics += node.submissions
|
|
284
706
|
this._client.increment(name, node.value, tags)
|
|
285
707
|
})
|
|
286
708
|
|
|
709
|
+
this.#metricsByType[TYPE_COUNTER_INDEX] += metrics
|
|
287
710
|
this._counters.clear()
|
|
711
|
+
|
|
712
|
+
return contexts
|
|
288
713
|
}
|
|
289
714
|
|
|
715
|
+
/**
|
|
716
|
+
* @returns {number} Number of histogram contexts flushed
|
|
717
|
+
*/
|
|
290
718
|
_captureHistograms () {
|
|
291
|
-
|
|
719
|
+
let metrics = 0
|
|
720
|
+
const contexts = this._captureTree(this._histograms, (node, name, tags) => {
|
|
721
|
+
metrics += node.submissions
|
|
292
722
|
const stats = node.value
|
|
293
723
|
|
|
294
724
|
this._client.gauge(`${name}.min`, stats.min, tags)
|
|
@@ -301,25 +731,49 @@ class MetricsAggregationClient {
|
|
|
301
731
|
this._client.gauge(`${name}.95percentile`, stats.p95, tags)
|
|
302
732
|
})
|
|
303
733
|
|
|
734
|
+
this.#metricsByType[TYPE_HISTOGRAM_INDEX] += metrics
|
|
304
735
|
this._histograms.clear()
|
|
736
|
+
|
|
737
|
+
return contexts
|
|
305
738
|
}
|
|
306
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
|
+
*/
|
|
307
745
|
_captureTree (tree, fn) {
|
|
746
|
+
let contexts = 0
|
|
747
|
+
|
|
308
748
|
for (const [name, root] of tree) {
|
|
309
|
-
this._captureNode(root, name, [], fn)
|
|
749
|
+
contexts += this._captureNode(root, name, [], fn)
|
|
310
750
|
}
|
|
751
|
+
|
|
752
|
+
return contexts
|
|
311
753
|
}
|
|
312
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
|
+
*/
|
|
313
762
|
_captureNode (node, name, tags, fn) {
|
|
314
|
-
|
|
763
|
+
let contexts = 0
|
|
764
|
+
|
|
765
|
+
if (node.submissions !== 0) {
|
|
315
766
|
fn(node, name, tags)
|
|
767
|
+
contexts++
|
|
316
768
|
}
|
|
317
769
|
|
|
318
770
|
for (const [tag, next] of node.nodes) {
|
|
319
771
|
tags.push(tag)
|
|
320
|
-
this._captureNode(next, name, tags, fn)
|
|
772
|
+
contexts += this._captureNode(next, name, tags, fn)
|
|
321
773
|
tags.pop()
|
|
322
774
|
}
|
|
775
|
+
|
|
776
|
+
return contexts
|
|
323
777
|
}
|
|
324
778
|
|
|
325
779
|
_ensureTree (tree, name, tags = [], value) {
|
|
@@ -333,7 +787,7 @@ class MetricsAggregationClient {
|
|
|
333
787
|
node = this._ensureNode(node.nodes, tag, value)
|
|
334
788
|
}
|
|
335
789
|
|
|
336
|
-
node.
|
|
790
|
+
node.submissions++
|
|
337
791
|
|
|
338
792
|
return node
|
|
339
793
|
}
|
|
@@ -342,7 +796,7 @@ class MetricsAggregationClient {
|
|
|
342
796
|
let node = container.get(key)
|
|
343
797
|
|
|
344
798
|
if (!node) {
|
|
345
|
-
node = { nodes: new Map(),
|
|
799
|
+
node = { nodes: new Map(), submissions: 0, value }
|
|
346
800
|
|
|
347
801
|
if (typeof key === 'string') {
|
|
348
802
|
container.set(key, node)
|
|
@@ -362,14 +816,20 @@ class CustomMetrics {
|
|
|
362
816
|
#client
|
|
363
817
|
constructor (config) {
|
|
364
818
|
const clientConfig = DogStatsDClient.generateClientConfig(config)
|
|
365
|
-
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
|
+
})
|
|
366
825
|
|
|
367
826
|
const flush = this.flush.bind(this)
|
|
368
827
|
|
|
369
828
|
// TODO(bengl) this magic number should be configurable
|
|
370
829
|
setInterval(flush, 10 * 1000).unref?.()
|
|
371
830
|
|
|
372
|
-
globalThis[Symbol.for('dd-trace')].beforeExitHandlers.add(flush)
|
|
831
|
+
globalThis[Symbol.for('dd-trace')].beforeExitHandlers.add(() => this.#client.flush(true))
|
|
832
|
+
registerTelemetryFlusher(done => this.flush(done))
|
|
373
833
|
}
|
|
374
834
|
|
|
375
835
|
increment (stat, value = 1, tags) {
|
|
@@ -392,8 +852,8 @@ class CustomMetrics {
|
|
|
392
852
|
this.#client.histogram(stat, value, CustomMetrics.tagTranslator(tags))
|
|
393
853
|
}
|
|
394
854
|
|
|
395
|
-
flush () {
|
|
396
|
-
return this.#client.flush()
|
|
855
|
+
flush (done) {
|
|
856
|
+
return this.#client.flush(false, done)
|
|
397
857
|
}
|
|
398
858
|
|
|
399
859
|
/**
|
|
@@ -419,4 +879,5 @@ module.exports = {
|
|
|
419
879
|
DogStatsDClient,
|
|
420
880
|
CustomMetrics,
|
|
421
881
|
MetricsAggregationClient,
|
|
882
|
+
createMetricsAggregationClient,
|
|
422
883
|
}
|