meta(changelog): Update changelog for 10.63.0#21874
Conversation
This adds a new method, `extendIntegration`, that can be used to safely
extend an integration.
Today, we have the problem that if we extend an integration (e.g. node
extending something from server-utils), we have to manually call the
parent integration functions. This is a bit brittle because a) it
requires you to know exactly what methods the parent integration has,
which we usually want to abstract away from users on purpose. also, if a
parent integration changes, this could be forgotten/lost.
Usage:
```js
const _denoMysqlIntegration = (() => {
const inner = mysqlChannelIntegration();
return extendIntegration(inner, {
name: INTEGRATION_NAME,
setupOnce() {
setAsyncLocalStorageAsyncContextStrategy();
},
});
}) satisfies IntegrationFn;
```
vs. before:
```js
onst _denoMysqlIntegration = (() => {
const inner = mysqlChannelIntegration();
return {
name: INTEGRATION_NAME,
setupOnce() {
setAsyncLocalStorageAsyncContextStrategy();
inner.setupOnce?.();
},
};
}) satisfies IntegrationFn;
```
In addition, I also improved `defineIntegration` to maintain a static
name, and made all integration names static for easier access.
Note: We have a bunch of places where we started to type integrations
manually, e.g.
```ts
export const denoMysqlIntegration = defineIntegration(_denoMysqlIntegration) as () => Integration & {
name: 'DenoMysql';
setupOnce: () => void;
};
```
We should revert this in v11. We purposefully did not do this because we
want to keep the underlying implementation flexible. I think this was
done sometimes to make it easier to extend/call things, but usually this
should not be necessary and the extend helper should help with making
things type safe a bit easier.
[Gitflow] Merge master into develop
…gin (#21782) Adds a Nuxt 4 E2E test app that shows how orchestrion (build-time `diagnostics_channel` injection) can be wired into a meta framework, so DB instrumentation works on bundled server code without `--import`. - Rollup plugin instead of Vite. Nuxt's server is built by Nitro (Rollup), so the orchestrion code transform is wired in as a Nitro Rollup plugin, mirroring the existing Vite plugin. - Local module to inject `init` to "server entry". Nitro does not expose an explicit server entry to place the Sentry init in. A small local Nuxt module (mirroring `@sentry/nuxt`'s `addSentryTopImport`) injects a top-level `import './sentry.server.config.mjs'` into Nitro's built entry, so `node .output/server/index.mjs` initializes Sentry first - Both instrumentation paths coexist. orchestrion handles the bundled `mysql` (auto.db.orchestrion.mysql), while OTel keeps instrumenting everything left external (`ioredis`, `http`, …)
closes #21626 Inspired by the Cloudflare and Bun integration tests runner: `.unordered()`
ref(node): Replace `@opentelemetry/api` in lru-memoizer instrumentation with sentry APIs
…21786) Adds an orchestrion-based lru-memoizer integration replacing the equivalent otel integration if enabled. This currently supports `>=2.1.0`, which should be sufficient I think since `<2.1.0` is [barely used anymore](https://www.npmjs.com/package/lru-memoizer?activeTab=versions). Closes #20759 --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
## Problem The Cloudflare SDK README and several Cloudflare test fixtures still allow or use `nodejs_als`, but the SDK now needs the broader `nodejs_compat` flag as the minimum compatibility setting for current and future Node.js API usage. Fixes #18803. ## Solution I updated the Cloudflare README to document `nodejs_compat` as the required flag, adjusted the async context comment to match`.
…21785) Exposes a public `isTracingSuppressed` helper from `@sentry/core` and routes it through the async context strategy, mirroring `suppressTracing`. Previously `isTracingSuppressed` only existed as a private helper that checked the scope's SDK processing metadata. That's correct for the core (stack) strategy, but on OTel-based SDKs (e.g. Node) tracing suppression lives on the active OpenTelemetry context — so a metadata-only check could disagree with `suppressTracing`. Routing through the async context strategy keeps the two consistent across runtimes. ### Changes - **`@sentry/core`**: export `isTracingSuppressed`, which delegates to `acs.isTracingSuppressed` and falls back to the scope-metadata check. Added `isTracingSuppressed` to the `AsyncContextStrategy` type and replaced the private `_isTracingSuppressed` usages. - **`@sentry/opentelemetry`**: implement `isTracingSuppressed` (reads the active OTel context) and register it on the async context strategy. - **`@sentry/node-core`**: `SentryHttpInstrumentation` and `SentryNodeFetchInstrumentation` now call core's `isTracingSuppressed()` instead of importing it from `@opentelemetry/core`. - Added unit tests in `@sentry/core` and `@sentry/opentelemetry`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…21778) This streamlines and de-otelifies the undici instrumentation in node package. Note that there is still a OTEL instrumentation (channel based, but still) in node-core which can be updated in a separate follow up step. This also updates to use sentry/conventions and drop any config we hard coded so far, streamlining this. Also, unsubscribe code was removed and other things we do not need were removed.
We rely on the async local storage instance for the tracing channel bindings. Previously, this was picked up from the otel context manager (in otel-mode). However, this is only setup after the integrations run, leading to a race condition where this was not available yet. The "fix" for this we used so far was to setup the channel-based listeners in the next tick via `Promise.resolve()`. Apart from being a bit hacky, this also has a concrete problem: If code runs synchrounsly after Sentry.init(), this could be unhandled by our integration (this was surfaced when moving vercel ai v6 to use orchestrion, where this failed in CJS). So this PR changes this a bit, so that in the happy path where we use our own context manager, we can use a consistent instance of async local storage even before the manager is setup. In this case, we can setup the integrations in a sync way and everything just works. If users use a custom context manager, this will/may not work though. So in this case we continue to use the lookup from the context manager (this can be removed in v11). Since this means we have the same problems as before, I added a small utility `waitForTracingChannelBinding` to abstract this away - this will try to see if this is setup and if so, exectute the provided callback synchronously. Else, it will wait a tick and try again (mirroring what we had before). While at this, I also cleaned up internal exports from core a bit - one of them can be reproduced with more public APIs already, we just never exported `getAsyncContextStrategy` from core but this is fine to export IMHO. I also adjusted the type a bit so that `asyncLocalStorage` in the binding is unknown but non nullable, as this could accidentally become undefined which would not be caught by types but is not really intended - in this case the whole binding should be undefined.
…pans (#21833) # What Forward the isolation scope's `normalizedRequest` into the `tracesSampler` sampling context for root spans. `_startRootSpan` now includes it in the object passed to the sampler. # Why `SamplingContext` already advertises a `normalizedRequest` field, and server SDKs already record the incoming request on the isolation scope, but core never wired the two together for root spans, so `tracesSampler` always received `normalizedRequest: undefined` on the standard root-span path. This closes that gap so custom samplers can decide based on the incoming request (drop health-check routes, raise the rate for `/api/*`, key off the HTTP method) instead of reverse-engineering it from the span name or attributes, which for an incoming HTTP root span may not yet carry the route at sampling time. Extracted standalone from #21666 (the `SentryTracerProvider` stack): it has no dependency on that work, ships on its own merits, and shrinks #21666's review surface. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Streamlines the vendored `@opentelemetry/instrumentation-mysql` (#20738), mirroring the already-streamlined `mysql2` sibling. ## Changes - **Step 4 (Sentry APIs):** span creation moves from the OTel tracer to `startInactiveSpan`, with the `auto.db.otel.mysql` origin set directly (new — matches mysql2; the existing tests don't assert origin). - **Step 2 (remove unused):** - Connection-pool **metrics** (`db.client.connections.usage` counter, pool event listeners, `_patchPoolEnd`/`_patchAdd`/`_setPoolCallbacks`, `getPoolNameOld`) — Sentry doesn't consume OTel metrics. - **Semconv-stability dual-emission** — collapsed to the default OLD attribute set (`db.system`/`db.name`/`db.user`/`db.statement`, `net.peer.name`/`net.peer.port`), which is what's emitted today. - The unused **`enhancedDatabaseReporting`** option (the public `mysqlIntegration()` can't set it) + `getDbValues`/`AttributeNames`; `MySQLInstrumentationConfig` collapses to `InstrumentationConfig` (so `types.ts`/`AttributeNames.ts` are deleted). - **Step 3 (lint):** removed `/* eslint-disable */` from all vendored files; they pass the type-aware linter via the shared vendored override. The OTel **context plumbing** (`trace.setSpan`/`context.with`/`context.bind` + the pool `getConnection` context propagation) is intentionally **kept** to minimize behavior change. ## Verification - Build + type-aware lint clean. - **No change to emitted spans.** The existing mysql integration tests (step 1, already present) cover the query/callback/stream paths against a real MySQL. Since Docker isn't available in my environment I couldn't run them locally, but a fake-connection smoke test confirms the span output is unchanged: `op: 'db'`, description `'SELECT 1 + 1 AS solution'` (from `db.statement`), `origin: 'auto.db.otel.mysql'`, `db.system`/`net.peer.name`/`net.peer.port`/`db.user` as asserted by the tests. Closes #20738 --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This PR adds the external contributor to the CHANGELOG.md file, so that they are credited for their contribution. See #21659 Co-authored-by: JPeer264 <10677263+JPeer264@users.noreply.github.com>
This PR changes two things: 1. Set `nodejs_als` where `nodejs_compat` is not needed, just to check if we are compatible in v10 2. Add `--include-runtime=false` for generating wrangler types
…lient` (#21814) Now that all our server runtime SDKs support span streaming (they didn't initially), we can move the per-SDK-init-based logic to add `spanStreamingIntegration` if `traceLifecycle: 'stream'` was set, to the `ServerRuntimeClient`. This makes span streaming enabling more consistent for setups that only use a client. Also for SDKs like electron that init themselves with e.g. a `NodeClient` and therefore had to manually add the integration. h/t @timfish for raising this
Instead of requiring us to keep this as a string list, we can infer this from the integrations that are defined. Also small cleanup of integration names to make infernal possible (not really used right now but may be helpful to debug things in the future...)
This fails on CI as soon as anything touching DC is exported (type wise) from server-utils, e.g. https://github.com/getsentry/sentry-javascript/actions/runs/28358230203/job/84050855524 This PR injects a minimal type shim for the diagnostics channel module into the 3.8 d.ts file so this works there as well. We can remove this in v11.
This attribute is needed for resource span description inference in Relay (span streaming) closes #21749
…fo extraction (#21822) When a traced operation throws or rejects, we now set `error.type` and `sentry.status.message` attributes on the span alongside the existing error status, so the error type lines up with the rest of our span conventions. We talked about this yday, generally tracing channels allow us to observe more errors than before in some cases but these errors are not necessarily "unhandled", but we can still set the error attributes so our users can understand more about the op failure even if handled.
…d http.fragment (#21848) ## What Strip the leading `?`/`#` from the inferred `http.query` and `http.fragment` span attributes in `descriptionForHttpMethod`. ## Why The inferred values were taken straight from `URL.search`/`URL.hash`, which include the leading `?`/`#`. The OTel SDK span exporter (`getData` in `spanExporter.ts`) slices these off, so the same logical attribute ended up in two different formats depending on the code path. Stripping the prefix here makes both paths emit the same canonical format. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…ation (#21852) Adds `TODO(v11)` markers at the two spots that strip the leading `?`/`#` from `http.query`/`http.fragment` — the span exporter's `getData` and `parseSpanDescription`'s `descriptionForHttpMethod`. Per the [discussion on #21848](#21848 (comment)), `http.query` is specced to *keep* the leading `?`, so the stripping diverges from our own conventions. The v11 direction is to emit the OTel-standard `url.query`/`url.fragment` (no leading char, already in `@sentry/conventions`) and drop `http.query`/`http.fragment`. Comment-only, no behavior change. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…essage` attribute (#21811) Fixes a semi-deliberate oversight (lol) from my end that caused span status messages to be discarded when creating a `StreamedSpanJSON` from a `Span` instance. The deliberate part was to get rid of complicated statuses overall. The oversight was that there's a good use case to augment an `error` status span with a message: We sometimes want to record that an operation tracked by a span errored but explicitly _not_ capture an error for it (since users likely try/catch said operation). Classic example is `Connection Refused` for databases. closes #21800
…21844) Uses the `whenIdleOrHidden` callback to send the initial session envelope when the browser has some idle time or at the latest, when the page is hidden. For old Safari versions, we fall back to `setTimeout` but otherwise we use `requestIdleCallback` (both within `whenIdleOrHidden`). This should improve initial SDK initialization performance as well as reduce implicitly caused overhead (like increased LCP), due to the eagerly sent network request. This should be a fairly safe change to make. I don't think we risk loosing a lot of sessions with it, given RIC should fire relatively soon. At the same time, I'm curious how much it improves lighthouse scores (which we can check easiest after merging). closes #21817
Streamlines the vendored Prisma instrumentation onto Sentry's span APIs (v6/v7): - Folds attributes previously set via the `spanStart` hook into span creation in the instrumentation. - Uses `startSpanManual`/`startInactiveSpan` from `@sentry/core` instead of the OTel tracer in the vendored tracing helper. - Removes unused code: `setTracerProvider`/`tracerProvider` and unused contract types (`EngineTrace`, `EngineTraceEvent`, `LogLevel`). v5 cleanup will be done in a followup. Part of #20744 Closes #21820 --------- Co-authored-by: Nicolas Hrubec <nico.hrubec@sentry.io>
## Background same as with #21856 I came across this with adding the Prisma integration to Cloudflare. <img width="822" height="428" alt="Screenshot 2026-06-30 at 11 23 57" src="https://github.com/user-attachments/assets/5eff699b-6275-4b99-8627-9062a0ee7e5d" /> ## What has changed The `@sentry/opentelemetry` SDK has some side effect imports, which don't do anything: https://unpkg.com/@sentry/opentelemetry@10.62.0/build/esm/index.js those are the imports ```js import '@sentry/conventions/attributes'; import '@opentelemetry/core'; import '@opentelemetry/sdk-trace-base'; ``` `hoistTransitiveImports` has been added to get rid of the side effect imports. However, I'm confused with this option, as [the docs](https://rollupjs.org/configuration-options/#output-hoisttransitiveimports) say that this option is actually ignored when `preserveModules` have been set (but it still has an effect). Also here, if someone has a better idea - I'm all ears.
…res (#21860) Docker-backed integration suites intermittently go red before any test logic runs. `runDockerCompose` executed `docker compose up --wait` exactly once and threw on the first non-zero exit, so transient Docker daemon races became build failures. This retries `up` up to 3 times, tearing down between attempts. Real healthcheck failures stay red on every attempt, so only the transient infra races are absorbed. ### Root cause The flake surfaces as the daemon creating the compose network and then losing it as the container starts: ``` Network mysql2_default Created Container integration-tests-knex-mysql2 Starting Error response from daemon: failed to set up container networking: network mysql2_default not found ``` This is a containerd/libnetwork race (more likely with 16 docker suites running concurrently under vitest threads), not a problem with the instrumentation under test. Fixes #21752 Fixes #21751 Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…abled (#21141) Problem is that we do not have a consistent way of detecting stale meta tags so we rather disable them for `cacheComponents`. This stops the SDK from enabling Next's `experimental.clientTraceMetadata` (`sentry-trace`/`baggage`) when `cacheComponents` is enabled. With no meta tags injected, the browser pageload starts a fresh, self-contained trace instead of stitching onto a stale one. Apps that don't use Cache Components are unaffected. closes https://linear.app/getsentry/issue/JS-2782/cachecomponents-setup-breaks-meta-tag-injection --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…tion` (#21706) This is the first part of #20745, streamlining fastify v5 instrumentation: * This is already diagnostics channel based! * Moved it from node to server-utils package, which now exports a fastifyIntegration (with support for fastify v5 only as of now) which can also be used by deno and bun and others in follow ups. * The integration itself was updated to properly use sentry-specific methods etc. * Missing is some any/eslint cleanup - there is a lot of this going on and it is rather cumbersome to fix it so I opted to mostly leave it as is. * Also added some node integration tests for fastify v5 for easier testing/iteration - this was previously only covered by e2e tests. * Already moved it to use sentry conventions while at it Some other general notes: * This deprecates the `instrumentFastify` method and no longer calls it in preload, as this does not actually need to be preloaded anymore. * error handler stuff is still exported from server-utils as we need this for the old-style error handler. this can probably all be moved to server-utils in later steps when we moved the v3/v4 instrumentation as well. * The auto-error handling in fastify v5 does not work on node 18. this is a pre-existing problem though and not related to this PR, you'll have to add the error handler there.
…d route (#21869) The `request data extraction` tests in `hono-4/tests/route-patterns.test.ts` fetched the bare `GET /test-routes` and `POST /test-routes` routes. Those same routes are hit in parallel by the `HTTP methods` and `route registration styles` test blocks, producing transactions with identical `transaction` names. Because `waitForTransaction` resolves on the first event matching its predicate, the request-data tests could match a transaction produced by an unrelated parallel test. This is most visible in the POST case: its filter (`POST /test-routes`) also matches the `HTTP methods` POST test's transaction, which does **not** carry the `X-Custom-Header`, so the `x-custom-header` assertion could fail non-deterministically. The file already established a precedent for this — the `/query-test` route was added specifically "to avoid transaction name collisions". This applies the same pattern to the remaining request-data tests by routing them through a dedicated `/request-data` endpoint, so their `waitForTransaction` filters can no longer collide with the other blocks. Assertions are unchanged: the `url` check still matches (`/test-routes/request-data` contains `/test-routes`), and the method/header assertions are preserved — coverage is identical, only the routing is isolated. _Root cause_: transaction-name collision between parallel test blocks sharing the `GET`/`POST /test-routes` routes. Fixes #21810 Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
…error ordering (#21870) Fixes the flaky `postgresjs auto instrumentation > basic > should auto-instrument \`postgres\` package [esm]` Node integration test. _Root cause_ The `basic` test is the only postgresjs test that expects **two differently-typed envelopes** — a `transaction` followed by an error `event`: ```ts .expect({ transaction: EXPECTED_TRANSACTION }) .expect({ event: EXPECTED_ERROR_EVENT }) ``` The scenario's final query targets the just-dropped `"User"` table, so it rejects with a `PostgresError`. That rejection propagates out of `run()` as an **unhandled promise rejection**, which Sentry captures on a *later* event-loop tick than the transaction (the transaction is captured synchronously when the root span ends). Because the two envelopes are produced on different ticks, their arrival order at the transport is not deterministic. By default the test runner matches envelopes in **order** (`expectedEnvelopes.shift()`), so whenever the error event happened to reach the transport before the transaction, the runner threw `Expected envelope item type 'transaction' but got 'event'` and the test failed — a textbook intermittent flake. The other three describe blocks each expect only a single transaction envelope, which is why only `basic` flaked. _Fix_ Add `.unordered()` to the `basic` runner chain so the two envelopes may arrive in either order. Both the transaction (with all its spans) and the error event are still fully asserted with the same content matchers — only the brittle arrival-order constraint is removed. This mirrors the existing precedent in `suites/tracing/apollo-graphql/test.ts`, which uses `.unordered()` for the same class of non-deterministic multi-envelope ordering. Fixes #21790 Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…est (#21868) The `knex` + `mysql2` node integration test (`suites/tracing/knex/mysql2/test.ts`) flakes intermittently on CI. ### Root cause The suite's `docker-compose.yml` gated readiness on: ```yaml test: ['CMD-SHELL', 'mysqladmin ping -h 127.0.0.1 -uroot -pdocker'] ``` `mysqladmin ping` reports the server as *alive* even when it receives an **access-denied** error, and MySQL 8 spins up a temporary bootstrap server during first-time initialization (before the root password is applied and the `tests` database is created). As a result, `docker compose up -d --wait` — which the runner uses to block until healthy — can return **before the real server is ready**. The scenario then races the DB init and the query spans never arrive, so the transaction assertion times out. The `pg` sibling suite already avoids this class of bug by checking the actual database with `pg_isready -U test -d tests`. ### Fix Make the mysql2 healthcheck run an authenticated query against the `tests` database, mirroring the pg pattern: ```yaml test: ['CMD-SHELL', 'mysql -h 127.0.0.1 -uroot -pdocker -e "SELECT 1" tests'] ``` This only passes once the real server is up, the root password works, and `tests` exists — so `--wait` no longer returns prematurely. Fixes #21861 Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
| - feat(server-utils): Add lru-memoizer diagnostics-channel integration ([#21786](https://github.com/getsentry/sentry-javascript/pull/21786)) | ||
| - feat(server-utils): Expose channel-based, streamlined `fastifyIntegration` ([#21706](https://github.com/getsentry/sentry-javascript/pull/21706)) |
There was a problem hiding this comment.
I actually moved all of them to internal (clanker put them originally in the main changes) - as server-utils itself is internal. I think I'll make a duplicated entry with feat(node)
There was a problem hiding this comment.
I duplicated 2 of them which also had node SDK changes and added experimentalUseDiagnosticsChannelInjection clarification in one of them
size-limit report 📦
|
f9680e5 to
4e16503
Compare
| export function defaultShouldHandleError(_error: Error, _request: FastifyRequest, reply: FastifyReply): boolean { | ||
| const statusCode = reply.statusCode; | ||
| // 3xx and 4xx errors are not sent by default. | ||
| return statusCode >= 500 || statusCode <= 299; | ||
| } |
There was a problem hiding this comment.
Bug: The defaultShouldHandleError function for Fastify incorrectly captures errors with 2xx status codes, leading to unnecessary Sentry reports. The condition should likely only capture 5xx errors.
Severity: LOW
Suggested Fix
The condition in defaultShouldHandleError should be changed to only capture server errors. Align the logic with the Express integration by changing return statusCode >= 500 || statusCode <= 299; to return statusCode >= 500;. This ensures that only genuine server-side errors (5xx) are reported to Sentry, matching the documented intent and behavior of other framework integrations.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location:
packages/server-utils/src/integrations/tracing-channel/fastify/utils.ts#L10-L14
Potential issue: The `defaultShouldHandleError` function for the Fastify integration
uses the condition `statusCode >= 500 || statusCode <= 299`. This logic incorrectly
captures errors that occur with a 2xx status code, which are not typically considered
server errors. While it's unusual for an error to be thrown when the status code is 2xx,
it is technically possible within Fastify's `onError` hook. This behavior is
inconsistent with other Sentry integrations like Express (`status >= 500`) and Hono, and
contradicts the code comment which implies only 5xx errors should be captured. This will
lead to unnecessary error reports in Sentry.
Did we get this right? 👍 / 👎 to inform future reviews.
No description provided.