dd-trace 5.109.0 → 5.110.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/ci/init.js +4 -3
- package/index.d.ts +66 -24
- package/loader-hook.mjs +88 -2
- package/package.json +18 -14
- package/packages/datadog-esbuild/index.js +26 -0
- package/packages/datadog-esbuild/src/utils.js +46 -3
- package/packages/datadog-instrumentations/src/connect.js +4 -3
- package/packages/datadog-instrumentations/src/fastify.js +4 -12
- package/packages/datadog-instrumentations/src/graphql.js +17 -6
- package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +81 -8
- package/packages/datadog-instrumentations/src/helpers/instrument.js +27 -2
- package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +65 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +6 -4
- package/packages/datadog-instrumentations/src/helpers/require-optional-peer.js +17 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +18 -2
- package/packages/datadog-instrumentations/src/koa.js +3 -2
- package/packages/datadog-instrumentations/src/mariadb.js +4 -2
- package/packages/datadog-instrumentations/src/mocha/main.js +3 -3
- package/packages/datadog-instrumentations/src/next.js +10 -10
- package/packages/datadog-instrumentations/src/otel-sdk-trace.js +7 -6
- package/packages/datadog-instrumentations/src/playwright.js +1 -1
- package/packages/datadog-instrumentations/src/restify.js +4 -3
- package/packages/datadog-instrumentations/src/router.js +18 -5
- package/packages/datadog-instrumentations/src/selenium.js +1 -2
- package/packages/datadog-instrumentations/src/tedious.js +28 -0
- package/packages/datadog-instrumentations/src/vitest.js +264 -7
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +2 -2
- package/packages/datadog-plugin-aws-sdk/src/base.js +13 -9
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +43 -6
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +83 -91
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +9 -1
- package/packages/datadog-plugin-http/src/client.js +8 -3
- package/packages/datadog-plugin-http2/src/client.js +5 -2
- package/packages/datadog-plugin-mysql/src/index.js +7 -2
- package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +2 -2
- package/packages/datadog-plugin-undici/src/index.js +5 -2
- package/packages/datadog-plugin-vitest/src/index.js +7 -1
- package/packages/datadog-webpack/index.js +17 -1
- package/packages/datadog-webpack/src/optional-peer-loader.js +17 -0
- package/packages/dd-trace/src/aiguard/sdk.js +13 -1
- package/packages/dd-trace/src/appsec/api_security/index.js +55 -0
- package/packages/dd-trace/src/appsec/api_security/sampler.js +147 -0
- package/packages/dd-trace/src/appsec/index.js +10 -6
- package/packages/dd-trace/src/appsec/reporter.js +1 -0
- package/packages/dd-trace/src/appsec/telemetry/api_security.js +34 -0
- package/packages/dd-trace/src/appsec/telemetry/index.js +26 -0
- package/packages/dd-trace/src/ci-visibility/requests/request.js +1 -17
- package/packages/dd-trace/src/config/defaults.js +22 -3
- package/packages/dd-trace/src/config/generated-config-types.d.ts +535 -1
- package/packages/dd-trace/src/config/helper.js +133 -33
- package/packages/dd-trace/src/config/index.js +4 -1
- package/packages/dd-trace/src/config/parsers.js +19 -1
- package/packages/dd-trace/src/config/supported-configurations.json +21 -3
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +47 -35
- package/packages/dd-trace/src/debugger/devtools_client/index.js +31 -66
- package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +83 -0
- package/packages/dd-trace/src/debugger/devtools_client/state.js +1 -0
- package/packages/dd-trace/src/debugger/index.js +5 -0
- package/packages/dd-trace/src/debugger/probe_sampler.js +112 -0
- package/packages/dd-trace/src/debugger/probe_sampler_constants.js +19 -0
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +77 -26
- package/packages/dd-trace/src/exporters/common/request.js +1 -20
- package/packages/dd-trace/src/exporters/common/url.js +34 -0
- package/packages/dd-trace/src/exporters/span-stats/writer.js +1 -3
- package/packages/dd-trace/src/guardrails/index.js +8 -5
- package/packages/dd-trace/src/index.js +6 -4
- package/packages/dd-trace/src/llmobs/constants/tags.js +6 -0
- package/packages/dd-trace/src/llmobs/index.js +16 -5
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +3 -3
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +5 -1
- package/packages/dd-trace/src/llmobs/sdk.js +5 -3
- package/packages/dd-trace/src/llmobs/span_processor.js +4 -0
- package/packages/dd-trace/src/llmobs/tagger.js +55 -0
- package/packages/dd-trace/src/llmobs/telemetry.js +1 -0
- package/packages/dd-trace/src/log/index.js +18 -20
- package/packages/dd-trace/src/openfeature/flagging_provider.js +2 -7
- package/packages/dd-trace/src/opentelemetry/bridge-span-base.js +5 -4
- package/packages/dd-trace/src/opentelemetry/metrics/instruments.js +2 -1
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +4 -3
- package/packages/dd-trace/src/opentelemetry/metrics/time.js +19 -0
- package/packages/dd-trace/src/opentelemetry/otlp/common.proto +2 -2
- package/packages/dd-trace/src/opentelemetry/otlp/logs.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/logs_service.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/metrics.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/metrics_service.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +11 -4
- package/packages/dd-trace/src/opentelemetry/span-helpers.js +29 -13
- package/packages/dd-trace/src/opentelemetry/span.js +14 -3
- package/packages/dd-trace/src/opentelemetry/span_context.js +1 -0
- package/packages/dd-trace/src/opentelemetry/trace/index.js +2 -1
- package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +3 -2
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +26 -13
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +3 -3
- package/packages/dd-trace/src/opentracing/span_context.js +17 -0
- package/packages/dd-trace/src/plugin_manager.js +7 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +9 -1
- package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +276 -0
- package/packages/dd-trace/src/plugins/util/test.js +80 -0
- package/packages/dd-trace/src/plugins/util/url.js +68 -1
- package/packages/dd-trace/src/plugins/util/web.js +19 -36
- package/packages/dd-trace/src/priority_sampler.js +1 -13
- package/packages/dd-trace/src/profiling/config.js +129 -198
- package/packages/dd-trace/src/profiling/constants.js +4 -7
- package/packages/dd-trace/src/profiling/exporter_cli.js +10 -10
- package/packages/dd-trace/src/profiling/exporters/agent.js +23 -16
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +45 -14
- package/packages/dd-trace/src/profiling/exporters/file.js +16 -5
- package/packages/dd-trace/src/profiling/oom.js +71 -0
- package/packages/dd-trace/src/profiling/profiler.js +35 -23
- package/packages/dd-trace/src/profiling/profilers/events.js +14 -7
- package/packages/dd-trace/src/profiling/profilers/space.js +27 -18
- package/packages/dd-trace/src/profiling/profilers/wall.js +35 -25
- package/packages/dd-trace/src/ritm.js +4 -2
- package/packages/dd-trace/src/serverless.js +1 -2
- package/packages/dd-trace/src/service-naming/schemas/util.js +36 -5
- package/packages/dd-trace/src/service-naming/schemas/v1/web.js +5 -3
- package/packages/dd-trace/src/span_format.js +2 -1
- package/packages/dd-trace/src/span_processor.js +6 -0
- package/packages/dd-trace/src/telemetry/send-data.js +12 -10
- package/packages/dd-trace/src/tracer.js +15 -1
- package/packages/dd-trace/src/util.js +14 -0
- package/register.js +58 -1
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +1 -1
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/pprof-format/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/packages/dd-trace/src/appsec/api_security_sampler.js +0 -117
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { channel } = require('dc-polyfill')
|
|
4
4
|
|
|
5
|
-
const { isError
|
|
5
|
+
const { isError } = require('../util')
|
|
6
6
|
const tracerVersion = require('../../../../package.json').version
|
|
7
7
|
const logger = require('../log')
|
|
8
8
|
const { getValueFromEnvSources } = require('../config/helper')
|
|
@@ -65,9 +65,11 @@ class LLMObs extends NoopLLMObs {
|
|
|
65
65
|
|
|
66
66
|
logger.debug('Enabling LLMObs')
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
// skipDefault: only an explicit DD_LLMOBS_ENABLED=false blocks enable(); an unset value
|
|
69
|
+
// (its default is false) must still allow this programmatic opt-in.
|
|
70
|
+
const DD_LLMOBS_ENABLED = getValueFromEnvSources('DD_LLMOBS_ENABLED', true)
|
|
69
71
|
|
|
70
|
-
if (DD_LLMOBS_ENABLED
|
|
72
|
+
if (DD_LLMOBS_ENABLED === false) {
|
|
71
73
|
logger.debug('LLMObs.enable() called when DD_LLMOBS_ENABLED is false. No action taken.')
|
|
72
74
|
return
|
|
73
75
|
}
|
|
@@ -33,6 +33,8 @@ const {
|
|
|
33
33
|
ROUTING_API_KEY,
|
|
34
34
|
ROUTING_SITE,
|
|
35
35
|
LLMOBS_SUBMITTED_TAG_KEY,
|
|
36
|
+
SAMPLE_RATE,
|
|
37
|
+
SAMPLING_DECISION,
|
|
36
38
|
} = require('./constants/tags')
|
|
37
39
|
const { UNSERIALIZABLE_VALUE_TEXT } = require('./constants/text')
|
|
38
40
|
const telemetry = require('./telemetry')
|
|
@@ -248,6 +250,8 @@ class LLMObsSpanProcessor {
|
|
|
248
250
|
_dd: {
|
|
249
251
|
span_id: span.context().toSpanId(),
|
|
250
252
|
trace_id: span.context().toTraceId(true),
|
|
253
|
+
sample_rate: mlObsTags[SAMPLE_RATE],
|
|
254
|
+
sampling_decision: mlObsTags[SAMPLING_DECISION],
|
|
251
255
|
},
|
|
252
256
|
}
|
|
253
257
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const log = require('../log')
|
|
4
|
+
const Sampler = require('../sampler')
|
|
5
|
+
const { formatKnuthRate } = require('../util')
|
|
4
6
|
const {
|
|
5
7
|
MODEL_NAME,
|
|
6
8
|
MODEL_PROVIDER,
|
|
@@ -41,6 +43,12 @@ const {
|
|
|
41
43
|
ROUTING_SITE,
|
|
42
44
|
PROMPT_TRACKING_INSTRUMENTATION_METHOD,
|
|
43
45
|
INSTRUMENTATION_METHOD_ANNOTATED,
|
|
46
|
+
SAMPLE_RATE,
|
|
47
|
+
SAMPLING_DECISION,
|
|
48
|
+
SAMPLING_DECISION_SAMPLED,
|
|
49
|
+
SAMPLING_DECISION_DROPPED,
|
|
50
|
+
PROPAGATED_SAMPLE_RATE_KEY,
|
|
51
|
+
PROPAGATED_SAMPLING_DECISION_KEY,
|
|
44
52
|
} = require('./constants/tags')
|
|
45
53
|
const { storage } = require('./storage')
|
|
46
54
|
const { findGenAIAncestorSpanId, validateCostTags, writeBridgeTags, validateToolDefinitions } = require('./util')
|
|
@@ -53,12 +61,31 @@ class LLMObsTagger {
|
|
|
53
61
|
/** @type {import('../config/config-base')} */
|
|
54
62
|
#config
|
|
55
63
|
|
|
64
|
+
/** @type {import('../sampler') | null} */
|
|
65
|
+
#sampler = null
|
|
66
|
+
|
|
56
67
|
constructor (config, softFail = false) {
|
|
57
68
|
this.#config = config
|
|
58
69
|
|
|
59
70
|
this.softFail = softFail
|
|
60
71
|
}
|
|
61
72
|
|
|
73
|
+
/**
|
|
74
|
+
* The sampler reads its rate from `config.llmobs.sampleRate`, which can change
|
|
75
|
+
* at runtime (e.g. via remote config). Rebuild the sampler whenever the rate
|
|
76
|
+
* changes so decisions reflect the current config, while reusing the existing
|
|
77
|
+
* sampler when it hasn't.
|
|
78
|
+
*
|
|
79
|
+
* @returns {import('../sampler')}
|
|
80
|
+
*/
|
|
81
|
+
#getSampler () {
|
|
82
|
+
const rate = this.#config.llmobs?.sampleRate ?? 1
|
|
83
|
+
if (this.#sampler === null || rate !== this.#sampler.rate()) {
|
|
84
|
+
this.#sampler = new Sampler(rate)
|
|
85
|
+
}
|
|
86
|
+
return this.#sampler
|
|
87
|
+
}
|
|
88
|
+
|
|
62
89
|
static get tagMap () {
|
|
63
90
|
return registry
|
|
64
91
|
}
|
|
@@ -124,6 +151,8 @@ class LLMObsTagger {
|
|
|
124
151
|
ROOT_PARENT_ID
|
|
125
152
|
this._setTag(span, PARENT_ID_KEY, parentId)
|
|
126
153
|
|
|
154
|
+
this.#tagSamplingDecision(span, parent)
|
|
155
|
+
|
|
127
156
|
// apply annotation context
|
|
128
157
|
const annotationContext = storage.getStore()?.annotationContext
|
|
129
158
|
|
|
@@ -153,6 +182,32 @@ class LLMObsTagger {
|
|
|
153
182
|
}
|
|
154
183
|
}
|
|
155
184
|
|
|
185
|
+
#tagSamplingDecision (span, parent) {
|
|
186
|
+
const traceTags = span.context()._trace.tags
|
|
187
|
+
const parentTags = registry.get(parent)
|
|
188
|
+
|
|
189
|
+
let sampleRate, samplingDecision
|
|
190
|
+
if (parentTags) {
|
|
191
|
+
// Local LLMObs parent: inherit its decision.
|
|
192
|
+
sampleRate = parentTags[SAMPLE_RATE]
|
|
193
|
+
samplingDecision = parentTags[SAMPLING_DECISION]
|
|
194
|
+
} else if (traceTags[PROPAGATED_PARENT_ID_KEY]) {
|
|
195
|
+
// Distributed LLMObs parent: inherit whatever was propagated. This may be
|
|
196
|
+
// absent if the upstream service predates sampling propagation, in which
|
|
197
|
+
// case we make no decision here rather than starting a divergent one.
|
|
198
|
+
sampleRate = traceTags[PROPAGATED_SAMPLE_RATE_KEY]
|
|
199
|
+
samplingDecision = traceTags[PROPAGATED_SAMPLING_DECISION_KEY]
|
|
200
|
+
} else {
|
|
201
|
+
// Root span: make the trace's one sampling decision.
|
|
202
|
+
const sampler = this.#getSampler()
|
|
203
|
+
sampleRate = formatKnuthRate(sampler.rate())
|
|
204
|
+
samplingDecision = sampler.isSampled(span) ? SAMPLING_DECISION_SAMPLED : SAMPLING_DECISION_DROPPED
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (sampleRate != null) this._setTag(span, SAMPLE_RATE, sampleRate)
|
|
208
|
+
if (samplingDecision != null) this._setTag(span, SAMPLING_DECISION, samplingDecision)
|
|
209
|
+
}
|
|
210
|
+
|
|
156
211
|
// TODO: similarly for the following `tag` methods,
|
|
157
212
|
// how can we transition from a span weakmap to core API functionality
|
|
158
213
|
tagLLMIO (span, inputData, outputData) {
|
|
@@ -87,6 +87,7 @@ function recordLLMObsEnabled (startTime, config, value = 1) {
|
|
|
87
87
|
site: config.site,
|
|
88
88
|
auto: Number(autoEnabled),
|
|
89
89
|
ml_app: config.llmobs.mlApp,
|
|
90
|
+
sample_rate: config.llmobs.sampleRate,
|
|
90
91
|
}
|
|
91
92
|
llmobsMetrics.count('product_enabled', tags).inc(value)
|
|
92
93
|
llmobsMetrics.distribution('init_time', tags).track(initTimeMs)
|
|
@@ -2,19 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
const { inspect } = require('util')
|
|
4
4
|
|
|
5
|
-
const { defaults } = require('../config/defaults')
|
|
6
|
-
const { isTrue } = require('../util')
|
|
7
5
|
const { getValueFromEnvSources } = require('../config/helper')
|
|
6
|
+
// Eager require restores the original startup module-load order: pulling `config/defaults` here
|
|
7
|
+
// also installs the instrumented `dns` it transitively loads before the tracer/agent connects.
|
|
8
|
+
// `config/defaults` defers its own `dns` require until after it exports, so this no longer hits
|
|
9
|
+
// the `config/defaults` <-> log parse cycle that motivated the lazy require below.
|
|
10
|
+
const { defaults } = require('../config/defaults')
|
|
8
11
|
const { traceChannel, debugChannel, infoChannel, warnChannel, errorChannel } = require('./channels')
|
|
9
12
|
const logWriter = require('./writer')
|
|
10
13
|
const { Log, LogConfig, NoTransmitError } = require('./log')
|
|
11
14
|
|
|
12
|
-
const config = {
|
|
13
|
-
enabled: defaults.DD_TRACE_DEBUG,
|
|
14
|
-
logger: undefined,
|
|
15
|
-
logLevel: defaults.logLevel,
|
|
16
|
-
}
|
|
17
|
-
|
|
18
15
|
// In most places where we know we want to mute a log we use log.error() directly
|
|
19
16
|
const NO_TRANSMIT = new LogConfig(false)
|
|
20
17
|
|
|
@@ -77,19 +74,17 @@ const log = {
|
|
|
77
74
|
},
|
|
78
75
|
|
|
79
76
|
configure (options) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
getValueFromEnvSources('DD_TRACE_LOG_LEVEL') ??
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
getValueFromEnvSources('DD_TRACE_DEBUG') ??
|
|
77
|
+
const logger = options.logger
|
|
78
|
+
const logLevel = options.logLevel ??
|
|
79
|
+
getValueFromEnvSources('DD_TRACE_LOG_LEVEL', true) ??
|
|
80
|
+
defaults?.logLevel
|
|
81
|
+
const enabled = getValueFromEnvSources('DD_TRACE_DEBUG', true) ??
|
|
86
82
|
// TODO: Handle this by adding a log buffer so that configure may be called with the actual configurations.
|
|
87
83
|
// eslint-disable-next-line eslint-rules/eslint-process-env
|
|
88
|
-
(process.env.OTEL_LOG_LEVEL === 'debug' ||
|
|
89
|
-
)
|
|
90
|
-
logWriter.configure(config.enabled, config.logLevel, options.logger)
|
|
84
|
+
(process.env.OTEL_LOG_LEVEL === 'debug' || defaults?.DD_TRACE_DEBUG)
|
|
85
|
+
logWriter.configure(enabled, logLevel, logger)
|
|
91
86
|
|
|
92
|
-
return
|
|
87
|
+
return enabled
|
|
93
88
|
},
|
|
94
89
|
}
|
|
95
90
|
|
|
@@ -113,6 +108,9 @@ function getErrorLog (err) {
|
|
|
113
108
|
return err
|
|
114
109
|
}
|
|
115
110
|
|
|
116
|
-
|
|
117
|
-
|
|
111
|
+
// Assign before the bootstrap configure() call: an invalid DD_TRACE_LOG_LEVEL
|
|
112
|
+
// makes config/defaults re-require this module to warn, which must observe the
|
|
113
|
+
// fully built log object rather than a half-initialized one.
|
|
118
114
|
module.exports = log
|
|
115
|
+
|
|
116
|
+
log.configure({})
|
|
@@ -1,18 +1,13 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { channel } = require('dc-polyfill')
|
|
4
|
+
const requireOptionalPeer = require('../../../datadog-instrumentations/src/helpers/require-optional-peer')
|
|
4
5
|
const log = require('../log')
|
|
5
6
|
const { EXPOSURE_CHANNEL } = require('./constants/constants')
|
|
6
7
|
const EvalMetricsHook = require('./eval-metrics-hook')
|
|
7
8
|
const SpanEnrichmentHook = require('./span-enrichment-hook')
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
// `@datadog/openfeature-node-server` -> `@openfeature/server-sdk` ->
|
|
11
|
-
// `@openfeature/core`. Same shape as `helpers/rewriter/compiler.js`.
|
|
12
|
-
// Refs: https://github.com/DataDog/dd-trace-js/issues/8635
|
|
13
|
-
// eslint-disable-next-line camelcase, no-undef
|
|
14
|
-
const runtimeRequire = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require
|
|
15
|
-
const { DatadogNodeServerProvider } = runtimeRequire(['@datadog/openfeature', 'node', 'server'].join('-'))
|
|
10
|
+
const { DatadogNodeServerProvider } = requireOptionalPeer('@datadog/openfeature-node-server')
|
|
16
11
|
|
|
17
12
|
/**
|
|
18
13
|
* OpenFeature provider that integrates with Datadog's feature flagging system.
|
|
@@ -30,6 +30,7 @@ class BridgeSpanBase {
|
|
|
30
30
|
*/
|
|
31
31
|
constructor (ddSpan) {
|
|
32
32
|
this._ddSpan = ddSpan
|
|
33
|
+
this._otelTraceSemanticsEnabled = false
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
get ended () {
|
|
@@ -45,7 +46,7 @@ class BridgeSpanBase {
|
|
|
45
46
|
* @param {import('@opentelemetry/api').AttributeValue} value
|
|
46
47
|
*/
|
|
47
48
|
setAttribute (key, value) {
|
|
48
|
-
setOtelAttribute(this._ddSpan, key, value)
|
|
49
|
+
setOtelAttribute(this._ddSpan, key, value, this._otelTraceSemanticsEnabled)
|
|
49
50
|
return this
|
|
50
51
|
}
|
|
51
52
|
|
|
@@ -53,7 +54,7 @@ class BridgeSpanBase {
|
|
|
53
54
|
* @param {import('@opentelemetry/api').Attributes} attributes
|
|
54
55
|
*/
|
|
55
56
|
setAttributes (attributes) {
|
|
56
|
-
setOtelAttributes(this._ddSpan, attributes)
|
|
57
|
+
setOtelAttributes(this._ddSpan, attributes, this._otelTraceSemanticsEnabled)
|
|
57
58
|
return this
|
|
58
59
|
}
|
|
59
60
|
|
|
@@ -91,14 +92,14 @@ class BridgeSpanBase {
|
|
|
91
92
|
* @param {import('@opentelemetry/api').TimeInput} [timeInput]
|
|
92
93
|
*/
|
|
93
94
|
recordException (exception, timeInput) {
|
|
94
|
-
recordException(this._ddSpan, exception, timeInput)
|
|
95
|
+
recordException(this._ddSpan, exception, timeInput, this._otelTraceSemanticsEnabled)
|
|
95
96
|
}
|
|
96
97
|
|
|
97
98
|
/**
|
|
98
99
|
* @param {import('@opentelemetry/api').SpanStatus} status
|
|
99
100
|
*/
|
|
100
101
|
setStatus (status) {
|
|
101
|
-
this.#statusCode = applyOtelStatus(this._ddSpan, this.#statusCode, status)
|
|
102
|
+
this.#statusCode = applyOtelStatus(this._ddSpan, this.#statusCode, status, this._otelTraceSemanticsEnabled)
|
|
102
103
|
return this
|
|
103
104
|
}
|
|
104
105
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { sanitizeAttributes } = require('../../../../../vendor/dist/@opentelemetry/core')
|
|
4
4
|
const { METRIC_TYPES } = require('./constants')
|
|
5
|
+
const { nowUnixNano } = require('./time')
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* @typedef {import('@opentelemetry/api').Attributes} Attributes
|
|
@@ -62,7 +63,7 @@ class Instrument {
|
|
|
62
63
|
type,
|
|
63
64
|
value,
|
|
64
65
|
attributes: sanitizeAttributes(attributes),
|
|
65
|
-
timestamp:
|
|
66
|
+
timestamp: nowUnixNano(),
|
|
66
67
|
}
|
|
67
68
|
}
|
|
68
69
|
}
|
|
@@ -6,6 +6,7 @@ const {
|
|
|
6
6
|
METRIC_TYPES, TEMPORALITY, DEFAULT_HISTOGRAM_BUCKETS, DEFAULT_MAX_MEASUREMENT_QUEUE_SIZE,
|
|
7
7
|
} = require('./constants')
|
|
8
8
|
const { ObservableInstrument } = require('./instruments')
|
|
9
|
+
const { nowUnixNano } = require('./time')
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* @typedef {import('@opentelemetry/api').Attributes} Attributes
|
|
@@ -309,7 +310,7 @@ class PeriodicMetricReader {
|
|
|
309
310
|
*
|
|
310
311
|
*/
|
|
311
312
|
class MetricAggregator {
|
|
312
|
-
#startTime =
|
|
313
|
+
#startTime = nowUnixNano()
|
|
313
314
|
#temporalityPreference
|
|
314
315
|
#maxBatchedQueueSize
|
|
315
316
|
|
|
@@ -444,8 +445,8 @@ class MetricAggregator {
|
|
|
444
445
|
*/
|
|
445
446
|
#isDeltaType (type) {
|
|
446
447
|
return type === METRIC_TYPES.COUNTER ||
|
|
447
|
-
|
|
448
|
-
|
|
448
|
+
type === METRIC_TYPES.OBSERVABLECOUNTER ||
|
|
449
|
+
type === METRIC_TYPES.HISTOGRAM
|
|
449
450
|
}
|
|
450
451
|
|
|
451
452
|
/**
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { timeOrigin } = performance
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Current wall-clock time as UNIX epoch nanoseconds, the unit OTLP requires for
|
|
7
|
+
* `timeUnixNano` / `startTimeUnixNano`. `process.hrtime.bigint()` is a monotonic
|
|
8
|
+
* clock with an arbitrary origin, so its values decode as 1970 timestamps that
|
|
9
|
+
* the Datadog Agent silently drops. Anchoring on `performance.timeOrigin` puts
|
|
10
|
+
* the value on the epoch while `performance.now()` keeps it monotonic, so a
|
|
11
|
+
* backward wall-clock jump can't make `timeUnixNano` precede `startTimeUnixNano`.
|
|
12
|
+
*
|
|
13
|
+
* @returns {number} Nanoseconds since the UNIX epoch
|
|
14
|
+
*/
|
|
15
|
+
function nowUnixNano () {
|
|
16
|
+
return Math.round((timeOrigin + performance.now()) * 1e6)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = { nowUnixNano }
|
|
@@ -68,7 +68,7 @@ message KeyValue {
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
// InstrumentationScope is a message representing the instrumentation scope information
|
|
71
|
-
// such as the fully qualified name and version.
|
|
71
|
+
// such as the fully qualified name and version.
|
|
72
72
|
message InstrumentationScope {
|
|
73
73
|
// An empty instrumentation scope name means the name is unknown.
|
|
74
74
|
string name = 1;
|
|
@@ -113,4 +113,4 @@ message EntityRef {
|
|
|
113
113
|
// These attribute keys are not part of entity's identity.
|
|
114
114
|
// These keys MUST exist in the containing {message}.attributes.
|
|
115
115
|
repeated string description_keys = 4;
|
|
116
|
-
}
|
|
116
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const http = require('http')
|
|
4
|
-
const
|
|
3
|
+
const http = require('node:http')
|
|
4
|
+
const https = require('node:https')
|
|
5
|
+
const { URL } = require('node:url')
|
|
5
6
|
const log = require('../../log')
|
|
6
7
|
const telemetryMetrics = require('../../telemetry/metrics')
|
|
7
8
|
|
|
@@ -16,6 +17,8 @@ const tracerMetrics = telemetryMetrics.manager.namespace('tracers')
|
|
|
16
17
|
* @class OtlpHttpExporterBase
|
|
17
18
|
*/
|
|
18
19
|
class OtlpHttpExporterBase {
|
|
20
|
+
#transport = https
|
|
21
|
+
|
|
19
22
|
/**
|
|
20
23
|
* Creates a new OtlpHttpExporterBase instance.
|
|
21
24
|
*
|
|
@@ -45,7 +48,7 @@ class OtlpHttpExporterBase {
|
|
|
45
48
|
this.setUrl(url)
|
|
46
49
|
|
|
47
50
|
this.telemetryTags = [
|
|
48
|
-
'
|
|
51
|
+
`protocol:${this.#transport === https ? 'https' : 'http'}`,
|
|
49
52
|
`encoding:${isJson ? 'json' : 'protobuf'}`,
|
|
50
53
|
]
|
|
51
54
|
}
|
|
@@ -81,7 +84,7 @@ class OtlpHttpExporterBase {
|
|
|
81
84
|
},
|
|
82
85
|
}
|
|
83
86
|
|
|
84
|
-
const req =
|
|
87
|
+
const req = this.#transport.request(options, (res) => {
|
|
85
88
|
let data = ''
|
|
86
89
|
|
|
87
90
|
res.on('data', (chunk) => {
|
|
@@ -124,6 +127,10 @@ class OtlpHttpExporterBase {
|
|
|
124
127
|
this.options.hostname = parsedUrl.hostname
|
|
125
128
|
this.options.port = parsedUrl.port
|
|
126
129
|
this.options.path = parsedUrl.pathname + parsedUrl.search
|
|
130
|
+
this.#transport = parsedUrl.protocol === 'http:' ? http : https
|
|
131
|
+
if (this.telemetryTags !== undefined) {
|
|
132
|
+
this.telemetryTags[0] = `protocol:${this.#transport === https ? 'https' : 'http'}`
|
|
133
|
+
}
|
|
127
134
|
}
|
|
128
135
|
|
|
129
136
|
/**
|
|
@@ -137,14 +137,17 @@ function normalizeLinkContext (context) {
|
|
|
137
137
|
* Mirrors `http.response.status_code` onto `http.status_code` (DD's special tag used by APM
|
|
138
138
|
* trace metrics and client-side stats); both names end up on the span.
|
|
139
139
|
*
|
|
140
|
+
* When OTel trace semantics are enabled, the behavior is disabled.
|
|
141
|
+
*
|
|
140
142
|
* @param {import('../opentracing/span')} ddSpan
|
|
141
143
|
* @param {string} key
|
|
142
144
|
* @param {unknown} value
|
|
145
|
+
* @param {boolean} [otelTraceSemanticsEnabled]
|
|
143
146
|
*/
|
|
144
|
-
function setOtelAttribute (ddSpan, key, value) {
|
|
147
|
+
function setOtelAttribute (ddSpan, key, value, otelTraceSemanticsEnabled) {
|
|
145
148
|
if (!isWritable(ddSpan)) return
|
|
146
149
|
|
|
147
|
-
if (key === 'http.response.status_code') {
|
|
150
|
+
if (!otelTraceSemanticsEnabled && key === 'http.response.status_code') {
|
|
148
151
|
ddSpan.setTag('http.status_code', String(value))
|
|
149
152
|
}
|
|
150
153
|
|
|
@@ -155,14 +158,17 @@ function setOtelAttribute (ddSpan, key, value) {
|
|
|
155
158
|
* Same `http.status_code` mirror as `setOtelAttribute`; does not mutate the caller's
|
|
156
159
|
* `attributes` object.
|
|
157
160
|
*
|
|
161
|
+
* When OTel trace semantics are enabled, the behavior is disabled.
|
|
162
|
+
*
|
|
158
163
|
* @param {import('../opentracing/span')} ddSpan
|
|
159
164
|
* @param {Record<string, unknown>} attributes
|
|
165
|
+
* @param {boolean} [otelTraceSemanticsEnabled]
|
|
160
166
|
*/
|
|
161
|
-
function setOtelAttributes (ddSpan, attributes) {
|
|
167
|
+
function setOtelAttributes (ddSpan, attributes, otelTraceSemanticsEnabled) {
|
|
162
168
|
if (!isWritable(ddSpan)) return
|
|
163
169
|
|
|
164
170
|
ddSpan.addTags(attributes)
|
|
165
|
-
if ('http.response.status_code' in attributes) {
|
|
171
|
+
if (!otelTraceSemanticsEnabled && 'http.response.status_code' in attributes) {
|
|
166
172
|
ddSpan.setTag('http.status_code', String(attributes['http.response.status_code']))
|
|
167
173
|
}
|
|
168
174
|
}
|
|
@@ -223,16 +229,19 @@ function addOtelEvent (ddSpan, name, attributesOrStartTime, startTime) {
|
|
|
223
229
|
* @param {import('../opentracing/span')} ddSpan
|
|
224
230
|
* @param {ExceptionLike} exception
|
|
225
231
|
* @param {TimeInput} [timeInput]
|
|
232
|
+
* @param {boolean} [otelTraceSemanticsEnabled]
|
|
226
233
|
*/
|
|
227
|
-
function recordException (ddSpan, exception, timeInput) {
|
|
234
|
+
function recordException (ddSpan, exception, timeInput, otelTraceSemanticsEnabled) {
|
|
228
235
|
if (!isWritable(ddSpan)) return
|
|
229
236
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
237
|
+
if (!otelTraceSemanticsEnabled) {
|
|
238
|
+
ddSpan.addTags({
|
|
239
|
+
[ERROR_TYPE]: exception.name,
|
|
240
|
+
[ERROR_MESSAGE]: exception.message,
|
|
241
|
+
[ERROR_STACK]: exception.stack,
|
|
242
|
+
[IGNORE_OTEL_ERROR]: ddSpan.context().getTag(IGNORE_OTEL_ERROR) ?? true,
|
|
243
|
+
})
|
|
244
|
+
}
|
|
236
245
|
|
|
237
246
|
const attributes = {}
|
|
238
247
|
if (exception.message) attributes['exception.message'] = exception.message
|
|
@@ -251,13 +260,20 @@ function recordException (ddSpan, exception, timeInput) {
|
|
|
251
260
|
* @param {import('../opentracing/span')} ddSpan
|
|
252
261
|
* @param {number} currentCode 0 = UNSET, 1 = OK, 2 = ERROR.
|
|
253
262
|
* @param {{ code?: number, message?: string }} [status]
|
|
263
|
+
* @param {boolean} [otelTraceSemanticsEnabled]
|
|
254
264
|
* @returns {number} The new status code to track on the caller.
|
|
255
265
|
*/
|
|
256
|
-
function applyOtelStatus (ddSpan, currentCode, status) {
|
|
266
|
+
function applyOtelStatus (ddSpan, currentCode, status, otelTraceSemanticsEnabled) {
|
|
257
267
|
if (!isWritable(ddSpan)) return currentCode
|
|
258
268
|
|
|
259
269
|
const code = status?.code
|
|
260
|
-
if (!code || currentCode === 1)
|
|
270
|
+
if (!code || currentCode === 1) {
|
|
271
|
+
if (otelTraceSemanticsEnabled) {
|
|
272
|
+
ddSpan.context().deleteTag(ERROR_MESSAGE)
|
|
273
|
+
ddSpan.context().deleteTag(IGNORE_OTEL_ERROR)
|
|
274
|
+
}
|
|
275
|
+
return currentCode
|
|
276
|
+
}
|
|
261
277
|
|
|
262
278
|
if (code === 2) {
|
|
263
279
|
ddSpan.addTags({
|
|
@@ -15,7 +15,7 @@ const kinds = require('../../../../ext/kinds')
|
|
|
15
15
|
const id = require('../id')
|
|
16
16
|
const BridgeSpanBase = require('./bridge-span-base')
|
|
17
17
|
const SpanContext = require('./span_context')
|
|
18
|
-
const { setOtelOperationName } = require('./span-helpers')
|
|
18
|
+
const { setOtelOperationName, setOtelResource } = require('./span-helpers')
|
|
19
19
|
|
|
20
20
|
const spanKindNames = {
|
|
21
21
|
[api.SpanKind.INTERNAL]: kinds.INTERNAL,
|
|
@@ -118,6 +118,8 @@ function spanNameMapper (spanName, kind, attributes) {
|
|
|
118
118
|
* surface; the underlying DD span carries the lifecycle.
|
|
119
119
|
*/
|
|
120
120
|
class Span extends BridgeSpanBase {
|
|
121
|
+
#otelName
|
|
122
|
+
|
|
121
123
|
/**
|
|
122
124
|
* @param {import('./tracer')} parentTracer
|
|
123
125
|
* @param {import('@opentelemetry/api').Context} context
|
|
@@ -159,12 +161,15 @@ class Span extends BridgeSpanBase {
|
|
|
159
161
|
|
|
160
162
|
super(ddSpan)
|
|
161
163
|
|
|
164
|
+
this._otelTraceSemanticsEnabled = _tracer._config.DD_TRACE_OTEL_SEMANTICS_ENABLED
|
|
165
|
+
|
|
162
166
|
if (attributes) {
|
|
163
167
|
this.setAttributes(attributes)
|
|
164
168
|
}
|
|
165
169
|
|
|
166
170
|
this._parentTracer = parentTracer
|
|
167
171
|
this._context = context
|
|
172
|
+
this.#otelName = spanName || this._ddSpan.context()._name
|
|
168
173
|
|
|
169
174
|
// NOTE: Need to grab the value before setting it on the span because the
|
|
170
175
|
// math for computing opentracing timestamps is apparently lossy...
|
|
@@ -192,7 +197,7 @@ class Span extends BridgeSpanBase {
|
|
|
192
197
|
}
|
|
193
198
|
|
|
194
199
|
get name () {
|
|
195
|
-
return this
|
|
200
|
+
return this.#otelName
|
|
196
201
|
}
|
|
197
202
|
|
|
198
203
|
spanContext () {
|
|
@@ -223,7 +228,13 @@ class Span extends BridgeSpanBase {
|
|
|
223
228
|
* @param {string} name
|
|
224
229
|
*/
|
|
225
230
|
updateName (name) {
|
|
226
|
-
|
|
231
|
+
if (this.ended) return this
|
|
232
|
+
this.#otelName = name
|
|
233
|
+
if (this._otelTraceSemanticsEnabled) {
|
|
234
|
+
setOtelResource(this._ddSpan, name)
|
|
235
|
+
} else {
|
|
236
|
+
setOtelOperationName(this._ddSpan, name)
|
|
237
|
+
}
|
|
227
238
|
return this
|
|
228
239
|
}
|
|
229
240
|
|
|
@@ -59,7 +59,8 @@ function createOtlpTraceExporter (config) {
|
|
|
59
59
|
config.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
|
|
60
60
|
config.OTEL_EXPORTER_OTLP_TRACES_HEADERS,
|
|
61
61
|
config.OTEL_EXPORTER_OTLP_TRACES_TIMEOUT,
|
|
62
|
-
buildResourceAttributes(config)
|
|
62
|
+
buildResourceAttributes(config),
|
|
63
|
+
config.DD_TRACE_OTEL_SEMANTICS_ENABLED
|
|
63
64
|
)
|
|
64
65
|
}
|
|
65
66
|
|
|
@@ -36,10 +36,11 @@ class OtlpHttpTraceExporter extends OtlpHttpExporterBase {
|
|
|
36
36
|
* corresponding `OTEL_EXPORTER_OTLP_*_HEADERS` env by the MAP parser.
|
|
37
37
|
* @param {number} timeout - Request timeout in milliseconds
|
|
38
38
|
* @param {import('@opentelemetry/api').Attributes} resourceAttributes - Resource attributes
|
|
39
|
+
* @param {boolean} otelTraceSemanticsEnabled - When true, do not emit Datadog-only attributes as span attributes
|
|
39
40
|
*/
|
|
40
|
-
constructor (url, headers, timeout, resourceAttributes) {
|
|
41
|
+
constructor (url, headers, timeout, resourceAttributes, otelTraceSemanticsEnabled) {
|
|
41
42
|
super(url, headers, timeout, 'http/json', 'traces')
|
|
42
|
-
this.#transformer = new OtlpTraceTransformer(resourceAttributes)
|
|
43
|
+
this.#transformer = new OtlpTraceTransformer(resourceAttributes, otelTraceSemanticsEnabled)
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
/**
|