Przejdź do treści

Jak monitorować aplikację Next.js na produkcji

Next.js na produkcji bez monitoringu to jazda w ciemno. Wybierz Sentry, Vercel Analytics lub OpenTelemetry i zobacz, jak spiąć ten stos w App Routerze.

Maciej Sala

Founder StriveLab

8 min czytaniaOpublikowano 10 kwietnia 2026 (Aktualizacja 17 lipca 2026)

Dlaczego monitoring Next.js na produkcji jest konieczny?

Monitoring w Next.js łączy kilka sygnałów: i logi, metryki techniczne oraz RUM (Core Web Vitals, TTFB, czasy odpowiedzi), requestów, a także syntetyczne testy dostępności wykonywane spoza aplikacji. Każdy odpowiada na inne pytanie i żaden samodzielnie nie daje pełnego obrazu produkcji.

Sentry w Next.js: error tracking i monitoring wydajności

Sentry jest popularnym narzędziem do error trackingu, które skutecznie łapie błędy JavaScript, błędy Server Components, crashe Server Actions i monitoruje wydajność. Same Server Actions warto dodatkowo pokryć testami walidacji i efektów ubocznych, monitoring wyłapie błędy na produkcji, ale testy łapią je wcześniej.

Instalacja Sentry w Next.js przez wizard

Code
npx @sentry/wizard@latest -i nextjs

W aktualnym setupie dla Next.js 15+ wizard tworzy lub aktualizuje: instrumentation-client.ts, sentry.server.config.ts, sentry.edge.config.ts, instrumentation.ts, app/global-error.tsx oraz wrapper withSentryConfig w next.config.ts. Po uruchomieniu zawsze przejrzyj diff — wizard może również dodać testową trasę albo ustawienia, których nie chcesz utrzymywać.

Konfiguracja Sentry po stronie klienta

Code
// instrumentation-client.ts
import * as Sentry from '@sentry/nextjs'
 
Sentry.init({
  dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
  environment:
    process.env.NEXT_PUBLIC_SENTRY_ENVIRONMENT ?? process.env.NODE_ENV,
 
  // Performance monitoring
  tracesSampleRate: 0.1, // 10% requestów — produkcja
  replaysSessionSampleRate: 0.01, // 1% sesji nagrywanych
  replaysOnErrorSampleRate: 1.0, // 100% sesji z błędem nagrywanych
 
  integrations: [
    Sentry.replayIntegration({
      maskAllText: true,
      maskAllInputs: true,
      blockAllMedia: true,
    }),
  ],
})
 
// Łączy nawigacje App Routera z tracingiem po stronie klienta.
export const onRouterTransitionStart = Sentry.captureRouterTransitionStart

Domyślne maskowanie Replay jest bezpieczniejszym punktem wyjścia. Nie wyłączaj go globalnie tylko po to, żeby nagrania były czytelniejsze. Najpierw ustal podstawę prawną i retencję, usuń dane osobowe przez konfigurację SDK oraz panel Sentry, a dopiero potem selektywnie odsłaniaj elementy, które nie zawierają treści użytkownika.

Konfiguracja Sentry po stronie serwera

Code
// sentry.server.config.ts
import * as Sentry from '@sentry/nextjs'
 
Sentry.init({
  dsn: process.env.SENTRY_DSN, // DSN nie jest sekretem, ale ten wariant nie trafia do klienta
  environment: process.env.SENTRY_ENVIRONMENT ?? process.env.NODE_ENV,
  tracesSampleRate: 0.2,
})

Instrumentation Sentry w App Router przez instrumentation.ts

Code
// instrumentation.ts — w katalogu głównym projektu albo w src/
import * as Sentry from '@sentry/nextjs'
 
export async function register() {
  if (process.env.NEXT_RUNTIME === 'nodejs') {
    await import('./sentry.server.config')
  }
 
  if (process.env.NEXT_RUNTIME === 'edge') {
    await import('./sentry.edge.config')
  }
}
 
// Błędy Server Components, requestów i Proxy z kontekstem znanym Next.js.
export const onRequestError = Sentry.captureRequestError

