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
|
@@ -4,6 +4,7 @@ const { URL, format } = require('node:url')
|
|
|
4
4
|
const path = require('node:path')
|
|
5
5
|
const request = require('../../exporters/common/request')
|
|
6
6
|
const { getEnvironmentVariable } = require('../../config/helper')
|
|
7
|
+
const { createServerlessDeliveryTracker } = require('../../serverless')
|
|
7
8
|
|
|
8
9
|
const logger = require('../../log')
|
|
9
10
|
|
|
@@ -16,13 +17,20 @@ const {
|
|
|
16
17
|
} = require('../constants/writers')
|
|
17
18
|
const { parseResponseAndLog } = require('./util')
|
|
18
19
|
|
|
20
|
+
const MAX_BUFFER_EVENTS = 1000
|
|
21
|
+
|
|
19
22
|
class LLMObsBuffer {
|
|
20
|
-
|
|
23
|
+
/**
|
|
24
|
+
* @param {{
|
|
25
|
+
* events: Record<string, unknown>[],
|
|
26
|
+
* size: number,
|
|
27
|
+
* routing?: { apiKey?: string, site?: string }
|
|
28
|
+
* }} options
|
|
29
|
+
*/
|
|
30
|
+
constructor ({ events, size, routing = {} }) {
|
|
21
31
|
this.events = events
|
|
22
32
|
this.size = size
|
|
23
33
|
this.routing = routing
|
|
24
|
-
this.isDefault = isDefault
|
|
25
|
-
this.limit = limit
|
|
26
34
|
}
|
|
27
35
|
|
|
28
36
|
clear () {
|
|
@@ -33,6 +41,9 @@ class LLMObsBuffer {
|
|
|
33
41
|
|
|
34
42
|
class BaseLLMObsWriter {
|
|
35
43
|
#destroyer
|
|
44
|
+
/** @type {Function[]} */
|
|
45
|
+
#pendingFlushes = []
|
|
46
|
+
#serverlessDeliveryTracker = createServerlessDeliveryTracker()
|
|
36
47
|
/** @type {Map<string, LLMObsBuffer>} */
|
|
37
48
|
#multiTenantBuffers = new Map()
|
|
38
49
|
|
|
@@ -42,7 +53,7 @@ class BaseLLMObsWriter {
|
|
|
42
53
|
this._eventType = eventType
|
|
43
54
|
|
|
44
55
|
/** @type {LLMObsBuffer} */
|
|
45
|
-
this._buffer = new LLMObsBuffer({ events: [], size: 0
|
|
56
|
+
this._buffer = new LLMObsBuffer({ events: [], size: 0 })
|
|
46
57
|
|
|
47
58
|
/** @type {import('../../config/config-base')} */
|
|
48
59
|
this._config = config
|
|
@@ -98,8 +109,8 @@ class BaseLLMObsWriter {
|
|
|
98
109
|
append (event, routing, byteLength) {
|
|
99
110
|
const buffer = this._getBuffer(routing)
|
|
100
111
|
|
|
101
|
-
if (buffer.events.length >=
|
|
102
|
-
logger.warn(`${this.constructor.name} event buffer full (limit is ${
|
|
112
|
+
if (buffer.events.length >= MAX_BUFFER_EVENTS) {
|
|
113
|
+
logger.warn(`${this.constructor.name} event buffer full (limit is ${MAX_BUFFER_EVENTS}), dropping event`)
|
|
103
114
|
telemetry.recordDroppedPayload(1, this._eventType, 'buffer_full')
|
|
104
115
|
return false
|
|
105
116
|
}
|
|
@@ -111,80 +122,118 @@ class BaseLLMObsWriter {
|
|
|
111
122
|
return true
|
|
112
123
|
}
|
|
113
124
|
|
|
114
|
-
|
|
125
|
+
/**
|
|
126
|
+
* Drains buffered events and joins requests active at this flush boundary.
|
|
127
|
+
* @param {Function} [done]
|
|
128
|
+
*/
|
|
129
|
+
flush (done) {
|
|
115
130
|
if (this._agentless == null) {
|
|
131
|
+
if (done) this.#pendingFlushes.push(done)
|
|
116
132
|
return
|
|
117
133
|
}
|
|
118
134
|
|
|
119
|
-
|
|
120
|
-
if (this
|
|
121
|
-
|
|
122
|
-
this
|
|
123
|
-
|
|
124
|
-
|
|
135
|
+
const requests = this.#drainBuffers()
|
|
136
|
+
if (!this.#serverlessDeliveryTracker) {
|
|
137
|
+
// Only invocation-retaining platforms need completion-aware delivery.
|
|
138
|
+
for (const request of requests) this.#sendSafely(request)
|
|
139
|
+
done?.()
|
|
140
|
+
return
|
|
141
|
+
}
|
|
125
142
|
|
|
126
|
-
|
|
143
|
+
for (const request of requests) this.#sendSafely(request)
|
|
144
|
+
this.#serverlessDeliveryTracker.waitForIdle(done)
|
|
145
|
+
}
|
|
127
146
|
|
|
128
|
-
|
|
147
|
+
#sendSafely (requestToSend) {
|
|
148
|
+
try {
|
|
149
|
+
if (this.#serverlessDeliveryTracker) {
|
|
150
|
+
this.#serverlessDeliveryTracker.track(done => this.#send(requestToSend, done))
|
|
151
|
+
} else {
|
|
152
|
+
this.#send(requestToSend)
|
|
153
|
+
}
|
|
154
|
+
} catch (error) {
|
|
155
|
+
logger.error('Failed to send LLMObs %s events: %s', this._eventType, error.message)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
129
158
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
159
|
+
#drainBuffers () {
|
|
160
|
+
const requests = []
|
|
161
|
+
if (this._buffer.events.length > 0) {
|
|
162
|
+
const events = this._buffer.events
|
|
163
|
+
this._buffer.clear()
|
|
164
|
+
requests.push({ events, options: this._getOptions(), url: this.url })
|
|
133
165
|
}
|
|
134
166
|
|
|
135
|
-
// Flush multi-tenant buffers
|
|
136
167
|
for (const [apiKey, buffer] of this.#multiTenantBuffers) {
|
|
137
|
-
if (buffer.events.length === 0)
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const payload = this._encode(this.makePayload(events))
|
|
168
|
+
if (buffer.events.length === 0) {
|
|
169
|
+
this.#multiTenantBuffers.delete(apiKey)
|
|
170
|
+
continue
|
|
171
|
+
}
|
|
143
172
|
const site = buffer.routing.site || this._config.site
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
173
|
+
const maskedApiKey = `****${apiKey.slice(-4)}`
|
|
174
|
+
let options
|
|
175
|
+
let url
|
|
176
|
+
try {
|
|
177
|
+
options = {
|
|
178
|
+
headers: {
|
|
179
|
+
'Content-Type': 'application/json',
|
|
180
|
+
'DD-API-KEY': apiKey,
|
|
181
|
+
},
|
|
182
|
+
method: 'POST',
|
|
183
|
+
timeout: this._timeout,
|
|
184
|
+
url: new URL(format({
|
|
185
|
+
protocol: 'https:',
|
|
186
|
+
hostname: `${this._intake}.${site}`,
|
|
187
|
+
})),
|
|
188
|
+
path: this._baseEndpoint,
|
|
189
|
+
}
|
|
190
|
+
url = this.#buildUrl(options.url.href, options.path)
|
|
191
|
+
} catch (error) {
|
|
192
|
+
buffer.clear()
|
|
193
|
+
this.#multiTenantBuffers.delete(apiKey)
|
|
194
|
+
logger.error(
|
|
195
|
+
'Failed to route LLMObs %s events for API key %s: %s',
|
|
196
|
+
this._eventType,
|
|
197
|
+
maskedApiKey,
|
|
198
|
+
error.message
|
|
199
|
+
)
|
|
200
|
+
continue
|
|
156
201
|
}
|
|
157
|
-
const url = this.#buildUrl(options.url.href, options.path)
|
|
158
|
-
const maskedApiKey = apiKey ? `****${apiKey.slice(-4)}` : ''
|
|
159
202
|
|
|
203
|
+
const events = buffer.events
|
|
204
|
+
buffer.clear()
|
|
205
|
+
this.#multiTenantBuffers.delete(apiKey)
|
|
160
206
|
log.debug('Encoding and flushing multi-tenant buffer for %s', maskedApiKey)
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
request(payload, options, (err, resp, code) => {
|
|
164
|
-
parseResponseAndLog(err, code, events.length, url, this._eventType)
|
|
165
|
-
})
|
|
207
|
+
requests.push({ events, options, url })
|
|
166
208
|
}
|
|
167
209
|
|
|
168
|
-
|
|
210
|
+
return requests
|
|
169
211
|
}
|
|
170
212
|
|
|
171
|
-
#
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
213
|
+
#send ({ events, options, url }, done) {
|
|
214
|
+
const payload = this._encode(this.makePayload(events))
|
|
215
|
+
log.debug('Encoded LLMObs payload: %s', payload)
|
|
216
|
+
request(payload, options, (err, resp, code) => {
|
|
217
|
+
parseResponseAndLog(err, code, events.length, url, this._eventType)
|
|
218
|
+
done?.()
|
|
219
|
+
})
|
|
177
220
|
}
|
|
178
221
|
|
|
179
222
|
makePayload (events) {}
|
|
180
223
|
|
|
181
|
-
|
|
224
|
+
/**
|
|
225
|
+
* Stops periodic flushing and drains buffered events.
|
|
226
|
+
* @param {Function} [done] Called after queued and active deliveries complete.
|
|
227
|
+
*/
|
|
228
|
+
destroy (done) {
|
|
182
229
|
if (this.#destroyer) {
|
|
183
230
|
logger.debug(`Stopping ${this.constructor.name}`)
|
|
184
231
|
clearInterval(this._periodic)
|
|
185
232
|
globalThis[Symbol.for('dd-trace')].beforeExitHandlers.delete(this.#destroyer)
|
|
186
|
-
this.flush()
|
|
233
|
+
this.flush(done)
|
|
187
234
|
this.#destroyer = undefined
|
|
235
|
+
} else {
|
|
236
|
+
done?.()
|
|
188
237
|
}
|
|
189
238
|
}
|
|
190
239
|
|
|
@@ -196,6 +245,10 @@ class BaseLLMObsWriter {
|
|
|
196
245
|
this._endpoint = endpoint
|
|
197
246
|
|
|
198
247
|
logger.debug(`Configuring ${this.constructor.name} to ${this.url}`)
|
|
248
|
+
|
|
249
|
+
const pendingFlushes = this.#pendingFlushes
|
|
250
|
+
this.#pendingFlushes = []
|
|
251
|
+
for (const done of pendingFlushes) this.flush(done)
|
|
199
252
|
}
|
|
200
253
|
|
|
201
254
|
_getUrlAndPath () {
|
|
@@ -29,7 +29,13 @@ const log = {
|
|
|
29
29
|
const stack = logRecord.stack.split('\n')
|
|
30
30
|
const fn = stack[1].replace(/^\s+at ([^\s]+) .+/, '$1')
|
|
31
31
|
const options = { depth: 2, breakLength: Infinity, compact: true, maxArrayLength: Infinity }
|
|
32
|
-
|
|
32
|
+
let params = ''
|
|
33
|
+
let isFirstParameter = true
|
|
34
|
+
for (const argument of args) {
|
|
35
|
+
if (!isFirstParameter) params += ', '
|
|
36
|
+
params += inspect(argument, options)
|
|
37
|
+
isFirstParameter = false
|
|
38
|
+
}
|
|
33
39
|
|
|
34
40
|
stack[0] = `Trace: ${fn}(${params})`
|
|
35
41
|
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { Writable } = require('node:stream')
|
|
4
|
+
|
|
5
|
+
const FinalFlushRequestTracker = require('../exporters/common/final-flush-request-tracker')
|
|
6
|
+
const request = require('../exporters/common/request')
|
|
7
|
+
const log = require('../log')
|
|
8
|
+
const Plugin = require('../plugins/plugin')
|
|
9
|
+
|
|
10
|
+
const MAX_BATCH_BYTES = 5 * 1024 * 1024
|
|
11
|
+
const MAX_BATCH_LOGS = 1000
|
|
12
|
+
const BATCH_FLUSH_INTERVAL = 1000
|
|
13
|
+
const FINAL_FLUSH_TIMEOUT = 60_000
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @returns {Error & { code: string }}
|
|
17
|
+
*/
|
|
18
|
+
function createFinalFlushTimeoutError () {
|
|
19
|
+
return Object.assign(
|
|
20
|
+
new Error('Timed out waiting for automatic log submission to flush'),
|
|
21
|
+
{ code: 'ERR_DD_LOG_SUBMISSION_FLUSH_TIMEOUT' }
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {import('../config/config-base')} config
|
|
27
|
+
* @returns {URL | void}
|
|
28
|
+
*/
|
|
29
|
+
function getLogSubmissionUrl (config) {
|
|
30
|
+
if (config.DD_AGENTLESS_LOG_SUBMISSION_URL) {
|
|
31
|
+
try {
|
|
32
|
+
const url = new URL(config.DD_AGENTLESS_LOG_SUBMISSION_URL)
|
|
33
|
+
if (url.protocol === 'http:' || url.protocol === 'https:') return url
|
|
34
|
+
|
|
35
|
+
log.error('Unsupported automatic log submission URL protocol: %s', url.protocol)
|
|
36
|
+
} catch {
|
|
37
|
+
log.error('Could not parse DD_AGENTLESS_LOG_SUBMISSION_URL')
|
|
38
|
+
}
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const hostname = `http-intake.logs.${config.site}`.toLowerCase()
|
|
43
|
+
try {
|
|
44
|
+
const url = new URL(`https://${hostname}`)
|
|
45
|
+
if (url.hostname === hostname) return url
|
|
46
|
+
} catch {}
|
|
47
|
+
|
|
48
|
+
log.error('Could not parse automatic log submission site: %s', config.site)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @param {unknown} config
|
|
53
|
+
* @returns {import('../config/config-base')}
|
|
54
|
+
*/
|
|
55
|
+
function asTracerConfig (config) {
|
|
56
|
+
return /** @type {import('../config/config-base')} */ (config)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @param {import('../config/config-base')} config
|
|
61
|
+
* @param {string} source
|
|
62
|
+
* @returns {string}
|
|
63
|
+
*/
|
|
64
|
+
function getLogSubmissionPath (config, source) {
|
|
65
|
+
return `/api/v2/logs?${new URLSearchParams({ ddsource: source, service: config.service })}`
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
class LogSubmissionPlugin extends Plugin {
|
|
69
|
+
static id = 'log-submission'
|
|
70
|
+
|
|
71
|
+
/** @type {string[]} */
|
|
72
|
+
#batch = []
|
|
73
|
+
#batchBytes = 2
|
|
74
|
+
#batchSource
|
|
75
|
+
/** @type {import('../config/config-base') | undefined} */
|
|
76
|
+
#config
|
|
77
|
+
#logSubmissionUrl
|
|
78
|
+
#requestTracker
|
|
79
|
+
#timer
|
|
80
|
+
#beforeExitHandler = () => this.#flushLogs()
|
|
81
|
+
#createWinstonJsonFormat
|
|
82
|
+
#winstonStreamClass
|
|
83
|
+
// Winston formats records inside its transports, not at logger.write time, so (unlike Bunyan/Pino)
|
|
84
|
+
// the instrumentation can't publish a post-format line. A Stream transport pipes format.json()
|
|
85
|
+
// output through this Writable into the shared sender, reusing Winston's own cycle-safe serializer.
|
|
86
|
+
#winstonOutput = new Writable({
|
|
87
|
+
decodeStrings: false,
|
|
88
|
+
write: (message, encoding, callback) => {
|
|
89
|
+
this.#enqueueLog({ source: 'winston', message })
|
|
90
|
+
callback()
|
|
91
|
+
},
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @param {object} tracer
|
|
96
|
+
* @param {unknown} tracerConfig
|
|
97
|
+
*/
|
|
98
|
+
constructor (tracer, tracerConfig) {
|
|
99
|
+
super(tracer, asTracerConfig(tracerConfig))
|
|
100
|
+
this.#requestTracker = new FinalFlushRequestTracker(
|
|
101
|
+
(done) => {
|
|
102
|
+
this.#flushLogs()
|
|
103
|
+
done?.()
|
|
104
|
+
},
|
|
105
|
+
createFinalFlushTimeoutError
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
// The main-module hook (configure) and the logger.js hook (add-transport) can fire in either
|
|
109
|
+
// order depending on how Winston is required, so buffer loggers that arrive before configure.
|
|
110
|
+
const pendingWinstonLoggers = new Set()
|
|
111
|
+
const addWinstonTransport = (logger) => {
|
|
112
|
+
if (!this.#winstonStreamClass || !this.#createWinstonJsonFormat) {
|
|
113
|
+
pendingWinstonLoggers.add(logger)
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
logger.add(new this.#winstonStreamClass({
|
|
118
|
+
format: this.#createWinstonJsonFormat(),
|
|
119
|
+
stream: this.#winstonOutput,
|
|
120
|
+
}))
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
this.addSub('ci:log-submission:winston:configure', ({ StreamTransport, createJsonFormat }) => {
|
|
124
|
+
this.#winstonStreamClass = StreamTransport
|
|
125
|
+
this.#createWinstonJsonFormat = createJsonFormat
|
|
126
|
+
|
|
127
|
+
for (const logger of pendingWinstonLoggers) {
|
|
128
|
+
addWinstonTransport(logger)
|
|
129
|
+
}
|
|
130
|
+
pendingWinstonLoggers.clear()
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
this.addSub('ci:log-submission:winston:add-transport', addWinstonTransport)
|
|
134
|
+
|
|
135
|
+
this.addSub('ci:log-submission:log', (payload) => {
|
|
136
|
+
this.#enqueueLog(payload)
|
|
137
|
+
})
|
|
138
|
+
this.addSub('ci:log-submission:flush', ({ onDone } = {}) => {
|
|
139
|
+
if (!onDone) {
|
|
140
|
+
this.#flushLogs()
|
|
141
|
+
return
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
this.#requestTracker.flush(onDone, {
|
|
145
|
+
deadline: Date.now() + FINAL_FLUSH_TIMEOUT,
|
|
146
|
+
})
|
|
147
|
+
})
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* @param {boolean | (Record<string, unknown> & { enabled: boolean })} config
|
|
152
|
+
* @returns {void}
|
|
153
|
+
*/
|
|
154
|
+
configure (config) {
|
|
155
|
+
if (this._enabled) this.#flushLogs()
|
|
156
|
+
|
|
157
|
+
const isEnabled = typeof config === 'boolean' ? config : config.enabled
|
|
158
|
+
this.#config = isEnabled && typeof config !== 'boolean' ? asTracerConfig(config) : undefined
|
|
159
|
+
this.#logSubmissionUrl = this.#config
|
|
160
|
+
? getLogSubmissionUrl(this.#config)
|
|
161
|
+
: undefined
|
|
162
|
+
super.configure(config)
|
|
163
|
+
|
|
164
|
+
const beforeExitHandlers = globalThis[Symbol.for('dd-trace')].beforeExitHandlers
|
|
165
|
+
if (this._enabled) {
|
|
166
|
+
beforeExitHandlers.add(this.#beforeExitHandler)
|
|
167
|
+
} else {
|
|
168
|
+
beforeExitHandlers.delete(this.#beforeExitHandler)
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* @param {{ source: string, message: string | Record<string, unknown> }} payload
|
|
174
|
+
* @returns {void}
|
|
175
|
+
*/
|
|
176
|
+
#enqueueLog ({ source, message }) {
|
|
177
|
+
if (!this.#logSubmissionUrl) return
|
|
178
|
+
|
|
179
|
+
let serializedMessage
|
|
180
|
+
try {
|
|
181
|
+
serializedMessage = typeof message === 'string' ? message : JSON.stringify(message)
|
|
182
|
+
} catch (error) {
|
|
183
|
+
log.error('Could not serialize %s log for automatic submission', source, error)
|
|
184
|
+
return
|
|
185
|
+
}
|
|
186
|
+
if (serializedMessage === undefined) return
|
|
187
|
+
|
|
188
|
+
const messageBytes = Buffer.byteLength(serializedMessage)
|
|
189
|
+
if (messageBytes + 2 > MAX_BATCH_BYTES) {
|
|
190
|
+
log.error('Could not submit %s log because it exceeds the %d byte payload limit', source, MAX_BATCH_BYTES)
|
|
191
|
+
return
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (this.#batch.length > 0 &&
|
|
195
|
+
(this.#batchSource !== source || this.#batchBytes + messageBytes + 1 > MAX_BATCH_BYTES)) {
|
|
196
|
+
this.#flushLogs()
|
|
197
|
+
if (!this.#logSubmissionUrl) return
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
this.#batchSource = source
|
|
201
|
+
if (this.#batch.length > 0) this.#batchBytes++
|
|
202
|
+
this.#batch.push(serializedMessage)
|
|
203
|
+
this.#batchBytes += messageBytes
|
|
204
|
+
|
|
205
|
+
if (this.#batch.length === MAX_BATCH_LOGS || this.#batchBytes === MAX_BATCH_BYTES) {
|
|
206
|
+
this.#flushLogs()
|
|
207
|
+
} else if (this.#timer === undefined) {
|
|
208
|
+
this.#timer = setTimeout(() => this.#flushLogs(), BATCH_FLUSH_INTERVAL)
|
|
209
|
+
this.#timer.unref?.()
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* @returns {void}
|
|
215
|
+
*/
|
|
216
|
+
#flushLogs () {
|
|
217
|
+
clearTimeout(this.#timer)
|
|
218
|
+
this.#timer = undefined
|
|
219
|
+
|
|
220
|
+
const config = this.#config
|
|
221
|
+
if (this.#batch.length === 0 || !this.#logSubmissionUrl || !config) return
|
|
222
|
+
|
|
223
|
+
const source = this.#batchSource
|
|
224
|
+
const data = `[${this.#batch.join(',')}]`
|
|
225
|
+
this.#batch = []
|
|
226
|
+
this.#batchBytes = 2
|
|
227
|
+
this.#batchSource = undefined
|
|
228
|
+
const options = {
|
|
229
|
+
path: getLogSubmissionPath(config, source),
|
|
230
|
+
method: 'POST',
|
|
231
|
+
headers: {
|
|
232
|
+
'DD-API-KEY': config.DD_API_KEY,
|
|
233
|
+
'Content-Type': 'application/json',
|
|
234
|
+
},
|
|
235
|
+
url: this.#logSubmissionUrl,
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
try {
|
|
239
|
+
this.#requestTracker.send(request, data, options, error => {
|
|
240
|
+
if (error) log.error('Error submitting %s logs', source, error)
|
|
241
|
+
})
|
|
242
|
+
} catch (error) {
|
|
243
|
+
this.#logSubmissionUrl = undefined
|
|
244
|
+
log.error('Error submitting %s logs', source, error)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
module.exports = LogSubmissionPlugin
|
|
@@ -26,6 +26,11 @@ class NoopSpan {
|
|
|
26
26
|
addLink (link) { return this }
|
|
27
27
|
addLinks (links) { return this }
|
|
28
28
|
addSpanPointer (ptrKind, ptrDir, ptrHash) { return this }
|
|
29
|
+
/**
|
|
30
|
+
* @param {import('../../../../index').Exception} exception
|
|
31
|
+
* @param {import('../../../../index').SpanEventAttributes} [attributes]
|
|
32
|
+
*/
|
|
33
|
+
recordException (exception, attributes) {}
|
|
29
34
|
log () { return this }
|
|
30
35
|
logEvent () {}
|
|
31
36
|
finish (finishTime) {}
|
|
@@ -214,21 +214,23 @@ class BaseFFEWriter {
|
|
|
214
214
|
* @returns {ActiveWriterRoute} Active route state
|
|
215
215
|
*/
|
|
216
216
|
#createRoute (route) {
|
|
217
|
+
const requestOptions = {
|
|
218
|
+
headers: {
|
|
219
|
+
...route.headers,
|
|
220
|
+
'Content-Type': 'application/json',
|
|
221
|
+
},
|
|
222
|
+
method: 'POST',
|
|
223
|
+
retry: true,
|
|
224
|
+
timeout: this._timeout,
|
|
225
|
+
url: route.url,
|
|
226
|
+
path: route.endpoint,
|
|
227
|
+
}
|
|
228
|
+
if (route.agent) requestOptions.agent = route.agent
|
|
229
|
+
|
|
217
230
|
return {
|
|
218
231
|
url: route.url,
|
|
219
232
|
endpoint: route.endpoint,
|
|
220
|
-
requestOptions
|
|
221
|
-
...(route.agent && { agent: route.agent }),
|
|
222
|
-
headers: {
|
|
223
|
-
...route.headers,
|
|
224
|
-
'Content-Type': 'application/json',
|
|
225
|
-
},
|
|
226
|
-
method: 'POST',
|
|
227
|
-
retry: true,
|
|
228
|
-
timeout: this._timeout,
|
|
229
|
-
url: route.url,
|
|
230
|
-
path: route.endpoint,
|
|
231
|
-
},
|
|
233
|
+
requestOptions,
|
|
232
234
|
}
|
|
233
235
|
}
|
|
234
236
|
|
|
@@ -37,6 +37,7 @@ const PENDING_MAX_EVENTS = 1000
|
|
|
37
37
|
* @property {string} flag.key - Flag key
|
|
38
38
|
* @property {object} variant - Variant information
|
|
39
39
|
* @property {string} variant.key - Variant key
|
|
40
|
+
* @property {number} [serial_id] - Serial id of the split the subject landed in
|
|
40
41
|
* @property {object} subject - Subject (user/entity) information
|
|
41
42
|
* @property {string} subject.id - Subject identifier
|
|
42
43
|
* @property {string} [subject.type] - Subject type
|
|
@@ -204,7 +205,7 @@ class ExposuresWriter extends BaseFFEWriter {
|
|
|
204
205
|
makePayload (events) {
|
|
205
206
|
const formattedEvents = events.map(event => {
|
|
206
207
|
/** @type {ExposureEvent} */
|
|
207
|
-
|
|
208
|
+
const formatted = {
|
|
208
209
|
timestamp: event.timestamp || Date.now(),
|
|
209
210
|
allocation: {
|
|
210
211
|
key: event.allocation?.key || event['allocation.key'],
|
|
@@ -221,6 +222,12 @@ class ExposuresWriter extends BaseFFEWriter {
|
|
|
221
222
|
attributes: event.subject?.attributes,
|
|
222
223
|
},
|
|
223
224
|
}
|
|
225
|
+
|
|
226
|
+
if (typeof event.serial_id === 'number') {
|
|
227
|
+
formatted.serial_id = event.serial_id
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return formatted
|
|
224
231
|
})
|
|
225
232
|
|
|
226
233
|
return {
|
|
@@ -75,8 +75,8 @@ function setAgentlessStrategy (config, setWriterEnabledValue) {
|
|
|
75
75
|
headers: {
|
|
76
76
|
[EVP_SUBDOMAIN_HEADER_NAME]: EVP_EVENT_PLATFORM_SUBDOMAIN,
|
|
77
77
|
},
|
|
78
|
-
...(directRoute && { fallback: directRoute }),
|
|
79
78
|
}
|
|
79
|
+
if (directRoute) route.fallback = directRoute
|
|
80
80
|
logger.debug('FFE Writer enabled with local EVP route %s', route.basePath)
|
|
81
81
|
setWriterEnabledValue(true, route)
|
|
82
82
|
return
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const log = require('../../log')
|
|
4
|
+
const { createServerlessDeliveryTracker } = require('../../serverless')
|
|
5
|
+
|
|
3
6
|
/**
|
|
4
7
|
* @typedef {import('@opentelemetry/api-logs').LogRecord} LogRecord
|
|
5
8
|
* @typedef {import('@opentelemetry/core').InstrumentationScope} InstrumentationScope
|
|
@@ -54,10 +57,48 @@ class BatchLogRecordProcessor {
|
|
|
54
57
|
|
|
55
58
|
/**
|
|
56
59
|
* Forces an immediate flush of all pending log records.
|
|
57
|
-
* @
|
|
60
|
+
* @param {Function} [done] Called after all pending log exports complete
|
|
58
61
|
*/
|
|
59
|
-
forceFlush () {
|
|
60
|
-
this.#
|
|
62
|
+
forceFlush (done) {
|
|
63
|
+
this.#clearTimer()
|
|
64
|
+
|
|
65
|
+
const deliveryTracker = createServerlessDeliveryTracker()
|
|
66
|
+
if (!deliveryTracker) {
|
|
67
|
+
// Normal processes preserve the existing fire-and-forget batch flush.
|
|
68
|
+
this.#export()
|
|
69
|
+
done?.()
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Flush only records present at this boundary. New records belong to the
|
|
74
|
+
// later request that produced them and must not extend this lifecycle flush.
|
|
75
|
+
const logRecords = this.#logRecords
|
|
76
|
+
this.#logRecords = []
|
|
77
|
+
|
|
78
|
+
// Join exports already active at this boundary before draining this snapshot.
|
|
79
|
+
if (typeof this.exporter.flush === 'function') {
|
|
80
|
+
deliveryTracker.track(complete => this.exporter.flush(complete))
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
deliveryTracker.track(complete => {
|
|
84
|
+
const flushNext = () => {
|
|
85
|
+
if (logRecords.length === 0) {
|
|
86
|
+
complete()
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Drain the boundary snapshot one batch at a time.
|
|
91
|
+
const batch = logRecords.splice(0, this.#maxExportBatchSize)
|
|
92
|
+
try {
|
|
93
|
+
this.exporter.export(batch, flushNext)
|
|
94
|
+
} catch (error) {
|
|
95
|
+
log.error('Error exporting OTLP logs:', error)
|
|
96
|
+
complete()
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
flushNext()
|
|
100
|
+
})
|
|
101
|
+
deliveryTracker.waitForIdle(done)
|
|
61
102
|
}
|
|
62
103
|
|
|
63
104
|
/**
|
|
@@ -79,6 +120,7 @@ class BatchLogRecordProcessor {
|
|
|
79
120
|
* @private
|
|
80
121
|
*/
|
|
81
122
|
#export () {
|
|
123
|
+
if (this.#logRecords.length === 0) return
|
|
82
124
|
const logRecords = this.#logRecords.slice(0, this.#maxExportBatchSize)
|
|
83
125
|
this.#logRecords = this.#logRecords.slice(this.#maxExportBatchSize)
|
|
84
126
|
|