dd-trace 5.132.0 → 5.133.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/diagnose.js +9 -14
- package/ci/init.js +1 -2
- package/ci/test-optimization-validation/ci-discovery.js +9 -4
- package/ci/test-optimization-validation/executable.js +1 -5
- package/ci/test-optimization-validation/framework-adapters/cypress.js +1 -4
- package/ci/test-optimization-validation/framework-adapters/vitest.js +1 -1
- package/ci/test-optimization-validation/generated-files.js +6 -4
- package/ci/test-optimization-validation/generated-test-contract.js +7 -3
- package/ci/test-optimization-validation/manifest-scaffold.js +7 -2
- package/ci/test-optimization-validation/manifest-schema.js +6 -7
- package/ci/test-optimization-validation/report-writer.js +6 -2
- package/ci/test-optimization-validation/runner-command.js +2 -4
- package/ci/test-optimization-validation/runner-contract.js +1 -2
- package/ci/test-optimization-validation/scenarios/auto-test-retries.js +1 -15
- package/ci/vitest-no-worker-init-setup.mjs +10 -7
- package/index.d.ts +25 -14
- package/initialize.mjs +1 -3
- package/openfeature/rules-based.d.ts +11 -0
- package/openfeature/rules-based.js +7 -0
- package/package.json +12 -10
- package/packages/datadog-core/src/storage.js +16 -0
- package/packages/datadog-esbuild/index.js +18 -12
- package/packages/datadog-instrumentations/src/aerospike.js +3 -3
- package/packages/datadog-instrumentations/src/ai.js +3 -3
- package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +0 -6
- package/packages/datadog-instrumentations/src/aws-sdk.js +21 -13
- package/packages/datadog-instrumentations/src/cassandra-driver.js +25 -20
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +9 -23
- package/packages/datadog-instrumentations/src/cucumber.js +48 -33
- package/packages/datadog-instrumentations/src/elasticsearch.js +1 -5
- package/packages/datadog-instrumentations/src/electron.js +6 -6
- package/packages/datadog-instrumentations/src/fastify.js +13 -4
- package/packages/datadog-instrumentations/src/fs.js +2 -2
- package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +9 -5
- package/packages/datadog-instrumentations/src/grpc/client.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/bundler-register.js +2 -2
- package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +1 -2
- package/packages/datadog-instrumentations/src/helpers/instrument.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/register.js +36 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentation-registry.js +87 -6
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +5 -6
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/postgres.js +48 -31
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +7 -7
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms/postgres.js +42 -4
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +7 -10
- package/packages/datadog-instrumentations/src/ioredis.js +7 -5
- package/packages/datadog-instrumentations/src/iovalkey.js +7 -5
- package/packages/datadog-instrumentations/src/jest.js +3 -10
- package/packages/datadog-instrumentations/src/knex.js +9 -5
- package/packages/datadog-instrumentations/src/mariadb-bundle.js +3 -3
- package/packages/datadog-instrumentations/src/mocha/common.js +20 -8
- package/packages/datadog-instrumentations/src/mocha/main.js +140 -43
- package/packages/datadog-instrumentations/src/mocha/utils.js +3 -2
- package/packages/datadog-instrumentations/src/mocha/worker.js +1 -2
- package/packages/datadog-instrumentations/src/mongodb-core.js +3 -3
- package/packages/datadog-instrumentations/src/mongoose.js +3 -3
- package/packages/datadog-instrumentations/src/nova-sonic/audio.js +173 -0
- package/packages/datadog-instrumentations/src/nova-sonic/index.js +209 -0
- package/packages/datadog-instrumentations/src/nova-sonic/session.js +483 -0
- package/packages/datadog-instrumentations/src/openai-realtime/index.js +18 -6
- package/packages/datadog-instrumentations/src/openai-realtime/session.js +1 -1
- package/packages/datadog-instrumentations/src/openai.js +7 -0
- package/packages/datadog-instrumentations/src/pino.js +5 -6
- package/packages/datadog-instrumentations/src/redis.js +27 -13
- package/packages/datadog-instrumentations/src/router.js +30 -0
- package/packages/datadog-instrumentations/src/rum-browser-scripts.js +2 -6
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +7 -5
- package/packages/datadog-instrumentations/src/vitest-main.js +9 -15
- package/packages/datadog-instrumentations/src/vitest-util.js +2 -2
- package/packages/datadog-instrumentations/src/vitest-worker.js +1 -2
- package/packages/datadog-instrumentations/src/vitest.js +2 -2
- package/packages/datadog-instrumentations/src/webdriverio.js +5 -8
- package/packages/datadog-instrumentations/src/ws.js +1 -4
- package/packages/datadog-plugin-amqplib/src/client.js +1 -2
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +1 -5
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +1 -2
- package/packages/datadog-plugin-aws-sdk/src/base.js +5 -8
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/index.js +18 -0
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/sonic.js +45 -0
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +9 -3
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +2 -5
- package/packages/datadog-plugin-azure-durable-functions/src/index.js +5 -4
- package/packages/datadog-plugin-azure-functions/src/index.js +5 -3
- package/packages/datadog-plugin-claude-agent-sdk/src/index.js +0 -2
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +1 -5
- package/packages/datadog-plugin-cypress/src/index.js +6 -6
- package/packages/datadog-plugin-cypress/src/source-map-utils.js +1 -2
- package/packages/datadog-plugin-cypress/src/support.js +1 -4
- package/packages/datadog-plugin-express/src/code_origin.js +1 -2
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -2
- package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +1 -2
- package/packages/datadog-plugin-graphql/src/execute.js +5 -5
- package/packages/datadog-plugin-grpc/src/util.js +1 -2
- package/packages/datadog-plugin-kafkajs/src/producer.js +1 -2
- package/packages/datadog-plugin-mocha/src/index.js +7 -10
- package/packages/datadog-plugin-mocha/src/webdriverio-video.js +8 -4
- package/packages/datadog-plugin-mongodb-core/src/query.js +8 -5
- package/packages/datadog-plugin-next/src/index.js +1 -2
- package/packages/datadog-plugin-openai/src/tracing.js +5 -2
- package/packages/datadog-plugin-openai-agents/src/integration.js +6 -2
- package/packages/datadog-plugin-openai-agents/src/processor.js +2 -4
- package/packages/datadog-plugin-postgres/src/index.js +24 -0
- package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -5
- package/packages/datadog-plugin-rhea/src/consumer.js +1 -4
- package/packages/datadog-plugin-vitest/src/index.js +5 -2
- package/packages/datadog-shimmer/src/shimmer.js +5 -4
- package/packages/datadog-webpack/index.js +55 -43
- package/packages/datadog-webpack/src/loader.js +56 -8
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -4
- package/packages/dd-trace/src/aiguard/messages/utils.js +1 -2
- package/packages/dd-trace/src/aiguard/redaction.js +8 -2
- package/packages/dd-trace/src/appsec/api_security/normalized-route.js +3 -2
- package/packages/dd-trace/src/appsec/api_security/sampler.js +2 -6
- package/packages/dd-trace/src/appsec/downstream_requests.js +1 -9
- package/packages/dd-trace/src/appsec/handlers/http-request.js +3 -9
- package/packages/dd-trace/src/appsec/handlers/http-response.js +7 -2
- package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +4 -3
- package/packages/dd-trace/src/appsec/iast/analyzers/set-cookies-header-interceptor.js +1 -4
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +7 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +1 -3
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-esm.mjs +1 -3
- package/packages/dd-trace/src/appsec/iast/telemetry/iast-metric.js +1 -6
- package/packages/dd-trace/src/appsec/lambda.js +1 -3
- package/packages/dd-trace/src/appsec/rasp/fs-plugin.js +37 -27
- package/packages/dd-trace/src/appsec/rasp/lfi.js +9 -5
- package/packages/dd-trace/src/appsec/reporter.js +21 -1
- package/packages/dd-trace/src/appsec/stack_trace.js +1 -2
- package/packages/dd-trace/src/ci-visibility/dynamic-atr-retries.js +3 -6
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +13 -7
- package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +1 -2
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +2 -8
- package/packages/dd-trace/src/ci-visibility/resolve-runfile-path.js +54 -0
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +5 -8
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +2 -53
- package/packages/dd-trace/src/config/config-types.d.ts +2 -2
- package/packages/dd-trace/src/config/defaults.js +5 -5
- package/packages/dd-trace/src/config/generated-config-types.d.ts +8 -0
- package/packages/dd-trace/src/config/helper.js +34 -3
- package/packages/dd-trace/src/config/index.js +10 -9
- package/packages/dd-trace/src/config/normalize-service.js +1 -1
- package/packages/dd-trace/src/config/parsers.js +2 -2
- package/packages/dd-trace/src/config/supported-configurations.json +35 -0
- package/packages/dd-trace/src/datastreams/processor.js +3 -2
- package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +1 -4
- package/packages/dd-trace/src/debugger/devtools_client/send.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -0
- package/packages/dd-trace/src/debugger/guardrail-metrics.js +2 -2
- package/packages/dd-trace/src/dogstatsd.js +1 -1
- package/packages/dd-trace/src/encode/tags-processors.js +2 -2
- package/packages/dd-trace/src/exporters/common/request.js +1 -5
- package/packages/dd-trace/src/id.js +14 -1
- package/packages/dd-trace/src/lambda/handler.js +179 -19
- package/packages/dd-trace/src/lambda/index.js +15 -1
- package/packages/dd-trace/src/lambda/runtime/patch.js +34 -7
- package/packages/dd-trace/src/llmobs/audio-utils.js +3 -2
- package/packages/dd-trace/src/llmobs/constants/tags.js +12 -0
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +15 -0
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +1 -2
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chat_model.js +1 -3
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/nova-sonic.js +92 -0
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +56 -2
- package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +38 -0
- package/packages/dd-trace/src/llmobs/prompts/manager.js +5 -7
- package/packages/dd-trace/src/llmobs/prompts/prompt.js +1 -1
- package/packages/dd-trace/src/llmobs/span_processor.js +1 -2
- package/packages/dd-trace/src/llmobs/tagger.js +2 -2
- package/packages/dd-trace/src/llmobs/util.js +1 -2
- package/packages/dd-trace/src/llmobs/writers/base.js +4 -5
- package/packages/dd-trace/src/log/log.js +1 -5
- package/packages/dd-trace/src/openfeature/evaluation-telemetry-opt-out-hook.js +51 -0
- package/packages/dd-trace/src/openfeature/flagging_provider.js +48 -21
- package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -2
- package/packages/dd-trace/src/opentelemetry/otlp/common.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +6 -23
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +53 -13
- package/packages/dd-trace/src/otel-sampling.js +4 -5
- package/packages/dd-trace/src/otel-thread-ctx.js +79 -13
- package/packages/dd-trace/src/plugins/database.js +7 -16
- package/packages/dd-trace/src/plugins/util/git.js +9 -25
- package/packages/dd-trace/src/plugins/util/inferred_proxy.js +1 -5
- package/packages/dd-trace/src/plugins/util/test.js +48 -4
- package/packages/dd-trace/src/priority_sampler.js +1 -2
- package/packages/dd-trace/src/profiling/profiler.js +4 -6
- package/packages/dd-trace/src/proxy.js +1 -2
- package/packages/dd-trace/src/ritm.js +4 -2
- package/packages/dd-trace/src/serverless.js +5 -0
- package/packages/dd-trace/src/span_processor.js +13 -17
- package/packages/dd-trace/src/telemetry/endpoints.js +1 -3
- package/packages/dd-trace/src/util.js +29 -0
- package/packages/dd-trace/src/web-tags-cache.js +6 -2
- package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
- package/vendor/dist/@datadog/openfeature-node-server/rules-based/LICENSE +201 -0
- package/vendor/dist/@datadog/openfeature-node-server/rules-based/index.d.ts +925 -0
- package/vendor/dist/@datadog/openfeature-node-server/rules-based/index.js +1 -0
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/vendor/dist/shell-quote/index.js +1 -1
|
@@ -8,7 +8,7 @@ const {
|
|
|
8
8
|
} = require('../../constants/tags')
|
|
9
9
|
const { audioMimeTypeFromFormat, formatAudioPart } = require('../../audio-utils')
|
|
10
10
|
const { safeJsonParse } = require('../../util')
|
|
11
|
-
const { AUDIO_MIME_TYPES } = require('./constants')
|
|
11
|
+
const { AUDIO_MIME_TYPES, IMAGE_FALLBACK, INPUT_TYPE_IMAGE } = require('./constants')
|
|
12
12
|
const {
|
|
13
13
|
extractChatTemplateFromInstructions,
|
|
14
14
|
normalizePromptVariables,
|
|
@@ -16,6 +16,7 @@ const {
|
|
|
16
16
|
extractContentParts,
|
|
17
17
|
hasMultimodalInputs,
|
|
18
18
|
getModelProviderAndClient,
|
|
19
|
+
getServerToolUsageMetrics,
|
|
19
20
|
} = require('./utils')
|
|
20
21
|
|
|
21
22
|
const allowedParamKeys = new Set([
|
|
@@ -107,6 +108,8 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
|
|
|
107
108
|
this._tagEmbedding(span, inputs, response, error)
|
|
108
109
|
} else if (operation === 'response') {
|
|
109
110
|
this.#tagResponse(span, inputs, response, error)
|
|
111
|
+
} else if (operation === 'decision') {
|
|
112
|
+
this.#tagDecision(span, inputs, response, error)
|
|
110
113
|
}
|
|
111
114
|
|
|
112
115
|
if (!error) {
|
|
@@ -148,12 +151,20 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
|
|
|
148
151
|
// Responses API - always include
|
|
149
152
|
const cacheReadTokens = tokenUsage.input_tokens_details.cached_tokens
|
|
150
153
|
if (cacheReadTokens !== undefined) metrics.cacheReadTokens = cacheReadTokens
|
|
154
|
+
const cacheWriteTokens = tokenUsage.input_tokens_details.cache_write_tokens
|
|
155
|
+
if (cacheWriteTokens != null) metrics.cacheWriteTokens = cacheWriteTokens
|
|
156
|
+
// Cache writes are billed at a different rate per TTL
|
|
157
|
+
const cacheWriteDetails = tokenUsage.input_tokens_details.cache_write_tokens_details
|
|
158
|
+
if (cacheWriteDetails?.ttl_30m_tokens != null) metrics.cacheWrite30mTokens = cacheWriteDetails.ttl_30m_tokens
|
|
159
|
+
if (cacheWriteDetails?.ttl_12h_tokens != null) metrics.cacheWrite12hTokens = cacheWriteDetails.ttl_12h_tokens
|
|
151
160
|
} else if (tokenUsage.prompt_tokens_details) {
|
|
152
161
|
// Chat/Completions API - only include if > 0
|
|
153
162
|
const cacheReadTokens = tokenUsage.prompt_tokens_details.cached_tokens
|
|
154
163
|
if (cacheReadTokens != null) {
|
|
155
164
|
metrics.cacheReadTokens = cacheReadTokens
|
|
156
165
|
}
|
|
166
|
+
const cacheWriteTokens = tokenUsage.prompt_tokens_details.cache_write_tokens
|
|
167
|
+
if (cacheWriteTokens != null) metrics.cacheWriteTokens = cacheWriteTokens
|
|
157
168
|
}
|
|
158
169
|
// Reasoning tokens - Responses API returns `output_tokens_details`, `completion_tokens_details`
|
|
159
170
|
const reasoningOutputObject = tokenUsage.output_tokens_details ?? tokenUsage.completion_tokens_details
|
|
@@ -161,6 +172,8 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
|
|
|
161
172
|
if (reasoningOutputTokens !== undefined) metrics.reasoningOutputTokens = reasoningOutputTokens
|
|
162
173
|
}
|
|
163
174
|
|
|
175
|
+
Object.assign(metrics, getServerToolUsageMetrics(response))
|
|
176
|
+
|
|
164
177
|
return metrics
|
|
165
178
|
}
|
|
166
179
|
|
|
@@ -464,6 +477,30 @@ class OpenAiLLMObsPlugin extends LLMObsPlugin {
|
|
|
464
477
|
|
|
465
478
|
this._tagger.tagMetadata(span, outputMetadata) // update the metadata with the output metadata
|
|
466
479
|
}
|
|
480
|
+
|
|
481
|
+
#tagDecision (span, inputs, response, error) {
|
|
482
|
+
const { input, questions } = inputs
|
|
483
|
+
|
|
484
|
+
const inputMessages = typeof input === 'string'
|
|
485
|
+
? [{ role: 'user', content: input }]
|
|
486
|
+
: (Array.isArray(input) ? input : []).map(message => ({
|
|
487
|
+
role: message.role ?? 'user',
|
|
488
|
+
content: decisionMessageContent(message.content),
|
|
489
|
+
}))
|
|
490
|
+
|
|
491
|
+
if (Array.isArray(questions)) {
|
|
492
|
+
this._tagger.tagMetadata(span, { questions })
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
if (error) {
|
|
496
|
+
this._tagger.tagLLMIO(span, inputMessages, [{ content: '' }])
|
|
497
|
+
return
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
this._tagger.tagLLMIO(span, inputMessages, [
|
|
501
|
+
{ role: 'assistant', content: JSON.stringify(response.answers ?? []) },
|
|
502
|
+
])
|
|
503
|
+
}
|
|
467
504
|
}
|
|
468
505
|
|
|
469
506
|
// TODO: this will be moved to the APM integration
|
|
@@ -485,13 +522,28 @@ function normalizeOpenAIResourceName (resource) {
|
|
|
485
522
|
case 'responses.create':
|
|
486
523
|
return 'createResponse'
|
|
487
524
|
|
|
525
|
+
// decisions
|
|
526
|
+
case 'decisions.create':
|
|
527
|
+
return 'createDecision'
|
|
528
|
+
|
|
488
529
|
default:
|
|
489
530
|
return resource
|
|
490
531
|
}
|
|
491
532
|
}
|
|
492
533
|
|
|
534
|
+
// Decision images must be inline data URLs, so they are replaced with a marker instead of copied onto the span
|
|
535
|
+
function decisionMessageContent (content) {
|
|
536
|
+
if (!Array.isArray(content)) return content ?? ''
|
|
537
|
+
|
|
538
|
+
return content
|
|
539
|
+
.map(part => (part?.type === INPUT_TYPE_IMAGE ? IMAGE_FALLBACK : part?.text ?? ''))
|
|
540
|
+
.filter(Boolean)
|
|
541
|
+
.join('\n')
|
|
542
|
+
}
|
|
543
|
+
|
|
493
544
|
function gateResource (resource) {
|
|
494
|
-
return ['createCompletion', 'createChatCompletion', 'createEmbedding', 'createResponse']
|
|
545
|
+
return ['createCompletion', 'createChatCompletion', 'createEmbedding', 'createResponse', 'createDecision']
|
|
546
|
+
.includes(resource)
|
|
495
547
|
? resource
|
|
496
548
|
: undefined
|
|
497
549
|
}
|
|
@@ -506,6 +558,8 @@ function getOperation (resource) {
|
|
|
506
558
|
return 'embedding'
|
|
507
559
|
case 'createResponse':
|
|
508
560
|
return 'response'
|
|
561
|
+
case 'createDecision':
|
|
562
|
+
return 'decision'
|
|
509
563
|
default:
|
|
510
564
|
// should never happen
|
|
511
565
|
return 'unknown'
|
|
@@ -198,6 +198,43 @@ function getModelProviderAndClient (baseUrl = '') {
|
|
|
198
198
|
return { modelProvider, client: 'OpenAI' }
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
+
/**
|
|
202
|
+
* Count the billable server-side tool calls in an OpenAI Responses response.
|
|
203
|
+
*
|
|
204
|
+
* Web searches are read from the undocumented `tool_usage.web_search.num_requests` when present, otherwise
|
|
205
|
+
* counted from completed `web_search_call` items whose action is `search` (`open_page` / `find_in_page` steps are
|
|
206
|
+
* not billed). File searches have no usage counter, so each completed `file_search_call` item counts once.
|
|
207
|
+
* Zero counts are omitted.
|
|
208
|
+
*
|
|
209
|
+
* @param {Record<string, unknown>} [response] - Any OpenAI response; only Responses API fields are read
|
|
210
|
+
* @returns {{ webSearchCount?: number, storageSearchCount?: number }}
|
|
211
|
+
*/
|
|
212
|
+
function getServerToolUsageMetrics (response) {
|
|
213
|
+
const metrics = {}
|
|
214
|
+
const reportedWebSearches = response?.tool_usage?.web_search?.num_requests
|
|
215
|
+
const hasReportedWebSearches = typeof reportedWebSearches === 'number'
|
|
216
|
+
if (hasReportedWebSearches && reportedWebSearches > 0) metrics.webSearchCount = reportedWebSearches
|
|
217
|
+
|
|
218
|
+
const output = response?.output
|
|
219
|
+
if (!Array.isArray(output)) return metrics
|
|
220
|
+
|
|
221
|
+
for (const item of output) {
|
|
222
|
+
if (item?.status !== 'completed') continue
|
|
223
|
+
|
|
224
|
+
if (item.type === 'web_search_call') {
|
|
225
|
+
if (hasReportedWebSearches) continue
|
|
226
|
+
// Items without an action (older payloads) are assumed to be searches.
|
|
227
|
+
const actionType = item.action?.type
|
|
228
|
+
if (actionType !== undefined && actionType !== 'search') continue
|
|
229
|
+
metrics.webSearchCount = (metrics.webSearchCount ?? 0) + 1
|
|
230
|
+
} else if (item.type === 'file_search_call') {
|
|
231
|
+
metrics.storageSearchCount = (metrics.storageSearchCount ?? 0) + 1
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return metrics
|
|
236
|
+
}
|
|
237
|
+
|
|
201
238
|
module.exports = {
|
|
202
239
|
extractChatTemplateFromInstructions,
|
|
203
240
|
normalizePromptVariables,
|
|
@@ -206,4 +243,5 @@ module.exports = {
|
|
|
206
243
|
hasMultimodalInputs,
|
|
207
244
|
getModelProviderAndClient,
|
|
208
245
|
getOpenAIModelProvider,
|
|
246
|
+
getServerToolUsageMetrics,
|
|
209
247
|
}
|
|
@@ -84,13 +84,11 @@ function promptFromData (data, source) {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
|
-
* Copy a prompt with a different source.
|
|
88
87
|
* @param {ManagedPrompt} prompt
|
|
89
|
-
* @param {'registry'|'cache'|'fallback'|'ff'|'resolve'} source
|
|
90
88
|
*/
|
|
91
|
-
function
|
|
92
|
-
if (
|
|
93
|
-
return new ManagedPrompt({ ...prompt, source })
|
|
89
|
+
function withCacheSource (prompt) {
|
|
90
|
+
if (SOURCE_CACHE === prompt.source) return prompt
|
|
91
|
+
return new ManagedPrompt({ ...prompt, source: /** @type {ManagedPrompt['source']} */ (SOURCE_CACHE) })
|
|
94
92
|
}
|
|
95
93
|
|
|
96
94
|
function detailFromBody (body) {
|
|
@@ -310,7 +308,7 @@ class PromptManager {
|
|
|
310
308
|
const cacheable = generationAtStart === this.cacheGeneration && latest
|
|
311
309
|
if (result.prompt) {
|
|
312
310
|
if (cacheable) {
|
|
313
|
-
const cached =
|
|
311
|
+
const cached = withCacheSource(result.prompt)
|
|
314
312
|
if (hot) this.hotCache.set(request.key, cached)
|
|
315
313
|
if (!request.resolve) this.warmCache.set(request.key, cached)
|
|
316
314
|
}
|
|
@@ -335,7 +333,7 @@ class PromptManager {
|
|
|
335
333
|
async #backgroundFetch (request, signal) {
|
|
336
334
|
const result = await this.#fetchAndCache(request, { hot: false, signal })
|
|
337
335
|
if (!result.cacheable) return
|
|
338
|
-
if (result.prompt) return
|
|
336
|
+
if (result.prompt) return withCacheSource(result.prompt)
|
|
339
337
|
if (result.notFound) return
|
|
340
338
|
throw new Error(result.reason)
|
|
341
339
|
}
|
|
@@ -93,7 +93,7 @@ class ManagedPrompt {
|
|
|
93
93
|
format (variables = {}) {
|
|
94
94
|
if (typeof this.template === 'string') return render(this.template, variables)
|
|
95
95
|
return this.template.flatMap(item => {
|
|
96
|
-
if (!isPlaceholder(item)) return
|
|
96
|
+
if (!isPlaceholder(item)) return { ...item, content: render(item.content, variables) }
|
|
97
97
|
if (!Object.hasOwn(variables, item.name)) {
|
|
98
98
|
throw new TypeError(`Missing message placeholder variable '${item.name}'`)
|
|
99
99
|
}
|
|
@@ -92,9 +92,8 @@ class LLMObsSpanProcessor {
|
|
|
92
92
|
|
|
93
93
|
// TODO: instead of relying on the tagger's weakmap registry, can we use some namespaced storage correlation?
|
|
94
94
|
process (span) {
|
|
95
|
-
if (!this.#config.llmobs.DD_LLMOBS_ENABLED) return
|
|
96
95
|
// if the span is not in our private tagger map, it is not an llmobs span
|
|
97
|
-
if (!LLMObsTagger.tagMap.has(span)) return
|
|
96
|
+
if (!this.#config.llmobs.DD_LLMOBS_ENABLED || !LLMObsTagger.tagMap.has(span)) return
|
|
98
97
|
|
|
99
98
|
try {
|
|
100
99
|
this.#setGenAiApmTags(span)
|
|
@@ -118,8 +118,8 @@ class LLMObsTagger {
|
|
|
118
118
|
integration,
|
|
119
119
|
_decorator,
|
|
120
120
|
} = {}) {
|
|
121
|
-
if
|
|
122
|
-
if (!kind) return
|
|
121
|
+
// Do not register it in the map if it does not have an LLMObs span kind.
|
|
122
|
+
if (!this.#config.llmobs.DD_LLMOBS_ENABLED || !kind) return
|
|
123
123
|
|
|
124
124
|
const spanMlApp =
|
|
125
125
|
mlApp ||
|
|
@@ -260,8 +260,7 @@ function findArgumentsBounds (str) {
|
|
|
260
260
|
|
|
261
261
|
const memo = new WeakMap()
|
|
262
262
|
function getFunctionArguments (fn, args = []) {
|
|
263
|
-
if (!fn) return
|
|
264
|
-
if (!args.length) return
|
|
263
|
+
if (!fn || !args.length) return
|
|
265
264
|
if (args.length === 1) return args[0]
|
|
266
265
|
|
|
267
266
|
try {
|
|
@@ -23,13 +23,12 @@ class LLMObsBuffer {
|
|
|
23
23
|
/**
|
|
24
24
|
* @param {{
|
|
25
25
|
* events: Record<string, unknown>[],
|
|
26
|
-
* size: number,
|
|
27
26
|
* routing?: { apiKey?: string, site?: string }
|
|
28
27
|
* }} options
|
|
29
28
|
*/
|
|
30
|
-
constructor ({ events,
|
|
29
|
+
constructor ({ events, routing = {} }) {
|
|
31
30
|
this.events = events
|
|
32
|
-
this.size =
|
|
31
|
+
this.size = 0
|
|
33
32
|
this.routing = routing
|
|
34
33
|
}
|
|
35
34
|
|
|
@@ -53,7 +52,7 @@ class BaseLLMObsWriter {
|
|
|
53
52
|
this._eventType = eventType
|
|
54
53
|
|
|
55
54
|
/** @type {LLMObsBuffer} */
|
|
56
|
-
this._buffer = new LLMObsBuffer({ events: []
|
|
55
|
+
this._buffer = new LLMObsBuffer({ events: [] })
|
|
57
56
|
|
|
58
57
|
/** @type {import('../../config/config-base')} */
|
|
59
58
|
this._config = config
|
|
@@ -92,7 +91,7 @@ class BaseLLMObsWriter {
|
|
|
92
91
|
const apiKey = routing.apiKey
|
|
93
92
|
let buffer = this.#multiTenantBuffers.get(apiKey)
|
|
94
93
|
if (!buffer) {
|
|
95
|
-
buffer = new LLMObsBuffer({ events: [],
|
|
94
|
+
buffer = new LLMObsBuffer({ events: [], routing })
|
|
96
95
|
this.#multiTenantBuffers.set(apiKey, buffer)
|
|
97
96
|
}
|
|
98
97
|
return buffer
|
|
@@ -17,11 +17,7 @@ class Log {
|
|
|
17
17
|
get formatted () {
|
|
18
18
|
const { message, args } = this
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
if (message && args && args.length) {
|
|
22
|
-
formatted = format(message, ...args)
|
|
23
|
-
}
|
|
24
|
-
return formatted
|
|
20
|
+
return message && args && args.length ? format(message, ...args) : message
|
|
25
21
|
}
|
|
26
22
|
|
|
27
23
|
static parse (...args) {
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const PARAMETER = 'dd_evaluation_telemetry_opt_out'
|
|
4
|
+
|
|
5
|
+
/** Gates an owned finally-only telemetry hook without changing evaluation or exposure handling. */
|
|
6
|
+
class EvaluationTelemetryOptOutHook {
|
|
7
|
+
#delegate
|
|
8
|
+
#decisions = new WeakMap()
|
|
9
|
+
|
|
10
|
+
/** @param {import('@openfeature/server-sdk').Hook} delegate */
|
|
11
|
+
constructor (delegate) {
|
|
12
|
+
this.#delegate = delegate
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** @param {import('@openfeature/core').HookContext} hookContext */
|
|
16
|
+
before (hookContext) {
|
|
17
|
+
// Hook context identity is stable across stages, including each MultiProvider branch.
|
|
18
|
+
this.#decisions.set(hookContext, isOptedOut(hookContext.context))
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** @param {import('@openfeature/core').HookContext} hookContext */
|
|
22
|
+
error (hookContext) {
|
|
23
|
+
// A customer before hook can fail before this hook runs. Capture opt-out before customer error hooks change it.
|
|
24
|
+
if (!this.#decisions.has(hookContext)) {
|
|
25
|
+
this.#decisions.set(hookContext, isOptedOut(hookContext.context))
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @param {import('@openfeature/core').HookContext} hookContext
|
|
31
|
+
* @param {import('@openfeature/core').EvaluationDetails<import('@openfeature/core').FlagValue>} evaluationDetails
|
|
32
|
+
* @param {import('@openfeature/core').HookHints} [hints]
|
|
33
|
+
*/
|
|
34
|
+
finally (hookContext, evaluationDetails, hints) {
|
|
35
|
+
const optedOut = this.#decisions.get(hookContext) === true || isOptedOut(hookContext.context)
|
|
36
|
+
this.#decisions.delete(hookContext)
|
|
37
|
+
if (!optedOut) return this.#delegate.finally?.(hookContext, evaluationDetails, hints)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** @param {import('@openfeature/core').EvaluationContext} context */
|
|
42
|
+
function isOptedOut (context) {
|
|
43
|
+
try {
|
|
44
|
+
return context?.[PARAMETER] === true
|
|
45
|
+
} catch {
|
|
46
|
+
// An unreadable attribute is not boolean true and must not change the evaluation result.
|
|
47
|
+
return false
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = EvaluationTelemetryOptOutHook
|
|
@@ -9,6 +9,7 @@ const configurationSource = require('./configuration_source')
|
|
|
9
9
|
const { EXPOSURE_CHANNEL } = require('./constants/constants')
|
|
10
10
|
const DebugLoggingHook = require('./debug-logging-hook')
|
|
11
11
|
const EvalMetricsHook = require('./eval-metrics-hook')
|
|
12
|
+
const EvaluationTelemetryOptOutHook = require('./evaluation-telemetry-opt-out-hook')
|
|
12
13
|
const SpanEnrichmentHook = require('./span-enrichment-hook')
|
|
13
14
|
const FlagEvalEVPHook = require('./writers/flag-eval-evp-hook')
|
|
14
15
|
|
|
@@ -17,6 +18,9 @@ const FlagEvalEVPHook = require('./writers/flag-eval-evp-hook')
|
|
|
17
18
|
* Extends DatadogNodeServerProvider to add tracer integration and configuration management.
|
|
18
19
|
*/
|
|
19
20
|
class FlaggingProvider extends DatadogNodeServerProvider {
|
|
21
|
+
// Track this wrapper's logging lifecycle without reading the upstream provider's private state.
|
|
22
|
+
#waitingForConfiguration = false
|
|
23
|
+
|
|
20
24
|
/** @type {SpanEnrichmentHook | undefined} */
|
|
21
25
|
#spanEnrichmentHook
|
|
22
26
|
|
|
@@ -36,8 +40,13 @@ class FlaggingProvider extends DatadogNodeServerProvider {
|
|
|
36
40
|
initializationTimeoutMs: config.featureFlags.DD_EXPERIMENTAL_FLAGGING_PROVIDER_INITIALIZATION_TIMEOUT_MS,
|
|
37
41
|
})
|
|
38
42
|
|
|
43
|
+
const enableOptOut =
|
|
44
|
+
config.featureFlags.DD_FEATURE_FLAGS_EVALUATION_TELEMETRY_PER_SUBJECT_OPT_OUT_ENABLED === true
|
|
45
|
+
/** @param {import('@openfeature/server-sdk').Hook} hook */
|
|
46
|
+
const telemetryHook = hook => enableOptOut ? new EvaluationTelemetryOptOutHook(hook) : hook
|
|
47
|
+
|
|
39
48
|
if (config.DD_METRICS_OTEL_ENABLED === true) {
|
|
40
|
-
this.hooks.push(new EvalMetricsHook(config))
|
|
49
|
+
this.hooks.push(telemetryHook(new EvalMetricsHook(config)))
|
|
41
50
|
} else {
|
|
42
51
|
log.debug('Feature Flags: evaluation metrics disabled; set %s=true to enable', 'DD_METRICS_OTEL_ENABLED')
|
|
43
52
|
}
|
|
@@ -49,7 +58,7 @@ class FlaggingProvider extends DatadogNodeServerProvider {
|
|
|
49
58
|
if (config.featureFlags.DD_EXPERIMENTAL_FLAGGING_PROVIDER_SPAN_ENRICHMENT_ENABLED) {
|
|
50
59
|
this.#spanEnrichmentHook = new SpanEnrichmentHook(tracer)
|
|
51
60
|
// @ts-expect-error The upstream constructor always initializes its optional hooks property.
|
|
52
|
-
this.hooks.push(this.#spanEnrichmentHook)
|
|
61
|
+
this.hooks.push(telemetryHook(this.#spanEnrichmentHook))
|
|
53
62
|
log.info('%s span enrichment enabled', this.constructor.name)
|
|
54
63
|
} else {
|
|
55
64
|
log.info('%s span enrichment disabled', this.constructor.name)
|
|
@@ -60,7 +69,7 @@ class FlaggingProvider extends DatadogNodeServerProvider {
|
|
|
60
69
|
|
|
61
70
|
if (config.featureFlags?.DD_FLAGGING_EVALUATION_COUNTS_ENABLED !== false) {
|
|
62
71
|
this.#flagEvalEVPHook = new FlagEvalEVPHook(config)
|
|
63
|
-
this.hooks.push(this.#flagEvalEVPHook)
|
|
72
|
+
this.hooks.push(telemetryHook(this.#flagEvalEVPHook))
|
|
64
73
|
}
|
|
65
74
|
|
|
66
75
|
this.#configurationSource = configurationSource.create(config, this.setConfiguration.bind(this))
|
|
@@ -74,6 +83,7 @@ class FlaggingProvider extends DatadogNodeServerProvider {
|
|
|
74
83
|
initialize (context) {
|
|
75
84
|
log.debug('Feature Flags: waiting for provider initialization...')
|
|
76
85
|
|
|
86
|
+
this.#waitingForConfiguration = !this.getConfiguration()
|
|
77
87
|
const promise = super.initialize(context)
|
|
78
88
|
|
|
79
89
|
// `DatadogNodeServerProvider#initialize` starts a timer that is never unref'd, which would
|
|
@@ -82,28 +92,45 @@ class FlaggingProvider extends DatadogNodeServerProvider {
|
|
|
82
92
|
// TODO: remove once `@datadog/openfeature-node-server` unrefs this timer itself.
|
|
83
93
|
this.initController?.timeoutId?.unref?.()
|
|
84
94
|
|
|
85
|
-
|
|
86
|
-
// Guarded so a logger failure can never leave the derived promise unhandled.
|
|
87
|
-
promise.then(
|
|
88
|
-
() => {
|
|
89
|
-
try {
|
|
90
|
-
log.debug('Feature Flags: provider initialized successfully')
|
|
91
|
-
} catch { /* logging failure must not crash the process */ }
|
|
92
|
-
},
|
|
93
|
-
(error) => {
|
|
94
|
-
try {
|
|
95
|
-
// errorWithoutTelemetry avoids inflating telemetry volume for every routine init timeout.
|
|
96
|
-
log.errorWithoutTelemetry(
|
|
97
|
-
'Feature Flags: provider failed to initialize: %s',
|
|
98
|
-
error instanceof Error ? error.message : String(error)
|
|
99
|
-
)
|
|
100
|
-
} catch { /* logging failure must not crash the process */ }
|
|
101
|
-
}
|
|
102
|
-
)
|
|
95
|
+
if (this.getConfiguration()) this.#logInitialConfiguration()
|
|
103
96
|
|
|
104
97
|
return promise
|
|
105
98
|
}
|
|
106
99
|
|
|
100
|
+
/**
|
|
101
|
+
* @param {Parameters<DatadogNodeServerProvider['setConfiguration']>[0]} configuration
|
|
102
|
+
*/
|
|
103
|
+
setConfiguration (configuration) {
|
|
104
|
+
const wasWaiting = this.#waitingForConfiguration
|
|
105
|
+
super.setConfiguration(configuration)
|
|
106
|
+
this.#waitingForConfiguration = false
|
|
107
|
+
if (wasWaiting && configuration != null) this.#logInitialConfiguration()
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* @param {unknown} error
|
|
112
|
+
*/
|
|
113
|
+
setError (error) {
|
|
114
|
+
const wasWaiting = this.#waitingForConfiguration
|
|
115
|
+
super.setError(error)
|
|
116
|
+
this.#waitingForConfiguration = false
|
|
117
|
+
if (!wasWaiting) return
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
// Avoid inflating telemetry volume for routine initialization timeouts.
|
|
121
|
+
log.errorWithoutTelemetry(
|
|
122
|
+
'Feature Flags: provider failed to initialize: %s',
|
|
123
|
+
error instanceof Error ? error.message : String(error)
|
|
124
|
+
)
|
|
125
|
+
} catch { /* logging failure must not affect initialization */ }
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
#logInitialConfiguration () {
|
|
129
|
+
try {
|
|
130
|
+
log.debug('Feature Flags: initial configuration available')
|
|
131
|
+
} catch { /* logging failure must not affect initialization */ }
|
|
132
|
+
}
|
|
133
|
+
|
|
107
134
|
/**
|
|
108
135
|
* Called when the provider is shut down.
|
|
109
136
|
* Cleans up resources including channel subscriptions.
|
|
@@ -52,7 +52,7 @@ function encodeHrTime (hrTime) {
|
|
|
52
52
|
* OtlpTransformer transforms log records to OTLP format.
|
|
53
53
|
*
|
|
54
54
|
* This implementation follows the OTLP Logs v1.7.0 Data Model specification:
|
|
55
|
-
* https://opentelemetry.
|
|
55
|
+
* https://github.com/open-telemetry/opentelemetry-proto/blob/v1.7.0/opentelemetry/proto/logs/v1/logs.proto
|
|
56
56
|
*
|
|
57
57
|
* @class OtlpTransformer
|
|
58
58
|
* @augments OtlpTransformerBase
|
|
@@ -18,7 +18,7 @@ const AGGREGATION_TEMPORALITY_CUMULATIVE = protoAggregationTemporality.values.AG
|
|
|
18
18
|
* OtlpTransformer transforms metrics to OTLP format.
|
|
19
19
|
*
|
|
20
20
|
* This implementation follows the OTLP Metrics v1.7.0 Data Model specification:
|
|
21
|
-
* https://opentelemetry.
|
|
21
|
+
* https://github.com/open-telemetry/opentelemetry-proto/blob/v1.7.0/opentelemetry/proto/metrics/v1/metrics.proto
|
|
22
22
|
*
|
|
23
23
|
* @class OtlpTransformer
|
|
24
24
|
* @augments OtlpTransformerBase
|
|
@@ -162,8 +162,7 @@ class PeriodicMetricReader {
|
|
|
162
162
|
addBatchObservableCallback (callback, observables) {
|
|
163
163
|
if (typeof callback !== 'function') return
|
|
164
164
|
const instruments = new Set(observables?.filter(isObservableInstrument))
|
|
165
|
-
if (instruments.size === 0) return
|
|
166
|
-
if (this.#findBatchCallback(callback, instruments) !== -1) return
|
|
165
|
+
if (instruments.size === 0 || this.#findBatchCallback(callback, instruments) !== -1) return
|
|
167
166
|
this.#batchCallbacks.push({ callback, instruments })
|
|
168
167
|
}
|
|
169
168
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Vendored from: https://github.com/open-telemetry/opentelemetry-proto/blob/v1.7.0/opentelemetry/proto/
|
|
1
|
+
// Vendored from: https://github.com/open-telemetry/opentelemetry-proto/blob/v1.7.0/opentelemetry/proto/common/v1/common.proto
|
|
2
2
|
// Copyright 2019, OpenTelemetry Authors
|
|
3
3
|
//
|
|
4
4
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -84,7 +84,7 @@ const DD_ERROR_META_KEYS = new Set(['error.message'])
|
|
|
84
84
|
* OtlpTraceTransformer transforms DD-formatted spans to OTLP trace JSON format.
|
|
85
85
|
*
|
|
86
86
|
* This implementation follows the OTLP trace data model:
|
|
87
|
-
* https://opentelemetry.
|
|
87
|
+
* https://github.com/open-telemetry/opentelemetry-proto/blob/v1.7.0/opentelemetry/proto/trace/v1/trace.proto
|
|
88
88
|
*
|
|
89
89
|
* It receives DD-formatted spans (from span_format.js) and produces
|
|
90
90
|
* an ExportTraceServiceRequest serialized as JSON (http/json protocol only).
|
|
@@ -168,8 +168,8 @@ class OtlpTraceTransformer extends OtlpTransformerBase {
|
|
|
168
168
|
|
|
169
169
|
return {
|
|
170
170
|
traceId: span.trace_id.toTraceIdHex(traceIdHigh).padStart(32, '0'),
|
|
171
|
-
spanId: this.#
|
|
172
|
-
parentSpanId:
|
|
171
|
+
spanId: this.#spanIdToHex(span.span_id),
|
|
172
|
+
parentSpanId: parentId && !parentId.equals(ZERO_ID) ? this.#spanIdToHex(parentId) : undefined,
|
|
173
173
|
traceState: span.trace_state,
|
|
174
174
|
name: span.resource,
|
|
175
175
|
kind: this.#mapSpanKind(span.meta?.['span.kind']),
|
|
@@ -338,26 +338,9 @@ class OtlpTraceTransformer extends OtlpTransformerBase {
|
|
|
338
338
|
}
|
|
339
339
|
}
|
|
340
340
|
|
|
341
|
-
/**
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
* Per the OTLP http/json spec, trace-ids and span-ids must be hex-encoded strings.
|
|
345
|
-
*
|
|
346
|
-
* @param {Identifier} identifier - DD Identifier
|
|
347
|
-
* @param {number} targetLength - Target byte length (16 for trace ID, 8 for span ID)
|
|
348
|
-
*/
|
|
349
|
-
#idToBytes (identifier, targetLength) {
|
|
350
|
-
const buffer = identifier.toBuffer()
|
|
351
|
-
if (buffer.length === targetLength) {
|
|
352
|
-
return Buffer.from(buffer).toString('hex')
|
|
353
|
-
}
|
|
354
|
-
if (buffer.length > targetLength) {
|
|
355
|
-
return Buffer.from(buffer.slice(buffer.length - targetLength)).toString('hex')
|
|
356
|
-
}
|
|
357
|
-
// Pad with leading zeros to reach target length.
|
|
358
|
-
const result = Buffer.alloc(targetLength)
|
|
359
|
-
Buffer.from(buffer).copy(result, targetLength - buffer.length)
|
|
360
|
-
return result.toString('hex')
|
|
341
|
+
/** @param {Identifier} identifier */
|
|
342
|
+
#spanIdToHex (identifier) {
|
|
343
|
+
return identifier.toString(16).slice(-16)
|
|
361
344
|
}
|
|
362
345
|
|
|
363
346
|
/**
|