Nie odtwarzaj ręcznie sygnatury onRequestError: zmieniała się wraz z Next.js, a captureRequestError zachowuje kontekst requestu oczekiwany przez SDK. Wymaga co najmniej Next.js 15 i @sentry/nextjs 8.28.0; dla nowych projektów użyj aktualnej wersji SDK.

Source maps i release po deploymencie

Bez map źródeł (source maps) komunikat błędu z przeglądarki wskazuje na zminifikowany plik zamiast na kod źródłowy TypeScript. Konfiguracja withSentryConfig pozwala na automatyczne przesłanie artefaktów podczas budowania, jednak proces CI wymaga bezpiecznego dostępu do tokena SENTRY_AUTH_TOKEN. Ponieważ jest to poufny sekret, nie wolno umieszczać go w zmiennych typu NEXT_PUBLIC_, logach ani w repozytorium kodu. Po zakończeniu wdrożenia warto wywołać kontrolowany błąd testowy i upewnić się w panelu Sentry, że zgłoszenie zawiera prawidłowe środowisko, wersję wydania, informacje o commicie oraz poprawnie zmapowane linie kodu.

Ręczne raportowanie błędów w Server Actions

Code
// app/actions/process-payment.ts
'use server'
 
import * as Sentry from '@sentry/nextjs'
import { requireUser } from '@/lib/auth'
import { paymentSchema, paymentService } from '@/lib/payments'
import { headers } from 'next/headers'
 
export async function processPayment(formData: FormData) {
  const session = await requireUser()
  const input = paymentSchema.parse(Object.fromEntries(formData))
 
  return Sentry.withServerActionInstrumentation(
    'processPayment',
    { headers: await headers() },
    async () => {
      try {
        await paymentService.process(input, session.user.id)
        return { success: true }
      } catch (error) {
        // Błąd jest zamieniany na wynik, więc raportujemy go jawnie.
        Sentry.captureException(error, {
          tags: { action: 'processPayment' },
          user: { id: session.user.id },
        })
 
        return { success: false, error: 'Płatność nie powiodła się' }
      }
    },
  )
}

Nie dołączaj automatycznie całego FormData, odpowiedzi, tokenów płatniczych ani e-maila użytkownika. Argumenty formData i recordResponse dostępne w helperze Sentry mogą być użyteczne diagnostycznie, ale najpierw wymagają przeglądu danych i zasad retencji. Nierzucone błędy requestu przejmie onRequestError; ręczne captureException jest potrzebne przede wszystkim wtedy, gdy świadomie łapiesz wyjątek i zwracasz oczekiwany wynik.

Error boundary z integracją Sentry

Code
// app/global-error.tsx
'use client'
 
import * as Sentry from '@sentry/nextjs'
import { useEffect } from 'react'
 
export default function GlobalError({
  error,
  reset,
}: {
  error: Error & { digest?: string }
  reset: () => void
}) {
  useEffect(() => {
    Sentry.captureException(error)
  }, [error])
 
  return (
    <html>
      <body>
        <div className="flex min-h-screen items-center justify-center">
          <div className="text-center">
            <h2 className="text-2xl font-bold">Coś poszło nie tak</h2>
            <p className="mt-2 text-gray-600">
              Błąd został zgłoszony automatycznie.
            </p>
            <button
              onClick={reset}
              className="mt-4 rounded-lg bg-blue-600 px-4 py-2 text-white"
            >
              Spróbuj ponownie
            </button>
          </div>
        </div>
      </body>
    </html>
  )
}

Vercel Analytics i Speed Insights dla Core Web Vitals

Jeśli hostujesz na Vercelu, to w takiej sytuacji wbudowane narzędzia monitorują Core Web Vitals z danych real-user:

Code
npm install @vercel/analytics @vercel/speed-insights
Code
// app/layout.tsx
import { Analytics } from '@vercel/analytics/next'
import { SpeedInsights } from '@vercel/speed-insights/next'
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="pl">
      <body>
        {children}
        <Analytics />
        <SpeedInsights />
      </body>
    </html>
  )
}

