| name | sentry-integration |
| description | Sentry error tracking i performance monitoring dla React + Supabase Edge Functions. Aktywuje się przy pracy z błędami, monitoringiem, captureException, error boundary, śledzeniem błędów, diagnostyką, loggerem, Edge Functions, crash, awaria, wydajność, raportowanie błędów, exception, wyjątek. |
Sentry Integration Guidelines
Kompleksowy przewodnik integracji Sentry error tracking i performance monitoring dla projektu React + Supabase Edge Functions.
Stan SDK
- React SDK: v10+ (funkcyjne integracje, React 19 error hooks) ✅
- Edge Functions:
@sentry/deno na Deno 2.x (instrumentacja Deno.serve świeża/eksperymentalna + beforeSend) ⚠️ — ustaw defaultIntegrations: false (dokumentacja Supabase nadal to zaleca na Edge Runtime), używaj withScope dla izolacji i await flush() przed Response
Table of Contents
Critical Rules
NIGDY NIE ŁAMIESZ TYCH ZASAD:
- ALL ERRORS MUST BE CAPTURED TO SENTRY - w produkcji każdy błąd musi trafić do Sentry
- NIGDY
console.error bez Sentry - w Edge Functions każdy console.error musi mieć captureError()
- MASKUJ DANE OSOBOWE - email musi być maskowany:
user@example.com → us***@example.com
- NIE WYSYŁAJ WRAŻLIWYCH DANYCH - hasła, tokeny, klucze API NIGDY nie trafiają do Sentry
- UŻYWAJ ODPOWIEDNICH POZIOMÓW -
fatal tylko dla krytycznych, error dla operacji
Dobre praktyki (Edge Functions / Supabase)
@sentry/deno działa na Supabase Edge Runtime (Deno 2.x) z instrumentacją requestów
i wsparciem beforeSend. Wsparcie instrumentacji Deno.serve jest jednak świeże/eksperymentalne —
oficjalna dokumentacja Supabase nadal zaleca defaultIntegrations: false na Edge Runtime, bo bez
tego nie ma gwarancji scope separation między requestami w tym samym isolate. Nadal stosuj:
| Zasada | Dlaczego |
|---|
defaultIntegrations: false w Sentry.init() | Bezpieczny default dopóki nie zweryfikujesz scope separation na swoim runtime |
Ustawiaj kontekst przez Sentry.withScope() | Izolacja per operacja; unikasz wycieku tagów między requestami |
| Nie ustawiaj globalnych tagów per-request | Globalny scope jest współdzielony w obrębie isolate'u |
await Sentry.flush() przed Response | Isolate może zostać zamrożony zaraz po odpowiedzi |
Maskuj PII w beforeSend | Jeden centralny punkt dla wszystkich zdarzeń |
Zawsze używaj tego wzorca:
Sentry.setTag('user_id', userId);
Sentry.captureException(error);
Sentry.withScope((scope) => {
scope.setTag('user_id', userId);
Sentry.captureException(error);
});
Szczegóły: edge-functions-sentry.md
Error Levels
| Level | Kiedy używać | Przykład |
|---|
fatal | System nie działa, wymaga natychmiastowej interwencji | Brak połączenia z bazą |
error | Operacja nie powiodła się, użytkownik dotknięty | Płatność Stripe nie przeszła |
warning | Problem odwracalny, nie wymaga natychmiastowej akcji | Retry po timeout |
info | Informacje operacyjne | Użytkownik zalogowany |
Quick Reference
Frontend (React)
Inicjalizacja w main.tsx:
import { initSentry } from '@/lib/sentry';
import * as Sentry from '@sentry/react';
initSentry();
ReactDOM.createRoot(document.getElementById('root')!).render(
<Sentry.ErrorBoundary fallback={<ErrorFallback />}>
<AppWrapper />
</Sentry.ErrorBoundary>
);
Użycie loggera (preferowane):
import { logger } from '@/lib/logger';
try {
await riskyOperation();
} catch (error) {
logger.error('Operacja nie powiodła się', error);
toast.error('Wystąpił błąd');
}
Bezpośrednie Sentry (gdy potrzeba więcej kontekstu):
import * as Sentry from '@sentry/react';
Sentry.withScope((scope) => {
scope.setTag('operation', 'payment');
scope.setContext('order', { orderId: '123', amount: 100 });
Sentry.captureException(error);
});
Edge Functions (Deno)
Każda funkcja MUSI mieć Sentry z withScope:
import { initSentry, captureError } from '../_shared/sentry.ts';
const Sentry = initSentry('function-name');
Deno.serve(async (req) => {
try {
} catch (error) {
captureError(error, {
operation: 'checkout',
user_id: userId
});
return new Response(JSON.stringify({ error: 'Error' }), { status: 500 });
}
});
Context Enrichment
ZAWSZE dodawaj kontekst do błędów:
Sentry.withScope((scope) => {
scope.setUser({ id: userId, email: maskedEmail });
scope.setTag('service', 'payments');
scope.setTag('endpoint', '/checkout');
scope.setContext('operation', {
type: 'stripe_checkout',
sessionId: session.id,
amount: amount
});
scope.addBreadcrumb({
category: 'payment',
message: 'Starting checkout',
level: 'info'
});
Sentry.captureException(error);
});
Sentry.captureException(error);
GDPR Compliance
Maskowanie emaili - OBOWIĄZKOWE:
beforeSend(event) {
if (event.user?.email) {
event.user.email = event.user.email.replace(/^(.{2}).*(@.*)$/, '$1***$2');
}
return event;
}
export function setSentryUser(user: { id: string; email: string } | null) {
if (user) {
Sentry.setUser({
id: user.id,
email: user.email.replace(/^(.{2}).*(@.*)$/, '$1***$2'),
});
} else {
Sentry.setUser(null);
}
}
Checklist dla Nowego Kodu
Przed każdym PR sprawdź:
Common Mistakes
NIE RÓB:
try {
await operation();
} catch (error) {
}
} catch (error) {
console.error('Error:', error);
}
Sentry.setContext('auth', { token: userToken });
RÓB:
try {
await operation();
} catch (error) {
logger.error('Operacja nie powiodła się', error);
toast.error('Wystąpił błąd. Spróbuj ponownie.');
}
Sentry.setContext('auth', {
userId: user.id,
provider: 'google'
});
Resources
Szczegółowe wzorce znajdują się w:
Skill Status: COMPLETE
Progressive Disclosure: Szczegółowe wzorce w plikach resources/