What is the feature you are proposing?
I'd like to propose adding first-class TracingChannel support to Remix 3, following the pattern established by undici in Node.js core and adopted by framework peers like h3, fastify, and srvx.
TracingChannel is a higher-level API built on top of diagnostics_channel, specifically designed for tracing async operations. It provides structured lifecycle channels (start, end, error, asyncStart, asyncEnd) and handles async context propagation correctly -- the missing piece that makes monkey-patching approaches fragile in real-world async applications.
We (at Sentry) are working on instrumentation for Remix 3 and would love to avoid the monkey-patching path entirely. Remix 3's closure-based router has no prototype chain to intercept, making external instrumentation especially difficult. TracingChannel gives APM tools a stable, first-party surface to subscribe to.
Since Remix 3 is a ground-up rewrite with no existing APM instrumentation, this is a rare opportunity to ship TracingChannel from day one -- so APM vendors never need to build monkey-patching infrastructure for this framework at all.
Proposed Channels
Three channels covering the three server-side subsystems:
| TracingChannel |
Subsystem |
Tracks |
Context fields |
remix:request |
fetch-router |
Middleware and route handler executions |
type, name, context |
remix:render |
ui/server |
Server-side rendering of component trees |
frameSrc |
remix:asset |
assets |
On-demand script/style compilation |
filePath, assetType |
remix:request traces individual middleware and handler executions. The type field ("middleware" or "handler") distinguishes them. The context field is the RequestContext object, from which APMs extract HTTP method, URL, params, and headers.
remix:render traces renderToStream(). Frame resolution sub-requests naturally appear as nested remix:request spans because resolveFrame() calls router.fetch().
remix:asset traces on-demand TypeScript/CSS compilation in the asset server.
Insertion Points
The implementation is minimal:
runMiddleware() in fetch-router/src/lib/middleware.ts -- wraps each middleware call with tracePromise
dispatchMatches() in fetch-router/src/lib/router.ts -- wraps matched handler calls with tracePromise
renderToStream() in ui/src/server/stream.ts -- wraps the render work
assetServer.fetch() in assets/src/lib/asset-server.ts -- wraps compilation paths
Each uses the standard hasSubscribers gating for zero cost when no APM tool is listening.
APM Subscriber Example
import { tracingChannel } from 'otel-tracing-channel';
import * as Sentry from '@sentry/node';
tracingChannel('remix:request', (ctx) => {
return Sentry.startSpanManual({
name: ctx.name,
op: ctx.type === 'handler' ? 'remix.handler' : 'middleware.remix',
attributes: {
'http.request.method': ctx.context.method,
'url.path': ctx.context.url.pathname,
},
}, (span) => span);
}).subscribe({
asyncEnd(ctx) { ctx.span?.end(); },
error(ctx) {
ctx.span?.setStatus({ code: 2, message: ctx.error?.message });
ctx.span?.end();
},
});
Working Implementation
We have a working proof-of-concept with a demo app that sends traces to Sentry:
Prior Art
This follows the same pattern adopted by other frameworks:
Happy to put together a PR if there's interest.
What is the feature you are proposing?
I'd like to propose adding first-class
TracingChannelsupport to Remix 3, following the pattern established byundiciin Node.js core and adopted by framework peers likeh3,fastify, andsrvx.TracingChannelis a higher-level API built on top ofdiagnostics_channel, specifically designed for tracing async operations. It provides structured lifecycle channels (start,end,error,asyncStart,asyncEnd) and handles async context propagation correctly -- the missing piece that makes monkey-patching approaches fragile in real-world async applications.We (at Sentry) are working on instrumentation for Remix 3 and would love to avoid the monkey-patching path entirely. Remix 3's closure-based router has no prototype chain to intercept, making external instrumentation especially difficult. TracingChannel gives APM tools a stable, first-party surface to subscribe to.
Since Remix 3 is a ground-up rewrite with no existing APM instrumentation, this is a rare opportunity to ship TracingChannel from day one -- so APM vendors never need to build monkey-patching infrastructure for this framework at all.
Proposed Channels
Three channels covering the three server-side subsystems:
remix:requesttype,name,contextremix:renderframeSrcremix:assetfilePath,assetTyperemix:requesttraces individual middleware and handler executions. Thetypefield ("middleware"or"handler") distinguishes them. Thecontextfield is theRequestContextobject, from which APMs extract HTTP method, URL, params, and headers.remix:rendertracesrenderToStream(). Frame resolution sub-requests naturally appear as nestedremix:requestspans becauseresolveFrame()callsrouter.fetch().remix:assettraces on-demand TypeScript/CSS compilation in the asset server.Insertion Points
The implementation is minimal:
runMiddleware()infetch-router/src/lib/middleware.ts-- wraps each middleware call withtracePromisedispatchMatches()infetch-router/src/lib/router.ts-- wraps matched handler calls withtracePromiserenderToStream()inui/src/server/stream.ts-- wraps the render workassetServer.fetch()inassets/src/lib/asset-server.ts-- wraps compilation pathsEach uses the standard
hasSubscribersgating for zero cost when no APM tool is listening.APM Subscriber Example
Working Implementation
We have a working proof-of-concept with a demo app that sends traces to Sentry:
Prior Art
This follows the same pattern adopted by other frameworks:
undici(Node.js core): ships TracingChannel since Node 20.12fastify: ships natively (tracing:fastify.request.handler)h3: h3js/h3#1251 ✅ mergedsrvx: h3js/srvx#141 ✅ mergednitro: nitrojs/nitro#4001 ✅ mergedexpress: pillarjs/router#196 PR openhono: honojs/hono#4842 issue openedelysia: elysiajs/elysia#1809 in discussionHappy to put together a PR if there's interest.