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.
- package/LICENSE-3rdparty.csv +5 -0
- package/index.d.ts +110 -1
- package/initialize.mjs +7 -1
- package/package.json +21 -2
- package/packages/datadog-instrumentations/src/anthropic.js +115 -0
- package/packages/datadog-instrumentations/src/azure-event-hubs.js +37 -0
- package/packages/datadog-instrumentations/src/azure-functions.js +3 -0
- package/packages/datadog-instrumentations/src/cucumber.js +7 -7
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
- package/packages/datadog-instrumentations/src/jest.js +29 -36
- package/packages/datadog-instrumentations/src/mocha/main.js +8 -9
- package/packages/datadog-instrumentations/src/mocha/utils.js +1 -1
- package/packages/datadog-instrumentations/src/mocha/worker.js +2 -2
- package/packages/datadog-instrumentations/src/pg.js +1 -1
- package/packages/datadog-instrumentations/src/playwright.js +5 -5
- package/packages/datadog-instrumentations/src/vitest.js +8 -8
- package/packages/datadog-plugin-anthropic/src/index.js +17 -0
- package/packages/datadog-plugin-anthropic/src/tracing.js +30 -0
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +73 -27
- package/packages/datadog-plugin-azure-event-hubs/src/index.js +15 -0
- package/packages/datadog-plugin-azure-event-hubs/src/producer.js +82 -0
- package/packages/datadog-plugin-azure-functions/src/index.js +37 -0
- package/packages/datadog-plugin-cucumber/src/index.js +3 -3
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +9 -9
- package/packages/datadog-plugin-jest/src/util.js +10 -2
- package/packages/datadog-plugin-mocha/src/index.js +2 -2
- package/packages/datadog-plugin-playwright/src/index.js +2 -2
- package/packages/datadog-plugin-vitest/src/index.js +2 -2
- package/packages/datadog-plugin-ws/src/server.js +5 -3
- package/packages/dd-trace/src/appsec/reporter.js +70 -21
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +1 -1
- package/packages/dd-trace/src/config.js +110 -26
- package/packages/dd-trace/src/config_defaults.js +14 -0
- package/packages/dd-trace/src/git_properties.js +90 -5
- package/packages/dd-trace/src/llmobs/plugins/anthropic.js +282 -0
- package/packages/dd-trace/src/llmobs/tagger.js +35 -0
- package/packages/dd-trace/src/noop/proxy.js +3 -0
- package/packages/dd-trace/src/openfeature/constants/constants.js +51 -0
- package/packages/dd-trace/src/openfeature/flagging_provider.js +45 -0
- package/packages/dd-trace/src/openfeature/index.js +77 -0
- package/packages/dd-trace/src/openfeature/noop.js +101 -0
- package/packages/dd-trace/src/openfeature/writers/base.js +181 -0
- package/packages/dd-trace/src/openfeature/writers/exposures.js +173 -0
- package/packages/dd-trace/src/openfeature/writers/util.js +43 -0
- package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +100 -0
- package/packages/dd-trace/src/opentelemetry/logs/index.js +87 -0
- package/packages/dd-trace/src/opentelemetry/logs/logger.js +77 -0
- package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +126 -0
- package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +173 -0
- package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +367 -0
- package/packages/dd-trace/src/opentelemetry/protos/common.proto +116 -0
- package/packages/dd-trace/src/opentelemetry/protos/logs.proto +226 -0
- package/packages/dd-trace/src/opentelemetry/protos/logs_service.proto +78 -0
- package/packages/dd-trace/src/opentelemetry/protos/protobuf_loader.js +48 -0
- package/packages/dd-trace/src/opentelemetry/protos/resource.proto +45 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +7 -6
- package/packages/dd-trace/src/plugins/index.js +2 -0
- package/packages/dd-trace/src/plugins/util/test.js +6 -5
- package/packages/dd-trace/src/profiling/config.js +21 -1
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +3 -2
- package/packages/dd-trace/src/profiling/profiler.js +44 -22
- package/packages/dd-trace/src/profiling/profilers/events.js +12 -3
- package/packages/dd-trace/src/profiling/profilers/space.js +35 -24
- package/packages/dd-trace/src/profiling/profilers/wall.js +14 -6
- package/packages/dd-trace/src/proxy.js +22 -1
- package/packages/dd-trace/src/remote_config/capabilities.js +2 -0
- package/packages/dd-trace/src/remote_config/index.js +3 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +4 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +8 -0
- package/packages/dd-trace/src/supported-configurations.json +18 -0
- package/packages/dd-trace/src/telemetry/telemetry.js +13 -1
- 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
|