v5.121.0 proposal - #9712
Conversation
* feat(llmobs): accept image_parts on messages
Adds image support to the LLM Observability SDK, mirroring audio_parts. A
message may carry imageParts, each `{mimeType, content | attachmentKey}`, which
the tagger validates and emits as the snake_case wire shape `image_parts:
[{mime_type, content | attachment_key}]` — the same shape dd-trace-py emits and
the backend already types.
formatAudioPart and formatImagePart share one builder, since audio and image
parts have an identical wire shape and the linter rejects the duplicate.
Manual annotation only; provider auto-capture is a follow-up.
* feat(llmobs): mirror image part types to v5 and tighten ImagePart
Address review feedback on the public typing surface.
index.d.v5.ts now declares Message.imageParts and ImagePart. AGENTS.md
requires a new public type in both files unless the API is v6-only, and
this one is not: the runtime backports and audioParts already ships in
v5. No tsconfig references index.d.v5.ts, so it was verified by compiling
that surface standalone and resolving llmobs.ImagePart against it.
ImagePart becomes an exclusive union carrying exactly one of content or
attachmentKey, using the "?: never" shape already used by
AssistantTextMessage and AssistantToolCallMessage in the same file.
docs/test.ts pins all four cases, two valid and two behind
ts-expect-error. Those assertions are load-bearing: reverting the type to
all-optional fields fails type:doc:test with TS2578 twice.
The union is enforced on a directly annotated ImagePart but not on an
inline literal passed to annotate(), since inputData and outputData
include a "{ [key: string]: any }" arm that disables excess-property
checking. Narrowing that affects every annotate() shape and is left out.
Tests: the image non-string-content case now asserts the
invalid_io_messages telemetry tag that its audio counterpart already
asserted, closing a hole where deleting the tag argument kept the suite
green. An SDK-level image test mirrors the audio one, and three image
test names are aligned to the audio wording.
## Summary The file-limit boundary test spends most of its runtime opening and deleting 10,000 real files, which can exceed Mocha's 30-second timeout on Windows. ## Why The limit still needs the last accepted and first rejected case, so only the filesystem backend is replaced while all 10,001 production sink calls remain.
…ontext (#9575) A batch that mixes instrumented and uninstrumented messages attributed the ones without a context to the previous message's producer, so DSM reported edges no producer ever wrote. 1. `setDataStreamsContext` ignored a falsy context and left the previous message's pathway active; it now clears. 2. The SQS and Kinesis consumers skipped the decode for a message without a carrier, so nothing cleared the pathway. 3. `DsmPathwayCodec.decode` read the carrier through `pick` before its own null check and threw for a context-free message under `DD_TRACE_DEBUG`.
The native producer wrapper only forwarded the seventh-argument headers it recognized to the diagnostic-channel message, so trace injection replaced the caller's entire native header list instead of the fields propagation actually wrote, dropping application headers, ordering, repeats, and casing. 1. `Producer.produce()` now merges only the exact propagation fields into the caller's native header list, keeping every other entry, its order, and its repeats untouched. 2. KafkaJS maps and native consumers expose repeated wire headers differently (arrays vs. one-key records per repeat), so header conversion and DSM payload sizing now walk both shapes the same way and count wire records instead of array indices. 3. Repeated propagation fields (baggage, tracestate, DSM pathway context, …) now go through one field-owned read/write policy in `carrier.js` instead of raw carrier access per call site, so list fields combine, singleton fields resolve to the last usable value, and `traceparent` rejects repeats per the W3C Trace Context spec. An ESLint rule enforces that call sites use this policy instead of reaching into carriers directly. Refs: #9588 Refs: https://www.rfc-editor.org/rfc/rfc7230#section-3.2.2 Refs: https://www.w3.org/TR/trace-context/#tracestate-header-field-values
Add DD_TRACE_HTTP_SERVER_ERROR_STATUSES with DD_HTTP_SERVER_ERROR_STATUSES as its fallback alias and compile valid 100-599 ranges once in shared web configuration. Next.js now uses the same matcher as the other web plugins. Server spans hardcoded 5xx responses, so Node.js ignored the cross-tracer HTTP server error-status configuration. The existing validateStatus callback remains the programmatic override. - Run config and web utility unit tests. - Run the full HTTP server plugin test file. - Run the targeted Next.js 16 integration test. - Run changed-line coverage, generated config verification, and the full lint suite. Fixes: #7060
* feat(mysql,mysql2): trace pool connection acquisition An explicit pool.getConnection() held for a transaction hid any time spent waiting for a busy pool, and a pooled query never surfaced its acquire wait. Each explicit acquire now opens a dedicated acquire span (mysql.pool.acquire / mysql2.pool.acquire) carrying a pool.wait_time metric and recording connection errors; the acquire that pool.query() / execute() runs internally reports its wait as a tag on the query span instead, so a given acquire is counted once. Refs: #1613 * fix(mysql2): preserve pool-query acquire across cluster failover retries A pool cluster namespace retries `getConnection` on the next node when the first acquire fails, and with `canRetry` (the default) that retry is dispatched from the first acquire's asynchronous failure callback — after `wrapPoolQueryMethod` has already cleared the synchronous pool-query flag. The failover acquire was therefore treated as an explicit user acquire, opening a standalone `mysql2.pool.acquire` span and dropping the `pool.wait_time` tag from the successful query span. The namespace `getConnection` now re-asserts the flag for acquires that belong to a pool query, recognising retries by their reused callback. * fix(mysql): fold pool-cluster query acquire into the query span A `mysql` pool cluster's `PoolNamespace#query` acquires its connection internally, but that acquire was not bracketed with the pool-query flag, so it opened a standalone `mysql.pool.acquire` span and dropped the `pool.wait_time` tag from the query span — unlike the regular `pool.query` path. Bracketing `PoolNamespace#query` folds the wait into the query span; a `canRetry` failover retries by re-invoking `query`, so the same bracket also covers the node it fails over to. * ci: exercise the mysql instrumentation spec The new mysql instrumentation spec under packages/datadog-instrumentations/test only runs when a workflow sets PLUGINS=mysql for test:instrumentations; no job did, so verify-exercised-tests fails and the spec would never run in CI. The new job mirrors instrumentation-mysql2's service container and pinned image SHA. * fix(mysql,mysql2,pg): preserve pool acquire classification Pool cluster retries and connection callbacks can cross an async boundary, causing an internal query acquire to be reported as explicit and dropping its pool wait time. Stable query or callback identity preserves that classification. Synchronous implementations keep the existing fast path. The synchronous wait transfer measured 29.65-29.74 ns/op with WeakMap storage and 8.38-8.39 ns/op with the stack handoff on Node.js 24.18.0. * refactor(mysql): reduce pool acquire instrumentation churn ## Summary - fold pool query classification into the existing mysql and mysql2 wrappers - consolidate shared pool acquire control flow and equivalent contract tests - retain pg on the same synchronous fast path ## Why The implementation carried duplicate wrappers and test setup that obscured the hot-path invariants. This keeps subscriber-off forwarding and synchronous wait handoff allocation-free while preserving deferred dispatch and cluster retry isolation. ## Test plan - npm run lint - run the pool acquire helper, mysql, mysql2, and pg instrumentation suites - run the mysql and mysql2 plugin suites - verify changed-line and branch coverage against origin/master * fix(mysql,mysql2,pg): trace terminal pool acquisition failures Pooled queries suppress the explicit acquire lifecycle because their wait normally moves to the query span. A connection failure creates no query span, which dropped both the wait and error. Emit a backdated acquire lifecycle only for the terminal failure. Pool-cluster retries retain classification until the final callback, and synchronous mysql2 stream construction finishes the explicit acquire before rethrowing. * fix(mysql,mysql2): finish pool acquire spans through plugin lifecycle ## Summary Use the context-backed outbound lifecycle for explicit MySQL and MySQL2 pool-acquisition spans. ## Why Direct span completion bypassed peer-service computation, mapping, and serverless overrides. It also kept a second span lifecycle beside the PostgreSQL path. ## Drive-by Align pool-helper JSDoc and the MySQL instrumentation checkout action with current master. ## Test plan - PLUGINS=mysql|mysql2|pg npm run test:plugins - full changed-line coverage against origin/master - npm run lint
Bumps the testing-and-build group with 1 update in the /packages/dd-trace/test/plugins/versions directory: [mocha](https://github.com/mochajs/mocha). Updates `mocha` from 11.7.6 to 11.8.0 - [Release notes](https://github.com/mochajs/mocha/releases) - [Changelog](https://github.com/mochajs/mocha/blob/v11.8.0/CHANGELOG.md) - [Commits](mochajs/mocha@v11.7.6...v11.8.0) --- updated-dependencies: - dependency-name: mocha dependency-version: 11.8.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: testing-and-build ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Overall package sizeSelf size: 7.92 MB Dependency sizes| name | version | self size | total size | |------|---------|-----------|------------| | import-in-the-middle | 3.3.3 | 125.43 kB | 441.68 kB | | opentracing | 0.14.7 | 194.81 kB | 194.81 kB | | dc-polyfill | 0.1.11 | 25.74 kB | 25.74 kB |🤖 This report was automatically generated by heaviest-objects-in-the-universe |
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## v5.x #9712 +/- ##
===========================================
+ Coverage 83.19% 98.48% +15.29%
===========================================
Files 476 965 +489
Lines 20153 137570 +117417
Branches 0 11716 +11716
===========================================
+ Hits 16766 135489 +118723
+ Misses 3387 2081 -1306 Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
BenchmarksBenchmark execution time: 2026-08-06 14:49:34 Comparing candidate commit 985270a in PR branch Found 0 performance improvements and 0 performance regressions! Performance is the same for 2319 metrics, 39 unstable metrics.
|
938bf91 to
985270a
Compare
Features
Fixes
Internal (CI, Testing, Benchmarking)