Skip to content

Commit f635678

Browse files
feat: OpenTelemetry Tracing (#13899)
Co-authored-by: Lukas Stracke <[email protected]>
1 parent c261f4d commit f635678

File tree

81 files changed

+3115
-440
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

81 files changed

+3115
-440
lines changed

.changeset/early-taxis-make.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sveltejs/kit': minor
3+
---
4+
5+
feat: OpenTelemetry tracing for `handle`, `sequence`, form actions, remote functions, and `load` functions running on the server

.changeset/whole-bananas-sort.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
'@sveltejs/adapter-cloudflare': minor
3+
'@sveltejs/adapter-netlify': minor
4+
'@sveltejs/adapter-vercel': minor
5+
'@sveltejs/adapter-node': minor
6+
'@sveltejs/kit': minor
7+
---
8+
9+
feat: add `instrumentation.server.ts` for tracing and observability setup

documentation/docs/10-getting-started/30-project-structure.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ my-project/
1919
│ ├ error.html
2020
│ ├ hooks.client.js
2121
│ ├ hooks.server.js
22-
│ └ service-worker.js
22+
| ├ service-worker.js
23+
│ └ tracing.server.js
2324
├ static/
2425
│ └ [your static assets]
2526
├ tests/
@@ -54,6 +55,8 @@ The `src` directory contains the meat of your project. Everything except `src/ro
5455
- `hooks.client.js` contains your client [hooks](hooks)
5556
- `hooks.server.js` contains your server [hooks](hooks)
5657
- `service-worker.js` contains your [service worker](service-workers)
58+
- `instrumentation.server.js` contains your [observability](observability) setup and instrumentation code
59+
- Requires adapter support. If your adapter supports it, it is guarnteed to run prior to loading and running your application code.
5760

5861
(Whether the project contains `.js` or `.ts` files depends on whether you opt to use TypeScript when you create your project.)
5962

documentation/docs/25-build-and-deploy/99-writing-adapters.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ export default function (options) {
3434
// Return `true` if the route with the given `config` can use `read`
3535
// from `$app/server` in production, return `false` if it can't.
3636
// Or throw a descriptive error describing how to configure the deployment
37+
},
38+
tracing: () => {
39+
// Return `true` if this adapter supports loading `tracing.server.js`.
40+
// Return `false if it can't, or throw a descriptive error.
3741
}
3842
}
3943
};
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
---
2+
title: Observability
3+
---
4+
5+
<blockquote class="since note">
6+
<p>Available since 2.29</p>
7+
</blockquote>
8+
9+
> [!NOTE] This feature is experimental. Expect bugs and breaking changes in minor versions (though we'll do our best to keep those to an absolute minimum). Please provide feedback!
10+
11+
Sometimes, you may need to observe how your application is behaving in order to improve performance or find the root cause of a pesky bug. To help with this, SvelteKit can emit server-side [OpenTelemetry](https://opentelemetry.io) spans for the following:
12+
13+
- [`handle`](hooks#Server-hooks-handle) hook (`handle` functions running in a [`sequence`](@sveltejs-kit-hooks#sequence) will show up as children of each other and the root handle hook)
14+
- [`load`](load) functions (includes universal `load` functions when they're run on the server)
15+
- [Form actions](form-actions)
16+
- [Remote functions](remote-functions)
17+
18+
Just telling SvelteKit to emit spans won't get you far, though — you need to actually collect them somewhere to be able to view them. SvelteKit provides `src/instrumentation.server.ts` as a place to write your tracing setup and instrumentation code. It's guaranteed to be run prior to your application code being imported, providing your deployment platform supports it and your adapter is aware of it.
19+
20+
To enable both of these features, add the following to your `svelte.config.js`:
21+
22+
```js
23+
/// file: svelte.config.js
24+
export default {
25+
kit: {
26+
+++experimental: {
27+
tracing: {
28+
server: true
29+
},
30+
instrumentation: {
31+
server: true
32+
}
33+
}+++
34+
}
35+
};
36+
```
37+
38+
> [!NOTE] Tracing — and more significantly, observability instrumentation — can have a nontrivial overhead. Before you go all-in on tracing, consider whether or not you really need it, or if it might be more appropriate to turn it on in development and preview environments only.
39+
40+
## Agumenting SvelteKit's builtin tracing
41+
42+
SvelteKit provides access to the `root` span and the `current` span on the request event. The root span is the one associated with your root `handle` function, and the current span could be associated with `handle`, `load`, a form action, or a remote function, depending on the context. You can annotate these spans with any attributes you wish to record:
43+
44+
```js
45+
/// file: $lib/authenticate.ts
46+
async function authenticate() {
47+
const event = getRequestEvent();
48+
const user = await getAuthenticatedUser(event);
49+
event.tracing.root.setAttribute('userId', user.id);
50+
}
51+
```
52+
53+
## Development quickstart
54+
55+
To view your first trace, you'll need to set up a local collector. We'll use [Jaeger](https://www.jaegertracing.io/docs/getting-started/) in this example, as they provide an easy-to-use quickstart command. Once your collector is running locally:
56+
57+
- Turn on the experimental flag mentioned above in your `svelte.config.js` file
58+
- Use your package manager to install the dependencies you'll need
59+
```sh
60+
npm i @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node @opentelemetry/exporter-trace-oltp-proto import-in-the-middle
61+
```
62+
- Create `src/instrumentation.server.ts` with the following:
63+
64+
```ts
65+
import { NodeSDK } from '@opentelemetry/sdk-node';
66+
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
67+
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';
68+
import { createAddHookMessageChannel } from 'import-in-the-middle';
69+
import { register } from 'module';
70+
71+
const { registerOptions } = createAddHookMessageChannel();
72+
register('import-in-the-middle/hook.mjs', import.meta.url, registerOptions);
73+
74+
const sdk = new NodeSDK({
75+
serviceName: 'test-sveltekit-tracing',
76+
traceExporter: new OTLPTraceExporter(),
77+
instrumentations: [getNodeAutoInstrumentations()]
78+
});
79+
80+
sdk.start();
81+
```
82+
83+
Any server-side requests will now begin generating traces, which you can view in Jaeger's web console at [localhost:16686](http://localhost:16686).

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"@parcel/watcher",
4141
"esbuild",
4242
"netlify-cli",
43+
"protobufjs",
4344
"rolldown",
4445
"sharp",
4546
"svelte-preprocess",

packages/adapter-auto/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ export default () => ({
152152
supports_error(
153153
'The read function imported from $app/server only works in certain environments'
154154
);
155+
},
156+
instrumentation: () => {
157+
supports_error('`instrumentation.server.js` only works in certain environments');
155158
}
156159
}
157160
});

packages/adapter-auto/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
"devDependencies": {
4343
"@sveltejs/kit": "workspace:^",
4444
"@sveltejs/vite-plugin-svelte": "catalog:",
45-
"@types/node": "^18.19.119",
45+
"@types/node": "catalog:",
4646
"typescript": "^5.3.3",
4747
"vitest": "catalog:"
4848
},

packages/adapter-cloudflare/index.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,12 @@ export default function (options = {}) {
113113
ASSETS: assets_binding
114114
}
115115
});
116+
if (builder.hasServerInstrumentationFile()) {
117+
builder.instrument({
118+
entrypoint: worker_dest,
119+
instrumentation: `${builder.getServerDirectory()}/instrumentation.server.js`
120+
});
121+
}
116122

117123
// _headers
118124
if (existsSync('_headers')) {
@@ -184,7 +190,8 @@ export default function (options = {}) {
184190
}
185191

186192
return true;
187-
}
193+
},
194+
instrumentation: () => true
188195
}
189196
};
190197
}

packages/adapter-cloudflare/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
"devDependencies": {
5151
"@playwright/test": "catalog:",
5252
"@sveltejs/kit": "workspace:^",
53-
"@types/node": "^18.19.119",
53+
"@types/node": "catalog:",
5454
"esbuild": "^0.25.4",
5555
"typescript": "^5.3.3",
5656
"vitest": "catalog:"

0 commit comments

Comments
 (0)