Utilizziamo i cookie per migliorare la tua esperienza sul sito
CodeWorlds

Monitoring wydajności i analityka błędów

W produkcyjnych aplikacjach Next.js, monitoring wydajności i śledzenie błędów są kluczowe dla utrzymania wysokiej jakości dostarczanych usług. W tym module poznamy narzędzia i techniki, które pozwolą skutecznie monitorować aplikację, wykrywać problemy przed użytkownikami i optymalizować wydajność na bieżąco.

Znaczenie monitoringu w aplikacjach produkcyjnych

Monitoring to proces ciągłego obserwowania aplikacji w celu:

  1. Wczesnego wykrywania problemów - zanim dotkną użytkowników
  2. Śledzenia wydajności - czasy ładowania, Core Web Vitals, przepustowość
  3. Analizy zachowań użytkowników - jak użytkownicy korzystają z aplikacji
  4. Identyfikacji waskóch gardeł - miejsca wymagające optymalizacji
  5. Zapewnienia SLA - spełniania uzgodnionych poziomów usług

Typy monitoringu

1. Monitoring wydajności (Performance Monitoring)

  • Core Web Vitals (LCP, FID, CLS)
  • Czasy ładowania stron
  • Czasy odpowiedzi API
  • Zużycie zasobów (CPU, pamięć)

2. Monitoring błędów (Error Monitoring)

  • JavaScript errors
  • API errors
  • Network errors
  • Stack traces

3. Monitoring infrastruktury

  • Dostępność serwerów
  • Zużycie zasobów
  • Metryki sieciowe

4. User Experience Monitoring (UX)

  • Real User Monitoring (RUM)
  • Session recordings
  • Heat maps
  • User flows

Integracja z Sentry dla monitoring błędów

Sentry to jedno z najpopularniejszych narzędzi do monitoringu błędów w aplikacjach JavaScript i Next.js.

1. Instalacja i konfiguracja Sentry

1npm install @sentry/nextjs
2# lub
3yarn add @sentry/nextjs

Utwórz pliki konfiguracyjne:

1// sentry.client.config.js
2import * as Sentry from '@sentry/nextjs';
3
4Sentry.init({
5  dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
6  
7  // Ustawienia środowiska
8  environment: process.env.NODE_ENV,
9  
10  // Sample rate dla transakcji wydajnościowych
11  tracesSampleRate: 1.0,
12  
13  // Włącz Replay dla sesji użytkowników
14  replaysSessionSampleRate: 0.1, // 10% sesji
15  replaysOnErrorSampleRate: 1.0,  // 100% sesji z błędami
16  
17  // Integracje
18  integrations: [
19    new Sentry.Replay({
20      maskAllText: false,
21      blockAllMedia: false,
22    }),
23  ],
24  
25  // Filtrowanie błędów
26  beforeSend(event) {
27    // Ignoruj pewne typy błędów
28    if (event.exception) {
29      const error = event.exception.values[0];
30      if (error.type === 'ChunkLoadError') {
31        return null; // Ignoruj błędy ładowania chunków
32      }
33    }
34    return event;
35  },
36});
1// sentry.server.config.js
2import * as Sentry from '@sentry/nextjs';
3
4Sentry.init({
5  dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
6  environment: process.env.NODE_ENV,
7  tracesSampleRate: 1.0,
8  
9  // Dodatkowe opcje dla serwera
10  debug: false,
11  
12  // Integracje specyficzne dla Node.js
13  integrations: [
14    new Sentry.Integrations.Http({ tracing: true }),
15  ],
16});
1// sentry.edge.config.js
2import * as Sentry from '@sentry/nextjs';
3
4Sentry.init({
5  dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
6  environment: process.env.NODE_ENV,
7  tracesSampleRate: 1.0,
8  debug: false,
9});

2. Konfiguracja Next.js dla Sentry

1// next.config.js
2const { withSentryConfig } = require('@sentry/nextjs');
3
4const nextConfig = {
5  // Twoja standardowa konfiguracja Next.js
6};
7
8const sentryWebpackPluginOptions = {
9  // Dodatkowe opcje konfiguracyjne dla Sentry webpack plugin
10  silent: true, // Wycisz logi Sentry podczas budowania
11  org: process.env.SENTRY_ORG,
12  project: process.env.SENTRY_PROJECT,
13};
14
15module.exports = withSentryConfig(nextConfig, sentryWebpackPluginOptions);

3. Dodawanie kontekstu do błędów

1// lib/sentry.ts
2import * as Sentry from '@sentry/nextjs';
3
4// Funkcja do dodawania kontekstu użytkownika
5export function setUserContext(user: { id: string; email: string; name?: string }) {
6  Sentry.setUser({
7    id: user.id,
8    email: user.email,
9    username: user.name,
10  });
11}
12
13// Funkcja do dodawania tagów
14export function addTags(tags: Record<string, string>) {
15  Sentry.setTags(tags);
16}
17
18// Funkcja do manualnego raportowania błędów
19export function captureException(error: Error, context?: Record<string, any>) {
20  Sentry.withScope((scope) => {
21    if (context) {
22      scope.setContext('additional_info', context);
23    }
24    Sentry.captureException(error);
25  });
26}
27
28// Funkcja do śledzenia wydajności
29export function startTransaction(name: string, op: string) {
30  return Sentry.startTransaction({ name, op });
31}