Sama instalacja komponentów nie wystarcza: włącz oba produkty w panelu projektu i wykonaj nowy deployment. Jeśli ruch przechodzi przez dodatkowe proxy lub CDN, upewnij się, że nie blokuje ono tras zbierających dane Vercela.

Analytics mierzy odsłony, odwiedzających i źródła ruchu. Speed Insights monitoruje LCP, INP, CLS, FCP i TTFB z danych realnych użytkowników. Core Web Vitals oceniaj na 75. percentylu, osobno co najmniej dla urządzeń mobilnych i desktopowych: dobry wynik to LCP do 2,5 s, INP do 200 ms i CLS do 0,1. Pojedynczy wolny pomiar nie oznacza regresji, a średnia potrafi ukryć złą jakość doświadczenia części użytkowników. Monitoring pokazuje, które metryki kuleją, a jak je realnie poprawić, opisałem w przewodniku o Core Web Vitals, a osobno o INP i wzorcach, które je psują.

Własny reporter Web Vitals w Next.js bez Vercela

Jeśli nie używasz Vercela, w App Routerze najczyściej skorzystać z useReportWebVitals w osobnym komponencie klienckim:

Code
// app/_components/web-vitals.tsx
'use client'
 
import { useReportWebVitals } from 'next/web-vitals'
 
type ReportWebVitalsCallback = Parameters<typeof useReportWebVitals>[0]
 
// Stała referencja callbacku zapobiega ponownemu raportowaniu metryk po renderze.
const reportWebVitals: ReportWebVitalsCallback = (metric) => {
  const body = JSON.stringify({
    name: metric.name,
    value: metric.value,
    delta: metric.delta,
    rating: metric.rating,
    id: metric.id,
    navigationType: metric.navigationType,
    page: window.location.pathname,
  })
 
  const endpoint = '/api/vitals'
 
  if (navigator.sendBeacon) {
    const payload = new Blob([body], { type: 'application/json' })
    navigator.sendBeacon(endpoint, payload)
    return
  }
 
  fetch(endpoint, {
    method: 'POST',
    body,
    headers: { 'Content-Type': 'application/json' },
    keepalive: true,
  })
}
 
export function WebVitals() {
  useReportWebVitals(reportWebVitals)
 
  return null
}

Endpoint /api/vitals również jest publicznym wejściem. Zadbaj o walidację nazw oraz zakresów metryk, kontroluj rozmiar treści żądań (body) oraz częstotliwość zapytań i unikaj zapisywania pełnych adresów URL zawierających parametry z danymi użytkowników. Samo zbieranie próbek to dopiero początek. Agreguj percentyle z uwzględnieniem trasy, urządzenia, kraju oraz wersji wdrożenia, zamiast ustawiać alerty na pojedyncze zgłoszenia.

Code
// app/layout.tsx
import { WebVitals } from './_components/web-vitals'
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="pl">
      <body>
        <WebVitals />
        {children}
      </body>
    </html>
  )
}

OpenTelemetry w Next.js i distributed tracing requestów

Nowoczesny App Router ma natywne wsparcie dla , czyli standardu śledzenia requestów między warstwami aplikacji i usługami. Najprostszy setup rekomendowany przez dokumentację Next.js wykorzystuje @vercel/otel:

Code
npm install @vercel/otel @opentelemetry/sdk-logs @opentelemetry/api-logs @opentelemetry/instrumentation
Code
// instrumentation.ts
import { registerOTel } from '@vercel/otel'
 
export function register() {
  registerOTel({ serviceName: 'example-next-app' })
}

Next.js tworzy domyślne spany m.in. dla żądania, renderowania App Routera, fetch() i Route Handlerów. Nie oznacza to automatycznego pokrycia zapytań do każdej bazy, kolejki czy operacji domenowej, potrzebujesz kompatybilnej instrumentacji biblioteki albo własnych spanów. Atrybuty spanów powinny mieć małą kardynalność i nie mogą zawierać tokenów, pełnych treści zapytań ani danych osobowych.

