dd-trace 5.82.0 → 5.83.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 +78 -79
- package/ci/init.js +6 -6
- package/index.d.ts +152 -3
- package/loader-hook.mjs +1 -1
- package/package.json +58 -55
- package/packages/datadog-core/src/storage.js +7 -7
- package/packages/datadog-esbuild/index.js +6 -0
- package/packages/datadog-instrumentations/src/ai.js +7 -3
- package/packages/datadog-instrumentations/src/child_process.js +1 -1
- package/packages/datadog-instrumentations/src/cucumber.js +1 -1
- package/packages/datadog-instrumentations/src/graphql.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/instrumentations.js +4 -3
- package/packages/datadog-instrumentations/src/helpers/register.js +3 -7
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +1 -1
- package/packages/datadog-instrumentations/src/http/client.js +2 -2
- package/packages/datadog-instrumentations/src/jest.js +35 -14
- package/packages/datadog-instrumentations/src/koa.js +2 -1
- package/packages/datadog-instrumentations/src/light-my-request.js +2 -2
- package/packages/datadog-instrumentations/src/mocha/main.js +2 -2
- package/packages/datadog-instrumentations/src/mocha/worker.js +1 -1
- package/packages/datadog-instrumentations/src/mocha.js +1 -1
- package/packages/datadog-instrumentations/src/mysql.js +1 -1
- package/packages/datadog-instrumentations/src/mysql2.js +2 -2
- package/packages/datadog-instrumentations/src/net.js +13 -5
- package/packages/datadog-instrumentations/src/nyc.js +1 -1
- package/packages/datadog-instrumentations/src/otel-sdk-trace.js +4 -4
- package/packages/datadog-instrumentations/src/pg.js +4 -2
- package/packages/datadog-instrumentations/src/playwright.js +3 -3
- package/packages/datadog-instrumentations/src/selenium.js +2 -2
- package/packages/datadog-instrumentations/src/undici.js +12 -1
- package/packages/datadog-plugin-aws-sdk/src/base.js +4 -4
- package/packages/datadog-plugin-azure-event-hubs/src/producer.js +2 -2
- package/packages/datadog-plugin-azure-service-bus/src/producer.js +2 -2
- package/packages/datadog-plugin-cucumber/src/index.js +2 -2
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +2 -2
- package/packages/datadog-plugin-dd-trace-api/src/index.js +2 -2
- package/packages/datadog-plugin-express/src/code_origin.js +21 -15
- package/packages/datadog-plugin-fastify/src/code_origin.js +17 -4
- package/packages/datadog-plugin-jest/src/index.js +2 -2
- package/packages/datadog-plugin-mocha/src/index.js +2 -2
- package/packages/datadog-plugin-mongodb-core/src/index.js +2 -2
- package/packages/datadog-plugin-playwright/src/index.js +3 -3
- package/packages/datadog-plugin-undici/src/index.js +305 -2
- package/packages/datadog-plugin-vitest/src/index.js +5 -5
- package/packages/dd-trace/index.js +19 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +1 -1
- package/packages/dd-trace/src/appsec/rasp/index.js +2 -4
- package/packages/dd-trace/src/azure_metadata.js +8 -3
- package/packages/dd-trace/src/baggage.js +36 -11
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +5 -1
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +3 -2
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +3 -3
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +4 -4
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -2
- package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +4 -4
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -4
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +2 -2
- package/packages/dd-trace/src/{config_defaults.js → config/defaults.js} +3 -3
- package/packages/dd-trace/src/{config-helper.js → config/helper.js} +88 -15
- package/packages/dd-trace/src/{config.js → config/index.js} +92 -45
- package/packages/dd-trace/src/config/remote_config.js +187 -19
- package/packages/dd-trace/src/{config_stable.js → config/stable.js} +20 -32
- package/packages/dd-trace/src/{supported-configurations.json → config/supported-configurations.json} +2 -0
- package/packages/dd-trace/src/crashtracking/crashtracker.js +1 -1
- package/packages/dd-trace/src/datastreams/processor.js +1 -1
- package/packages/dd-trace/src/datastreams/writer.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/condition.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/config.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/send.js +3 -3
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/constants.js +1 -1
- package/packages/dd-trace/src/debugger/index.js +83 -15
- package/packages/dd-trace/src/dogstatsd.js +2 -2
- package/packages/dd-trace/src/encode/0.4.js +2 -2
- package/packages/dd-trace/src/exporter.js +1 -1
- package/packages/dd-trace/src/exporters/agent/index.js +2 -4
- package/packages/dd-trace/src/exporters/agent/writer.js +9 -14
- package/packages/dd-trace/src/exporters/common/agent-info-exporter.js +1 -1
- package/packages/dd-trace/src/exporters/common/docker.js +2 -2
- package/packages/dd-trace/src/exporters/common/request.js +1 -1
- package/packages/dd-trace/src/exporters/common/util.js +2 -2
- package/packages/dd-trace/src/exporters/span-stats/index.js +1 -1
- package/packages/dd-trace/src/flare/index.js +1 -1
- package/packages/dd-trace/src/guardrails/telemetry.js +1 -1
- package/packages/dd-trace/src/index.js +4 -4
- package/packages/dd-trace/src/lambda/handler.js +2 -2
- package/packages/dd-trace/src/lambda/index.js +2 -2
- package/packages/dd-trace/src/lambda/runtime/patch.js +2 -2
- package/packages/dd-trace/src/lambda/runtime/ritm.js +2 -2
- package/packages/dd-trace/src/llmobs/constants/tags.js +8 -1
- package/packages/dd-trace/src/llmobs/index.js +2 -2
- package/packages/dd-trace/src/llmobs/noop.js +2 -0
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +3 -4
- package/packages/dd-trace/src/llmobs/sdk.js +33 -6
- package/packages/dd-trace/src/llmobs/span_processor.js +17 -7
- package/packages/dd-trace/src/llmobs/tagger.js +175 -1
- package/packages/dd-trace/src/llmobs/writers/base.js +116 -37
- package/packages/dd-trace/src/llmobs/writers/spans.js +4 -3
- package/packages/dd-trace/src/log/index.js +5 -5
- package/packages/dd-trace/src/noop/proxy.js +3 -3
- package/packages/dd-trace/src/openfeature/writers/base.js +7 -8
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +2 -2
- package/packages/dd-trace/src/opentelemetry/tracer.js +48 -6
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +45 -21
- package/packages/dd-trace/src/opentracing/span.js +4 -4
- package/packages/dd-trace/src/plugin_manager.js +8 -6
- package/packages/dd-trace/src/plugins/util/ci.js +5 -8
- package/packages/dd-trace/src/plugins/util/git-cache.js +3 -3
- package/packages/dd-trace/src/plugins/util/test.js +1 -1
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +41 -43
- package/packages/dd-trace/src/profiler.js +4 -39
- package/packages/dd-trace/src/profiling/config.js +74 -31
- package/packages/dd-trace/src/profiling/exporter_cli.js +5 -5
- package/packages/dd-trace/src/profiling/exporters/agent.js +1 -1
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +9 -2
- package/packages/dd-trace/src/profiling/index.js +1 -1
- package/packages/dd-trace/src/profiling/libuv-size.js +1 -1
- package/packages/dd-trace/src/profiling/profiler.js +57 -2
- package/packages/dd-trace/src/proxy.js +34 -5
- package/packages/dd-trace/src/remote_config/capabilities.js +3 -0
- package/packages/dd-trace/src/remote_config/index.js +1 -1
- package/packages/dd-trace/src/ritm.js +8 -4
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +2 -2
- package/packages/dd-trace/src/serverless.js +2 -2
- package/packages/dd-trace/src/span_processor.js +2 -2
- package/packages/dd-trace/src/startup-log.js +6 -15
- package/packages/dd-trace/src/telemetry/endpoints.js +67 -5
- package/packages/dd-trace/src/telemetry/send-data.js +103 -4
- package/packages/dd-trace/src/telemetry/telemetry.js +229 -110
- /package/packages/dd-trace/src/{git_properties.js → config/git_properties.js} +0 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const { URL, format } = require('node:url')
|
|
4
4
|
const path = require('node:path')
|
|
5
5
|
const request = require('../../exporters/common/request')
|
|
6
|
-
const { getEnvironmentVariable } = require('../../config
|
|
6
|
+
const { getEnvironmentVariable } = require('../../config/helper')
|
|
7
7
|
|
|
8
8
|
const logger = require('../../log')
|
|
9
9
|
|
|
@@ -16,85 +16,163 @@ const {
|
|
|
16
16
|
} = require('../constants/writers')
|
|
17
17
|
const { parseResponseAndLog } = require('./util')
|
|
18
18
|
|
|
19
|
+
class LLMObsBuffer {
|
|
20
|
+
constructor ({ events, size, routing = {}, isDefault = false, limit = 1000 }) {
|
|
21
|
+
this.events = events
|
|
22
|
+
this.size = size
|
|
23
|
+
this.routing = routing
|
|
24
|
+
this.isDefault = isDefault
|
|
25
|
+
this.limit = limit
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
clear () {
|
|
29
|
+
this.events = []
|
|
30
|
+
this.size = 0
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
19
34
|
class BaseLLMObsWriter {
|
|
35
|
+
#destroyer
|
|
36
|
+
/** @type {Map<string, LLMObsBuffer>} */
|
|
37
|
+
#multiTenantBuffers = new Map()
|
|
38
|
+
|
|
20
39
|
constructor ({ interval, timeout, eventType, config, endpoint, intake }) {
|
|
21
40
|
this._interval = interval ?? getEnvironmentVariable('_DD_LLMOBS_FLUSH_INTERVAL') ?? 1000 // 1s
|
|
22
41
|
this._timeout = timeout ?? getEnvironmentVariable('_DD_LLMOBS_TIMEOUT') ?? 5000 // 5s
|
|
23
42
|
this._eventType = eventType
|
|
24
43
|
|
|
25
|
-
|
|
26
|
-
this.
|
|
27
|
-
this._bufferSize = 0
|
|
44
|
+
/** @type {LLMObsBuffer} */
|
|
45
|
+
this._buffer = new LLMObsBuffer({ events: [], size: 0, isDefault: true })
|
|
28
46
|
|
|
29
47
|
this._config = config
|
|
30
48
|
this._endpoint = endpoint
|
|
49
|
+
this._baseEndpoint = endpoint // should not be unset
|
|
31
50
|
this._intake = intake
|
|
32
51
|
|
|
33
52
|
this._periodic = setInterval(() => {
|
|
34
53
|
this.flush()
|
|
35
54
|
}, this._interval).unref()
|
|
36
55
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
56
|
+
const destroyer = this.destroy.bind(this)
|
|
57
|
+
globalThis[Symbol.for('dd-trace')].beforeExitHandlers.add(destroyer)
|
|
58
|
+
|
|
59
|
+
this.#destroyer = destroyer
|
|
60
|
+
}
|
|
41
61
|
|
|
42
|
-
|
|
62
|
+
// Split on protocol separator to preserve it
|
|
63
|
+
// path.join will remove some slashes unnecessarily
|
|
64
|
+
#buildUrl (baseUrl, endpoint) {
|
|
65
|
+
const [protocol, rest] = baseUrl.split('://')
|
|
66
|
+
return protocol + '://' + path.join(rest, endpoint)
|
|
43
67
|
}
|
|
44
68
|
|
|
45
69
|
get url () {
|
|
46
70
|
if (this._agentless == null) return null
|
|
71
|
+
return this.#buildUrl(this._baseUrl.href, this._endpoint)
|
|
72
|
+
}
|
|
47
73
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
74
|
+
_getBuffer (routing) {
|
|
75
|
+
if (!routing?.apiKey) {
|
|
76
|
+
return this._buffer
|
|
77
|
+
}
|
|
78
|
+
const apiKey = routing.apiKey
|
|
79
|
+
let buffer = this.#multiTenantBuffers.get(apiKey)
|
|
80
|
+
if (!buffer) {
|
|
81
|
+
buffer = new LLMObsBuffer({ events: [], size: 0, routing })
|
|
82
|
+
this.#multiTenantBuffers.set(apiKey, buffer)
|
|
83
|
+
}
|
|
84
|
+
return buffer
|
|
55
85
|
}
|
|
56
86
|
|
|
57
|
-
append (event, byteLength) {
|
|
58
|
-
|
|
59
|
-
|
|
87
|
+
append (event, routing, byteLength) {
|
|
88
|
+
const buffer = this._getBuffer(routing)
|
|
89
|
+
|
|
90
|
+
if (buffer.events.length >= buffer.limit) {
|
|
91
|
+
logger.warn(`${this.constructor.name} event buffer full (limit is ${buffer.limit}), dropping event`)
|
|
60
92
|
telemetry.recordDroppedPayload(1, this._eventType, 'buffer_full')
|
|
61
93
|
return
|
|
62
94
|
}
|
|
63
95
|
|
|
64
|
-
|
|
65
|
-
|
|
96
|
+
const eventSize = byteLength || Buffer.byteLength(JSON.stringify(event))
|
|
97
|
+
|
|
98
|
+
buffer.size += eventSize
|
|
99
|
+
buffer.events.push(event)
|
|
66
100
|
}
|
|
67
101
|
|
|
68
102
|
flush () {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (this._buffer.length === 0 || noAgentStrategy) {
|
|
103
|
+
if (this._agentless == null) {
|
|
72
104
|
return
|
|
73
105
|
}
|
|
74
106
|
|
|
75
|
-
|
|
76
|
-
this._buffer
|
|
77
|
-
|
|
78
|
-
|
|
107
|
+
// Flush default buffer
|
|
108
|
+
if (this._buffer.events.length > 0) {
|
|
109
|
+
const events = this._buffer.events
|
|
110
|
+
this._buffer.clear()
|
|
111
|
+
|
|
112
|
+
const payload = this._encode(this.makePayload(events))
|
|
79
113
|
|
|
80
|
-
|
|
114
|
+
log.debug('Encoded LLMObs payload: %s', payload)
|
|
81
115
|
|
|
82
|
-
|
|
116
|
+
const options = this._getOptions()
|
|
117
|
+
|
|
118
|
+
request(payload, options, (err, resp, code) => {
|
|
119
|
+
parseResponseAndLog(err, code, events.length, this.url, this._eventType)
|
|
120
|
+
})
|
|
121
|
+
}
|
|
83
122
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
123
|
+
// Flush multi-tenant buffers
|
|
124
|
+
for (const [apiKey, buffer] of this.#multiTenantBuffers) {
|
|
125
|
+
if (buffer.events.length === 0) continue
|
|
126
|
+
|
|
127
|
+
const events = buffer.events
|
|
128
|
+
buffer.clear()
|
|
129
|
+
|
|
130
|
+
const payload = this._encode(this.makePayload(events))
|
|
131
|
+
const site = buffer.routing.site || this._config.site
|
|
132
|
+
const options = {
|
|
133
|
+
headers: {
|
|
134
|
+
'Content-Type': 'application/json',
|
|
135
|
+
'DD-API-KEY': apiKey
|
|
136
|
+
},
|
|
137
|
+
method: 'POST',
|
|
138
|
+
timeout: this._timeout,
|
|
139
|
+
url: new URL(format({
|
|
140
|
+
protocol: 'https:',
|
|
141
|
+
hostname: `${this._intake}.${site}`
|
|
142
|
+
})),
|
|
143
|
+
path: this._baseEndpoint
|
|
144
|
+
}
|
|
145
|
+
const url = this.#buildUrl(options.url.href, options.path)
|
|
146
|
+
const maskedApiKey = apiKey ? `****${apiKey.slice(-4)}` : ''
|
|
147
|
+
|
|
148
|
+
log.debug('Encoding and flushing multi-tenant buffer for %s', maskedApiKey)
|
|
149
|
+
log.debug('Encoded LLMObs payload: %s', payload)
|
|
150
|
+
|
|
151
|
+
request(payload, options, (err, resp, code) => {
|
|
152
|
+
parseResponseAndLog(err, code, events.length, url, this._eventType)
|
|
153
|
+
})
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
this.#cleanupEmptyBuffers()
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
#cleanupEmptyBuffers () {
|
|
160
|
+
for (const [key, buffer] of this.#multiTenantBuffers) {
|
|
161
|
+
if (buffer.events.length === 0) {
|
|
162
|
+
this.#multiTenantBuffers.delete(key)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
87
165
|
}
|
|
88
166
|
|
|
89
167
|
makePayload (events) {}
|
|
90
168
|
|
|
91
169
|
destroy () {
|
|
92
|
-
if (
|
|
170
|
+
if (this.#destroyer) {
|
|
93
171
|
logger.debug(`Stopping ${this.constructor.name}`)
|
|
94
172
|
clearInterval(this._periodic)
|
|
95
|
-
|
|
173
|
+
globalThis[Symbol.for('dd-trace')].beforeExitHandlers.delete(this.#destroyer)
|
|
96
174
|
this.flush()
|
|
97
|
-
this
|
|
175
|
+
this.#destroyer = undefined
|
|
98
176
|
}
|
|
99
177
|
}
|
|
100
178
|
|
|
@@ -110,10 +188,11 @@ class BaseLLMObsWriter {
|
|
|
110
188
|
|
|
111
189
|
_getUrlAndPath () {
|
|
112
190
|
if (this._agentless) {
|
|
191
|
+
const site = this._config.site
|
|
113
192
|
return {
|
|
114
193
|
url: new URL(format({
|
|
115
194
|
protocol: 'https:',
|
|
116
|
-
hostname: `${this._intake}.${
|
|
195
|
+
hostname: `${this._intake}.${site}`
|
|
117
196
|
})),
|
|
118
197
|
endpoint: this._endpoint
|
|
119
198
|
}
|
|
@@ -24,7 +24,7 @@ class LLMObsSpanWriter extends BaseWriter {
|
|
|
24
24
|
})
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
append (event) {
|
|
27
|
+
append (event, routing) {
|
|
28
28
|
const eventSizeBytes = Buffer.byteLength(JSON.stringify(event))
|
|
29
29
|
telemetry.recordLLMObsRawSpanSize(event, eventSizeBytes)
|
|
30
30
|
|
|
@@ -39,12 +39,13 @@ class LLMObsSpanWriter extends BaseWriter {
|
|
|
39
39
|
|
|
40
40
|
telemetry.recordLLMObsSpanSize(event, processedEventSizeBytes, shouldTruncate)
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
const buffer = this._getBuffer(routing)
|
|
43
|
+
if (buffer.size + processedEventSizeBytes > EVP_PAYLOAD_SIZE_LIMIT) {
|
|
43
44
|
logger.debug('Flushing queue because queuing next event will exceed EvP payload limit')
|
|
44
45
|
this.flush()
|
|
45
46
|
}
|
|
46
47
|
|
|
47
|
-
super.append(event, processedEventSizeBytes)
|
|
48
|
+
super.append(event, routing, processedEventSizeBytes)
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
makePayload (events) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
const { inspect } = require('util')
|
|
3
3
|
const { isTrue } = require('../util')
|
|
4
|
-
const {
|
|
4
|
+
const { getValueFromEnvSources } = require('../config/helper')
|
|
5
5
|
const { traceChannel, debugChannel, infoChannel, warnChannel, errorChannel } = require('./channels')
|
|
6
6
|
const logWriter = require('./writer')
|
|
7
7
|
const { Log, LogConfig, NoTransmitError } = require('./log')
|
|
@@ -112,8 +112,8 @@ const log = {
|
|
|
112
112
|
isEnabled (fleetStableConfigValue, localStableConfigValue) {
|
|
113
113
|
return isTrue(
|
|
114
114
|
fleetStableConfigValue ??
|
|
115
|
-
|
|
116
|
-
(
|
|
115
|
+
getValueFromEnvSources('DD_TRACE_DEBUG') ??
|
|
116
|
+
(getValueFromEnvSources('OTEL_LOG_LEVEL') === 'debug' || undefined) ??
|
|
117
117
|
localStableConfigValue ??
|
|
118
118
|
config.enabled
|
|
119
119
|
)
|
|
@@ -126,8 +126,8 @@ const log = {
|
|
|
126
126
|
) {
|
|
127
127
|
return optionsValue ??
|
|
128
128
|
fleetStableConfigValue ??
|
|
129
|
-
|
|
130
|
-
|
|
129
|
+
getValueFromEnvSources('DD_TRACE_LOG_LEVEL') ??
|
|
130
|
+
getValueFromEnvSources('OTEL_LOG_LEVEL') ??
|
|
131
131
|
localStableConfigValue ??
|
|
132
132
|
config.logLevel
|
|
133
133
|
}
|
|
@@ -23,10 +23,10 @@ class NoopProxy {
|
|
|
23
23
|
this.llmobs = noopLLMObs
|
|
24
24
|
this.openfeature = noopOpenFeatureProvider
|
|
25
25
|
this.aiguard = noopAIGuard
|
|
26
|
-
this.setBaggageItem = () => {}
|
|
27
|
-
this.getBaggageItem = () => {}
|
|
26
|
+
this.setBaggageItem = (key, value) => {}
|
|
27
|
+
this.getBaggageItem = (key) => {}
|
|
28
28
|
this.getAllBaggageItems = () => {}
|
|
29
|
-
this.removeBaggageItem = () => {}
|
|
29
|
+
this.removeBaggageItem = (keyToRemove) => {}
|
|
30
30
|
this.removeAllBaggageItems = () => {}
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -23,6 +23,7 @@ const log = require('../../log')
|
|
|
23
23
|
* @class BaseFFEWriter
|
|
24
24
|
*/
|
|
25
25
|
class BaseFFEWriter {
|
|
26
|
+
#destroyer
|
|
26
27
|
/**
|
|
27
28
|
* @param {BaseFFEWriterOptions} options - Writer configuration options
|
|
28
29
|
*/
|
|
@@ -56,12 +57,10 @@ class BaseFFEWriter {
|
|
|
56
57
|
this.flush()
|
|
57
58
|
}, this._interval).unref()
|
|
58
59
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
process.once('beforeExit', this._beforeExitHandler)
|
|
60
|
+
const destroyer = this.destroy.bind(this)
|
|
61
|
+
globalThis[Symbol.for('dd-trace')].beforeExitHandlers.add(destroyer)
|
|
63
62
|
|
|
64
|
-
this
|
|
63
|
+
this.#destroyer = destroyer
|
|
65
64
|
this._droppedEvents = 0
|
|
66
65
|
}
|
|
67
66
|
|
|
@@ -141,12 +140,12 @@ class BaseFFEWriter {
|
|
|
141
140
|
* Cleans up resources and flushes remaining events
|
|
142
141
|
*/
|
|
143
142
|
destroy () {
|
|
144
|
-
if (
|
|
143
|
+
if (this.#destroyer) {
|
|
145
144
|
log.debug(() => `Stopping ${this.constructor.name}`)
|
|
146
145
|
clearInterval(this._periodic)
|
|
147
|
-
process.removeListener('beforeExit', this._beforeExitHandler)
|
|
148
146
|
this.flush()
|
|
149
|
-
this
|
|
147
|
+
globalThis[Symbol.for('dd-trace')].beforeExitHandlers.delete(this.#destroyer)
|
|
148
|
+
this.#destroyer = undefined
|
|
150
149
|
|
|
151
150
|
if (this._droppedEvents > 0) {
|
|
152
151
|
log.warn(`${this.constructor.name} dropped ${this._droppedEvents} events due to buffer overflow`)
|
|
@@ -90,7 +90,7 @@ class OtlpHttpExporterBase {
|
|
|
90
90
|
data += chunk
|
|
91
91
|
})
|
|
92
92
|
|
|
93
|
-
res.
|
|
93
|
+
res.once('end', () => {
|
|
94
94
|
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
95
95
|
resultCallback({ code: 0 })
|
|
96
96
|
} else {
|
|
@@ -105,7 +105,7 @@ class OtlpHttpExporterBase {
|
|
|
105
105
|
resultCallback({ code: 1, error })
|
|
106
106
|
})
|
|
107
107
|
|
|
108
|
-
req.
|
|
108
|
+
req.once('timeout', () => {
|
|
109
109
|
req.destroy()
|
|
110
110
|
const error = new Error('Request timeout')
|
|
111
111
|
resultCallback({ code: 1, error })
|
|
@@ -5,12 +5,48 @@ const { sanitizeAttributes } = require('../../../../vendor/dist/@opentelemetry/c
|
|
|
5
5
|
|
|
6
6
|
const id = require('../id')
|
|
7
7
|
const log = require('../log')
|
|
8
|
+
const DatadogSpanContext = require('../opentracing/span_context')
|
|
8
9
|
const TextMapPropagator = require('../opentracing/propagation/text_map')
|
|
9
10
|
const TraceState = require('../opentracing/propagation/tracestate')
|
|
10
11
|
const SpanContext = require('./span_context')
|
|
11
12
|
const Span = require('./span')
|
|
12
13
|
const Sampler = require('./sampler')
|
|
13
14
|
|
|
15
|
+
function normalizeLinkContext (context) {
|
|
16
|
+
if (!context) return
|
|
17
|
+
|
|
18
|
+
// OTel API bridge SpanContext wrapper
|
|
19
|
+
if (context._ddContext) return context._ddContext
|
|
20
|
+
|
|
21
|
+
// Datadog span context
|
|
22
|
+
if (typeof context.toTraceId === 'function' && typeof context.toSpanId === 'function') {
|
|
23
|
+
return context
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Standard OTel SpanContext (traceId/spanId)
|
|
27
|
+
if (typeof context.traceId !== 'string' || typeof context.spanId !== 'string') {
|
|
28
|
+
// Invalid
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let sampling
|
|
33
|
+
if (typeof context.traceFlags === 'number') {
|
|
34
|
+
sampling = { priority: context.traceFlags & 1 }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let tracestate
|
|
38
|
+
if (context.traceState?.serialize) {
|
|
39
|
+
tracestate = TraceState.fromString(context.traceState.serialize())
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return new DatadogSpanContext({
|
|
43
|
+
traceId: id(context.traceId, 16),
|
|
44
|
+
spanId: id(context.spanId, 16),
|
|
45
|
+
sampling,
|
|
46
|
+
tracestate
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
|
|
14
50
|
class Tracer {
|
|
15
51
|
constructor (library, config, tracerProvider) {
|
|
16
52
|
this._sampler = new Sampler()
|
|
@@ -96,7 +132,7 @@ class Tracer {
|
|
|
96
132
|
context = api.trace.deleteSpan(context)
|
|
97
133
|
}
|
|
98
134
|
const parentSpan = api.trace.getSpan(context)
|
|
99
|
-
const parentSpanContext = parentSpan
|
|
135
|
+
const parentSpanContext = parentSpan?.spanContext()
|
|
100
136
|
let spanContext
|
|
101
137
|
if (parentSpanContext && api.trace.isSpanContextValid(parentSpanContext)) {
|
|
102
138
|
spanContext = parentSpanContext._ddContext
|
|
@@ -107,12 +143,18 @@ class Tracer {
|
|
|
107
143
|
}
|
|
108
144
|
|
|
109
145
|
const spanKind = options.kind || api.SpanKind.INTERNAL
|
|
110
|
-
const links =
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
146
|
+
const links = []
|
|
147
|
+
if (options.links?.length) {
|
|
148
|
+
for (const link of options.links) {
|
|
149
|
+
const ddContext = normalizeLinkContext(link?.context)
|
|
150
|
+
if (!ddContext) continue
|
|
151
|
+
|
|
152
|
+
links.push({
|
|
153
|
+
context: ddContext,
|
|
154
|
+
attributes: sanitizeAttributes(link.attributes)
|
|
155
|
+
})
|
|
114
156
|
}
|
|
115
|
-
}
|
|
157
|
+
}
|
|
116
158
|
const attributes = sanitizeAttributes(options.attributes)
|
|
117
159
|
|
|
118
160
|
// TODO: sampling API is not yet supported
|
|
@@ -34,11 +34,19 @@ const b3HeaderKey = 'b3'
|
|
|
34
34
|
const sqsdHeaderHey = 'x-aws-sqsd-attr-_datadog'
|
|
35
35
|
const b3HeaderExpr = /^(([0-9a-f]{16}){1,2}-[0-9a-f]{16}(-[01d](-[0-9a-f]{16})?)?|[01d])$/i
|
|
36
36
|
const baggageExpr = new RegExp(`^${baggagePrefix}(.+)$`)
|
|
37
|
+
// W3C Baggage key grammar: key = token (RFC 7230).
|
|
38
|
+
// Spec (up-to-date): "Propagation format for distributed context: Baggage" §3.3.1
|
|
39
|
+
// https://www.w3.org/TR/baggage/#header-content
|
|
40
|
+
// https://www.rfc-editor.org/rfc/rfc7230#section-3.2.6
|
|
41
|
+
const baggageTokenExpr = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/
|
|
37
42
|
const tagKeyExpr = /^_dd\.p\.[\x21-\x2B\x2D-\x7E]+$/ // ASCII minus spaces and commas
|
|
38
43
|
const tagValueExpr = /^[\x20-\x2B\x2D-\x7E]*$/ // ASCII minus commas
|
|
39
44
|
// RFC7230 token (used by HTTP header field-name) and compatible with Node's header name validation.
|
|
40
45
|
// See https://www.rfc-editor.org/rfc/rfc7230#section-3.2.6
|
|
41
46
|
const httpHeaderNameExpr = /^[0-9A-Za-z!#$%&'*+\-.^_`|~]+$/
|
|
47
|
+
// Compatible with Node's internal header value validation (allows HTAB, SP-~, and \x80-\xFF only)
|
|
48
|
+
// https://github.com/nodejs/node/blob/main/lib/_http_common.js
|
|
49
|
+
const invalidHeaderValueCharExpr = /[^\t\x20-\x7E\x80-\xFF]/
|
|
42
50
|
const traceparentExpr = /^([a-f0-9]{2})-([a-f0-9]{32})-([a-f0-9]{16})-([a-f0-9]{2})(-.*)?$/i
|
|
43
51
|
const traceparentKey = 'traceparent'
|
|
44
52
|
const tracestateKey = 'tracestate'
|
|
@@ -124,13 +132,6 @@ class TextMapPropagator {
|
|
|
124
132
|
}
|
|
125
133
|
}
|
|
126
134
|
|
|
127
|
-
_encodeOtelBaggageKey (key) {
|
|
128
|
-
let encoded = encodeURIComponent(key)
|
|
129
|
-
encoded = encoded.replaceAll('(', '%28')
|
|
130
|
-
encoded = encoded.replaceAll(')', '%29')
|
|
131
|
-
return encoded
|
|
132
|
-
}
|
|
133
|
-
|
|
134
135
|
_injectBaggageItems (spanContext, carrier) {
|
|
135
136
|
if (this._config.legacyBaggageEnabled) {
|
|
136
137
|
const baggageItems = spanContext?._baggageItems
|
|
@@ -145,7 +146,12 @@ class TextMapPropagator {
|
|
|
145
146
|
continue
|
|
146
147
|
}
|
|
147
148
|
|
|
148
|
-
|
|
149
|
+
let headerValue = String(baggageItems[key])
|
|
150
|
+
// Avoid Node throwing ERR_INVALID_CHAR when setting header values (e.g. newline from decoded OTEL baggage).
|
|
151
|
+
if (invalidHeaderValueCharExpr.test(headerValue)) {
|
|
152
|
+
headerValue = encodeURIComponent(headerValue)
|
|
153
|
+
}
|
|
154
|
+
carrier[headerName] = headerValue
|
|
149
155
|
}
|
|
150
156
|
}
|
|
151
157
|
}
|
|
@@ -157,7 +163,13 @@ class TextMapPropagator {
|
|
|
157
163
|
const baggageItems = getAllBaggageItems()
|
|
158
164
|
if (!baggageItems) return
|
|
159
165
|
for (const [key, value] of Object.entries(baggageItems)) {
|
|
160
|
-
const
|
|
166
|
+
const baggageKey = String(key).trim()
|
|
167
|
+
if (!baggageKey || !baggageTokenExpr.test(baggageKey)) continue
|
|
168
|
+
|
|
169
|
+
// Do not trim values. If callers include leading/trailing whitespace, it must be percent-encoded.
|
|
170
|
+
// W3C list-member allows optional properties after ';'.
|
|
171
|
+
// https://www.w3.org/TR/baggage/#header-content
|
|
172
|
+
const item = `${baggageKey}=${encodeURIComponent(String(value))},`
|
|
161
173
|
itemCounter += 1
|
|
162
174
|
byteCounter += Buffer.byteLength(item)
|
|
163
175
|
|
|
@@ -640,13 +652,6 @@ class TextMapPropagator {
|
|
|
640
652
|
}
|
|
641
653
|
}
|
|
642
654
|
|
|
643
|
-
_decodeOtelBaggageKey (key) {
|
|
644
|
-
let decoded = decodeURIComponent(key)
|
|
645
|
-
decoded = decoded.replaceAll('%28', '(')
|
|
646
|
-
decoded = decoded.replaceAll('%29', ')')
|
|
647
|
-
return decoded
|
|
648
|
-
}
|
|
649
|
-
|
|
650
655
|
_extractLegacyBaggageItems (carrier, spanContext) {
|
|
651
656
|
if (this._config.legacyBaggageEnabled) {
|
|
652
657
|
Object.keys(carrier).forEach(key => {
|
|
@@ -668,19 +673,38 @@ class TextMapPropagator {
|
|
|
668
673
|
? undefined
|
|
669
674
|
: new Set(this._config.baggageTagKeys.split(','))
|
|
670
675
|
for (const keyValue of baggages) {
|
|
671
|
-
if (!keyValue
|
|
676
|
+
if (!keyValue) continue
|
|
677
|
+
|
|
678
|
+
// Per W3C baggage, list-members can contain optional properties after `;`.
|
|
679
|
+
// Example: key=value;prop=1;prop2
|
|
680
|
+
// https://www.w3.org/TR/baggage/#header-content
|
|
681
|
+
const semicolonIdx = keyValue.indexOf(';')
|
|
682
|
+
const member = (semicolonIdx === -1 ? keyValue : keyValue.slice(0, semicolonIdx)).trim()
|
|
683
|
+
if (!member) continue
|
|
684
|
+
|
|
685
|
+
const eqIdx = member.indexOf('=')
|
|
686
|
+
if (eqIdx === -1) {
|
|
672
687
|
tracerMetrics.count('context_header_style.malformed', ['header_style:baggage']).inc()
|
|
673
688
|
removeAllBaggageItems()
|
|
674
689
|
return
|
|
675
690
|
}
|
|
676
|
-
|
|
677
|
-
key =
|
|
678
|
-
value =
|
|
679
|
-
|
|
691
|
+
|
|
692
|
+
const key = member.slice(0, eqIdx).trim()
|
|
693
|
+
let value = member.slice(eqIdx + 1).trim()
|
|
694
|
+
|
|
695
|
+
if (!baggageTokenExpr.test(key) || !value) {
|
|
680
696
|
tracerMetrics.count('context_header_style.malformed', ['header_style:baggage']).inc()
|
|
681
697
|
removeAllBaggageItems()
|
|
682
698
|
return
|
|
683
699
|
}
|
|
700
|
+
try {
|
|
701
|
+
value = decodeURIComponent(value)
|
|
702
|
+
} catch {
|
|
703
|
+
tracerMetrics.count('context_header_style.malformed', ['header_style:baggage']).inc()
|
|
704
|
+
removeAllBaggageItems()
|
|
705
|
+
return
|
|
706
|
+
}
|
|
707
|
+
|
|
684
708
|
if (spanContext && (tagAllKeys || keysToSpanTag?.has(key))) {
|
|
685
709
|
spanContext._trace.tags['baggage.' + key] = value
|
|
686
710
|
}
|
|
@@ -12,18 +12,18 @@ const runtimeMetrics = require('../runtime_metrics')
|
|
|
12
12
|
const log = require('../log')
|
|
13
13
|
const { storage } = require('../../../datadog-core')
|
|
14
14
|
const telemetryMetrics = require('../telemetry/metrics')
|
|
15
|
-
const {
|
|
15
|
+
const { getValueFromEnvSources } = require('../config/helper')
|
|
16
16
|
const SpanContext = require('./span_context')
|
|
17
17
|
|
|
18
18
|
const tracerMetrics = telemetryMetrics.manager.namespace('tracers')
|
|
19
19
|
|
|
20
|
-
const DD_TRACE_EXPERIMENTAL_STATE_TRACKING =
|
|
21
|
-
const DD_TRACE_EXPERIMENTAL_SPAN_COUNTS =
|
|
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
22
|
|
|
23
23
|
const unfinishedRegistry = createRegistry('unfinished')
|
|
24
24
|
const finishedRegistry = createRegistry('finished')
|
|
25
25
|
|
|
26
|
-
const OTEL_ENABLED = !!
|
|
26
|
+
const OTEL_ENABLED = !!getValueFromEnvSources('DD_TRACE_OTEL_ENABLED')
|
|
27
27
|
const ALLOWED = new Set(['string', 'number', 'boolean'])
|
|
28
28
|
|
|
29
29
|
const integrationCounters = {
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { channel } = require('dc-polyfill')
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
const { getEnvironmentVariable, getValueFromEnvSources } = require('./config/helper')
|
|
5
6
|
const { isFalse, isTrue, normalizePluginEnvName } = require('./util')
|
|
6
7
|
const plugins = require('./plugins')
|
|
7
8
|
const log = require('./log')
|
|
@@ -24,7 +25,7 @@ if (getEnvironmentVariable('AWS_LAMBDA_FUNCTION_NAME') !== undefined) {
|
|
|
24
25
|
require('./lambda')
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
const DD_TRACE_DISABLED_PLUGINS =
|
|
28
|
+
const DD_TRACE_DISABLED_PLUGINS = getValueFromEnvSources('DD_TRACE_DISABLED_PLUGINS')
|
|
28
29
|
|
|
29
30
|
const disabledPlugins = new Set(
|
|
30
31
|
DD_TRACE_DISABLED_PLUGINS && DD_TRACE_DISABLED_PLUGINS.split(',').map(plugin => plugin.trim())
|
|
@@ -41,7 +42,7 @@ loadChannel.subscribe(({ name }) => {
|
|
|
41
42
|
function maybeEnable (Plugin) {
|
|
42
43
|
if (!Plugin || typeof Plugin !== 'function') return
|
|
43
44
|
if (!pluginClasses[Plugin.id]) {
|
|
44
|
-
const enabled =
|
|
45
|
+
const enabled = getEnabled(Plugin)
|
|
45
46
|
|
|
46
47
|
// TODO: remove the need to load the plugin class in order to disable the plugin
|
|
47
48
|
if (isFalse(enabled) || disabledPlugins.has(Plugin.id)) {
|
|
@@ -54,9 +55,9 @@ function maybeEnable (Plugin) {
|
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
57
|
|
|
57
|
-
function
|
|
58
|
+
function getEnabled (Plugin) {
|
|
58
59
|
const envName = `DD_TRACE_${Plugin.id.toUpperCase()}_ENABLED`
|
|
59
|
-
return
|
|
60
|
+
return getValueFromEnvSources(normalizePluginEnvName(envName))
|
|
60
61
|
}
|
|
61
62
|
|
|
62
63
|
// TODO this must always be a singleton.
|
|
@@ -94,7 +95,8 @@ module.exports = class PluginManager {
|
|
|
94
95
|
this._pluginsByName[name] = new Plugin(this._tracer, this._tracerConfig)
|
|
95
96
|
}
|
|
96
97
|
const pluginConfig = this._configsByName[name] || {
|
|
97
|
-
enabled: this._tracerConfig.plugins !== false &&
|
|
98
|
+
enabled: this._tracerConfig.plugins !== false &&
|
|
99
|
+
(!Plugin.experimental || isTrue(getEnabled(Plugin)))
|
|
98
100
|
}
|
|
99
101
|
|
|
100
102
|
// extracts predetermined configuration from tracer and combines it with plugin-specific config
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { readFileSync } = require('fs')
|
|
4
|
-
const { getEnvironmentVariable, getEnvironmentVariables } = require('../../config
|
|
4
|
+
const { getEnvironmentVariable, getEnvironmentVariables, getValueFromEnvSources } = require('../../config/helper')
|
|
5
5
|
const {
|
|
6
6
|
GIT_BRANCH,
|
|
7
7
|
GIT_COMMIT_SHA,
|
|
@@ -120,12 +120,12 @@ module.exports = {
|
|
|
120
120
|
GIT_COMMIT: JENKINS_GIT_COMMIT,
|
|
121
121
|
GIT_URL: JENKINS_GIT_REPOSITORY_URL,
|
|
122
122
|
GIT_URL_1: JENKINS_GIT_REPOSITORY_URL_1,
|
|
123
|
-
DD_CUSTOM_TRACE_ID,
|
|
124
123
|
NODE_NAME,
|
|
125
124
|
NODE_LABELS,
|
|
126
125
|
CHANGE_ID,
|
|
127
126
|
CHANGE_TARGET
|
|
128
127
|
} = env
|
|
128
|
+
const DD_CUSTOM_TRACE_ID = getValueFromEnvSources('DD_CUSTOM_TRACE_ID')
|
|
129
129
|
|
|
130
130
|
tags = {
|
|
131
131
|
[CI_PIPELINE_ID]: BUILD_TAG,
|
|
@@ -689,16 +689,13 @@ module.exports = {
|
|
|
689
689
|
}
|
|
690
690
|
|
|
691
691
|
if (env.CODEBUILD_INITIATOR?.startsWith('codepipeline/')) {
|
|
692
|
-
const
|
|
693
|
-
|
|
694
|
-
DD_ACTION_EXECUTION_ID,
|
|
695
|
-
DD_PIPELINE_EXECUTION_ID
|
|
696
|
-
} = env
|
|
692
|
+
const DD_ACTION_EXECUTION_ID = getValueFromEnvSources('DD_ACTION_EXECUTION_ID')
|
|
693
|
+
const DD_PIPELINE_EXECUTION_ID = getValueFromEnvSources('DD_PIPELINE_EXECUTION_ID')
|
|
697
694
|
tags = {
|
|
698
695
|
[CI_PROVIDER_NAME]: 'awscodepipeline',
|
|
699
696
|
[CI_PIPELINE_ID]: DD_PIPELINE_EXECUTION_ID,
|
|
700
697
|
[CI_ENV_VARS]: JSON.stringify({
|
|
701
|
-
CODEBUILD_BUILD_ARN,
|
|
698
|
+
CODEBUILD_BUILD_ARN: env.CODEBUILD_BUILD_ARN,
|
|
702
699
|
DD_PIPELINE_EXECUTION_ID,
|
|
703
700
|
DD_ACTION_EXECUTION_ID
|
|
704
701
|
}),
|