Skip to content

Commit 17898be

Browse files
feat: add universal IDs to client nodes
1 parent 19db921 commit 17898be

36 files changed

+1461
-210
lines changed

packages/kit/src/core/config/index.spec.js

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ const get_defaults = (prefix = '') => ({
101101
serviceWorker: {
102102
register: true
103103
},
104-
tracing: false,
104+
tracing: undefined,
105105
typescript: {},
106106
paths: {
107107
base: '',
@@ -407,58 +407,59 @@ test('errors on loading config with incorrect default export', async () => {
407407
});
408408

409409
test('accepts valid tracing values', () => {
410-
// Test boolean values
411410
assert.doesNotThrow(() => {
412411
validate_config({
413412
kit: {
414-
tracing: true
413+
tracing: 'server'
415414
}
416415
});
417416
});
418417

419418
assert.doesNotThrow(() => {
420419
validate_config({
421420
kit: {
422-
tracing: false
421+
tracing: undefined
423422
}
424423
});
425424
});
425+
});
426426

427-
// Test string values
428-
assert.doesNotThrow(() => {
427+
test('errors on invalid tracing values', () => {
428+
assert.throws(() => {
429429
validate_config({
430430
kit: {
431-
tracing: 'server'
431+
// @ts-expect-error - given value expected to throw
432+
tracing: true
432433
}
433434
});
434-
});
435+
}, /^config\.kit\.tracing should be undefined or "server"$/);
435436

436-
assert.doesNotThrow(() => {
437+
assert.throws(() => {
437438
validate_config({
438439
kit: {
439-
tracing: 'client'
440+
// @ts-expect-error - given value expected to throw
441+
tracing: false
440442
}
441443
});
442-
});
444+
}, /^config\.kit\.tracing should be undefined or "server"$/);
443445

444-
assert.doesNotThrow(() => {
446+
assert.throws(() => {
445447
validate_config({
446448
kit: {
447-
tracing: undefined
449+
// @ts-expect-error - given value expected to throw
450+
tracing: 'client'
448451
}
449452
});
450-
});
451-
});
453+
}, /^config\.kit\.tracing should be undefined or "server"$/);
452454

453-
test('errors on invalid tracing values', () => {
454455
assert.throws(() => {
455456
validate_config({
456457
kit: {
457458
// @ts-expect-error - given value expected to throw
458459
tracing: 'invalid'
459460
}
460461
});
461-
}, /^config\.kit\.tracing should be true, false, "server", or "client"$/);
462+
}, /^config\.kit\.tracing should be undefined or "server"$/);
462463

463464
assert.throws(() => {
464465
validate_config({
@@ -467,7 +468,7 @@ test('errors on invalid tracing values', () => {
467468
tracing: 42
468469
}
469470
});
470-
}, /^config\.kit\.tracing should be true, false, "server", or "client"$/);
471+
}, /^config\.kit\.tracing should be undefined or "server"$/);
471472

472473
assert.throws(() => {
473474
validate_config({
@@ -476,5 +477,5 @@ test('errors on invalid tracing values', () => {
476477
tracing: null
477478
}
478479
});
479-
}, /^config\.kit\.tracing should be true, false, "server", or "client"$/);
480+
}, /^config\.kit\.tracing should be undefined or "server"$/);
480481
});

packages/kit/src/core/config/options.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -270,10 +270,11 @@ const options = object(
270270
files: fun((filename) => !/\.DS_Store/.test(filename))
271271
}),
272272

