dd-trace 5.71.0 → 5.72.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 (63) hide show
  1. package/LICENSE-3rdparty.csv +5 -0
  2. package/index.d.ts +93 -1
  3. package/package.json +21 -2
  4. package/packages/datadog-instrumentations/src/azure-event-hubs.js +37 -0
  5. package/packages/datadog-instrumentations/src/azure-functions.js +3 -0
  6. package/packages/datadog-instrumentations/src/cucumber.js +7 -7
  7. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  8. package/packages/datadog-instrumentations/src/jest.js +29 -36
  9. package/packages/datadog-instrumentations/src/mocha/main.js +8 -9
  10. package/packages/datadog-instrumentations/src/mocha/utils.js +1 -1
  11. package/packages/datadog-instrumentations/src/mocha/worker.js +2 -2
  12. package/packages/datadog-instrumentations/src/pg.js +1 -1
  13. package/packages/datadog-instrumentations/src/playwright.js +5 -5
  14. package/packages/datadog-instrumentations/src/vitest.js +8 -8
  15. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +73 -27
  16. package/packages/datadog-plugin-azure-event-hubs/src/index.js +15 -0
  17. package/packages/datadog-plugin-azure-event-hubs/src/producer.js +82 -0
  18. package/packages/datadog-plugin-azure-functions/src/index.js +37 -0
  19. package/packages/datadog-plugin-cucumber/src/index.js +3 -3
  20. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +9 -9
  21. package/packages/datadog-plugin-jest/src/util.js +10 -2
  22. package/packages/datadog-plugin-mocha/src/index.js +2 -2
  23. package/packages/datadog-plugin-playwright/src/index.js +2 -2
  24. package/packages/datadog-plugin-vitest/src/index.js +2 -2
  25. package/packages/datadog-plugin-ws/src/server.js +5 -3
  26. package/packages/dd-trace/src/config.js +108 -26
  27. package/packages/dd-trace/src/config_defaults.js +12 -0
  28. package/packages/dd-trace/src/git_properties.js +90 -5
  29. package/packages/dd-trace/src/noop/proxy.js +3 -0
  30. package/packages/dd-trace/src/openfeature/constants/constants.js +51 -0
  31. package/packages/dd-trace/src/openfeature/flagging_provider.js +45 -0
  32. package/packages/dd-trace/src/openfeature/index.js +77 -0
  33. package/packages/dd-trace/src/openfeature/noop.js +101 -0
  34. package/packages/dd-trace/src/openfeature/writers/base.js +181 -0
  35. package/packages/dd-trace/src/openfeature/writers/exposures.js +173 -0
  36. package/packages/dd-trace/src/openfeature/writers/util.js +43 -0
  37. package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +100 -0
  38. package/packages/dd-trace/src/opentelemetry/logs/index.js +87 -0
  39. package/packages/dd-trace/src/opentelemetry/logs/logger.js +77 -0
  40. package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +126 -0
  41. package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +173 -0
  42. package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +367 -0
  43. package/packages/dd-trace/src/opentelemetry/protos/common.proto +116 -0
  44. package/packages/dd-trace/src/opentelemetry/protos/logs.proto +226 -0
  45. package/packages/dd-trace/src/opentelemetry/protos/logs_service.proto +78 -0
  46. package/packages/dd-trace/src/opentelemetry/protos/protobuf_loader.js +48 -0
  47. package/packages/dd-trace/src/opentelemetry/protos/resource.proto +45 -0
  48. package/packages/dd-trace/src/plugins/ci_plugin.js +7 -6
  49. package/packages/dd-trace/src/plugins/index.js +1 -0
  50. package/packages/dd-trace/src/plugins/util/test.js +6 -5
  51. package/packages/dd-trace/src/profiling/config.js +21 -1
  52. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +3 -2
  53. package/packages/dd-trace/src/profiling/profiler.js +44 -22
  54. package/packages/dd-trace/src/profiling/profilers/events.js +12 -3
  55. package/packages/dd-trace/src/profiling/profilers/space.js +35 -24
  56. package/packages/dd-trace/src/profiling/profilers/wall.js +14 -6
  57. package/packages/dd-trace/src/proxy.js +22 -1
  58. package/packages/dd-trace/src/remote_config/capabilities.js +1 -0
  59. package/packages/dd-trace/src/remote_config/index.js +1 -0
  60. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +4 -0
  61. package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +8 -0
  62. package/packages/dd-trace/src/supported-configurations.json +17 -0
  63. package/packages/dd-trace/src/telemetry/telemetry.js +13 -1
