This step-by-step implementation guide walks you through different concepts while helping you add "observability" to your Svelte web application. If you're new to terms like OpenTelemetry, SigNoz, or even what observability means, that is alright - this tutorial is designed for an audience of different knowledge levels.
This tutorial expects you to have basic understanding of Svelte or SvelteKit and JavaScript.
Imagine your Svelte application is a complex machine, like a modern car:
That's essentially what observability brings to your Svelte or any application. It's about understanding and 'tracing' the state and behavior of your app by examining the data it produces, requests it processes and more. This is incredibly helpful for:
We collect different types of data, often called telemetry data:
Traces: These are like a detective following a single user's action through your app. For example, when a user clicks a button to load a new page, a trace shows every step of that journey – the click itself, any data fetching from a server, and the time it took to display the new page. Traces are our main focus in this tutorial.
Metrics: These are numbers measured over time. Think of them as your app's vital signs: how many users are on a page, the average time a page takes to load, or the number of errors happening per minute.
Logs: These are like a detailed diary or journal kept by your app. They are text records of specific events, messages, or errors that happen, like "User [username] logged in successfully at [time]" or "Error: Failed to load image [image_name]."
Think of OpenTelemetry as a universal toolkit or a set of standard "sensors" and "wiring instructions" for your application. It provides a vendor-neutral, open-source way for your app to generate and send out its telemetry data (traces, metrics, logs). Because it's a standard, you're not locked into one specific company's tools. You can use OpenTelemetry to instrument your app once, and then choose where you want to send that valuable data (Spoiler: we will be sending this treasure of data to SigNoz).
SigNoz is an open-source observability platform. It's the "mission control center" or the "advanced diagnostic workshop" that receives, stores, and helps you make sense of all the telemetry data sent by OpenTelemetry from your app. SigNoz provides you with:
This is a separate, highly configurable helper application. Imagine it as a smart, local "data processing and forwarding station" that sits between your app and SigNoz (or any other observability vendor).
It is a best practice to begin with, and here are few other reasons:
In this tutorial, we will:
/health
)/health
pageSvelteKit and all the OpenTelemetry JavaScript libraries require Node.js and its package manager, npm.
Open your computer's Terminal
Applications > Utilities
or search using Spotlight (Cmd + Space
, then type "Terminal")Ctrl + Alt + T
opens a terminalCheck Node.js version:
bash node -v
If installed, you'll see a version number like v18.17.0
or v20.5.0
Check npm version:
bash npm -v
If installed, you'll see a version number like 9.6.7
or 10.1.0
node -v
and npm -v
againSigNoz is where your app's performance data will be sent, stored, and visualized.
https://ingest.<region>.signoz.cloud:443/v1/traces
Store these values securely in a text file:
text SigNoz OTLP Endpoint URL: <paste the URL here> SigNoz Access Token: <paste the token here>
If you have any difficulties finding these values or need more details, refer to:
Open your terminal and navigate to your projects directory:
# macOS/Linux
cd ~/Documents/Projects
# Windows
cd C:\Users\YourUserName\Documents\Projects
Create a new SvelteKit project:
bash npm create svelte@latest demo-otel-app
Follow the setup prompts:
Navigate to your project and install dependencies:
bash cd demo-otel-app npm install
Start the development server:
bash npm run dev
Visit http://localhost:5173/
in your browser to verify the setup
Create a new directory and file:
src/routes/health/+page.svelte
Add the following code to +page.svelte
:
<script>
import { onMount } from 'svelte';
onMount(() => {
console.log('The /health page has loaded in the browser. (OpenTelemetry should trace this page load if active).');
});
</script>
<div>
<h1>Application Health Status</h1>
<p>Status: OK - Everything is running smoothly!</p>
<p>
This page is typically used by automated monitoring systems (like load balancers or uptime checkers)
to verify that the application is responsive and healthy.
</p>
<p>
For our OpenTelemetry setup, we will configure the OpenTelemetry Collector to specifically
<strong>filter out (ignore)</strong> any performance traces generated from visits to this `/health` page.
This helps keep our main performance data clean and focused on real user interactions.
</p>
</div>
<style>
div {
padding: 25px;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
text-align: center;
max-width: 650px;
margin: 50px auto;
border: 1px solid #d3d3d3;
border-radius: 8px;
background-color: #f9f9f9;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
h1 {
color: #2c3e50;
margin-bottom: 20px;
}
p {
color: #34495e;
font-size: 1.1em;
line-height: 1.6;
}
</style>
```
Test the healthcheck page:
npm run dev
http://localhost:5173/health
Ctrl+C
when doneThe Collector will act as our local telemetry agent, receiving data from the Svelte app, filtering it, and forwarding it to SigNoz Cloud.
otelcol-contrib_VERSION_windows_amd64.exe
otelcol-contrib_VERSION_darwin_arm64
otelcol-contrib_VERSION_linux_amd64
Create a dedicated directory for the Collector:
# macOS/Linux
mkdir ~/otel-collector-workspace
# Windows
mkdir C:\OTelCollector
Move the downloaded binary into this directory and rename it to otelcol-contrib
(or otelcol-contrib.exe
for Windows)
Set executable permissions (macOS/Linux only):
chmod +x otelcol-contrib
Create a new file named collector-config.yaml
in your Collector directory
Add the following configuration (replace placeholders with your actual SigNoz credentials):
receivers:
otlp:
protocols:
http:
endpoint: "0.0.0.0:4318"
processors:
batch: {}
filter:
traces:
exclude:
match_type: strict
spans:
- 'attributes["url.path"] == "/health"'
exporters:
otlphttp/signoz:
endpoint: "YOUR_SIGNOZ_OTLP_HTTP_ENDPOINT"
headers:
"signoz-access-token": "YOUR_SIGNOZ_ACCESS_TOKEN"
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch, filter]
exporters: [otlphttp/signoz]
Important: Replace the placeholders:
YOUR_SIGNOZ_OTLP_HTTP_ENDPOINT
with your actual endpointYOUR_SIGNOZ_ACCESS_TOKEN
with your actual tokenOpen a new terminal window for the Collector
Navigate to your Collector directory
Start the Collector:
# macOS/Linux
./otelcol-contrib --config ./collector-config.yaml
# Windows
.\otelcol-contrib.exe --config .\collector-config.yaml
Monitor the terminal output for successful startup
Keep this terminal window open while working with your Svelte app
npm install @opentelemetry/api \
@opentelemetry/resources \
@opentelemetry/semantic-conventions \
@opentelemetry/sdk-trace-web \
@opentelemetry/sdk-trace-base \
@opentelemetry/exporter-trace-otlp-http \
@opentelemetry/instrumentation \
@opentelemetry/auto-instrumentations-web \
@opentelemetry/context-zone
src/lib/otel-init.js
with the following code:import * as otelApi from '@opentelemetry/api';
import * as otelResources from '@opentelemetry/resources';
import * as otelSemanticConventions from '@opentelemetry/semantic-conventions';
import * as otelSdkTraceWeb from '@opentelemetry/sdk-trace-web';
import * as otelExporterTraceOtlpHttp from '@opentelemetry/exporter-trace-otlp-http';
import * as otelInstrumentation from '@opentelemetry/instrumentation';
import * as otelAutoInstWeb from '@opentelemetry/auto-instrumentations-web';
import * as otelContextZone from '@opentelemetry/context-zone';
import * as otelSdkTraceBase from '@opentelemetry/sdk-trace-base';
const { trace: otelApiTrace, DiagConsoleLogger, DiagLogLevel, diag } = otelApi;
const { Resource } = otelResources;
const { SemanticResourceAttributes } = otelSemanticConventions;
const { WebTracerProvider, SimpleSpanProcessor, BatchSpanProcessor } = otelSdkTraceWeb;
const { OTLPTraceExporter } = otelExporterTraceOtlpHttp;
const { registerInstrumentations } = otelInstrumentation;
const { getWebAutoInstrumentations } = otelAutoInstWeb;
const { ZoneContextManager } = otelContextZone;
const { ConsoleSpanExporter } = otelSdkTraceBase;
const COLLECTOR_OTLP_HTTP_ENDPOINT = 'http://localhost:4318/v1/traces';
export function initializeOpenTelemetry(serviceName = 'default-svelte-app') {
console.log(`[OTel Init] Starting OpenTelemetry initialization for service: ${serviceName}`);
try {
// Enable debug logging
diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG);
// Create resource
const resource = Resource.default().merge(
new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: serviceName,
[SemanticResourceAttributes.DEPLOYMENT_ENVIRONMENT]: 'development',
[SemanticResourceAttributes.SERVICE_VERSION]: '1.0.0',
})
);
// Initialize provider
const provider = new WebTracerProvider({ resource });
// Add console exporter for debugging
const consoleExporter = new ConsoleSpanExporter();
provider.addSpanProcessor(new SimpleSpanProcessor(consoleExporter));
// Add OTLP exporter
const otlpExporter = new OTLPTraceExporter({
url: COLLECTOR_OTLP_HTTP_ENDPOINT,
headers: { 'Content-Type': 'application/json' }
});
provider.addSpanProcessor(
new BatchSpanProcessor(otlpExporter, {
scheduledDelayMillis: 1000,
maxQueueSize: 2048,
maxExportBatchSize: 512,
})
);
// Register provider
provider.register({
contextManager: new ZoneContextManager()
});
// Register auto-instrumentations
registerInstrumentations({
instrumentations: [
getWebAutoInstrumentations({
'@opentelemetry/instrumentation-fetch': {
propagateTraceHeaderCorsUrls: [/.+/g],
clearTimingResources: true,
},
'@opentelemetry/instrumentation-document-load': {},
'@opentelemetry/instrumentation-user-interaction': {},
'@opentelemetry/instrumentation-xml-http-request': {}
}),
],
});
console.log(`[OTel Init] OpenTelemetry initialization completed for service: ${serviceName}`);
} catch (error) {
console.error('[OTel Init] Error during initialization:', error);
}
}
src/routes/+layout.svelte
:<script>
import { onMount } from 'svelte';
import { browser } from '$app/environment';
onMount(async () => {
if (browser) {
try {
const otelModule = await import('$lib/otel-init.js');
otelModule.initializeOpenTelemetry('my-svelte-app');
} catch (error) {
console.error('[Layout] Failed to initialize OpenTelemetry:', error);
}
}
});
</script>
<slot />
This section helps you verify if your observability setup is working correctly, from trace generation to visualization in SigNoz.
Clear Vite Cache:
# From your SvelteKit project root
rm -rf .vite
This ensures Vite rebuilds dependencies with the latest changes.
Start Components in Order:
# Terminal 1: Start OpenTelemetry Collector
cd ~/otel-collector-workspace
./otelcol-contrib --config ./collector-config.yaml
# Terminal 2: Start SvelteKit Development Server
cd ~/path/to/your/svelte-app
npm run dev
Open Browser Developer Tools:
http://localhost:5173
Browser Console Verification:
otel-init.js
Network Activity:
http://localhost:4318/v1/traces
Collector Logs:
Service Discovery:
Trace Verification:
# Visit these URLs in your browser
http://localhost:5173/
http://localhost:5173/about
http://localhost:5173/health
/
and /about
/health
traces are filtered outIf /health
traces appear in SigNoz:
Temporarily Disable Filter:
# collector-config.yaml
service:
pipelines:
traces:
processors: [batch] # Remove 'filter' temporarily
Capture Test Trace:
/health
endpointUpdate Filter Rule:
# collector-config.yaml
processors:
filter:
traces:
exclude:
match_type: strict
spans:
- 'attributes["url.path"] == "/health"'
Re-enable and Test:
/health
againNo Traces in SigNoz:
Filter Not Working:
Import Errors:
Client-Side:
// Add to otel-init.js for debugging
diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG);
Collector:
# collector-config.yaml
service:
telemetry:
logs:
level: debug
Network:
Trace List View:
Trace Detail View:
Page Load Traces:
documentLoad
API Call Traces:
fetch
or XMLHttpRequest
spansUser Interaction Traces:
Trace Queries:
Saved Views:
With that, you have successfully instrumented your Svelte application and send the traces to Signoz cloud through OpenTelemetry collector.
If you need help or want to learn more:
Community Support:
Documentation:
Contributing: