Jeg stoler mere på mine logs end på min hukommelse
De fleste begynder først at gå op i logging den dag noget bryder sammen i produktion. Jeg synes, det er for sent.
Hvis du lærer logging for begyndere ordentligt fra start, slipper du for rigtig meget gætteri senere.
Hvorfor du overhovedet gider logge
Jeg tænker på logs som en slags tidslinje over, hvad din kode faktisk gjorde. Ikke hvad du troede, den gjorde.
Typisk bruger du logs til tre ting:
- Debugging – hvad skete lige før fejlen?
- Overblik – hvor tit sker noget, hvor langsomt er det?
- Audit/sporbarhed – kan jeg forklare, hvorfor noget skete, hvis nogen spørger senere?
Som begynderen (og faktisk også som let øvet) handler det især om de to første. Få styr på, hvad der sker, og hvornår det går galt.
Problemet er bare, at mange enten logger alt for lidt (“der skete en fejl” og intet mere) eller alt for meget (hele request bodies, tokens, passwords, you name it). Begge dele rammer dig senere.
Log levels uden teori-overload
Du har sikkert set ord som debug, info, warn, error. De er bare etiketter på, hvor alvorlig en besked er.
Min helt enkle tommelfingerregel:
- debug – detaljer du kun gider se, mens du udvikler
- info – normale ting, der sker: en request, et login, et job der kører
- warn – noget der ser forkert ud, men ikke vælter appen (endnu)
- error – noget gik i stykker, der skal du kigge
I frontend bruger du typisk bare console.log, console.warn og console.error. I backend kan du bruge et logger-bibliotek, men idéen er den samme.
Hvis du bare spammer console.log til alt, ender du med et støjhelvede. Prøv at tænke “hvilket niveau er det her egentlig?” hver gang du logger.
Frontend logging – hvad giver mening?
I browseren har du mest console.* til rådighed. Det er fint. Men brug det bevidst.
Det giver mening at logge i frontend
- Når du sender et API-kald: metode, URL (uden tokens), og hvad du forventer
- Når et API-kald fejler: statuskode, hvilken operation det var, fejlbesked
- Ukendte fejl der lander i en
catch-blok
Eksempel på et API-kald i frontend:
async function fetchUser(id) {
console.info('[fetchUser] start', { id });
try {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) {
console.warn('[fetchUser] non-200 response', {
id,
status: res.status,
});
return null;
}
const data = await res.json();
console.debug('[fetchUser] success');
return data;
} catch (err) {
console.error('[fetchUser] error', err);
return null;
}
}
Bemærk hvad jeg ikke logger: ingen tokens, ingen hele user-objekter, ingen passwords.
Det giver sjældent mening at logge i frontend
- Alle taste-tryk, klik og input-værdier (lyder overdrevet, men jeg har set det)
- Store JSON-svar direkte i
console.loghver gang - Hele fejl-objekter i produktion uden filtrering, hvis de kan indeholde hemmeligheder
Som udgangspunkt: log hvad du prøver at gøre, og hvordan det gik. Log ikke alt indholdet.
Hvis du vil dykke mere ned i fejlfinding i frontend, har jeg også skrevet om at debugge rigtigt i browseren.
Backend logging – request-id er din nye ven
På backend har du en luksus, du ikke har i frontend: du kan styre log-outputtet meget mere, og du kan samle det et sted.
Det første, jeg næsten altid gør i en lille Node/Express app, er at give hver request et request-id. Så kan jeg følge én bruger-forespørgsel igennem alle logs.
Eksempel: simpel Express-server med request-id
import express from 'express';
import { randomUUID } from 'crypto';
const app = express();
// lille middleware der giver hver request et id
app.use((req, res, next) => {
req.id = randomUUID();
console.info('request:start', {
requestId: req.id,
method: req.method,
path: req.path,
});
const startedAt = Date.now();
res.on('finish', () => {
const durationMs = Date.now() - startedAt;
console.info('request:end', {
requestId: req.id,
statusCode: res.statusCode,
durationMs,
});
});
next();
});
app.get('/api/users/:id', async (req, res) => {
console.debug('handler:users', { requestId: req.id, userId: req.params.id });
// ... din logik her
res.json({ id: req.params.id, name: 'Test' });
});
app.listen(3000, () => {
console.info('server:listening', { port: 3000 });
});
Her får du:
- Et
requestId, du kan søge efter i logs - En start- og slut-log for hver request
- Response-status og hvor lang tid requesten tog
Det er guld værd, når en bruger siger “siden er langsom”. Så kan du rent faktisk se, om det er sandt.
Fejl-logging uden at lække hemmeligheder
Det mest følsomme ved logging er typisk fejl. Vi har en tendens til at logge alt, når noget går galt.
Den sikre version af fejl-logging
En fornuftig grundstruktur for en error-log kunne være:
- request-id
- hvilken del af koden der fejlede (fx
userService.createUser) - en kort, menneskelig besked
- fejltype og stacktrace (på backend, ikke vist til bruger)
Eksempel i Express:
app.use((err, req, res, next) => {
console.error('error', {
requestId: req.id,
message: err.message,
name: err.name,
stack: err.stack,
});
res.status(500).json({
error: 'Internal server error',
requestId: req.id,
});
});
Ingen tokens, ingen passwords, ingen fulde request bodies. Hvis du er i tvivl: log mindre, ikke mere.
På frontend kan du gøre noget lignende i en global error-boundary eller window.onerror, men vær ekstra forsigtig, hvis du sender fejl videre til et eksternt error tracking værktøj.
Strukturerede logs kontra “printf” logs
Du kan logge på to måder:
- “printf”-stil: fritekst, som du selv formaterer
- Struktureret: fx JSON-objekter
Eksempel på printf-stil:
console.log(`User ${userId} logged in from ${ip}`);
Eksempel på struktureret log:
console.info('user:login', {
userId,
ip,
});
Til små projekter kan du fint starte med printf-stil. Men jeg vil klart anbefale, at du allerede nu vænner dig til at logge noget, der ligner strukturerede logs: en kort “event key” plus et objekt.
Det gør det langt nemmere senere at smide det ind i en log-tjeneste, søge i det og filtrere på felter. Dit fremtidige jeg vil takke dig.
Hvis du er i gang med backend generelt, er det et fint tidspunkt også at læse om fx håndtering af secrets og environment variabler, for logs og secrets hænger skræmmende tæt sammen.
Et lille end-to-end log-mønster for et API-kald
Nu samler vi det. Et simpel mønster fra client til server.
Frontend: log konteksten, ikke alt
async function updateProfile(profile) {
console.info('[updateProfile] start');
try {
const res = await fetch('/api/profile', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(profile),
});
if (!res.ok) {
console.warn('[updateProfile] failed', { status: res.status });
return { ok: false };
}
console.debug('[updateProfile] success');
return { ok: true };
} catch (err) {
console.error('[updateProfile] error', err);
return { ok: false };
}
}
Vi logger:
- at vi starter
- om det fejler, med status
- ukendte fejl med
console.error
Ingen passwords i logs. Ingen tokens. Ingen hele profile-objekter.
Backend: bind request-id på
app.put('/api/profile', async (req, res, next) => {
console.info('profile:update:start', { requestId: req.id });
try {
// forestil dig at req.body allerede er parsed JSON
const userId = req.user.id; // fx fra auth-middleware
await updateUserProfile(userId, req.body);
console.info('profile:update:success', { requestId: req.id });
res.sendStatus(204);
} catch (err) {
console.error('profile:update:error', {
requestId: req.id,
message: err.message,
});
next(err);
}
});
Hvis du nu får en fejl, kan du:
- tage
requestIdfra backend-logs - matche det med frontend-loggen (fx hvis du også viser det for brugeren eller sender det tilbage)
Det er sådan noget, der får dig til at ligne en der “bare lige” kan finde fejlen.
10 ting jeg aldrig logger (heller ikke for at “debugge hurtigt”)
Her er min personlige “never log” liste. Jeg mener det bogstaveligt.
- Passwords – hverken i klartekst eller hash
- Tokens – JWT, session tokens, API keys, refresh tokens
- Hemmeligheder – database credentials, SMTP-koder, webhook-secrets
- CPR-numre – uanset om det “bare er test”
- Kreditkortdata – fulde numre, CVC, udløbsdatoer
- Hele request bodies – især i auth-routes eller formularer med følsomt indhold
- Uploads – filer, billeder, dokumenter
- Private beskeder – chat, mails, fritekstfelter
- Personligt identificerbare data i store mængder (navn + email + telefon + adresse samlet)
- Produktion data på din egen maskine, hvis du dumper det via logs for “lige at kigge”
Hvis du faktisk har behov for at se noget af det til fejlfinding, så gør det kontrolleret: lokal kopi af testdata, admin-tool med adgangsstyring, eller meget kortvarig, midlertidig logging som du fjerner igen. Ikke standard-logging.
Næste skridt når du har styr på dine basale logs
Når du har de her ting nogenlunde på plads:
- forståelse for log levels
- bevidst console-brug i frontend
- request-id og strukturerede logs i backend
- en mental “never log” liste
så er du faktisk klar til at kigge på mere avancerede værktøjer uden at drukne.
Nogle begreber du vil støde på:
- Error tracking – værktøjer der samler fejl ét sted (Sentry, Rollbar osv.)
- Centraliseret logging – log-aggregatorer der kan søge og filtrere i dine logs
- Metrics – målbare ting som responstid, antal errors per minut
Du behøver ikke vælge noget tungt. Ofte kan noget så simpelt som en hosted error-tracker være nok til små projekter. Fokusér på at få exceptions samlet ét sted, så du ikke er afhængig af tilfældigt at have devtools åbent.
Hvis du er i gang med at bygge små webapps og API’er, giver det også mening at koble logging sammen med de ting, du allerede arbejder med, fx deployment og debug flows. Mange af de ting, jeg skriver om på Coding Class, kan du kombinere med logging-mønstrene her.
Jeg plejer at sige det sådan: hvis du er bange for, at nogen læser dine logs, logger du forkerte ting. Logs skal kunne tåle dagslys. Det er koden, der skal skamme sig, ikke loggen.









Send kommentar
Du skal være logget ind for at skrive en kommentar.