Har du også først opdaget en bug, når brugerne skrev på Facebook?

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:

  1. timestamp (hvornår skete det)
  2. request_id (unik ID for hver request)
  3. path + metode (“GET /api/orders”)
  4. status code (200, 400, 500 osv.)
  5. 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:

  1. Error rate – procentdelen af requests der ender i 5xx
  2. Latency – hvor lang tid en typisk request tager
  3. Throughput – hvor mange requests pr. minut
  4. 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

  1. Error rate for 5xx er høj i et stykke tid
    Fx: “mere end 2 % 5xx over 10 minutter”.
  2. Median eller p95 latency hopper op
    Fx: “p95 > 2000 ms i 10 minutter”.
  3. 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”.

  1. Har du structured logs for requests?
    Ikke bare “console.log(‘her’)” overalt, men noget du kan søge i.
  2. Logger du 5xx-fejl med stack trace og request-id?
    Så du kan finde præcis den request, der fejlede.
  3. Har du mindst én metric for error rate?
    Uanset om den kommer fra hosten eller noget du selv tæller.
  4. Har du en idé om latency?
    Ikke nødvendigvis perfekt, men et eller andet tal for hvor hurtig din app er.
  5. Har du et health endpoint?
    Fx /health der returnerer 200 når appen kører.
  6. Har du sat 1-3 alerts op, der handler om rigtige brugere?
    Ikke CPU-flimren, men fejl og langsomhed.
  7. Har du en plan for fejltracking?
    Om det så bare er “vi installerer Sentry, når vi får de første 100 brugere”.
  8. Ligger din monitoring-plan et sted i repoet?
    Fx MONITORING.md med 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.

Start med at måle baseline i en uge for p50/p95/p99 latency og normal error rate. Brug flere niveauer - warn (fx p95-stigning +2x) til chat/kanal og critical (fejlrate over X% i Y minutter eller total downtime) til telefon/pager. Juster thresholds efter trafikmønstre, undgå single-request alerts og tilføj reduceringslogik som sliding windows og deduplikation.
For metrics og alerts kan du starte med Prometheus + Grafana eller en hosted Grafana Cloud; for strukturerede logs er Grafana Loki eller Logflare/ Papertrail gode, billige valg; til fejlsporing og performance er Sentry en effektiv SaaS med gratis niveau. Kombiner et par værktøjer i starten i stedet for at købe en stor platform med det samme.
Log aldrig rå PII som fulde e-mailadresser eller CPR-numre; gem i stedet en pseudonymiseret user_id eller en HMAC-hash med en hemmelig salt, så du kan korrelere internt uden at eksponere data. Implementer retention-politikker og filtrer følsomme felter før logningspipeline, og dokumenter hvem der kan dekryptere hvis nødvendigt.
Brug syntetiske tests og rehearse-alarmer i staging: injicer fejl (500), simuler langsomme svar og øg trafik for at se om metrics rammer thresholds. Bekræft også alert-kanaler (email, Slack, pager) og gennemfør et par tabletop-øvelser med et simpelt runbook for at sikre, at opkald og eskalation fungerer.

Lasse Falkenberg er typen, der begyndte at rode med HTML og CSS for at lave en simpel bandside – og opdagede, at det var langt sjovere at få knapperne til at virke end at stå på scenen. Siden har han kastet sig over alt fra små JavaScript-snippets til Python-scripts, der kan spare ham for kedeligt, manuelt arbejde i hverdagen.

Han har lært det meste ved at bygge ting, der lige præcis løser hans egne problemer: en lille webapp til at holde styr på brætspilsaftener, et script til at rydde op i rodede mapper, eller en enkel side til at dele noter med venner. Undervejs har han kæmpet sig gennem alle de klassiske fejl – semikolon, forkerte indrykninger og variabler, der hedder noget helt andet end man tror – og det er præcis den rejse, han deler på Coding Class.

På Coding Class skriver Lasse praktiske, jordnære guides, der tager udgangspunkt i små, konkrete opgaver: noget du kan se, teste og bygge videre på med det samme. Han elsker at bryde en opgave ned i små bidder, vise den fulde kode og forklare linje for linje, hvad der sker – inklusive de typiske bugs, du med stor sandsynlighed også støder på.

For Lasse handler kodning ikke om flotte titler eller store ord, men om følelsen af at få noget til at virke – og om at du som læser kan gå derfra med noget, du selv har bygget. Hvis du kan kende glæden ved at få en fejl til endelig at forsvinde, er du lige på bølgelængde med hans måde at lære fra sig på.

1 kommentar

comments user
Søren

Den del om logs gav sååå mening mht at finde fejl når kunder skriver.

Send kommentar

You May Have Missed