dd-trace 5.70.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 (72) hide show
  1. package/LICENSE-3rdparty.csv +5 -0
  2. package/index.d.ts +110 -1
  3. package/initialize.mjs +7 -1
  4. package/package.json +21 -2
  5. package/packages/datadog-instrumentations/src/anthropic.js +115 -0
  6. package/packages/datadog-instrumentations/src/azure-event-hubs.js +37 -0
  7. package/packages/datadog-instrumentations/src/azure-functions.js +3 -0
  8. package/packages/datadog-instrumentations/src/cucumber.js +7 -7
  9. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  10. package/packages/datadog-instrumentations/src/jest.js +29 -36
  11. package/packages/datadog-instrumentations/src/mocha/main.js +8 -9
  12. package/packages/datadog-instrumentations/src/mocha/utils.js +1 -1
  13. package/packages/datadog-instrumentations/src/mocha/worker.js +2 -2
  14. package/packages/datadog-instrumentations/src/pg.js +1 -1
  15. package/packages/datadog-instrumentations/src/playwright.js +5 -5
  16. package/packages/datadog-instrumentations/src/vitest.js +8 -8
  17. package/packages/datadog-plugin-anthropic/src/index.js +17 -0
  18. package/packages/datadog-plugin-anthropic/src/tracing.js +30 -0
  19. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +73 -27
  20. package/packages/datadog-plugin-azure-event-hubs/src/index.js +15 -0
  21. package/packages/datadog-plugin-azure-event-hubs/src/producer.js +82 -0
  22. package/packages/datadog-plugin-azure-functions/src/index.js +37 -0
  23. package/packages/datadog-plugin-cucumber/src/index.js +3 -3
  24. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +9 -9
  25. package/packages/datadog-plugin-jest/src/util.js +10 -2
  26. package/packages/datadog-plugin-mocha/src/index.js +2 -2
  27. package/packages/datadog-plugin-playwright/src/index.js +2 -2
  28. package/packages/datadog-plugin-vitest/src/index.js +2 -2
  29. package/packages/datadog-plugin-ws/src/server.js +5 -3
  30. package/packages/dd-trace/src/appsec/reporter.js +70 -21
  31. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +1 -1
  32. package/packages/dd-trace/src/config.js +110 -26
  33. package/packages/dd-trace/src/config_defaults.js +14 -0
  34. package/packages/dd-trace/src/git_properties.js +90 -5
  35. package/packages/dd-trace/src/llmobs/plugins/anthropic.js +282 -0
  36. package/packages/dd-trace/src/llmobs/tagger.js +35 -0
  37. package/packages/dd-trace/src/noop/proxy.js +3 -0
  38. package/packages/dd-trace/src/openfeature/constants/constants.js +51 -0
  39. package/packages/dd-trace/src/openfeature/flagging_provider.js +45 -0
  40. package/packages/dd-trace/src/openfeature/index.js +77 -0
  41. package/packages/dd-trace/src/openfeature/noop.js +101 -0
  42. package/packages/dd-trace/src/openfeature/writers/base.js +181 -0
  43. package/packages/dd-trace/src/openfeature/writers/exposures.js +173 -0
  44. package/packages/dd-trace/src/openfeature/writers/util.js +43 -0
  45. package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +100 -0
  46. package/packages/dd-trace/src/opentelemetry/logs/index.js +87 -0
  47. package/packages/dd-trace/src/opentelemetry/logs/logger.js +77 -0
  48. package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +126 -0
  49. package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +173 -0
  50. package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +367 -0
  51. package/packages/dd-trace/src/opentelemetry/protos/common.proto +116 -0
  52. package/packages/dd-trace/src/opentelemetry/protos/logs.proto +226 -0
  53. package/packages/dd-trace/src/opentelemetry/protos/logs_service.proto +78 -0
  54. package/packages/dd-trace/src/opentelemetry/protos/protobuf_loader.js +48 -0
  55. package/packages/dd-trace/src/opentelemetry/protos/resource.proto +45 -0
  56. package/packages/dd-trace/src/plugins/ci_plugin.js +7 -6
  57. package/packages/dd-trace/src/plugins/index.js +2 -0
  58. package/packages/dd-trace/src/plugins/util/test.js +6 -5
  59. package/packages/dd-trace/src/profiling/config.js +21 -1
  60. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +3 -2
  61. package/packages/dd-trace/src/profiling/profiler.js +44 -22
  62. package/packages/dd-trace/src/profiling/profilers/events.js +12 -3
  63. package/packages/dd-trace/src/profiling/profilers/space.js +35 -24
  64. package/packages/dd-trace/src/profiling/profilers/wall.js +14 -6
  65. package/packages/dd-trace/src/proxy.js +22 -1
  66. package/packages/dd-trace/src/remote_config/capabilities.js +2 -0
  67. package/packages/dd-trace/src/remote_config/index.js +3 -0
  68. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +4 -0
  69. package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +8 -0
  70. package/packages/dd-trace/src/supported-configurations.json +18 -0
  71. package/packages/dd-trace/src/telemetry/telemetry.js +13 -1
  72. package/register.js +9 -1