4. Wykorzystanie Sentry w komponentach

1// components/ErrorBoundary.tsx
2'use client';
3
4import React from 'react';
5import * as Sentry from '@sentry/nextjs';
6
7interface ErrorBoundaryState {
8  hasError: boolean;
9  eventId?: string;
10}
11
12class ErrorBoundary extends React.Component<
13  React.PropsWithChildren<{}>,
14  ErrorBoundaryState
15> {
16  constructor(props: React.PropsWithChildren<{}>) {
17    super(props);
18    this.state = { hasError: false };
19  }
20
21  static getDerivedStateFromError(_: Error): ErrorBoundaryState {
22    return { hasError: true };
23  }
24
25  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
26    const eventId = Sentry.captureException(error, {
27      contexts: {
28        react: {
29          componentStack: errorInfo.componentStack,
30        },
31      },
32    });
33    
34    this.setState({ eventId });
35  }
36
37  render() {
38    if (this.state.hasError) {
39      return (
40        <div className="error-fallback">
41          <h2>Ups! Coś poszło nie tak</h2>
42          <p>Wystąpił nieoczekiwany błąd. Nasz zespół został powiadomiony.</p>
43          <details style={{ whiteSpace: 'pre-wrap' }}>
44            ID błędu: {this.state.eventId}
45          </details>
46          <button onClick={() => window.location.reload()}>
47            Odśwież stronę
48          </button>
49        </div>
50      );
51    }
52
53    return this.props.children;
54  }
55}
56
57export default ErrorBoundary;

Monitoring Core Web Vitals

Core Web Vitals to kluczowe metryki wydajności, które Google używa do oceny doswiadczenia użytkownika:

  • LCP (Largest Contentful Paint) - czas ładowania największego elementu
  • FID (First Input Delay) - czas odpowiedzi na pierwszą interakcję
  • CLS (Cumulative Layout Shift) - niestabilność layoutu

1. Implementacja Web Vitals w Next.js

1// lib/web-vitals.ts
2import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';
3import * as Sentry from '@sentry/nextjs';
4
5interface WebVitalMetric {
6  name: string;
7  value: number;
8  id: string;
9  delta: number;
10}
11
12// Funkcja do raportowania metryk
13function sendToAnalytics(metric: WebVitalMetric) {
14  // Wyślij do Google Analytics
15  if (typeof window !== 'undefined' && window.gtag) {
16    window.gtag('event', metric.name, {
17      event_category: 'Web Vitals',
18      event_label: metric.id,
19      value: Math.round(metric.name === 'CLS' ? metric.value * 1000 : metric.value),
20      non_interaction: true,
21    });
22  }
23  
24  // Wyślij do Sentry
25  Sentry.addBreadcrumb({
26    category: 'web-vital',
27    message: `${metric.name}: ${metric.value}`,
28    level: 'info',
29    data: {
30      name: metric.name,
31      value: metric.value,
32      id: metric.id,
33      delta: metric.delta,
34    },
35  });
36  
37  // W przypadku słabych wyników, wyślij jako event do Sentry
38  const thresholds = {
39    LCP: 2500,
40    FID: 100,
41    CLS: 0.1,
42  };
43  
44  if (metric.value > thresholds[metric.name as keyof typeof thresholds]) {
45    Sentry.captureMessage(`Poor ${metric.name}: ${metric.value}`, 'warning');
46  }
47}
48
49// Zainicjuj pomiary Web Vitals
50export function initWebVitals() {
51  getCLS(sendToAnalytics);
52  getFID(sendToAnalytics);
53  getFCP(sendToAnalytics);
54  getLCP(sendToAnalytics);
55  getTTFB(sendToAnalytics);
56}

2. Komponent do monitoringu Web Vitals

1// components/WebVitalsReporter.tsx
2'use client';
3
4import { useEffect } from 'react';
5import { useReportWebVitals } from 'next/web-vitals';
6
7export default function WebVitalsReporter() {
8  useReportWebVitals((metric) => {
9    // Wyślij do różnych systemów analitycznych
10    switch (metric.name) {
11      case 'FCP':
12      case 'LCP':
13      case 'CLS':
14      case 'FID':
15      case 'TTFB':
16        // Wyślij do własnego API
17        fetch('/api/analytics/web-vitals', {
18          method: 'POST',
19          headers: { 'Content-Type': 'application/json' },
20          body: JSON.stringify(metric),
21        }).catch(console.error);
22        break;
23      default:
24        break;
25    }
26  });
27  
28  return null;
29}

3. API endpoint do zbierania metryk