Code
import { trace, SpanStatusCode } from '@opentelemetry/api'
 
const tracer = trace.getTracer('example-next-app')
 
export async function createOrder(input: CreateOrderInput) {
  return tracer.startActiveSpan('orders.create', async (span) => {
    try {
      const order = await db.order.create({ data: input })
      span.setAttribute('order.status', order.status)
      return order
    } catch (error) {
      span.recordException(error as Error)
      span.setStatus({ code: SpanStatusCode.ERROR })
      throw error
    } finally {
      span.end()
    }
  })
}

Pełny tracing rozproszony wymaga też propagacji kontekstu między usługami, zwykle przez nagłówki W3C Trace Context, oraz instrumentacji po obu stronach połączenia. Przy własnym hostingu warto wysyłać dane przez OpenTelemetry Collector do Jaegera, Grafana Tempo, Honeycomb lub innego backendu, zamiast wiązać aplikację bezpośrednio z jednym dostawcą. Jeśli używasz ręcznie skonfigurowanego NodeSDK, dokumentacja Next.js zaleca inicjalizować go w instrumentation.node.ts importowanym warunkowo dla środowiska Node; ten SDK nie działa w Edge Runtime.

Nie uruchamiaj bez sprawdzenia dwóch niezależnych providerów tracingu, np. osobnego SDK Sentry i osobnego NodeSDK, ponieważ mogą dublować spany albo nadpisywać globalnego providera. Integrację trzeba zweryfikować na stagingu i wtedy zdecydować, które narzędzie odpowiada za eksport śladów.

Logi strukturalne i korelacja z trace'ami

Tracing nie zastępuje logów. Na produkcji zapisuj logi strukturalne, najlepiej jako JSON, ze stałymi polami takimi jak level, event, route, requestId lub traceId, release, durationMs i wynik operacji. Dzięki identyfikatorowi śladu przejdziesz z alertu do konkretnego trace'a i powiązanych logów bez szukania po samym czasie.

Nie loguj nagłówka Authorization, cookies, tokenów, pełnego FormData, treści płatności ani danych osobowych. Kontroluj też retencję i dostęp do logów. W środowisku serverless logi powinny trafiać na standardowe wyjście obsługiwane przez hosting albo do wybranego backendu; lokalny plik nie jest trwałym magazynem.

Monitoring uptime i alerty dla aplikacji Next.js

Cały powyższy stos zakłada, że strona w ogóle odpowiada. Najgroźniejszy scenariusz, czyli całkowita niedostępność, wymaga osobnego, zewnętrznego narzędzia, więc monitoring uptime żyje poza aplikacją. Usługa typu UptimeRobot, BetterStack czy Pingdom odpytuje wybrany URL co minutę z zewnątrz i alarmuje, gdy przestaje odpowiadać albo zwraca błąd.

W praktyce rozdziel płytki endpoint liveness, np. /api/health/live, od readiness, np. /api/health/ready, który sprawdza tylko zależności niezbędne do obsługi ruchu. Odpowiedzi powinny omijać cache (Cache-Control: no-store) i nie ujawniać wersji, sekretów ani szczegółów infrastruktury. W środowisku serverless sam liveness ma ograniczoną wartość, dlatego zewnętrzny monitor powinien dodatkowo otwierać publiczną stronę lub wykonywać krytyczny scenariusz co 1–5 minut.

Alert ma wartość tylko wtedy, gdy trafia do osoby, która może zareagować. Progi ustalaj względem SLO i normalnego ruchu: pojedynczy błąd 500 albo pojedynczy słaby pomiar LCP zwykle nie powinien budzić zespołu. Reguła powinna uwzględniać okno czasowe, minimalny wolumen i kilka kolejnych nieudanych pomiarów. Dla każdego alertu określ właściciela, kanał, instrukcję reakcji i okna prac serwisowych; inaczej szybko pojawi się zmęczenie alarmami.

