dd-trace 5.88.0 → 5.89.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/ext/tags.js +2 -0
  2. package/index.d.ts +9 -0
  3. package/package.json +12 -8
  4. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +26 -111
  5. package/packages/datadog-instrumentations/src/helpers/rewriter/{compiler.js → orchestrion/compiler.js} +5 -5
  6. package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/index.js +43 -0
  7. package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/matcher.js +49 -0
  8. package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/transformer.js +121 -0
  9. package/packages/datadog-instrumentations/src/helpers/rewriter/{transforms.js → orchestrion/transforms.js} +6 -6
  10. package/packages/datadog-instrumentations/src/jest.js +101 -43
  11. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +36 -5
  12. package/packages/datadog-plugin-cypress/src/source-map-utils.js +297 -0
  13. package/packages/datadog-plugin-cypress/src/support.js +4 -1
  14. package/packages/dd-trace/src/aiguard/sdk.js +5 -1
  15. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +1 -0
  16. package/packages/dd-trace/src/config/index.js +2 -0
  17. package/packages/dd-trace/src/config/supported-configurations.json +10 -0
  18. package/packages/dd-trace/src/datastreams/checkpointer.js +13 -0
  19. package/packages/dd-trace/src/datastreams/index.js +3 -0
  20. package/packages/dd-trace/src/datastreams/manager.js +9 -0
  21. package/packages/dd-trace/src/datastreams/processor.js +126 -3
  22. package/packages/dd-trace/src/encode/agentless-json.js +16 -2
  23. package/packages/dd-trace/src/exporters/agent/writer.js +7 -8
  24. package/packages/dd-trace/src/pkg.js +1 -1
  25. package/packages/dd-trace/src/proxy.js +2 -1
  26. package/packages/dd-trace/src/startup-log.js +52 -18
  27. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  28. package/vendor/dist/@datadog/source-map/index.js +1 -1
  29. package/vendor/dist/@isaacs/ttlcache/index.js +1 -1
  30. package/vendor/dist/@opentelemetry/core/index.js +1 -1
  31. package/vendor/dist/@opentelemetry/resources/index.js +1 -1
  32. package/vendor/dist/astring/index.js +1 -1
  33. package/vendor/dist/crypto-randomuuid/index.js +1 -1
  34. package/vendor/dist/escape-string-regexp/index.js +1 -1
  35. package/vendor/dist/esquery/index.js +1 -1
  36. package/vendor/dist/ignore/index.js +1 -1
  37. package/vendor/dist/istanbul-lib-coverage/index.js +1 -1
  38. package/vendor/dist/jest-docblock/index.js +1 -1
  39. package/vendor/dist/jsonpath-plus/index.js +1 -1
  40. package/vendor/dist/limiter/index.js +1 -1
  41. package/vendor/dist/lodash.sortby/index.js +1 -1
  42. package/vendor/dist/lru-cache/index.js +1 -1
  43. package/vendor/dist/meriyah/index.js +1 -1
  44. package/vendor/dist/module-details-from-path/index.js +1 -1
  45. package/vendor/dist/mutexify/promise/index.js +1 -1
  46. package/vendor/dist/opentracing/index.js +1 -1
  47. package/vendor/dist/path-to-regexp/index.js +1 -1
  48. package/vendor/dist/pprof-format/index.js +1 -1
  49. package/vendor/dist/protobufjs/index.js +1 -1
  50. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  51. package/vendor/dist/retry/index.js +1 -1
  52. package/vendor/dist/rfdc/index.js +1 -1
  53. package/vendor/dist/semifies/index.js +1 -1
  54. package/vendor/dist/shell-quote/index.js +1 -1
  55. package/vendor/dist/source-map/index.js +1 -1
  56. package/vendor/dist/source-map/lib/util/index.js +1 -1
  57. package/vendor/dist/tlhunter-sorted-set/index.js +1 -1
  58. package/vendor/dist/ttl-set/index.js +1 -1
  59. package/packages/datadog-instrumentations/src/helpers/rewriter/transformer.js +0 -21
@@ -60,6 +60,19 @@ class DataStreamsCheckpointer {
60
60
 
61
61
  return ctx
62
62
  }