1// app/api/analytics/web-vitals/route.ts
2import { NextRequest, NextResponse } from 'next/server';
3import { prisma } from '@/lib/prisma'; // Założmy, że używasz Prisma
4
5export async function POST(request: NextRequest) {
6  try {
7    const metric = await request.json();
8    
9    // Walidacja podstawowa
10    if (!metric.name || !metric.value) {
11      return NextResponse.json({ error: 'Invalid metric data' }, { status: 400 });
12    }
13    
14    // Zapisz do bazy danych
15    await prisma.webVitalMetric.create({
16      data: {
17        name: metric.name,
18        value: metric.value,
19        id: metric.id,
20        delta: metric.delta,
21        url: request.headers.get('referer') || '',
22        userAgent: request.headers.get('user-agent') || '',
23        timestamp: new Date(),
24      },
25    });
26    
27    return NextResponse.json({ success: true });
28  } catch (error) {
29    console.error('Error saving web vital metric:', error);
30    return NextResponse.json({ error: 'Internal server error' }, { status: 500 });
31  }
32}

Monitoring wydajności z New Relic

New Relic oferuje kompleksowe rozwiązanie do monitoringu wydajności aplikacji Next.js.

1. Konfiguracja New Relic

1npm install newrelic
1// newrelic.js (w root projektu)
2'use strict';
3
4exports.config = {
5  app_name: ['Next.js App'],
6  license_key: process.env.NEW_RELIC_LICENSE_KEY,
7  
8  // Konfiguracja logowania
9  logging: {
10    level: 'info',
11  },
12  
13  // Włącz distributed tracing
14  distributed_tracing: {
15    enabled: true,
16  },
17  
18  // Monitoring browserów
19  browser_monitoring: {
20    enable: true,
21  },
22  
23  // Konfiguracja specyficzna dla Next.js
24  allow_all_headers: true,
25  attributes: {
26    exclude: [
27      'request.headers.cookie',
28      'request.headers.authorization',
29      'request.headers.x-*',
30    ],
31  },
32};
1// next.config.js
2if (process.env.NODE_ENV === 'production') {
3  require('./newrelic');
4}
5
6const nextConfig = {
7  // Twoja konfiguracja Next.js
8};
9
10module.exports = nextConfig;

2. Custom instrumentation w New Relic

1// lib/monitoring.ts
2import newrelic from 'newrelic';
3
4// Funkcja do śledzenia niestandardowych metryk
5export function recordCustomMetric(name: string, value: number) {
6  if (process.env.NODE_ENV === 'production') {
7    newrelic.recordMetric(name, value);
8  }
9}
10
11// Funkcja do śledzenia eventów biznesowych
12export function recordCustomEvent(eventType: string, attributes: Record<string, any>) {
13  if (process.env.NODE_ENV === 'production') {
14    newrelic.recordCustomEvent(eventType, attributes);
15  }
16}
17
18// Wrapper dla API calls z automatycznym śledzeniem
19export async function instrumentedApiCall<T>(
20  name: string,
21  apiCall: () => Promise<T>
22): Promise<T> {
23  const startTime = Date.now();
24  
25  try {
26    const result = await apiCall();
27    
28    // Zapisz metryki sukcesu
29    recordCustomMetric(`Custom/API/${name}/Duration`, Date.now() - startTime);
30    recordCustomEvent('APICall', {
31      name,
32      status: 'success',
33      duration: Date.now() - startTime,
34    });
35    
36    return result;
37  } catch (error) {
38    // Zapisz metryki błędu
39    recordCustomMetric(`Custom/API/${name}/Error`, 1);
40    recordCustomEvent('APICall', {
41      name,
42      status: 'error',
43      duration: Date.now() - startTime,
44      error: error.message,
45    });
46    
47    throw error;
48  }
49}

Vercel Analytics - Monitoring nowej generacji w Metropolii Quantum

W Metropolii Quantum 2150, każda interakcja użytkownika jest monitorowana przez zaawansowane systemy analityczne. Vercel Analytics to natywne rozwiązanie monitoringu dla aplikacji Next.js, zaprojektowane z myślą o prywatności użytkowników i zgodności z GDPR. W przeciwieństwie do Google Analytics, Vercel Analytics działa bezpośrednio w Vercel Edge Network, co zapewnia minimalne opóźnienia i dokładne pomiary.

Vercel Analytics vs Google Analytics

Zanim zdecydujesz się na wybór narzędzia analitycznego, warto zrozumieć kluczowe różnice:

Vercel Analytics:

  • ✅ Zintegrowany z Vercel Edge Network
  • ✅ Automatyczne śledzenie Core Web Vitals
  • ✅ Privacy-first (bez cookies, zgodny z GDPR)
  • ✅ Zerowa konfiguracja dla podstawowych metryk
  • ✅ Real-time data w dashboard Vercel
  • ❌ Płatny plan dla większej liczby page views
  • ❌ Podstawowe funkcje segmentacji

Google Analytics 4:

  • ✅ Zaawansowana segmentacja użytkowników
  • ✅ Funnel analysis i conversion tracking
  • ✅ Integracja z Google Ads i innymi narzędziami Google
  • ✅ Darmowy plan dla większości przypadków użycia
  • ❌ Wymaga zgody na cookies (GDPR compliance)
  • ❌ Większy wpływ na wydajność strony
  • ❌ Bardziej skomplikowana konfiguracja

