Har du også først opdaget en bug, når brugerne skrev på Facebook?
Har du nogensinde siddet en søndag aften og opdaget via en vred besked, at din app har været nede hele dagen?
1. Hvad prøver du egentlig at opdage?
Hvis du tænker “monitoring” og straks går i gang med at installere fem dashboards, så hopper du et trin over.
Start med et simpelt mål:
- Opdag når brugere får fejl
- Opdag når appen er langsom nok til at irritere
- Opdag når noget er helt dødt (down)
Du behøver ikke følge alle CPU-kerner, RAM, disk, pod-antal og alt det der. En lille webapp kan komme ret langt med meget få signaler.
Jeg tænker typisk i tre typer data:
- Logs – hvad er der sket?
- Metrics – hvor ofte / hvor hurtigt / hvor slemt?
- Alerts – hvornår skal du rent faktisk vækkes?
Det er også grundstrukturen bag observability: du vil kunne svare på spørgsmål om din app uden at skulle gætte.
2. Logs: de 5 ting du skal have med i hver request
Logs er din tidsmaskine. Hvis noget gik galt kl. 13:07, er det her, du læser historien bagefter.
Til en lille webapp vil jeg altid prøve at få de her felter med i mine request-logs:
- timestamp (hvornår skete det)
- request_id (unik ID for hver request)
- path + metode (“GET /api/orders”)
- status code (200, 400, 500 osv.)
- latency (hvor lang tid tog det i ms)
Hvis du har login i din app, er det også guld at have en eller anden form for anonymiseret bruger-reference, fx user_id eller account_id.
Structured logging vs “bare console.log”
Hvis alle dine logs ser sådan her ud:
console.log("Noget gik galt");
… så får du det hårdt, når du endelig skal finde fejlen.
Structured logging betyder bare, at du logger data som felter, typisk som JSON, i stedet for som fritekst. For eksempel i Node/Express:
app.use(async (req, res, next) => {
const start = Date.now();
const requestId = crypto.randomUUID();
res.on("finish", () => {
const durationMs = Date.now() - start;
console.log(JSON.stringify({
level: "info",
timestamp: new Date().toISOString(),
request_id: requestId,
method: req.method,
path: req.originalUrl,
status: res.statusCode,
duration_ms: durationMs
}));
});
next();
});
Nu kan du senere søge i logs efter "status":500 eller alle requests over 1000 ms.
HTTP status codes er dit sprog her. Hvis du ikke er helt tryg ved dem endnu, er MDN’s oversigt ret god som reference.
Typisk fejl: log alt for meget og alt for tilfældigt
De fleste starter sådan her:
- Logger alt i hele koden
- Blander flere sprog i beskederne
- Ingen IDs, ingen struktur
Resultat: du drukner i tekst, men mangler stadig det ene felt, du faktisk skulle bruge.
Hvis du kun gør én ting med dine logs, så sørg for at request-logs er strukturerede og konsistente. Ekstra debug-logs kan du altid smide ind lokalt.
3. Metrics: 4 tal der giver dig 80 % af overblikket
Metrics er tal, der kan tegnes som grafer. Du kan godt begynde simpelt, uden Prometheus og Kubernetes-klynge.
Jeg ville starte med de her fire:
- Error rate – procentdelen af requests der ender i 5xx
- Latency – hvor lang tid en typisk request tager
- Throughput – hvor mange requests pr. minut
- Saturation – “er vi ved at løbe tør for ressourcer?”
De tre første kan du ofte få bare ved at tælle på dine logs.
Error rate: hvor mange 5xx-fejl har du?
En simpel variant kan være:
- tæl alle requests
- tæl alle der har status mellem 500 og 599
- error_rate = errors / total_requests
Hvis du bruger en host som Vercel, Render eller Railway, har de ofte simple grafer for det her. Ellers kan du skubbe metrics til fx Grafana Cloud eller tilsvarende.
Latency: ikke bare gennemsnit
Gennemsnit kan snyde. Hvis 90 % af dine requests tager 100 ms og 10 % tager 10 sekunder, ser gennemsnittet bare lidt kedeligt ud.
Hvis værktøjet understøtter det, så kig efter percentiler:
- p50 – den typiske oplevelse
- p95 – de langsomme, som brugerne begynder at bemærke
Hvis ikke, kan du nøjes med at måle “andel af requests over X ms” som en metrik.
Throughput og saturation
Throughput er bare “hvor travlt har vi”. Det er mest nyttigt, når du kigger sammen med de andre tal.
Saturation er lidt mere afhængig af hosting:
- På en simpel VPS: CPU- og RAM-usage
- På en serverless-funktion: hvor mange invocations vs. limits
- På en delt PaaS: eventuelle throttling-metrics
Du behøver ikke nørde alle detaljer i starten. Du vil bare gerne kunne se: “Var maskinen ved at koge over, da fejlen skete?”.
Hvis du er nysgerrig på mere struktur omkring de her ting, er Grafanas docs faktisk overraskende begyndervenlige, også selv om du ikke bruger Grafana selv.
4. Alerts: hvornår giver det mening at blive forstyrret?
Alerts er der, hvor mange går galt. Enten er der ingen, eller også er der 40, som alle støjer hele tiden.
En nyttig alarm har to egenskaber:
- Den betyder, at rigtige brugere har et problem
- Den betyder, at du bør gøre noget nu, ikke om tre dage
Tre alarmer jeg ville starte med
- Error rate for 5xx er høj i et stykke tid
Fx: “mere end 2 % 5xx over 10 minutter”. - Median eller p95 latency hopper op
Fx: “p95 > 2000 ms i 10 minutter”. - Health check fejler
Et simpelt endpoint, fx/health, som tjekker at appen svarer.
Det kan du typisk sætte op i de fleste APM/monitoring-værktøjer eller via din host.
Hvornår en alarm ikke er værd at have
- “CPU over 70 %” i 30 sekunder
- “En enkelt 500-fejl”
- “Latency over 500 ms én gang”
Sådan noget er fint at kunne se på et dashboard, men det skal ikke vække dig.
En tommelfingerregel jeg bruger: hvis du realistisk set ikke ville gøre noget kl. 03:00 om natten, så skal den alarm heller ikke til Slack/telefon.
5. Fejltracking: hvornår giver Sentry og venner mening?
Logs og metrics giver overblik. Fejltracking-værktøjer som Sentry giver dig detaljer om den enkelte fejl.
De er særligt gode til:
- Stack traces (hvilken linje i koden fejlede)
- Context (hvilken user, hvilken browser, hvilken route)
- Grouping (samle 1000 ens fejl til ét issue)
For en lille Node/React-app kan et Sentry-setup se nogenlunde sådan her i backend:
import * as Sentry from "@sentry/node";
Sentry.init({
dsn: process.env.SENTRY_DSN,
environment: process.env.NODE_ENV || "development"
});
app.use(Sentry.Handlers.requestHandler());
// ... dine routes her ...
app.use(Sentry.Handlers.errorHandler());
Og i frontend, fx React:
import * as Sentry from "@sentry/react";
Sentry.init({
dsn: import.meta.env.VITE_SENTRY_DSN,
tracesSampleRate: 0.1
});
Nu får du automatisk besked, når der sker en uncaught exception i browseren eller i din server.
Hvornår er det overkill?
Hvis du lige har bygget din første simple side, og der næsten ingen brugere er, kan du sagtens vente.
Jeg plejer at sige: når du har rigtige brugere, som ikke lige kan skrive til dig på Messenger, så er det tid til at få sat en eller anden form for fejltracking på.
På Coding Class har vi også eksempler på simple fejl-logs og basic debugging, fx i artikler som om API-fejl og statuskoder, hvis du vil bygge din forståelse langsomt op.
6. En lille monitoring-plan til en simpel webapp
Nu til det, der faktisk gør forskellen: skriv din plan ned. Gerne på én side.
Her er et eksempel for en lille “to-do” webapp med Node backend og React frontend:
Logs
- Request logs i JSON med felterne:
timestamp,request_id,method,path,status,duration_ms,user_id(hvis logget ind)
- Error logs med:
- stack trace
- request_id
- bruger- og endpoint-info
Metrics
- http_requests_total (labels: status, path)
- http_request_duration_ms (histogram eller buckets: <100ms, <500ms, <2000ms, >2000ms)
- error_rate beregnet fra antal 5xx
- cpu_usage og memory_usage hvis du har adgang til det
Alerts
- Error rate > 2 % over 10 min → Slack-kanal “#alerts”
- p95 latency > 2000 ms over 15 min → samme Slack-kanal
- Health check fejler 3 gange i træk → email til udvikler
Fejltracking
- Sentry på backend for unhandled exceptions
- Stryg-bare setup i frontend for runtime-fejl i produktion
Hele planen kan ligge som MONITORING.md i repoet, sammen med fx din DEPLOYMENT.md. Hvis du har læst artiklen om monorepos her på sitet, er idéen lidt den samme: skriv ned, hvordan du forventer tingene opfører sig, før det går galt.
7. Tjekliste: før du shipper til produktion
Som lovet, en lille “før du trykker deploy”-tjekliste. Tænk den som monitoring-versionen af “har du husket bukser”.
- Har du structured logs for requests?
Ikke bare “console.log(‘her’)” overalt, men noget du kan søge i. - Logger du 5xx-fejl med stack trace og request-id?
Så du kan finde præcis den request, der fejlede. - Har du mindst én metric for error rate?
Uanset om den kommer fra hosten eller noget du selv tæller. - Har du en idé om latency?
Ikke nødvendigvis perfekt, men et eller andet tal for hvor hurtig din app er. - Har du et health endpoint?
Fx/healthder returnerer 200 når appen kører. - Har du sat 1-3 alerts op, der handler om rigtige brugere?
Ikke CPU-flimren, men fejl og langsomhed. - Har du en plan for fejltracking?
Om det så bare er “vi installerer Sentry, når vi får de første 100 brugere”. - Ligger din monitoring-plan et sted i repoet?
FxMONITORING.mdmed de vigtigste signaler og alerts.
Hvis du vil bygge videre på det her, hænger det meget godt sammen med andre DevOps-ting, vi allerede har skrevet om, fx performance og deploy-fejl. Start småt, men skriv dine valg ned. Det gør det senere meget nemmere at forbedre tingene i stedet for at starte forfra hver gang.
Hvis du kun gør én ting anderledes efter at have læst det her, så få lavet strukturerede request-logs med statuskoder, varighed og et request-id, før du shipper næste gang.








1 kommentar