dd-trace 5.97.0 → 5.98.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/index.d.ts +26 -2
- package/package.json +1 -1
- package/packages/datadog-instrumentations/src/cucumber.js +65 -3
- package/packages/datadog-instrumentations/src/cypress-config.js +31 -37
- package/packages/datadog-instrumentations/src/jest.js +104 -12
- package/packages/datadog-instrumentations/src/mocha/utils.js +8 -0
- package/packages/datadog-instrumentations/src/redis.js +12 -6
- package/packages/datadog-plugin-aws-sdk/src/base.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +1 -0
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -0
- package/packages/datadog-plugin-cucumber/src/index.js +6 -0
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +109 -1
- package/packages/datadog-plugin-cypress/src/index.js +59 -2
- package/packages/datadog-plugin-fs/src/index.js +1 -1
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -1
- package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +2 -7
- package/packages/datadog-plugin-http/src/client.js +1 -1
- package/packages/datadog-plugin-http/src/server.js +10 -2
- package/packages/datadog-plugin-http2/src/client.js +1 -1
- package/packages/datadog-plugin-http2/src/server.js +10 -2
- package/packages/datadog-plugin-mongodb-core/src/index.js +3 -3
- package/packages/datadog-plugin-mysql/src/index.js +1 -1
- package/packages/datadog-plugin-next/src/index.js +8 -2
- package/packages/datadog-plugin-pg/src/index.js +1 -1
- package/packages/datadog-plugin-tedious/src/index.js +1 -1
- package/packages/datadog-plugin-ws/src/close.js +1 -1
- package/packages/datadog-plugin-ws/src/receiver.js +1 -1
- package/packages/dd-trace/src/aiguard/sdk.js +22 -22
- package/packages/dd-trace/src/appsec/blocked_templates.js +4 -3
- package/packages/dd-trace/src/appsec/blocking.js +62 -34
- package/packages/dd-trace/src/appsec/sdk/set_user.js +1 -1
- package/packages/dd-trace/src/appsec/sdk/track_event.js +5 -5
- package/packages/dd-trace/src/appsec/sdk/user_blocking.js +2 -2
- package/packages/dd-trace/src/appsec/sdk/utils.js +4 -2
- package/packages/dd-trace/src/config/defaults.js +0 -1
- package/packages/dd-trace/src/config/generated-config-types.d.ts +5 -0
- package/packages/dd-trace/src/config/index.js +55 -28
- package/packages/dd-trace/src/config/supported-configurations.json +61 -4
- package/packages/dd-trace/src/constants.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/processor.js +5 -2
- package/packages/dd-trace/src/encode/0.4.js +7 -6
- package/packages/dd-trace/src/encode/span-stats.js +4 -1
- package/packages/dd-trace/src/log/index.js +0 -10
- package/packages/dd-trace/src/openfeature/remote_config.js +6 -1
- package/packages/dd-trace/src/opentelemetry/context_manager.js +6 -4
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +17 -2
- package/packages/dd-trace/src/opentelemetry/otlp/protobuf_loader.js +14 -2
- package/packages/dd-trace/src/opentelemetry/otlp/trace.proto +358 -0
- package/packages/dd-trace/src/opentelemetry/otlp/trace_service.proto +78 -0
- package/packages/dd-trace/src/opentelemetry/trace/index.js +75 -0
- package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +66 -0
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +332 -0
- package/packages/dd-trace/src/opentracing/tracer.js +9 -4
- package/packages/dd-trace/src/plugins/log_plugin.js +3 -0
- package/packages/dd-trace/src/plugins/plugin.js +6 -11
- package/packages/dd-trace/src/plugins/storage.js +2 -2
- package/packages/dd-trace/src/plugins/tracing.js +22 -5
- package/packages/dd-trace/src/plugins/util/test.js +2 -0
- package/packages/dd-trace/src/plugins/util/web.js +6 -88
- package/packages/dd-trace/src/profiling/profiler.js +34 -77
- package/packages/dd-trace/src/proxy.js +8 -3
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +15 -11
- package/packages/dd-trace/src/service-naming/index.js +1 -1
- package/packages/dd-trace/src/service-naming/schemas/definition.js +4 -1
- package/packages/dd-trace/src/service-naming/schemas/util.js +15 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +24 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +60 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/web.js +17 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/websocket.js +5 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +17 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/web.js +11 -1
- package/packages/dd-trace/src/service-naming/schemas/v1/websocket.js +6 -0
- package/packages/dd-trace/src/span_stats.js +5 -1
- package/packages/dd-trace/src/tracer.js +2 -2
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +28 -6
- package/vendor/dist/protobufjs/index.js +1 -1
- package/packages/dd-trace/src/log/utils.js +0 -16
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const OtlpHttpExporterBase = require('../otlp/otlp_http_exporter_base')
|
|
4
|
+
const { SAMPLING_PRIORITY_KEY } = require('../../constants')
|
|
5
|
+
const { AUTO_KEEP } = require('../../../../../ext/priority')
|
|
6
|
+
const OtlpTraceTransformer = require('./otlp_transformer')
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* OtlpHttpTraceExporter exports DD-formatted spans via OTLP over HTTP/JSON.
|
|
10
|
+
*
|
|
11
|
+
* This implementation follows the OTLP HTTP v1.7.0 specification:
|
|
12
|
+
* https://opentelemetry.io/docs/specs/otlp/#otlphttp
|
|
13
|
+
*
|
|
14
|
+
* It receives DD-formatted spans (from span_format.js), transforms them
|
|
15
|
+
* to OTLP ExportTraceServiceRequest JSON format, and sends them to the
|
|
16
|
+
* configured OTLP endpoint via HTTP POST.
|
|
17
|
+
*
|
|
18
|
+
* @class OtlpHttpTraceExporter
|
|
19
|
+
* @augments OtlpHttpExporterBase
|
|
20
|
+
*/
|
|
21
|
+
class OtlpHttpTraceExporter extends OtlpHttpExporterBase {
|
|
22
|
+
#transformer
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Creates a new OtlpHttpTraceExporter instance.
|
|
26
|
+
*
|
|
27
|
+
* @param {string} url - OTLP endpoint URL
|
|
28
|
+
* @param {string} headers - Additional HTTP headers as comma-separated key=value string
|
|
29
|
+
* @param {number} timeout - Request timeout in milliseconds
|
|
30
|
+
* @param {import('@opentelemetry/api').Attributes} resourceAttributes - Resource attributes
|
|
31
|
+
*/
|
|
32
|
+
constructor (url, headers, timeout, resourceAttributes) {
|
|
33
|
+
super(url, headers, timeout, 'http/json', '/v1/traces', 'traces')
|
|
34
|
+
this.#transformer = new OtlpTraceTransformer(resourceAttributes)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Exports DD-formatted spans via OTLP over HTTP.
|
|
39
|
+
*
|
|
40
|
+
* @param {import('./otlp_transformer').DDFormattedSpan[]} spans - Array of DD-formatted spans to export
|
|
41
|
+
* @returns {void}
|
|
42
|
+
*/
|
|
43
|
+
export (spans) {
|
|
44
|
+
if (spans.length === 0) {
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Drop unsampled traces — OTLP endpoints have no agent-side sampling.
|
|
49
|
+
const priority = spans[0]?.metrics?.[SAMPLING_PRIORITY_KEY]
|
|
50
|
+
if (priority !== undefined && priority < AUTO_KEEP) {
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const additionalTags = [`spans:${spans.length}`]
|
|
55
|
+
this.recordTelemetry('otel.traces_export_attempts', 1, additionalTags)
|
|
56
|
+
|
|
57
|
+
const payload = this.#transformer.transformSpans(spans)
|
|
58
|
+
this.sendPayload(payload, (result) => {
|
|
59
|
+
if (result.code === 0) {
|
|
60
|
+
this.recordTelemetry('otel.traces_export_successes', 1, additionalTags)
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = OtlpHttpTraceExporter
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const OtlpTransformerBase = require('../otlp/otlp_transformer_base')
|
|
4
|
+
const { getProtobufTypes } = require('../otlp/protobuf_loader')
|
|
5
|
+
const { VERSION } = require('../../../../../version')
|
|
6
|
+
|
|
7
|
+
const { protoSpanKind } = getProtobufTypes()
|
|
8
|
+
const SPAN_KIND_UNSPECIFIED = protoSpanKind.values.SPAN_KIND_UNSPECIFIED
|
|
9
|
+
const SPAN_KIND_INTERNAL = protoSpanKind.values.SPAN_KIND_INTERNAL
|
|
10
|
+
const SPAN_KIND_SERVER = protoSpanKind.values.SPAN_KIND_SERVER
|
|
11
|
+
const SPAN_KIND_CLIENT = protoSpanKind.values.SPAN_KIND_CLIENT
|
|
12
|
+
const SPAN_KIND_PRODUCER = protoSpanKind.values.SPAN_KIND_PRODUCER
|
|
13
|
+
const SPAN_KIND_CONSUMER = protoSpanKind.values.SPAN_KIND_CONSUMER
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {object} DDFormattedSpan
|
|
17
|
+
* @property {import('../../id')} trace_id - DD Identifier for trace ID
|
|
18
|
+
* @property {import('../../id')} span_id - DD Identifier for span ID
|
|
19
|
+
* @property {import('../../id')} parent_id - DD Identifier for parent span ID
|
|
20
|
+
* @property {string} name - Span operation name
|
|
21
|
+
* @property {string} resource - Resource name
|
|
22
|
+
* @property {string} [service] - Service name
|
|
23
|
+
* @property {string} [type] - Span type
|
|
24
|
+
* @property {number} error - Error flag (0 or 1)
|
|
25
|
+
* @property {{[key: string]: string}} meta - String key-value tags
|
|
26
|
+
* @property {{[key: string]: number}} metrics - Numeric key-value tags
|
|
27
|
+
* @property {number} start - Start time in nanoseconds since epoch
|
|
28
|
+
* @property {number} duration - Duration in nanoseconds
|
|
29
|
+
* @property {object[]} [span_events] - Span events
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
// Map DD span.kind string values to OTLP SpanKind numeric values
|
|
33
|
+
const SPAN_KIND_MAP = {
|
|
34
|
+
internal: SPAN_KIND_INTERNAL,
|
|
35
|
+
server: SPAN_KIND_SERVER,
|
|
36
|
+
client: SPAN_KIND_CLIENT,
|
|
37
|
+
producer: SPAN_KIND_PRODUCER,
|
|
38
|
+
consumer: SPAN_KIND_CONSUMER,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// OTLP StatusCode values (from trace.proto Status.StatusCode enum)
|
|
42
|
+
const STATUS_CODE_UNSET = 0
|
|
43
|
+
const STATUS_CODE_ERROR = 2
|
|
44
|
+
|
|
45
|
+
// DD meta keys that are mapped to dedicated OTLP span fields and should not appear as attributes
|
|
46
|
+
const EXCLUDED_META_KEYS = new Set([
|
|
47
|
+
'_dd.span_links',
|
|
48
|
+
'span.kind',
|
|
49
|
+
])
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* OtlpTraceTransformer transforms DD-formatted spans to OTLP trace JSON format.
|
|
53
|
+
*
|
|
54
|
+
* This implementation follows the OTLP Trace v1.7.0 Data Model specification:
|
|
55
|
+
* https://opentelemetry.io/docs/specs/otlp/#trace-data-model
|
|
56
|
+
*
|
|
57
|
+
* It receives DD-formatted spans (from span_format.js) and produces
|
|
58
|
+
* an ExportTraceServiceRequest serialized as JSON (http/json protocol only).
|
|
59
|
+
*
|
|
60
|
+
* @class OtlpTraceTransformer
|
|
61
|
+
* @augments OtlpTransformerBase
|
|
62
|
+
*/
|
|
63
|
+
class OtlpTraceTransformer extends OtlpTransformerBase {
|
|
64
|
+
/**
|
|
65
|
+
* Creates a new OtlpTraceTransformer instance.
|
|
66
|
+
*
|
|
67
|
+
* @param {import('@opentelemetry/api').Attributes} resourceAttributes - Resource attributes
|
|
68
|
+
*/
|
|
69
|
+
constructor (resourceAttributes) {
|
|
70
|
+
super(resourceAttributes, 'http/json', 'traces')
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Transforms DD-formatted spans to OTLP JSON format.
|
|
75
|
+
*
|
|
76
|
+
* @param {DDFormattedSpan[]} spans - Array of DD-formatted spans to transform
|
|
77
|
+
* @returns {Buffer} JSON-encoded trace data
|
|
78
|
+
*/
|
|
79
|
+
transformSpans (spans) {
|
|
80
|
+
const traceData = {
|
|
81
|
+
resourceSpans: [{
|
|
82
|
+
resource: this.transformResource(),
|
|
83
|
+
scopeSpans: this.#transformScopeSpans(spans),
|
|
84
|
+
}],
|
|
85
|
+
}
|
|
86
|
+
return this.serializeToJson(traceData)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Creates scope spans. DD spans do not carry instrumentation scope info,
|
|
91
|
+
* so all spans are placed under a single default scope.
|
|
92
|
+
*
|
|
93
|
+
* @param {DDFormattedSpan[]} spans - Array of DD-formatted spans
|
|
94
|
+
* @returns {object[]} Array of scope span objects
|
|
95
|
+
*/
|
|
96
|
+
#transformScopeSpans (spans) {
|
|
97
|
+
return [{
|
|
98
|
+
scope: {
|
|
99
|
+
name: 'dd-trace-js',
|
|
100
|
+
version: VERSION,
|
|
101
|
+
attributes: [],
|
|
102
|
+
droppedAttributesCount: 0,
|
|
103
|
+
},
|
|
104
|
+
schemaUrl: '',
|
|
105
|
+
spans: spans.map(span => this.#transformSpan(span)),
|
|
106
|
+
}]
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Transforms a single DD-formatted span to an OTLP Span object.
|
|
111
|
+
*
|
|
112
|
+
* @param {DDFormattedSpan} span - DD-formatted span to transform
|
|
113
|
+
* @returns {object} OTLP Span object
|
|
114
|
+
*/
|
|
115
|
+
#transformSpan (span) {
|
|
116
|
+
const parentId = span.parent_id
|
|
117
|
+
const links = this.#extractLinks(span.meta?.['_dd.span_links'])
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
traceId: this.#idToBytes(span.trace_id, 16),
|
|
121
|
+
spanId: this.#idToBytes(span.span_id, 8),
|
|
122
|
+
parentSpanId: (parentId && !this.#isZeroId(parentId)) ? this.#idToBytes(parentId, 8) : undefined,
|
|
123
|
+
name: span.resource,
|
|
124
|
+
kind: this.#mapSpanKind(span.meta?.['span.kind']),
|
|
125
|
+
startTimeUnixNano: span.start,
|
|
126
|
+
endTimeUnixNano: span.start + span.duration,
|
|
127
|
+
attributes: this.#buildAttributes(span),
|
|
128
|
+
droppedAttributesCount: 0,
|
|
129
|
+
events: span.span_events?.length ? span.span_events.map(event => this.#transformEvent(event)) : undefined,
|
|
130
|
+
droppedEventsCount: 0,
|
|
131
|
+
links: links.length ? links : undefined,
|
|
132
|
+
droppedLinksCount: 0,
|
|
133
|
+
status: this.#mapStatus(span),
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Builds OTLP attributes from DD span fields.
|
|
139
|
+
* Merges top-level DD fields (service, resource, type), meta (string tags),
|
|
140
|
+
* and metrics (numeric tags) into a single OTLP KeyValue array.
|
|
141
|
+
*
|
|
142
|
+
* @param {DDFormattedSpan} span - DD-formatted span
|
|
143
|
+
* @returns {object[]} Array of OTLP KeyValue objects
|
|
144
|
+
*/
|
|
145
|
+
#buildAttributes (span) {
|
|
146
|
+
const attributes = []
|
|
147
|
+
|
|
148
|
+
// Add top-level DD span fields as OTLP attributes
|
|
149
|
+
if (span.service) {
|
|
150
|
+
attributes.push({ key: 'service.name', value: { stringValue: span.service } })
|
|
151
|
+
}
|
|
152
|
+
if (span.name) {
|
|
153
|
+
attributes.push({ key: 'operation.name', value: { stringValue: span.name } })
|
|
154
|
+
}
|
|
155
|
+
if (span.resource) {
|
|
156
|
+
attributes.push({ key: 'resource.name', value: { stringValue: span.resource } })
|
|
157
|
+
}
|
|
158
|
+
if (span.type) {
|
|
159
|
+
attributes.push({ key: 'span.type', value: { stringValue: span.type } })
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Add meta string tags, skipping keys that map to dedicated OTLP fields
|
|
163
|
+
if (span.meta) {
|
|
164
|
+
for (const [key, value] of Object.entries(span.meta)) {
|
|
165
|
+
if (EXCLUDED_META_KEYS.has(key)) continue
|
|
166
|
+
attributes.push({ key, value: { stringValue: value } })
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Add metrics as numeric attributes
|
|
171
|
+
if (span.metrics) {
|
|
172
|
+
for (const [key, value] of Object.entries(span.metrics)) {
|
|
173
|
+
if (Number.isInteger(value)) {
|
|
174
|
+
attributes.push({ key, value: { intValue: value } })
|
|
175
|
+
} else {
|
|
176
|
+
attributes.push({ key, value: { doubleValue: value } })
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Add meta_struct as bytesValue attributes (JSON-serialized, base64-encoded per proto JSON mapping)
|
|
182
|
+
if (span.meta_struct) {
|
|
183
|
+
for (const [key, value] of Object.entries(span.meta_struct)) {
|
|
184
|
+
const bytes = Buffer.from(JSON.stringify(value))
|
|
185
|
+
attributes.push({ key, value: { bytesValue: bytes.toString('base64') } })
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return attributes
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Maps a DD span.kind string to an OTLP SpanKind enum value.
|
|
194
|
+
*
|
|
195
|
+
* @param {string | undefined} kind - DD span kind string
|
|
196
|
+
* @returns {number} OTLP SpanKind enum value
|
|
197
|
+
*/
|
|
198
|
+
#mapSpanKind (kind) {
|
|
199
|
+
if (!kind) return SPAN_KIND_UNSPECIFIED
|
|
200
|
+
return SPAN_KIND_MAP[kind] ?? SPAN_KIND_UNSPECIFIED
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Maps DD span error state to an OTLP Status object.
|
|
205
|
+
*
|
|
206
|
+
* @param {DDFormattedSpan} span - DD-formatted span
|
|
207
|
+
* @returns {object} OTLP Status object with code and message
|
|
208
|
+
*/
|
|
209
|
+
#mapStatus (span) {
|
|
210
|
+
if (span.error === 1) {
|
|
211
|
+
return {
|
|
212
|
+
code: STATUS_CODE_ERROR,
|
|
213
|
+
message: span.meta?.['error.message'] || '',
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return { code: STATUS_CODE_UNSET, message: '' }
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Transforms a DD span event to an OTLP Event object.
|
|
221
|
+
*
|
|
222
|
+
* @param {object} event - DD span event with name, time_unix_nano, and attributes
|
|
223
|
+
* @returns {object} OTLP Event object
|
|
224
|
+
*/
|
|
225
|
+
#transformEvent (event) {
|
|
226
|
+
return {
|
|
227
|
+
timeUnixNano: event.time_unix_nano,
|
|
228
|
+
name: event.name || '',
|
|
229
|
+
attributes: event.attributes && Object.keys(event.attributes).length > 0
|
|
230
|
+
? this.transformAttributes(event.attributes)
|
|
231
|
+
: [],
|
|
232
|
+
droppedAttributesCount: 0,
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Extracts and transforms span links from the DD _dd.span_links meta JSON string.
|
|
238
|
+
*
|
|
239
|
+
* @param {string | undefined} spanLinksJson - JSON-encoded array of DD span links
|
|
240
|
+
* @returns {object[]} Array of OTLP Link objects
|
|
241
|
+
*/
|
|
242
|
+
#extractLinks (spanLinksJson) {
|
|
243
|
+
if (!spanLinksJson) return []
|
|
244
|
+
|
|
245
|
+
let parsedLinks
|
|
246
|
+
try {
|
|
247
|
+
parsedLinks = JSON.parse(spanLinksJson)
|
|
248
|
+
} catch {
|
|
249
|
+
return []
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (!Array.isArray(parsedLinks)) return []
|
|
253
|
+
|
|
254
|
+
return parsedLinks.map(link => this.#transformLink(link))
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Transforms a single DD span link to an OTLP Link object.
|
|
259
|
+
*
|
|
260
|
+
* @param {object} link - DD span link with trace_id, span_id, attributes, flags, tracestate
|
|
261
|
+
* @returns {object} OTLP Link object
|
|
262
|
+
*/
|
|
263
|
+
#transformLink (link) {
|
|
264
|
+
return {
|
|
265
|
+
traceId: this.#hexToBytes(link.trace_id, 16),
|
|
266
|
+
spanId: this.#hexToBytes(link.span_id, 8),
|
|
267
|
+
traceState: link.tracestate || '',
|
|
268
|
+
attributes: link.attributes && Object.keys(link.attributes).length > 0
|
|
269
|
+
? this.transformAttributes(link.attributes)
|
|
270
|
+
: [],
|
|
271
|
+
droppedAttributesCount: 0,
|
|
272
|
+
flags: link.flags,
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Converts a DD Identifier object to a hex-encoded string of the specified byte length.
|
|
278
|
+
* Pads with leading zeros if the identifier buffer is shorter than the target.
|
|
279
|
+
* Per the OTLP http/json spec, trace-ids and span-ids must be hex-encoded strings.
|
|
280
|
+
*
|
|
281
|
+
* @param {object} identifier - DD Identifier object with toBuffer() method
|
|
282
|
+
* @param {number} targetLength - Target byte length (16 for trace ID, 8 for span ID)
|
|
283
|
+
* @returns {string} Hex-encoded string of the specified length
|
|
284
|
+
*/
|
|
285
|
+
#idToBytes (identifier, targetLength) {
|
|
286
|
+
const buffer = identifier.toBuffer()
|
|
287
|
+
if (buffer.length === targetLength) {
|
|
288
|
+
return Buffer.from(buffer).toString('hex')
|
|
289
|
+
}
|
|
290
|
+
if (buffer.length > targetLength) {
|
|
291
|
+
return Buffer.from(buffer.slice(buffer.length - targetLength)).toString('hex')
|
|
292
|
+
}
|
|
293
|
+
// Pad with leading zeros to reach target length
|
|
294
|
+
const result = Buffer.alloc(targetLength)
|
|
295
|
+
const offset = targetLength - buffer.length
|
|
296
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
297
|
+
result[offset + i] = buffer[i]
|
|
298
|
+
}
|
|
299
|
+
return result.toString('hex')
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Checks if a DD Identifier represents a zero ID (all bytes are 0).
|
|
304
|
+
*
|
|
305
|
+
* @param {object} identifier - DD Identifier object with toBuffer() method
|
|
306
|
+
* @returns {boolean} True if the identifier is all zeros
|
|
307
|
+
*/
|
|
308
|
+
#isZeroId (identifier) {
|
|
309
|
+
const buffer = identifier.toBuffer()
|
|
310
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
311
|
+
if (buffer[i] !== 0) return false
|
|
312
|
+
}
|
|
313
|
+
return true
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Normalizes a hex string to the specified byte length.
|
|
318
|
+
* Pads with leading zeros if the hex string is shorter than expected.
|
|
319
|
+
* Per the OTLP http/json spec, trace-ids and span-ids must be hex-encoded strings.
|
|
320
|
+
*
|
|
321
|
+
* @param {string | undefined} hexString - Hex string to normalize
|
|
322
|
+
* @param {number} targetLength - Target byte length
|
|
323
|
+
* @returns {string} Hex-encoded string of the specified length
|
|
324
|
+
*/
|
|
325
|
+
#hexToBytes (hexString, targetLength) {
|
|
326
|
+
if (!hexString) return '0'.repeat(targetLength * 2)
|
|
327
|
+
const cleanHex = hexString.startsWith('0x') ? hexString.slice(2) : hexString
|
|
328
|
+
return cleanHex.padStart(targetLength * 2, '0')
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
module.exports = OtlpTraceTransformer
|
|
@@ -20,9 +20,7 @@ const REFERENCE_CHILD_OF = 'child_of'
|
|
|
20
20
|
const REFERENCE_FOLLOWS_FROM = 'follows_from'
|
|
21
21
|
|
|
22
22
|
class DatadogTracer {
|
|
23
|
-
constructor (config, prioritySampler) {
|
|
24
|
-
const Exporter = getExporter(config.experimental.exporter)
|
|
25
|
-
|
|
23
|
+
constructor (config, prioritySampler, exporter) {
|
|
26
24
|
this._config = config
|
|
27
25
|
this._service = config.service
|
|
28
26
|
this._version = config.version
|
|
@@ -30,7 +28,14 @@ class DatadogTracer {
|
|
|
30
28
|
this._logInjection = config.logInjection
|
|
31
29
|
this._debug = config.debug
|
|
32
30
|
this._prioritySampler = prioritySampler ?? new PrioritySampler(config.env, config.sampler)
|
|
33
|
-
|
|
31
|
+
|
|
32
|
+
if (exporter) {
|
|
33
|
+
this._exporter = exporter
|
|
34
|
+
} else {
|
|
35
|
+
const Exporter = getExporter(config.experimental.exporter)
|
|
36
|
+
this._exporter = new Exporter(config, this._prioritySampler)
|
|
37
|
+
}
|
|
38
|
+
|
|
34
39
|
this._processor = new SpanProcessor(this._exporter, this._prioritySampler, config)
|
|
35
40
|
this._url = this._exporter._url
|
|
36
41
|
this._enableGetRumData = config.experimental.enableGetRumData
|
|
@@ -13,6 +13,9 @@ function messageProxy (message, holder) {
|
|
|
13
13
|
|
|
14
14
|
return target[key]
|
|
15
15
|
},
|
|
16
|
+
set (target, key, value) {
|
|
17
|
+
return Reflect.set(target, key, value)
|
|
18
|
+
},
|
|
16
19
|
ownKeys (target) {
|
|
17
20
|
const ownKeys = Reflect.ownKeys(target)
|
|
18
21
|
if (!Object.hasOwn(target, 'dd') && Reflect.isExtensible(target)) {
|
|
@@ -106,12 +106,6 @@ module.exports = class Plugin {
|
|
|
106
106
|
storage('legacy').enterWith({ ...store, span })
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
// TODO: Implement filters on resource name for all plugins.
|
|
110
|
-
/** Prevents creation of spans here and for all async descendants. */
|
|
111
|
-
skip () {
|
|
112
|
-
storage('legacy').enterWith({ noop: true })
|
|
113
|
-
}
|
|
114
|
-
|
|
115
109
|
/**
|
|
116
110
|
* Subscribe to a diagnostic channel with automatic error handling and enable/disable lifecycle.
|
|
117
111
|
*
|
|
@@ -157,8 +151,9 @@ module.exports = class Plugin {
|
|
|
157
151
|
|
|
158
152
|
if (!store || !store.span) return
|
|
159
153
|
|
|
160
|
-
|
|
161
|
-
|
|
154
|
+
const span = /** @type {import('../opentracing/span')} */ (store.span)
|
|
155
|
+
if (!span._spanContext._tags.error) {
|
|
156
|
+
span.setTag('error', error || 1)
|
|
162
157
|
}
|
|
163
158
|
}
|
|
164
159
|
|
|
@@ -167,12 +162,12 @@ module.exports = class Plugin {
|
|
|
167
162
|
*
|
|
168
163
|
* TODO: Remove the overloading with `enabled` and use the config object directly.
|
|
169
164
|
*
|
|
170
|
-
* @param {boolean|import('../config/config-base')} config Either a boolean to
|
|
171
|
-
* or a configuration object containing at least `{ enabled: boolean }`.
|
|
165
|
+
* @param {boolean | import('../config/config-base') & {enabled: boolean}} config Either a boolean to
|
|
166
|
+
* enable/disable or a configuration object containing at least `{ enabled: boolean }`.
|
|
172
167
|
*/
|
|
173
168
|
configure (config) {
|
|
174
169
|
if (typeof config === 'boolean') {
|
|
175
|
-
config = { enabled: config }
|
|
170
|
+
config = /** @type {import('../config/config-base') & {enabled: boolean}} */ ({ enabled: config })
|
|
176
171
|
}
|
|
177
172
|
this.config = config
|
|
178
173
|
if (config.enabled) {
|
|
@@ -12,8 +12,8 @@ class StoragePlugin extends ClientPlugin {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
startSpan (name, options, ctx) {
|
|
15
|
-
if (!options.service && this.system) {
|
|
16
|
-
options.service = `${this.tracer._service}-${this.system}
|
|
15
|
+
if (!options.service?.name && this.system) {
|
|
16
|
+
options.service = { name: `${this.tracer._service}-${this.system}`, source: this.system }
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
return super.startSpan(name, options, ctx)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { storage } = require('../../../datadog-core')
|
|
4
4
|
const analyticsSampler = require('../analytics_sampler')
|
|
5
|
-
const { COMPONENT } = require('../constants')
|
|
5
|
+
const { COMPONENT, SVC_SRC_KEY } = require('../constants')
|
|
6
6
|
const Plugin = require('./plugin')
|
|
7
7
|
|
|
8
8
|
class TracingPlugin extends Plugin {
|
|
@@ -26,7 +26,7 @@ class TracingPlugin extends Plugin {
|
|
|
26
26
|
* @param {string} [opts.type]
|
|
27
27
|
* @param {string} [opts.id]
|
|
28
28
|
* @param {string} [opts.kind]
|
|
29
|
-
* @returns {string}
|
|
29
|
+
* @returns {{ name: string, source: string | undefined }}
|
|
30
30
|
*/
|
|
31
31
|
serviceName (opts = {}) {
|
|
32
32
|
const {
|
|
@@ -157,7 +157,8 @@ class TracingPlugin extends Plugin {
|
|
|
157
157
|
* @param {string} [options.kind] - The kind of the span.
|
|
158
158
|
* @param {object} [options.meta] - The meta data for the span.
|
|
159
159
|
* @param {object} [options.metrics] - The metrics for the span.
|
|
160
|
-
* @param {string} [options.service] - The service name
|
|
160
|
+
* @param {string | { name: string, source?: string }} [options.service] - The service name, or an object with
|
|
161
|
+
* name and source.
|
|
161
162
|
* @param {number} [options.startTime] - The start time of the span.
|
|
162
163
|
* @param {string} [options.resource] - The resource name.
|
|
163
164
|
* @param {string} [options.type] - The type of the span.
|
|
@@ -180,24 +181,40 @@ class TracingPlugin extends Plugin {
|
|
|
180
181
|
resource,
|
|
181
182
|
type,
|
|
182
183
|
} = options
|
|
183
|
-
|
|
184
|
+
let serviceSource
|
|
184
185
|
const tracer = options.tracer || this.tracer
|
|
185
186
|
const config = options.config || this.config
|
|
186
187
|
|
|
188
|
+
if (service && typeof service === 'object') {
|
|
189
|
+
serviceSource = service.source
|
|
190
|
+
service = service.name
|
|
191
|
+
} else if (service !== undefined) {
|
|
192
|
+
// service is a plain value returned by service naming/config logic
|
|
193
|
+
serviceSource = service ? 'opt.plugin' : undefined
|
|
194
|
+
}
|
|
195
|
+
|
|
187
196
|
const store = storage('legacy').getStore()
|
|
188
197
|
if (store && childOf === undefined) {
|
|
189
198
|
childOf = /** @type {import('../opentracing/span') | undefined} */ (store.span)
|
|
190
199
|
}
|
|
191
200
|
|
|
201
|
+
// clear service source if service is the same as tracer._service
|
|
202
|
+
const serviceName = service || meta?.service
|
|
203
|
+
|
|
204
|
+
if (!serviceName || serviceName === tracer._service) {
|
|
205
|
+
serviceSource = undefined
|
|
206
|
+
}
|
|
207
|
+
|
|
192
208
|
const span = tracer.startSpan(name, {
|
|
193
209
|
startTime,
|
|
194
210
|
childOf,
|
|
195
211
|
tags: {
|
|
196
212
|
[COMPONENT]: component,
|
|
197
|
-
'service.name':
|
|
213
|
+
'service.name': serviceName || tracer._service,
|
|
198
214
|
'resource.name': resource,
|
|
199
215
|
'span.kind': kind,
|
|
200
216
|
'span.type': type,
|
|
217
|
+
...(serviceSource === undefined ? undefined : { [SVC_SRC_KEY]: serviceSource }),
|
|
201
218
|
...meta,
|
|
202
219
|
...metrics,
|
|
203
220
|
},
|
|
@@ -137,6 +137,7 @@ const JEST_WORKER_TRACE_PAYLOAD_CODE = 60
|
|
|
137
137
|
const JEST_WORKER_COVERAGE_PAYLOAD_CODE = 61
|
|
138
138
|
const JEST_WORKER_LOGS_PAYLOAD_CODE = 62
|
|
139
139
|
const JEST_WORKER_TELEMETRY_PAYLOAD_CODE = 63
|
|
140
|
+
const JEST_WORKER_QUARANTINE_PAYLOAD_CODE = 64
|
|
140
141
|
|
|
141
142
|
// cucumber worker variables
|
|
142
143
|
const CUCUMBER_WORKER_TRACE_PAYLOAD_CODE = 70
|
|
@@ -262,6 +263,7 @@ module.exports = {
|
|
|
262
263
|
JEST_WORKER_COVERAGE_PAYLOAD_CODE,
|
|
263
264
|
JEST_WORKER_LOGS_PAYLOAD_CODE,
|
|
264
265
|
JEST_WORKER_TELEMETRY_PAYLOAD_CODE,
|
|
266
|
+
JEST_WORKER_QUARANTINE_PAYLOAD_CODE,
|
|
265
267
|
CUCUMBER_WORKER_TRACE_PAYLOAD_CODE,
|
|
266
268
|
MOCHA_WORKER_TRACE_PAYLOAD_CODE,
|
|
267
269
|
PLAYWRIGHT_WORKER_TRACE_PAYLOAD_CODE,
|