Real User Monitoring (RUM) z Vercel Analytics

Real User Monitoring to technika zbierania danych o rzeczywistych doświadczeniach użytkowników, a nie syntetycznych testów. Vercel Analytics automatycznie śledzi wszystkie kluczowe metryki wydajności.

Instalacja Vercel Analytics

1npm install @vercel/analytics
2# lub
3yarn add @vercel/analytics
4# lub
5pnpm add @vercel/analytics

Podstawowa konfiguracja

1// app/layout.tsx
2import { Analytics } from '@vercel/analytics/react';
3
4export default function RootLayout({
5  children
6}: {
7  children: React.ReactNode
8}) {
9  return (
10    <html lang="pl">
11      <body>
12        {children}
13        <Analytics />
14      </body>
15    </html>
16  );
17}

To wszystko! Vercel Analytics automatycznie rozpocznie śledzenie:

  • Page views
  • Unique visitors
  • Top pages
  • Top referrers
  • Devices i browsers
  • Geographic data

Core Web Vitals Tracking

Core Web Vitals to kluczowe metryki wydajności web, które Google używa jako sygnały rankingowe. Vercel Analytics automatycznie śledzi wszystkie trzy metryki:

1. Largest Contentful Paint (LCP)

  • Mierzy czas ładowania największego elementu content
  • Target: < 2.5s
  • Wpływa na: First impression, perceived performance

2. First Input Delay (FID) / Interaction to Next Paint (INP)

  • Mierzy responsywność na interakcje użytkownika
  • FID target: < 100ms
  • INP target: < 200ms
  • Wpływa na: Interaktywność, user experience

3. Cumulative Layout Shift (CLS)

  • Mierzy stabilność wizualną (niespodziewane przesunięcia layoutu)
  • Target: < 0.1
  • Wpływa na: Visual stability, user frustration

Dostęp do Core Web Vitals w kodzie

Możesz programatically dostać się do metryk Web Vitals:

1// app/web-vitals.tsx
2'use client';
3
4import { useReportWebVitals } from 'next/web-vitals';
5
6export function WebVitals() {
7  useReportWebVitals((metric) => {
8    console.log(metric);
9
10    // Możesz wysłać metryki do własnego endpointu
11    if (metric.label === 'web-vital') {
12      fetch('/api/analytics/web-vitals', {
13        method: 'POST',
14        body: JSON.stringify(metric),
15        headers: {
16          'Content-Type': 'application/json'
17        }
18      });
19    }
20  });
21
22  return null;
23}
1// app/layout.tsx
2import { Analytics } from '@vercel/analytics/react';
3import { WebVitals } from './web-vitals';
4
5export default function RootLayout({ children }: { children: React.ReactNode }) {
6  return (
7    <html lang="pl">
8      <body>
9        {children}
10        <Analytics />
11        <WebVitals />
12      </body>
13    </html>
14  );
15}

Custom Events - śledzenie niestandardowych interakcji

Vercel Analytics pozwala śledzić custom events dla specyficznych akcji użytkowników:

1'use client';
2
3import { track } from '@vercel/analytics';
4
5export default function NewsletterForm() {
6  const handleSubscribe = async (email: string) => {
7    try {
8      await subscribeToNewsletter(email);
9
10      // Śledź udaną subskrypcję
11      track('Newsletter Subscribe', {
12        email_domain: email.split('@')[1],
13        source: 'homepage'
14      });
15
16    } catch (error) {
17      // Śledź błąd
18      track('Newsletter Subscribe Error', {
19        error: error.message
20      });
21    }
22  };
23
24  return (
25    <form onSubmit={(e) => {
26      e.preventDefault();
27      const email = e.target.email.value;
28      handleSubscribe(email);
29    }}>
30      <input type="email" name="email" required />
31      <button type="submit">Subskrybuj</button>
32    </form>
33  );
34}

Zaawansowane custom events

1'use client';
2
3import { track } from '@vercel/analytics';
4
5export default function ProductPage({ product }: { product: Product }) {
6  // Śledź wyświetlenie produktu
7  useEffect(() => {
8    track('Product View', {
9      product_id: product.id,
10      product_name: product.name,
11      product_category: product.category,
12      product_price: product.price
13    });
14  }, [product]);
15
16  // Śledź dodanie do koszyka
17  const handleAddToCart = () => {
18    track('Add to Cart', {
19      product_id: product.id,
20      quantity: 1,
21      value: product.price
22    });
23
24    addToCart(product);
25  };
26
27  // Śledź rozpoczęcie checkout
28  const handleCheckout = () => {
29    track('Begin Checkout', {
30      value: cartTotal,
31      items: cartItems.length
32    });
33
34    router.push('/checkout');
35  };
36
37  return (
38    <div>
39      <h1>{product.name}</h1>
40      <p>{product.price} PLN</p>
41      <button onClick={handleAddToCart}>Dodaj do koszyka</button>
42      <button onClick={handleCheckout}>Kup teraz</button>
43    </div>
44  );
45}

