keloola-errly-js
v0.1.2
Published
Frontend error tracking SDK for Keloola Errly
Maintainers
Readme
keloola-errly-js
Universal JavaScript SDK untuk frontend error tracking. Menangkap JS errors, unhandled promise rejections, dan HTTP errors di browser lalu mengirimnya ke Errly Dashboard.
- 🪶 Ringan (~3 kB gzip), zero dependency
- 📦 ESM + CJS + UMD — bundler-friendly atau lewat
<script> - 🧩 Wrapper resmi: React, Vue, Next.js, Nuxt
- 🛡️ Tidak pernah meng-crash app host — semua operasi internal dibungkus
try/catch - 🔒 Strip data sensitif (password, token, kartu kredit) sebelum kirim
Instalasi
npm install keloola-errly-jsAtau via CDN (vanilla JS):
<script src="https://cdn.errly.app/keloola-errly-js.min.js"></script>Quick start
import Errly from 'keloola-errly-js'
Errly.init({
apiKey: 'proj_xxxxxxxxxxxx', // dari dashboard Errly
dsn: 'https://errly.yourdomain.com', // URL dashboard
})Setelah init(), global JS error & promise rejection otomatis tertangkap.
Env variable per framework
SDK berjalan di browser, jadi API key harus di-expose ke client — perhatikan prefix-nya:
| Framework / bundler | Akses env | Prefix wajib |
| --- | --- | --- |
| Vite (React/Vue + Vite) | import.meta.env.VITE_ERRLY_API_KEY | VITE_ |
| Next.js | process.env.NEXT_PUBLIC_ERRLY_API_KEY | NEXT_PUBLIC_ |
| Create React App | process.env.REACT_APP_ERRLY_API_KEY | REACT_APP_ |
| Nuxt | useRuntimeConfig().public.errlyApiKey | via runtimeConfig |
| Astro | import.meta.env.PUBLIC_ERRLY_API_KEY | PUBLIC_ |
Konfigurasi (Errly.init)
Errly.init({
apiKey: 'proj_xxxxxxxxxxxx', // WAJIB
dsn: 'https://...', // WAJIB
environment: 'production', // default: window.location.hostname
release: '1.0.0', // versi app, opsional
sampleRate: 1.0, // 0.0–1.0, default 1.0 (100%)
ignoreErrors: [], // string | RegExp yang diabaikan
beforeSend: null, // hook modifikasi/cancel payload (return null = cancel)
debug: false, // log ke console kalau true
maxBreadcrumbs: 20, // default 20
attachStacktrace: true, // default true
})Detail opsi
Ringkasan cepat:
| Opsi | Default | Singkatnya |
| --- | --- | --- |
| sampleRate | 1.0 | Berapa persen error dikirim (kontrol volume) |
| ignoreErrors | [] | Buang error yang message-nya cocok |
| beforeSend | null | Hook terakhir: modifikasi / batalkan payload |
| debug | false | Log aktivitas SDK ke console |
| maxBreadcrumbs | 20 | Jumlah jejak aksi user yang disimpan |
| attachStacktrace | true | Sertakan stack trace di payload |
Penjelasan lengkap di bawah.
sampleRate — default 1.0
Membatasi berapa banyak error yang benar-benar dikirim. Berguna saat traffic
tinggi supaya dashboard tidak banjir dan hemat kuota. Nilainya 0.0–1.0:
| Nilai | Efek |
| --- | --- |
| 1.0 | Kirim semua (100%) |
| 0.25 | Kirim ±25%, sisanya dibuang acak |
| 0 | Tidak mengirim apa pun |
Di balik layar: if (Math.random() > sampleRate) return.
Errly.init({ apiKey, dsn, sampleRate: 0.2 }) // kirim ~20% error⚠️ Dua hal penting:
- Sampling per-event acak, bukan per-sesi/per-user — satu user bisa saja error pertamanya terkirim tapi error kedua tidak.
- Hanya berlaku di
capture()(exception: JS error global, promise rejection,Errly.capture()).captureMessage()tidak terpengaruh — jadi pesan manual dan auto-capture HTTP 5xx selalu terkirim 100%.
ignoreErrors — default []
Daftar pola untuk membuang error yang message-nya cocok. Biasanya untuk noise
yang tidak bisa kamu kontrol: ResizeObserver loop limit exceeded, error dari
browser extension, ChunkLoadError, dll.
Errly.init({
apiKey, dsn,
ignoreErrors: [
'ResizeObserver loop', // STRING = cocok kalau message MENGANDUNG teks ini
/^Script error\.?$/, // REGEXP = kontrol presisi
/ChunkLoadError/,
],
})- String → substring (
message.includes(pattern)).'Network'akan mencocokkan'NetworkError when fetching'. - RegExp →
pattern.test(message).
⚠️ Yang dicek hanya
error.message— bukan stack trace, filename, atau URL. Berlaku dicapture()dancaptureMessage().
beforeSend — default null
Hook terakhir sebelum payload dikirim. Titik kontrol paling fleksibel untuk memodifikasi, memperkaya, atau membatalkan event.
Errly.init({
apiKey, dsn,
beforeSend: (payload) => {
// a) Batalkan kondisional → return null
if (payload.message.includes('ResizeObserver')) return null
// b) Tambah/ubah data
payload.extra = { ...payload.extra, appVersion: '1.2.3' }
// c) Scrub manual data sensitif yang lolos
if (payload.user?.email) payload.user.email = '[hidden]'
return payload // WAJIB return payload (atau objek) kalau tidak mau batal
},
})return null→ event dibuang.return payload/ objek → di-merge ke payload viaObject.assign.
⚠️ Hook ini jalan paling akhir — setelah
sampleRate&ignoreErrors(yang nge-gate lebih awal) dan setelahstripSensitiveData()otomatis. Jadi yang kamu terima sudah payload final; cocok untuk scrub/enrich tambahan. Lupareturn(jadiundefined) → payload tetap terkirim apa adanya.
debug — default false
Kalau true, SDK menulis log ke console: saat init, tiap kirim
([Errly] Sending payload: …), dan saat pengiriman gagal.
Errly.init({ apiKey, dsn, debug: true })
// → [Errly] Initialized. Environment: localhost
// → [Errly] Sending payload: { type: 'exception', message: '…', … }⚠️ Matikan di production supaya payload error tidak bocor ke console user. Semua file
test/*.htmlmemang pakaidebug: true.
maxBreadcrumbs — default 20
Batas jumlah "jejak" aksi user yang disimpan sebelum error (klik, navigasi, request HTTP), lalu ikut dikirim di payload untuk membantu reproduksi.
Ini ring buffer: saat penuh, breadcrumb paling lama dibuang. Jadi
maxBreadcrumbs: 20 = selalu simpan 20 jejak terbaru.
Errly.init({ apiKey, dsn, maxBreadcrumbs: 50 }) // konteks lebih panjang⚠️ Trade-off: angka lebih besar = konteks lebih kaya, tapi payload lebih berat (tiap breadcrumb punya type, message, data, timestamp). 20 umumnya cukup.
attachStacktrace — default true
Sertakan stack trace di payload — info paling berharga untuk debugging (file:line:col tempat error terjadi). Stack dipotong maksimal 10 baris pertama.
Errly.init({ apiKey, dsn, attachStacktrace: false }) // tanpa stack (lebih ringan/privat)⚠️ Hanya berpengaruh pada exception via
capture().captureMessage()memang tidak punya stack.falsemembuat debugging jauh lebih susah — umumnya biarkantrue.
Urutan pemrosesan satu error:
Errly.capture(err)
→ sampleRate? drop acak (capture() saja)
→ ignoreErrors? drop kalau message cocok
→ buildPayload() + browser, breadcrumbs (maxBreadcrumbs), stack (attachStacktrace)
→ stripSensitiveData redact otomatis (password/token/kartu kredit)
→ beforeSend() modifikasi / cancel
→ transport.send() kirim (+ log kalau debug)Public API
| Method | Return | Singkatnya |
| --- | --- | --- |
| init(options) | void | Inisialisasi SDK + pasang semua handler |
| capture(error, context?) | void | Kirim exception manual |
| captureMessage(msg, level?) | void | Kirim pesan (bukan Error object) |
| setUser(user) | void | Set identitas user di tiap payload |
| clearUser() | void | Hapus identitas user |
| addBreadcrumb(crumb) | void | Tambah jejak aksi manual |
| flush() | Promise<void> | Tunggu semua event pending terkirim |
🛡️ Semua method aman — dibungkus
try/catch, tidak pernah throw / meng-crash app. Kalau dipanggil sebeluminit(), method diabaikan diam-diam (kecualiflush()yang mengembalikanPromise.resolve()).
Errly.init(options)
Inisialisasi SDK: validasi apiKey/dsn, lalu pasang semua integrasi —
window.onerror, unhandledrejection, fetch interceptor, dan breadcrumb tracker
(klik & navigasi). Wajib dipanggil sekali sebelum method lain.
Errly.init({ apiKey: 'proj_xxx', dsn: 'https://errly.yourdomain.com' })⚠️ Kalau
apiKey/dsnkosong atau init gagal → munculconsole.warndan SDK jadi no-op (tidak crash). Lihat Konfigurasi untuk daftar opsi lengkap.
Errly.capture(error, context?)
Kirim exception secara manual (mis. di dalam try/catch). Argumen error
boleh Error atau nilai apa pun (otomatis di-coerce jadi Error). context
opsional:
Errly.capture(error, {
level: 'error', // 'info' | 'warning' | 'error' | 'fatal' (default 'error')
tags: { section: 'checkout' }, // label pendek untuk filter di dashboard
extra: { cartId, retryCount: 2 }, // data bebas untuk konteks
})⚠️ Kena
sampleRate&ignoreErrors(bisa di-drop). Stack ikut kalauattachStacktrace: true. Fieldcontext.usertidak dipakai — identitas user diambil darisetUser(), bukan dari sini.
Errly.captureMessage(message, level?)
Kirim pesan (bukan objek Error) — untuk log peristiwa penting yang bukan
exception. level default 'error'.
Errly.captureMessage('Pembayaran ditolak gateway', 'warning')⚠️
leveldi sini hanya'info' | 'warning' | 'error'. Pesan tidak punya stack trace. KenaignoreErrors, tapi tidak kenasampleRate(selalu terkirim).
Errly.setUser(user) / Errly.clearUser()
Set/hapus identitas user yang otomatis disertakan di setiap payload berikutnya — mempermudah melacak error per-user di dashboard.
// saat login
Errly.setUser({ id: 42, email: '[email protected]', name: 'Andi', plan: 'pro' }) // boleh atribut custom
// saat logout
Errly.clearUser()💡
id,nameopsional, plus atribut custom bebas. PanggilclearUser()saat logout supaya error user berikutnya tidak salah atribusi.
Errly.addBreadcrumb(crumb)
Tambah jejak manual ke timeline (selain klik/navigasi/HTTP yang sudah otomatis). Berguna menandai langkah bisnis penting sebelum error.
Errly.addBreadcrumb({
type: 'custom', // 'navigation' | 'click' | 'http' | 'console' | 'custom'
category: 'checkout',
message: 'User klik "Bayar sekarang"',
data: { amount: 150000 },
})💡
timestampterisi otomatis kalau dikosongkan. Jejak disimpan sebanyakmaxBreadcrumbsterbaru (ring buffer).
Errly.flush()
Mengembalikan Promise yang resolve setelah semua event pending terkirim.
Pengiriman SDK bersifat async/antrian, jadi panggil ini sebelum aksi yang bisa
menghentikan halaman (redirect, logout, beforeunload).
await Errly.flush()
window.location.href = '/login'Framework wrappers
React — keloola-errly-js/react
import Errly from 'keloola-errly-js'
import { ErrlyErrorBoundary } from 'keloola-errly-js/react'
Errly.init({
apiKey: import.meta.env.VITE_ERRLY_API_KEY,
dsn: import.meta.env.VITE_ERRLY_DSN,
})
function App() {
return (
<ErrlyErrorBoundary fallback={<p>Oops, something broke.</p>}>
<MyApp />
</ErrlyErrorBoundary>
)
}Tersedia juga HOC withErrly(Component, fallback?).
Vue — keloola-errly-js/vue
import { createApp } from 'vue'
import { ErrlyVuePlugin } from 'keloola-errly-js/vue'
import App from './App.vue'
createApp(App)
.use(ErrlyVuePlugin, {
apiKey: import.meta.env.VITE_ERRLY_API_KEY,
dsn: import.meta.env.VITE_ERRLY_DSN,
})
.mount('#app')Next.js (App Router, 14+) — keloola-errly-js/next
⚠️ Jangan init lewat
instrumentation.ts.register()di sana hanya jalan di server (NEXT_RUNTIME='nodejs'/'edge', tak pernah'browser'), sedangkan SDK ini butuhwindow. Init dari client component.
1. Buat client component app/errly-init.tsx:
'use client'
import { register } from 'keloola-errly-js/next'
register({
apiKey: process.env.NEXT_PUBLIC_ERRLY_API_KEY!,
dsn: process.env.NEXT_PUBLIC_ERRLY_DSN!,
environment: process.env.NODE_ENV,
})
export function ErrlyInit() {
return null
}2. Pasang di app/layout.tsx:
import { ErrlyInit } from './errly-init'
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html><body><ErrlyInit />{children}</body></html>
)
}3. Tangkap render error di app/global-error.tsx:
'use client'
import { useEffect } from 'react'
import Errly from 'keloola-errly-js'
export default function GlobalError({ error, reset }: { error: Error; reset: () => void }) {
useEffect(() => { Errly.capture(error) }, [error])
return (
<html><body><h2>Something went wrong.</h2><button onClick={reset}>Try again</button></body></html>
)
}⚠️ Tanpa prefix
NEXT_PUBLIC_, variable tidak tersedia di browser. 📌 Di Next 15.3+, isiregister(...)boleh dipindah keinstrumentation-client.ts.
Nuxt
Salin plugin berikut ke plugins/errly.client.ts (lihat docs/integrations.md):
export default defineNuxtPlugin(() => {
const config = useRuntimeConfig()
Errly.init({
apiKey: config.public.errlyApiKey as string,
dsn: config.public.errlyDsn as string,
})
})nuxt.config.ts:
export default defineNuxtConfig({
plugins: ['~/plugins/errly.client.ts'],
runtimeConfig: {
public: {
errlyApiKey: process.env.NUXT_PUBLIC_ERRLY_API_KEY,
errlyDsn: process.env.NUXT_PUBLIC_ERRLY_DSN,
},
},
})Astro — keloola-errly-js/astro
Tambahkan integration di astro.config.mjs — script init otomatis ter-inject ke
setiap halaman, global error & promise rejection ditangkap handler bawaan SDK:
import { defineConfig } from 'astro/config'
import { errly } from 'keloola-errly-js/astro'
export default defineConfig({
integrations: [
errly({
apiKey: import.meta.env.PUBLIC_ERRLY_API_KEY,
dsn: import.meta.env.PUBLIC_ERRLY_DSN,
}),
],
})⚠️ Astro pakai prefix
PUBLIC_untuk env yang boleh diexpose ke client.
Untuk komponen island React/Vue di dalam Astro, pakai wrapper masing-masing
(keloola-errly-js/react, /vue) seperti biasa.
Development
npm install # install deps
npm test # jalankan unit tests (vitest)
npm run test:coverage
npm run typecheck # tsc --noEmit
npm run build # build dist/ + wrapper (react/vue/next)Build menghasilkan:
dist/keloola-errly.js(ESM),dist/keloola-errly.cjs(CJS),dist/keloola-errly.umd.js(UMD)dist/index.d.ts(type definitions)dist/{react,vue,next}/(wrapper terkompilasi — ikut di-publish lewatfiles: ["dist"])
Smoke-test manual di browser: npx serve test/ lalu buka test/manual.html.
Lisensi
MIT