@@ -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
@@ -0,0 +1,126 @@
1
+ 'use strict'
2
+ const { logs } = require('@opentelemetry/api-logs')
3
+ const { context } = require('@opentelemetry/api')
4
+ const Logger = require('./logger')
5
+ const log = require('../../log')
6
+ const ContextManager = require('../context_manager')
7
+
8
+ /**
9
+ * @typedef {import('@opentelemetry/api-logs').Logger} Logger
10
+ * @typedef {import('./batch_log_processor')} BatchLogRecordProcessor
11
+ */
12
+
13
+ /**
14
+ * LoggerProvider is the main entry point for creating loggers with a single processor for Datadog Agent export.
15
+ *
16
+ * This implementation follows the OpenTelemetry JavaScript API LoggerProvider interface:
17
+ * https://github.com/open-telemetry/opentelemetry-js/blob/a7a36499f70f25201949aeabb84c5fd4ca80e860/experimental/packages/api-logs/src/types/LoggerProvider.ts
18
+ *
19
+ * @class LoggerProvider
20
+ * @implements {import('@opentelemetry/api-logs').LoggerProvider}
21
+ */
22
+ class LoggerProvider {
23
+ #loggers
24
+ #contextManager
25
+
26
+ /**
27
+ * Creates a new LoggerProvider instance with a single processor for Datadog Agent export.
28
+ *
29
+ * @param {Object} [options] - LoggerProvider options
30
+ * @param {BatchLogRecordProcessor} [options.processor] - Single LogRecordProcessor instance for
31
+ * exporting logs to Datadog Agent
32
+ */
33
+ constructor (options = {}) {
34
+ this.processor = options.processor
35
+ this.#loggers = new Map()
36
+ this.#contextManager = new ContextManager()
37
+ this.isShutdown = false
38
+ }
39
+
40
+ /**
41
+ * Gets or creates a logger instance.
42
+ *
43
+ * @param {string|Object} nameOrOptions - Logger name or options object
44
+ * @param {string} [version] - Logger version (when nameOrOptions is a string)
45
+ * @param {Object} [options] - Additional options (when nameOrOptions is a string)
46
+ * @returns {Logger} Logger instance
47
+ */
48
+ getLogger (nameOrOptions, version, options = {}) {
49
+ if (this.isShutdown) {
50
+ return this.#createNoOpLogger()
51
+ }
52
+
53
+ let name, loggerOptions
54
+ if (typeof nameOrOptions === 'string') {
55
+ name = nameOrOptions
56
+ loggerOptions = { version, ...options }
57
+ } else {
58
+ name = nameOrOptions.name
59
+ loggerOptions = nameOrOptions
60
+ }
61
+
62
+ const loggerVersion = loggerOptions.version || ''
63
+ const loggerSchemaUrl = loggerOptions?.schemaUrl || ''
64
+ const key = `${name}@${loggerVersion}`
65
+
66
+ if (!this.#loggers.has(key)) {
67
+ this.#loggers.set(key, new Logger(this, { name, version: loggerVersion, schemaUrl: loggerSchemaUrl }))
68
+ }
69
+ return this.#loggers.get(key)
70
+ }
71
+
72
+ /**
73
+ * Registers this logger provider as the global provider.
74
+ */
75
+ register () {
76
+ if (this.isShutdown) {
77
+ log.warn('Cannot register after shutdown')
78
+ return
79
+ }
80
+ // Set context manager, this is required to correlate logs to spans
81
+ context.setGlobalContextManager(this.#contextManager)
82
+ logs.setGlobalLoggerProvider(this)
83
+ }
84
+
85
+ /**
86
+ * Forces a flush of all pending log records.
87
+ * @returns {undefined} Promise that resolves when flush is n ssue cncomplete
88
+ */
89
+ forceFlush () {
90
+ if (!this.isShutdown) {
91
+ return this.processor?.forceFlush()
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Shuts down the logger provider and all associated processors.
97
+ * @returns {undefined} Promise that resolves when shutdown is complete
98
+ */
99
+ shutdown () {
100
+ if (!this.isShutdown) {
101
+ this.isShutdown = true
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Creates a no-op logger for use when the provider is shutdown.
107
+ * @returns {Logger} A no-op logger instance
108
+ * @private
109
+ */
110
+ #createNoOpLogger () {
111
+ return {
112
+ instrumentationScope: {
113
+ name: 'dd-trace-js',
114
+ version: ''
115
+ },
116
+ emit: () => {},
117
+ debug: () => {},
118
+ info: () => {},
119
+ warn: () => {},
120
+ error: () => {},
121
+ fatal: () => {}
122
+ }
123
+ }
124
+ }
125
+
126
+ module.exports = LoggerProvider
@@ -0,0 +1,173 @@
1
+ 'use strict'
2
+
3
+ const http = require('http')
4
+ const { URL } = require('url')
5
+ const log = require('../../log')
6
+ const OtlpTransformer = require('./otlp_transformer')
7
+ const telemetryMetrics = require('../../telemetry/metrics')
8
+
9
+ /**
10
+ * @typedef {import('@opentelemetry/resources').Resource} Resource
11
+ * @typedef {import('@opentelemetry/api-logs').LogRecord} LogRecord
12
+ *
13
+ */
14
+
15
+ const tracerMetrics = telemetryMetrics.manager.namespace('tracers')
16
+
17
+ /**
18
+ * OtlpHttpLogExporter exports log records via OTLP over HTTP.
19
+ *
20
+ * This implementation follows the OTLP HTTP specification:
21
+ * https://opentelemetry.io/docs/specs/otlp/#otlphttp
22
+ *
23
+ * @class OtlpHttpLogExporter
24
+ */
25
+ class OtlpHttpLogExporter {
26
+ #telemetryTags
27
+
28
+ DEFAULT_LOGS_PATH = '/v1/logs'
29
+
30
+ /**
31
+ * Creates a new OtlpHttpLogExporter instance.
32
+ *
33
+ * @param {string} url - OTLP endpoint URL
34
+ * @param {string} headers - Additional HTTP headers as comma-separated key=value string
35
+ * @param {number} timeout - Request timeout in milliseconds
36
+ * @param {string} protocol - OTLP protocol (http/protobuf or http/json)
37
+ * @param {Resource} resource - Resource attributes
38
+ */
39
+ constructor (url, headers, timeout, protocol, resource) {
40
+ const parsedUrl = new URL(url)
41
+
42
+ this.protocol = protocol
43
+ this.transformer = new OtlpTransformer(resource, protocol)
44
+ // If no path is provided, use default path
45
+ const path = parsedUrl.pathname === '/' ? this.DEFAULT_LOGS_PATH : parsedUrl.pathname
46
+ const isJson = protocol === 'http/json'
47
+ this.options = {
48
+ hostname: parsedUrl.hostname,
49
+ port: parsedUrl.port,
50
+ path: path + parsedUrl.search,
51
+ method: 'POST',
52
+ timeout,
53
+ headers: {
54
+ 'Content-Type': isJson ? 'application/json' : 'application/x-protobuf',
55
+ ...this.#parseAdditionalHeaders(headers)
56
+ }
57
+ }
58
+ this.#telemetryTags = [
59
+ 'protocol:http',
60
+ `encoding:${isJson ? 'json' : 'protobuf'}`
61
+ ]
62
+ }
63
+
64
+ /**
65
+ * Exports log records via OTLP over HTTP.
66
+ *
67
+ * @param {LogRecord[]} logRecords - Array of enriched log records to export
68
+ * @param {Function} resultCallback - Callback function for export result
69
+ */
70
+ export (logRecords, resultCallback) {
71
+ if (logRecords.length === 0) {
72
+ resultCallback({ code: 0 })
73
+ return
74
+ }
75
+
76
+ const payload = this.transformer.transformLogRecords(logRecords)
77
+ this.#sendPayload(payload, resultCallback)
78
+ tracerMetrics.count('otel.log_records', this.#telemetryTags).inc(logRecords.length)
79
+ }
80
+
81
+ /**
82
+ * Sends the payload via HTTP request.
83
+ * @param {Buffer|string} payload - The payload to send
84
+ * @param {Function} resultCallback - Callback for the result
85
+ * @private
86
+ */
87
+ #sendPayload (payload, resultCallback) {
88
+ const options = {
89
+ ...this.options,
90
+ headers: {
91
+ ...this.options.headers,
92
+ 'Content-Length': payload.length
93
+ }
94
+ }
95
+
96
+ const req = http.request(options, (res) => {
97
+ let data = ''
98
+
99
+ res.on('data', (chunk) => {
100
+ data += chunk
101
+ })
102
+
103
+ res.on('end', () => {
104
+ if (res.statusCode >= 200 && res.statusCode < 300) {
105
+ resultCallback({ code: 0 })
106
+ } else {
107
+ const error = new Error(`HTTP ${res.statusCode}: ${data}`)
108
+ resultCallback({ code: 1, error })
109
+ }
110
+ })
111
+ })
112
+
113
+ req.on('error', (error) => {
114
+ log.error('Error sending OTLP logs:', error)
115
+ resultCallback({ code: 1, error })
116
+ })
117
+
118
+ req.on('timeout', () => {
119
+ req.destroy()
120
+ const error = new Error('Request timeout')
121
+ resultCallback({ code: 1, error })
122
+ })
123
+
124
+ req.write(payload)
125
+ req.end()
126
+ }
127
+
128
+ /**
129
+ * Parses additional HTTP headers from a comma-separated string.
130
+ * @param {string} headersString - Comma-separated key=value pairs
131
+ * @returns {Record<string, string>} Parsed headers object
132
+ * @private
133
+ */
134
+ #parseAdditionalHeaders (headersString) {
135
+ const headers = {}
136
+ let key = ''
137
+ let value = ''
138
+ let readingKey = true
139
+
140
+ for (const char of headersString) {
141
+ if (readingKey) {
142
+ if (char === '=') {
143
+ readingKey = false
144
+ key = key.trim()
145
+ } else {
146
+ key += char
147
+ }
148
+ } else if (char === ',') {
149
+ value = value.trim()
150
+ if (key && value) {
151
+ headers[key] = value
152
+ }
153
+ key = ''
154
+ value = ''
155
+ readingKey = true
156
+ } else {
157
+ value += char
158
+ }
159
+ }
160
+
161
+ // Add the last pair if present
162
+ if (!readingKey) {
163
+ value = value.trim()
164
+ if (value) {
165
+ headers[key] = value
166
+ }
167
+ }
168
+
169
+ return headers
170
+ }
171
+ }
172
+
173
+ module.exports = OtlpHttpLogExporter