Vercel Speed Insights - monitoring wydajności w czasie rzeczywistym

Speed Insights to rozszerzenie Analytics, które dostarcza szczegółowe informacje o wydajności aplikacji dla prawdziwych użytkowników.

Instalacja

1npm install @vercel/speed-insights

Konfiguracja

1// app/layout.tsx
2import { Analytics } from '@vercel/analytics/react';
3import { SpeedInsights } from '@vercel/speed-insights/next';
4
5export default function RootLayout({ children }: { children: React.ReactNode }) {
6  return (
7    <html lang="pl">
8      <body>
9        {children}
10        <Analytics />
11        <SpeedInsights />
12      </body>
13    </html>
14  );
15}

Co śledzi Speed Insights?

  1. Performance Score

    • Overall performance rating (0-100)
    • Based on Core Web Vitals i inne metryki
  2. Field Data (RUM)

    • Rzeczywiste dane od użytkowników
    • Segmentowane po device type, connection speed, geographic location
  3. Lab Data

    • Syntetyczne testy wydajności
    • Kontrolowane warunki dla consistency
  4. Opportunities

    • Konkretne sugestie optymalizacji
    • Estimated time savings

Kombinacja z innymi narzędziami analitycznymi

Możesz łączyć Vercel Analytics z innymi narzędziami dla pełniejszego obrazu:

1// app/layout.tsx
2import { Analytics } from '@vercel/analytics/react';
3import { SpeedInsights } from '@vercel/speed-insights/next';
4import { GoogleAnalytics } from '@next/third-parties/google';
5
6export default function RootLayout({ children }: { children: React.ReactNode }) {
7  return (
8    <html lang="pl">
9      <body>
10        {children}
11
12        {/* Vercel Analytics - dla Core Web Vitals i podstawowych metryk */}
13        <Analytics />
14        <SpeedInsights />
15
16        {/* Google Analytics - dla zaawansowanej segmentacji */}
17        <GoogleAnalytics gaId={process.env.NEXT_PUBLIC_GA_ID} />
18      </body>
19    </html>
20  );
21}

Best practices dla Vercel Analytics

1. Używaj environment-specific tracking

1// app/layout.tsx
2import { Analytics } from '@vercel/analytics/react';
3
4export default function RootLayout({ children }: { children: React.ReactNode }) {
5  return (
6    <html lang="pl">
7      <body>
8        {children}
9
10        {/* Tylko w produkcji */}
11        {process.env.NODE_ENV === 'production' && <Analytics />}
12      </body>
13    </html>
14  );
15}

2. Grupuj custom events logicznie

1// lib/analytics.ts
2import { track } from '@vercel/analytics';
3
4export const analytics = {
5  // User events
6  user: {
7    signup: (method: string) => track('User Signup', { method }),
8    login: (method: string) => track('User Login', { method }),
9    logout: () => track('User Logout')
10  },
11
12  // E-commerce events
13  ecommerce: {
14    viewProduct: (productId: string) =>
15      track('Product View', { product_id: productId }),
16    addToCart: (productId: string, value: number) =>
17      track('Add to Cart', { product_id: productId, value }),
18    purchase: (orderId: string, value: number) =>
19      track('Purchase', { order_id: orderId, value })
20  },
21
22  // Engagement events
23  engagement: {
24    shareContent: (contentType: string, method: string) =>
25      track('Share', { content_type: contentType, method }),
26    search: (query: string) =>
27      track('Search', { search_term: query }),
28    playVideo: (videoId: string) =>
29      track('Video Play', { video_id: videoId })
30  }
31};

3. Monitoruj conversion funnel

1'use client';
2
3import { track } from '@vercel/analytics';
4
5export default function CheckoutFlow() {
6  // Step 1: View cart
7  useEffect(() => {
8    track('Checkout Step 1 - Cart', {
9      items: cart.length,
10      value: cartTotal
11    });
12  }, []);
13
14  // Step 2: Shipping info
15  const handleShippingSubmit = () => {
16    track('Checkout Step 2 - Shipping', {
17      value: cartTotal
18    });
19  };
20
21  // Step 3: Payment
22  const handlePaymentSubmit = () => {
23    track('Checkout Step 3 - Payment', {
24      value: cartTotal,
25      payment_method: selectedPaymentMethod
26    });
27  };
28
29  // Step 4: Confirmation
30  const handleOrderComplete = (orderId: string) => {
31    track('Purchase Complete', {
32      order_id: orderId,
33      value: cartTotal,
34      items: cart.length
35    });
36  };
37
38  return <CheckoutSteps />;
39}

Dashboard i raporty

Vercel Analytics Dashboard oferuje:

  1. Overview

    • Total page views
    • Unique visitors
    • Top pages
    • Top referrers
  2. Audiences

    • Device breakdown (mobile, desktop, tablet)
    • Browser usage
    • Operating systems
    • Geographic distribution
  3. Real-time

    • Live visitor count
    • Current popular pages
    • Recent events
  4. Web Vitals

    • LCP, FID/INP, CLS scores
    • Performance over time
    • Device-specific metrics
  5. Custom Events

    • Event counts
    • Event properties
    • Conversion tracking