To właśnie dlatego error tracking i uptime są „minimum": wyłapują dwa najdotkliwsze problemy, takie jak niewidoczne crashe i całkowitą niedostępność, przy najmniejszym nakładzie konfiguracji.

Co monitorować w aplikacji Next.js: lista kontrolna alertów

CoNarzędziePrzykładowy warunek alertu
Błędy JavaScript (klient)SentryNowa regresja lub wzrost error rate ponad baseline
Błędy serwera (5xx)Sentry / logiError rate > 1% przez 5 min przy minimalnym ruchu
Core Web Vitals (LCP)Speed Insights / customp75 > 2,5 s w ustalonym oknie i segmencie
Czas odpowiedzi APIOpenTelemetryp95 przekracza SLO przez 5–10 min
UptimeUptimeRobot / BetterStack2–3 kolejne błędy, najlepiej z kilku lokalizacji
Certyfikat SSLUptimeRobot14 dni przed wygaśnięciem
Zużycie zasobówPanel hostinguUtrzymujące się nasycenie CPU lub pamięci

Wartości tabelaryczne sprawdzą się na początek. Przykładowo, system alertów Vercela zależy od wybranego planu i bazuje raczej na wykrywaniu anomalii niż na prostych regułach dla dowolnej metryki ze Speed Insights. W sytuacji, gdy wybrane narzędzie nie obsługuje potrzebnego progu, przekaż zagregowane metryki do dedykowanego systemu alertowego.

Próbkowanie bez utraty najważniejszych sygnałów

Próbkowanie błędów, trace'ów i nagrań sesji to osobne decyzje. Błędy warto zachować możliwie szeroko, a kosztowne trace'y i replaye ograniczać zależnie od środowiska, trasy i rodzaju operacji. W tracingu rozproszonym stosuj próbkowanie respektujące decyzję rodzica, aby jeden request nie urywał się w połowie łańcucha usług. Po zmianie sample rate sprawdzaj nie tylko rachunek, ale też czy nadal widzisz rzadkie, istotne regresje.

Audyt techniczny i optymalizacja pod kątem SEO i GEO.
Audyt techniczny SEO

Często zadawane pytania

Czy Sentry spowalnia stronę?

Zwykle minimalnie, ale realny wpływ zależy od włączonych funkcji, np. sam error tracking jest lekki. Największy koszt po stronie klienta generują nagrania sesji (session replay) i zbyt wysoki sample rate dla tracingu. Dlatego warto włączać te funkcje selektywnie, czyli przykładowo nagrywać tylko ułamek sesji, a pełne 100% rezerwować dla sesji zakończonych błędem. Replay wymaga też oceny prywatności: pozostaw maskowanie tekstu, pól formularzy i mediów, a wyjątki konfiguruj tylko dla danych, które na pewno nie są poufne.

Ile kosztuje monitoring?

To zależy od dostawcy, wolumenu zdarzeń oraz tego, czy płacisz za tracing, nagrania sesji i retencję danych. Plany darmowe zwykle wystarczają małym projektom, ale przy większym ruchu liczba zdarzeń szybko rośnie. Przed wyborem sprawdź aktualny cennik oraz limity, bo najczęściej to właśnie liczba błędów, transakcji tracingu i czas przechowywania danych decydują o rachunku.

Czy OpenTelemetry jest konieczny?

Nie dla małych projektów. OpenTelemetry zaczyna mieć sens, gdy masz wiele współpracujących serwisów (API, bazę, kolejki, zewnętrzne integracje) i potrzebujesz prześledzić pojedynczy request przez cały ten łańcuch. Dla monolitycznej aplikacji Next.js połączenie Sentry i Speed Insights zwykle w zupełności wystarcza, a dodawanie tracingu rozproszonego byłoby przedwczesną komplikacją.

Czym różni się monitoring od analityki?

Analityka (np. liczba wizyt, źródła ruchu) odpowiada na pytania biznesowe o zachowanie użytkowników. Monitoring odpowiada na pytania techniczne o zdrowie aplikacji: czy są błędy, jak szybko ładują się strony u realnych użytkowników, czy API odpowiada w akceptowalnym czasie. Oba są potrzebne, ale to monitoring budzi Cię w nocy, gdy coś przestaje działać, więc to od niego warto zacząć.