@@ -0,0 +1,181 @@
1
+ 'use strict'
2
+
3
+ const request = require('../../exporters/common/request')
4
+ const { safeJSONStringify } = require('../../exporters/common/util')
5
+ const { URL, format } = require('node:url')
6
+
7
+ const log = require('../../log')
8
+
9
+ /**
10
+ * @typedef {Object} BaseFFEWriterOptions
11
+ * @property {number} [interval] - Flush interval in milliseconds
12
+ * @property {number} [timeout] - Request timeout in milliseconds
13
+ * @property {Object} config - Tracer configuration object
14
+ * @property {string} endpoint - API endpoint path
15
+ * @property {URL} [agentUrl] - Base URL for the agent
16
+ * @property {number} [payloadSizeLimit] - Maximum payload size in bytes
17
+ * @property {number} [eventSizeLimit] - Maximum individual event size in bytes
18
+ * @property {Object} [headers] - Additional HTTP headers
19
+ */
20
+
21
+ /**
22
+ * BaseFFEWriter is the base class for sending Feature Flagging & Exposure Events payloads to the Datadog Agent.
23
+ * @class BaseFFEWriter
24
+ */
25
+ class BaseFFEWriter {
26
+ /**
27
+ * @param {BaseFFEWriterOptions} options - Writer configuration options
28
+ */
29
+ constructor ({ interval, timeout, config, endpoint, agentUrl, payloadSizeLimit, eventSizeLimit, headers }) {
30
+ this._interval = interval ?? 1000
31
+ this._timeout = timeout ?? 5000
32
+
33
+ this._buffer = []
34
+ this._bufferLimit = 1000
35
+ this._bufferSize = 0
36
+
37
+ this._config = config
38
+ this._endpoint = endpoint
39
+ this._baseUrl = agentUrl ?? this._getAgentUrl()
40
+ this._payloadSizeLimit = payloadSizeLimit
41
+ this._eventSizeLimit = eventSizeLimit
42
+ this._headers = headers || {}
43
+
44
+ this._requestOptions = {
45
+ headers: {
46
+ ...this._headers,
47
+ 'Content-Type': 'application/json'
48
+ },
49
+ method: 'POST',
50
+ timeout: this._timeout,
51
+ url: this._baseUrl,
52
+ path: this._endpoint
53
+ }
54
+
55
+ this._periodic = setInterval(() => {
56
+ this.flush()
57
+ }, this._interval).unref()
58
+
59
+ this._beforeExitHandler = () => {
60
+ this.destroy()
61
+ }
62
+ process.once('beforeExit', this._beforeExitHandler)
63
+
64
+ this._destroyed = false
65
+ this._droppedEvents = 0
66
+ }
67
+
68
+ /**
69
+ * Appends an event array to the buffer
70
+ * @param {Array|Object} events - Event object(s) to append to buffer
71
+ */
72
+ append (events) {
73
+ const eventArray = Array.isArray(events) ? events : [events]
74
+
75
+ for (const event of eventArray) {
76
+ if (this._buffer.length >= this._bufferLimit) {
77
+ log.warn(`${this.constructor.name} event buffer full (limit is ${this._bufferLimit}), dropping event`)
78
+ this._droppedEvents++
79
+ continue
80
+ }
81
+
82
+ const eventSizeBytes = Buffer.byteLength(JSON.stringify(event))
83
+
84
+ // Check individual event size limit if configured
85
+ if (this._eventSizeLimit && eventSizeBytes > this._eventSizeLimit) {
86
+ log.warn(`${this.constructor.name} event size
87
+ ${eventSizeBytes} bytes exceeds limit ${this._eventSizeLimit}, dropping event`)
88
+ this._droppedEvents++
89
+ continue
90
+ }
91
+
92
+ // Check if adding this event would exceed payload size limit if configured
93
+ if (this._payloadSizeLimit && this._bufferSize + eventSizeBytes > this._payloadSizeLimit) {
94
+ log.debug(() => `${this.constructor.name}
95
+ buffer size would exceed ${this._payloadSizeLimit} bytes, flushing first`)
96
+ this.flush()
97
+ }
98
+
99
+ this._bufferSize += eventSizeBytes
100
+ this._buffer.push(event)
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Flushes all buffered events to the agent
106
+ */
107
+ flush () {
108
+ if (this._buffer.length === 0) {
109
+ return
110
+ }
111
+ const events = this._buffer
112
+ this._buffer = []
113
+ this._bufferSize = 0
114
+
115
+ const payload = this._encode(this.makePayload(events))
116
+
117
+ log.debug(() => `${this.constructor.name} flushing payload: ${safeJSONStringify(payload)}`)
118
+
119
+ request(payload, this._requestOptions, (err, resp, code) => {
120
+ if (err) {
121
+ log.error(`Failed to send events to ${this._baseUrl.href}${this._endpoint}: ${err.message}`)
122
+ } else if (code >= 200 && code < 300) {
123
+ log.debug(() => `Successfully sent ${events.length} events`)
124
+ } else {
125
+ log.warn(`Events request returned status ${code}`)
126
+ }
127
+ })
128
+ }
129
+
130
+ /**
131
+ * Override in subclass to customize payload structure
132
+ * @param {Array} events - Array of events to be sent
133
+ * @returns {object} Formatted payload
134
+ */
135
+ makePayload (events) {
136
+ // Override in subclass
137
+ return events
138
+ }
139
+
140
+ /**
141
+ * Cleans up resources and flushes remaining events
142
+ */
143
+ destroy () {
144
+ if (!this._destroyed) {
145
+ log.debug(() => `Stopping ${this.constructor.name}`)
146
+ clearInterval(this._periodic)
147
+ process.removeListener('beforeExit', this._beforeExitHandler)
148
+ this.flush()
149
+ this._destroyed = true
150
+
151
+ if (this._droppedEvents > 0) {
152
+ log.warn(`${this.constructor.name} dropped ${this._droppedEvents} events due to buffer overflow`)
153
+ }
154
+ }
155
+ }
156
+
157
+ /**
158
+ * @private
159
+ * @returns {URL} Constructs agent URL from config
160
+ */
161
+ _getAgentUrl () {
162
+ const { hostname, port } = this._config
163
+
164
+ return this._config.url ?? new URL(format({
165
+ protocol: 'http:',
166
+ hostname: hostname || 'localhost',
167
+ port: port || 8126
168
+ }))
169
+ }
170
+
171
+ /**
172
+ * @private
173
+ * @param {Array<object>} payload - Payload to encode
174
+ * @returns {string} JSON-stringified payload
175
+ */
176
+ _encode (payload) {
177
+ return JSON.stringify(payload)
178
+ }
179
+ }
180
+
181
+ module.exports = BaseFFEWriter
@@ -0,0 +1,173 @@
1
+ 'use strict'
2
+
3
+ const BaseFFEWriter = require('./base')
4
+ const {
5
+ EXPOSURES_ENDPOINT,
6
+ EVP_PROXY_AGENT_BASE_PATH,
7
+ EVP_SUBDOMAIN_HEADER_NAME,
8
+ EVP_SUBDOMAIN_VALUE,
9
+ EVP_PAYLOAD_SIZE_LIMIT,
10
+ EVP_EVENT_SIZE_LIMIT
11
+ } = require('../constants/constants')
12
+
13
+ /**
14
+ * @typedef {Object} ExposureEvent
15
+ * @property {number} timestamp - Unix timestamp in milliseconds
16
+ * @property {Object} allocation - Allocation information
17
+ * @property {string} allocation.key - Allocation key
18
+ * @property {Object} flag - Flag information
19
+ * @property {string} flag.key - Flag key
20
+ * @property {Object} variant - Variant information
21
+ * @property {string} variant.key - Variant key
22
+ * @property {Object} subject - Subject (user/entity) information
23
+ * @property {string} subject.id - Subject identifier
24
+ * @property {string} [subject.type] - Subject type
25
+ * @property {Object} [subject.attributes] - Additional subject attributes
26
+ */
27
+
28
+ /**
29
+ * @typedef {Object} ExposureContext
30
+ * @property {string} service_name - Service name
31
+ * @property {string} [version] - Service version
32
+ * @property {string} [env] - Service environment
33
+ */
34
+
35
+ /**
36
+ * @typedef {Object} ExposureEventPayload
37
+ * @property {ExposureContext} context - Service context metadata
38
+ * @property {ExposureEvent[]} exposures - Formatted exposure events
39
+ */
40
+
41
+ /**
42
+ * ExposuresWriter is responsible for sending exposure events to the Datadog Agent.
43
+ */
44
+ class ExposuresWriter extends BaseFFEWriter {
45
+ /**
46
+ * @param {import('../../config')} config - Tracer configuration object
47
+ */
48
+ constructor (config) {
49
+ // Build full EVP endpoint path
50
+ const basePath = EVP_PROXY_AGENT_BASE_PATH.replace(/\/+$/, '')
51
+ const endpoint = EXPOSURES_ENDPOINT.replace(/^\/+/, '')
52
+ const fullEndpoint = `${basePath}/${endpoint}`
53
+
54
+ super({
55
+ config,
56
+ endpoint: fullEndpoint,
57
+ payloadSizeLimit: EVP_PAYLOAD_SIZE_LIMIT,
58
+ eventSizeLimit: EVP_EVENT_SIZE_LIMIT,
59
+ headers: {
60
+ [EVP_SUBDOMAIN_HEADER_NAME]: EVP_SUBDOMAIN_VALUE
61
+ }
62
+ })
63
+ this._enabled = false // Start disabled until agent strategy is set
64
+ this._pendingEvents = [] // Buffer events until enabled
65
+ this._context = this._buildContext()
66
+ }
67
+
68
+ /**
69
+ * @param {boolean} enabled - Whether to enable the writer
70
+ */
71
+ setEnabled (enabled) {
72
+ this._enabled = enabled
73
+
74
+ if (enabled && this._pendingEvents.length > 0) {
75
+ // Flush all pending events as a batch
76
+ super.append(this._pendingEvents)
77
+ this._pendingEvents = []
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Appends exposure event(s) to the buffer
83
+ * @param {ExposureEvent|ExposureEvent[]} events - Exposure event(s) to append
84
+ */
85
+ append (events) {
86
+ if (!this._enabled) {
87
+ // Buffer events until writer is ready
88
+ if (Array.isArray(events)) {
89
+ this._pendingEvents.push(...events)
90
+ } else {
91
+ this._pendingEvents.push(events)
92
+ }
93
+ return
94
+ }
95
+ super.append(events)
96
+ }
97
+
98
+ /**
99
+ * Flushes buffered exposure events to the agent
100
+ */
101
+ flush () {
102
+ if (!this._enabled) {
103
+ // Don't flush when disabled
104
+ return
105
+ }
106
+ super.flush()
107
+ }
108
+
109
+ /**
110
+ * Formats exposure events with service context metadata
111
+ * @param {Array<ExposureEvent>} events - Array of exposure events
112
+ * @returns {ExposureEventPayload} Formatted payload with service context
113
+ */
114
+ makePayload (events) {
115
+ const formattedEvents = events.map(event => this._formatExposureEvent(event))
116
+
117
+ return {
118
+ context: this._context,
119
+ exposures: formattedEvents
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Builds service context metadata
125
+ * @private
126
+ * @returns {ExposureContext} Service context
127
+ */
128
+ _buildContext () {
129
+ const context = {
130
+ service_name: this._config.service || 'unknown'
131
+ }
132
+
133
+ // Only include version and env if they are defined
134
+ if (this._config.version !== undefined) {
135
+ context.version = this._config.version
136
+ }
137
+
138
+ if (this._config.env !== undefined) {
139
+ context.env = this._config.env
140
+ }
141
+
142
+ return context
143
+ }
144
+
145
+ /**
146
+ * @private
147
+ * @param {ExposureEvent} event - Raw exposure event
148
+ * @returns {ExposureEvent} Formatted exposure event
149
+ */
150
+ _formatExposureEvent (event) {
151
+ // Ensure the event matches the expected schema
152
+ const formattedEvent = {
153
+ timestamp: event.timestamp || Date.now(),
154
+ allocation: {
155
+ key: event.allocation?.key || event['allocation.key']
156
+ },
157
+ flag: {
158
+ key: event.flag?.key || event['flag.key']
159
+ },
160
+ variant: {
161
+ key: event.variant?.key || event['variant.key']
162
+ },
163
+ subject: {
164
+ id: event.subject?.id || event['subject.id'],
165
+ type: event.subject?.type,
166
+ attributes: event.subject?.attributes
167
+ }
168
+ }
169
+ return formattedEvent
170
+ }
171
+ }
172
+
173
+ module.exports = ExposuresWriter
@@ -0,0 +1,43 @@
1
+ 'use strict'
2
+
3
+ const logger = require('../../log')
4
+ const { EVP_PROXY_AGENT_BASE_PATH } = require('../constants/constants')
5
+
6
+ const AgentInfoExporter = require('../../exporters/common/agent-info-exporter')
7
+ let agentInfoExporter
8
+
9
+ /**
10
+ * Determines if the agent supports EVP proxy and sets the writer enabled state accordingly
11
+ * @param {import('../../config')} config - Tracer configuration object
12
+ * @param {Function} setWriterEnabledValue - Callback to set the writer enabled state
13
+ */
14
+ function setAgentStrategy (config, setWriterEnabledValue) {
15
+ if (!agentInfoExporter) {
16
+ agentInfoExporter = new AgentInfoExporter(config)
17
+ }
18
+
19
+ agentInfoExporter.getAgentInfo((err, agentInfo) => {
20
+ if (err) {
21
+ logger.debug('FFE Writer disabled - error getting agent info:', err.message)
22
+ setWriterEnabledValue(false)
23
+ return
24
+ }
25
+
26
+ const endpoints = agentInfo.endpoints
27
+ const normalizedPath = EVP_PROXY_AGENT_BASE_PATH.replace(/\/+$/, '')
28
+ const hasEndpoint = Array.isArray(endpoints) &&
29
+ endpoints.includes(normalizedPath) || endpoints.includes(normalizedPath + '/')
30
+
31
+ if (hasEndpoint) {
32
+ logger.debug('FFE Writer enabled - agent has EVP proxy support')
33
+ setWriterEnabledValue(true)
34
+ } else {
35
+ logger.debug('FFE Writer disabled - agent does not have EVP proxy support')
36
+ setWriterEnabledValue(false)
37
+ }
38
+ })
39
+ }
40
+
41
+ module.exports = {
42
+ setAgentStrategy
43
+ }
@@ -0,0 +1,100 @@
1
+ 'use strict'
2
+
3
+ /**
4
+ * @typedef {import('@opentelemetry/api-logs').LogRecord} LogRecord
5
+ * @typedef {import('@opentelemetry/core').InstrumentationScope} InstrumentationScope
6
+ */
7
+
8
+ /**
9
+ * BatchLogRecordProcessor processes log records in batches for efficient export to Datadog Agent.
10
+ *
11
+ * This implementation follows the OpenTelemetry JavaScript SDK BatchLogRecordProcessor:
12
+ * https://open-telemetry.github.io/opentelemetry-js/classes/_opentelemetry_sdk-logs.BatchLogRecordProcessor.html
13
+ *
14
+ * @class BatchLogRecordProcessor
15
+ */
16
+ class BatchLogRecordProcessor {
17
+ #logRecords
18
+ #timer
19
+ #batchTimeout
20
+ #maxExportBatchSize
21
+
22
+ /**
23
+ * Creates a new BatchLogRecordProcessor instance.
24
+ *
25
+ * @param {OtlpHttpLogExporter} exporter - Log processor for exporting batches to Datadog Agent
26
+ * @param {number} batchTimeout - Timeout in milliseconds for batch processing
27
+ * @param {number} maxExportBatchSize - Maximum number of log records per batch
28
+ */
29
+ constructor (exporter, batchTimeout, maxExportBatchSize) {
30
+ this.exporter = exporter
31
+ this.#batchTimeout = batchTimeout
32
+ this.#maxExportBatchSize = maxExportBatchSize
33
+ this.#logRecords = []
34
+ this.#timer = null
35
+ }
36
+
37
+ /**
38
+ * Processes a single log record.
39
+ *
40
+ * @param {LogRecord} logRecord - The enriched log record with trace correlation and metadata
41
+ * @param {InstrumentationScope} instrumentationScope - The instrumentation library
42
+ */
43
+ onEmit (logRecord, instrumentationScope) {
44
+ // Store the log record (already enriched by Logger.emit)
45
+ this.#logRecords.push({ ...logRecord, instrumentationScope })
46
+
47
+ if (this.#logRecords.length >= this.#maxExportBatchSize) {
48
+ this.#export()
49
+ } else if (this.#logRecords.length === 1) {
50
+ this.#startTimer()
51
+ }
52
+ }
53
+
54
+ /**
55
+ * Forces an immediate flush of all pending log records.
56
+ * @returns {undefined} Promise that resolves when flush is complete
57
+ */
58
+ forceFlush () {
59
+ this.#export()
60
+ }
61
+
62
+ /**
63
+ * Starts the batch timeout timer.
64
+ * @private
65
+ */
66
+ #startTimer () {
67
+ if (this.#timer) {
68
+ return
69
+ }
70
+
71
+ this.#timer = setTimeout(() => {
72
+ this.#export()
73
+ }, this.#batchTimeout)
74
+ }
75
+
76
+ /**
77
+ * Exports the current batch of log records.
78
+ * @private
79
+ */
80
+ #export () {
81
+ const logRecords = this.#logRecords.slice(0, this.#maxExportBatchSize)
82
+ this.#logRecords = this.#logRecords.slice(this.#maxExportBatchSize)
83
+
84
+ this.#clearTimer()
85
+ this.exporter.export(logRecords, () => {})
86
+ }
87
+
88
+ /**
89
+ * Clears the batch timeout timer.
90
+ * @private
91
+ */
92
+ #clearTimer () {
93
+ if (this.#timer) {
94
+ clearTimeout(this.#timer)
95
+ this.#timer = null
96
+ }
97
+ }
98
+ }
99
+
100
+ module.exports = BatchLogRecordProcessor
@@ -0,0 +1,87 @@
1
+ 'use strict'
2
+
3
+ const os = require('os')
4
+
5
+ /**
6
+ * @typedef {import('../../config')} Config
7
+ */
8
+
9
+ /**
10
+ * @fileoverview OpenTelemetry Logs Implementation for dd-trace-js
11
+ *
12
+ * This package provides a custom OpenTelemetry Logs implementation that integrates
13
+ * with the Datadog tracing library. It includes all necessary components for
14
+ * emitting, processing, and exporting log records via OTLP (OpenTelemetry Protocol).
15
+ *
16
+ * Key Components:
17
+ * - LoggerProvider: Main entry point for creating loggers
18
+ * - Logger: Provides methods to emit log records
19
+ * - BatchLogRecordProcessor: Processes log records in batches for efficient export
20
+ * - OtlpHttpLogExporter: Exports log records via OTLP over HTTP
21
+ * - OtlpTransformer: Transforms log records to OTLP format
22
+ *
23
+ * This is a custom implementation to avoid pulling in the full OpenTelemetry SDK,
24
+ * based on OTLP Protocol v1.7.0. It supports both protobuf and JSON serialization
25
+ * formats and integrates with Datadog's configuration system.
26
+ *
27
+ * @package
28
+ */
29
+
30
+ const LoggerProvider = require('./logger_provider')
31
+ const BatchLogRecordProcessor = require('./batch_log_processor')
32
+ const OtlpHttpLogExporter = require('./otlp_http_log_exporter')
33
+
34
+ /**
35
+ * Initializes OpenTelemetry Logs support
36
+ * @param {Config} config - Tracer configuration instance
37
+ */
38
+ function initializeOpenTelemetryLogs (config) {
39
+ // Build resource attributes
40
+ const resourceAttributes = {
41
+ 'service.name': config.service,
42
+ 'service.version': config.version,
43
+ 'deployment.environment': config.env
44
+ }
45
+
46
+ // Add all tracer tags (includes DD_TAGS, OTEL_RESOURCE_ATTRIBUTES, DD_TRACE_TAGS, etc.)
47
+ // Exclude Datadog-style keys that duplicate OpenTelemetry standard keys
48
+ if (config.tags) {
49
+ const filteredTags = { ...config.tags }
50
+ delete filteredTags.service
51
+ delete filteredTags.version
52
+ delete filteredTags.env
53
+ Object.assign(resourceAttributes, filteredTags)
54
+ }
55
+
56
+ // Add host.name if reportHostname is enabled
57
+ if (config.reportHostname) {
58
+ resourceAttributes['host.name'] = os.hostname()
59
+ }
60
+
61
+ // Create OTLP exporter using resolved config values
62
+ const exporter = new OtlpHttpLogExporter(
63
+ config.otelLogsUrl,
64
+ config.otelLogsHeaders,
65
+ config.otelLogsTimeout,
66
+ config.otelLogsProtocol,
67
+ resourceAttributes
68
+ )
69
+
70
+ // Create batch processor for exporting logs to Datadog Agent
71
+ const processor = new BatchLogRecordProcessor(
72
+ exporter,
73
+ config.otelLogsBatchTimeout,
74
+ config.otelLogsMaxExportBatchSize
75
+ )
76
+
77
+ // Create logger provider with processor for Datadog Agent export
78
+ const loggerProvider = new LoggerProvider({ processor })
79
+
80
+ // Register the logger provider globally with OpenTelemetry API
81
+ loggerProvider.register()
82
+ }
83
+
84
+ module.exports = {
85
+ LoggerProvider,
86
+ initializeOpenTelemetryLogs
87
+ }
@@ -0,0 +1,77 @@
1
+ 'use strict'
2
+
3
+ const { sanitizeAttributes } = require('@opentelemetry/core')
4
+ const { context } = require('@opentelemetry/api')
5
+ const packageVersion = require('../../../../../package.json').version
6
+ /**
7
+ * @typedef {import('@opentelemetry/api-logs').LogRecord} LogRecord
8
+ * @typedef {import('@opentelemetry/api').SpanContext} SpanContext
9
+ * @typedef {import('@opentelemetry/api').Attributes} Attributes
10
+ * @typedef {import('@opentelemetry/resources').Resource} Resource
11
+ * @typedef {import('@opentelemetry/core').InstrumentationScope} InstrumentationScope
12
+ */
13
+
14
+ /**
15
+ * Logger provides methods to emit log records.
16
+ *
17
+ * This implementation follows the OpenTelemetry JavaScript API Logger:
18
+ * https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_api-logs.Logger.html
19
+ *
20
+ * @class Logger
21
+ */
22
+ class Logger {
23
+ #instrumentationScope
24
+
25
+ /**
26
+ * Creates a new Logger instance.
27
+ *
28
+ * @param {LoggerProvider} loggerProvider - Parent logger provider
29
+ * @param {InstrumentationScope} [instrumentationScope] - Instrumentation scope information (newer API)
30
+ * @param {Object} [instrumentationLibrary] - Instrumentation library information (legacy API) [DEPRECATED in v1.3.0]
31
+ * @param {InstrumentationScope} [instrumentationScope.name] - Library name (defaults to 'dd-trace-js')
32
+ * @param {InstrumentationScope} [instrumentationScope.version] - Library version (defaults to tracer version)
33
+ * @param {string} [instrumentationLibrary.name] - Library name (legacy, defaults to 'dd-trace-js')
34
+ * @param {string} [instrumentationLibrary.version] - Library version (legacy, defaults to tracer version)
35
+ */
36
+ constructor (loggerProvider, instrumentationScope, instrumentationLibrary) {
37
+ this.loggerProvider = loggerProvider
38
+
39
+ // Support both newer instrumentationScope and legacy instrumentationLibrary
40
+ const scope = instrumentationScope || instrumentationLibrary
41
+ this.#instrumentationScope = {
42
+ name: scope?.name || 'dd-trace-js',
43
+ version: scope?.version || packageVersion,
44
+ schemaUrl: scope?.schemaUrl || '',
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Emits a log record.
50
+ *
51
+ * @param {LogRecord} logRecord - The log record to emit
52
+ * @returns {void}
53
+ */
54
+ emit (logRecord) {
55
+ if (this.loggerProvider.isShutdown || !this.loggerProvider.processor) {
56
+ return
57
+ }
58
+
59
+ if (logRecord.attributes) {
60
+ logRecord.attributes = sanitizeAttributes(logRecord.attributes)
61
+ }
62
+
63
+ // Note: timestamp is in nanoseconds (as defined by OpenTelemetry LogRecord API)
64
+ if (!logRecord.timestamp) {
65
+ logRecord.timestamp = Number(process.hrtime.bigint())
66
+ }
67
+
68
+ if (!logRecord.context) {
69
+ // Store span context in the log record context for trace correlation
70
+ logRecord.context = context.active()
71
+ }
72
+
73
+ this.loggerProvider.processor.onEmit(logRecord, this.#instrumentationScope)
74
+ }
75
+ }
76
+
77
+ module.exports = Logger