Limitacje i pricing

Free tier:

  • 2,500 events/month
  • Podstawowe analytics
  • 1 member

Pro tier ($10/member/month):

  • 100,000 events/month
  • Advanced filtering
  • Unlimited members
  • Data export

Enterprise:

  • Custom event limits
  • Dedicated support
  • SLA guarantees

Integracja z Vercel Edge Config

Możesz dynamicznie kontrolować feature flags bazując na analytics:

1// lib/feature-flags.ts
2import { get } from '@vercel/edge-config';
3import { track } from '@vercel/analytics';
4
5export async function checkFeatureFlag(flagName: string): Promise<boolean> {
6  const isEnabled = await get(flagName);
7
8  // Śledź użycie feature flag
9  if (isEnabled) {
10    track('Feature Flag Enabled', { flag: flagName });
11  }
12
13  return isEnabled || false;
14}

Podsumowanie Vercel Analytics

W Metropolii Quantum 2150, gdzie każda milisekunda ma znaczenie, Vercel Analytics dostarcza kwantowej precyzji w monitoringu aplikacji Next.js:

Kluczowe zalety:

  • ✅ Zero-config setup dla podstawowych metryk
  • ✅ Automatyczne śledzenie Core Web Vitals
  • ✅ Privacy-first approach (GDPR compliant)
  • ✅ Real-time data w Vercel Dashboard
  • ✅ Integracja z Vercel Edge Network
  • ✅ Custom events dla business metrics

Kiedy używać Vercel Analytics:

  • Aplikacje hostowane na Vercel
  • Potrzebujesz privacy-first analytics
  • Chcesz zero-config Web Vitals monitoring
  • Podstawowa analityka wystarczy dla Twojego case'u
  • Budżet pozwala na płatny plan dla większego traffic

Kiedy rozważyć alternatywy:

  • Potrzebujesz zaawansowanej segmentacji użytkowników
  • Wymagasz funnel analysis i attribution modeling
  • Masz bardzo duży traffic (> 1M page views/month)
  • Potrzebujesz integracji z ad platforms
  • Preferujesz self-hosted solution

Vercel Analytics to idealne rozwiązanie dla większości aplikacji Next.js, oferujące doskonałą równowagę między prostotą, wydajnością i funkcjonalnością. W połączeniu z Speed Insights tworzy kompletny system monitoringu wydajności aplikacji w czasie rzeczywistym!

Monitoring niestandardowy z Prometheus i Grafana

Dla bardziej zaawansowanego monitoringu możesz skonfigurować własny stack z Prometheus i Grafana.

1. Konfiguracja metryk Prometheus

1// lib/metrics.ts
2import client from 'prom-client';
3
4// Rejestr metryk
5const register = new client.Registry();
6
7// Metryki HTTP
8const httpRequestDuration = new client.Histogram({
9  name: 'http_request_duration_seconds',
10  help: 'Duration of HTTP requests in seconds',
11  labelNames: ['method', 'route', 'status_code'],
12  buckets: [0.1, 0.3, 0.5, 0.7, 1, 3, 5, 7, 10],
13});
14
15const httpRequestTotal = new client.Counter({
16  name: 'http_requests_total',
17  help: 'Total number of HTTP requests',
18  labelNames: ['method', 'route', 'status_code'],
19});
20
21// Metryki biznesowe
22const userRegistrations = new client.Counter({
23  name: 'user_registrations_total',
24  help: 'Total number of user registrations',
25});
26
27const ordersTotal = new client.Counter({
28  name: 'orders_total',
29  help: 'Total number of orders',
30  labelNames: ['status'],
31});
32
33// Zarejestruj metryki
34register.registerMetric(httpRequestDuration);
35register.registerMetric(httpRequestTotal);
36register.registerMetric(userRegistrations);
37register.registerMetric(ordersTotal);
38
39// Dodaj domyślne metryki Node.js
40client.collectDefaultMetrics({ register });
41
42export {
43  register,
44  httpRequestDuration,
45  httpRequestTotal,
46  userRegistrations,
47  orderTotal
48};

2. Middleware do śledzenia HTTP

1// middleware.ts
2import { NextRequest, NextResponse } from 'next/server';
3import { httpRequestDuration, httpRequestTotal } from '@/lib/metrics';
4
5export function middleware(request: NextRequest) {
6  const start = Date.now();
7  
8  // Kontynuuj przetwarzanie żądania
9  const response = NextResponse.next();
10  
11  // Zapisz metryki po przetworzeniu żądania
12  response.headers.set('x-response-time', (Date.now() - start).toString());
13  
14  // Async zapisanie metryk (nie blokuje odpowiedzi)
15  setImmediate(() => {
16    const duration = (Date.now() - start) / 1000;
17    const labels = {
18      method: request.method,
19      route: request.nextUrl.pathname,
20      status_code: response.status.toString(),
21    };
22    
23    httpRequestDuration.observe(labels, duration);
24    httpRequestTotal.inc(labels);
25  });
26  
27  return response;
28}
29
30export const config = {
31  matcher: [
32    '/((?!api|_next/static|_next/image|favicon.ico).*)',
33  ],
34};

