Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
0c3aad6
feat: Initial tracing setup (peer deps + utils)
elliott-with-the-longest-name-on-github Jun 17, 2025
62777fe
Merge branch 'main' into elliott/init-tracing
eltigerchino Jul 22, 2025
78a65ea
feat: Add tracing to `load`, server actions, and `handle`/`resolve` (…
elliott-with-the-longest-name-on-github Jul 23, 2025
f65b78f
Update packages/kit/src/exports/public.d.ts
elliott-with-the-longest-name-on-github Jul 23, 2025
77d447f
chore: Switch to `tracing: { server: boolean }`
elliott-with-the-longest-name-on-github Jul 23, 2025
965bfea
chore: Make enablement / importing of otel make more sense
elliott-with-the-longest-name-on-github Jul 23, 2025
65752bc
chore: merge_tracing function
elliott-with-the-longest-name-on-github Jul 23, 2025
e50615b
fix rich's bad comment that shouldn't have ever existed >:(
elliott-with-the-longest-name-on-github Jul 23, 2025
5b583ec
test stuff
elliott-with-the-longest-name-on-github Jul 23, 2025
90e961d
Update packages/kit/test/utils.js
elliott-with-the-longest-name-on-github Jul 23, 2025
dd516a9
i am truly among the dumbest
elliott-with-the-longest-name-on-github Jul 24, 2025
0cf24bc
Merge branch 'main' into elliott/init-tracing
elliott-with-the-longest-name-on-github Jul 24, 2025
962daf7
types
elliott-with-the-longest-name-on-github Jul 24, 2025
6a81d8b
remove now-useless comment
elliott-with-the-longest-name-on-github Jul 24, 2025
f143f5b
lockfile
elliott-with-the-longest-name-on-github Jul 24, 2025
fc3f734
fix dumb import analysis
elliott-with-the-longest-name-on-github Jul 24, 2025
4b64316
changeset
elliott-with-the-longest-name-on-github Jul 24, 2025
3192797
Merge branch 'main' into elliott/init-tracing
elliott-with-the-longest-name-on-github Aug 6, 2025
b6b7d6a
fix: conflict
elliott-with-the-longest-name-on-github Aug 6, 2025
60540ce
i do not know why this fixed it but it did
elliott-with-the-longest-name-on-github Aug 6, 2025
723d5a4
Merge remote-tracking branch 'origin/main' into elliott/init-tracing
elliott-with-the-longest-name-on-github Aug 7, 2025
091eaef
idk man
elliott-with-the-longest-name-on-github Aug 7, 2025
acf66e9
i do not understand why this fixed anything
elliott-with-the-longest-name-on-github Aug 7, 2025
587be22
chore: catalog node types
elliott-with-the-longest-name-on-github Aug 7, 2025
83a24aa
upstream
elliott-with-the-longest-name-on-github Aug 7, 2025
874717f
fix: idiocy on my part tbh
elliott-with-the-longest-name-on-github Aug 7, 2025
1d06059
optionally load tracing, add remote functions
elliott-with-the-longest-name-on-github Aug 8, 2025
4c95395
chore: Better attributes for actions
elliott-with-the-longest-name-on-github Aug 8, 2025
45d3eaa
only load module if exists
elliott-with-the-longest-name-on-github Aug 8, 2025
7a91d03
Merge remote-tracking branch 'origin/main' into elliott/init-tracing
elliott-with-the-longest-name-on-github Aug 8, 2025
cbfaa03
try this to prevent package issues
elliott-with-the-longest-name-on-github Aug 8, 2025
0001ab9
fix: unit tests
elliott-with-the-longest-name-on-github Aug 8, 2025
4486b21
fix again
elliott-with-the-longest-name-on-github Aug 8, 2025
30ffc68
move event.js to internal
elliott-with-the-longest-name-on-github Aug 11, 2025
b526831
move event-state to internal
elliott-with-the-longest-name-on-github Aug 11, 2025
5954ae4
changeset and docs
elliott-with-the-longest-name-on-github Aug 11, 2025
fb508f7
types
elliott-with-the-longest-name-on-github Aug 11, 2025
4ccef4b
types
elliott-with-the-longest-name-on-github Aug 11, 2025
21f84be
fix: use resolve module
elliott-with-the-longest-name-on-github Aug 12, 2025
eb3e876
feat: `tracing.server.ts` (#14117)
elliott-with-the-longest-name-on-github Aug 12, 2025
b57f927
Merge remote-tracking branch 'origin/main' into elliott/init-tracing
elliott-with-the-longest-name-on-github Aug 12, 2025
c73569c
server file option
elliott-with-the-longest-name-on-github Aug 12, 2025
f654784
docs
elliott-with-the-longest-name-on-github Aug 12, 2025
5f5129c
feedback
elliott-with-the-longest-name-on-github Aug 12, 2025
5c3cc72
fix: adapters
elliott-with-the-longest-name-on-github Aug 12, 2025
afc7e15
Merge remote-tracking branch 'origin/main' into elliott/init-tracing
elliott-with-the-longest-name-on-github Aug 12, 2025
8c804a6
fix event state
elliott-with-the-longest-name-on-github Aug 12, 2025
00eec4a
harder
elliott-with-the-longest-name-on-github Aug 12, 2025
5660820
exit hell
elliott-with-the-longest-name-on-github Aug 12, 2025
3a8b5ac
Update packages/kit/src/exports/vite/utils.js
elliott-with-the-longest-name-on-github Aug 12, 2025
8e0ba7a
address feedback
elliott-with-the-longest-name-on-github Aug 12, 2025
e65a25e
Update documentation/docs/30-advanced/68-observability.md
elliott-with-the-longest-name-on-github Aug 12, 2025
06486db
Update documentation/docs/30-advanced/68-observability.md
elliott-with-the-longest-name-on-github Aug 12, 2025
dc35aa1
Merge branch 'elliott/init-tracing' of github.com:sveltejs/kit into e…
elliott-with-the-longest-name-on-github Aug 12, 2025
512538e
Update documentation/docs/30-advanced/68-observability.md
elliott-with-the-longest-name-on-github Aug 12, 2025
4b09980
Update documentation/docs/30-advanced/68-observability.md
elliott-with-the-longest-name-on-github Aug 12, 2025
a111b7d
Update documentation/docs/30-advanced/68-observability.md
elliott-with-the-longest-name-on-github Aug 12, 2025
3188b73
we can't use here... long story but it breaks the docs
Rich-Harris Aug 12, 2025
268cd82
destroy event-state
elliott-with-the-longest-name-on-github Aug 13, 2025
8126882
Merge branch 'elliott/init-tracing' of github.com:sveltejs/kit into e…
elliott-with-the-longest-name-on-github Aug 13, 2025
738db55
Merge remote-tracking branch 'origin/main' into elliott/init-tracing
elliott-with-the-longest-name-on-github Aug 13, 2025
a1cad37
Update documentation/docs/30-advanced/68-observability.md
Rich-Harris Aug 13, 2025
3322f89
remove config
elliott-with-the-longest-name-on-github Aug 13, 2025
2fe0fbf
last feedbacks
elliott-with-the-longest-name-on-github Aug 13, 2025
2ad5182
Merge branch 'elliott/init-tracing' of github.com:sveltejs/kit into e…
elliott-with-the-longest-name-on-github Aug 13, 2025
98669e6
tests
elliott-with-the-longest-name-on-github Aug 13, 2025
0c03fbe
fix type generation and remove hack
eltigerchino Aug 13, 2025
f1d9fe2
Merge branch 'main' into elliott/init-tracing
eltigerchino Aug 13, 2025
54831e7
em dashes and tabs
Rich-Harris Aug 13, 2025
71b4cdd
Update packages/kit/src/exports/public.d.ts
Rich-Harris Aug 13, 2025
d15b9f5
bump since tags
Rich-Harris Aug 13, 2025
6edfc8a
Apply suggestions from code review
Rich-Harris Aug 13, 2025
b16c012
Update packages/kit/src/exports/vite/dev/index.js
Rich-Harris Aug 13, 2025
04b7fde
Merge branch 'main' into elliott/init-tracing
Rich-Harris Aug 13, 2025
6a73667
oops
Rich-Harris Aug 13, 2025
f34728a
Update documentation/docs/30-advanced/68-observability.md
Rich-Harris Aug 13, 2025
ace50a3
fix: type nightmares
elliott-with-the-longest-name-on-github Aug 13, 2025
bf8cb46
Merge branch 'elliott/init-tracing' of github.com:sveltejs/kit into e…
elliott-with-the-longest-name-on-github Aug 13, 2025
f0acd03
bump since tags
Rich-Harris Aug 14, 2025
7334888
smarter
elliott-with-the-longest-name-on-github Aug 14, 2025
7d26bf4
Merge branch 'elliott/init-tracing' of github.com:sveltejs/kit into e…
elliott-with-the-longest-name-on-github Aug 14, 2025
281cdb4
types
elliott-with-the-longest-name-on-github Aug 14, 2025
0a1834b
hopefully I oneshot this with my brain
elliott-with-the-longest-name-on-github Aug 14, 2025
bd73dc9
last fixes
elliott-with-the-longest-name-on-github Aug 14, 2025
50d942a
fix
elliott-with-the-longest-name-on-github Aug 14, 2025
d9ab921
fix
elliott-with-the-longest-name-on-github Aug 14, 2025
a7b2430
small docs addition
elliott-with-the-longest-name-on-github Aug 14, 2025
477e50e
fix
elliott-with-the-longest-name-on-github Aug 14, 2025
ab91eed
fix
elliott-with-the-longest-name-on-github Aug 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"@parcel/watcher",
"esbuild",
"netlify-cli",
"protobufjs",
"rolldown",
"sharp",
"svelte-preprocess",
Expand Down
7 changes: 7 additions & 0 deletions packages/kit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"sirv": "^3.0.0"
},
"devDependencies": {
"@opentelemetry/api": "^1.0.0",
"@playwright/test": "catalog:",
"@sveltejs/vite-plugin-svelte": "catalog:",
"@types/connect": "^3.4.38",
Expand All @@ -47,9 +48,15 @@
},
"peerDependencies": {
"@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0",
"@opentelemetry/api": "^1.0.0",
"svelte": "^4.0.0 || ^5.0.0-next.0",
"vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0"
},
"peerDependenciesMeta": {
"@opentelemetry/api": {
"optional": true
}
},
"bin": {
"svelte-kit": "svelte-kit.js"
},
Expand Down
60 changes: 60 additions & 0 deletions packages/kit/src/core/config/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ const get_defaults = (prefix = '') => ({
publicPrefix: 'PUBLIC_',
privatePrefix: ''
},
experimental: {
tracing: undefined
},
files: {
assets: join(prefix, 'static'),
hooks: {
Expand Down Expand Up @@ -404,3 +407,60 @@ test('errors on loading config with incorrect default export', async () => {
'The Svelte config file must have a configuration object as its default export. See https://svelte.dev/docs/kit/configuration'
);
});

test('accepts valid tracing values', () => {
assert.doesNotThrow(() => {
validate_config({
kit: {
experimental: {
tracing: 'server'
}
}
});
});

assert.doesNotThrow(() => {
validate_config({
kit: {
experimental: {
tracing: undefined
}
}
});
});
});

test('errors on invalid tracing values', () => {
assert.throws(() => {
validate_config({
kit: {
experimental: {
// @ts-expect-error - given value expected to throw
tracing: true
}
}
});
}, /^config\.kit\.experimental\.tracing should be undefined or "server"$/);

assert.throws(() => {
validate_config({
kit: {
experimental: {
// @ts-expect-error - given value expected to throw
tracing: false
}
}
});
}, /^config\.kit\.experimental\.tracing should be undefined or "server"$/);

assert.throws(() => {
validate_config({
kit: {
experimental: {
// @ts-expect-error - given value expected to throw
tracing: 'client'
}
}
});
}, /^config\.kit\.experimental\.tracing should be undefined or "server"$/);
});
9 changes: 9 additions & 0 deletions packages/kit/src/core/config/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,15 @@ const options = object(
privatePrefix: string('')
}),

experimental: object({
tracing: validate(undefined, (input, keypath) => {
if (input !== 'server') {
throw new Error(`${keypath} should be undefined or "server"`);
}
return input;
})
}),

files: object({
assets: string('static'),
hooks: object({
Expand Down
6 changes: 6 additions & 0 deletions packages/kit/src/core/sync/write_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ import { set_building, set_prerendering } from '__sveltekit/environment';
import { set_assets } from '__sveltekit/paths';
import { set_manifest, set_read_implementation } from '__sveltekit/server';
import { set_private_env, set_public_env, set_safe_public_env } from '${runtime_directory}/shared-server.js';
import { get_tracer, enable_tracing } from '${runtime_directory}/telemetry/get_tracer.js';

if (${s(config.kit.experimental.tracing === 'server')}) {
enable_tracing();
}

export const options = {
app_template_contains_nonce: ${template.includes('%sveltekit.nonce%')},
Expand Down Expand Up @@ -60,6 +65,7 @@ export const options = {
.replace(/%sveltekit\.status%/g, '" + status + "')
.replace(/%sveltekit\.error\.message%/g, '" + message + "')}
},
tracer: get_tracer(),
version_hash: ${s(hash(config.kit.version.name))}
};

Expand Down
91 changes: 59 additions & 32 deletions packages/kit/src/exports/hooks/sequence.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
/** @import { Handle, RequestEvent, ResolveOptions } from '@sveltejs/kit' */
/** @import { MaybePromise } from 'types' */
import { with_event } from '../../runtime/app/server/event.js';
import { get_tracer } from '../../runtime/telemetry/get_tracer.js';
import { record_span } from '../../runtime/telemetry/record_span.js';

/**
* A helper function for sequencing multiple `handle` calls in a middleware-like manner.
* The behavior for the `handle` options is as follows:
Expand Down Expand Up @@ -66,56 +72,77 @@
* first post-processing
* ```
*
* @param {...import('@sveltejs/kit').Handle} handlers The chain of `handle` functions
* @returns {import('@sveltejs/kit').Handle}
* @param {...Handle} handlers The chain of `handle` functions
* @returns {Handle}
*/
export function sequence(...handlers) {
const length = handlers.length;
if (!length) return ({ event, resolve }) => resolve(event);

return ({ event, resolve }) => {
return async ({ event, resolve }) => {
// there's an assumption here that people aren't doing something insane like sequence(() => {}, sequence(() => {}))
// worst case there is that future spans get a lower-down span as their root span -- the tracing would still work,
// it'd just look a little weird
const { rootSpan } = event.tracing;
const tracer = await get_tracer();
return apply_handle(0, event, {});

/**
* @param {number} i
* @param {import('@sveltejs/kit').RequestEvent} event
* @param {import('@sveltejs/kit').ResolveOptions | undefined} parent_options
* @returns {import('types').MaybePromise<Response>}
* @param {RequestEvent} event
* @param {ResolveOptions | undefined} parent_options
* @returns {MaybePromise<Response>}
*/
function apply_handle(i, event, parent_options) {
const handle = handlers[i];

return handle({
event,
resolve: (event, options) => {
/** @type {import('@sveltejs/kit').ResolveOptions['transformPageChunk']} */
const transformPageChunk = async ({ html, done }) => {
if (options?.transformPageChunk) {
html = (await options.transformPageChunk({ html, done })) ?? '';
}
return record_span({
tracer,
name: 'sveltekit.handle.child',
attributes: {
'sveltekit.handle.child.index': i
},
fn: async (span) => {
const traced_event = { ...event, tracing: { rootSpan, currentSpan: span } };
return await with_event(traced_event, () =>
handle({
event: traced_event,
resolve: (event, options) => {
/** @type {ResolveOptions['transformPageChunk']} */
const transformPageChunk = async ({ html, done }) => {
if (options?.transformPageChunk) {
html = (await options.transformPageChunk({ html, done })) ?? '';
}

if (parent_options?.transformPageChunk) {
html = (await parent_options.transformPageChunk({ html, done })) ?? '';
}
if (parent_options?.transformPageChunk) {
html = (await parent_options.transformPageChunk({ html, done })) ?? '';
}

return html;
};
return html;
};

/** @type {import('@sveltejs/kit').ResolveOptions['filterSerializedResponseHeaders']} */
const filterSerializedResponseHeaders =
parent_options?.filterSerializedResponseHeaders ??
options?.filterSerializedResponseHeaders;
/** @type {ResolveOptions['filterSerializedResponseHeaders']} */
const filterSerializedResponseHeaders =
parent_options?.filterSerializedResponseHeaders ??
options?.filterSerializedResponseHeaders;

/** @type {import('@sveltejs/kit').ResolveOptions['preload']} */
const preload = parent_options?.preload ?? options?.preload;
/** @type {ResolveOptions['preload']} */
const preload = parent_options?.preload ?? options?.preload;

return i < length - 1
? apply_handle(i + 1, event, {
transformPageChunk,
filterSerializedResponseHeaders,
preload
})
: resolve(event, { transformPageChunk, filterSerializedResponseHeaders, preload });
return i < length - 1
? apply_handle(i + 1, event, {
transformPageChunk,
filterSerializedResponseHeaders,
preload
})
: resolve(event, {
transformPageChunk,
filterSerializedResponseHeaders,
preload
});
}
})
);
}
});
}
Expand Down
19 changes: 9 additions & 10 deletions packages/kit/src/exports/hooks/sequence.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import { installPolyfills } from '../node/polyfills.js';

installPolyfills();

const dummy_event = /** @type {import('@sveltejs/kit').RequestEvent} */ ({
tracing: { rootSpan: {} }
});

test('applies handlers in sequence', async () => {
/** @type {string[]} */
const order = [];
Expand All @@ -29,10 +33,9 @@ test('applies handlers in sequence', async () => {
}
);

const event = /** @type {import('@sveltejs/kit').RequestEvent} */ ({});
const response = new Response();

assert.equal(await handler({ event, resolve: () => response }), response);
assert.equal(await handler({ event: dummy_event, resolve: () => response }), response);
expect(order).toEqual(['1a', '2a', '3a', '3b', '2b', '1b']);
});

Expand All @@ -47,9 +50,8 @@ test('uses transformPageChunk option passed to non-terminal handle function', as
async ({ event, resolve }) => resolve(event)
);

const event = /** @type {import('@sveltejs/kit').RequestEvent} */ ({});
const response = await handler({
event,
event: dummy_event,
resolve: async (_event, opts = {}) => {
let html = '';

Expand Down Expand Up @@ -84,9 +86,8 @@ test('merges transformPageChunk option', async () => {
}
);

const event = /** @type {import('@sveltejs/kit').RequestEvent} */ ({});
const response = await handler({
event,
event: dummy_event,
resolve: async (_event, opts = {}) => {
let html = '';

Expand Down Expand Up @@ -117,9 +118,8 @@ test('uses first defined preload option', async () => {
}
);

const event = /** @type {import('@sveltejs/kit').RequestEvent} */ ({});
const response = await handler({
event,
event: dummy_event,
resolve: (_event, opts = {}) => {
let html = '';

Expand Down Expand Up @@ -150,9 +150,8 @@ test('uses first defined filterSerializedResponseHeaders option', async () => {
}
);

const event = /** @type {import('@sveltejs/kit').RequestEvent} */ ({});
const response = await handler({
event,
event: dummy_event,
resolve: (_event, opts = {}) => {
let html = '';

Expand Down
Loading
Loading