dd-trace 5.123.0 → 5.124.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/ci/test-optimization-validation/framework-adapters/cypress.js +9 -11
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +5 -4
- package/index.d.ts +128 -26
- package/package.json +10 -10
- package/packages/datadog-instrumentations/src/bunyan.js +4 -2
- package/packages/datadog-instrumentations/src/helpers/bunyan.js +12 -3
- package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +28 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
- package/packages/datadog-instrumentations/src/jest.js +140 -18
- package/packages/datadog-instrumentations/src/mariadb-bundle.js +1597 -0
- package/packages/datadog-instrumentations/src/mariadb.js +7 -0
- package/packages/datadog-instrumentations/src/pino.js +15 -4
- package/packages/datadog-instrumentations/src/playwright.js +34 -20
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +7 -4
- package/packages/datadog-instrumentations/src/vitest-worker.js +11 -0
- package/packages/datadog-instrumentations/src/webdriverio.js +26 -0
- package/packages/datadog-instrumentations/src/winston.js +6 -3
- package/packages/datadog-plugin-cypress/src/support.js +1 -0
- package/packages/datadog-plugin-http/src/client.js +2 -15
- package/packages/datadog-plugin-http2/src/client.js +2 -15
- package/packages/datadog-plugin-openai-agents/src/integration.js +15 -11
- package/packages/datadog-plugin-playwright/src/index.js +9 -1
- package/packages/datadog-plugin-undici/src/index.js +2 -15
- package/packages/dd-trace/src/aiguard/evaluation.js +94 -20
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -3
- package/packages/dd-trace/src/aiguard/messages/anthropic.js +21 -9
- package/packages/dd-trace/src/aiguard/noop.js +9 -1
- package/packages/dd-trace/src/aiguard/redaction.js +183 -0
- package/packages/dd-trace/src/aiguard/sdk.js +7 -2
- package/packages/dd-trace/src/aiguard/tags.js +2 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +10 -9
- package/packages/dd-trace/src/appsec/rule_manager.js +2 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +51 -26
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +5 -11
- package/packages/dd-trace/src/ci-visibility/final-flush.js +20 -0
- package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +196 -29
- package/packages/dd-trace/src/config/generated-config-types.d.ts +6 -0
- package/packages/dd-trace/src/config/index.js +2 -1
- package/packages/dd-trace/src/config/supported-configurations.json +27 -0
- package/packages/dd-trace/src/debugger/devtools_client/send.js +4 -3
- package/packages/dd-trace/src/dogstatsd.js +45 -18
- package/packages/dd-trace/src/encode/agentless-json.js +5 -4
- package/packages/dd-trace/src/exporters/agent/index.js +22 -2
- package/packages/dd-trace/src/exporters/agent/writer.js +4 -4
- package/packages/dd-trace/src/exporters/common/writer.js +24 -2
- package/packages/dd-trace/src/exporters/span-stats/index.js +23 -4
- package/packages/dd-trace/src/external-logger/src/index.js +4 -3
- package/packages/dd-trace/src/flush.js +102 -0
- package/packages/dd-trace/src/llmobs/experiments/client.js +4 -0
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +51 -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 +80 -17
- package/packages/dd-trace/src/llmobs/noop.js +2 -1
- package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +8 -4
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +6 -3
- package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +13 -6
- package/packages/dd-trace/src/llmobs/plugins/openai-agents/utils.js +5 -7
- package/packages/dd-trace/src/llmobs/writers/base.js +60 -24
- package/packages/dd-trace/src/noop/span.js +5 -0
- package/packages/dd-trace/src/openfeature/writers/exposures.js +8 -1
- package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +45 -3
- package/packages/dd-trace/src/opentelemetry/logs/index.js +4 -1
- package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +7 -4
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +3 -1
- package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +8 -0
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +4 -2
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +4 -2
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +25 -5
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +61 -25
- package/packages/dd-trace/src/opentelemetry/tracer.js +26 -2
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +410 -342
- package/packages/dd-trace/src/opentracing/span.js +28 -0
- package/packages/dd-trace/src/otel-thread-ctx.js +23 -5
- package/packages/dd-trace/src/plugin_manager.js +2 -0
- package/packages/dd-trace/src/plugins/util/status-validator.js +98 -0
- package/packages/dd-trace/src/plugins/util/test.js +74 -21
- package/packages/dd-trace/src/plugins/util/web.js +2 -65
- package/packages/dd-trace/src/profiling/oom.js +4 -3
- package/packages/dd-trace/src/profiling/profiler.js +7 -0
- package/packages/dd-trace/src/profiling/profilers/events.js +3 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +14 -14
- package/packages/dd-trace/src/proxy.js +36 -8
- package/packages/dd-trace/src/runtime_metrics/index.js +5 -0
- package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +5 -0
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +17 -6
- package/packages/dd-trace/src/serverless/telemetry-delivery-tracker.js +55 -0
- package/packages/dd-trace/src/serverless/vercel.js +122 -0
- package/packages/dd-trace/src/serverless.js +37 -20
- package/packages/dd-trace/src/span_stats.js +40 -3
- package/packages/dd-trace/src/tracer.js +22 -0
- package/packages/dd-trace/src/web-tags-cache.js +67 -13
- package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const BaseWriter = require('../../../exporters/common/writer')
|
|
4
|
+
const { createFinalFlushTimeoutError } = require('../../final-flush')
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class TestOptimizationRequestTracker {
|
|
8
|
-
#writer
|
|
6
|
+
class FinalFlushRequestTracker {
|
|
7
|
+
#flush
|
|
9
8
|
#pendingRequests = new Set()
|
|
10
9
|
#finalFlushes = new Set()
|
|
11
10
|
#activeFinalFlush
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
|
-
* Creates request tracking for a Test Optimization
|
|
13
|
+
* Creates request tracking for a Test Optimization exporter.
|
|
15
14
|
*
|
|
16
|
-
* @param {
|
|
15
|
+
* @param {(done?: (error?: Error) => void, options?: { deadline?: number }) => void} flush
|
|
17
16
|
*/
|
|
18
|
-
constructor (
|
|
19
|
-
this.#
|
|
17
|
+
constructor (flush) {
|
|
18
|
+
this.#flush = flush
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
/**
|
|
@@ -30,7 +29,7 @@ class TestOptimizationRequestTracker {
|
|
|
30
29
|
*/
|
|
31
30
|
flush (done, options) {
|
|
32
31
|
if (options?.deadline === undefined) {
|
|
33
|
-
|
|
32
|
+
this.#flush(done, options)
|
|
34
33
|
return
|
|
35
34
|
}
|
|
36
35
|
|
|
@@ -50,8 +49,7 @@ class TestOptimizationRequestTracker {
|
|
|
50
49
|
|
|
51
50
|
const remaining = Math.max(0, options.deadline - Date.now())
|
|
52
51
|
finalFlush.timeoutId = setTimeout(() => {
|
|
53
|
-
const error =
|
|
54
|
-
error.code = FINAL_FLUSH_TIMEOUT_CODE
|
|
52
|
+
const error = createFinalFlushTimeoutError()
|
|
55
53
|
|
|
56
54
|
finalFlush.error ||= error
|
|
57
55
|
finalFlush.writerDone = true
|
|
@@ -72,7 +70,7 @@ class TestOptimizationRequestTracker {
|
|
|
72
70
|
const previousFinalFlush = this.#activeFinalFlush
|
|
73
71
|
this.#activeFinalFlush = finalFlush
|
|
74
72
|
try {
|
|
75
|
-
|
|
73
|
+
this.#flush((error) => {
|
|
76
74
|
finalFlush.error ||= error
|
|
77
75
|
finalFlush.writerDone = true
|
|
78
76
|
this.#finishFinalFlush(finalFlush)
|
|
@@ -100,22 +98,37 @@ class TestOptimizationRequestTracker {
|
|
|
100
98
|
this.#pendingRequests.add(pendingRequest)
|
|
101
99
|
if (this.#activeFinalFlush) this.#attachRequest(this.#activeFinalFlush, pendingRequest)
|
|
102
100
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
101
|
+
try {
|
|
102
|
+
request(data, requestOptions, (error, result, statusCode, headers) => {
|
|
103
|
+
if (error) {
|
|
104
|
+
for (const finalFlush of pendingRequest.finalFlushes) finalFlush.error ||= error
|
|
105
|
+
}
|
|
107
106
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
for (const finalFlush of pendingRequest.finalFlushes) {
|
|
113
|
-
finalFlush.requests.delete(pendingRequest)
|
|
114
|
-
this.#finishFinalFlush(finalFlush)
|
|
107
|
+
try {
|
|
108
|
+
callback(error, result, statusCode, headers)
|
|
109
|
+
} finally {
|
|
110
|
+
this.#dropRequest(pendingRequest)
|
|
115
111
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
112
|
+
})
|
|
113
|
+
} catch (error) {
|
|
114
|
+
this.#dropRequest(pendingRequest)
|
|
115
|
+
throw error
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Stops tracking a settled request and releases final flushes waiting for it.
|
|
121
|
+
*
|
|
122
|
+
* @param {object} pendingRequest
|
|
123
|
+
* @returns {void}
|
|
124
|
+
*/
|
|
125
|
+
#dropRequest (pendingRequest) {
|
|
126
|
+
this.#pendingRequests.delete(pendingRequest)
|
|
127
|
+
for (const finalFlush of pendingRequest.finalFlushes) {
|
|
128
|
+
finalFlush.requests.delete(pendingRequest)
|
|
129
|
+
this.#finishFinalFlush(finalFlush)
|
|
130
|
+
}
|
|
131
|
+
pendingRequest.finalFlushes.clear()
|
|
119
132
|
}
|
|
120
133
|
|
|
121
134
|
/**
|
|
@@ -161,4 +174,16 @@ class TestOptimizationRequestTracker {
|
|
|
161
174
|
}
|
|
162
175
|
}
|
|
163
176
|
|
|
177
|
+
class TestOptimizationRequestTracker extends FinalFlushRequestTracker {
|
|
178
|
+
/**
|
|
179
|
+
* Creates request tracking for a Test Optimization writer.
|
|
180
|
+
*
|
|
181
|
+
* @param {BaseWriter} writer
|
|
182
|
+
*/
|
|
183
|
+
constructor (writer) {
|
|
184
|
+
super((done, options) => BaseWriter.prototype.flushDirect.call(writer, done, options))
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
164
188
|
module.exports = TestOptimizationRequestTracker
|
|
189
|
+
module.exports.FinalFlushRequestTracker = FinalFlushRequestTracker
|
|
@@ -22,6 +22,11 @@ const { getSegment } = require('../../util')
|
|
|
22
22
|
const BufferingExporter = require('../../exporters/common/buffering-exporter')
|
|
23
23
|
const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('../../plugins/util/tags')
|
|
24
24
|
const { TEST_STATUS } = require('../../plugins/util/test')
|
|
25
|
+
const {
|
|
26
|
+
createFinalFlushTimeoutError,
|
|
27
|
+
FINAL_FLUSH_FALLBACK_DELAY,
|
|
28
|
+
FINAL_FLUSH_TIMEOUT,
|
|
29
|
+
} = require('../final-flush')
|
|
25
30
|
const { sendGitMetadata: sendGitMetadataRequest } = require('./git/git_metadata')
|
|
26
31
|
|
|
27
32
|
const hostname = getHostname()
|
|
@@ -60,17 +65,6 @@ function getIsTestSessionTrace (trace) {
|
|
|
60
65
|
const GIT_UPLOAD_TIMEOUT = 60_000 // 60 seconds
|
|
61
66
|
const CAN_USE_CI_VIS_PROTOCOL_TIMEOUT = GIT_UPLOAD_TIMEOUT
|
|
62
67
|
const MAX_COVERAGE_REPORT_FLAGS = 32
|
|
63
|
-
const FINAL_FLUSH_TIMEOUT = 10_000
|
|
64
|
-
const FINAL_FLUSH_FALLBACK_DELAY = 100
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* @returns {Error}
|
|
68
|
-
*/
|
|
69
|
-
function createFinalFlushTimeoutError () {
|
|
70
|
-
const error = new Error('Timed out waiting for Test Optimization to flush')
|
|
71
|
-
error.code = 'ERR_DD_TEST_OPTIMIZATION_FLUSH_TIMEOUT'
|
|
72
|
-
return error
|
|
73
|
-
}
|
|
74
68
|
|
|
75
69
|
function appendLogTag (tags, key, value) {
|
|
76
70
|
if (value !== undefined) {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const FINAL_FLUSH_TIMEOUT = 10_000
|
|
4
|
+
const FINAL_FLUSH_FALLBACK_DELAY = 100
|
|
5
|
+
const FINAL_FLUSH_TIMEOUT_CODE = 'ERR_DD_TEST_OPTIMIZATION_FLUSH_TIMEOUT'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @returns {Error & { code: string }}
|
|
9
|
+
*/
|
|
10
|
+
function createFinalFlushTimeoutError () {
|
|
11
|
+
const error = new Error('Timed out waiting for Test Optimization to flush')
|
|
12
|
+
error.code = FINAL_FLUSH_TIMEOUT_CODE
|
|
13
|
+
return error
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module.exports = {
|
|
17
|
+
createFinalFlushTimeoutError,
|
|
18
|
+
FINAL_FLUSH_FALLBACK_DELAY,
|
|
19
|
+
FINAL_FLUSH_TIMEOUT,
|
|
20
|
+
}
|
|
@@ -1,53 +1,220 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const { Writable } = require('node:stream')
|
|
4
|
+
|
|
5
|
+
const request = require('../../exporters/common/request')
|
|
4
6
|
const log = require('../../log')
|
|
7
|
+
const Plugin = require('../../plugins/plugin')
|
|
8
|
+
const { FinalFlushRequestTracker } = require('../exporters/agentless/request-tracker')
|
|
9
|
+
const { FINAL_FLUSH_TIMEOUT } = require('../final-flush')
|
|
5
10
|
|
|
6
|
-
|
|
7
|
-
|
|
11
|
+
const MAX_BATCH_BYTES = 5 * 1024 * 1024
|
|
12
|
+
const MAX_BATCH_LOGS = 1000
|
|
13
|
+
const BATCH_FLUSH_INTERVAL = 1000
|
|
8
14
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
/**
|
|
16
|
+
* @param {import('../../config/config-base')} config
|
|
17
|
+
* @returns {URL | undefined}
|
|
18
|
+
*/
|
|
19
|
+
function getLogSubmissionUrl (config) {
|
|
20
|
+
if (config.DD_AGENTLESS_LOG_SUBMISSION_URL) {
|
|
21
|
+
try {
|
|
22
|
+
const url = new URL(config.DD_AGENTLESS_LOG_SUBMISSION_URL)
|
|
23
|
+
if (url.protocol === 'http:' || url.protocol === 'https:') return url
|
|
17
24
|
|
|
18
|
-
|
|
19
|
-
|
|
25
|
+
log.error('Unsupported automatic log submission URL protocol: %s', url.protocol)
|
|
26
|
+
} catch {
|
|
27
|
+
log.error('Could not parse DD_AGENTLESS_LOG_SUBMISSION_URL')
|
|
28
|
+
}
|
|
29
|
+
return
|
|
20
30
|
}
|
|
21
31
|
|
|
32
|
+
const hostname = `http-intake.logs.${config.site}`.toLowerCase()
|
|
22
33
|
try {
|
|
23
|
-
const url = new URL(
|
|
24
|
-
return
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
const url = new URL(`https://${hostname}`)
|
|
35
|
+
if (url.hostname === hostname) return url
|
|
36
|
+
} catch {}
|
|
37
|
+
|
|
38
|
+
log.error('Could not parse automatic log submission site: %s', config.site)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @param {import('../../config/config-base')} config
|
|
43
|
+
* @param {string} source
|
|
44
|
+
* @returns {string}
|
|
45
|
+
*/
|
|
46
|
+
function getLogSubmissionPath (config, source) {
|
|
47
|
+
return `/api/v2/logs?${new URLSearchParams({ ddsource: source, service: config.service })}`
|
|
35
48
|
}
|
|
36
49
|
|
|
37
50
|
class LogSubmissionPlugin extends Plugin {
|
|
38
51
|
static id = 'log-submission'
|
|
39
52
|
|
|
53
|
+
/** @type {string[]} */
|
|
54
|
+
#batch = []
|
|
55
|
+
#batchBytes = 2
|
|
56
|
+
#batchSource
|
|
57
|
+
#logSubmissionUrl
|
|
58
|
+
#requestTracker
|
|
59
|
+
#timer
|
|
60
|
+
#beforeExitHandler = () => this.#flushLogs()
|
|
61
|
+
#createWinstonJsonFormat
|
|
62
|
+
#winstonStreamClass
|
|
63
|
+
// Winston formats records inside its transports, not at logger.write time, so (unlike Bunyan/Pino)
|
|
64
|
+
// the instrumentation can't publish a post-format line. A Stream transport pipes format.json()
|
|
65
|
+
// output through this Writable into the shared sender, reusing Winston's own cycle-safe serializer.
|
|
66
|
+
#winstonOutput = new Writable({
|
|
67
|
+
decodeStrings: false,
|
|
68
|
+
write: (message, encoding, callback) => {
|
|
69
|
+
this.#enqueueLog({ source: 'winston', message })
|
|
70
|
+
callback()
|
|
71
|
+
},
|
|
72
|
+
})
|
|
73
|
+
|
|
40
74
|
constructor (...args) {
|
|
41
75
|
super(...args)
|
|
76
|
+
this.#requestTracker = new FinalFlushRequestTracker((done) => {
|
|
77
|
+
this.#flushLogs()
|
|
78
|
+
done?.()
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
// The main-module hook (configure) and the logger.js hook (add-transport) can fire in either
|
|
82
|
+
// order depending on how Winston is required, so buffer loggers that arrive before configure.
|
|
83
|
+
const pendingWinstonLoggers = new Set()
|
|
84
|
+
const addWinstonTransport = (logger) => {
|
|
85
|
+
if (!this.#winstonStreamClass || !this.#createWinstonJsonFormat) {
|
|
86
|
+
pendingWinstonLoggers.add(logger)
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
logger.add(new this.#winstonStreamClass({
|
|
91
|
+
format: this.#createWinstonJsonFormat(),
|
|
92
|
+
stream: this.#winstonOutput,
|
|
93
|
+
}))
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
this.addSub('ci:log-submission:winston:configure', ({ StreamTransport, createJsonFormat }) => {
|
|
97
|
+
this.#winstonStreamClass = StreamTransport
|
|
98
|
+
this.#createWinstonJsonFormat = createJsonFormat
|
|
42
99
|
|
|
43
|
-
|
|
44
|
-
|
|
100
|
+
for (const logger of pendingWinstonLoggers) {
|
|
101
|
+
addWinstonTransport(logger)
|
|
102
|
+
}
|
|
103
|
+
pendingWinstonLoggers.clear()
|
|
45
104
|
})
|
|
46
105
|
|
|
47
|
-
this.addSub('ci:log-submission:winston:add-transport',
|
|
48
|
-
|
|
106
|
+
this.addSub('ci:log-submission:winston:add-transport', addWinstonTransport)
|
|
107
|
+
|
|
108
|
+
this.addSub('ci:log-submission:log', (payload) => {
|
|
109
|
+
this.#enqueueLog(payload)
|
|
110
|
+
})
|
|
111
|
+
this.addSub('ci:log-submission:flush', ({ onDone } = {}) => {
|
|
112
|
+
if (!onDone) {
|
|
113
|
+
this.#flushLogs()
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
this.#requestTracker.flush(onDone, {
|
|
118
|
+
deadline: Date.now() + FINAL_FLUSH_TIMEOUT,
|
|
119
|
+
})
|
|
49
120
|
})
|
|
50
121
|
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* @param {boolean | (Record<string, unknown> & { enabled: boolean })} config
|
|
125
|
+
* @returns {void}
|
|
126
|
+
*/
|
|
127
|
+
configure (config) {
|
|
128
|
+
if (this._enabled) this.#flushLogs()
|
|
129
|
+
|
|
130
|
+
const isEnabled = typeof config === 'boolean' ? config : config.enabled
|
|
131
|
+
this.#logSubmissionUrl = isEnabled && typeof config !== 'boolean'
|
|
132
|
+
? getLogSubmissionUrl(config)
|
|
133
|
+
: undefined
|
|
134
|
+
super.configure(config)
|
|
135
|
+
|
|
136
|
+
const beforeExitHandlers = globalThis[Symbol.for('dd-trace')].beforeExitHandlers
|
|
137
|
+
if (this._enabled) {
|
|
138
|
+
beforeExitHandlers.add(this.#beforeExitHandler)
|
|
139
|
+
} else {
|
|
140
|
+
beforeExitHandlers.delete(this.#beforeExitHandler)
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* @param {{ source: string, message: string | Record<string, unknown> }} payload
|
|
146
|
+
* @returns {void}
|
|
147
|
+
*/
|
|
148
|
+
#enqueueLog ({ source, message }) {
|
|
149
|
+
if (!this.#logSubmissionUrl) return
|
|
150
|
+
|
|
151
|
+
let serializedMessage
|
|
152
|
+
try {
|
|
153
|
+
serializedMessage = typeof message === 'string' ? message : JSON.stringify(message)
|
|
154
|
+
} catch (error) {
|
|
155
|
+
log.error('Could not serialize %s log for automatic submission', source, error)
|
|
156
|
+
return
|
|
157
|
+
}
|
|
158
|
+
if (serializedMessage === undefined) return
|
|
159
|
+
|
|
160
|
+
const messageBytes = Buffer.byteLength(serializedMessage)
|
|
161
|
+
if (messageBytes + 2 > MAX_BATCH_BYTES) {
|
|
162
|
+
log.error('Could not submit %s log because it exceeds the %d byte payload limit', source, MAX_BATCH_BYTES)
|
|
163
|
+
return
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (this.#batch.length > 0 &&
|
|
167
|
+
(this.#batchSource !== source || this.#batchBytes + messageBytes + 1 > MAX_BATCH_BYTES)) {
|
|
168
|
+
this.#flushLogs()
|
|
169
|
+
if (!this.#logSubmissionUrl) return
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
this.#batchSource = source
|
|
173
|
+
if (this.#batch.length > 0) this.#batchBytes++
|
|
174
|
+
this.#batch.push(serializedMessage)
|
|
175
|
+
this.#batchBytes += messageBytes
|
|
176
|
+
|
|
177
|
+
if (this.#batch.length === MAX_BATCH_LOGS || this.#batchBytes === MAX_BATCH_BYTES) {
|
|
178
|
+
this.#flushLogs()
|
|
179
|
+
} else if (this.#timer === undefined) {
|
|
180
|
+
this.#timer = setTimeout(() => this.#flushLogs(), BATCH_FLUSH_INTERVAL)
|
|
181
|
+
this.#timer.unref?.()
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* @returns {void}
|
|
187
|
+
*/
|
|
188
|
+
#flushLogs () {
|
|
189
|
+
clearTimeout(this.#timer)
|
|
190
|
+
this.#timer = undefined
|
|
191
|
+
|
|
192
|
+
if (this.#batch.length === 0 || !this.#logSubmissionUrl) return
|
|
193
|
+
|
|
194
|
+
const source = this.#batchSource
|
|
195
|
+
const data = `[${this.#batch.join(',')}]`
|
|
196
|
+
this.#batch = []
|
|
197
|
+
this.#batchBytes = 2
|
|
198
|
+
this.#batchSource = undefined
|
|
199
|
+
const options = {
|
|
200
|
+
path: getLogSubmissionPath(this.config, source),
|
|
201
|
+
method: 'POST',
|
|
202
|
+
headers: {
|
|
203
|
+
'DD-API-KEY': this.config.DD_API_KEY,
|
|
204
|
+
'Content-Type': 'application/json',
|
|
205
|
+
},
|
|
206
|
+
url: this.#logSubmissionUrl,
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
try {
|
|
210
|
+
this.#requestTracker.send(request, data, options, error => {
|
|
211
|
+
if (error) log.error('Error submitting %s logs', source, error)
|
|
212
|
+
})
|
|
213
|
+
} catch (error) {
|
|
214
|
+
this.#logSubmissionUrl = undefined
|
|
215
|
+
log.error('Error submitting %s logs', source, error)
|
|
216
|
+
}
|
|
217
|
+
}
|
|
51
218
|
}
|
|
52
219
|
|
|
53
220
|
module.exports = LogSubmissionPlugin
|
|
@@ -272,6 +272,7 @@ export interface GeneratedConfig {
|
|
|
272
272
|
DD_TRACE_HAPI_ENABLED: boolean;
|
|
273
273
|
DD_TRACE_HAPI_HAPI_ENABLED: boolean;
|
|
274
274
|
DD_TRACE_HONO_ENABLED: boolean;
|
|
275
|
+
DD_TRACE_HTTP_CLIENT_ERROR_STATUSES: string;
|
|
275
276
|
DD_TRACE_HTTP_ENABLED: boolean;
|
|
276
277
|
DD_TRACE_HTTP_SERVER_ERROR_STATUSES: string;
|
|
277
278
|
DD_TRACE_HTTP2_ENABLED: boolean;
|
|
@@ -418,6 +419,7 @@ export interface GeneratedConfig {
|
|
|
418
419
|
endpoint: string | undefined;
|
|
419
420
|
maxContentSize: number;
|
|
420
421
|
maxMessagesLength: number;
|
|
422
|
+
redactionEnabled: boolean;
|
|
421
423
|
timeout: number;
|
|
422
424
|
};
|
|
423
425
|
appsec: {
|
|
@@ -473,6 +475,7 @@ export interface GeneratedConfig {
|
|
|
473
475
|
agentlessEnabled: boolean | undefined;
|
|
474
476
|
DD_LLMOBS_ENABLED: boolean;
|
|
475
477
|
mlApp: string | undefined;
|
|
478
|
+
projectName: string | undefined;
|
|
476
479
|
sampleRate: number;
|
|
477
480
|
};
|
|
478
481
|
logInjection: boolean;
|
|
@@ -614,6 +617,7 @@ export interface GeneratedEnvVarConfig {
|
|
|
614
617
|
DD_AI_GUARD_ENDPOINT: string | undefined;
|
|
615
618
|
DD_AI_GUARD_MAX_CONTENT_SIZE: number;
|
|
616
619
|
DD_AI_GUARD_MAX_MESSAGES_LENGTH: number;
|
|
620
|
+
DD_AI_GUARD_REDACTION_ENABLED: boolean;
|
|
617
621
|
DD_AI_GUARD_TIMEOUT: number;
|
|
618
622
|
DD_API_KEY: string | undefined;
|
|
619
623
|
DD_API_SECURITY_DOWNSTREAM_BODY_ANALYSIS_SAMPLE_RATE: number;
|
|
@@ -758,6 +762,7 @@ export interface GeneratedEnvVarConfig {
|
|
|
758
762
|
DD_LLMOBS_AGENTLESS_ENABLED: boolean | undefined;
|
|
759
763
|
DD_LLMOBS_ENABLED: boolean;
|
|
760
764
|
DD_LLMOBS_ML_APP: string | undefined;
|
|
765
|
+
DD_LLMOBS_PROJECT_NAME: string | undefined;
|
|
761
766
|
DD_LLMOBS_SAMPLE_RATE: number;
|
|
762
767
|
DD_LOG_LEVEL: "debug" | "info" | "warn" | "error";
|
|
763
768
|
DD_LOGS_INJECTION: boolean;
|
|
@@ -970,6 +975,7 @@ export interface GeneratedEnvVarConfig {
|
|
|
970
975
|
DD_TRACE_HAPI_HAPI_ENABLED: boolean;
|
|
971
976
|
DD_TRACE_HEADER_TAGS: string[];
|
|
972
977
|
DD_TRACE_HONO_ENABLED: boolean;
|
|
978
|
+
DD_TRACE_HTTP_CLIENT_ERROR_STATUSES: string;
|
|
973
979
|
DD_TRACE_HTTP_ENABLED: boolean;
|
|
974
980
|
DD_TRACE_HTTP_SERVER_ERROR_STATUSES: string;
|
|
975
981
|
DD_TRACE_HTTP2_ENABLED: boolean;
|
|
@@ -510,7 +510,8 @@ class Config extends ConfigBase {
|
|
|
510
510
|
if (!this.llmobs.DD_LLMOBS_ENABLED &&
|
|
511
511
|
!trackedConfigOrigins.has('llmobs.DD_LLMOBS_ENABLED') &&
|
|
512
512
|
(trackedConfigOrigins.has('llmobs.agentlessEnabled') ||
|
|
513
|
-
trackedConfigOrigins.has('llmobs.mlApp')
|
|
513
|
+
trackedConfigOrigins.has('llmobs.mlApp') ||
|
|
514
|
+
trackedConfigOrigins.has('llmobs.projectName'))) {
|
|
514
515
|
setAndTrack(this, 'llmobs.DD_LLMOBS_ENABLED', true)
|
|
515
516
|
}
|
|
516
517
|
|
|
@@ -93,6 +93,16 @@
|
|
|
93
93
|
"default": "16"
|
|
94
94
|
}
|
|
95
95
|
],
|
|
96
|
+
"DD_AI_GUARD_REDACTION_ENABLED": [
|
|
97
|
+
{
|
|
98
|
+
"implementation": "A",
|
|
99
|
+
"type": "boolean",
|
|
100
|
+
"configurationNames": [
|
|
101
|
+
"experimental.aiguard.redactionEnabled"
|
|
102
|
+
],
|
|
103
|
+
"default": "true"
|
|
104
|
+
}
|
|
105
|
+
],
|
|
96
106
|
"DD_AI_GUARD_TIMEOUT": [
|
|
97
107
|
{
|
|
98
108
|
"implementation": "A",
|
|
@@ -1341,6 +1351,16 @@
|
|
|
1341
1351
|
"default": null
|
|
1342
1352
|
}
|
|
1343
1353
|
],
|
|
1354
|
+
"DD_LLMOBS_PROJECT_NAME": [
|
|
1355
|
+
{
|
|
1356
|
+
"implementation": "A",
|
|
1357
|
+
"type": "string",
|
|
1358
|
+
"configurationNames": [
|
|
1359
|
+
"llmobs.projectName"
|
|
1360
|
+
],
|
|
1361
|
+
"default": null
|
|
1362
|
+
}
|
|
1363
|
+
],
|
|
1344
1364
|
"DD_LLMOBS_SAMPLE_RATE": [
|
|
1345
1365
|
{
|
|
1346
1366
|
"implementation": "A",
|
|
@@ -2999,6 +3019,13 @@
|
|
|
2999
3019
|
"default": "true"
|
|
3000
3020
|
}
|
|
3001
3021
|
],
|
|
3022
|
+
"DD_TRACE_HTTP_CLIENT_ERROR_STATUSES": [
|
|
3023
|
+
{
|
|
3024
|
+
"implementation": "A",
|
|
3025
|
+
"type": "string",
|
|
3026
|
+
"default": "400-499"
|
|
3027
|
+
}
|
|
3028
|
+
],
|
|
3002
3029
|
"DD_TRACE_HTTP_ENABLED": [
|
|
3003
3030
|
{
|
|
3004
3031
|
"implementation": "A",
|
|
@@ -141,7 +141,7 @@ function setInputPath (newPath) {
|
|
|
141
141
|
* @returns {string} The serialized tags.
|
|
142
142
|
*/
|
|
143
143
|
function buildTags (tags) {
|
|
144
|
-
|
|
144
|
+
let serializedTags = ''
|
|
145
145
|
|
|
146
146
|
for (const [key, rawValue] of tags) {
|
|
147
147
|
if (rawValue === undefined) continue
|
|
@@ -151,8 +151,9 @@ function buildTags (tags) {
|
|
|
151
151
|
continue
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
serializedTags
|
|
154
|
+
if (serializedTags) serializedTags += ','
|
|
155
|
+
serializedTags += `${key}:${rawValue}`
|
|
155
156
|
}
|
|
156
157
|
|
|
157
|
-
return serializedTags
|
|
158
|
+
return serializedTags
|
|
158
159
|
}
|