3. Endpoint dla metryk Prometheus

1// app/api/metrics/route.ts
2import { NextRequest, NextResponse } from 'next/server';
3import { register } from '@/lib/metrics';
4
5export async function GET(request: NextRequest) {
6  const metrics = await register.metrics();
7  
8  return new NextResponse(metrics, {
9    headers: {
10      'Content-Type': register.contentType,
11    },
12  });
13}

Alerting i powiadomienia

Ważnym aspektem monitoringu jest automatyczne powiadamianie o problemach.

1. Integracja ze Slack

1// lib/alerts.ts
2import { IncomingWebhook } from '@slack/webhook';
3
4const webhook = new IncomingWebhook(process.env.SLACK_WEBHOOK_URL);
5
6export async function sendSlackAlert(message: string, severity: 'info' | 'warning' | 'error') {
7  const colors = {
8    info: '#36a64f',
9    warning: '#ffcc00',
10    error: '#ff0000',
11  };
12  
13  try {
14    await webhook.send({
15      attachments: [
16        {
17          color: colors[severity],
18          fields: [
19            {
20              title: 'Alert',
21              value: message,
22              short: false,
23            },
24            {
25              title: 'Time',
26              value: new Date().toISOString(),
27              short: true,
28            },
29            {
30              title: 'Environment',
31              value: process.env.NODE_ENV,
32              short: true,
33            },
34          ],
35        },
36      ],
37    });
38  } catch (error) {
39    console.error('Failed to send Slack alert:', error);
40  }
41}
42
43// Funkcja do sprawdzania thresholdów i wysyłania alertów
44export function checkPerformanceThresholds(metrics: {
45  lcp: number;
46  fid: number;
47  cls: number;
48}) {
49  if (metrics.lcp > 2500) {
50    sendSlackAlert(`High LCP detected: ${metrics.lcp}ms`, 'warning');
51  }
52  
53  if (metrics.fid > 100) {
54    sendSlackAlert(`High FID detected: ${metrics.fid}ms`, 'warning');
55  }
56  
57  if (metrics.cls > 0.1) {
58    sendSlackAlert(`High CLS detected: ${metrics.cls}`, 'warning');
59  }
60}

2. Monitoring uptime

1// lib/uptime-monitor.ts
2import { sendSlackAlert } from './alerts';
3
4interface HealthCheckResult {
5  service: string;
6  status: 'healthy' | 'unhealthy';
7  responseTime: number;
8  timestamp: Date;
9}
10
11export async function checkServiceHealth(url: string, service: string): Promise<HealthCheckResult> {
12  const start = Date.now();
13  
14  try {
15    const response = await fetch(url, {
16      method: 'GET',
17      timeout: 10000, // 10 second timeout
18    });
19    
20    const responseTime = Date.now() - start;
21    
22    if (response.ok) {
23      return {
24        service,
25        status: 'healthy',
26        responseTime,
27        timestamp: new Date(),
28      };
29    } else {
30      await sendSlackAlert(`Service ${service} returned ${response.status}`, 'error');
31      return {
32        service,
33        status: 'unhealthy',
34        responseTime,
35        timestamp: new Date(),
36      };
37    }
38  } catch (error) {
39    await sendSlackAlert(`Service ${service} is unreachable: ${error.message}`, 'error');
40    return {
41      service,
42      status: 'unhealthy',
43      responseTime: Date.now() - start,
44      timestamp: new Date(),
45    };
46  }
47}
48
49// Funkcja do uruchamiania health checków
50export async function runHealthChecks() {
51  const services = [
52    { name: 'Main App', url: 'https://your-app.com/api/health' },
53    { name: 'Database', url: 'https://your-app.com/api/health/db' },
54    { name: 'External API', url: 'https://api.external-service.com/health' },
55  ];
56  
57  const results = await Promise.allSettled(
58    services.map(service => checkServiceHealth(service.url, service.name))
59  );
60  
61  results.forEach((result, index) => {
62    if (result.status === 'fulfilled') {
63      console.log(`Health check for ${services[index].name}:`, result.value);
64    } else {
65      console.error(`Health check failed for ${services[index].name}:`, result.reason);
66    }
67  });
68}

Dashboard i wizualizacja danych

1. Prosty dashboard w Next.js

