dd-trace 5.71.0 → 5.73.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 +7 -0
- package/index.d.ts +114 -1
- package/package.json +25 -4
- package/packages/datadog-esbuild/index.js +8 -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/azure-service-bus.js +49 -22
- package/packages/datadog-instrumentations/src/cookie-parser.js +2 -0
- package/packages/datadog-instrumentations/src/cucumber.js +7 -7
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/jest.js +85 -47
- package/packages/datadog-instrumentations/src/mocha/main.js +8 -9
- package/packages/datadog-instrumentations/src/mocha/utils.js +4 -5
- 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-amqplib/src/consumer.js +1 -1
- 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 +50 -3
- package/packages/datadog-plugin-azure-service-bus/src/index.js +1 -1
- package/packages/datadog-plugin-azure-service-bus/src/producer.js +60 -12
- 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/index.js +53 -18
- 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/close.js +1 -1
- package/packages/datadog-plugin-ws/src/producer.js +1 -1
- package/packages/datadog-plugin-ws/src/receiver.js +1 -1
- package/packages/datadog-plugin-ws/src/server.js +5 -3
- package/packages/dd-trace/src/appsec/index.js +9 -1
- package/packages/dd-trace/src/appsec/reporter.js +2 -3
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +5 -0
- package/packages/dd-trace/src/config.js +108 -26
- package/packages/dd-trace/src/config_defaults.js +12 -0
- package/packages/dd-trace/src/git_properties.js +90 -5
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +8 -3
- package/packages/dd-trace/src/llmobs/plugins/base.js +11 -12
- package/packages/dd-trace/src/llmobs/sdk.js +20 -4
- package/packages/dd-trace/src/llmobs/tagger.js +12 -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 +53 -0
- package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +252 -0
- package/packages/dd-trace/src/opentelemetry/otlp/common.proto +116 -0
- package/packages/dd-trace/src/opentelemetry/otlp/logs.proto +226 -0
- package/packages/dd-trace/src/opentelemetry/otlp/logs_service.proto +78 -0
- package/packages/dd-trace/src/opentelemetry/otlp/metrics.proto +720 -0
- package/packages/dd-trace/src/opentelemetry/otlp/metrics_service.proto +78 -0
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +177 -0
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +163 -0
- package/packages/dd-trace/src/opentelemetry/otlp/protobuf_loader.js +66 -0
- package/packages/dd-trace/src/opentelemetry/otlp/resource.proto +45 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +7 -6
- package/packages/dd-trace/src/plugins/index.js +1 -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 +1 -0
- package/packages/dd-trace/src/remote_config/index.js +1 -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
|
@@ -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,53 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const OtlpHttpExporterBase = require('../otlp/otlp_http_exporter_base')
|
|
4
|
+
const OtlpTransformer = require('./otlp_transformer')
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {import('@opentelemetry/resources').Resource} Resource
|
|
8
|
+
* @typedef {import('@opentelemetry/api-logs').LogRecord} LogRecord
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* OtlpHttpLogExporter exports log records via OTLP over HTTP.
|
|
13
|
+
*
|
|
14
|
+
* This implementation follows the OTLP HTTP specification:
|
|
15
|
+
* https://opentelemetry.io/docs/specs/otlp/#otlphttp
|
|
16
|
+
*
|
|
17
|
+
* @class OtlpHttpLogExporter
|
|
18
|
+
* @extends OtlpHttpExporterBase
|
|
19
|
+
*/
|
|
20
|
+
class OtlpHttpLogExporter extends OtlpHttpExporterBase {
|
|
21
|
+
/**
|
|
22
|
+
* Creates a new OtlpHttpLogExporter instance.
|
|
23
|
+
*
|
|
24
|
+
* @param {string} url - OTLP endpoint URL
|
|
25
|
+
* @param {string} headers - Additional HTTP headers as comma-separated key=value string
|
|
26
|
+
* @param {number} timeout - Request timeout in milliseconds
|
|
27
|
+
* @param {string} protocol - OTLP protocol (http/protobuf or http/json)
|
|
28
|
+
* @param {Resource} resource - Resource attributes
|
|
29
|
+
*/
|
|
30
|
+
constructor (url, headers, timeout, protocol, resource) {
|
|
31
|
+
super(url, headers, timeout, protocol, '/v1/logs', 'logs')
|
|
32
|
+
this.transformer = new OtlpTransformer(resource, protocol)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Exports log records via OTLP over HTTP.
|
|
37
|
+
*
|
|
38
|
+
* @param {LogRecord[]} logRecords - Array of enriched log records to export
|
|
39
|
+
* @param {Function} resultCallback - Callback function for export result
|
|
40
|
+
*/
|
|
41
|
+
export (logRecords, resultCallback) {
|
|
42
|
+
if (logRecords.length === 0) {
|
|
43
|
+
resultCallback({ code: 0 })
|
|
44
|
+
return
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const payload = this.transformer.transformLogRecords(logRecords)
|
|
48
|
+
this._sendPayload(payload, resultCallback)
|
|
49
|
+
this._recordTelemetry('otel.log_records', logRecords.length)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = OtlpHttpLogExporter
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const OtlpTransformerBase = require('../otlp/otlp_transformer_base')
|
|
4
|
+
const { SeverityNumber } = require('@opentelemetry/api-logs')
|
|
5
|
+
const { getProtobufTypes } = require('../otlp/protobuf_loader')
|
|
6
|
+
const { trace } = require('@opentelemetry/api')
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {import('@opentelemetry/api-logs').LogRecord} LogRecord
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// Global severity mapping constant - no need to regenerate
|
|
13
|
+
const SEVERITY_MAP = {
|
|
14
|
+
[SeverityNumber.TRACE]: 'SEVERITY_NUMBER_TRACE',
|
|
15
|
+
[SeverityNumber.TRACE2]: 'SEVERITY_NUMBER_TRACE2',
|
|
16
|
+
[SeverityNumber.TRACE3]: 'SEVERITY_NUMBER_TRACE3',
|
|
17
|
+
[SeverityNumber.TRACE4]: 'SEVERITY_NUMBER_TRACE4',
|
|
18
|
+
[SeverityNumber.DEBUG]: 'SEVERITY_NUMBER_DEBUG',
|
|
19
|
+
[SeverityNumber.DEBUG2]: 'SEVERITY_NUMBER_DEBUG2',
|
|
20
|
+
[SeverityNumber.DEBUG3]: 'SEVERITY_NUMBER_DEBUG3',
|
|
21
|
+
[SeverityNumber.DEBUG4]: 'SEVERITY_NUMBER_DEBUG4',
|
|
22
|
+
[SeverityNumber.INFO]: 'SEVERITY_NUMBER_INFO',
|
|
23
|
+
[SeverityNumber.INFO2]: 'SEVERITY_NUMBER_INFO2',
|
|
24
|
+
[SeverityNumber.INFO3]: 'SEVERITY_NUMBER_INFO3',
|
|
25
|
+
[SeverityNumber.INFO4]: 'SEVERITY_NUMBER_INFO4',
|
|
26
|
+
[SeverityNumber.WARN]: 'SEVERITY_NUMBER_WARN',
|
|
27
|
+
[SeverityNumber.WARN2]: 'SEVERITY_NUMBER_WARN2',
|
|
28
|
+
[SeverityNumber.WARN3]: 'SEVERITY_NUMBER_WARN3',
|
|
29
|
+
[SeverityNumber.WARN4]: 'SEVERITY_NUMBER_WARN4',
|
|
30
|
+
[SeverityNumber.ERROR]: 'SEVERITY_NUMBER_ERROR',
|
|
31
|
+
[SeverityNumber.ERROR2]: 'SEVERITY_NUMBER_ERROR2',
|
|
32
|
+
[SeverityNumber.ERROR3]: 'SEVERITY_NUMBER_ERROR3',
|
|
33
|
+
[SeverityNumber.ERROR4]: 'SEVERITY_NUMBER_ERROR4',
|
|
34
|
+
[SeverityNumber.FATAL]: 'SEVERITY_NUMBER_FATAL',
|
|
35
|
+
[SeverityNumber.FATAL2]: 'SEVERITY_NUMBER_FATAL2',
|
|
36
|
+
[SeverityNumber.FATAL3]: 'SEVERITY_NUMBER_FATAL3',
|
|
37
|
+
[SeverityNumber.FATAL4]: 'SEVERITY_NUMBER_FATAL4'
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* OtlpTransformer transforms log records to OTLP format.
|
|
42
|
+
*
|
|
43
|
+
* This implementation follows the OTLP Logs Data Model specification:
|
|
44
|
+
* https://opentelemetry.io/docs/specs/otlp/#log-data-model
|
|
45
|
+
*
|
|
46
|
+
* @class OtlpTransformer
|
|
47
|
+
* @extends OtlpTransformerBase
|
|
48
|
+
*/
|
|
49
|
+
class OtlpTransformer extends OtlpTransformerBase {
|
|
50
|
+
/**
|
|
51
|
+
* Creates a new OtlpTransformer instance.
|
|
52
|
+
*
|
|
53
|
+
* @param {import('@opentelemetry/api').Attributes} resourceAttributes - Resource attributes
|
|
54
|
+
* @param {string} protocol - OTLP protocol (http/protobuf or http/json)
|
|
55
|
+
*/
|
|
56
|
+
constructor (resourceAttributes, protocol) {
|
|
57
|
+
super(resourceAttributes, protocol, 'logs')
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Transforms log records to OTLP format based on the configured protocol.
|
|
62
|
+
* @param {LogRecord[]} logRecords - Array of enriched log records to transform
|
|
63
|
+
* @returns {Buffer} Transformed log records in the appropriate format
|
|
64
|
+
*/
|
|
65
|
+
transformLogRecords (logRecords) {
|
|
66
|
+
if (this.protocol === 'http/json') {
|
|
67
|
+
return this.#transformToJson(logRecords)
|
|
68
|
+
}
|
|
69
|
+
return this.#transformToProtobuf(logRecords)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Transforms log records to protobuf format.
|
|
74
|
+
* @param {LogRecord[]} logRecords - Array of enriched log records to transform
|
|
75
|
+
* @returns {Buffer} Protobuf-encoded log records
|
|
76
|
+
* @private
|
|
77
|
+
*/
|
|
78
|
+
#transformToProtobuf (logRecords) {
|
|
79
|
+
const { protoLogsService } = getProtobufTypes()
|
|
80
|
+
|
|
81
|
+
const logsData = {
|
|
82
|
+
resourceLogs: [{
|
|
83
|
+
resource: this._transformResource(),
|
|
84
|
+
scopeLogs: this.#transformScope(logRecords),
|
|
85
|
+
}]
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return this._serializeToProtobuf(protoLogsService, logsData)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Transforms log records to JSON format.
|
|
93
|
+
* @param {LogRecord[]} logRecords - Array of enriched log records to transform
|
|
94
|
+
* @returns {Buffer} JSON-encoded log records
|
|
95
|
+
* @private
|
|
96
|
+
*/
|
|
97
|
+
#transformToJson (logRecords) {
|
|
98
|
+
const logsData = {
|
|
99
|
+
resourceLogs: [{
|
|
100
|
+
resource: this._transformResource(),
|
|
101
|
+
scopeLogs: this.#transformScope(logRecords)
|
|
102
|
+
}]
|
|
103
|
+
}
|
|
104
|
+
return this._serializeToJson(logsData)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Creates scope logs grouped by instrumentation library.
|
|
109
|
+
* @param {LogRecord[]} logRecords - Array of log records to transform
|
|
110
|
+
* @returns {Object[]} Array of scope log objects
|
|
111
|
+
* @private
|
|
112
|
+
*/
|
|
113
|
+
#transformScope (logRecords) {
|
|
114
|
+
const groupedRecords = this._groupByInstrumentationScope(logRecords)
|
|
115
|
+
const scopeLogs = []
|
|
116
|
+
|
|
117
|
+
for (const records of groupedRecords.values()) {
|
|
118
|
+
const schemaUrl = records[0]?.instrumentationScope?.schemaUrl || ''
|
|
119
|
+
scopeLogs.push({
|
|
120
|
+
scope: {
|
|
121
|
+
name: records[0]?.instrumentationScope?.name || 'dd-trace-js',
|
|
122
|
+
version: records[0]?.instrumentationScope?.version || '',
|
|
123
|
+
attributes: [],
|
|
124
|
+
droppedAttributesCount: 0
|
|
125
|
+
},
|
|
126
|
+
schemaUrl,
|
|
127
|
+
logRecords: records.map(record => this.#transformLogRecord(record))
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return scopeLogs
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Transforms a single log record to OTLP format.
|
|
136
|
+
* @param {LogRecord} logRecord - Log record to transform
|
|
137
|
+
* @returns {Object} OTLP log record object
|
|
138
|
+
* @private
|
|
139
|
+
*/
|
|
140
|
+
#transformLogRecord (logRecord) {
|
|
141
|
+
const spanContext = this.#extractSpanContext(logRecord.context)
|
|
142
|
+
|
|
143
|
+
const result = {
|
|
144
|
+
timeUnixNano: logRecord.timestamp,
|
|
145
|
+
body: this.#transformBody(logRecord.body)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Add optional fields only if they are set
|
|
149
|
+
if (logRecord.observedTimestamp) {
|
|
150
|
+
result.observedTimeUnixNano = logRecord.observedTimestamp
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (logRecord.severityNumber !== undefined) {
|
|
154
|
+
result.severityNumber = this.#mapSeverityNumber(logRecord.severityNumber)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (logRecord.severityText) {
|
|
158
|
+
result.severityText = logRecord.severityText
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (logRecord.attributes) {
|
|
162
|
+
result.attributes = this._transformAttributes(logRecord.attributes)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (spanContext?.traceFlags !== undefined) {
|
|
166
|
+
result.flags = spanContext.traceFlags
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Only include traceId and spanId if they are valid
|
|
170
|
+
if (spanContext?.traceId && spanContext.traceId !== '00000000000000000000000000000000') {
|
|
171
|
+
result.traceId = this.#hexToBytes(spanContext.traceId)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (spanContext?.spanId && spanContext.spanId !== '0000000000000000') {
|
|
175
|
+
result.spanId = this.#hexToBytes(spanContext.spanId)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return result
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Extracts span context from the log record's context.
|
|
183
|
+
* @param {Object} logContext - The log record's context
|
|
184
|
+
* @returns {Object|null} Span context or null if not available
|
|
185
|
+
* @private
|
|
186
|
+
*/
|
|
187
|
+
#extractSpanContext (logContext) {
|
|
188
|
+
if (!logContext) return null
|
|
189
|
+
|
|
190
|
+
const activeSpan = trace.getSpan(logContext)
|
|
191
|
+
if (activeSpan) {
|
|
192
|
+
return activeSpan.spanContext()
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return null
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Maps OpenTelemetry severity number to protobuf severity number.
|
|
200
|
+
* @param {number} severityNumber - OpenTelemetry severity number
|
|
201
|
+
* @returns {number} Protobuf severity number
|
|
202
|
+
* @private
|
|
203
|
+
*/
|
|
204
|
+
#mapSeverityNumber (severityNumber) {
|
|
205
|
+
const { protoSeverityNumber } = getProtobufTypes()
|
|
206
|
+
const severityName = SEVERITY_MAP[severityNumber] || 'SEVERITY_NUMBER_INFO'
|
|
207
|
+
return protoSeverityNumber.values[severityName]
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Converts a hex string to a Buffer.
|
|
212
|
+
* @param {string} hexString - Hex string to convert
|
|
213
|
+
* @returns {Buffer} Buffer containing the hex data
|
|
214
|
+
* @private
|
|
215
|
+
*/
|
|
216
|
+
#hexToBytes (hexString) {
|
|
217
|
+
const cleanHex = hexString ? (hexString.startsWith('0x') ? hexString.slice(2) : hexString) : ''
|
|
218
|
+
const paddedHex = cleanHex.length % 2 === 0 ? cleanHex : '0' + cleanHex
|
|
219
|
+
return Buffer.from(paddedHex, 'hex')
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Transforms log body to OTLP AnyValue format.
|
|
224
|
+
* @param {any} body - Log body to transform
|
|
225
|
+
* @returns {Object} OTLP AnyValue object
|
|
226
|
+
* @private
|
|
227
|
+
*/
|
|
228
|
+
#transformBody (body) {
|
|
229
|
+
if (typeof body === 'string') {
|
|
230
|
+
return { stringValue: body }
|
|
231
|
+
} else if (typeof body === 'number') {
|
|
232
|
+
if (Number.isInteger(body)) {
|
|
233
|
+
return { intValue: body }
|
|
234
|
+
}
|
|
235
|
+
return { doubleValue: body }
|
|
236
|
+
} else if (typeof body === 'boolean') {
|
|
237
|
+
return { boolValue: body }
|
|
238
|
+
} else if (body && typeof body === 'object') {
|
|
239
|
+
return {
|
|
240
|
+
kvlistValue: {
|
|
241
|
+
values: Object.entries(body).map(([key, value]) => ({
|
|
242
|
+
key,
|
|
243
|
+
value: this._transformAnyValue(value)
|
|
244
|
+
}))
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return { stringValue: String(body) }
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
module.exports = OtlpTransformer
|