Skip to content

Commit b504608

Browse files
feat: Add tracing to load, server actions, and handle/resolve
1 parent cc0eef5 commit b504608

File tree

13 files changed

+276
-129
lines changed

13 files changed

+276
-129
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ const get_defaults = (prefix = '') => ({
101101
serviceWorker: {
102102
register: true
103103
},
104+
tracing: false,
104105
typescript: {},
105106
paths: {
106107
base: '',

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

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

273+
tracing: boolean(false),
274+
273275
typescript: object({
274276
config: fun((config) => config)
275277
}),

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,8 @@ export function write_client_manifest(kit, manifest_data, output, metadata) {
175175
176176
export const hash = ${s(kit.router.type === 'hash')};
177177
178+
export const tracing = ${s(kit.tracing)};
179+
178180
export const decode = (type, value) => decoders[type](value);
179181
180182
export { default as root } from '../root.${isSvelte5Plus() ? 'js' : 'svelte'}';

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export const options = {
6060
.replace(/%sveltekit\.status%/g, '" + status + "')
6161
.replace(/%sveltekit\.error\.message%/g, '" + message + "')}
6262
},
63+
tracing: ${config.kit.tracing},
6364
version_hash: ${s(hash(config.kit.version.name))}
6465
};
6566

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,12 @@ export interface KitConfig {
694694
*/
695695
files?(filepath: string): boolean;
696696
};
697+
/**
698+
* Whether to enable OpenTelemetry tracing for SvelteKit operations including handle hooks, load functions, and form actions.
699+
* @default false
700+
* @since 2.22.0
701+
*/
702+
tracing?: boolean;
697703
typescript?: {
698704
/**
699705
* A function that allows you to edit the generated `tsconfig.json`. You can mutate the config (recommended) or return a new one.

packages/kit/src/runtime/client/client.js

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ import { get_message, get_status } from '../../utils/error.js';
4141
import { writable } from 'svelte/store';
4242
import { page, update, navigating } from './state.svelte.js';
4343
import { add_data_suffix, add_resolution_suffix } from '../pathname.js';
44+
import { record_span } from '../server/telemetry/record-span.js';
45+
import { get_tracer } from '../server/telemetry/get-tracer.js';
4446

4547
export { load_css };
4648

@@ -753,13 +755,30 @@ async function load_node({ loader, parent, url, params, route, server_data_node
753755
}
754756
};
755757

758+
async function traced_load() {
759+
const tracer = get_tracer({ is_enabled: app.tracing });
760+
761+
return record_span({
762+
name: 'sveltekit.load.universal',
763+
tracer,
764+
attributes: {
765+
'sveltekit.load.node_id': node.universal_id || 'unknown',
766+
'sveltekit.load.type': 'universal',
767+
'sveltekit.load.environment': 'client',
768+
'sveltekit.route.id': route.id || 'unknown'
769+
},
770+
fn: async () => (await node.universal?.load?.call(null, load_input)) ?? null
771+
});
772+
}
773+
756774
if (DEV) {
757775
try {
758776
lock_fetch();
759-
data = (await node.universal.load.call(null, load_input)) ?? null;
777+
data = await traced_load();
778+
760779
if (data != null && Object.getPrototypeOf(data) !== Object.prototype) {
761780
throw new Error(
762-
`a load function related to route '${route.id}' returned ${
781+
`the load function located in ${node.universal_id} returned ${
763782
typeof data !== 'object'
764783
? `a ${typeof data}`
765784
: data instanceof Response
@@ -774,7 +793,7 @@ async function load_node({ loader, parent, url, params, route, server_data_node
774793
unlock_fetch();
775794
}
776795
} else {
777-
data = (await node.universal.load.call(null, load_input)) ?? null;
796+
data = await traced_load();
778797
}
779798
}
780799

packages/kit/src/runtime/client/types.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ export interface SvelteKitApp {
5656
*/
5757
hash: boolean;
5858

59+
/**
60+
* Whether OpenTelemetry tracing is enabled
61+
*/
62+
tracing: boolean;
63+
5964
root: typeof SvelteComponent;
6065
}
6166

packages/kit/src/runtime/server/page/actions.js

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { is_form_content_type, negotiate } from '../../../utils/http.js';
66
import { HttpError, Redirect, ActionFailure, SvelteKitError } from '../../control.js';
77
import { handle_error_and_jsonify } from '../utils.js';
88
import { with_event } from '../../app/server/event.js';
9+
import { record_span } from '../telemetry/record-span.js';
10+
import { get_tracer } from '../telemetry/get-tracer.js';
911

1012
/** @param {import('@sveltejs/kit').RequestEvent} event */
1113
export function is_action_json_request(event) {
@@ -51,7 +53,7 @@ export async function handle_action_json_request(event, options, server) {
5153
check_named_default_separate(actions);
5254

5355
try {
54-
const data = await call_action(event, actions);
56+
const data = await call_action(event, actions, options.tracing);
5557

5658
if (__SVELTEKIT_DEV__) {
5759
validate_action_return(data);
@@ -139,9 +141,10 @@ export function is_action_request(event) {
139141
/**
140142
* @param {import('@sveltejs/kit').RequestEvent} event
141143
* @param {import('types').SSRNode['server'] | undefined} server
144+
* @param {boolean} tracing
142145
* @returns {Promise<import('@sveltejs/kit').ActionResult>}
143146
*/
144-
export async function handle_action_request(event, server) {
147+
export async function handle_action_request(event, server, tracing) {
145148
const actions = server?.actions;
146149

147150
if (!actions) {
@@ -164,7 +167,7 @@ export async function handle_action_request(event, server) {
164167
check_named_default_separate(actions);
165168

166169
try {
167-
const data = await call_action(event, actions);
170+
const data = await call_action(event, actions, tracing);
168171

169172
if (__SVELTEKIT_DEV__) {
170173
validate_action_return(data);
@@ -216,9 +219,10 @@ function check_named_default_separate(actions) {
216219
/**
217220
* @param {import('@sveltejs/kit').RequestEvent} event
218221
* @param {NonNullable<import('types').ServerNode['actions']>} actions
222+
* @param {boolean} tracing
219223
* @throws {Redirect | HttpError | SvelteKitError | Error}
220224
*/
221-
async function call_action(event, actions) {
225+
async function call_action(event, actions, tracing) {
222226
const url = new URL(event.request.url);
223227

224228
let name = 'default';
@@ -247,7 +251,30 @@ async function call_action(event, actions) {
247251
);
248252
}
249253

250-
return with_event(event, () => action(event));
254+
const tracer = get_tracer({ is_enabled: tracing });
255+
256+
return record_span({
257+
name: 'sveltekit.action',
258+
tracer,
259+
attributes: {
260+
'sveltekit.action.name': name,
261+
'sveltekit.route.id': event.route.id || 'unknown'
262+
},
263+
fn: async (action_span) => {
264+
const result = await with_event(event, () => action(event));
265+
if (result instanceof ActionFailure) {
266+
action_span.setAttributes({
267+
'sveltekit.action.result.type': 'failure',
268+
'sveltekit.action.result.status': result.status
269+
});
270+
} else {
271+
action_span.setAttributes({
272+
'sveltekit.action.result.type': 'success'
273+
});
274+
}
275+
return result;
276+
}
277+
});
251278
}
252279

253280
/** @param {any} data */

packages/kit/src/runtime/server/page/index.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export async function render_page(event, page, options, manifest, state, nodes,
5656
if (is_action_request(event)) {
5757
// for action requests, first call handler in +page.server.js
5858
// (this also determines status code)
59-
action_result = await handle_action_request(event, leaf_node.server);
59+
action_result = await handle_action_request(event, leaf_node.server, options.tracing);
6060
if (action_result?.type === 'redirect') {
6161
return redirect_response(action_result.status, action_result.location);
6262
}
@@ -166,7 +166,8 @@ export async function render_page(event, page, options, manifest, state, nodes,
166166
if (parent) Object.assign(data, parent.data);
167167
}
168168
return data;
169-
}
169+
},
170+
tracing: options.tracing
170171
});
171172
} catch (e) {
172173
load_error = /** @type {Error} */ (e);
@@ -194,7 +195,8 @@ export async function render_page(event, page, options, manifest, state, nodes,
194195
resolve_opts,
195196
server_data_promise: server_promises[i],
196197
state,
197-
csr
198+
csr,
199+
tracing: options.tracing
198200
});
199201
} catch (e) {
200202
load_error = /** @type {Error} */ (e);

0 commit comments

Comments
 (0)