1// app/admin/monitoring/page.tsx
2import { getWebVitalMetrics, getErrorMetrics } from '@/lib/analytics';
3import { LineChart, BarChart } from '@/components/Charts';
4
5export default async function MonitoringDashboard() {
6  const webVitals = await getWebVitalMetrics();
7  const errors = await getErrorMetrics();
8  
9  return (
10    <div className="p-6">
11      <h1 className="text-3xl font-bold mb-8">Monitoring Dashboard</h1>
12      
13      <div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-8">
14        {/* Core Web Vitals */}
15        <div className="bg-white p-6 rounded-lg shadow">
16          <h2 className="text-xl font-semibold mb-4">Core Web Vitals</h2>
17          <LineChart 
18            data={webVitals} 
19            categories={['lcp', 'fid', 'cls']} 
20            xKey="timestamp" 
21          />
22        </div>
23        
24        {/* Error Rate */}
25        <div className="bg-white p-6 rounded-lg shadow">
26          <h2 className="text-xl font-semibold mb-4">Error Rate</h2>
27          <BarChart 
28            data={errors} 
29            category="count" 
30            xKey="hour" 
31          />
32        </div>
33      </div>
34      
35      {/* Real-time metrics */}
36      <div className="grid grid-cols-1 md:grid-cols-4 gap-4">
37        <MetricCard title="Active Users" value="1,234" change="+5.2%" />
38        <MetricCard title="Response Time" value="156ms" change="-2.1%" />
39        <MetricCard title="Error Rate" value="0.01%" change="-50%" />
40        <MetricCard title="Uptime" value="99.9%" change="+0.1%" />
41      </div>
42    </div>
43  );
44}
45
46function MetricCard({ title, value, change }: {
47  title: string;
48  value: string;
49  change: string;
50}) {
51  const isPositive = change.startsWith('+');
52  const isNegative = change.startsWith('-');
53  
54  return (
55    <div className="bg-white p-4 rounded-lg shadow">
56      <h3 className="text-sm font-medium text-gray-500">{title}</h3>
57      <div className="mt-2 flex items-baseline">
58        <p className="text-2xl font-semibold text-gray-900">{value}</p>
59        <p className={`ml-2 text-sm font-medium ${
60          isPositive ? 'text-green-600' : isNegative ? 'text-red-600' : 'text-gray-500'
61        }`}>
62          {change}
63        </p>
64      </div>
65    </div>
66  );
67}

Najlepsze praktyki monitoringu

1. Strategie sampling

1// lib/sampling.ts
2
3// Funkcja do określania, czy zdarzenie powinno być śledzone
4export function shouldSample(sampleRate: number): boolean {
5  return Math.random() < sampleRate;
6}
7
8// Adaptive sampling - więcej próbek dla błędów
9export function adaptiveSample(isError: boolean): boolean {
10  return isError ? shouldSample(1.0) : shouldSample(0.1);
11}
12
13// Sampling based on user tier
14export function userTierSample(userTier: 'free' | 'premium' | 'enterprise'): boolean {
15  const rates = {
16    free: 0.05,     // 5% dla darmowych użytkowników
17    premium: 0.25,  // 25% dla premium
18    enterprise: 1.0, // 100% dla enterprise
19  };
20  
21  return shouldSample(rates[userTier]);
22}

2. Optymalizacja wydajności monitoringu

1// lib/performance-monitoring.ts
2
3// Buffer dla metryk - zbieraj i wysyłaj partiami
4class MetricsBuffer {
5  private buffer: any[] = [];
6  private maxSize = 100;
7  private flushInterval = 30000; // 30 sekund
8  
9  constructor() {
10    // Automatyczne oproznianie bufora
11    setInterval(() => this.flush(), this.flushInterval);
12  }
13  
14  add(metric: any) {
15    this.buffer.push(metric);
16    
17    if (this.buffer.length >= this.maxSize) {
18      this.flush();
19    }
20  }
21  
22  private async flush() {
23    if (this.buffer.length === 0) return;
24    
25    const metricsToSend = [...this.buffer];
26    this.buffer = [];
27    
28    try {
29      await fetch('/api/metrics/batch', {
30        method: 'POST',
31        headers: { 'Content-Type': 'application/json' },
32        body: JSON.stringify({ metrics: metricsToSend }),
33      });
34    } catch (error) {
35      console.error('Failed to send metrics batch:', error);
36      // Wróć metryki do bufora w przypadku błędu
37      this.buffer.unshift(...metricsToSend);
38    }
39  }
40}
41
42export const metricsBuffer = new MetricsBuffer();

Podsumowanie

Monitoring wydajności i analityka błędów to kluczowe elementy każdej produkcyjnej aplikacji Next.js. Kompleksowe podejście do monitoringu obejmuje:

  1. Śledzenie błędów - automatyczne wykrywanie i raportowanie problemów
  2. Monitoring wydajności - Core Web Vitals i inne kluczowe metryki
  3. Alerting - natychmiastowe powiadomienia o krytycznych problemach
  4. Dashboardy - wizualizacja danych dla podejmowania decyzji
  5. Analityka użytkowników - zrozumienie, jak użytkownicy korzystają z aplikacji

Wybierając narzędzia monitoringu, rozważ:

  • Sentry - dla kompleksowego śledzenia błędów
  • Vercel Analytics - dla prostego monitoringu na Vercel
  • New Relic/DataDog - dla zaawansowanego monitoringu wydajności
  • Własne rozwiązania - dla specjalistycznych potrzeb

Pamiętaj o balansie między głębokością monitoringu a wpływem na wydajność aplikacji. Używaj sampling, bufferingu i optymalizuj zapytania, aby monitoring nie wpływał negatywnie na doświadczenie użytkownika.

Vai a CodeWorlds