63
+
64
+ /**
65
+ * Records a transaction ID at a named checkpoint without pathway propagation.
66
+ * Tags the active span (or the provided span) with the transaction ID and checkpoint name.
67
+ * @param {string} transactionId - The transaction identifier to track.
68
+ * @param {string} checkpointName - The logical checkpoint name.
69
+ * @param {object|null} [span=null] - Span to tag. Defaults to the currently active span.
70
+ */
71
+ trackTransaction (transactionId, checkpointName, span = null) {
72
+ if (!this.config.dsmEnabled) return
73
+ const activeSpan = span ?? this.tracer.scope().active()
74
+ this.dsmProcessor.trackTransaction(transactionId, checkpointName, activeSpan)
75
+ }
63
76
  }
64
77
 
65
78
  module.exports = {
@@ -71,6 +71,7 @@ const DsmPathwayCodec = lazyClass(() => require('./pathway').DsmPathwayCodec, []
71
71
  const DataStreamsCheckpointer = lazyClass(() => require('./checkpointer').DataStreamsCheckpointer, [
72
72
  'setProduceCheckpoint',
73
73
  'setConsumeCheckpoint',
74
+ 'trackTransaction',
74
75
  ])
75
76
 
76
77
  /**
@@ -79,6 +80,7 @@ const DataStreamsCheckpointer = lazyClass(() => require('./checkpointer').DataSt
79
80
  const DataStreamsManager = lazyClass(() => require('./manager').DataStreamsManager, [
80
81
  'setCheckpoint',
81
82
  'decodeDataStreamsContext',
83
+ 'trackTransaction',
82
84
  ])
83
85
 
84
86
  // TODO: Are all those methods actually public?
@@ -92,6 +94,7 @@ const DataStreamsProcessor = lazyClass(() => require('./processor').DataStreamsP
92
94
  'setCheckpoint',
93
95
  'recordOffset',
94
96
  'setOffset',
97
+ 'trackTransaction',
95
98
  'setUrl',
96
99
  'trySampleSchema',
97
100
  'canSampleSchema',
@@ -22,6 +22,15 @@ class DataStreamsManager {
22
22
  DataStreamsContext.setDataStreamsContext(ctx)
23
23
  return ctx
24
24
  }
25
+
26
+ /**
27
+ * @param {string} transactionId
28
+ * @param {string} checkpointName
29
+ * @param {object|null} [span=null]
30
+ */
31
+ trackTransaction (transactionId, checkpointName, span = null) {
32
+ this._dataStreamsProcessor.trackTransaction(transactionId, checkpointName, span)
33
+ }
25
34
  }
26
35
 
27
36
  module.exports = { DataStreamsManager }
@@ -4,7 +4,7 @@ const os = require('os')
4
4
  const pkg = require('../../../../package.json')
5
5
 
6
6
  const { LogCollapsingLowestDenseDDSketch } = require('../../../../vendor/dist/@datadog/sketches-js')
7
- const { PATHWAY_HASH } = require('../../../../ext/tags')
7
+ const { PATHWAY_HASH, DSM_TRANSACTION_ID, DSM_TRANSACTION_CHECKPOINT } = require('../../../../ext/tags')
8
8
  const log = require('../log')
9
9
  const processTags = require('../process-tags')
10
10
  const propagationHash = require('../propagation-hash')
@@ -69,10 +69,66 @@ class Backlog {
69
69
  }
70
70
  }
71
71
 