273-
tracing: validate(false, (input, keypath) => {
274-
if (typeof input === 'boolean') return input;
275-
if (input === 'server' || input === 'client') return input;
276-
throw new Error(`${keypath} should be true, false, "server", or "client"`);
273+
tracing: validate(undefined, (input, keypath) => {
274+
if (input !== 'server') {
275+
throw new Error(`${keypath} should be undefined or "server"`);
276+
}
277+
return input;
277278
}),
278279

279280
typescript: object({

packages/kit/src/core/sync/write_client_manifest.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,6 @@ export function write_client_manifest(kit, manifest_data, output, metadata) {
174174
175175
export const hash = ${s(kit.router.type === 'hash')};
176176
177-
export const tracing = ${s(kit.tracing === true || kit.tracing === 'client')};
178-
179177
export const decode = (type, value) => decoders[type](value);
180178
181179
export { default as root } from '../root.${isSvelte5Plus() ? 'js' : 'svelte'}';

packages/kit/src/core/sync/write_server.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@ import { set_building, set_prerendering } from '__sveltekit/environment';
3333
import { set_assets } from '__sveltekit/paths';
3434
import { set_manifest, set_read_implementation } from '__sveltekit/server';
3535
import { set_private_env, set_public_env, set_safe_public_env } from '${runtime_directory}/shared-server.js';
36+
import { get_tracer, enable_tracing } from '${runtime_directory}/telemetry/get_tracer.js';
37+
38+
if (${s(config.kit.tracing === 'server')}) {
39+
enable_tracing();
40+
}
3641
3742
export const options = {
3843
app_template_contains_nonce: ${template.includes('%sveltekit.nonce%')},
@@ -60,7 +65,7 @@ export const options = {
6065
.replace(/%sveltekit\.status%/g, '" + status + "')
6166
.replace(/%sveltekit\.error\.message%/g, '" + message + "')}
6267
},
63-
tracing: ${config.kit.tracing === true || config.kit.tracing === 'server'},
68+
tracer: get_tracer(),
6469
version_hash: ${s(hash(config.kit.version.name))}
6570
};
6671

packages/kit/src/exports/hooks/sequence.js

Lines changed: 58 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
/** @import { Handle, RequestEvent, ResolveOptions } from '@sveltejs/kit' */
2+
/** @import { MaybePromise } from 'types' */
3+
import { with_event } from '../../runtime/app/server/event.js';
4+
import { get_tracer } from '../../runtime/telemetry/get_tracer.js';
5+
import { record_span } from '../../runtime/telemetry/record_span.js';
6+
17
/**
28
* A helper function for sequencing multiple `handle` calls in a middleware-like manner.
39
* The behavior for the `handle` options is as follows:
@@ -66,56 +72,76 @@
6672
* first post-processing
6773
* ```
6874
*
69-
* @param {...import('@sveltejs/kit').Handle} handlers The chain of `handle` functions
70-
* @returns {import('@sveltejs/kit').Handle}
75+
* @param {...Handle} handlers The chain of `handle` functions
76+
* @returns {Handle}
7177
*/
7278
export function sequence(...handlers) {
7379
const length = handlers.length;
7480
if (!length) return ({ event, resolve }) => resolve(event);
7581

76-
return ({ event, resolve }) => {
82+
return async ({ event, resolve }) => {
83+
const rootSpan = /** @type {NonNullable<RequestEvent['tracing']>['rootSpan']} */ (
84+
event.tracing?.rootSpan
85+
);
86+
const tracer = await get_tracer();
7787
return apply_handle(0, event, {});
7888

7989
/**
8090
* @param {number} i
81-
* @param {import('@sveltejs/kit').RequestEvent} event
82-
* @param {import('@sveltejs/kit').ResolveOptions | undefined} parent_options
83-
* @returns {import('types').MaybePromise<Response>}
91+
* @param {RequestEvent} event
92+
* @param {ResolveOptions | undefined} parent_options
93+
* @returns {MaybePromise<Response>}
8494
*/
8595
function apply_handle(i, event, parent_options) {
8696
const handle = handlers[i];
8797

88-
return handle({
89-
event,
90-
resolve: (event, options) => {
91-
/** @type {import('@sveltejs/kit').ResolveOptions['transformPageChunk']} */
92-
const transformPageChunk = async ({ html, done }) => {
93-
if (options?.transformPageChunk) {
94-
html = (await options.transformPageChunk({ html, done })) ?? '';
95-
}
98+
return record_span({
99+
tracer,
100+
name: 'sveltekit.handle.child',
101+
attributes: {
102+
'sveltekit.handle.child.index': i
103+
},
104+
fn: async (span) => {
105+
const traced_event = { ...event, tracing: { rootSpan, currentSpan: span } };
106+
return await with_event(traced_event, () =>
107+
handle({
108+
event: traced_event,
109+
resolve: (event, options) => {
110+
/** @type {ResolveOptions['transformPageChunk']} */
111+
const transformPageChunk = async ({ html, done }) => {
112+
if (options?.transformPageChunk) {
113+
html = (await options.transformPageChunk({ html, done })) ?? '';
114+
}
96115

97-
if (parent_options?.transformPageChunk) {
98-
html = (await parent_options.transformPageChunk({ html, done })) ?? '';
99-
}
116+
if (parent_options?.transformPageChunk) {
117+
html = (await parent_options.transformPageChunk({ html, done })) ?? '';
118+
}
100119

101-
return html;
102-
};
120+
return html;
121+
};
103122

104-
/** @type {import('@sveltejs/kit').ResolveOptions['filterSerializedResponseHeaders']} */
105-
const filterSerializedResponseHeaders =
106-
parent_options?.filterSerializedResponseHeaders ??
107-
options?.filterSerializedResponseHeaders;
123+
/** @type {ResolveOptions['filterSerializedResponseHeaders']} */
124+
const filterSerializedResponseHeaders =
125+
parent_options?.filterSerializedResponseHeaders ??
126+
options?.filterSerializedResponseHeaders;
108127

109-
/** @type {import('@sveltejs/kit').ResolveOptions['preload']} */
110-
const preload = parent_options?.preload ?? options?.preload;
128+
/** @type {ResolveOptions['preload']} */
129+
const preload = parent_options?.preload ?? options?.preload;
111130

112-
return i < length - 1
113-
? apply_handle(i + 1, event, {
114-
transformPageChunk,
115-
filterSerializedResponseHeaders,
116-
preload
117-
})
118-
: resolve(event, { transformPageChunk, filterSerializedResponseHeaders, preload });
131+
return i < length - 1
132+
? apply_handle(i + 1, event, {
133+
transformPageChunk,
134+
filterSerializedResponseHeaders,
135+
preload
136+
})
137+
: resolve(event, {
138+
transformPageChunk,
139+
filterSerializedResponseHeaders,
140+
preload
141+
});
142+
}
143+
})
144+
);
119145
}
120146
});
121147
}

packages/kit/src/exports/public.d.ts

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
} from '../types/private.js';
1919
import { BuildData, SSRNodeLoader, SSRRoute, ValidatedConfig } from 'types';
2020
import type { SvelteConfig } from '@sveltejs/vite-plugin-svelte';
21+
import { Span } from '@opentelemetry/api';
2122

2223
export { PrerenderOption } from '../types/private.js';
2324

@@ -687,15 +688,11 @@ export interface KitConfig {
687688
files?(filepath: string): boolean;
688689
};
689690
/**
690-
* Whether to enable OpenTelemetry tracing for SvelteKit operations including handle hooks, load functions, and form actions.
691-
* - `true` - Enable tracing for both server and client
692-
* - `false` - Disable tracing
693-
* - `'server'` - Enable tracing only on the server side
694-
* - `'client'` - Enable tracing only on the client side
695-
* @default false
691+
* Whether to enable serverside OpenTelemetry tracing for SvelteKit operations including handle hooks, load functions, and form actions.
692+
* @default undefined
696693
* @since 2.22.0
697694
*/
698-
tracing?: boolean | 'server' | 'client';
695+
tracing?: 'server';
699696
typescript?: {
700697
/**
701698
* A function that allows you to edit the generated `tsconfig.json`. You can mutate the config (recommended) or return a new one.
@@ -977,6 +974,16 @@ export interface LoadEvent<
977974
* ```
978975
*/
979976
untrack: <T>(fn: () => T) => T;
977+
978+
/**
979+
* Access to spans for tracing. If tracing is not enabled, this will be `undefined`.
980+
*/
981+
tracing?: {
982+
/** The root span for the request. This span is named `sveltekit.handle.root`. */
983+
rootSpan: Span;
984+
/** The span associated with the current `load` function. */
985+
currentSpan: Span;
986+
};
980987
}
981988

982989
export interface NavigationEvent<
@@ -1252,6 +1259,16 @@ export interface RequestEvent<
12521259
* `true` for `+server.js` calls coming from SvelteKit without the overhead of actually making an HTTP request. This happens when you make same-origin `fetch` requests on the server.
12531260
*/
12541261
isSubRequest: boolean;
1262+
1263+
/**
1264+
* Access to spans for tracing. If tracing is not enabled, this will be `undefined`.
1265+
*/
1266+
tracing?: {
1267+
/** The root span for the request. This span is named `sveltekit.handle.root`. */
1268+
rootSpan: Span;
1269+
/** The span associated with the current `handle` hook, `load` function, or server action. */
1270+
currentSpan: Span;
1271+
};
12551272
}
12561273

12571274
/**
@@ -1408,6 +1425,16 @@ export interface ServerLoadEvent<
14081425
* ```
14091426
*/
14101427
untrack: <T>(fn: () => T) => T;
1428+
1429+
/**
1430+
* Access to spans for tracing. If tracing is not enabled, this will be `undefined`.
1431+
*/
1432+
tracing?: {
1433+
/** The root span for the request. This span is named `sveltekit.handle.root`. */
1434+
rootSpan: Span;
1435+
/** The span associated with the current `load` function. */
1436+
currentSpan: Span;
1437+
};
14111438
}
14121439

14131440
/**

0 commit comments

Comments
 (0)