Logs & Observabilité
Journalisation structurée pour Node, Edge et Workers avec IDs de requête, masquage des secrets et exemples par route. Fonctionne sur Vercel, Cloudflare et serveurs Node.
Notions clés
- Logs structurés (JSON), corrélés via
request_id. - Niveaux:
debug,info,warn,error. - Masquage des secrets (cookies, tokens, etc.).
Ce qui est inclus
- API commune côté Node et Edge/Workers (même forme).
- Node (fonctions serveur Next, serverless, Node): Pino (JSON rapide).
- Edge/Workers (Vercel Edge, Cloudflare Workers, middleware): implémentation légère via
console.logJSON.
- Propagation
request_idvia middleware (sans toucher aux cookies). - Exemple branché sur la route de présignature de stockage.
Fichiers
- Node:
src/lib/logger/server.ts - Edge:
src/lib/logger/edge.ts - Types:
src/lib/logger/types.ts - Middleware:
src/middleware.ts - Exemple:
src/app/api/storage/uploads/route.ts
Fichiers
- Node:
src/lib/logger/server.ts - Edge:
src/lib/logger/edge.ts - Types:
src/lib/logger/types.ts - Middleware:
src/middleware.ts - Exemple:
src/app/api/storage/uploads/route.ts
Voir les logs
- Local: terminal (JSON sur une ligne). Pour un affichage lisible, utilisez
pino-pretty:pnpm dev 2>&1 | pnpx pino-pretty- sauvegarde fichier:
pnpm dev 2>&1 | pnpx pino-pretty | tee logs/dev.log
- Vercel: Deployments → Functions → Logs, ou
vercel logs. - Cloudflare Workers:
wrangler tail.
Utilisation (Node)
import { logger, requestIdFromHeaders } from '@/lib/logger/server'
export async function POST(req: Request){
const rid = requestIdFromHeaders(req.headers)
const log = logger.child({ request_id: rid, route: '/api/exemple' })
log.info({ event: 'exemple.start' })
// ...
log.info({ event: 'exemple.ok' })
return new Response('ok')
}Utilisation (Edge)
import { logger } from '@/lib/logger/edge'
export const runtime = 'edge'
export function GET(){
logger.info({ event: 'edge.heartbeat' })
return new Response('ok')
}Conseils
- Définir
LOG_LEVELet ajuster la redaction selon vos besoins. - Pas de fichier de logs par défaut: écriture vers stdout/stderr. En local, utilisez
teesi besoin:pnpm dev 2>&1 | tee logs/dev.json. - Remplacer
console.*par le logger côté serveur.
Utilisation (Node)
import { logger, requestIdFromHeaders } from '@/lib/logger/server'
export async function POST(req: Request){
const rid = requestIdFromHeaders(req.headers)
const log = logger.child({ request_id: rid, route: '/api/exemple' })
const start = Date.now()
try {
log.info({ event: 'exemple.start' })
// ...
log.info({ event: 'exemple.ok', duration_ms: Date.now() - start })
return new Response('ok')
} catch (e:any) {
log.error({ event: 'exemple.error', message: e?.message })
return new Response('error', { status: 500 })
}
}Utilisation (Edge)
import { logger } from '@/lib/logger/edge'
export const runtime = 'edge'
export function GET(){
logger.info({ event: 'edge.heartbeat' })
return new Response('ok')
}Aide: withApiLogging (Node)
import { withApiLogging } from '@/lib/logger/server'
export const POST = withApiLogging(async (req) => {
return new Response('ok')
}, { route: '/api/foo', event: 'foo.process' })Exemple déjà branché
src/app/api/storage/uploads/route.tsémetstorage.presign.createetstorage.presign.create.error.
Personnaliser le masquage
- Node:
redactPathsdanssrc/lib/logger/server.ts. - Edge:
redactKeysdanssrc/lib/logger/edge.ts.
Intégrations optionnelles
- Sentry (
@sentry/nextjs) pour exceptions et traces. - Sinks gérés (Axiom, Better Stack, Datadog, New Relic) pour recherche/rétention.
Dépannage
- Redirections inattendues vers login: ne pas surcharger les en‑têtes de requête/cookies dans le middleware.
- Pas de logs en Edge: vérifier
runtime='edge'et utiliserwrangler tail/Vercel logs.
Téléversements privés de fichiers (S3 / R2)
Guide pas à pas, accessible aux débutants, pour ajouter des téléversements privés avec un stockage compatible S3. Inclut concepts, configuration, variables d’env, API, UI, erreurs et migration S3↔R2.
Service Email (Resend)
Intégrez l’email transactionnel avec Resend. Vérifiez votre domaine, créez des clés API, générez les modèles de bienvenue et de paiement côté serveur et envoyez-les via Resend grâce à la configuration d’environnement.