Introducing integrated observability in SvelteKit
SvelteKit apps can now emit OpenTelemetry traces and reliably set up observability instrumentation using instrumentation.server.ts
“Observability” is one of those buzzwords that is commonly thrown around as something essential to all good apps
SvelteKit is proud to announce two new experimental features geared towards making observing and debugging your app as easy as possible.
First-party OpenTelemetry traces
SvelteKit can now emit OpenTelemetry traces for the following:
handlehook (handlefunctions running in asequencewill show up as children of each other and the root handle hook)loadfunctions (includes universalloadfunctions when they’re run on the server)- Form actions
- Remote functions
To enable trace emission, add the following to svelte.config.js:
export default {
kit: {
experimental: {
tracing: {
server: boolean;
};
};
}
kit: {
experimental: {
tracing: {
server: boolean;
};
}
experimental: {
tracing: {
server: boolean;
}
tracing: {
server: booleanserver: true
}
}
}
};If there are additional attributes you think might be useful, please file an issue on the SvelteKit Github issue tracker.
A convenient home for all of your instrumentation
Emitting traces alone is not enough: You also need to collect them and send them somewhere. Under normal circumstances, this can be a bit challenging. Because of the nature of observability instrumentation, it needs to be loaded prior to loading any of the code from your app. To aid in this, SvelteKit now supports a src/instrumentation.server.ts file which, assuming your adapter supports it, is guaranteed to be loaded prior to your application code. In Node, your instrumentation might look something like this:
import { import NodeSDKNodeSDK } from '@opentelemetry/sdk-node';
import { import getNodeAutoInstrumentationsgetNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
import { import OTLPTraceExporterOTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';
import { import createAddHookMessageChannelcreateAddHookMessageChannel } from 'import-in-the-middle';
import { function register<Data = any>(specifier: string | URL, parentURL?: string | URL, options?: Module.RegisterOptions<Data>): void (+1 overload)Register a module that exports hooks that customize Node.js module
resolution and loading behavior. See
Customization hooks.
register } from 'module';
const { const registerOptions: anyregisterOptions } = import createAddHookMessageChannelcreateAddHookMessageChannel();
register<any>(specifier: string | URL, parentURL?: string | URL, options?: Module.RegisterOptions<any> | undefined): void (+1 overload)Register a module that exports hooks that customize Node.js module
resolution and loading behavior. See
Customization hooks.
register('import-in-the-middle/hook.mjs', import.meta.ImportMeta.url: stringThe absolute file: URL of the module.
url, const registerOptions: anyregisterOptions);
const const sdk: anysdk = new import NodeSDKNodeSDK({
serviceName: stringserviceName: 'test-sveltekit-tracing',
traceExporter: anytraceExporter: new import OTLPTraceExporterOTLPTraceExporter(),
instrumentations: any[]instrumentations: [import getNodeAutoInstrumentationsgetNodeAutoInstrumentations()]
});
const sdk: anysdk.start();...and on Vercel, it would look something like this:
import { import registerOTelregisterOTel } from '@vercel/otel';
import registerOTelregisterOTel({
serviceName: stringserviceName: 'test-sveltekit-tracing'
});Consult your platform’s documentation for specific instrumentation instructions. As of now, all of the official SvelteKit adapters with a server component (sorry, adapter-static) support instrumentation.server.ts.
Acknowledgements
A huge thank-you to Lukas Stracke, who kicked us off on this adventure with his excellent talk at Svelte Summit 2025 and his initial draft PR for instrumentation.server.ts. Another thank-you to Sentry for allowing him to spend his working hours reviewing and testing our work.