dd-trace 5.87.0 → 5.89.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 +60 -32
- package/ext/exporters.d.ts +1 -0
- package/ext/exporters.js +1 -0
- package/ext/tags.js +2 -0
- package/index.d.ts +234 -4
- package/package.json +18 -11
- package/packages/datadog-instrumentations/src/ai.js +54 -90
- package/packages/datadog-instrumentations/src/helpers/hook.js +17 -11
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +27 -110
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +103 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/bullmq.js +108 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/compiler.js +74 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/index.js +43 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/matcher.js +49 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/transformer.js +121 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/{transforms.js → orchestrion/transforms.js} +143 -17
- package/packages/datadog-instrumentations/src/jest.js +176 -54
- package/packages/datadog-instrumentations/src/kafkajs.js +20 -17
- package/packages/datadog-instrumentations/src/playwright.js +1 -1
- package/packages/datadog-plugin-amqplib/src/consumer.js +14 -10
- package/packages/datadog-plugin-amqplib/src/producer.js +23 -19
- package/packages/datadog-plugin-bullmq/src/consumer.js +33 -11
- package/packages/datadog-plugin-bullmq/src/producer.js +60 -31
- package/packages/datadog-plugin-cucumber/src/index.js +9 -6
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +62 -5
- package/packages/datadog-plugin-cypress/src/source-map-utils.js +297 -0
- package/packages/datadog-plugin-cypress/src/support.js +52 -9
- package/packages/datadog-plugin-jest/src/index.js +12 -2
- package/packages/datadog-plugin-jest/src/util.js +2 -1
- package/packages/datadog-plugin-kafkajs/src/consumer.js +22 -12
- package/packages/datadog-plugin-kafkajs/src/producer.js +33 -22
- package/packages/datadog-plugin-mocha/src/index.js +9 -6
- package/packages/datadog-plugin-playwright/src/index.js +10 -6
- package/packages/datadog-plugin-vitest/src/index.js +13 -8
- package/packages/dd-trace/src/aiguard/sdk.js +5 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/cookie-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/ssrf-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/unvalidated-redirect-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/vulnerability-analyzer.js +4 -5
- package/packages/dd-trace/src/appsec/iast/path-line.js +36 -25
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/command-sensitive-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +3 -4
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +3 -2
- package/packages/dd-trace/src/azure_metadata.js +0 -2
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +3 -0
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +1 -1
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -1
- package/packages/dd-trace/src/ci-visibility/requests/request.js +236 -0
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +1 -1
- package/packages/dd-trace/src/config/defaults.js +148 -197
- package/packages/dd-trace/src/config/helper.js +43 -1
- package/packages/dd-trace/src/config/index.js +38 -14
- package/packages/dd-trace/src/config/supported-configurations.json +4125 -512
- package/packages/dd-trace/src/constants.js +0 -2
- package/packages/dd-trace/src/crashtracking/crashtracker.js +10 -3
- package/packages/dd-trace/src/datastreams/checkpointer.js +13 -0
- package/packages/dd-trace/src/datastreams/index.js +3 -0
- package/packages/dd-trace/src/datastreams/manager.js +9 -0
- package/packages/dd-trace/src/datastreams/pathway.js +22 -3
- package/packages/dd-trace/src/datastreams/processor.js +140 -4
- package/packages/dd-trace/src/encode/agentless-json.js +155 -0
- package/packages/dd-trace/src/exporter.js +2 -0
- package/packages/dd-trace/src/exporters/agent/writer.js +21 -8
- package/packages/dd-trace/src/exporters/agentless/index.js +89 -0
- package/packages/dd-trace/src/exporters/agentless/writer.js +184 -0
- package/packages/dd-trace/src/exporters/common/request.js +4 -4
- package/packages/dd-trace/src/llmobs/plugins/ai/index.js +5 -3
- package/packages/dd-trace/src/opentelemetry/context_manager.js +19 -46
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +3 -4
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +3 -5
- package/packages/dd-trace/src/opentracing/span.js +6 -4
- package/packages/dd-trace/src/pkg.js +1 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +57 -5
- package/packages/dd-trace/src/plugins/database.js +15 -2
- package/packages/dd-trace/src/plugins/util/test.js +48 -0
- package/packages/dd-trace/src/profiling/exporter_cli.js +1 -0
- package/packages/dd-trace/src/propagation-hash/index.js +145 -0
- package/packages/dd-trace/src/proxy.js +6 -1
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +1 -1
- package/packages/dd-trace/src/startup-log.js +53 -19
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/@datadog/source-map/index.js +1 -1
- package/vendor/dist/@isaacs/ttlcache/index.js +1 -1
- package/vendor/dist/@opentelemetry/core/index.js +1 -1
- package/vendor/dist/@opentelemetry/resources/index.js +1 -1
- package/vendor/dist/astring/index.js +1 -1
- package/vendor/dist/crypto-randomuuid/index.js +1 -1
- package/vendor/dist/escape-string-regexp/index.js +1 -1
- package/vendor/dist/esquery/index.js +1 -1
- package/vendor/dist/ignore/index.js +1 -1
- package/vendor/dist/istanbul-lib-coverage/index.js +1 -1
- package/vendor/dist/jest-docblock/index.js +1 -1
- package/vendor/dist/jsonpath-plus/index.js +1 -1
- package/vendor/dist/limiter/index.js +1 -1
- package/vendor/dist/lodash.sortby/index.js +1 -1
- package/vendor/dist/lru-cache/index.js +1 -1
- package/vendor/dist/meriyah/index.js +1 -1
- package/vendor/dist/module-details-from-path/index.js +1 -1
- package/vendor/dist/mutexify/promise/index.js +1 -1
- package/vendor/dist/opentracing/index.js +1 -1
- package/vendor/dist/path-to-regexp/index.js +1 -1
- package/vendor/dist/pprof-format/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/vendor/dist/retry/index.js +1 -1
- package/vendor/dist/rfdc/index.js +1 -1
- package/vendor/dist/semifies/index.js +1 -1
- package/vendor/dist/shell-quote/index.js +1 -1
- package/vendor/dist/source-map/index.js +1 -1
- package/vendor/dist/source-map/lib/util/index.js +1 -1
- package/vendor/dist/tlhunter-sorted-set/index.js +1 -1
- package/vendor/dist/ttl-set/index.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +0 -33
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/bullmq.json +0 -106
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secrets-rules.js +0 -741
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-regex.js +0 -11
- package/packages/dd-trace/src/scope/noop/scope.js +0 -21
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { getValueFromEnvSources } = require('../../config/helper')
|
|
4
|
+
const log = require('../../log')
|
|
5
|
+
const request = require('../common/request')
|
|
6
|
+
const tracerVersion = require('../../../../../package.json').version
|
|
7
|
+
|
|
8
|
+
const BaseWriter = require('../common/writer')
|
|
9
|
+
const { AgentlessJSONEncoder } = require('../../encode/agentless-json')
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Writer for agentless APM span intake.
|
|
13
|
+
* Sends spans directly to the Datadog intake endpoint without an agent.
|
|
14
|
+
*/
|
|
15
|
+
class AgentlessWriter extends BaseWriter {
|
|
16
|
+
#apiKeyMissing = false
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param {object} options - Writer options
|
|
20
|
+
* @param {URL} [options.url] - The intake URL. If not provided, constructed from site.
|
|
21
|
+
* @param {string} [options.site='datadoghq.com'] - The Datadog site
|
|
22
|
+
*/
|
|
23
|
+
constructor ({ url, site = 'datadoghq.com' }) {
|
|
24
|
+
super({ url })
|
|
25
|
+
this._encoder = new AgentlessJSONEncoder()
|
|
26
|
+
|
|
27
|
+
if (!url) {
|
|
28
|
+
try {
|
|
29
|
+
this._url = new URL(`https://public-trace-http-intake.logs.${site}`)
|
|
30
|
+
} catch (err) {
|
|
31
|
+
log.error(
|
|
32
|
+
'Invalid site value for agentless intake: %s. Cannot construct URL. Error: %s',
|
|
33
|
+
site,
|
|
34
|
+
err.message
|
|
35
|
+
)
|
|
36
|
+
this._url = null
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!getValueFromEnvSources('DD_API_KEY')) {
|
|
41
|
+
this.#apiKeyMissing = true
|
|
42
|
+
log.error('DD_API_KEY is required for agentless span intake. Set DD_API_KEY. Spans will not be sent.')
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Flushes the current trace. Since we flush after each trace, this sends
|
|
48
|
+
* a single request.
|
|
49
|
+
* @param {Function} [done] - Callback when send completes
|
|
50
|
+
*/
|
|
51
|
+
flush (done = () => {}) {
|
|
52
|
+
if (!request.writable) {
|
|
53
|
+
this._encoder.reset()
|
|
54
|
+
done()
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const count = this._encoder.count()
|
|
59
|
+
|
|
60
|
+
if (count === 0) {
|
|
61
|
+
done()
|
|
62
|
+
return
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const payload = this._encoder.makePayload()
|
|
66
|
+
|
|
67
|
+
if (payload.length === 0) {
|
|
68
|
+
log.debug('Skipping send of empty payload')
|
|
69
|
+
done()
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
this._sendPayload(payload, count, done)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Sends the encoded payload to the intake endpoint.
|
|
78
|
+
* @param {Buffer} data - The encoded JSON payload
|
|
79
|
+
* @param {number} count - Number of spans in the payload
|
|
80
|
+
* @param {Function} done - Callback when complete
|
|
81
|
+
*/
|
|
82
|
+
_sendPayload (data, count, done) {
|
|
83
|
+
if (!data || data.length === 0) {
|
|
84
|
+
log.debug('Skipping send of empty payload')
|
|
85
|
+
done()
|
|
86
|
+
return
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (!this._url) {
|
|
90
|
+
log.debug('Skipping send due to invalid URL configuration')
|
|
91
|
+
done()
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const apiKey = getValueFromEnvSources('DD_API_KEY')
|
|
96
|
+
if (!apiKey) {
|
|
97
|
+
if (!this.#apiKeyMissing) {
|
|
98
|
+
this.#apiKeyMissing = true
|
|
99
|
+
log.error('DD_API_KEY is required for agentless span intake. Set DD_API_KEY. Spans will not be sent.')
|
|
100
|
+
}
|
|
101
|
+
log.debug('Dropping %d span(s) due to missing DD_API_KEY', count)
|
|
102
|
+
done()
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
this.#apiKeyMissing = false
|
|
106
|
+
|
|
107
|
+
const options = {
|
|
108
|
+
path: '/v1/input',
|
|
109
|
+
method: 'POST',
|
|
110
|
+
headers: {
|
|
111
|
+
'Content-Type': 'application/json',
|
|
112
|
+
'dd-api-key': apiKey,
|
|
113
|
+
'Datadog-Meta-Lang': 'nodejs',
|
|
114
|
+
'Datadog-Meta-Lang-Version': process.version,
|
|
115
|
+
'Datadog-Meta-Lang-Interpreter': process.versions.bun ? 'JavaScriptCore' : 'v8',
|
|
116
|
+
'Datadog-Meta-Tracer-Version': tracerVersion,
|
|
117
|
+
},
|
|
118
|
+
timeout: 15_000,
|
|
119
|
+
url: this._url,
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
log.debug('Request to the agentless intake: %j', options)
|
|
123
|
+
|
|
124
|
+
request(data, options, (err, res, statusCode) => {
|
|
125
|
+
if (err) {
|
|
126
|
+
this._logRequestError(err, statusCode, count)
|
|
127
|
+
done()
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
log.debug('Response from the agentless intake: %s', res)
|
|
132
|
+
done()
|
|
133
|
+
})
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Logs request errors with status-specific guidance.
|
|
138
|
+
* @param {Error} err - The error object
|
|
139
|
+
* @param {number} statusCode - HTTP status code (if available)
|
|
140
|
+
* @param {number} count - Number of spans that were being sent
|
|
141
|
+
*/
|
|
142
|
+
_logRequestError (err, statusCode, count) {
|
|
143
|
+
if (statusCode === 401 || statusCode === 403) {
|
|
144
|
+
log.error(
|
|
145
|
+
'Authentication failed sending %d span(s) (status %s). Verify DD_API_KEY is valid.',
|
|
146
|
+
count,
|
|
147
|
+
statusCode
|
|
148
|
+
)
|
|
149
|
+
} else if (statusCode === 404) {
|
|
150
|
+
log.error(
|
|
151
|
+
'Span intake endpoint not found (status %s). Verify DD_SITE is correctly configured. %d span(s) dropped.',
|
|
152
|
+
statusCode,
|
|
153
|
+
count
|
|
154
|
+
)
|
|
155
|
+
} else if (statusCode === 429) {
|
|
156
|
+
log.error(
|
|
157
|
+
'Rate limited by span intake (status 429). %d span(s) dropped.',
|
|
158
|
+
count
|
|
159
|
+
)
|
|
160
|
+
} else if (statusCode >= 500) {
|
|
161
|
+
log.error(
|
|
162
|
+
'Span intake server error (status %s). %d span(s) dropped. This may be transient.',
|
|
163
|
+
statusCode,
|
|
164
|
+
count
|
|
165
|
+
)
|
|
166
|
+
} else if (statusCode) {
|
|
167
|
+
log.error(
|
|
168
|
+
'Error sending agentless payload (status %s): %s. %d span(s) dropped.',
|
|
169
|
+
statusCode,
|
|
170
|
+
err.message,
|
|
171
|
+
count
|
|
172
|
+
)
|
|
173
|
+
} else {
|
|
174
|
+
log.error(
|
|
175
|
+
'Network error sending %d span(s) to %s: %s',
|
|
176
|
+
count,
|
|
177
|
+
this._url?.hostname || 'unknown',
|
|
178
|
+
err.message
|
|
179
|
+
)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
module.exports = AgentlessWriter
|
|
@@ -88,13 +88,13 @@ function request (data, options, callback) {
|
|
|
88
88
|
zlib.gunzip(buffer, (err, result) => {
|
|
89
89
|
if (err) {
|
|
90
90
|
log.error('Could not gunzip response: %s', err.message)
|
|
91
|
-
callback(null, '', res.statusCode)
|
|
91
|
+
callback(null, '', res.statusCode, res.headers)
|
|
92
92
|
} else {
|
|
93
|
-
callback(null, result.toString(), res.statusCode)
|
|
93
|
+
callback(null, result.toString(), res.statusCode, res.headers)
|
|
94
94
|
}
|
|
95
95
|
})
|
|
96
96
|
} else {
|
|
97
|
-
callback(null, buffer.toString(), res.statusCode)
|
|
97
|
+
callback(null, buffer.toString(), res.statusCode, res.headers)
|
|
98
98
|
}
|
|
99
99
|
} else {
|
|
100
100
|
let errorMessage = ''
|
|
@@ -115,7 +115,7 @@ function request (data, options, callback) {
|
|
|
115
115
|
const error = new log.NoTransmitError(errorMessage)
|
|
116
116
|
error.status = res.statusCode
|
|
117
117
|
|
|
118
|
-
callback(error, null, res.statusCode)
|
|
118
|
+
callback(error, null, res.statusCode, res.headers)
|
|
119
119
|
}
|
|
120
120
|
})
|
|
121
121
|
}
|
|
@@ -4,7 +4,7 @@ const { channel } = require('dc-polyfill')
|
|
|
4
4
|
const BaseLLMObsPlugin = require('../base')
|
|
5
5
|
const { getModelProvider } = require('../../../../../datadog-plugin-ai/src/utils')
|
|
6
6
|
|
|
7
|
-
const toolCreationCh = channel('
|
|
7
|
+
const toolCreationCh = channel('tracing:orchestrion:ai:tool:start')
|
|
8
8
|
const setAttributesCh = channel('dd-trace:vercel-ai:span:setAttributes')
|
|
9
9
|
|
|
10
10
|
const { MODEL_NAME, MODEL_PROVIDER, NAME } = require('../../constants/tags')
|
|
@@ -94,8 +94,10 @@ class VercelAILLMObsPlugin extends BaseLLMObsPlugin {
|
|
|
94
94
|
|
|
95
95
|
this.#toolCallIdsToName = {}
|
|
96
96
|
this.#availableTools = new Set()
|
|
97
|
-
toolCreationCh.subscribe(
|
|
98
|
-
|
|
97
|
+
toolCreationCh.subscribe(ctx => {
|
|
98
|
+
const toolArgs = ctx.arguments
|
|
99
|
+
const tool = toolArgs[0] ?? {}
|
|
100
|
+
this.#availableTools.add(tool)
|
|
99
101
|
})
|
|
100
102
|
|
|
101
103
|
setAttributesCh.subscribe(({ ctx, attributes }) => {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { trace, ROOT_CONTEXT, propagation } = require('@opentelemetry/api')
|
|
4
4
|
const { storage } = require('../../../datadog-core')
|
|
5
|
+
const { getAllBaggageItems, setBaggageItem, removeAllBaggageItems } = require('../baggage')
|
|
5
6
|
|
|
6
7
|
const tracer = require('../../')
|
|
7
8
|
const SpanContext = require('./span_context')
|
|
@@ -19,31 +20,26 @@ class ContextManager {
|
|
|
19
20
|
|
|
20
21
|
const storedSpan = store ? trace.getSpan(store) : null
|
|
21
22
|
|
|
23
|
+
// Convert DD baggage to OTel format
|
|
24
|
+
const baggages = getAllBaggageItems()
|
|
25
|
+
const hasBaggage = Object.keys(baggages).length > 0
|
|
26
|
+
let otelBaggages
|
|
27
|
+
if (hasBaggage) {
|
|
28
|
+
const entries = {}
|
|
29
|
+
for (const [key, value] of Object.entries(baggages)) {
|
|
30
|
+
entries[key] = { value }
|
|
31
|
+
}
|
|
32
|
+
otelBaggages = propagation.createBaggage(entries)
|
|
33
|
+
}
|
|
34
|
+
|
|
22
35
|
// If stored span wraps the active DD span, prefer the stored context
|
|
23
36
|
if (storedSpan && storedSpan._ddSpan === activeSpan) {
|
|
24
|
-
|
|
25
|
-
if (Object.keys(baggages).length > 0) {
|
|
26
|
-
const entries = {}
|
|
27
|
-
for (const [key, value] of Object.entries(baggages)) {
|
|
28
|
-
entries[key] = { value }
|
|
29
|
-
}
|
|
30
|
-
const otelBaggages = propagation.createBaggage(entries)
|
|
31
|
-
return propagation.setBaggage(store, otelBaggages)
|
|
32
|
-
}
|
|
37
|
+
if (otelBaggages) return propagation.setBaggage(store, otelBaggages)
|
|
33
38
|
return store
|
|
34
39
|
}
|
|
35
40
|
|
|
36
41
|
if (!activeSpan) {
|
|
37
|
-
|
|
38
|
-
if (storedBaggageItems) {
|
|
39
|
-
const baggages = storedBaggageItems
|
|
40
|
-
const entries = {}
|
|
41
|
-
for (const [key, value] of Object.entries(baggages)) {
|
|
42
|
-
entries[key] = { value }
|
|
43
|
-
}
|
|
44
|
-
const otelBaggages = propagation.createBaggage(entries)
|
|
45
|
-
return propagation.setBaggage(baseContext, otelBaggages)
|
|
46
|
-
}
|
|
42
|
+
if (otelBaggages) return propagation.setBaggage(baseContext, otelBaggages)
|
|
47
43
|
return baseContext
|
|
48
44
|
}
|
|
49
45
|
|
|
@@ -53,18 +49,6 @@ class ContextManager {
|
|
|
53
49
|
ddContext._otelSpanContext = new SpanContext(ddContext)
|
|
54
50
|
}
|
|
55
51
|
|
|
56
|
-
// Convert DD baggage to OTel format
|
|
57
|
-
const baggages = JSON.parse(activeSpan.getAllBaggageItems())
|
|
58
|
-
const hasBaggage = Object.keys(baggages).length > 0
|
|
59
|
-
let otelBaggages
|
|
60
|
-
if (hasBaggage) {
|
|
61
|
-
const entries = {}
|
|
62
|
-
for (const [key, value] of Object.entries(baggages)) {
|
|
63
|
-
entries[key] = { value }
|
|
64
|
-
}
|
|
65
|
-
otelBaggages = propagation.createBaggage(entries)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
52
|
if (store && trace.getSpanContext(store) === ddContext._otelSpanContext) {
|
|
69
53
|
return otelBaggages ? propagation.setBaggage(store, otelBaggages) : store
|
|
70
54
|
}
|
|
@@ -86,22 +70,11 @@ class ContextManager {
|
|
|
86
70
|
if (baggages) {
|
|
87
71
|
baggageItems = baggages.getAllEntries()
|
|
88
72
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
for (const baggage of baggageItems) {
|
|
93
|
-
span._ddSpan.setBaggageItem(baggage[0], baggage[1].value)
|
|
94
|
-
}
|
|
95
|
-
return ddScope.activate(span._ddSpan, run)
|
|
96
|
-
}
|
|
97
|
-
// span instanceof NonRecordingSpan
|
|
98
|
-
const ddContext = span?._spanContext?._ddContext
|
|
99
|
-
if (ddContext && ddContext._baggageItems) {
|
|
100
|
-
ddContext._baggageItems = {}
|
|
101
|
-
for (const baggage of baggageItems) {
|
|
102
|
-
ddContext._baggageItems[baggage[0]] = baggage[1].value
|
|
103
|
-
}
|
|
73
|
+
removeAllBaggageItems()
|
|
74
|
+
for (const baggage of baggageItems) {
|
|
75
|
+
setBaggageItem(baggage[0], baggage[1].value)
|
|
104
76
|
}
|
|
77
|
+
if (span && span._ddSpan) return ddScope.activate(span._ddSpan, run)
|
|
105
78
|
return run()
|
|
106
79
|
}
|
|
107
80
|
|
|
@@ -20,7 +20,7 @@ class OtlpHttpExporterBase {
|
|
|
20
20
|
* Creates a new OtlpHttpExporterBase instance.
|
|
21
21
|
*
|
|
22
22
|
* @param {string} url - OTLP endpoint URL
|
|
23
|
-
* @param {string} headers - Additional HTTP headers as comma-separated key=value string
|
|
23
|
+
* @param {string|undefined} headers - Additional HTTP headers as comma-separated key=value string
|
|
24
24
|
* @param {number} timeout - Request timeout in milliseconds
|
|
25
25
|
* @param {string} protocol - OTLP protocol (http/protobuf or http/json)
|
|
26
26
|
* @param {string} defaultPath - Default path to use if URL has no path
|
|
@@ -117,11 +117,10 @@ class OtlpHttpExporterBase {
|
|
|
117
117
|
|
|
118
118
|
/**
|
|
119
119
|
* Parses additional HTTP headers from a comma-separated string.
|
|
120
|
-
* @param {string} headersString - Comma-separated key=value pairs
|
|
120
|
+
* @param {string} [headersString=''] - Comma-separated key=value pairs
|
|
121
121
|
* @returns {Record<string, string>} Parsed headers object
|
|
122
|
-
* @private
|
|
123
122
|
*/
|
|
124
|
-
#parseAdditionalHeaders (headersString) {
|
|
123
|
+
#parseAdditionalHeaders (headersString = '') {
|
|
125
124
|
const headers = {}
|
|
126
125
|
let key = ''
|
|
127
126
|
let value = ''
|
|
@@ -670,10 +670,8 @@ class TextMapPropagator {
|
|
|
670
670
|
if (!this._hasPropagationStyle('extract', 'baggage')) return
|
|
671
671
|
if (!carrier?.baggage) return
|
|
672
672
|
const baggages = carrier.baggage.split(',')
|
|
673
|
-
const
|
|
674
|
-
const
|
|
675
|
-
? undefined
|
|
676
|
-
: new Set(this._config.baggageTagKeys.split(','))
|
|
673
|
+
const baggageTagKeys = new Set(this._config.baggageTagKeys)
|
|
674
|
+
const tagAllKeys = baggageTagKeys.has('*')
|
|
677
675
|
for (const keyValue of baggages) {
|
|
678
676
|
if (!keyValue) continue
|
|
679
677
|
|
|
@@ -707,7 +705,7 @@ class TextMapPropagator {
|
|
|
707
705
|
return
|
|
708
706
|
}
|
|
709
707
|
|
|
710
|
-
if (spanContext && (tagAllKeys ||
|
|
708
|
+
if (spanContext && (tagAllKeys || baggageTagKeys.has(key))) {
|
|
711
709
|
spanContext._trace.tags['baggage.' + key] = value
|
|
712
710
|
}
|
|
713
711
|
setBaggageItem(key, value)
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
// TODO (new internal tracer): use DC events for lifecycle metrics and test them
|
|
4
4
|
const { performance } = require('perf_hooks')
|
|
5
5
|
const now = performance.now.bind(performance)
|
|
6
|
-
const dateNow = Date.now
|
|
7
6
|
const util = require('util')
|
|
8
7
|
const { channel } = require('dc-polyfill')
|
|
9
8
|
const id = require('../id')
|
|
@@ -13,12 +12,15 @@ const log = require('../log')
|
|
|
13
12
|
const { storage } = require('../../../datadog-core')
|
|
14
13
|
const telemetryMetrics = require('../telemetry/metrics')
|
|
15
14
|
const { getValueFromEnvSources } = require('../config/helper')
|
|
15
|
+
const { isTrue } = require('../util')
|
|
16
16
|
const SpanContext = require('./span_context')
|
|
17
17
|
|
|
18
|
+
const dateNow = Date.now
|
|
19
|
+
|
|
18
20
|
const tracerMetrics = telemetryMetrics.manager.namespace('tracers')
|
|
19
21
|
|
|
20
|
-
const DD_TRACE_EXPERIMENTAL_STATE_TRACKING = getValueFromEnvSources('DD_TRACE_EXPERIMENTAL_STATE_TRACKING')
|
|
21
|
-
const DD_TRACE_EXPERIMENTAL_SPAN_COUNTS = getValueFromEnvSources('DD_TRACE_EXPERIMENTAL_SPAN_COUNTS')
|
|
22
|
+
const DD_TRACE_EXPERIMENTAL_STATE_TRACKING = isTrue(getValueFromEnvSources('DD_TRACE_EXPERIMENTAL_STATE_TRACKING'))
|
|
23
|
+
const DD_TRACE_EXPERIMENTAL_SPAN_COUNTS = isTrue(getValueFromEnvSources('DD_TRACE_EXPERIMENTAL_SPAN_COUNTS'))
|
|
22
24
|
|
|
23
25
|
const unfinishedRegistry = createRegistry('unfinished')
|
|
24
26
|
const finishedRegistry = createRegistry('finished')
|
|
@@ -251,7 +253,7 @@ class DatadogSpan {
|
|
|
251
253
|
return
|
|
252
254
|
}
|
|
253
255
|
|
|
254
|
-
if (DD_TRACE_EXPERIMENTAL_STATE_TRACKING
|
|
256
|
+
if (DD_TRACE_EXPERIMENTAL_STATE_TRACKING && !this._spanContext._tags['service.name']) {
|
|
255
257
|
log.error('Finishing invalid span: %s', this)
|
|
256
258
|
}
|
|
257
259
|
|
|
@@ -63,6 +63,8 @@ const {
|
|
|
63
63
|
getPullRequestDiff,
|
|
64
64
|
getModifiedFilesFromDiff,
|
|
65
65
|
getPullRequestBaseBranch,
|
|
66
|
+
getSessionRequestErrorTags,
|
|
67
|
+
DD_CI_LIBRARY_CONFIGURATION_ERROR,
|
|
66
68
|
TEST_IS_TEST_FRAMEWORK_WORKER,
|
|
67
69
|
TEST_IS_NEW,
|
|
68
70
|
TEST_IS_RUM_ACTIVE,
|
|
@@ -120,6 +122,7 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
120
122
|
this.fileLineToProbeId = new Map()
|
|
121
123
|
this.rootDir = process.cwd() // fallback in case :session:start events are not emitted
|
|
122
124
|
this._testSuiteSpansByTestSuite = new Map()
|
|
125
|
+
this._pendingRequestErrorTags = []
|
|
123
126
|
|
|
124
127
|
this.addSub(`ci:${this.constructor.id}:library-configuration`, (ctx) => {
|
|
125
128
|
const { onDone, isParallel, frameworkVersion } = ctx
|
|
@@ -131,10 +134,15 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
131
134
|
this.tracer._exporter.getLibraryConfiguration(this.testConfiguration, (err, libraryConfig) => {
|
|
132
135
|
if (err) {
|
|
133
136
|
log.error('Library configuration could not be fetched. %s', err.message)
|
|
137
|
+
this._addRequestErrorTag(DD_CI_LIBRARY_CONFIGURATION_ERROR, err)
|
|
134
138
|
} else {
|
|
135
139
|
this.libraryConfig = libraryConfig
|
|
136
140
|
}
|
|
137
141
|
|
|
142
|
+
const requestErrorTags = this.testSessionSpan
|
|
143
|
+
? getSessionRequestErrorTags(this.testSessionSpan)
|
|
144
|
+
: Object.fromEntries(this._pendingRequestErrorTags.map(({ tag, value }) => [tag, value]))
|
|
145
|
+
|
|
138
146
|
const libraryCapabilitiesTags = getLibraryCapabilitiesTags(this.constructor.id, isParallel, frameworkVersion)
|
|
139
147
|
const metadataTags = {
|
|
140
148
|
test: {
|
|
@@ -142,7 +150,7 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
142
150
|
},
|
|
143
151
|
}
|
|
144
152
|
this.tracer._exporter.addMetadataTags(metadataTags)
|
|
145
|
-
onDone({ err, libraryConfig })
|
|
153
|
+
onDone({ err, libraryConfig, requestErrorTags })
|
|
146
154
|
})
|
|
147
155
|
})
|
|
148
156
|
|
|
@@ -200,6 +208,10 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
200
208
|
},
|
|
201
209
|
integrationName: this.constructor.id,
|
|
202
210
|
})
|
|
211
|
+
for (const { tag, value } of this._pendingRequestErrorTags) {
|
|
212
|
+
this.testSessionSpan.setTag(tag, value)
|
|
213
|
+
}
|
|
214
|
+
this._pendingRequestErrorTags = []
|
|
203
215
|
// TODO: add telemetry tag when we can add `is_agentless_log_submission_enabled` for agentless log submission
|
|
204
216
|
this.telemetry.ciVisEvent(TELEMETRY_EVENT_CREATED, 'session')
|
|
205
217
|
|
|
@@ -209,6 +221,7 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
209
221
|
[COMPONENT]: this.constructor.id,
|
|
210
222
|
...this.testEnvironmentMetadata,
|
|
211
223
|
...testModuleSpanMetadata,
|
|
224
|
+
...getSessionRequestErrorTags(this.testSessionSpan),
|
|
212
225
|
},
|
|
213
226
|
integrationName: this.constructor.id,
|
|
214
227
|
})
|
|
@@ -230,7 +243,10 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
230
243
|
this.addSub(`ci:${this.constructor.id}:itr:skipped-suites`, ({ skippedSuites, frameworkVersion }) => {
|
|
231
244
|
const testCommand = this.testSessionSpan.context()._tags[TEST_COMMAND]
|
|
232
245
|
for (const testSuite of skippedSuites) {
|
|
233
|
-
const testSuiteMetadata =
|
|
246
|
+
const testSuiteMetadata = {
|
|
247
|
+
...getTestSuiteCommonTags(testCommand, frameworkVersion, testSuite, this.constructor.id),
|
|
248
|
+
...getSessionRequestErrorTags(this.testSessionSpan),
|
|
249
|
+
}
|
|
234
250
|
if (this.itrCorrelationId) {
|
|
235
251
|
testSuiteMetadata[ITR_CORRELATION_ID] = this.itrCorrelationId
|
|
236
252
|
}
|
|
@@ -261,8 +277,10 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
261
277
|
this.tracer._exporter.getKnownTests(this.testConfiguration, (err, knownTests) => {
|
|
262
278
|
if (err) {
|
|
263
279
|
log.error('Known tests could not be fetched. %s', err.message)
|
|
264
|
-
this.libraryConfig
|
|
265
|
-
|
|
280
|
+
if (this.libraryConfig) {
|
|
281
|
+
this.libraryConfig.isEarlyFlakeDetectionEnabled = false
|
|
282
|
+
this.libraryConfig.isKnownTestsEnabled = false
|
|
283
|
+
}
|
|
266
284
|
}
|
|
267
285
|
onDone({ err, knownTests })
|
|
268
286
|
})
|
|
@@ -279,7 +297,9 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
279
297
|
this.tracer._exporter.getTestManagementTests(this.testConfiguration, (err, testManagementTests) => {
|
|
280
298
|
if (err) {
|
|
281
299
|
log.error('Test management tests could not be fetched. %s', err.message)
|
|
282
|
-
this.libraryConfig
|
|
300
|
+
if (this.libraryConfig) {
|
|
301
|
+
this.libraryConfig.isTestManagementEnabled = false
|
|
302
|
+
}
|
|
283
303
|
}
|
|
284
304
|
onDone({ err, testManagementTests })
|
|
285
305
|
})
|
|
@@ -346,8 +366,15 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
346
366
|
span.meta = {
|
|
347
367
|
...span.meta,
|
|
348
368
|
...testSuiteTags,
|
|
369
|
+
...getSessionRequestErrorTags(this.testSessionSpan),
|
|
349
370
|
}
|
|
350
371
|
}
|
|
372
|
+
|
|
373
|
+
// Jest and Vitest worker test spans are serialized in the worker and may not include
|
|
374
|
+
// request error tags; add them from the session span in the main process.
|
|
375
|
+
if ((span.name === 'jest.test' || span.name === 'vitest.test') && this.testSessionSpan) {
|
|
376
|
+
Object.assign(span.meta, getSessionRequestErrorTags(this.testSessionSpan))
|
|
377
|
+
}
|
|
351
378
|
}
|
|
352
379
|
this.tracer._exporter.export(trace)
|
|
353
380
|
}
|
|
@@ -418,6 +445,30 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
418
445
|
}
|
|
419
446
|
}
|
|
420
447
|
|
|
448
|
+
/**
|
|
449
|
+
* Adds a hidden _dd tag to the test session span when a test-optimization request fails.
|
|
450
|
+
* If the session span does not exist yet (e.g. library-configuration failed before session:start),
|
|
451
|
+
* the tag is queued and applied when the span is created.
|
|
452
|
+
* @param {string} tag - Tag name (e.g. DD_CI_LIBRARY_CONFIGURATION_ERROR)
|
|
453
|
+
* @param {Error} err - Request error
|
|
454
|
+
*/
|
|
455
|
+
_addRequestErrorTag (tag, err) {
|
|
456
|
+
const value = 'true'
|
|
457
|
+
if (this.testSessionSpan) {
|
|
458
|
+
this.testSessionSpan.setTag(tag, value)
|
|
459
|
+
} else {
|
|
460
|
+
this._pendingRequestErrorTags.push({ tag, value })
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Returns request error tags from the test session span for propagation to module, suite and test spans.
|
|
466
|
+
* @returns {Record<string, string>}
|
|
467
|
+
*/
|
|
468
|
+
getSessionRequestErrorTags () {
|
|
469
|
+
return getSessionRequestErrorTags(this.testSessionSpan)
|
|
470
|
+
}
|
|
471
|
+
|
|
421
472
|
configure (config, shouldGetEnvironmentData = true) {
|
|
422
473
|
super.configure(config)
|
|
423
474
|
|
|
@@ -529,6 +580,7 @@ module.exports = class CiPlugin extends Plugin {
|
|
|
529
580
|
[TEST_SESSION_ID]: testSuiteSpan.context().toTraceId(),
|
|
530
581
|
[TEST_COMMAND]: testSuiteSpan.context()._tags[TEST_COMMAND],
|
|
531
582
|
[TEST_MODULE]: this.constructor.id,
|
|
583
|
+
...getSessionRequestErrorTags(this.testSessionSpan),
|
|
532
584
|
}
|
|
533
585
|
if (testSuiteSpan.context()._parentId) {
|
|
534
586
|
suiteTags[TEST_MODULE_ID] = testSuiteSpan.context()._parentId.toString(10)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { PEER_SERVICE_KEY, PEER_SERVICE_SOURCE_KEY } = require('../constants')
|
|
4
|
+
const propagationHash = require('../propagation-hash')
|
|
4
5
|
const StoragePlugin = require('./storage')
|
|
5
6
|
|
|
6
7
|
class DatabasePlugin extends StoragePlugin {
|
|
@@ -59,13 +60,25 @@ class DatabasePlugin extends StoragePlugin {
|
|
|
59
60
|
const dbmService = this.#getDbmServiceName(serviceName, peerData)
|
|
60
61
|
const servicePropagation = this.#createDBMPropagationCommentService(dbmService, span, peerData)
|
|
61
62
|
|
|
63
|
+
let dbmComment = servicePropagation
|
|
64
|
+
|
|
65
|
+
// Add propagation hash if both process tags and SQL base hash injection are enabled
|
|
66
|
+
if (propagationHash.isEnabled() && this.config['dbm.injectSqlBaseHash']) {
|
|
67
|
+
const hashBase64 = propagationHash.getHashBase64()
|
|
68
|
+
if (hashBase64) {
|
|
69
|
+
dbmComment += `,ddsh='${hashBase64}'`
|
|
70
|
+
// Add hash to span meta as a tag
|
|
71
|
+
span.setTag('_dd.dbm.propagation_hash', hashBase64)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
62
75
|
if (disableFullMode || mode === 'service') {
|
|
63
|
-
return
|
|
76
|
+
return dbmComment
|
|
64
77
|
} else if (mode === 'full') {
|
|
65
78
|
span.setTag('_dd.dbm_trace_injected', 'true')
|
|
66
79
|
span._processor.sample(span)
|
|
67
80
|
const traceparent = span._spanContext.toTraceparent()
|
|
68
|
-
return `${
|
|
81
|
+
return `${dbmComment},traceparent='${traceparent}'`
|
|
69
82
|
}
|
|
70
83
|
}
|
|
71
84
|
|