Cours Next.js · Module 1: Configuration de Next.js
Instrumentation et débogage des applications
Dans cette leçon9
À Quantum Metropolis, des systèmes de diagnostic avancés sont essentiels au bon fonctionnement de toute l'infrastructure. Des réseaux de capteurs et d'analyseurs, invisibles pour le citoyen moyen, surveillent en permanence chaque aspect des systèmes de la ville, permettant aux ingénieurs de détecter et de corriger rapidement les problèmes. De même, dans le monde de Next.js, les outils d'instrumentation et de débogage donnent aux développeurs un aperçu du fonctionnement des applications et la possibilité de résoudre rapidement les problèmes.
Qu'est-ce que l'instrumentation dans Next.js ?
L'instrumentation, dans le contexte de Next.js, désigne le processus consistant à ajouter du code de surveillance à une application pour collecter des données sur ses performances, ses erreurs et son comportement. C'est comme un réseau de capteurs quantiques placés aux points clés de Quantum Metropolis - ils permettent de surveiller l'état des systèmes et de collecter les données nécessaires à l'analyse de leur fonctionnement.
Next.js 16 propose des outils d'instrumentation avancés qui permettent aux développeurs de mieux comprendre et d'optimiser le comportement de leur application.
Configurer l'instrumentation dans Next.js 16
Dans Next.js 16, c'est le rôle du fichier instrumentation.ts, stable depuis Next.js 15, qui s'exécute une seule fois au démarrage du serveur. Tu peux l'utiliser pour configurer des outils de surveillance comme Sentry, New Relic ou OpenTelemetry.
Pour activer l'instrumentation dans une application Next.js, suis ces étapes :
Crée un fichier nommé
instrumentation.ts(ouinstrumentation.js) à la racine de l'application (à côté deappousrc/app).N'ajoute aucun flag dans
next.config. Dans Next.js 13 et 14, l'instrumentation était expérimentale et nécessitait le réglageexperimental.instrumentationHook, mais depuis Next.js 15 elle est stable : dans Next.js 16, le fichier suffit. Tu croiseras peut-être encore l'ancienne configuration dans des projets plus anciens :1// next.config.js - Next.js 13 and 14 only, not needed in Next.js 16 2/** @type {import('next').NextConfig} */ 3const nextConfig = { 4 experimental: { 5 instrumentationHook: true, 6 } 7}; 8 9module.exports = nextConfig;Implémente le hook
registerdans le fichier d'instrumentation :1// instrumentation.ts 2export async function register() { 3 if (process.env.NEXT_RUNTIME === 'nodejs') { 4 // Code executed only in the Node.js runtime (not in the Edge Runtime) 5 console.log('Initializing Quantum Metropolis monitoring system...'); 6 7 // Here you can implement actual instrumentation 8 // e.g., initializing Sentry, LogRocket, or OpenTelemetry 9 } 10}
La fonction register est appelée au démarrage de l'application et peut servir à mettre en place la surveillance au niveau de l'application.
Intégration avec des outils de surveillance populaires
À Quantum Metropolis, différents systèmes de surveillance jouent différents rôles - de la surveillance des niveaux d'énergie au suivi des déplacements des citoyens. De même, dans le monde du développement web, différents outils servent à différentes fins de surveillance.
1. OpenTelemetry
OpenTelemetry est un ensemble d'API, de bibliothèques et d'outils pour collecter des données de télémétrie (métriques, logs et traces) à partir des applications.
1// instrumentation.ts
2export async function register() {
3 if (process.env.NEXT_RUNTIME === 'nodejs') {
4 // Dynamic import to avoid issues during application building
5 const { registerOTel } = await import('./monitoring/otel');
6 registerOTel();
7 }
8}
9
10// monitoring/otel.ts
11import * as opentelemetry from '@opentelemetry/sdk-node';
12import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
13import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';
14
15export function registerOTel() {
16 const sdk = new opentelemetry.NodeSDK({
17 // The exporter reads the collector address from OTEL_EXPORTER_OTLP_ENDPOINT and appends /v1/traces itself
18 traceExporter: new OTLPTraceExporter(),
19 instrumentations: [getNodeAutoInstrumentations()],
20 });
21
22 sdk.start();
23 console.log('OpenTelemetry has been initialized');
24}2. Sentry
Sentry est une plateforme populaire de surveillance des erreurs qui permet de suivre, prioriser et corriger les erreurs en temps réel.
1// instrumentation.ts
2export async function register() {
3 if (process.env.NEXT_RUNTIME === 'nodejs') {
4 const { initSentry } = await import('./monitoring/sentry');
5 initSentry();
6 }
7}
8
9// monitoring/sentry.ts
10import * as Sentry from '@sentry/nextjs';
11
12export function initSentry() {
13 Sentry.init({
14 dsn: process.env.SENTRY_DSN,
15 environment: process.env.NODE_ENV,
16 tracesSampleRate: 1.0,
17 });
18
19 console.log('Sentry has been initialized');
20}3. Analytics personnalisé
Tu peux aussi implémenter ta propre solution d'analytics :
1// instrumentation.ts
2export async function register() {
3 if (process.env.NEXT_RUNTIME === 'nodejs') {
4 const { initAnalytics } = await import('./monitoring/analytics');
5 initAnalytics();
6 }
7}
8
9// monitoring/analytics.ts
10import { NextRequest } from 'next/server';
11
12// Middleware for collecting analytics data
13export async function analyticsMiddleware(req: NextRequest, handler: () => Promise<Response>) {
14 const start = Date.now();
15
16 // Handle the request and measure how long it took
17 const response = await handler();
18 const responseTime = Date.now() - start;
19
20 // Record performance data
21 console.log(`Path: ${req.nextUrl.pathname}, Time: ${responseTime}ms`);
22
23 // In a real application, you would send this data to an analytics system
24 return response;
25}
26
27export function initAnalytics() {
28 console.log('Quantum Metropolis analytics system has been initialized');
29}Instrumentation côté client
L'instrumentation ne se limite pas au serveur. Dans les composants client, tu peux utiliser divers outils pour surveiller l'expérience utilisateur et les performances de l'application.
React Profiler
React Profiler est un outil intégré à React pour mesurer le temps de rendu des composants. Tu peux l'utiliser pour identifier les goulots d'étranglement de performance dans ton application.
1// components/PerformanceWrapper.tsx
2"use client";
3
4import { Profiler, ProfilerOnRenderCallback, ReactNode } from 'react';
5
6interface PerformanceWrapperProps {
7 id: string;
8 children: ReactNode;
9}
10
11export function PerformanceWrapper({ id, children }: PerformanceWrapperProps) {
12 const handleRender: ProfilerOnRenderCallback = (
13 id, // id of the component that was rendered
14 phase, // "mount", "update" or "nested-update"
15 actualDuration, // time spent rendering
16 baseDuration, // estimated rendering time without optimizations
17 startTime, // when React started rendering
18 commitTime // when React finished rendering
19 ) => {
20 // Record performance data
21 console.log(`[Profiler] ${id} - ${phase}: ${actualDuration.toFixed(2)}ms`);
22
23 // Note: a regular production build disables the Profiler, so onRender is not called here
24 // (you need a profiling build: next build --profile)
25 if (process.env.NODE_ENV === 'production') {
26 // sendToAnalytics({ id, phase, actualDuration, baseDuration });
27 }
28 };
29
30 return (
31 <Profiler id={id} onRender={handleRender}>
32 {children}
33 </Profiler>
34 );
35}Tu peux maintenant envelopper n'importe quel composant dans PerformanceWrapper pour mesurer ses performances :
1// app/dashboard/page.tsx
2"use client";
3
4import { PerformanceWrapper } from '@/components/PerformanceWrapper';
5import { DashboardContent } from '@/components/DashboardContent';
6
7export default function DashboardPage() {
8 return (
9 <PerformanceWrapper id="dashboard-page">
10 <DashboardContent />
11 </PerformanceWrapper>
12 );
13}Web Vitals
Web Vitals est une initiative de Google visant à fournir des recommandations unifiées sur les métriques de qualité essentielles pour offrir une excellente expérience utilisateur sur le web.
Next.js 16 facilite le reporting des Web Vitals :
1// app/_components/web-vitals.tsx
2"use client";
3
4import { useReportWebVitals } from 'next/web-vitals';
5
6export function WebVitalsReporter() {
7 useReportWebVitals((metric) => {
8 const { id, name, rating, value } = metric;
9
10 // Record Web Vitals data
11 console.log(`[Web Vitals] ${name} (${rating}): ${value}`);
12
13 // In production, you would send this data to an analytics system
14 if (process.env.NODE_ENV === 'production') {
15 // sendToAnalytics({ id, name, rating, value });
16 }
17 });
18
19 return null;
20}Le layout racine reste un Server Component et importe seulement ce petit composant client :
1// app/layout.tsx
2import { WebVitalsReporter } from './_components/web-vitals';
3
4export default function RootLayout({ children }: { children: React.ReactNode }) {
5 return (
6 <html lang="en">
7 <body>
8 <WebVitalsReporter />
9 {children}
10 </body>
11 </html>
12 );
13}Le champ rating vaut good, needs-improvement ou poor : c'est l'évaluation de la métrique selon les seuils de Google.
Déboguer les applications Next.js 16
À Quantum Metropolis, les ingénieurs utilisent des outils de diagnostic avancés pour identifier et corriger les problèmes des systèmes urbains. De même, les développeurs Next.js ont accès à une gamme d'outils qui facilitent le débogage des applications.
1. Débogage côté serveur
a) Logs serveur
La façon la plus simple de déboguer sur le serveur est d'ajouter des logs :
1// app/api/users/route.ts
2export async function GET() {
3 console.log('Starting to fetch users...');
4 try {
5 const users = await fetchUsers();
6 console.log(`Fetched ${users.length} users`);
7 return Response.json(users);
8 } catch (error) {
9 console.error('Error fetching users:', error);
10 return Response.json({ error: 'Internal Server Error' }, { status: 500 });
11 }
12}Ces logs seront visibles dans la console serveur où l'application Next.js s'exécute.
b) Débogage avec Node Inspector
Next.js prend en charge le débogueur Node.js intégré. Depuis Next.js 16.1, il suffit d'ajouter le flag --inspect à next dev (auparavant, on utilisait la variable NODE_OPTIONS='--inspect', qui attachait le débogueur à tous les processus lancés par Next.js) :
1npx next dev --inspectOuvre ensuite Chrome et va à chrome://inspect, où tu peux te connecter au processus Node.js et utiliser les outils de développement de Chrome pour le débogage.
Tu peux aussi ajouter des points d'arrêt dans le code à l'aide de l'instruction debugger :
1// app/api/data/route.ts
2export async function GET() {
3 debugger; // Will pause execution when debugger is attached
4 const data = await processData();
5 return Response.json(data);
6}2. Débogage côté client
a) React Developer Tools
React Developer Tools est une extension de navigateur qui permet d'inspecter l'arbre des composants React, de surveiller les props, l'état et les hooks.
Pour déboguer efficacement avec React Developer Tools :
- Installe l'extension dans ton navigateur (Chrome, Firefox)
- Ouvre les outils de développement du navigateur et va dans l'onglet « Components » ou « Profiler »
- Examine la structure des composants et leurs propriétés
Particulièrement utile lors du débogage des Server et Client Components : les indications à côté du nom des composants :
- Les Server Components ont à côté de leur nom un badge indiquant leur environnement (par exemple « Server »)
- Les Client Components s'affichent sans ce badge
b) Déboguer l'état avec React DevTools
1// components/UserProfile.tsx
2"use client";
3
4import { useState, useEffect } from 'react';
5
6export default function UserProfile({ userId }) {
7 const [user, setUser] = useState(null);
8 const [loading, setLoading] = useState(true);
9 const [error, setError] = useState(null);
10
11 useEffect(() => {
12 async function fetchUser() {
13 try {
14 const response = await fetch(`/api/users/${userId}`);
15 if (!response.ok) throw new Error('Failed to fetch user');
16 const data = await response.json();
17 setUser(data);
18 } catch (err) {
19 setError(err.message);
20 } finally {
21 setLoading(false);
22 }
23 }
24
25 fetchUser();
26 }, [userId]);
27
28 if (loading) return <div>Loading user data...</div>;
29 if (error) return <div>Error: {error}</div>;
30 if (!user) return <div>No user found</div>;
31
32 return (
33 <div>
34 <h1>{user.name}</h1>
35 <p>{user.email}</p>
36 </div>
37 );
38}Dans React DevTools, tu peux surveiller en temps réel les changements des états user, loading et error.
3. Déboguer les Server Actions
Les Server Actions, stables depuis Next.js 14, permettent d'exécuter du code sur le serveur depuis des composants client. Leur débogage nécessite une approche particulière :
1// app/actions.ts
2"use server";
3
4import { revalidatePath } from 'next/cache';
5
6export async function updateUserProfile(formData: FormData) {
7 const userId = formData.get('userId');
8 const name = formData.get('name');
9 const email = formData.get('email');
10
11 console.log(`Updating profile for user ${userId}:`, { name, email });
12
13 try {
14 // Profile update logic
15 const result = await updateUserInDatabase(userId, { name, email });
16 console.log('Update result:', result);
17
18 // Refresh the path to show updated data
19 revalidatePath(`/users/${userId}`);
20
21 return { success: true };
22 } catch (error) {
23 console.error('Error updating profile:', error);
24 return { success: false, error: error.message };
25 }
26}Déboguer les Server Actions consiste principalement à :
- Ajouter des logs serveur
- Vérifier que les données passées depuis le formulaire sont correctes
- Surveiller les réponses renvoyées aux composants client
Depuis Next.js 16.2, le serveur de développement fait une partie de ce travail pour toi : à chaque exécution d'une Server Function, il affiche dans le terminal son nom, ses arguments, son temps d'exécution et le fichier où elle est définie.
4. Outils intégrés de Next.js 16
Next.js 16 inclut plusieurs outils intégrés qui facilitent le débogage :
a) Messages d'erreur avancés
Next.js 16 affiche des messages d'erreur détaillés dans l'interface de développement, indiquant exactement où le problème est survenu et suggérant des solutions.
b) Fast Refresh
Fast Refresh permet de préserver l'état des composants tout en modifiant le code, ce qui facilite grandement le débogage et le développement itératif.
c) Next.js DevTools
En mode développement, Next.js 16 affiche dans un coin de la page l'indicateur Next.js Dev Tools. Il fonctionne sans aucune configuration : il signale les erreurs de compilation et d'exécution, indique si la route actuelle est statique ou dynamique et ouvre un panneau avec plus de détails. Tu peux le déplacer avec l'option devIndicators, et le serveur Next.js DevTools MCP donne les mêmes informations aux assistants IA.
Si tu veux que les rapports Web Vitals désignent l'élément responsable d'un problème (par exemple l'image LCP ou l'élément qui a décalé la mise en page), active l'option expérimentale webVitalsAttribution :
1// next.config.js
2/** @type {import('next').NextConfig} */
3const nextConfig = {
4 experimental: {
5 webVitalsAttribution: ['CLS', 'LCP'],
6 },
7};
8
9module.exports = nextConfig;L'option est expérimentale : active-la seulement le temps de chercher un problème ; l'indicateur Dev Tools fonctionne très bien sans elle.
Surveillance des performances de l'application
À Quantum Metropolis, les ingénieurs surveillent en permanence les performances de tous les systèmes pour garantir une expérience optimale aux habitants. Dans le monde de Next.js, la surveillance des performances est tout aussi importante pour garantir une bonne expérience utilisateur.
1. Next.js Analytics
Next.js propose un outil intégré pour surveiller les Web Vitals qui peut être intégré à des outils d'analytics :
1// pages/_app.js - Pages Router only (in the App Router use the useReportWebVitals hook shown above)
2export function reportWebVitals(metric) {
3 const { id, name, label, value } = metric;
4
5 // Example: sending metrics to Google Analytics 4
6 window.gtag('event', name, {
7 value: Math.round(name === 'CLS' ? value * 1000 : value), // CLS values are typically small
8 event_label: id,
9 non_interaction: true,
10 });
11}2. Surveillance personnalisée des performances
Tu peux aussi implémenter ta propre surveillance des performances à l'aide de la Performance API :
1// lib/performance.ts
2export function measurePageLoad() {
3 if (typeof window !== 'undefined' && window.performance) {
4 const performanceEntries = performance.getEntriesByType('navigation');
5
6 if (performanceEntries.length > 0) {
7 const navigationEntry = performanceEntries[0] as PerformanceNavigationTiming;
8
9 const metrics = {
10 // Time from navigation start to DOM completion
11 domComplete: navigationEntry.domComplete,
12
13 // Time from navigation start to full page load
14 loadComplete: navigationEntry.loadEventEnd,
15
16 // Time from navigation start to first render
17 firstPaint: performance.getEntriesByName('first-paint')[0]?.startTime || 0,
18
19 // Time from navigation start to first content render
20 firstContentfulPaint: performance.getEntriesByName('first-contentful-paint')[0]?.startTime || 0,
21 };
22
23 console.log('Performance Metrics:', metrics);
24
25 // In production, you would send this data to an analytics system
26 if (process.env.NODE_ENV === 'production') {
27 // sendToAnalytics(metrics);
28 }
29 }
30 }
31}Tu peux appeler cette fonction dans un composant client, par exemple :
1// components/PerformanceMonitor.tsx
2"use client";
3
4import { useEffect } from 'react';
5import { measurePageLoad } from '@/lib/performance';
6
7export function PerformanceMonitor() {
8 useEffect(() => {
9 // Measure performance after page load
10 if (document.readyState === 'complete') {
11 measurePageLoad();
12 } else {
13 // loadEventEnd is only known after the load event handlers finish
14 const onLoad = () => setTimeout(measurePageLoad, 0);
15 window.addEventListener('load', onLoad);
16 return () => window.removeEventListener('load', onLoad);
17 }
18 }, []);
19
20 return null; // Component doesn't render anything visible
21}Diagnostiquer les problèmes de Server et Client Components
L'un des aspects les plus avancés de Next.js 16 est le système de Server et Client Components. Cependant, cela introduit de nouveaux défis de débogage.
1. Problèmes courants des Server Components
a) Tenter d'utiliser un hook React dans un Server Component
1// app/dashboard/page.tsx
2export default function Dashboard() {
3 // ERROR: Cannot use hooks in Server Components
4 const [count, setCount] = useState(0);
5
6 return <div>Dashboard</div>;
7}Solution : déplace la logique d'état dans un Client Component.
1// app/dashboard/page.tsx
2import { DashboardClient } from './dashboard-client';
3
4export default function Dashboard() {
5 // Server Component
6 return <DashboardClient />;
7}
8
9// dashboard-client.tsx
10"use client";
11
12import { useState } from 'react';
13
14export function DashboardClient() {
15 // Now we can use a React hook
16 const [count, setCount] = useState(0);
17
18 return <div>Dashboard Count: {count}</div>;
19}b) Tenter d'accéder aux API du navigateur dans un Server Component
1// app/dashboard/page.tsx
2export default function Dashboard() {
3 // ERROR: there is no window object on the server (ReferenceError: window is not defined)
4 const windowWidth = window.innerWidth;
5
6 return <div>Width: {windowWidth}</div>;
7}Solution : déplace le code utilisant les API du navigateur dans un Client Component.
1// app/dashboard/page.tsx
2import { WindowSizeClient } from './window-size-client';
3
4export default function Dashboard() {
5 return <WindowSizeClient />;
6}
7
8// window-size-client.tsx
9"use client";
10
11import { useState, useEffect } from 'react';
12
13export function WindowSizeClient() {
14 const [windowWidth, setWindowWidth] = useState(0);
15
16 useEffect(() => {
17 setWindowWidth(window.innerWidth);
18
19 const handleResize = () => setWindowWidth(window.innerWidth);
20 window.addEventListener('resize', handleResize);
21 return () => window.removeEventListener('resize', handleResize);
22 }, []);
23
24 return <div>Width: {windowWidth}</div>;
25}2. Problèmes courants des Client Components
a) Importer un Server Component dans un Client Component
1// ERROR: Cannot directly import a Server Component into a Client Component
2// components/dashboard-wrapper.tsx
3"use client";
4
5import { ServerDataFetcher } from './server-data-fetcher';
6
7export function DashboardWrapper() {
8 return (
9 <div className="dashboard">
10 <ServerDataFetcher /> {/* This won't work */}
11 </div>
12 );
13}Solution : passe les Server Components en tant que props aux Client Components.
1// app/dashboard/page.tsx
2import { DashboardWrapper } from '@/components/dashboard-wrapper';
3import { ServerDataFetcher } from '@/components/server-data-fetcher';
4
5export default function Dashboard() {
6 return (
7 <DashboardWrapper>
8 <ServerDataFetcher /> {/* Passed as children */}
9 </DashboardWrapper>
10 );
11}
12
13// components/dashboard-wrapper.tsx
14"use client";
15
16export function DashboardWrapper({ children }) {
17 return (
18 <div className="dashboard">
19 {children}
20 </div>
21 );
22}
23
24// components/server-data-fetcher.tsx
25// Server Component
26export async function ServerDataFetcher() {
27 const data = await fetchData();
28 return <div>{data}</div>;
29}Outils externes pour la surveillance en production
À Quantum Metropolis, les systèmes de surveillance centraux collectent des données de milliers de capteurs répartis dans toute la ville, créant une image complète de l'état de l'infrastructure urbaine. Dans le monde des applications web, des outils externes similaires aident à surveiller les déploiements en production.
1. Vercel Analytics
Si tu héberges ton application sur Vercel (les créateurs de Next.js), tu peux utiliser Vercel Analytics :
1npm install @vercel/analytics1// app/layout.tsx
2import { Analytics } from '@vercel/analytics/react';
3
4export default function RootLayout({ children }: { children: React.ReactNode }) {
5 return (
6 <html>
7 <body>
8 {children}
9 <Analytics />
10 </body>
11 </html>
12 );
13}2. New Relic
New Relic est un outil complet de surveillance d'applications qui permet de suivre les performances, les erreurs et le comportement des utilisateurs.
1npm install newrelicCrée ensuite un fichier newrelic.js à la racine du projet :
1// newrelic.js
2'use strict';
3
4exports.config = {
5 app_name: ['Quantum Voyages - Next.js'],
6 license_key: process.env.NEW_RELIC_LICENSE_KEY,
7 logging: {
8 level: 'info'
9 },
10 allow_all_headers: true,
11 attributes: {
12 exclude: [
13 'request.headers.cookie',
14 'request.headers.authorization',
15 'request.headers.proxyAuthorization',
16 'request.headers.setCookie*',
17 'request.headers.x*',
18 'response.headers.cookie',
19 'response.headers.authorization',
20 'response.headers.proxyAuthorization',
21 'response.headers.setCookie*',
22 'response.headers.x*'
23 ]
24 }
25};3. Datadog
Datadog est une plateforme de surveillance cloud qui assure la surveillance des serveurs, bases de données, outils et services via une plateforme d'analyse de données.
1npm install @datadog/browser-rum1// app/datadog-rum.tsx
2"use client";
3
4import { useEffect } from 'react';
5import { datadogRum } from '@datadog/browser-rum';
6
7export function DatadogRUM() {
8 useEffect(() => {
9 if (process.env.NODE_ENV === 'production') {
10 datadogRum.init({
11 applicationId: process.env.NEXT_PUBLIC_DATADOG_APPLICATION_ID,
12 clientToken: process.env.NEXT_PUBLIC_DATADOG_CLIENT_TOKEN,
13 site: 'datadoghq.eu', // or 'datadoghq.com' depending on region
14 service: 'quantum-voyages',
15 env: process.env.NODE_ENV,
16 version: '1.0.0',
17 sessionSampleRate: 100,
18 sessionReplaySampleRate: 100,
19 trackUserInteractions: true,
20 trackResources: true,
21 trackLongTasks: true,
22 defaultPrivacyLevel: 'mask-user-input'
23 });
24 }
25 }, []);
26
27 return null;
28}Ajoute ensuite le composant au layout :
1// app/layout.tsx
2import { DatadogRUM } from './datadog-rum';
3
4export default function RootLayout({ children }: { children: React.ReactNode }) {
5 return (
6 <html>
7 <body>
8 {children}
9 <DatadogRUM />
10 </body>
11 </html>
12 );
13}Résumé
L'instrumentation et le débogage sont des aspects cruciaux du développement d'applications Next.js 16, tout comme les systèmes de surveillance sont essentiels au bon fonctionnement de Quantum Metropolis. Avec les bons outils et les bonnes pratiques, tu peux :
- Surveiller les performances de l'application en environnement de production
- Suivre les erreurs et les corriger rapidement
- Comprendre le comportement des utilisateurs et optimiser l'expérience
- Déboguer des scénarios complexes liés aux Server et Client Components
- Garantir la fiabilité de l'application à mesure qu'elle grandit
Souviens-toi que la meilleure approche consiste à intégrer l'instrumentation et la surveillance tôt dans le cycle de développement de l'application, plutôt que de les ajouter après coup. Tout comme les ingénieurs de Quantum Metropolis conçoivent les systèmes de surveillance en même temps que la construction de l'infrastructure urbaine, tu devrais implémenter les outils de débogage et de surveillance dès les premières étapes du développement de ton application Next.js.
Dans la prochaine leçon, tu découvriras les outils de développement de Next.js 16 ; la gestion des variables d'environnement dans la configuration de production viendra au module 8, ce qui permettra de stocker en toute sécurité les données sensibles et d'adapter le comportement de l'application aux différents environnements.
Code de cette leçon : App.tsx
1import React, { useState } from 'react';
2
3interface LogEntry {
4 id: number;
5 timestamp: string;
6 level: 'info' | 'warn' | 'error';
7 system: string;
8 message: string;
9}
10
11const InstrumentationDashboard = () => {
12 const [logs, setLogs] = useState<LogEntry[]>([]);
13 const [filter, setFilter] = useState<string>('all');
14 const [otelEnabled, setOtelEnabled] = useState(false);
15 const [sentryEnabled, setSentryEnabled] = useState(false);
16
17 const addLog = (level: LogEntry['level'], system: string, message: string) => {
18 const entry: LogEntry = {
19 id: Date.now() + Math.random(),
20 timestamp: new Date().toISOString().slice(11, 23),
21 level,
22 system,
23 message,
24 };
25 setLogs(prev => [entry, ...prev].slice(0, 20));
26 };
27
28 const simulateRegister = () => {
29 addLog('info', 'instrumentation.ts', 'register() called - NEXT_RUNTIME=nodejs');
30 setTimeout(() => {
31 if (otelEnabled) addLog('info', 'OpenTelemetry', 'Trace exporter initialized');
32 if (sentryEnabled) addLog('info', 'Sentry', 'Error tracking enabled');
33 addLog('info', 'Server', 'Instrumentation hooks registered');
34 }, 500);
35 };
36
37 const simulateError = () => {
38 addLog('error', 'API Route', 'Unhandled exception in /api/users');
39 if (sentryEnabled) {
40 setTimeout(() => addLog('warn', 'Sentry', 'Error captured and sent to dashboard'), 300);
41 }
42 };
43
44 const simulateRequest = () => {
45 addLog('info', 'Middleware', 'Incoming request: GET /dashboard');
46 if (otelEnabled) {
47 addLog('info', 'OpenTelemetry', 'Span created: HTTP GET /dashboard');
48 setTimeout(() => addLog('info', 'OpenTelemetry', 'Span completed: 145ms'), 400);
49 }
50 setTimeout(() => addLog('info', 'Server', 'Response sent: 200 OK'), 600);
51 };
52
53 const levelColor = (level: string) => {
54 switch (level) {
55 case 'info': return '#4caf50';
56 case 'warn': return '#ff9800';
57 case 'error': return '#f44336';
58 default: return '#999';
59 }
60 };
61
62 const filteredLogs = filter === 'all' ? logs : logs.filter(l => l.level === filter);
63
64 return (
65 <div style={{ background: '#0f0f23', minHeight: '100vh', padding: '20px', color: '#fff', fontFamily: 'monospace' }}>
66 <h1 style={{ color: '#64ffda', textAlign: 'center', marginBottom: '8px' }}>
67 Instrumentation Dashboard
68 </h1>
69 <p style={{ textAlign: 'center', color: '#888', marginBottom: '24px' }}>
70 Metropolis Quantum - Système de surveillance
71 </p>
72
73 <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '16px', maxWidth: '900px', margin: '0 auto 20px' }}>
74 <div style={{ background: 'rgba(255,255,255,0.05)', padding: '16px', borderRadius: '10px', border: '1px solid #333' }}>
75 <h3 style={{ color: '#7c4dff', marginBottom: '12px' }}>Outils de surveillance</h3>
76 <label style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '8px', cursor: 'pointer' }}>
77 <input type="checkbox" checked={otelEnabled} onChange={() => setOtelEnabled(!otelEnabled)} />
78 <span style={{ color: otelEnabled ? '#4caf50' : '#666' }}>OpenTelemetry</span>
79 </label>
80 <label style={{ display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer' }}>
81 <input type="checkbox" checked={sentryEnabled} onChange={() => setSentryEnabled(!sentryEnabled)} />
82 <span style={{ color: sentryEnabled ? '#4caf50' : '#666' }}>Sentry</span>
83 </label>
84 </div>
85
86 <div style={{ background: 'rgba(255,255,255,0.05)', padding: '16px', borderRadius: '10px', border: '1px solid #333' }}>
87 <h3 style={{ color: '#7c4dff', marginBottom: '12px' }}>Simuler</h3>
88 <div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>
89 <button onClick={simulateRegister} style={{ padding: '8px', background: '#1a73e8', border: 'none', borderRadius: '6px', color: '#fff', cursor: 'pointer' }}>
90 Hook register()
91 </button>
92 <button onClick={simulateRequest} style={{ padding: '8px', background: '#0f9d58', border: 'none', borderRadius: '6px', color: '#fff', cursor: 'pointer' }}>
93 Requête HTTP
94 </button>
95 <button onClick={simulateError} style={{ padding: '8px', background: '#db4437', border: 'none', borderRadius: '6px', color: '#fff', cursor: 'pointer' }}>
96 Déclencher une erreur
97 </button>
98 </div>
99 </div>
100 </div>
101
102 <div style={{ maxWidth: '900px', margin: '0 auto' }}>
103 <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '10px' }}>
104 <h3 style={{ color: '#64ffda' }}>Journal des événements</h3>
105 <div style={{ display: 'flex', gap: '6px' }}>
106 {['all', 'info', 'warn', 'error'].map(f => (
107 <button key={f} onClick={() => setFilter(f)}
108 style={{ padding: '4px 12px', background: filter === f ? '#7c4dff' : 'rgba(255,255,255,0.1)',
109 border: 'none', borderRadius: '4px', color: '#fff', cursor: 'pointer', fontSize: '12px' }}>
110 {f.toUpperCase()}
111 </button>
112 ))}
113 <button onClick={() => setLogs([])}
114 style={{ padding: '4px 12px', background: 'rgba(255,0,0,0.2)', border: 'none', borderRadius: '4px', color: '#f44336', cursor: 'pointer', fontSize: '12px' }}>
115 EFFACER
116 </button>
117 </div>
118 </div>
119
120 <div style={{ background: '#000', borderRadius: '8px', padding: '12px', minHeight: '200px', maxHeight: '300px', overflowY: 'auto' }}>
121 {filteredLogs.length === 0 ? (
122 <p style={{ color: '#555', textAlign: 'center', paddingTop: '80px' }}>
123 Active les outils et simule des événements pour voir les logs...
124 </p>
125 ) : (
126 filteredLogs.map(log => (
127 <div key={log.id} style={{ padding: '4px 0', borderBottom: '1px solid #1a1a1a', fontSize: '13px' }}>
128 <span style={{ color: '#555' }}>{log.timestamp}</span>
129 {' '}
130 <span style={{ color: levelColor(log.level), fontWeight: 'bold' }}>[{log.level.toUpperCase()}]</span>
131 {' '}
132 <span style={{ color: '#7c4dff' }}>[{log.system}]</span>
133 {' '}
134 <span style={{ color: '#ccc' }}>{log.message}</span>
135 </div>
136 ))
137 )}
138 </div>
139 </div>
140 </div>
141 );
142};
143
144export default InstrumentationDashboard;Tu as repéré une erreur dans cette leçon ?
Teste tes connaissances
Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.
1. Quel fichier, situé à la racine du projet Next.js, est exécuté au démarrage du serveur pour configurer le monitoring ?
2. Quelle fonction implémentes-tu dans le fichier d'instrumentation pour démarrer le monitoring au lancement de l'application ?
3. Que dois-tu faire quand une erreur apparaît à propos de l'utilisation du hook useState dans un Server Component ?
Tâches pratiques dans le jeu
- Clique dans l'ordre
Ordonne la commande qui lance le serveur de développement avec le débogueur Node.js dans Next.js 16.1+