dd-trace 5.42.0 → 5.43.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/package.json +2 -2
- package/packages/datadog-instrumentations/src/vitest.js +19 -4
- package/packages/dd-trace/src/appsec/telemetry/common.js +2 -1
- package/packages/dd-trace/src/appsec/telemetry/waf.js +46 -0
- package/packages/dd-trace/src/config.js +4 -0
- package/packages/dd-trace/src/dogstatsd.js +3 -3
- package/packages/dd-trace/src/encode/0.4.js +108 -12
- package/packages/dd-trace/src/encode/0.5.js +7 -1
- package/packages/dd-trace/src/exporters/agent/index.js +2 -1
- package/packages/dd-trace/src/exporters/agent/writer.js +3 -2
- package/packages/dd-trace/src/format.js +34 -32
- package/packages/dd-trace/src/llmobs/sdk.js +3 -3
- package/packages/dd-trace/src/log/index.js +1 -13
- package/packages/dd-trace/src/log/utils.js +16 -0
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +13 -14
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dd-trace",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.43.0",
|
|
4
4
|
"description": "Datadog APM tracing client for JavaScript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"typings": "index.d.ts",
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
"@datadog/native-iast-rewriter": "2.8.0",
|
|
90
90
|
"@datadog/native-iast-taint-tracking": "3.3.0",
|
|
91
91
|
"@datadog/native-metrics": "^3.1.0",
|
|
92
|
-
"@datadog/pprof": "5.
|
|
92
|
+
"@datadog/pprof": "5.6.0",
|
|
93
93
|
"@datadog/sketches-js": "^2.1.0",
|
|
94
94
|
"@isaacs/ttlcache": "^1.4.1",
|
|
95
95
|
"@opentelemetry/api": ">=1.0.0 <1.9.0",
|
|
@@ -94,6 +94,10 @@ function isReporterPackageNewest (vitestPackage) {
|
|
|
94
94
|
return vitestPackage.h?.name === 'BaseSequencer'
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
function isBaseSequencer (vitestPackage) {
|
|
98
|
+
return vitestPackage.b?.name === 'BaseSequencer'
|
|
99
|
+
}
|
|
100
|
+
|
|
97
101
|
function getChannelPromise (channelToPublishTo) {
|
|
98
102
|
return new Promise(resolve => {
|
|
99
103
|
sessionAsyncResource.runInAsyncScope(() => {
|
|
@@ -615,11 +619,22 @@ addHook({
|
|
|
615
619
|
|
|
616
620
|
addHook({
|
|
617
621
|
name: 'vitest',
|
|
618
|
-
versions: ['>=3.0.
|
|
622
|
+
versions: ['>=3.0.9'],
|
|
623
|
+
filePattern: 'dist/chunks/coverage.*'
|
|
624
|
+
}, (coveragePackage) => {
|
|
625
|
+
if (isBaseSequencer(coveragePackage)) {
|
|
626
|
+
shimmer.wrap(coveragePackage.b.prototype, 'sort', getSortWrapper)
|
|
627
|
+
}
|
|
628
|
+
return coveragePackage
|
|
629
|
+
})
|
|
630
|
+
|
|
631
|
+
addHook({
|
|
632
|
+
name: 'vitest',
|
|
633
|
+
versions: ['>=3.0.0 <3.0.9'],
|
|
619
634
|
filePattern: 'dist/chunks/resolveConfig.*'
|
|
620
|
-
}, (
|
|
621
|
-
shimmer.wrap(
|
|
622
|
-
return
|
|
635
|
+
}, (resolveConfigPackage) => {
|
|
636
|
+
shimmer.wrap(resolveConfigPackage.B.prototype, 'sort', getSortWrapper)
|
|
637
|
+
return resolveConfigPackage
|
|
623
638
|
})
|
|
624
639
|
|
|
625
640
|
// Can't specify file because compiled vitest includes hashes in their files
|
|
@@ -7,7 +7,8 @@ const tags = {
|
|
|
7
7
|
RULE_TRIGGERED: 'rule_triggered',
|
|
8
8
|
WAF_TIMEOUT: 'waf_timeout',
|
|
9
9
|
WAF_VERSION: 'waf_version',
|
|
10
|
-
EVENT_RULES_VERSION: 'event_rules_version'
|
|
10
|
+
EVENT_RULES_VERSION: 'event_rules_version',
|
|
11
|
+
INPUT_TRUNCATED: 'input_truncated'
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
function getVersionsTags (wafVersion, rulesVersion) {
|
|
@@ -7,6 +7,12 @@ const appsecMetrics = telemetryMetrics.manager.namespace('appsec')
|
|
|
7
7
|
|
|
8
8
|
const DD_TELEMETRY_WAF_RESULT_TAGS = Symbol('_dd.appsec.telemetry.waf.result.tags')
|
|
9
9
|
|
|
10
|
+
const TRUNCATION_FLAGS = {
|
|
11
|
+
STRING: 1,
|
|
12
|
+
CONTAINER_SIZE: 2,
|
|
13
|
+
CONTAINER_DEPTH: 4
|
|
14
|
+
}
|
|
15
|
+
|
|
10
16
|
function addWafRequestMetrics (store, { duration, durationExt, wafTimeout, errorCode }) {
|
|
11
17
|
store[DD_TELEMETRY_REQUEST_METRICS].duration += duration || 0
|
|
12
18
|
store[DD_TELEMETRY_REQUEST_METRICS].durationExt += durationExt || 0
|
|
@@ -58,6 +64,12 @@ function trackWafMetrics (store, metrics) {
|
|
|
58
64
|
metricTags[tags.WAF_TIMEOUT] = true
|
|
59
65
|
}
|
|
60
66
|
|
|
67
|
+
const truncationReason = getTruncationReason(metrics)
|
|
68
|
+
if (truncationReason > 0) {
|
|
69
|
+
metricTags[tags.INPUT_TRUNCATED] = true
|
|
70
|
+
incrementTruncatedMetrics(metrics, truncationReason)
|
|
71
|
+
}
|
|
72
|
+
|
|
61
73
|
return metricTags
|
|
62
74
|
}
|
|
63
75
|
|
|
@@ -69,6 +81,7 @@ function getOrCreateMetricTags (store, versionsTags) {
|
|
|
69
81
|
[tags.REQUEST_BLOCKED]: false,
|
|
70
82
|
[tags.RULE_TRIGGERED]: false,
|
|
71
83
|
[tags.WAF_TIMEOUT]: false,
|
|
84
|
+
[tags.INPUT_TRUNCATED]: false,
|
|
72
85
|
|
|
73
86
|
...versionsTags
|
|
74
87
|
}
|
|
@@ -98,6 +111,39 @@ function incrementWafRequests (store) {
|
|
|
98
111
|
}
|
|
99
112
|
}
|
|
100
113
|
|
|
114
|
+
function incrementTruncatedMetrics (metrics, truncationReason) {
|
|
115
|
+
const truncationTags = { truncation_reason: truncationReason }
|
|
116
|
+
appsecMetrics.count('waf.input_truncated', truncationTags).inc(1)
|
|
117
|
+
|
|
118
|
+
if (metrics?.maxTruncatedString) {
|
|
119
|
+
appsecMetrics.distribution('waf.truncated_value_size', {
|
|
120
|
+
truncation_reason: TRUNCATION_FLAGS.STRING
|
|
121
|
+
}).track(metrics.maxTruncatedString)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (metrics?.maxTruncatedContainerSize) {
|
|
125
|
+
appsecMetrics.distribution('waf.truncated_value_size', {
|
|
126
|
+
truncation_reason: TRUNCATION_FLAGS.CONTAINER_SIZE
|
|
127
|
+
}).track(metrics.maxTruncatedContainerSize)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (metrics?.maxTruncatedContainerDepth) {
|
|
131
|
+
appsecMetrics.distribution('waf.truncated_value_size', {
|
|
132
|
+
truncation_reason: TRUNCATION_FLAGS.CONTAINER_DEPTH
|
|
133
|
+
}).track(metrics.maxTruncatedContainerDepth)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function getTruncationReason ({ maxTruncatedString, maxTruncatedContainerSize, maxTruncatedContainerDepth }) {
|
|
138
|
+
let reason = 0
|
|
139
|
+
|
|
140
|
+
if (maxTruncatedString) reason |= TRUNCATION_FLAGS.STRING
|
|
141
|
+
if (maxTruncatedContainerSize) reason |= TRUNCATION_FLAGS.CONTAINER_SIZE
|
|
142
|
+
if (maxTruncatedContainerDepth) reason |= TRUNCATION_FLAGS.CONTAINER_DEPTH
|
|
143
|
+
|
|
144
|
+
return reason
|
|
145
|
+
}
|
|
146
|
+
|
|
101
147
|
module.exports = {
|
|
102
148
|
addWafRequestMetrics,
|
|
103
149
|
trackWafMetrics,
|
|
@@ -595,6 +595,7 @@ class Config {
|
|
|
595
595
|
this._setValue(defaults, 'vertexai.spanCharLimit', 128)
|
|
596
596
|
this._setValue(defaults, 'vertexai.spanPromptCompletionSampleRate', 1.0)
|
|
597
597
|
this._setValue(defaults, 'trace.aws.addSpanPointers', true)
|
|
598
|
+
this._setValue(defaults, 'trace.nativeSpanEvents', false)
|
|
598
599
|
}
|
|
599
600
|
|
|
600
601
|
_applyLocalStableConfig () {
|
|
@@ -765,6 +766,7 @@ class Config {
|
|
|
765
766
|
DD_VERTEXAI_SPAN_PROMPT_COMPLETION_SAMPLE_RATE,
|
|
766
767
|
DD_VERTEXAI_SPAN_CHAR_LIMIT,
|
|
767
768
|
DD_TRACE_INFERRED_PROXY_SERVICES_ENABLED,
|
|
769
|
+
DD_TRACE_NATIVE_SPAN_EVENTS,
|
|
768
770
|
OTEL_METRICS_EXPORTER,
|
|
769
771
|
OTEL_PROPAGATORS,
|
|
770
772
|
OTEL_RESOURCE_ATTRIBUTES,
|
|
@@ -977,6 +979,7 @@ class Config {
|
|
|
977
979
|
this._setBoolean(env, 'trace.aws.addSpanPointers', DD_TRACE_AWS_ADD_SPAN_POINTERS)
|
|
978
980
|
this._setString(env, 'trace.dynamoDb.tablePrimaryKeys', DD_TRACE_DYNAMODB_TABLE_PRIMARY_KEYS)
|
|
979
981
|
this._setArray(env, 'graphqlErrorExtensions', DD_TRACE_GRAPHQL_ERROR_EXTENSIONS)
|
|
982
|
+
this._setBoolean(env, 'trace.nativeSpanEvents', DD_TRACE_NATIVE_SPAN_EVENTS)
|
|
980
983
|
this._setValue(
|
|
981
984
|
env,
|
|
982
985
|
'vertexai.spanPromptCompletionSampleRate',
|
|
@@ -1114,6 +1117,7 @@ class Config {
|
|
|
1114
1117
|
this._setString(opts, 'version', options.version || tags.version)
|
|
1115
1118
|
this._setBoolean(opts, 'inferredProxyServicesEnabled', options.inferredProxyServicesEnabled)
|
|
1116
1119
|
this._setBoolean(opts, 'graphqlErrorExtensions', options.graphqlErrorExtensions)
|
|
1120
|
+
this._setBoolean(opts, 'trace.nativeSpanEvents', options.trace?.nativeSpanEvents)
|
|
1117
1121
|
|
|
1118
1122
|
// For LLMObs, we want the environment variable to take precedence over the options.
|
|
1119
1123
|
// This is reliant on environment config being set before options.
|
|
@@ -234,7 +234,7 @@ class MetricsAggregationClient {
|
|
|
234
234
|
this._histograms[name].get(tag).record(value)
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
-
count (name, count, tag, monotonic =
|
|
237
|
+
count (name, count, tag, monotonic = true) {
|
|
238
238
|
if (typeof tag === 'boolean') {
|
|
239
239
|
monotonic = tag
|
|
240
240
|
tag = undefined
|
|
@@ -254,8 +254,8 @@ class MetricsAggregationClient {
|
|
|
254
254
|
this._gauges[name].set(tag, value)
|
|
255
255
|
}
|
|
256
256
|
|
|
257
|
-
increment (name, count = 1, tag
|
|
258
|
-
this.count(name, count, tag
|
|
257
|
+
increment (name, count = 1, tag) {
|
|
258
|
+
this.count(name, count, tag)
|
|
259
259
|
}
|
|
260
260
|
|
|
261
261
|
decrement (name, count = 1, tag) {
|
|
@@ -5,11 +5,22 @@ const { Chunk, MsgpackEncoder } = require('../msgpack')
|
|
|
5
5
|
const log = require('../log')
|
|
6
6
|
const { isTrue } = require('../util')
|
|
7
7
|
const coalesce = require('koalas')
|
|
8
|
+
const { memoize } = require('../log/utils')
|
|
8
9
|
|
|
9
10
|
const SOFT_LIMIT = 8 * 1024 * 1024 // 8MB
|
|
10
11
|
|
|
11
|
-
function formatSpan (span) {
|
|
12
|
-
|
|
12
|
+
function formatSpan (span, config) {
|
|
13
|
+
span = normalizeSpan(truncateSpan(span, false))
|
|
14
|
+
if (span.span_events) {
|
|
15
|
+
// ensure span events are encoded as tags if agent doesn't support native top level span events
|
|
16
|
+
if (!config?.trace?.nativeSpanEvents) {
|
|
17
|
+
span.meta.events = JSON.stringify(span.span_events)
|
|
18
|
+
delete span.span_events
|
|
19
|
+
} else {
|
|
20
|
+
formatSpanEvents(span)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return span
|
|
13
24
|
}
|
|
14
25
|
|
|
15
26
|
class AgentEncoder {
|
|
@@ -24,6 +35,7 @@ class AgentEncoder {
|
|
|
24
35
|
process.env.DD_TRACE_ENCODING_DEBUG,
|
|
25
36
|
false
|
|
26
37
|
))
|
|
38
|
+
this._config = this._writer?._config
|
|
27
39
|
}
|
|
28
40
|
|
|
29
41
|
count () {
|
|
@@ -74,16 +86,18 @@ class AgentEncoder {
|
|
|
74
86
|
this._encodeArrayPrefix(bytes, trace)
|
|
75
87
|
|
|
76
88
|
for (let span of trace) {
|
|
77
|
-
span = formatSpan(span)
|
|
89
|
+
span = formatSpan(span, this._config)
|
|
78
90
|
bytes.reserve(1)
|
|
79
91
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
92
|
+
// this is the original size of the fixed map for span attributes that always exist
|
|
93
|
+
let mapSize = 11
|
|
94
|
+
|
|
95
|
+
// increment the payload map size depending on if some optional fields exist
|
|
96
|
+
if (span.type) mapSize += 1
|
|
97
|
+
if (span.meta_struct) mapSize += 1
|
|
98
|
+
if (span.span_events) mapSize += 1
|
|
99
|
+
|
|
100
|
+
bytes.buffer[bytes.length - 1] = 0x80 + mapSize
|
|
87
101
|
|
|
88
102
|
if (span.type) {
|
|
89
103
|
this._encodeString(bytes, 'type')
|
|
@@ -112,6 +126,10 @@ class AgentEncoder {
|
|
|
112
126
|
this._encodeMap(bytes, span.meta)
|
|
113
127
|
this._encodeString(bytes, 'metrics')
|
|
114
128
|
this._encodeMap(bytes, span.metrics)
|
|
129
|
+
if (span.span_events) {
|
|
130
|
+
this._encodeString(bytes, 'span_events')
|
|
131
|
+
this._encodeObjectAsArray(bytes, span.span_events, new Set())
|
|
132
|
+
}
|
|
115
133
|
if (span.meta_struct) {
|
|
116
134
|
this._encodeString(bytes, 'meta_struct')
|
|
117
135
|
this._encodeMetaStruct(bytes, span.meta_struct)
|
|
@@ -200,6 +218,9 @@ class AgentEncoder {
|
|
|
200
218
|
case 'number':
|
|
201
219
|
this._encodeFloat(bytes, value)
|
|
202
220
|
break
|
|
221
|
+
case 'boolean':
|
|
222
|
+
this._encodeBool(bytes, value)
|
|
223
|
+
break
|
|
203
224
|
default:
|
|
204
225
|
// should not happen
|
|
205
226
|
}
|
|
@@ -258,7 +279,7 @@ class AgentEncoder {
|
|
|
258
279
|
this._encodeObjectAsArray(bytes, value, circularReferencesDetector)
|
|
259
280
|
} else if (value !== null && typeof value === 'object') {
|
|
260
281
|
this._encodeObjectAsMap(bytes, value, circularReferencesDetector)
|
|
261
|
-
} else if (typeof value === 'string' || typeof value === 'number') {
|
|
282
|
+
} else if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
262
283
|
this._encodeValue(bytes, value)
|
|
263
284
|
}
|
|
264
285
|
}
|
|
@@ -268,7 +289,7 @@ class AgentEncoder {
|
|
|
268
289
|
const validKeys = keys.filter(key => {
|
|
269
290
|
const v = value[key]
|
|
270
291
|
return typeof v === 'string' ||
|
|
271
|
-
typeof v === 'number' ||
|
|
292
|
+
typeof v === 'number' || typeof v === 'boolean' ||
|
|
272
293
|
(v !== null && typeof v === 'object' && !circularReferencesDetector.has(v))
|
|
273
294
|
})
|
|
274
295
|
|
|
@@ -319,4 +340,79 @@ class AgentEncoder {
|
|
|
319
340
|
}
|
|
320
341
|
}
|
|
321
342
|
|
|
343
|
+
const memoizedLogDebug = memoize((key, message) => {
|
|
344
|
+
log.debug(message)
|
|
345
|
+
// return something to store in memoize cache
|
|
346
|
+
return true
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
function formatSpanEvents (span) {
|
|
350
|
+
for (const spanEvent of span.span_events) {
|
|
351
|
+
if (spanEvent.attributes) {
|
|
352
|
+
for (const [key, value] of Object.entries(spanEvent.attributes)) {
|
|
353
|
+
const newValue = convertSpanEventAttributeValues(key, value)
|
|
354
|
+
if (newValue !== undefined) {
|
|
355
|
+
spanEvent.attributes[key] = newValue
|
|
356
|
+
} else {
|
|
357
|
+
delete spanEvent.attributes[key] // delete from attributes if undefined
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
if (Object.entries(spanEvent.attributes).length === 0) {
|
|
361
|
+
delete spanEvent.attributes
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function convertSpanEventAttributeValues (key, value, depth = 0) {
|
|
368
|
+
if (typeof value === 'string') {
|
|
369
|
+
return {
|
|
370
|
+
type: 0,
|
|
371
|
+
string_value: value
|
|
372
|
+
}
|
|
373
|
+
} else if (typeof value === 'boolean') {
|
|
374
|
+
return {
|
|
375
|
+
type: 1,
|
|
376
|
+
bool_value: value
|
|
377
|
+
}
|
|
378
|
+
} else if (Number.isInteger(value)) {
|
|
379
|
+
return {
|
|
380
|
+
type: 2,
|
|
381
|
+
int_value: value
|
|
382
|
+
}
|
|
383
|
+
} else if (typeof value === 'number') {
|
|
384
|
+
return {
|
|
385
|
+
type: 3,
|
|
386
|
+
double_value: value
|
|
387
|
+
}
|
|
388
|
+
} else if (Array.isArray(value)) {
|
|
389
|
+
if (depth === 0) {
|
|
390
|
+
const convertedArray = value
|
|
391
|
+
.map((val) => convertSpanEventAttributeValues(key, val, 1))
|
|
392
|
+
.filter((convertedVal) => convertedVal !== undefined)
|
|
393
|
+
|
|
394
|
+
// Only include array_value if there are valid elements
|
|
395
|
+
if (convertedArray.length > 0) {
|
|
396
|
+
return {
|
|
397
|
+
type: 4,
|
|
398
|
+
array_value: convertedArray
|
|
399
|
+
}
|
|
400
|
+
} else {
|
|
401
|
+
// If all elements were unsupported, return undefined
|
|
402
|
+
return undefined
|
|
403
|
+
}
|
|
404
|
+
} else {
|
|
405
|
+
memoizedLogDebug(key, 'Encountered nested array data type for span event v0.4 encoding. ' +
|
|
406
|
+
`Skipping encoding key: ${key}: with value: ${typeof value}.`
|
|
407
|
+
)
|
|
408
|
+
return undefined
|
|
409
|
+
}
|
|
410
|
+
} else {
|
|
411
|
+
memoizedLogDebug(key, 'Encountered unsupported data type for span event v0.4 encoding, key: ' +
|
|
412
|
+
`${key}: with value: ${typeof value}. Skipping encoding of pair.`
|
|
413
|
+
)
|
|
414
|
+
return undefined
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
322
418
|
module.exports = { AgentEncoder }
|
|
@@ -7,7 +7,13 @@ const ARRAY_OF_TWO = 0x92
|
|
|
7
7
|
const ARRAY_OF_TWELVE = 0x9c
|
|
8
8
|
|
|
9
9
|
function formatSpan (span) {
|
|
10
|
-
|
|
10
|
+
span = normalizeSpan(truncateSpan(span, false))
|
|
11
|
+
// ensure span events are encoded as tags
|
|
12
|
+
if (span.span_events) {
|
|
13
|
+
span.meta.events = JSON.stringify(span.span_events)
|
|
14
|
+
delete span.span_events
|
|
15
|
+
}
|
|
16
|
+
return span
|
|
11
17
|
}
|
|
12
18
|
|
|
13
19
|
class AgentEncoder extends BaseEncoder {
|
|
@@ -10,15 +10,16 @@ const BaseWriter = require('../common/writer')
|
|
|
10
10
|
const METRIC_PREFIX = 'datadog.tracer.node.exporter.agent'
|
|
11
11
|
|
|
12
12
|
class Writer extends BaseWriter {
|
|
13
|
-
constructor ({ prioritySampler, lookup, protocolVersion, headers }) {
|
|
13
|
+
constructor ({ prioritySampler, lookup, protocolVersion, headers, config = {} }) {
|
|
14
14
|
super(...arguments)
|
|
15
15
|
const AgentEncoder = getEncoder(protocolVersion)
|
|
16
16
|
|
|
17
17
|
this._prioritySampler = prioritySampler
|
|
18
18
|
this._lookup = lookup
|
|
19
19
|
this._protocolVersion = protocolVersion
|
|
20
|
-
this._encoder = new AgentEncoder(this)
|
|
21
20
|
this._headers = headers
|
|
21
|
+
this._config = config
|
|
22
|
+
this._encoder = new AgentEncoder(this)
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
_sendPayload (data, count, done) {
|
|
@@ -68,7 +68,7 @@ function setSingleSpanIngestionTags (span, options) {
|
|
|
68
68
|
addTag({}, span.metrics, SPAN_SAMPLING_MAX_PER_SECOND, options.maxPerSecond)
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
function extractSpanLinks (
|
|
71
|
+
function extractSpanLinks (formattedSpan, span) {
|
|
72
72
|
const links = []
|
|
73
73
|
if (span._links) {
|
|
74
74
|
for (const link of span._links) {
|
|
@@ -87,10 +87,10 @@ function extractSpanLinks (trace, span) {
|
|
|
87
87
|
links.push(formattedLink)
|
|
88
88
|
}
|
|
89
89
|
}
|
|
90
|
-
if (links.length > 0) {
|
|
90
|
+
if (links.length > 0) { formattedSpan.meta['_dd.span_links'] = JSON.stringify(links) }
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
function extractSpanEvents (
|
|
93
|
+
function extractSpanEvents (formattedSpan, span) {
|
|
94
94
|
const events = []
|
|
95
95
|
if (span._events) {
|
|
96
96
|
for (const event of span._events) {
|
|
@@ -103,10 +103,12 @@ function extractSpanEvents (trace, span) {
|
|
|
103
103
|
events.push(formattedEvent)
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
|
-
if (events.length > 0) {
|
|
106
|
+
if (events.length > 0) {
|
|
107
|
+
formattedSpan.span_events = events
|
|
108
|
+
}
|
|
107
109
|
}
|
|
108
110
|
|
|
109
|
-
function extractTags (
|
|
111
|
+
function extractTags (formattedSpan, span) {
|
|
110
112
|
const context = span.context()
|
|
111
113
|
const origin = context._trace.origin
|
|
112
114
|
const tags = context._tags
|
|
@@ -114,7 +116,7 @@ function extractTags (trace, span) {
|
|
|
114
116
|
const priority = context._sampling.priority
|
|
115
117
|
|
|
116
118
|
if (tags['span.kind'] && tags['span.kind'] !== 'internal') {
|
|
117
|
-
addTag({},
|
|
119
|
+
addTag({}, formattedSpan.metrics, MEASURED, 1)
|
|
118
120
|
}
|
|
119
121
|
|
|
120
122
|
const tracerService = span.tracer()._service.toLowerCase()
|
|
@@ -129,22 +131,22 @@ function extractTags (trace, span) {
|
|
|
129
131
|
case 'service.name':
|
|
130
132
|
case 'span.type':
|
|
131
133
|
case 'resource.name':
|
|
132
|
-
addTag(
|
|
134
|
+
addTag(formattedSpan, {}, map[tag], tags[tag])
|
|
133
135
|
break
|
|
134
136
|
// HACK: remove when Datadog supports numeric status code
|
|
135
137
|
case 'http.status_code':
|
|
136
|
-
addTag(
|
|
138
|
+
addTag(formattedSpan.meta, {}, tag, tags[tag] && String(tags[tag]))
|
|
137
139
|
break
|
|
138
140
|
case 'analytics.event':
|
|
139
|
-
addTag({},
|
|
141
|
+
addTag({}, formattedSpan.metrics, ANALYTICS, tags[tag] === undefined || tags[tag] ? 1 : 0)
|
|
140
142
|
break
|
|
141
143
|
case HOSTNAME_KEY:
|
|
142
144
|
case MEASURED:
|
|
143
|
-
addTag({},
|
|
145
|
+
addTag({}, formattedSpan.metrics, tag, tags[tag] === undefined || tags[tag] ? 1 : 0)
|
|
144
146
|
break
|
|
145
147
|
case 'error':
|
|
146
148
|
if (context._name !== 'fs.operation') {
|
|
147
|
-
extractError(
|
|
149
|
+
extractError(formattedSpan, tags[tag])
|
|
148
150
|
}
|
|
149
151
|
break
|
|
150
152
|
case ERROR_TYPE:
|
|
@@ -152,60 +154,60 @@ function extractTags (trace, span) {
|
|
|
152
154
|
case ERROR_STACK:
|
|
153
155
|
// HACK: remove when implemented in the backend
|
|
154
156
|
if (context._name !== 'fs.operation') {
|
|
155
|
-
// HACK: to ensure otel.recordException does not influence
|
|
157
|
+
// HACK: to ensure otel.recordException does not influence formattedSpan.error
|
|
156
158
|
if (tags.setTraceError) {
|
|
157
|
-
|
|
159
|
+
formattedSpan.error = 1
|
|
158
160
|
}
|
|
159
161
|
} else {
|
|
160
162
|
break
|
|
161
163
|
}
|
|
162
164
|
default: // eslint-disable-line no-fallthrough
|
|
163
|
-
addTag(
|
|
165
|
+
addTag(formattedSpan.meta, formattedSpan.metrics, tag, tags[tag])
|
|
164
166
|
}
|
|
165
167
|
}
|
|
166
|
-
setSingleSpanIngestionTags(
|
|
168
|
+
setSingleSpanIngestionTags(formattedSpan, context._spanSampling)
|
|
167
169
|
|
|
168
|
-
addTag(
|
|
169
|
-
addTag(
|
|
170
|
-
addTag(
|
|
171
|
-
addTag(
|
|
172
|
-
addTag(
|
|
170
|
+
addTag(formattedSpan.meta, formattedSpan.metrics, 'language', 'javascript')
|
|
171
|
+
addTag(formattedSpan.meta, formattedSpan.metrics, PROCESS_ID, process.pid)
|
|
172
|
+
addTag(formattedSpan.meta, formattedSpan.metrics, SAMPLING_PRIORITY_KEY, priority)
|
|
173
|
+
addTag(formattedSpan.meta, formattedSpan.metrics, ORIGIN_KEY, origin)
|
|
174
|
+
addTag(formattedSpan.meta, formattedSpan.metrics, HOSTNAME_KEY, hostname)
|
|
173
175
|
}
|
|
174
176
|
|
|
175
|
-
function extractRootTags (
|
|
177
|
+
function extractRootTags (formattedSpan, span) {
|
|
176
178
|
const context = span.context()
|
|
177
179
|
const isLocalRoot = span === context._trace.started[0]
|
|
178
180
|
const parentId = context._parentId
|
|
179
181
|
|
|
180
182
|
if (!isLocalRoot || (parentId && parentId.toString(10) !== '0')) return
|
|
181
183
|
|
|
182
|
-
addTag({},
|
|
183
|
-
addTag({},
|
|
184
|
-
addTag({},
|
|
185
|
-
addTag({},
|
|
184
|
+
addTag({}, formattedSpan.metrics, SAMPLING_RULE_DECISION, context._trace[SAMPLING_RULE_DECISION])
|
|
185
|
+
addTag({}, formattedSpan.metrics, SAMPLING_LIMIT_DECISION, context._trace[SAMPLING_LIMIT_DECISION])
|
|
186
|
+
addTag({}, formattedSpan.metrics, SAMPLING_AGENT_DECISION, context._trace[SAMPLING_AGENT_DECISION])
|
|
187
|
+
addTag({}, formattedSpan.metrics, TOP_LEVEL_KEY, 1)
|
|
186
188
|
}
|
|
187
189
|
|
|
188
|
-
function extractChunkTags (
|
|
190
|
+
function extractChunkTags (formattedSpan, span) {
|
|
189
191
|
const context = span.context()
|
|
190
192
|
const isLocalRoot = span === context._trace.started[0]
|
|
191
193
|
|
|
192
194
|
if (!isLocalRoot) return
|
|
193
195
|
|
|
194
196
|
for (const key in context._trace.tags) {
|
|
195
|
-
addTag(
|
|
197
|
+
addTag(formattedSpan.meta, formattedSpan.metrics, key, context._trace.tags[key])
|
|
196
198
|
}
|
|
197
199
|
}
|
|
198
200
|
|
|
199
|
-
function extractError (
|
|
201
|
+
function extractError (formattedSpan, error) {
|
|
200
202
|
if (!error) return
|
|
201
203
|
|
|
202
|
-
|
|
204
|
+
formattedSpan.error = 1
|
|
203
205
|
|
|
204
206
|
if (isError(error)) {
|
|
205
207
|
// AggregateError only has a code and no message.
|
|
206
|
-
addTag(
|
|
207
|
-
addTag(
|
|
208
|
-
addTag(
|
|
208
|
+
addTag(formattedSpan.meta, formattedSpan.metrics, ERROR_MESSAGE, error.message || error.code)
|
|
209
|
+
addTag(formattedSpan.meta, formattedSpan.metrics, ERROR_TYPE, error.name)
|
|
210
|
+
addTag(formattedSpan.meta, formattedSpan.metrics, ERROR_STACK, error.stack)
|
|
209
211
|
}
|
|
210
212
|
}
|
|
211
213
|
|
|
@@ -105,12 +105,12 @@ class LLMObs extends NoopLLMObs {
|
|
|
105
105
|
|
|
106
106
|
if (fn.length > 1) {
|
|
107
107
|
return this._tracer.trace(name, spanOptions, (span, cb) =>
|
|
108
|
-
this._activate(span, { kind,
|
|
108
|
+
this._activate(span, { kind, ...llmobsOptions }, () => fn(span, cb))
|
|
109
109
|
)
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
return this._tracer.trace(name, spanOptions, span =>
|
|
113
|
-
this._activate(span, { kind,
|
|
113
|
+
this._activate(span, { kind, ...llmobsOptions }, () => fn(span))
|
|
114
114
|
)
|
|
115
115
|
}
|
|
116
116
|
|
|
@@ -166,7 +166,7 @@ class LLMObs extends NoopLLMObs {
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
try {
|
|
169
|
-
const result = llmobs._activate(span, { kind,
|
|
169
|
+
const result = llmobs._activate(span, { kind, ...llmobsOptions }, () => fn.apply(this, fnArgs))
|
|
170
170
|
|
|
171
171
|
if (result && typeof result.then === 'function') {
|
|
172
172
|
return result.then(
|
|
@@ -6,19 +6,7 @@ const { isTrue } = require('../util')
|
|
|
6
6
|
const { traceChannel, debugChannel, infoChannel, warnChannel, errorChannel } = require('./channels')
|
|
7
7
|
const logWriter = require('./writer')
|
|
8
8
|
const { Log } = require('./log')
|
|
9
|
-
|
|
10
|
-
const memoize = func => {
|
|
11
|
-
const cache = {}
|
|
12
|
-
const memoized = function (key) {
|
|
13
|
-
if (!cache[key]) {
|
|
14
|
-
cache[key] = func.apply(this, arguments)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return cache[key]
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return memoized
|
|
21
|
-
}
|
|
9
|
+
const { memoize } = require('./utils')
|
|
22
10
|
|
|
23
11
|
const config = {
|
|
24
12
|
enabled: false,
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const memoize = func => {
|
|
4
|
+
const cache = {}
|
|
5
|
+
const memoized = function (key) {
|
|
6
|
+
if (!cache[key]) {
|
|
7
|
+
cache[key] = func.apply(this, arguments)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return cache[key]
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return memoized
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module.exports = { memoize }
|
|
@@ -111,11 +111,11 @@ const runtimeMetrics = module.exports = {
|
|
|
111
111
|
},
|
|
112
112
|
|
|
113
113
|
increment (name, tag, monotonic) {
|
|
114
|
-
|
|
114
|
+
this.count(name, 1, tag, monotonic)
|
|
115
115
|
},
|
|
116
116
|
|
|
117
117
|
decrement (name, tag) {
|
|
118
|
-
|
|
118
|
+
this.count(name, -1, tag)
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
|
|
@@ -211,7 +211,7 @@ function captureGCMetrics () {
|
|
|
211
211
|
histogram('runtime.node.gc.pause', pauseAll)
|
|
212
212
|
|
|
213
213
|
for (const type in pause) {
|
|
214
|
-
histogram('runtime.node.gc.pause.by.type', pause[type],
|
|
214
|
+
histogram('runtime.node.gc.pause.by.type', pause[type], `gc_type:${type}`)
|
|
215
215
|
}
|
|
216
216
|
|
|
217
217
|
gcProfiler.start()
|
|
@@ -265,7 +265,7 @@ function captureNativeMetrics () {
|
|
|
265
265
|
if (type === 'all') {
|
|
266
266
|
histogram('runtime.node.gc.pause', stats.gc[type])
|
|
267
267
|
} else {
|
|
268
|
-
histogram('runtime.node.gc.pause.by.type', stats.gc[type],
|
|
268
|
+
histogram('runtime.node.gc.pause.by.type', stats.gc[type], `gc_type:${type}`)
|
|
269
269
|
}
|
|
270
270
|
})
|
|
271
271
|
|
|
@@ -279,16 +279,15 @@ function captureNativeMetrics () {
|
|
|
279
279
|
}
|
|
280
280
|
}
|
|
281
281
|
|
|
282
|
-
function histogram (name, stats,
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
}
|
|
282
|
+
function histogram (name, stats, tag) {
|
|
283
|
+
client.gauge(`${name}.min`, stats.min, tag)
|
|
284
|
+
client.gauge(`${name}.max`, stats.max, tag)
|
|
285
|
+
client.increment(`${name}.sum`, stats.sum, tag)
|
|
286
|
+
client.increment(`${name}.total`, stats.sum, tag)
|
|
287
|
+
client.gauge(`${name}.avg`, stats.avg, tag)
|
|
288
|
+
client.increment(`${name}.count`, stats.count, tag)
|
|
289
|
+
client.gauge(`${name}.median`, stats.median, tag)
|
|
290
|
+
client.gauge(`${name}.95percentile`, stats.p95, tag)
|
|
292
291
|
}
|
|
293
292
|
|
|
294
293
|
function startGCObserver () {
|