dd-trace 5.98.0 → 5.99.1
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 +0 -1
- package/ext/tags.js +1 -0
- package/index.d.ts +9 -1
- package/package.json +68 -47
- package/packages/datadog-instrumentations/src/crypto.js +45 -0
- package/packages/datadog-instrumentations/src/cypress-config.js +122 -16
- package/packages/datadog-instrumentations/src/dns.js +24 -56
- package/packages/datadog-instrumentations/src/graphql.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +74 -0
- package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +4 -1
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +10 -3
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/modelcontextprotocol-sdk.js +59 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +11 -2
- package/packages/datadog-instrumentations/src/jest.js +5 -5
- package/packages/datadog-instrumentations/src/modelcontextprotocol-sdk.js +7 -0
- package/packages/datadog-instrumentations/src/pino.js +4 -28
- package/packages/datadog-instrumentations/src/playwright-browser-scripts.js +27 -0
- package/packages/datadog-instrumentations/src/playwright.js +5 -17
- package/packages/datadog-instrumentations/src/stripe.js +38 -24
- package/packages/datadog-instrumentations/src/vitest.js +32 -4
- package/packages/datadog-instrumentations/src/zlib.js +29 -0
- package/packages/datadog-plugin-aws-sdk/src/base.js +1 -2
- package/packages/datadog-plugin-azure-event-hubs/src/producer.js +8 -15
- package/packages/datadog-plugin-azure-service-bus/src/producer.js +4 -9
- package/packages/datadog-plugin-cucumber/src/index.js +2 -2
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +5 -5
- package/packages/datadog-plugin-cypress/src/source-map-utils.js +48 -1
- package/packages/datadog-plugin-dd-trace-api/src/index.js +1 -1
- package/packages/datadog-plugin-graphql/src/utils.js +2 -2
- package/packages/datadog-plugin-http/src/server.js +11 -11
- package/packages/datadog-plugin-jest/src/index.js +2 -2
- package/packages/datadog-plugin-memcached/src/index.js +1 -1
- package/packages/datadog-plugin-mocha/src/index.js +1 -2
- package/packages/datadog-plugin-modelcontextprotocol-sdk/src/index.js +24 -0
- package/packages/datadog-plugin-modelcontextprotocol-sdk/src/tracing.js +55 -0
- package/packages/datadog-plugin-mongodb-core/src/index.js +1 -6
- package/packages/datadog-plugin-playwright/src/index.js +2 -3
- package/packages/datadog-plugin-vitest/src/index.js +14 -6
- package/packages/datadog-plugin-ws/src/close.js +2 -0
- package/packages/datadog-plugin-ws/src/producer.js +2 -0
- package/packages/datadog-plugin-ws/src/receiver.js +1 -0
- package/packages/dd-trace/src/aiguard/channels.js +8 -0
- package/packages/dd-trace/src/aiguard/index.js +7 -3
- package/packages/dd-trace/src/aiguard/sdk.js +44 -0
- package/packages/dd-trace/src/aiguard/tags.js +1 -0
- package/packages/dd-trace/src/appsec/blocking.js +18 -6
- package/packages/dd-trace/src/appsec/graphql.js +7 -7
- package/packages/dd-trace/src/appsec/index.js +9 -11
- package/packages/dd-trace/src/appsec/rasp/command_injection.js +4 -5
- package/packages/dd-trace/src/appsec/rasp/lfi.js +8 -4
- package/packages/dd-trace/src/appsec/rasp/sql_injection.js +5 -10
- package/packages/dd-trace/src/appsec/rasp/ssrf.js +5 -6
- package/packages/dd-trace/src/appsec/recommended.json +2438 -13
- package/packages/dd-trace/src/appsec/reporter.js +6 -5
- package/packages/dd-trace/src/appsec/sdk/user_blocking.js +4 -8
- package/packages/dd-trace/src/appsec/store.js +50 -0
- package/packages/dd-trace/src/appsec/waf/index.js +3 -5
- package/packages/dd-trace/src/baggage.js +16 -13
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +3 -4
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +2 -2
- package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +4 -5
- package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +3 -4
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +6 -6
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +2 -2
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +2 -2
- package/packages/dd-trace/src/config/config-types.d.ts +0 -4
- package/packages/dd-trace/src/config/defaults.js +10 -10
- package/packages/dd-trace/src/config/generated-config-types.d.ts +39 -38
- package/packages/dd-trace/src/config/index.js +29 -39
- package/packages/dd-trace/src/config/parsers.js +26 -9
- package/packages/dd-trace/src/config/supported-configurations.json +46 -78
- package/packages/dd-trace/src/debugger/config.js +2 -0
- package/packages/dd-trace/src/debugger/devtools_client/send.js +25 -5
- package/packages/dd-trace/src/dogstatsd.js +5 -8
- package/packages/dd-trace/src/encode/0.4.js +4 -5
- package/packages/dd-trace/src/exporter.js +1 -1
- package/packages/dd-trace/src/exporters/agent/index.js +0 -1
- package/packages/dd-trace/src/exporters/agent/writer.js +1 -2
- package/packages/dd-trace/src/exporters/agentless/writer.js +3 -3
- package/packages/dd-trace/src/exporters/common/util.js +2 -2
- package/packages/dd-trace/src/git_metadata_tagger.js +1 -1
- package/packages/dd-trace/src/id.js +2 -0
- package/packages/dd-trace/src/index.js +2 -5
- package/packages/dd-trace/src/lambda/handler.js +1 -3
- package/packages/dd-trace/src/llmobs/constants/tags.js +3 -0
- package/packages/dd-trace/src/llmobs/plugins/{anthropic.js → anthropic/index.js} +5 -63
- package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +106 -0
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chain.js +3 -2
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chat_model.js +3 -2
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/embedding.js +2 -1
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +0 -49
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/vectorstore.js +2 -1
- package/packages/dd-trace/src/llmobs/plugins/langchain/messages.js +76 -0
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -26
- package/packages/dd-trace/src/llmobs/plugins/modelcontextprotocol-sdk/index.js +68 -0
- package/packages/dd-trace/src/llmobs/plugins/modelcontextprotocol-sdk/utils.js +57 -0
- package/packages/dd-trace/src/llmobs/sdk.js +23 -3
- package/packages/dd-trace/src/llmobs/span_processor.js +14 -1
- package/packages/dd-trace/src/llmobs/writers/base.js +7 -1
- package/packages/dd-trace/src/llmobs/writers/spans.js +1 -1
- package/packages/dd-trace/src/openfeature/eval-metrics-hook.js +103 -0
- package/packages/dd-trace/src/openfeature/flagging_provider.js +3 -0
- package/packages/dd-trace/src/opentelemetry/logs/index.js +6 -6
- package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +3 -2
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +7 -7
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +3 -2
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +19 -66
- package/packages/dd-trace/src/opentelemetry/trace/index.js +11 -16
- package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +11 -3
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +51 -41
- package/packages/dd-trace/src/opentelemetry/tracer.js +9 -11
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +30 -23
- package/packages/dd-trace/src/opentracing/span.js +2 -2
- package/packages/dd-trace/src/opentracing/tracer.js +12 -5
- package/packages/dd-trace/src/plugin_manager.js +6 -6
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/log_plugin.js +1 -1
- package/packages/dd-trace/src/plugins/util/test.js +128 -7
- package/packages/dd-trace/src/plugins/util/url.js +2 -1
- package/packages/dd-trace/src/profiling/profilers/event_plugins/crypto.js +32 -0
- package/packages/dd-trace/src/profiling/profilers/event_plugins/zlib.js +19 -0
- package/packages/dd-trace/src/profiling/profilers/events.js +35 -0
- package/packages/dd-trace/src/proxy.js +8 -14
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +2 -2
- package/packages/dd-trace/src/service-naming/schemas/v0/web.js +4 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/web.js +4 -0
- package/packages/dd-trace/src/span_processor.js +1 -2
- package/packages/dd-trace/src/tagger.js +2 -2
- package/packages/dd-trace/src/telemetry/send-data.js +5 -7
- package/packages/dd-trace/src/tracer.js +2 -2
- package/vendor/dist/ignore/LICENSE +0 -21
- package/vendor/dist/ignore/index.js +0 -1
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const log = require('../log')
|
|
4
|
+
|
|
5
|
+
const METER_NAME = 'dd-trace-js/openfeature'
|
|
6
|
+
const COUNTER_NAME = 'feature_flag.evaluations'
|
|
7
|
+
const COUNTER_DESCRIPTION = 'Number of feature flag evaluations'
|
|
8
|
+
const COUNTER_UNIT = '{evaluation}'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* OpenFeature hook that tracks feature flag evaluation metrics using an
|
|
12
|
+
* OpenTelemetry counter.
|
|
13
|
+
*
|
|
14
|
+
* Implements the OpenFeature `finally` hook interface so it can be pushed
|
|
15
|
+
* directly onto a provider's `hooks` array. We use the `finally` stage
|
|
16
|
+
* (not diagnostic channels inside the provider's `resolve*` methods) because
|
|
17
|
+
* the OpenFeature SDK short-circuits before calling the provider when it is in
|
|
18
|
+
* NOT_READY state; the `finally` hook still fires, ensuring all evaluations are
|
|
19
|
+
* captured. It also catches type-mismatch errors detected by the SDK client
|
|
20
|
+
* after the provider returns.
|
|
21
|
+
*
|
|
22
|
+
* The counter is created lazily on the first successful `finally()` call rather
|
|
23
|
+
* than in the constructor. This is necessary because `FlaggingProvider` is
|
|
24
|
+
* constructed eagerly by `proxy.js#updateTracing()`, which runs *before*
|
|
25
|
+
* `initializeOpenTelemetryMetrics()` sets the global OTel meter provider.
|
|
26
|
+
* Calling `getMeter()` in the constructor would return the noop meter and
|
|
27
|
+
* produce a noop counter that silently discards all measurements. By deferring
|
|
28
|
+
* to `finally()` time we give the meter provider a chance to be set up first.
|
|
29
|
+
*
|
|
30
|
+
* If counter creation fails (e.g. the OTel API is not yet available), the call
|
|
31
|
+
* is silently skipped and retried on the next `finally()` invocation.
|
|
32
|
+
*
|
|
33
|
+
* When `config.otelMetricsEnabled` is false, `finally()` is always a no-op.
|
|
34
|
+
*/
|
|
35
|
+
class EvalMetricsHook {
|
|
36
|
+
#enabled = false
|
|
37
|
+
#counter = null
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param {import('../config')} config - Tracer configuration object
|
|
41
|
+
*/
|
|
42
|
+
constructor (config) {
|
|
43
|
+
this.#enabled = config.otelMetricsEnabled === true
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Returns the OTel counter, creating it on first successful call.
|
|
48
|
+
* Returns `null` if counter creation fails; will retry on next call.
|
|
49
|
+
*
|
|
50
|
+
* @returns {import('@opentelemetry/api').Counter | null}
|
|
51
|
+
*/
|
|
52
|
+
#getCounter () {
|
|
53
|
+
if (this.#counter) return this.#counter
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
const { metrics } = require('@opentelemetry/api')
|
|
57
|
+
const meter = metrics.getMeter(METER_NAME)
|
|
58
|
+
this.#counter = meter.createCounter(COUNTER_NAME, {
|
|
59
|
+
description: COUNTER_DESCRIPTION,
|
|
60
|
+
unit: COUNTER_UNIT,
|
|
61
|
+
})
|
|
62
|
+
} catch (e) {
|
|
63
|
+
log.warn('EvalMetricsHook: failed to create counter: %s', e.message)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return this.#counter
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Called by the OpenFeature SDK after every flag evaluation (success or error).
|
|
71
|
+
*
|
|
72
|
+
* @param {{ flagKey: string }} hookContext - Hook context containing the flag key
|
|
73
|
+
* @param {{ variant?: string, reason?: string, errorCode?: string, flagMetadata?: object }} evaluationDetails
|
|
74
|
+
* - Full evaluation details
|
|
75
|
+
* @returns {void}
|
|
76
|
+
*/
|
|
77
|
+
finally (hookContext, evaluationDetails) {
|
|
78
|
+
if (!this.#enabled) return
|
|
79
|
+
|
|
80
|
+
const counter = this.#getCounter()
|
|
81
|
+
if (!counter) return
|
|
82
|
+
|
|
83
|
+
const attributes = {
|
|
84
|
+
'feature_flag.key': hookContext?.flagKey ?? '',
|
|
85
|
+
'feature_flag.result.variant': evaluationDetails?.variant ?? '',
|
|
86
|
+
'feature_flag.result.reason': evaluationDetails?.reason?.toLowerCase() ?? 'unknown',
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const errorCode = evaluationDetails?.errorCode
|
|
90
|
+
if (errorCode) {
|
|
91
|
+
attributes['error.type'] = errorCode.toLowerCase()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const allocationKey = evaluationDetails?.flagMetadata?.allocationKey
|
|
95
|
+
if (allocationKey) {
|
|
96
|
+
attributes['feature_flag.result.allocation_key'] = allocationKey
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
counter.add(1, attributes)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
module.exports = EvalMetricsHook
|
|
@@ -4,6 +4,7 @@ const { DatadogNodeServerProvider } = require('@datadog/openfeature-node-server'
|
|
|
4
4
|
const { channel } = require('dc-polyfill')
|
|
5
5
|
const log = require('../log')
|
|
6
6
|
const { EXPOSURE_CHANNEL } = require('./constants/constants')
|
|
7
|
+
const EvalMetricsHook = require('./eval-metrics-hook')
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* OpenFeature provider that integrates with Datadog's feature flagging system.
|
|
@@ -24,6 +25,8 @@ class FlaggingProvider extends DatadogNodeServerProvider {
|
|
|
24
25
|
this._tracer = tracer
|
|
25
26
|
this._config = config
|
|
26
27
|
|
|
28
|
+
this.hooks.push(new EvalMetricsHook(config))
|
|
29
|
+
|
|
27
30
|
log.debug('%s created with timeout: %dms', this.constructor.name,
|
|
28
31
|
config.experimental.flaggingProvider.initializationTimeoutMs)
|
|
29
32
|
}
|
|
@@ -60,18 +60,18 @@ function initializeOpenTelemetryLogs (config) {
|
|
|
60
60
|
|
|
61
61
|
// Create OTLP exporter using resolved config values
|
|
62
62
|
const exporter = new OtlpHttpLogExporter(
|
|
63
|
-
config.
|
|
64
|
-
config.
|
|
65
|
-
config.
|
|
66
|
-
config.
|
|
63
|
+
config.OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,
|
|
64
|
+
config.OTEL_EXPORTER_OTLP_LOGS_HEADERS,
|
|
65
|
+
config.OTEL_EXPORTER_OTLP_LOGS_TIMEOUT,
|
|
66
|
+
config.OTEL_EXPORTER_OTLP_LOGS_PROTOCOL,
|
|
67
67
|
resourceAttributes
|
|
68
68
|
)
|
|
69
69
|
|
|
70
70
|
// Create batch processor for exporting logs to Datadog Agent
|
|
71
71
|
const processor = new BatchLogRecordProcessor(
|
|
72
72
|
exporter,
|
|
73
|
-
config.
|
|
74
|
-
config.
|
|
73
|
+
config.OTEL_BSP_SCHEDULE_DELAY,
|
|
74
|
+
config.OTEL_BSP_MAX_EXPORT_BATCH_SIZE
|
|
75
75
|
)
|
|
76
76
|
|
|
77
77
|
// Create logger provider with processor for Datadog Agent export
|
|
@@ -22,13 +22,14 @@ class OtlpHttpLogExporter extends OtlpHttpExporterBase {
|
|
|
22
22
|
* Creates a new OtlpHttpLogExporter instance.
|
|
23
23
|
*
|
|
24
24
|
* @param {string} url - OTLP endpoint URL
|
|
25
|
-
* @param {string} headers - Additional HTTP headers
|
|
25
|
+
* @param {Record<string, string>|undefined} headers - Additional HTTP headers parsed from the
|
|
26
|
+
* corresponding `OTEL_EXPORTER_OTLP_*_HEADERS` env by the MAP parser.
|
|
26
27
|
* @param {number} timeout - Request timeout in milliseconds
|
|
27
28
|
* @param {string} protocol - OTLP protocol (http/protobuf or http/json)
|
|
28
29
|
* @param {Resource} resource - Resource attributes
|
|
29
30
|
*/
|
|
30
31
|
constructor (url, headers, timeout, protocol, resource) {
|
|
31
|
-
super(url, headers, timeout, protocol, '
|
|
32
|
+
super(url, headers, timeout, protocol, 'logs')
|
|
32
33
|
this.transformer = new OtlpTransformer(resource, protocol)
|
|
33
34
|
}
|
|
34
35
|
|
|
@@ -57,18 +57,18 @@ function initializeOpenTelemetryMetrics (config) {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
const exporter = new OtlpHttpMetricExporter(
|
|
60
|
-
config.
|
|
61
|
-
config.
|
|
62
|
-
config.
|
|
63
|
-
config.
|
|
60
|
+
config.OTEL_EXPORTER_OTLP_METRICS_ENDPOINT,
|
|
61
|
+
config.OTEL_EXPORTER_OTLP_METRICS_HEADERS,
|
|
62
|
+
config.OTEL_EXPORTER_OTLP_METRICS_TIMEOUT,
|
|
63
|
+
config.OTEL_EXPORTER_OTLP_METRICS_PROTOCOL,
|
|
64
64
|
resourceAttributes
|
|
65
65
|
)
|
|
66
66
|
|
|
67
67
|
const reader = new PeriodicMetricReader(
|
|
68
68
|
exporter,
|
|
69
|
-
config.
|
|
70
|
-
config.
|
|
71
|
-
config.
|
|
69
|
+
config.OTEL_METRIC_EXPORT_INTERVAL,
|
|
70
|
+
config.OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE,
|
|
71
|
+
config.OTEL_BSP_MAX_QUEUE_SIZE
|
|
72
72
|
)
|
|
73
73
|
|
|
74
74
|
const meterProvider = new MeterProvider({ reader })
|
|
@@ -18,13 +18,14 @@ class OtlpHttpMetricExporter extends OtlpHttpExporterBase {
|
|
|
18
18
|
* Creates a new OtlpHttpMetricExporter instance.
|
|
19
19
|
*
|
|
20
20
|
* @param {string} url - OTLP endpoint URL
|
|
21
|
-
* @param {string} headers - Additional HTTP headers
|
|
21
|
+
* @param {Record<string, string>|undefined} headers - Additional HTTP headers parsed from the
|
|
22
|
+
* corresponding `OTEL_EXPORTER_OTLP_*_HEADERS` env by the MAP parser.
|
|
22
23
|
* @param {number} timeout - Request timeout in milliseconds
|
|
23
24
|
* @param {string} protocol - OTLP protocol (http/protobuf or http/json)
|
|
24
25
|
* @param {Resource} resource - Resource attributes
|
|
25
26
|
*/
|
|
26
27
|
constructor (url, headers, timeout, protocol, resource) {
|
|
27
|
-
super(url, headers, timeout, protocol, '
|
|
28
|
+
super(url, headers, timeout, protocol, 'metrics')
|
|
28
29
|
this.transformer = new OtlpTransformer(resource, protocol)
|
|
29
30
|
}
|
|
30
31
|
|
|
@@ -19,34 +19,31 @@ class OtlpHttpExporterBase {
|
|
|
19
19
|
/**
|
|
20
20
|
* Creates a new OtlpHttpExporterBase instance.
|
|
21
21
|
*
|
|
22
|
-
* @param {string} url - OTLP endpoint URL
|
|
23
|
-
* @param {string
|
|
22
|
+
* @param {string} url - OTLP endpoint URL (callers are expected to supply the full signal URL)
|
|
23
|
+
* @param {Record<string, string>|undefined} headers - Additional HTTP headers parsed from the
|
|
24
|
+
* corresponding `OTEL_EXPORTER_OTLP_*_HEADERS` env by the MAP parser.
|
|
24
25
|
* @param {number} timeout - Request timeout in milliseconds
|
|
25
26
|
* @param {string} protocol - OTLP protocol (http/protobuf or http/json)
|
|
26
|
-
* @param {string} defaultPath - Default path to use if URL has no path
|
|
27
27
|
* @param {string} signalType - Signal type for error messages (e.g., 'logs', 'metrics')
|
|
28
28
|
*/
|
|
29
|
-
constructor (url, headers, timeout, protocol,
|
|
30
|
-
const parsedUrl = new URL(url)
|
|
31
|
-
|
|
29
|
+
constructor (url, headers, timeout, protocol, signalType) {
|
|
32
30
|
this.protocol = protocol
|
|
33
31
|
this.signalType = signalType
|
|
34
32
|
|
|
35
|
-
// If no path is provided, use default path
|
|
36
|
-
const path = parsedUrl.pathname === '/' ? defaultPath : parsedUrl.pathname
|
|
37
33
|
const isJson = protocol === 'http/json'
|
|
38
34
|
|
|
35
|
+
// Initialize fields setUrl doesn't touch; it fills in hostname/port/path below.
|
|
39
36
|
this.options = {
|
|
40
|
-
hostname: parsedUrl.hostname,
|
|
41
|
-
port: parsedUrl.port,
|
|
42
|
-
path: path + parsedUrl.search,
|
|
43
37
|
method: 'POST',
|
|
44
38
|
timeout,
|
|
45
39
|
headers: {
|
|
46
40
|
'Content-Type': isJson ? 'application/json' : 'application/x-protobuf',
|
|
47
|
-
...
|
|
41
|
+
...headers,
|
|
48
42
|
},
|
|
49
43
|
}
|
|
44
|
+
|
|
45
|
+
this.setUrl(url)
|
|
46
|
+
|
|
50
47
|
this.telemetryTags = [
|
|
51
48
|
'protocol:http',
|
|
52
49
|
`encoding:${isJson ? 'json' : 'protobuf'}`,
|
|
@@ -61,6 +58,7 @@ class OtlpHttpExporterBase {
|
|
|
61
58
|
* @protected
|
|
62
59
|
*/
|
|
63
60
|
recordTelemetry (metricName, count, additionalTags) {
|
|
61
|
+
// @ts-expect-error - additionalTags is optional and can be undefined
|
|
64
62
|
if (additionalTags?.length > 0) {
|
|
65
63
|
tracerMetrics.count(metricName, [...this.telemetryTags, ...additionalTags || []]).inc(count)
|
|
66
64
|
} else {
|
|
@@ -91,6 +89,7 @@ class OtlpHttpExporterBase {
|
|
|
91
89
|
})
|
|
92
90
|
|
|
93
91
|
res.once('end', () => {
|
|
92
|
+
// @ts-expect-error - res.statusCode can be undefined
|
|
94
93
|
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
95
94
|
resultCallback({ code: 0 })
|
|
96
95
|
} else {
|
|
@@ -116,61 +115,15 @@ class OtlpHttpExporterBase {
|
|
|
116
115
|
}
|
|
117
116
|
|
|
118
117
|
/**
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
* @
|
|
118
|
+
* Updates the target URL used by this exporter. The URL is used as-is per the OTel spec: the
|
|
119
|
+
* caller is responsible for including the signal-specific path (`/v1/traces` etc.).
|
|
120
|
+
* @param {string} url - New OTLP endpoint URL
|
|
122
121
|
*/
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const result = {}
|
|
129
|
-
for (const [k, v] of Object.entries(headersString)) {
|
|
130
|
-
if (v === '' && k.includes('=')) {
|
|
131
|
-
const idx = k.indexOf('=')
|
|
132
|
-
result[k.slice(0, idx).trim()] = k.slice(idx + 1).trim()
|
|
133
|
-
} else {
|
|
134
|
-
result[k] = v
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
return result
|
|
138
|
-
}
|
|
139
|
-
const headers = {}
|
|
140
|
-
let key = ''
|
|
141
|
-
let value = ''
|
|
142
|
-
let readingKey = true
|
|
143
|
-
|
|
144
|
-
for (const char of headersString) {
|
|
145
|
-
if (readingKey) {
|
|
146
|
-
if (char === '=') {
|
|
147
|
-
readingKey = false
|
|
148
|
-
key = key.trim()
|
|
149
|
-
} else {
|
|
150
|
-
key += char
|
|
151
|
-
}
|
|
152
|
-
} else if (char === ',') {
|
|
153
|
-
value = value.trim()
|
|
154
|
-
if (key && value) {
|
|
155
|
-
headers[key] = value
|
|
156
|
-
}
|
|
157
|
-
key = ''
|
|
158
|
-
value = ''
|
|
159
|
-
readingKey = true
|
|
160
|
-
} else {
|
|
161
|
-
value += char
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Add the last pair if present
|
|
166
|
-
if (!readingKey) {
|
|
167
|
-
value = value.trim()
|
|
168
|
-
if (value) {
|
|
169
|
-
headers[key] = value
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
return headers
|
|
122
|
+
setUrl (url) {
|
|
123
|
+
const parsedUrl = new URL(url)
|
|
124
|
+
this.options.hostname = parsedUrl.hostname
|
|
125
|
+
this.options.port = parsedUrl.port
|
|
126
|
+
this.options.path = parsedUrl.pathname + parsedUrl.search
|
|
174
127
|
}
|
|
175
128
|
|
|
176
129
|
/**
|
|
@@ -4,7 +4,7 @@ const { VERSION } = require('../../../../../version')
|
|
|
4
4
|
const OtlpHttpTraceExporter = require('./otlp_http_trace_exporter')
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* @typedef {import('../../config')} Config
|
|
7
|
+
* @typedef {import('../../config/config-base')} Config
|
|
8
8
|
* @typedef {import('../../opentracing/tracer')} DatadogTracer
|
|
9
9
|
*/
|
|
10
10
|
|
|
@@ -33,21 +33,17 @@ const OtlpHttpTraceExporter = require('./otlp_http_trace_exporter')
|
|
|
33
33
|
*/
|
|
34
34
|
function buildResourceAttributes (config) {
|
|
35
35
|
const resourceAttributes = {
|
|
36
|
-
'service.name': config.service
|
|
36
|
+
'service.name': config.service,
|
|
37
37
|
'telemetry.sdk.name': 'datadog',
|
|
38
38
|
'telemetry.sdk.version': VERSION,
|
|
39
39
|
'telemetry.sdk.language': 'nodejs',
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
if (
|
|
44
|
-
const version = config.version || config.tags.version
|
|
45
|
-
if (version) resourceAttributes['service.version'] = version
|
|
42
|
+
if (config.env) resourceAttributes['deployment.environment.name'] = config.env
|
|
43
|
+
if (config.version) resourceAttributes['service.version'] = config.version
|
|
46
44
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
Object.assign(resourceAttributes, filteredTags)
|
|
50
|
-
}
|
|
45
|
+
const { service, version, env, ...filteredTags } = config.tags
|
|
46
|
+
Object.assign(resourceAttributes, filteredTags)
|
|
51
47
|
|
|
52
48
|
return resourceAttributes
|
|
53
49
|
}
|
|
@@ -56,15 +52,14 @@ function buildResourceAttributes (config) {
|
|
|
56
52
|
* Creates the OTLP HTTP/JSON trace exporter.
|
|
57
53
|
*
|
|
58
54
|
* @param {Config} config - Tracer configuration instance
|
|
59
|
-
* @param {import('@opentelemetry/api').Attributes} resourceAttributes - Resource attributes
|
|
60
55
|
* @returns {OtlpHttpTraceExporter} The OTLP HTTP/JSON exporter
|
|
61
56
|
*/
|
|
62
|
-
function createOtlpTraceExporter (config
|
|
57
|
+
function createOtlpTraceExporter (config) {
|
|
63
58
|
return new OtlpHttpTraceExporter(
|
|
64
|
-
config.
|
|
65
|
-
config.
|
|
66
|
-
config.
|
|
67
|
-
|
|
59
|
+
config.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
|
|
60
|
+
config.OTEL_EXPORTER_OTLP_TRACES_HEADERS,
|
|
61
|
+
config.OTEL_EXPORTER_OTLP_TRACES_TIMEOUT,
|
|
62
|
+
buildResourceAttributes(config)
|
|
68
63
|
)
|
|
69
64
|
}
|
|
70
65
|
|
|
@@ -8,13 +8,20 @@ const OtlpTraceTransformer = require('./otlp_transformer')
|
|
|
8
8
|
/**
|
|
9
9
|
* OtlpHttpTraceExporter exports DD-formatted spans via OTLP over HTTP/JSON.
|
|
10
10
|
*
|
|
11
|
-
* This implementation follows the OTLP HTTP
|
|
11
|
+
* This implementation follows the OTLP HTTP specification:
|
|
12
12
|
* https://opentelemetry.io/docs/specs/otlp/#otlphttp
|
|
13
13
|
*
|
|
14
14
|
* It receives DD-formatted spans (from span_format.js), transforms them
|
|
15
15
|
* to OTLP ExportTraceServiceRequest JSON format, and sends them to the
|
|
16
16
|
* configured OTLP endpoint via HTTP POST.
|
|
17
17
|
*
|
|
18
|
+
* TODO: Add batch handling similar to the OpenTelemetry SDK Batch Processor
|
|
19
|
+
* (https://opentelemetry.io/docs/specs/otel/trace/sdk/#batching-processor).
|
|
20
|
+
* Currently each finished trace is sent as its own HTTP request, which is
|
|
21
|
+
* unsuitable for high-traffic production environments. The config values
|
|
22
|
+
* `OTEL_BSP_SCHEDULE_DELAY`, `OTEL_BSP_MAX_EXPORT_BATCH_SIZE`, and `OTEL_BSP_MAX_QUEUE_SIZE`
|
|
23
|
+
* (OTEL_BSP_*) are already defined and should drive that implementation.
|
|
24
|
+
*
|
|
18
25
|
* @class OtlpHttpTraceExporter
|
|
19
26
|
* @augments OtlpHttpExporterBase
|
|
20
27
|
*/
|
|
@@ -25,12 +32,13 @@ class OtlpHttpTraceExporter extends OtlpHttpExporterBase {
|
|
|
25
32
|
* Creates a new OtlpHttpTraceExporter instance.
|
|
26
33
|
*
|
|
27
34
|
* @param {string} url - OTLP endpoint URL
|
|
28
|
-
* @param {string} headers - Additional HTTP headers
|
|
35
|
+
* @param {Record<string, string>|undefined} headers - Additional HTTP headers parsed from the
|
|
36
|
+
* corresponding `OTEL_EXPORTER_OTLP_*_HEADERS` env by the MAP parser.
|
|
29
37
|
* @param {number} timeout - Request timeout in milliseconds
|
|
30
38
|
* @param {import('@opentelemetry/api').Attributes} resourceAttributes - Resource attributes
|
|
31
39
|
*/
|
|
32
40
|
constructor (url, headers, timeout, resourceAttributes) {
|
|
33
|
-
super(url, headers, timeout, 'http/json', '
|
|
41
|
+
super(url, headers, timeout, 'http/json', 'traces')
|
|
34
42
|
this.#transformer = new OtlpTraceTransformer(resourceAttributes)
|
|
35
43
|
}
|
|
36
44
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const OtlpTransformerBase = require('../otlp/otlp_transformer_base')
|
|
4
4
|
const { getProtobufTypes } = require('../otlp/protobuf_loader')
|
|
5
5
|
const { VERSION } = require('../../../../../version')
|
|
6
|
+
const id = require('../../id')
|
|
6
7
|
|
|
7
8
|
const { protoSpanKind } = getProtobufTypes()
|
|
8
9
|
const SPAN_KIND_UNSPECIFIED = protoSpanKind.values.SPAN_KIND_UNSPECIFIED
|
|
@@ -12,11 +13,28 @@ const SPAN_KIND_CLIENT = protoSpanKind.values.SPAN_KIND_CLIENT
|
|
|
12
13
|
const SPAN_KIND_PRODUCER = protoSpanKind.values.SPAN_KIND_PRODUCER
|
|
13
14
|
const SPAN_KIND_CONSUMER = protoSpanKind.values.SPAN_KIND_CONSUMER
|
|
14
15
|
|
|
16
|
+
// Cached zero Identifier used to detect zero IDs without re-allocating per span.
|
|
17
|
+
const ZERO_ID = id('0')
|
|
18
|
+
|
|
15
19
|
/**
|
|
20
|
+
* @typedef {import('../../id').Identifier} Identifier
|
|
21
|
+
*
|
|
22
|
+
* @typedef {object} DDSpanLink
|
|
23
|
+
* @property {string} trace_id - Hex-encoded trace ID
|
|
24
|
+
* @property {string} span_id - Hex-encoded span ID
|
|
25
|
+
* @property {Record<string, string | number | boolean>} [attributes] - Link attributes
|
|
26
|
+
* @property {number} [flags] - Trace flags
|
|
27
|
+
* @property {string} [tracestate] - W3C trace state
|
|
28
|
+
*
|
|
29
|
+
* @typedef {object} DDSpanEvent
|
|
30
|
+
* @property {string} name - Event name
|
|
31
|
+
* @property {number} time_unix_nano - Event time in nanoseconds since epoch
|
|
32
|
+
* @property {Record<string, string | number | boolean>} [attributes] - Event attributes
|
|
33
|
+
*
|
|
16
34
|
* @typedef {object} DDFormattedSpan
|
|
17
|
-
* @property {
|
|
18
|
-
* @property {
|
|
19
|
-
* @property {
|
|
35
|
+
* @property {Identifier} trace_id - DD Identifier for trace ID
|
|
36
|
+
* @property {Identifier} span_id - DD Identifier for span ID
|
|
37
|
+
* @property {Identifier} parent_id - DD Identifier for parent span ID
|
|
20
38
|
* @property {string} name - Span operation name
|
|
21
39
|
* @property {string} resource - Resource name
|
|
22
40
|
* @property {string} [service] - Service name
|
|
@@ -24,9 +42,10 @@ const SPAN_KIND_CONSUMER = protoSpanKind.values.SPAN_KIND_CONSUMER
|
|
|
24
42
|
* @property {number} error - Error flag (0 or 1)
|
|
25
43
|
* @property {{[key: string]: string}} meta - String key-value tags
|
|
26
44
|
* @property {{[key: string]: number}} metrics - Numeric key-value tags
|
|
45
|
+
* @property {{[key: string]: object}} [meta_struct] - Structured tags (JSON-serialized, bytes in protobuf)
|
|
27
46
|
* @property {number} start - Start time in nanoseconds since epoch
|
|
28
47
|
* @property {number} duration - Duration in nanoseconds
|
|
29
|
-
* @property {
|
|
48
|
+
* @property {DDSpanEvent[]} [span_events] - Span events
|
|
30
49
|
*/
|
|
31
50
|
|
|
32
51
|
// Map DD span.kind string values to OTLP SpanKind numeric values
|
|
@@ -51,7 +70,7 @@ const EXCLUDED_META_KEYS = new Set([
|
|
|
51
70
|
/**
|
|
52
71
|
* OtlpTraceTransformer transforms DD-formatted spans to OTLP trace JSON format.
|
|
53
72
|
*
|
|
54
|
-
* This implementation follows the OTLP
|
|
73
|
+
* This implementation follows the OTLP trace data model:
|
|
55
74
|
* https://opentelemetry.io/docs/specs/otlp/#trace-data-model
|
|
56
75
|
*
|
|
57
76
|
* It receives DD-formatted spans (from span_format.js) and produces
|
|
@@ -119,7 +138,7 @@ class OtlpTraceTransformer extends OtlpTransformerBase {
|
|
|
119
138
|
return {
|
|
120
139
|
traceId: this.#idToBytes(span.trace_id, 16),
|
|
121
140
|
spanId: this.#idToBytes(span.span_id, 8),
|
|
122
|
-
parentSpanId: (parentId && !
|
|
141
|
+
parentSpanId: (parentId && !parentId.equals(ZERO_ID)) ? this.#idToBytes(parentId, 8) : undefined,
|
|
123
142
|
name: span.resource,
|
|
124
143
|
kind: this.#mapSpanKind(span.meta?.['span.kind']),
|
|
125
144
|
startTimeUnixNano: span.start,
|
|
@@ -178,7 +197,10 @@ class OtlpTraceTransformer extends OtlpTransformerBase {
|
|
|
178
197
|
}
|
|
179
198
|
}
|
|
180
199
|
|
|
181
|
-
//
|
|
200
|
+
// TODO: meta_struct values are logically raw bytes. The OTLP http/json spec encodes the bytesValue
|
|
201
|
+
// field as base64, but when http/protobuf or gRPC support is added the payload should be sent as
|
|
202
|
+
// raw bytes directly (no JSON.stringify + base64). The backend decoding side will need to be
|
|
203
|
+
// updated in parallel to accept the unencoded bytes.
|
|
182
204
|
if (span.meta_struct) {
|
|
183
205
|
for (const [key, value] of Object.entries(span.meta_struct)) {
|
|
184
206
|
const bytes = Buffer.from(JSON.stringify(value))
|
|
@@ -202,33 +224,40 @@ class OtlpTraceTransformer extends OtlpTransformerBase {
|
|
|
202
224
|
|
|
203
225
|
/**
|
|
204
226
|
* Maps DD span error state to an OTLP Status object.
|
|
227
|
+
* Combines error.type and error.message when both are present so error type
|
|
228
|
+
* information is preserved on the OTel side.
|
|
205
229
|
*
|
|
206
230
|
* @param {DDFormattedSpan} span - DD-formatted span
|
|
207
231
|
* @returns {object} OTLP Status object with code and message
|
|
208
232
|
*/
|
|
209
233
|
#mapStatus (span) {
|
|
210
|
-
if (span.error
|
|
211
|
-
return {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
234
|
+
if (span.error !== 1) {
|
|
235
|
+
return { code: STATUS_CODE_UNSET, message: '' }
|
|
236
|
+
}
|
|
237
|
+
const errorType = span.meta?.['error.type']
|
|
238
|
+
const errorMessage = span.meta?.['error.message']
|
|
239
|
+
let message = ''
|
|
240
|
+
if (errorType && errorMessage) {
|
|
241
|
+
message = `${errorType}: ${errorMessage}`
|
|
242
|
+
} else if (errorType) {
|
|
243
|
+
message = errorType
|
|
244
|
+
} else if (errorMessage) {
|
|
245
|
+
message = errorMessage
|
|
215
246
|
}
|
|
216
|
-
return { code:
|
|
247
|
+
return { code: STATUS_CODE_ERROR, message }
|
|
217
248
|
}
|
|
218
249
|
|
|
219
250
|
/**
|
|
220
251
|
* Transforms a DD span event to an OTLP Event object.
|
|
221
252
|
*
|
|
222
|
-
* @param {
|
|
253
|
+
* @param {DDSpanEvent} event - DD span event
|
|
223
254
|
* @returns {object} OTLP Event object
|
|
224
255
|
*/
|
|
225
256
|
#transformEvent (event) {
|
|
226
257
|
return {
|
|
227
258
|
timeUnixNano: event.time_unix_nano,
|
|
228
259
|
name: event.name || '',
|
|
229
|
-
attributes:
|
|
230
|
-
? this.transformAttributes(event.attributes)
|
|
231
|
-
: [],
|
|
260
|
+
attributes: this.transformAttributes(event.attributes ?? {}),
|
|
232
261
|
droppedAttributesCount: 0,
|
|
233
262
|
}
|
|
234
263
|
}
|
|
@@ -257,7 +286,7 @@ class OtlpTraceTransformer extends OtlpTransformerBase {
|
|
|
257
286
|
/**
|
|
258
287
|
* Transforms a single DD span link to an OTLP Link object.
|
|
259
288
|
*
|
|
260
|
-
* @param {
|
|
289
|
+
* @param {DDSpanLink} link - DD span link
|
|
261
290
|
* @returns {object} OTLP Link object
|
|
262
291
|
*/
|
|
263
292
|
#transformLink (link) {
|
|
@@ -265,9 +294,7 @@ class OtlpTraceTransformer extends OtlpTransformerBase {
|
|
|
265
294
|
traceId: this.#hexToBytes(link.trace_id, 16),
|
|
266
295
|
spanId: this.#hexToBytes(link.span_id, 8),
|
|
267
296
|
traceState: link.tracestate || '',
|
|
268
|
-
attributes:
|
|
269
|
-
? this.transformAttributes(link.attributes)
|
|
270
|
-
: [],
|
|
297
|
+
attributes: this.transformAttributes(link.attributes ?? {}),
|
|
271
298
|
droppedAttributesCount: 0,
|
|
272
299
|
flags: link.flags,
|
|
273
300
|
}
|
|
@@ -278,7 +305,7 @@ class OtlpTraceTransformer extends OtlpTransformerBase {
|
|
|
278
305
|
* Pads with leading zeros if the identifier buffer is shorter than the target.
|
|
279
306
|
* Per the OTLP http/json spec, trace-ids and span-ids must be hex-encoded strings.
|
|
280
307
|
*
|
|
281
|
-
* @param {
|
|
308
|
+
* @param {Identifier} identifier - DD Identifier
|
|
282
309
|
* @param {number} targetLength - Target byte length (16 for trace ID, 8 for span ID)
|
|
283
310
|
* @returns {string} Hex-encoded string of the specified length
|
|
284
311
|
*/
|
|
@@ -290,29 +317,12 @@ class OtlpTraceTransformer extends OtlpTransformerBase {
|
|
|
290
317
|
if (buffer.length > targetLength) {
|
|
291
318
|
return Buffer.from(buffer.slice(buffer.length - targetLength)).toString('hex')
|
|
292
319
|
}
|
|
293
|
-
// Pad with leading zeros to reach target length
|
|
320
|
+
// Pad with leading zeros to reach target length.
|
|
294
321
|
const result = Buffer.alloc(targetLength)
|
|
295
|
-
|
|
296
|
-
for (let i = 0; i < buffer.length; i++) {
|
|
297
|
-
result[offset + i] = buffer[i]
|
|
298
|
-
}
|
|
322
|
+
Buffer.from(buffer).copy(result, targetLength - buffer.length)
|
|
299
323
|
return result.toString('hex')
|
|
300
324
|
}
|
|
301
325
|
|
|
302
|
-
/**
|
|
303
|
-
* Checks if a DD Identifier represents a zero ID (all bytes are 0).
|
|
304
|
-
*
|
|
305
|
-
* @param {object} identifier - DD Identifier object with toBuffer() method
|
|
306
|
-
* @returns {boolean} True if the identifier is all zeros
|
|
307
|
-
*/
|
|
308
|
-
#isZeroId (identifier) {
|
|
309
|
-
const buffer = identifier.toBuffer()
|
|
310
|
-
for (let i = 0; i < buffer.length; i++) {
|
|
311
|
-
if (buffer[i] !== 0) return false
|
|
312
|
-
}
|
|
313
|
-
return true
|
|
314
|
-
}
|
|
315
|
-
|
|
316
326
|
/**
|
|
317
327
|
* Normalizes a hex string to the specified byte length.
|
|
318
328
|
* Pads with leading zeros if the hex string is shorter than expected.
|