72
+ /**
73
+ * Maps checkpoint name strings to single-byte IDs (1–254).
74
+ * ID 0 is reserved; 254 is the maximum number of unique names.
75
+ * Scope is per-processor so IDs are stable across bucket boundaries within a process lifetime.
76
+ */
77
+ class CheckpointRegistry {
78
+ /** @type {Map<string, number>} */
79
+ #nameToId = new Map()
80
+ #nextId = 1
81
+ /** @type {Buffer[]} Pre-built [id uint8][nameLen uint8][name bytes] entries, one per registered name. */
82
+ #entryBuffers = []
83
+ /** @type {Buffer | null} Cached concat of #entryBuffers; reset when a new name is added. */
84
+ #encodedKeysCache = null
85
+
86
+ /**
87
+ * Returns the byte ID for the given checkpoint name, assigning one if not seen before.
88
+ * Returns undefined when registry is full (254 entries exhausted).
89
+ * @param {string} name
90
+ * @returns {number | undefined}
91
+ */
92
+ getId (name) {
93
+ const existing = this.#nameToId.get(name)
94
+ if (existing !== undefined) return existing
95
+ if (this.#nextId > 254) return
96
+ const id = this.#nextId++
97
+ this.#nameToId.set(name, id)
98
+ // Build the wire entry now with a bounded write so long names never materialise
99
+ // their full UTF-8 encoding — buf.write() stops at the supplied byte limit.
100
+ const nameBuf = Buffer.allocUnsafe(255)
101
+ const nameByteLen = nameBuf.write(name, 0, 255, 'utf8')
102
+ const entry = Buffer.allocUnsafe(2 + nameByteLen)
103
+ entry.writeUInt8(id, 0)
104
+ entry.writeUInt8(nameByteLen, 1)
105
+ nameBuf.copy(entry, 2, 0, nameByteLen)
106
+ this.#entryBuffers.push(entry)
107
+ this.#encodedKeysCache = null
108
+ return id
109
+ }
110
+
111
+ /**
112
+ * Returns a Buffer encoding all registered names as [id uint8][nameLen uint8][name bytes].
113
+ * Names are truncated to 255 UTF-8 bytes.
114
+ * Result is cached and only recomputed when new names are registered.
115
+ * @returns {Buffer}
116
+ */
117
+ get encodedKeys () {
118
+ if (this.#encodedKeysCache !== null) return this.#encodedKeysCache
119
+ this.#encodedKeysCache = this.#entryBuffers.length > 0
120
+ ? Buffer.concat(this.#entryBuffers)
121
+ : Buffer.alloc(0)
122
+ return this.#encodedKeysCache
123
+ }
124
+ }
125
+
72
126
  class StatsBucket {
73
127
  constructor () {
74
128
  this._checkpoints = new Map()
75
129
  this._backlogs = new Map()
130
+ /** @type {Buffer[]} Accumulated transaction byte chunks, concatenated lazily. */
131
+ this._transactionChunks = []
76
132
  }
77
133
 
78
134
  get checkpoints () {
@@ -83,6 +139,16 @@ class StatsBucket {
83
139
  return this._backlogs
84
140
  }
85
141
 
142
+ /**
143
+ * Returns the concatenated transaction bytes, or null if no transactions have been added.
144
+ * Concatenation is deferred to read time to avoid O(N²) copies during accumulation.
145
+ * @returns {Buffer | null}
146
+ */
147
+ get transactions () {
148
+ if (this._transactionChunks.length === 0) return null
149
+ return Buffer.concat(this._transactionChunks)
150
+ }
151
+
86
152
  forCheckpoint ({ hash, parentHash, edgeTags }) {
87
153
  let checkpoint = this._checkpoints.get(hash)
88
154
  if (!checkpoint) {
@@ -93,6 +159,14 @@ class StatsBucket {
93
159
  return checkpoint
94
160
  }
95
161
 
162
+ /**
163
+ * Appends pre-encoded transaction bytes to this bucket.
164
+ * @param {Buffer} bytes
165
+ */
166
+ addTransaction (bytes) {
167
+ this._transactionChunks.push(bytes)
168
+ }
169
+
96
170
  /**
97
171
  * Conditionally add a backlog to the bucket. If there is currently an offset
98
172
  * matching the backlog's tags, overwrite the offset IFF the backlog's offset
@@ -153,6 +227,7 @@ class DataStreamsProcessor {
153
227
  this.sequence = 0
154
228
  this.flushInterval = flushInterval
155
229
  this._schemaSamplers = {}
230
+ this._checkpointRegistry = new CheckpointRegistry()
156
231
 
157
232
  if (this.enabled) {
158
233
  this.timer = setInterval(this.onInterval.bind(this), flushInterval)
@@ -294,9 +369,47 @@ class DataStreamsProcessor {
294
369
  this.recordOffset(backlogData)
295
370
  }
296
371
 
372
+ /**
373
+ * Records a transaction ID at a named checkpoint using the binary wire format shared with Go/Java tracers.
374
+ *
375
+ * Wire format per entry: [checkpointId uint8][timestamp int64 big-endian 8 bytes][idLen uint8][id bytes]
376
+ *
377
+ * @param {string} transactionId - Truncated to 255 UTF-8 bytes.
378
+ * @param {string} checkpointName - Mapped to a stable 1-byte ID; silently dropped if registry full.
379
+ * @param {import('../opentelemetry/span').Span|null} [span=null] - Active span to tag with DSM transaction metadata.
380
+ */
381
+ trackTransaction (transactionId, checkpointName, span = null) {
382
+ if (!this.enabled) {
383
+ log.warn('trackTransaction called but DD_DATA_STREAMS_ENABLED is not set. Transaction will not be tracked.')
384
+ return
385
+ }
386
+
387
+ const checkpointId = this._checkpointRegistry.getId(checkpointName)
388
+ if (checkpointId === undefined) return
389
+
390
+ const idBytes = Buffer.from(transactionId, 'utf8').subarray(0, 255)
391
+ // Multiply as BigInt to avoid precision loss past MAX_SAFE_INTEGER
392
+ const timestampNs = BigInt(Date.now()) * 1_000_000n
393
+
394
+ const entry = Buffer.alloc(1 + 8 + 1 + idBytes.length)
395
+ entry.writeUInt8(checkpointId, 0)
396
+ entry.writeBigInt64BE(timestampNs, 1)
397
+ entry.writeUInt8(idBytes.length, 9)
398
+ idBytes.copy(entry, 10)
399
+
400
+ // Number() cast is safe here: 10s bucket granularity tolerates ~0.5ns precision loss
401
+ this.bucketFromTimestamp(Number(timestampNs)).addTransaction(entry)
402
+
403
+ if (span) {
404
+ span.setTag(DSM_TRANSACTION_ID, transactionId)
405
+ span.setTag(DSM_TRANSACTION_CHECKPOINT, checkpointName)
406
+ }
407
+ }
408
+
297
409
  _serializeBuckets () {
298
410
  // TimeBuckets
299
411
  const serializedBuckets = []
412
+ const registrySnapshot = this._checkpointRegistry.encodedKeys
300
413
 
301
414
  for (const [timeNs, bucket] of this.buckets.entries()) {
302
415
  const points = []
@@ -311,12 +424,21 @@ class DataStreamsProcessor {
311
424
  for (const backlog of bucket._backlogs.values()) {
312
425
  backlogs.push(backlog.encode())
313
426
  }
314
- serializedBuckets.push({
427
+
428
+ const serializedBucket = {
315
429
  Start: BigInt(timeNs),
316
430
  Duration: BigInt(this.bucketSizeNs),
317
431
  Stats: points,
318
432
  Backlogs: backlogs,
319
- })
433
+ }
434
+
435
+ const transactions = bucket.transactions
436
+ if (transactions !== null) {
437
+ serializedBucket.Transactions = transactions
438
+ serializedBucket.TransactionCheckpointIds = registrySnapshot
439
+ }
440
+
441
+ serializedBuckets.push(serializedBucket)
320
442
  }
321
443
 
322
444
  this.buckets.clear()
@@ -358,6 +480,7 @@ class DataStreamsProcessor {
358
480
  }
359
481
 
360
482
  module.exports = {
483
+ CheckpointRegistry,
361
484
  DataStreamsProcessor,
362
485
  StatsPoint,
363
486
  StatsBucket,
@@ -1,14 +1,16 @@
1
1
  'use strict'
2
2
 
3
3
  const log = require('../log')
4
+ const { TOP_LEVEL_KEY } = require('../constants')
4
5
  const { truncateSpan, normalizeSpan } = require('./tags-processors')
5
6
 
6
7
  /**
7
8
  * Formats a span for JSON encoding.
8
9
  * @param {object} span - The span to format
10
+ * @param {boolean} isFirstSpan - Whether this is the first span in the trace
9
11
  * @returns {object} The formatted span
10
12
  */
11
- function formatSpan (span) {
13
+ function formatSpan (span, isFirstSpan) {
12
14
  span = normalizeSpan(truncateSpan(span, false))
13
15
 
14
16
  if (span.span_events) {
@@ -16,6 +18,18 @@ function formatSpan (span) {
16
18
  delete span.span_events
17
19
  }
18
20
 
21
+ if (isFirstSpan) {
22
+ span.meta['_dd.compute_stats'] = '1'
23
+ }
24
+
25
+ if (span.parent_id?.toString(10) === '0') {
26
+ span.metrics._trace_root = 1
27
+ }
28
+
29
+ if (span.metrics[TOP_LEVEL_KEY]) {
30
+ span.metrics._top_level = 1
31
+ }
32
+
19
33
  return span
20
34
  }
21
35
 
@@ -83,7 +97,7 @@ class AgentlessJSONEncoder {
83
97
  encode (trace) {
84
98
  for (const span of trace) {
85
99
  try {
86
- const formattedSpan = formatSpan(span)
100
+ const formattedSpan = formatSpan(span, this._spanCount === 0)
87
101
  const jsonSpan = spanToJSON(formattedSpan)
88
102
 
89
103
  this._spans.push(jsonSpan)
@@ -1,7 +1,8 @@
1
1
  'use strict'
2
2
 
3
+ const { inspect } = require('node:util')
3
4
  const request = require('../common/request')
4
- const { startupLog } = require('../../startup-log')
5
+ const { logIntegrations, logAgentError } = require('../../startup-log')
5
6
  const runtimeMetrics = require('../../runtime_metrics')
6
7
  const log = require('../../log')
7
8
  const tracerVersion = require('../../../../../package.json').version
@@ -28,7 +29,7 @@ class AgentWriter extends BaseWriter {
28
29
  runtimeMetrics.increment(`${METRIC_PREFIX}.requests`, true)
29
30
 
30
31
  const { _headers, _lookup, _protocolVersion, _url } = this
31
- makeRequest(_protocolVersion, data, count, _url, _headers, _lookup, true, (err, res, status, headers) => {
32
+ makeRequest(_protocolVersion, data, count, _url, _headers, _lookup, (err, res, status, headers) => {
32
33
  if (status) {
33
34
  runtimeMetrics.increment(`${METRIC_PREFIX}.responses`, true)
34
35
  runtimeMetrics.increment(`${METRIC_PREFIX}.responses.by.status`, `status:${status}`, true)
@@ -78,7 +79,7 @@ function getEncoder (protocolVersion) {
78
79
  : require('../../encode/0.4').AgentEncoder
79
80
  }
80
81
 
81
- function makeRequest (version, data, count, url, headers, lookup, needsStartupLog, cb) {
82
+ function makeRequest (version, data, count, url, headers, lookup, cb) {
82
83
  const options = {
83
84
  path: `/v${version}/traces`,
84
85
  method: 'PUT',
@@ -98,11 +99,9 @@ function makeRequest (version, data, count, url, headers, lookup, needsStartupLo
98
99
  log.debug('Request to the agent: %j', options)
99
100
 
100
101
  request(data, options, (err, res, status, headers) => {
101
- if (needsStartupLog) {
102
- // Note that logging will only happen once, regardless of how many times this is called.
103
- startupLog({
104
- agentError: status !== 404 && status !== 200 ? err : undefined,
105
- })
102
+ logIntegrations()
103
+ if (status !== 404 && status !== 200 && err) {
104
+ logAgentError({ status, message: err.message ?? inspect(err) })
106
105
  }
107
106
  cb(err, res, status, headers)
108
107
  })
@@ -23,7 +23,7 @@ function findPkg () {
23
23
  if (filePath === undefined) return {}
24
24
 
25
25
  try {
26
- return require(filePath)
26
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'))
27
27
  } catch {
28
28
  return {}
29
29
  }
@@ -6,7 +6,7 @@ const DatadogTracer = require('./tracer')
6
6
  const getConfig = require('./config')
7
7
  const runtimeMetrics = require('./runtime_metrics')
8
8
  const log = require('./log')
9
- const { setStartupLogPluginManager } = require('./startup-log')
9
+ const { setStartupLogPluginManager, startupLog } = require('./startup-log')
10
10
  const DynamicInstrumentation = require('./debugger')
11
11
  const telemetry = require('./telemetry')
12
12
  const nomenclature = require('./service-naming')
@@ -292,6 +292,7 @@ class Tracer extends NoopProxy {
292
292
  this._pluginManager.configure(config)
293
293
  DynamicInstrumentation.configure(config)
294
294
  setStartupLogPluginManager(this._pluginManager)
295
+ startupLog()
295
296
  }
296
297
  }
297
298
 
@@ -11,41 +11,66 @@ let config
11
11
  let pluginManager
12
12
  /** @type {import('./sampling_rule')[]} */
13
13
  let samplingRules = []
14
- let alreadyRan = false
14
+ let configAlreadyRan = false
15
+ let integrationsAlreadyRan = false
16
+ let agentErrorAlreadyRan = false
15
17
 
16
18
  /**
17
- * @param {{ status: number, message: string } } [agentError]
19
+ * Logs DATADOG TRACER CONFIGURATION immediately at init time.
20
+ * Excludes integrations_loaded since plugins haven't loaded yet.
18
21
  */
19
- function startupLog (agentError) {
20
- if (alreadyRan || !config || !config.startupLogs || !pluginManager) {
22
+ function startupLog () {
23
+ if (configAlreadyRan || !config || !config.startupLogs) {
21
24
  return
22
25
  }
23
26
 
24
- alreadyRan = true
27
+ configAlreadyRan = true
25
28
 
26
- const out = tracerInfo()
29
+ const out = configInfo()
27
30
 
28
- if (agentError) {
29
- out.agent_error = agentError.message
31
+ warn('DATADOG TRACER CONFIGURATION - ' + out)
32
+ }
33
+
34
+ /**
35
+ * Logs loaded integrations. Called from writer.js on first agent payload,
36
+ * by which time the app has loaded its dependencies.
37
+ */
38
+ function logIntegrations () {
39
+ if (integrationsAlreadyRan || !config || !config.startupLogs || !pluginManager) {
40
+ return
30
41
  }
31
42
 
32
- warn('DATADOG TRACER CONFIGURATION - ' + out)
33
- if (agentError) {
34
- warn('DATADOG TRACER DIAGNOSTIC - Agent Error: ' + agentError.message)
35
- errors.agentError = {
36
- code: agentError.status,
37
- message: `Agent Error: ${agentError.message}`,
38
- }
43
+ integrationsAlreadyRan = true
44
+
45
+ warn('DATADOG TRACER INTEGRATIONS LOADED - ' + JSON.stringify(Object.keys(pluginManager._pluginsByName)))
46
+ }
47
+
48
+ /**
49
+ * Logs agent error diagnostic.
50
+ * @param {{ status: number, message: string }} agentError
51
+ */
52
+ function logAgentError (agentError) {
53
+ if (agentErrorAlreadyRan || !config || !config.startupLogs) {
54
+ return
55
+ }
56
+
57
+ agentErrorAlreadyRan = true
58
+
59
+ warn('DATADOG TRACER DIAGNOSTIC - Agent Error: ' + agentError.message)
60
+ errors.agentError = {
61
+ code: agentError.status,
62
+ message: `Agent Error: ${agentError.message}`,
39
63
  }
40
64
  }
41
65
 
42
66
  /**
67
+ * Returns config info without integrations (used by startupLog).
43
68
  * @returns {Record<string, unknown>}
44
69
  */
45
- function tracerInfo () {
70
+ function configInfo () {
46
71
  const url = getAgentUrl(config)
47
72
 
48
- const out = {
73
+ return {
49
74
  [inspect.custom] () {
50
75
  return String(this)
51
76
  },
@@ -73,11 +98,18 @@ function tracerInfo () {
73
98
  log_injection_enabled: !!config.logInjection,
74
99
  runtime_metrics_enabled: !!config.runtimeMetrics,
75
100
  profiling_enabled: config.profiling?.enabled === 'true' || config.profiling?.enabled === 'auto',
76
- integrations_loaded: Object.keys(pluginManager._pluginsByName),
77
101
  appsec_enabled: config.appsec.enabled,
78
102
  data_streams_enabled: !!config.dsmEnabled,
79
103
  }
104
+ }
80
105
 
106
+ /**
107
+ * Returns full tracer info including integrations (used by flare module).
108
+ * @returns {Record<string, unknown>}
109
+ */
110
+ function tracerInfo () {
111
+ const out = configInfo()
112
+ out.integrations_loaded = Object.keys(pluginManager._pluginsByName)
81
113
  return out
82
114
  }
83
115
 
@@ -104,6 +136,8 @@ function setSamplingRules (theRules) {
104
136
 
105
137
  module.exports = {
106
138
  startupLog,
139
+ logIntegrations,
140
+ logAgentError,
107
141
  setStartupLogConfig,
108
142
  setStartupLogPluginManager,
109
143
  setSamplingRules,