Je SaaS draait, klanten stromen binnen, en alles lijkt goed te gaan — tot je op maandagochtend wakker wordt met een inbox vol klachten. "De app is traag." "Ik kan niet inloggen." "Mijn data is weg." Wat ging er mis? Wanneer begon het? Geen idee, want je hebt geen monitoring.
Dit scenario is herkenbaar voor veel SaaS-founders. In dit artikel duiken we diep in monitoring en observability: wat het is, waarom het cruciaal is, en hoe je het praktisch implementeert — zonder dat je er een volledig DevOps-team voor nodig hebt.
Monitoring vs. Observability: het verschil
Monitoring vertelt je dat er iets mis is. Je stelt alerts in voor bekende problemen: CPU boven 90%, response time boven 2 seconden, error rate boven 1%.
Observability vertelt je waarom er iets mis is. Het geeft je de tools om onbekende problemen te onderzoeken door je systeem van binnenuit te begrijpen.
Het verschil is cruciaal. Monitoring is reactief — je controleert wat je verwacht. Observability is proactief — je kunt vragen stellen die je van tevoren niet had bedacht.
De drie pijlers van observability
1. Logs: het verhaal van je applicatie
Logs zijn de meest basale vorm van inzicht. Maar niet alle logs zijn gelijk.
Slecht:
Error occurred
Something went wrong
Goed:
{
"timestamp": "2026-03-09T07:15:23Z",
"level": "error",
"service": "payment-service",
"traceId": "abc-123-def",
"userId": "usr_5k2j1",
"tenantId": "tenant_acme",
"message": "Stripe webhook processing failed",
"error": "Card declined",
"stripeEventId": "evt_1234",
"duration_ms": 342
}
Best practices voor logging in SaaS:
- Structured logging (JSON) — maak logs machine-readable
- Voeg altijd context toe: userId, tenantId, requestId, traceId
- Log op het juiste niveau: DEBUG voor development, INFO voor flow, WARN voor recoverable issues, ERROR voor echte problemen
- Vermijd PII (Personally Identifiable Information) in logs — GDPR!
- Centraliseer je logs — niet SSH'en naar 5 servers om iets uit te zoeken
// Voorbeeld: structured logger met Pino (Node.js)
import pino from 'pino';
const logger = pino({
level: process.env.LOG_LEVEL || 'info',
formatters: {
level: (label) => ({ level: label }),
},
redact: ['req.headers.authorization', 'user.email'],
});
// Gebruik met context
export function createRequestLogger(req: Request) {
return logger.child({
requestId: req.headers['x-request-id'],
tenantId: req.tenantId,
userId: req.userId,
});
}
2. Metrics: de hartslag van je systeem
Metrics zijn numerieke waarden over tijd. Ze vertellen je hoe je systeem presteert op macro-niveau.
De vier golden signals (Google SRE):
- Latency — Hoe lang duren requests? (p50, p95, p99)
- Traffic — Hoeveel requests per seconde?
- Errors — Welk percentage requests faalt?
- Saturation — Hoe vol zijn je resources?
Business metrics die je ook moet tracken:
- Signup conversie per stap
- Time-to-first-value (hoe snel bereikt een gebruiker z'n eerste succes?)
- Feature adoption rates
- API usage per tenant (voor fair-use en upselling)
// Voorbeeld: custom metrics met Prometheus client
import { Counter, Histogram, register } from 'prom-client';
const httpRequestDuration = new Histogram({
name: 'http_request_duration_seconds',
help: 'Duration of HTTP requests in seconds',
labelNames: ['method', 'route', 'status_code', 'tenant'],
buckets: [0.01, 0.05, 0.1, 0.5, 1, 2, 5],
});
const businessEvents = new Counter({
name: 'business_events_total',
help: 'Business event counter',
labelNames: ['event', 'tenant', 'plan'],
});
// In je middleware
app.use((req, res, next) => {
const end = httpRequestDuration.startTimer();
res.on('finish', () => {
end({
method: req.method,
route: req.route?.path || 'unknown',
status_code: res.statusCode,
tenant: req.tenantId,
});
});
next();
});
3. Traces: volg een request door je hele systeem
In een microservices-architectuur (of zelfs een monolith met externe services) gaat één gebruikersactie vaak door meerdere systemen: API → database → cache → externe API → queue → worker.
Distributed tracing laat je dit hele pad volgen.
// Voorbeeld: OpenTelemetry setup (de open standaard)
import { NodeSDK } from '@opentelemetry/sdk-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
const sdk = new NodeSDK({
traceExporter: new OTLPTraceExporter({
url: process.env.OTEL_EXPORTER_ENDPOINT,
}),
instrumentations: [
getNodeAutoInstrumentations({
'@opentelemetry/instrumentation-http': { enabled: true },
'@opentelemetry/instrumentation-pg': { enabled: true },
'@opentelemetry/instrumentation-redis': { enabled: true },
}),
],
});
sdk.start();
Met OpenTelemetry krijg je automatisch traces voor HTTP-requests, database queries, en Redis-calls. Je ziet precies waar de tijd verloren gaat.
De praktische stack: wat gebruik je?
Je hoeft geen Datadog-contract van €50.000/jaar af te sluiten. Er zijn uitstekende opties voor elke fase:
Bootstrap / Early Stage (€0-50/maand)
- Logs: Axiom (gratis tier), Logtail, of Grafana Cloud
- Uptime: BetterStack, UptimeRobot
- Error tracking: Sentry (gratis voor kleine teams)
- Metrics: Grafana Cloud free tier
Growth Stage (€100-500/maand)
- All-in-one: Grafana Cloud, Datadog Essentials
- APM: New Relic (generous free tier), Elastic APM
- Tracing: Jaeger (self-hosted) of Grafana Tempo
Scale Stage
- Full observability: Datadog, Grafana Enterprise, Splunk
- Custom dashboards per tenant voor enterprise klanten
Alerts: de kunst van niet-irriteren
De grootste fout bij monitoring? Te veel alerts. Alert fatigue is echt — als alles urgent is, is niets urgent.
Goede alert-strategie:
# Voorbeeld: alert levels
critical: # Bel me wakker (PagerDuty/Opsgenie)
- Error rate > 5% voor 5 minuten
- Geen successful health checks voor 2 minuten
- Database connection pool uitgeput
- Betaalverwerking faalt
warning: # Slack notificatie
- Response time p95 > 2s voor 10 minuten
- Disk usage > 80%
- Memory usage > 85%
- Queue backlog > 1000 items
info: # Dashboard only
- Deployment completed
- Nieuwe tenant aangemeld
- Daily report
SLOs (Service Level Objectives) als basis:
Definieer wat "goed genoeg" is voordat je alerts instelt:
- 99.9% uptime = max 43 minuten downtime per maand
- p95 latency < 500ms
- Error rate < 0.1%
Multi-tenant monitoring: de SaaS-specifieke uitdaging
In een multi-tenant SaaS moet je niet alleen je systeem als geheel monitoren, maar ook per tenant. Eén "noisy neighbor" kan je hele platform beïnvloeden.
// Middleware: track per-tenant resource usage
app.use(async (req, res, next) => {
const tenantId = req.tenantId;
const start = performance.now();
res.on('finish', () => {
const duration = performance.now() - start;
// Track per tenant
metrics.tenantRequestDuration
.labels(tenantId)
.observe(duration / 1000);
// Detect noisy neighbors
if (duration > SLOW_REQUEST_THRESHOLD) {
logger.warn({
tenantId,
duration,
route: req.route?.path,
message: 'Slow request detected — possible noisy neighbor',
});
}
});
next();
});
Wat je per tenant wilt weten:
- Request volume en patronen
- Error rates
- Storage en bandwidth gebruik
- API quota verbruik
- Kosten per tenant (voor je eigen margeberekening)
Health checks: meer dan een ping
Een goede health check controleert niet alleen of je app draait, maar of hij functioneel is.
// Uitgebreide health check endpoint
app.get('/health', async (req, res) => {
const checks = {
database: await checkDatabase(),
redis: await checkRedis(),
stripe: await checkStripeAPI(),
storage: await checkS3(),
queue: await checkQueueConnection(),
};
const healthy = Object.values(checks).every(c => c.status === 'ok');
res.status(healthy ? 200 : 503).json({
status: healthy ? 'healthy' : 'degraded',
timestamp: new Date().toISOString(),
version: process.env.APP_VERSION,
checks,
});
});
async function checkDatabase() {
try {
const start = Date.now();
await prisma.$queryRaw\`SELECT 1\`;
return { status: 'ok', latency_ms: Date.now() - start };
} catch (error) {
return { status: 'error', message: error.message };
}
}
Stappenplan: van nul naar observability
Week 1: De basis
- Implementeer structured logging (Pino/Winston)
- Voeg request IDs toe aan alle logs
- Zet Sentry op voor error tracking
- Configureer een uptime monitor
Week 2: Metrics
- Voeg de vier golden signals toe
- Maak een basis-dashboard (Grafana)
- Stel 3-5 kritische alerts in
- Begin met per-tenant tracking
Week 3: Verdieping
- Implementeer OpenTelemetry voor tracing
- Voeg business metrics toe
- Maak een health check endpoint
- Definieer je SLOs
Week 4: Cultuur
- Documenteer je runbooks (wat doe je bij alert X?)
- Train je team op de dashboards
- Plan een maandelijkse review van je alerts
- Verwijder alerts die niemand actie op onderneemt
Conclusie
Monitoring en observability zijn geen luxe — ze zijn een vereiste voor elke serieuze SaaS. Het verschil tussen een SaaS die schaalt en een die crasht onder druk, zit vaak niet in de code, maar in hoeveel je ziet van wat er gebeurt.
Begin klein: structured logging, error tracking, en uptime monitoring. Bouw van daaruit op naar volledige observability. Je toekomstige zelf — die om 3 uur 's nachts een productie-incident moet oplossen — zal je dankbaar zijn.
De kosten van goede monitoring zijn altijd lager dan de kosten van een outage die je niet zag aankomen.