Od czego zacząć monitoring w nowym projekcie?

Od dwóch rzeczy: error trackingu (np. Sentry) i monitoringu dostępności (uptime). To minimum, które wyłapie najczęstsze i najdotkliwsze problemy, czyli niewidoczne crashe oraz całkowite niedostępności strony. Performance monitoring z Core Web Vitals dołóż wcześnie, jeśli zależy Ci na SEO. Tracing rozproszony zostaw na moment, gdy architektura faktycznie urośnie do wielu serwisów.

Jak monitorować uptime aplikacji Next.js?

Monitoringu dostępności nie da się zrobić wewnątrz aplikacji, w sytuacji, gdy serwer leży, nie zaraportuje własnej awarii. Potrzebujesz zewnętrznej usługi (UptimeRobot, BetterStack, Pingdom), która odpytuje Twój URL co 1–5 minut z zewnątrz i alarmuje przy braku odpowiedzi lub błędzie. Dobrą praktyką jest rozdzielenie prostego liveness (czy proces odpowiada) od readiness (czy aplikacja może obsłużyć ruch, w tym używane przez nią kluczowe zależności). Nie każda zewnętrzna integracja musi blokować readiness, więc sprawdzaj tylko zależności niezbędne do obsługi danego ruchu. Warto poza tym monitorować prawdziwą stronę lub krytyczny scenariusz, bo sam endpoint zdrowia nie wykryje błędu renderowania UI.

O autorze

Maciej Sala

Maciej Sala — Product Manager i Frontend Developer z bogatym doświadczeniem w marketingu internetowym oraz SEO. Na co dzień pracuje z Reactem, Next.js i TypeScriptem, a ostatnio także z Astro i narzędziami do automatyzacji procesów AI. Sprawnie łączy perspektywę produktową z praktycznym podejściem do kodu. Przez kilka lat był związany z branżą gier wideo jako project manager i game designer. Absolwent historii na Uniwersytecie Jagiellońskim oraz studiów podyplomowych z marketingu internetowego na AGH w Krakowie. Po godzinach trenuje na siłowni, maluje figurki i rozwijam własne projekty.

Pomagam przekładać takie tematy na konkretne wdrożenia w frontendzie, SEO, analityce i procesie produktowym.

Skontaktuj się ze mną

Biblioteka wiedzy na temat Next.js

Czytaj dalej

Zobacz więcej wpisów
Core Web Vitals — jak przyspieszyć stronę i poprawić pozycję w Google

Google od wielu lat podkreśla, że wydajność i doświadczenie użytkownika mają znaczenie i Core Web Vitals są częścią sygnałów page experience. Najważniejsze jest jednak to, że wreszcie mierzą coś bardzo konkretnego: czy strona błyskawicznie pokazuje treść, czy odpowiednio reaguje na interakcje i czy nie „skacze" nieprzyjemnie podczas ładowania.

Maciej Sala

Maciej Sala

Founder StriveLab

Backend dla frontendowca: Cache, deployment, security

To trzecia część mojej małej serii „Backend dla frontendowca” i po fundamentach API oraz tematach real-time, webhooków i uwierzytelniania zostaje warstwa, która decyduje o tym, czy aplikacja wytrzyma prawdziwe użytkowanie: cache , kolejki, pliki, deployment, monitoring i bezpieczeństwo .

Maciej Sala

Maciej Sala

Founder StriveLab

Jak osiągnąć 100/100 w Lighthouse Performance w Next.js? Case study

Mamy tu typową stronę usługową zbudowaną w Next.js, wyposażoną w Google Analytics, Google Tag Manager , prosty formularz kontaktowy, galerię zdjęć i animacje. Na początku testy PageSpeed Insights pokazały wyniki: 67/100 na urządzeniach mobilnych i 89/100 na komputerach stacjonarnych.

Maciej Sala

Maciej Sala

Founder StriveLab