# Setup Error Tracking (Sentry SDK) di NestJS Dokumentasi ini buat masang error tracking (capture exception, request context, stack trace) ke instance Sentry-compatible (GlitchTip self-hosted atau Sentry Cloud) di aplikasi NestJS. ## 1. Prasyarat - Sudah punya DSN dari project di dashboard GlitchTip/Sentry (format: `https://@/`) - Project NestJS pakai `.env` buat konfigurasi ## 2. Instalasi ```bash npm install @sentry/nestjs --save ``` ## 3. Environment variable Tambahin di `.env`: ``` SENTRY_DSN="https://@/" ``` ## 4. Buat file `src/instrument.ts` File ini **harus terpisah** dari `main.ts`, dan berisi `Sentry.init()`. ```ts import 'dotenv/config'; // wajib, load .env sebelum Sentry.init() dipanggil import * as Sentry from '@sentry/nestjs'; Sentry.init({ dsn: process.env.SENTRY_DSN, sendDefaultPii: true, // biar IP, headers, user info ikut kecapture environment: process.env.NODE_ENV || 'production', // Konfigurasi eksplisit biar request body & query params PASTI kecapture // (default behaviour beda-beda antar versi SDK, jadi jangan andelin default) integrations: (defaultIntegrations) => [ ...defaultIntegrations.filter((i) => i.name !== 'RequestData'), Sentry.requestDataIntegration({ include: { ip: true, data: true, // request body query_string: true, // query params headers: true, cookies: true, user: true, }, }), ], }); ``` > Kenapa perlu `dotenv/config` manual: `instrument.ts` diimport paling awal, > sebelum NestJS `ConfigModule` sempat jalan dan load `.env`. Tanpa ini, > `process.env.SENTRY_DSN` masih `undefined` pas `Sentry.init()` dipanggil, > dan SDK diam-diam gak akan pernah ngirim event (cuma warning > "No DSN provided" di log kalau `debug: true`). ## 5. Import di baris paling atas `main.ts` ```ts import './instrument'; // HARUS baris pertama, sebelum import apapun lain import { NestFactory } from '@nestjs/core'; // ...import lain seperti biasa ``` Penting: - **Tanpa** ekstensi `.ts` di akhir (`'./instrument'`, bukan `'./instrument.ts'`) - File-nya harus ada di `src/` (folder yang sama dengan `main.ts`) ## 6. Register `SentryModule` & exception filter global di `app.module.ts` ```ts import { APP_FILTER } from '@nestjs/core'; import { SentryModule, SentryGlobalFilter } from '@sentry/nestjs/setup'; @Module({ imports: [ SentryModule.forRoot(), // HARUS import PERTAMA di array imports // ...module lain ], providers: [ { provide: APP_FILTER, useClass: SentryGlobalFilter }, // HARUS provider PERTAMA // ...provider lain ], }) export class AppModule {} ``` ## 7. Kalau project sudah punya custom exception filter Ini kasus yang sering kelewat. `SentryGlobalFilter` cuma nangkep exception yang **gak** ketangkep filter lain duluan. Kalau ada filter spesifik seperti: ```ts @Catch(PrismaClientKnownRequestError) export class SomeCustomFilter implements ExceptionFilter { ... } ``` ...exception yang match `@Catch(...)` itu **tidak akan pernah** sampai ke `SentryGlobalFilter`, walaupun `SentryModule` sudah ke-setup benar. Solusinya: tambahin `Sentry.captureException(exception)` manual di filter itu. Aturan yang dipakai: **status 5xx / error tak terduga → capture. Status 4xx yang memang disengaja (validasi, not found, unauthorized) → boleh skip**, karena itu flow normal, bukan bug. Helper biar konsisten di semua filter: ```ts // src/common/sentry-report.util.ts import * as Sentry from '@sentry/nestjs'; export function reportIfUnexpected(exception: unknown, httpStatus: number) { if (httpStatus >= 500) { Sentry.captureException(exception); } } ``` Panggil di ujung `catch()` tiap custom filter: ```ts reportIfUnexpected(exception, status); ``` **Checklist:** cari semua custom filter di project sebelum anggap setup selesai: ```bash grep -rln "@Catch(" src/ --include="*.ts" | grep -v node_modules ``` ## 8. Scrub data sensitif (opsional tapi disarankan) Karena `sendDefaultPii: true` bikin request body ikut terkirim penuh, tambahin `beforeSend` di `instrument.ts` buat mask field sensitif: ```ts Sentry.init({ // ...config di atas beforeSend(event) { const sensitiveFields = ['password', 'token', 'secret']; if (event.request?.data) { for (const field of sensitiveFields) { if (event.request.data[field]) event.request.data[field] = '[FILTERED]'; } } return event; }, }); ``` ## 9. Testing Penting: `SentryGlobalFilter` **tidak** mengirim `HttpException` bawaan NestJS (`NotFoundException`, `BadRequestException`, dll) secara default — itu dianggap flow kontrol normal, bukan bug. Buat testing, trigger error yang beneran unhandled, misal lewat constraint DB (contoh Prisma `create()` dengan data yang melanggar unique constraint), bukan `throw new HttpException(...)`. Cara cepat verifikasi: 1. Trigger error via endpoint yang ada request body/query params 2. Cek dashboard → tab **Issues** → buka issue-nya 3. Pastikan section **Request** ada: Body, Query String, Headers, IP ## 10. Troubleshooting | Gejala | Kemungkinan penyebab | |---|---| | `Cannot find module './instrument.ts'` | Ada `.ts` literal di baris import `main.ts`, atau `dist/` lama ke-cache — hapus `dist` dan build ulang | | `Cannot find module './instrument'` padahal sudah tanpa `.ts` | File `instrument.ts` gak ada di `src/`, atau salah folder | | `No DSN provided, client will not send events` (pakai `debug: true`) | `.env` belum ke-load pas `Sentry.init()` jalan — tambahin `import 'dotenv/config'` di baris pertama `instrument.ts` | | Event gak muncul di dashboard padahal `Sentry Logger` bilang "Captured error event" | Cek log container GlitchTip (`docker compose logs glitchtip-web`) barengan waktu trigger — kalau gak ada request masuk sama sekali, cek konektivitas network / DSN salah domain | | Request body / query params kosong di dashboard | Konfigurasi `requestDataIntegration` secara eksplisit (lihat bagian 4), jangan andelin default SDK | | Error yang "seharusnya" muncul tapi gak ada | Kemungkinan ketangkep custom exception filter duluan — lihat bagian 7 | ## Referensi - Dokumentasi resmi Sentry untuk NestJS: https://docs.sentry.io/platforms/javascript/guides/nestjs/