Mundarija (40)
- Bu darsda
- 1. Nega bu kerak?
- 2. Xato qayerdan keladi
- 2.1 To'rt manba
- 2.2 Jonli: uchtasini bitta joyga yig'amiz
- 2.3 Promise xatosi
- 3. Xato yozuvi: nima yuboriladi
- 3.1 Bir xatoning "pasporti"
- 3.2 Nima KIRMASLIGI kerak
- 3.3 Jonli: yozuvni yasaymiz
- 3.4 Iz, takror va chegara
- 4. Source map: index-BSuMvEAh.js:1:48211 qayerda?
- 4.1 Minify qilingan stack
- 4.2 Map kimda bo'lishi kerak
- 5. Sentry React'da (hujjat bo'yicha)
- 5.1 Sentry nima va nega hisob kerak
- 5.2 init: birinchi import
- 5.3 React 19: createRoot ilgaklari va ErrorBoundary
- 5.4 Qo'lda: captureException, iz, foydalanuvchi
- 5.5 Tracing va replay
- 5.6 Source map'ni Vite plagini bilan yuklash
- 5.7 Biz sinab ko'rdik: hajm va CSP
- 6. Sentry'dan boshqa yo'llar
- 7. Vazifalar qadami: o'z xato kuzatuvi (holat 19)
- 7.1 Branch va fayllar
- 7.2 Yozuv: xato-yozuvi.ts
- 7.3 Yuborish: xatolar.ts
- 7.4 main.tsx va reliz
- 7.5 Natija
- 7.6 Commit
- 8. Hujumchi nigohi
- 9. Ko'p uchraydigan xatolar
- 10. Mashqlar
- 1-mashq (oson): bo'sh joylarni to'ldiring
- 2-mashq (o'rta): scrub ni kengaytiring
- 3-mashq (qiyin): boundary + "Qayta urinish" + yuborish
- 4-mashq: Vazifalar qadami
- 11. Real ishda
- Xulosa
- Manbalar
Xatolarni kuzatish React'da: Sentry, source map, reliz va maxfiylik
Qisqacha: Foydalanuvchi telefonidagi xato sizning konsolingizga chiqmaydi. Xatolarni kuzatish (error tracking) — brauzerdagi har ushlanmagan xatoni yig'ib, serverga yuborish: nomi, xabari, stack, qaysi reliz, qaysi sahifa va undan oldin nima bo'lgani. Xato to'rt joydan keladi:
windowningerrorhodisasi,unhandledrejection, React'ningonUncaughtError/onCaughtErrorva qo'ldatry/catch. Sentry buni tayyor beradi (+29.6 KB gzip, hisob va DSN kerak),vazifalaresa o'zinikini yozdi — production'da hech qayerga yubormaydi.
Bu darsda
- Brauzerdagi xatoning to'rt manbasini ushlaysiz va bitta "xato yozuvi" ga aylantirasiz.
- Yozuvga nima kirishi (reliz, muhit, sahifa, iz) va nima kirmasligi (email, telefon, token) kerakligini bilasiz.
- Minify qilingan stack'ni source map bilan asl faylga qaytarish va map'larni yashirish yo'lini tushuntirasiz.
@sentry/reactni hujjat bo'yicha ulaysiz:init, React 19 xato ilgaklari,ErrorBoundary, tracing, replay,beforeSend.- Sentry, GlitchTip va o'z yechim orasida tanlov qilasiz;
vazifalardagi xato kuzatuvini o'qiysiz.
Oldin bilishingiz kerak: Error boundary, React ilovani debug qilish, Source map'lar, Analitika.
1. Nega bu kerak?
O'tgan darsda «Bahor» ilovasiga analitika qo'shdik: qaysi sahifa ochildi, qaysi tugma bosildi. Bugun Jasur aka boshqa xabar bilan keldi: "Bir mehmon yozibdi — buyurtma tugmasini bossa, hech narsa bo'lmayapti. Menda hammasi ishlayapti".
Sizda ham ishlayapti. Xato esa mehmonning telefonida, eski brauzerda, ruscha interfeysda yuz bergan. Uning konsoli sizga ko'rinmaydi. Mehmon esa "ishlamayapti" dan boshqa narsa aytolmaydi.
Bunday xatolarni topish uchun ilovaning o'zi "xabarchi" bo'lishi kerak: xato yuz berganda nima bo'lganini yozib, serverga yuboradi. Bu — xatolarni kuzatish (error tracking). Siz ertalab ro'yxatni ochasiz: "Kecha 14 ta mehmonda TypeError: Cannot read properties of undefined (reading 'narx'), reliz a1b2c3d, sahifa /savatcha, hammasi ruscha interfeysda".
Hayotiy o'xshatish: samolyotning "qora qutisi". Parvoz paytida hech kim unga qaramaydi. Biror narsa bo'lsa — qutidan oxirgi daqiqalar, asboblar ko'rsatkichi, ekipaj gaplari olinadi. Xato kuzatuvi ham shunday: xato bo'lganda uning atrofidagi hamma narsa saqlanadi.
2. Xato qayerdan keladi
2.1 To'rt manba
Brauzerda ushlanmagan xato to'rt yo'l bilan paydo bo'ladi:
| Manba | Qachon | Qanday ushlanadi |
|---|---|---|
| Sinxron xato | tugma ishlovchisida, taymerda throw |
window dagi error hodisasi |
| Rad etilgan Promise | await siz fetch, .catch yo'q |
window dagi unhandledrejection |
| Render xatosi | komponent chizishda yiqildi | createRoot ning onUncaughtError / onCaughtError |
| Ushlangan xato | o'zingiz try/catch qildingiz |
qo'lda yuborasiz |
React render xatosi window ga chiqmaydi — React uni o'zi ushlaydi. Shuning uchun 17-qismdagi onUncaughtError va error boundary dagi onCaughtError alohida manba. Tugma ishlovchisidagi xatoni esa React ushlamaydi — u oddiy JS xatosi kabi window ga chiqadi.
2.2 Jonli: uchtasini bitta joyga yig'amiz
«Bahor» menyusi: narxlar bo'limi yiqiladi (boundary ushlaydi), buyurtma tugmasi esa xato tashlaydi. Ikkalasi ham bitta report funksiyasiga tushadi:
import { Component } from "react";
import { createRoot } from "react-dom/client";
function report(source, error) {
console.log(`[${source}] ${error.name}: ${error.message}`);
}
// 1-manba: sinxron xato (tugma, taymer)
addEventListener("error", (event) => {
report("window", event.error);
event.preventDefault(); // natija oynasida qizil quti chiqmasin
});
class Boundary extends Component {
state = { failed: false };
static getDerivedStateFromError() {
return { failed: true };
}
render() {
if (this.state.failed) return <p>Narxlar yuklanmadi</p>;
return this.props.children;
}
}
function Prices() {
const menu = undefined; // server bo'sh javob berdi
return <p>Osh: {menu.osh} so'm</p>;
}
function OrderButton() {
return (
<button
onClick={() => {
throw new Error("Savatcha topilmadi");
}}
>
Buyurtma berish
</button>
);
}
// 3-manba: React render xatolari
createRoot(document.getElementById("root"), {
onCaughtError: (error) => report("chegara", error),
onUncaughtError: (error) => report("react", error),
}).render(
<>
<Boundary>
<Prices />
</Boundary>
<OrderButton />
</>,
);Konsolda:
[chegara] TypeError: Cannot read properties of undefined (reading 'osh')
[window] Error: Savatcha topilmadiEkranda:
Narxlar yuklanmadi
Buyurtma berishIkki narsaga qarang:
- Boundary narxlar bo'limini almashtirdi, ilova ishlayveradi. Lekin xato yashirinib qolmadi —
onCaughtErrorunireportga berdi. Error boundary foydalanuvchini himoya qiladi, kuzatuv esa dasturchiga xabar beradi. - Tugma xatosini React ushlamadi: u
windowga chiqdi. Boundary bunday xatoni ko'rmaydi (error boundary nimani ushlamaydi).
event.preventDefault() — faqat natija oynasi uchun: u xatoni "ko'rib chiqildi" deb belgilaydi va brauzer uni konsolga qizil qilib yozmaydi. Sentry buni qilmaydi — DevTools'da xato avvalgidek ko'rinadi.
2.3 Promise xatosi
To'rtinchi manba — rad etilgan va hech kim .catch qilmagan Promise. Masalan, .catch siz qolgan saqlash. Natija oynasida tarmoq yo'q, shuning uchun server javobini soxta Promise bilan beramiz: biri Error bilan, ikkinchisi oddiy satr bilan rad etiladi:
addEventListener("unhandledrejection", (event) => {
// event.reason — reject qilingan qiymat (har doim Error emas!)
console.log("[promise]", String(event.reason));
event.preventDefault(); // natija oynasida qizil yozuv chiqmasin
});
// soxta server: 50 ms dan keyin rad etadi
const fail = (reason) =>
new Promise((_, reject) => setTimeout(() => reject(reason), 50));
export default function App() {
return (
<>
<button onClick={() => fail(new Error("Saqlanmadi: 500"))}>
Saqlash
</button>{" "}
<button onClick={() => fail("vaqt tugadi")}>Vaqt tugadi</button>
</>
);
}Konsolda:
[promise] Error: Saqlanmadi: 500
[promise] vaqt tugadiIkkala tugma ham Promise qaytaradi, lekin uni hech kim kutmaydi va .catch qilmaydi — brauzer uni "ushlanmagan" deb unhandledrejection ga beradi. event.reason — reject() ga nima berilgan bo'lsa, o'sha: birinchisida Error, ikkinchisida oddiy satr (reject({ kod: 42 }) ham bo'ladi). Shuning uchun xato yozuvini yasovchi funksiya har qanday qiymatni qabul qilishi kerak — keyingi bo'limda. event.preventDefault() — xuddi error dagidek, faqat natija oynasi uchun: busiz Chrome konsolda Uncaught (in promise) ni ham qizil qilib chiqaradi.
Tekshirib ko'ring: Komponent
useEffectichidafetch(...).then(r => r.json())qiladi, server HTML qaytaradi vajson()yiqiladi. Bu xato error boundary'ga tushadimi? Qaysi manba uni ushlaydi?
Javob
Yo'q. Boundary faqat render paytidagi xatoni ushlaydi. Effekt ichidagi Promise rad etilishi — asinxron, React'dan tashqarida. Uni unhandledrejection ushlaydi. Boundary'ga yetkazish kerak bo'lsa — xatoni state'ga yozib, render'da throw qilish kerak (TanStack Query ning throwOnError sozlamasi shunday qiladi).
3. Xato yozuvi: nima yuboriladi
3.1 Bir xatoning "pasporti"
Faqat error.message yetmaydi. "Cannot read properties of undefined" — qayerda? Qaysi versiyada? Nima qilganda? Yaxshi yozuvda shular bor:
| Maydon | Misol | Nega kerak |
|---|---|---|
| nom, xabar | TypeError, Cannot read … |
xato turi |
| stack | at Prices (index-BS….js:1:2345) |
qaysi qator |
| reliz (release) | a1b2c3d |
qaysi build — source map shunga bog'lanadi |
| muhit (environment) | production, staging |
sinov va haqiqiy xatolar aralashmasin |
| sahifa | /vazifa/:id |
qayerda |
| iz (breadcrumbs) | ["/", "/savatcha"] |
xatodan oldin nima bo'ldi |
| til, brauzer | ru, Chrome 155 |
faqat bir guruhdami? |
Reliz (release) — build'ning nomi. Odatda commit SHA: git dagi o'sha kod. Xato yozuvida reliz bo'lsa, siz aynan o'sha kodni ochasiz va "bu xato yangi relizdan keyin boshlandimi?" degan savolga javob olasiz.
Iz (breadcrumbs — Sentry'dagi nomi, so'zma-so'z "non ushoqlari") — xatodan oldingi oxirgi harakatlar: sahifa o'tishlari, bosishlar, so'rovlar. Ertakdagi bola o'rmonda yo'lini non ushoqlari bilan belgilagan. Bu yerda ham shunday — xato "qanday yo'l bilan" kelganini ko'rasiz.
3.2 Nima KIRMASLIGI kerak
Xato xabari kutilmagan joydan shaxsiy ma'lumot olib keladi:
TypeError: aziz@example.com uchun buyurtma topilmadi
Error: GET /api/profil?token=abc123&tel=998900000000 — 500Bu matnlar xato serveriga, u yerdan — xatolarni ko'radigan har bir dasturchiga boradi. Email, telefon, so'rov qatoridagi token — PII (personally identifiable information — shaxsni aniqlaydigan ma'lumot). Ular yuborilishidan oldin yashiriladi. Analitika darsidagi qoida bu yerda ham: foydalanuvchi kim ekanini emas, nima buzilganini bilishimiz kerak.
3.3 Jonli: yozuvni yasaymiz
vazifalar ning xato-yozuvi.ts va maxfiylik.ts dagi mantiq (o'quv modeli, inglizcha nomlar bilan):
function scrub(text) {
return text
.replace(/[\w.+-]+@[\w-]+\.[\w.]+/g, "[email]")
.replace(/\+?\d[\d\s()-]{7,}\d/g, "[raqam]")
.replace(/\?[^\s)'"]*/g, "?…");
}
function normalizePath(path) {
const [onlyPath = "/"] = path.split(/[?#]/);
return onlyPath.replace(/\/\d+(?=\/|$)/g, "/:id") || "/";
}
function buildEvent(thrown, source, env) {
const e =
thrown instanceof Error
? thrown
: new Error(
typeof thrown === "string"
? thrown
: JSON.stringify(thrown),
);
return {
name: e.name,
message: scrub(e.message).slice(0, 300),
source,
path: normalizePath(env.path),
release: env.release,
breadcrumbs: [...env.breadcrumbs],
};
}
const env = {
path: "/vazifa/42/tahrir?filtr=faol",
release: "a1b2c3d",
breadcrumbs: ["/", "/vazifa/:id"],
};
const samples = [
new TypeError("aziz@example.com uchun buyurtma topilmadi"),
"vaqt tugadi",
{ kod: 42 },
new Error("GET /api/profil?token=abc123 — 500"),
];
for (const s of samples) {
const ev = buildEvent(s, "promise", env);
console.log(`${ev.name}: ${ev.message}`);
}
console.log(JSON.stringify(buildEvent(samples[0], "window", env)));
export default function App() {
return <p>Konsolga qarang</p>;
}Konsolda:
TypeError: [email] uchun buyurtma topilmadi
Error: vaqt tugadi
Error: {"kod":42}
Error: GET /api/profil?… — 500
{"name":"TypeError","message":"[email] uchun buyurtma topilmadi","source":"window","path":"/vazifa/:id/tahrir","release":"a1b2c3d","breadcrumbs":["/","/vazifa/:id"]}Qatorma-qator:
thrown instanceof Error—throw "matn"yokireject({kod: 42})ham yozuvga aylanadi. Kanon testi aynan shu ikki holatni tekshiradi.scrub— email, uzun raqam (telefon, karta) va so'rov qatori (?token=…) o'rniga belgi. Stack'ga ham qo'llanadi: unda ham manzil bor.normalizePath—/vazifa/42→/vazifa/:id. "42-vazifa" kimniki — kerak emas, "vazifa sahifasi" — kerak. Shunda bir xil xatolar bitta guruhga tushadi.slice(0, 300)— xabar cheksiz uzun bo'lishi mumkin (butun JSON javob). Chegara qo'yiladi.
Diqqat: Regex — to'liq himoya emas.
"Aziz Karimov topilmadi"dagi ism yashirilmaydi. Asosiy qoida: xato xabariga foydalanuvchi ma'lumotini o'zingiz yozmang (throw new Error(\${email} topilmadi`)o'rniga —"Foydalanuvchi topilmadi"`). Regex — ikkinchi qatlam.
3.4 Iz, takror va chegara
Bitta xato sikl ichida 1000 marta qaytarilishi mumkin. Har biri serverga ketsa — tarmoq band, xato serveri limiti tugaydi. Kanon ikki qoida qo'ygan: bir xil xato (nom + xabar + stack'ning birinchi qatori) bir marta yuboriladi, bir sessiyada ko'pi bilan 10 ta. Iz esa oxirgi 10 sahifa bilan cheklangan.
const MAX = 2;
const crumbs = [];
const seen = new Set();
function addCrumb(text) {
crumbs.push(text);
if (crumbs.length > 3) crumbs.shift(); // oxirgi 3 tasi
}
function capture(error) {
const key = `${error.name}:${error.message}`;
if (seen.has(key) || seen.size >= MAX) {
console.log("tashlandi:", error.message);
return;
}
seen.add(key);
const trail = crumbs.join(" → ");
console.log("yuborildi:", error.message, "| iz:", trail);
}
export default function App() {
return (
<>
<button onClick={() => addCrumb("menyu")}>Menyu</button>
<button onClick={() => addCrumb("savatcha")}>Savatcha</button>
<button onClick={() => capture(new Error("narx yo'q"))}>
Xato
</button>
<button onClick={() => capture(new Error("tarmoq"))}>
Tarmoq
</button>
</>
);
}Konsolda:
yuborildi: narx yo'q | iz: menyu → savatcha
tashlandi: narx yo'q
yuborildi: tarmoq | iz: menyu → savatchaMAX ni 1 qilib ko'ring — "tarmoq" ham tashlanadi. vazifalar da MAX = 10. Sentry SDK'da ham shunday himoyalar bor: bir xil xatolarni birlashtiradi (dedupeIntegration) va iz sonini cheklaydi (maxBreadcrumbs, sukut 100).
4. Source map: index-BSuMvEAh.js:1:48211 qayerda?
4.1 Minify qilingan stack
Production build'da kod siqilgan: nomlar qisqargan, hammasi bir qatorda. Mehmondan kelgan stack shunday ko'rinadi:
TypeError: Cannot read properties of undefined (reading 'osh')
at Kt (index-BSuMvEAh.js:1:48211)
at Ii (index-BSuMvEAh.js:1:61022)Kt — qaysi komponent? 48211-belgi — qaysi fayl? Bu savolga source map javob beradi: .map fayli siqilgan koddagi har pozitsiyani asl fayl va qatorga bog'laydi. Buni Source map'lar darsida ko'rgan edik. vazifalar build'i map'larni ham yasaydi (natijalar/tekshiruv-19.txt):
dist/assets/index-BSuMvEAh.js 673.30 kB │ gzip: 220.61 kB │ map: 3,660.08 kBAsosiy fayl 220 KB, uning xaritasi esa 3.6 MB. Brauzer map'ni faqat DevTools ochiq bo'lsa yuklaydi — oddiy mehmon uni yuklamaydi.
4.2 Map kimda bo'lishi kerak
Ikki talab bir-biriga zid:
- Xato serveri stack'ni ochishi uchun map unda bo'lishi kerak.
- Map'da butun asl kod bor (izohlar bilan). Saytda ochiq tursa — har kim o'qiydi.
Yechim — yashirin map (build.sourcemap: "hidden"): .map fayllar yasaladi, lekin JS faylning oxiriga //# sourceMappingURL=… qatori yozilmaydi. Keyin map'lar xato serveriga yuklanadi va dist/ dan o'chiriladi. Saytda map yo'q, xato serverida bor.
Map qaysi build'niki ekanini xato serveri reliz bo'yicha topadi. Shuning uchun reliz nomi ikki joyda bir xil bo'lishi kerak: build ichida (xato yozuviga yoziladi) va map yuklanganda.
sequenceDiagram
participant CI as CI (build)
participant S as Xato serveri
participant B as Mehmon brauzeri
participant D as Dasturchi
CI->>CI: vite build (reliz = commit SHA)
CI->>S: .map fayllar + reliz
CI->>CI: dist/ dan .map o'chiriladi
B->>S: xato: Kt (index.js:1:48211), reliz
S->>S: reliz bo'yicha map'ni topadi
D->>S: ro'yxatni ochadi
S-->>D: Prices (Prices.tsx:12:20)Nimaga qarang: map brauzerga umuman bormaydi. Mehmondan faqat siqilgan stack keladi, uni asl qatorga server o'giradi.
vazifalar esa hozircha sourcemap: true bilan yig'iladi — ataylab. vite.config.ts dagi izoh: "Repo ochiq (Pages) — xaritada sir yo'q; .map faqat DevTools ochilganda yuklanadi". Kod GitHub'da baribir ochiq, map yangi narsa ochmaydi. Yopiq kodli loyihada esa bu qaror noto'g'ri bo'lardi.
Tekshirib ko'ring: Siz
sourcemap: trueqo'ydingiz vadist/ni shundayligicha deploy qildingiz. Xato serveri stack'ni ochadimi? Nima yomon bo'ldi?
Javob
Ochishi mumkin (map'ni saytdan yuklab oladi), lekin endi butun asl kod ochiq: har kim DevTools → Sources'da src/ papkangizni, izohlaringizni ko'radi. Ichida maxfiy narsa bo'lmasligi kerak (kalitlar umuman frontend'da bo'lmaydi), baribir bu ortiqcha ochiqlik. To'g'risi — "hidden", yuklash, o'chirish.
5. Sentry React'da (hujjat bo'yicha)
5.1 Sentry nima va nega hisob kerak
Sentry — eng ko'p ishlatiladigan xato kuzatuvi xizmati. Brauzer tomonidagi qism — SDK (@sentry/react): u yuqoridagi to'rt manbani o'zi ushlaydi, iz yig'adi va yozuvni Sentry serveriga yuboradi. Server tomoni — guruhlash, source map bilan ochish, bildirishnoma (Slack, email), grafiklar.
Ulash uchun DSN (Data Source Name) kerak — loyihangizning "manzili": https://<kalit>@o<raqam>.ingest.sentry.io/<loyiha>. DSN faqat Sentry'da hisob ochib, loyiha yaratilganda beriladi. Kurs topshirig'iga ko'ra biz hisob ochmadik. Shuning uchun quyidagi kod — hujjat bo'yicha (docs.sentry.io, React qo'llanmasi), haqiqiy Sentry bilan ishga tushirilmagan. Kanon esa SDK'ni soxta DSN bilan o'lchadi — pastda.
npm i -E @sentry/react@11.6.05.2 init: birinchi import
Sentry ilovadagi hamma narsadan oldin ishga tushishi kerak — aks holda birinchi xatolar ushlanmaydi. Hujjat alohida instrument.ts faylini tavsiya qiladi va main.tsx ning birinchi qatorida import qiladi:
// src/instrument.ts
import * as Sentry from "@sentry/react";
Sentry.init({
dsn: import.meta.env.VITE_SENTRY_DSN, // yo'q bo'lsa — o'chiq
environment: import.meta.env.MODE, // "production" | "development"
release: __RELIZ__, // build'dagi commit SHA
sendDefaultPii: false, // IP va cookie yuborilmasin (sukut)
integrations: [
Sentry.browserTracingIntegration(),
Sentry.replayIntegration(), // matn va media sukut bo'yicha yopiq
],
tracesSampleRate: 0.1, // sahifa yuklanishlarining 10% i
replaysSessionSampleRate: 0, // oddiy sessiyalar yozilmaydi
replaysOnErrorSampleRate: 1.0, // xato bo'lganlari — hammasi
beforeSend(event) {
// oxirgi filtr: yuborishdan oldin o'zgartirish yoki tashlash
if (event.request?.url) {
event.request.url = event.request.url.split("?")[0];
}
return event; // null qaytarsa — yuborilmaydi
},
});Har sozlama nima qiladi:
dsn— bo'sh bo'lsa SDK hech narsa yubormaydi. Dev'da DSN bermaslik — sinov xatolari bilan ro'yxatni to'ldirmaslik yo'li.environment— bir loyihadaproduction,staging,developmentxatolarini ajratadi.release— «Source map» bo'limidagi reliz: map shu nom bilan yuklanadi.sendDefaultPii: false— Sentry 10-versiyadan beri IP manzil faqattruebo'lsa yig'iladi (hujjatdagi "v9 → v10" o'tish yo'riqnomasi).beforeSend— yozuv ketishidan oldingi oxirgi ilgak: «Xato yozuvi» bo'limidagiscrubshu yerga qo'yiladi.nullqaytarsa — xato tashlanadi (masalan, brauzer kengaytmasidan kelgan xatolar).
5.3 React 19: createRoot ilgaklari va ErrorBoundary
React 19 dagi onUncaughtError/onCaughtError ga Sentry tayyor ishlovchi beradi — reactErrorHandler:
import "./instrument"; // birinchi qator!
import * as Sentry from "@sentry/react";
import { createRoot } from "react-dom/client";
createRoot(document.getElementById("root")!, {
onUncaughtError: Sentry.reactErrorHandler(),
onCaughtError: Sentry.reactErrorHandler(),
onRecoverableError: Sentry.reactErrorHandler(),
}).render(<App />);Bu «Jonli: uchtasini bitta joyga yig'amiz» dagi report ning tayyor varianti. onRecoverableError — React o'zi tiklagan xatolar (masalan, gidratatsiya nomuvofiqligi, Next.js qismida uchraydi).
Sentry'da o'z error boundary'si ham bor. U xatoni ushlaydi, yuboradi va zaxira UI chizadi:
<Sentry.ErrorBoundary
fallback={({ resetError }) => (
<p>
Narxlar yuklanmadi.{" "}
<button onClick={resetError}>Qayta urinish</button>
</p>
)}
beforeCapture={(scope) => scope.setTag("bolim", "narxlar")}
>
<Prices />
</Sentry.ErrorBoundary>Bu 18/#08 dagi react-error-boundary ga o'xshaydi: fallback, resetError. Farqi — xato avtomatik yuboriladi va setTag bilan "qaysi bo'lim" belgisi qo'shiladi.
5.4 Qo'lda: captureException, iz, foydalanuvchi
try {
await saveOrder(order);
} catch (error) {
Sentry.captureException(error, { tags: { qadam: "tolov" } });
showToast("Buyurtma saqlanmadi, qayta urinib ko'ring");
}
// O'z iz yozuvingiz (sahifa o'tishlari, bosishlarni SDK o'zi yozadi)
Sentry.addBreadcrumb({
category: "savat",
message: "taom qo'shildi",
});
// Kim — faqat ichki id (email emas)
Sentry.setUser({ id: "u_1842" });try/catch da ushlangan xato window ga chiqmaydi — SDK uni bilmaydi. Foydalanuvchiga xabar ko'rsatib, xatoni jim yutib yuborish — eng ko'p uchraydigan xato. Ushladingizmi — yuboring.
5.5 Tracing va replay
Sentry faqat xatolar emas:
- Tracing (kuzatuv izi) — sahifa qancha vaqtda yuklandi, qaysi
fetchsekin.browserTracingIntegrationsahifa yuklanishi va navigatsiyani o'lchaydi.tracesSampleRate: 0.1— har 10 tashrifdan bittasi. Namuna olish (sampling) — hammasini yozish qimmat (tarmoq, server limiti), 10% esa umumiy rasmni beradi. - Session replay (sessiya yozuvi) — xatodan oldingi daqiqalarni "video" kabi qayta ko'rsatadi. Aslida video emas: DOM o'zgarishlari yoziladi. Hujjat bo'yicha sukut holatida barcha matn
***bilan, rasm va media yopiq (maskAllText,blockAllMedia) — kiritilgan parol va ism yozuvga tushmaydi.
Replay — eng og'ir va eng maxfiylikka sezgir qism. replaysSessionSampleRate: 0 va replaysOnErrorSampleRate: 1.0 — odatiy murosa: faqat xato bo'lgan sessiyalar. Kanon replay hajmini o'lchamagan — raqam aytmaymiz.
5.6 Source map'ni Vite plagini bilan yuklash
// vite.config.ts
import { sentryVitePlugin } from "@sentry/vite-plugin";
export default defineConfig({
build: { sourcemap: "hidden" },
plugins: [
react(),
// Sentry plagini — eng oxirida
sentryVitePlugin({
org: "sizning-tashkilot",
project: "vazifalar",
authToken: process.env.SENTRY_AUTH_TOKEN, // faqat CI'da
release: { name: process.env.GITHUB_SHA },
sourcemaps: { filesToDeleteAfterUpload: ["./dist/**/*.map"] },
}),
],
});«Map kimda bo'lishi kerak» dagi uch qadam — bitta plaginda: map yasash (hidden), yuklash, o'chirish. SENTRY_AUTH_TOKEN — maxfiy kalit (DSN'dan farqli): u bilan loyihangizga map yuklash mumkin. U faqat CI sirlarida turadi, .env ga yozilmaydi, VITE_ prefiksi bilan hech qachon nomlanmaydi — aks holda build'ga, ya'ni har kimning brauzeriga tushadi (env o'zgaruvchilar).
Hujjat ogohlantiradi: plagin dev rejimda map yuklamaydi — sinash uchun production build kerak.
5.7 Biz sinab ko'rdik: hajm va CSP
Kanon tajribasida (tajriba/kutubxonalar/src/k/xato-sentry.tsx) SDK eng kichik misol bilan yig'ildi. DSN soxta — o'z origin'imizdagi manzil, Sentry hisobi yo'q:
// #19 @sentry/react 11.6.0 — DSN soxta: lokal qabul qiluvchi
import * as Sentry from "@sentry/react";
Sentry.init({
dsn: location.protocol + "//sinov@" + location.host + "/1",
tracesSampleRate: 0,
});Natija (kutubxonalar-21.txt):
xato-sentry JS 97981 (+ 29.6 KB) CSS 0 | CSP 0 | xato: yo'q | ochiq: tashqi yo'q- +29.6 KB gzip — faqat
init, tracing va replay'siz. Ular qo'shilsa hajm oshadi (o'lchanmadi). - CSP 0 —
vazifalarsiyosati va Trusted Types ostida buzilish yo'q: SDKinnerHTMLishlatmaydi va tashqi skript yuklamaydi. Lekin bu DSN o'z origin'imizda bo'lgani uchun. Haqiqiy DSN bilan yozuvlaro<raqam>.ingest.sentry.ioga ketadi —connect-srcga shu host qo'shilishi kerak. Bu — maxfiylik qarori ham: xato matni endi uchinchi tomon serverida.
- vazifalar: o'z kuzatuvibirinchi yuklash farqi, holat 18 → 190,1 KB
- @sentry/react 11.6.0 (faqat init)React'dan tashqari29,6 KB
Manba: O'lchandi: kanon, Vite 8.3.3, gzip -9, natijalar/tekshiruv-18.txt va -19.txt, natijalar/tajriba/kutubxonalar-21.txt, 2026-10-17
Ikki raqam bir xil narsani o'lchamaydi: Sentry — tayyor guruhlash, map, bildirishnoma va dashboard bilan keladi. Bizniki — faqat yig'ish va yuborish. Server tomoni yo'q.
6. Sentry'dan boshqa yo'llar
| Yo'l | Nima | Qachon |
|---|---|---|
| Sentry (bulut) | tayyor xizmat, bepul tarifi bor (limit bilan) | ko'p loyihalar uchun birinchi tanlov |
| Sentry (o'z serverida) | ochiq kod, lekin og'ir: ko'p servis (Kafka, ClickHouse…) | katta kompaniya, ma'lumot o'z serverida bo'lishi shart |
| GlitchTip | ochiq kodli, yengil, Sentry SDK bilan ishlaydi | ma'lumot O'zbekistonda / o'z VPS'ingizda qolishi kerak |
| O'z yechim | error + unhandledrejection + sendBeacon |
o'rganish, juda kichik loyiha, qat'iy CSP |
GlitchTip — alohida loyiha, lekin Sentry protokolini tushunadi: ilovada @sentry/react qoladi, faqat dsn GlitchTip serveringizga ko'rsatiladi. Bu qulay chiqish yo'li: kod o'zgarmaydi, ma'lumot sizning serveringizda. Shaxsga doir ma'lumotni mamlakat ichida saqlash talabi bo'lsa (analitika darsidagi qonun), shu muhim.
Xatolarni kuzatish backend'da ham bor — Node va NestJS uchun Sentry SDK, sampling, reliz va PII tozalash 34-qismda to'liq.
7. Vazifalar qadami: o'z xato kuzatuvi (holat 19)
7.1 Branch va fayllar
- Branch:
feat/xato-kuzatuvi
| Fayl | O'zgarish |
|---|---|
src/kuzatuv/xato-yozuvi.ts |
yangi: yozuv shakli va yasovchisi (sof — node:test) |
src/kuzatuv/xatolar.ts |
yangi: manbalar, iz, takror, yuborish |
src/main.tsx |
onCaughtError/onUncaughtError → xatoniYubor, router → izQoldir |
vite.config.ts, vitest.config.ts, vite-env.d.ts |
__RELIZ__ |
tekshiruv/maxfiylik.test.js |
+2 test |
Nega Sentry emas? Fayl boshidagi izoh (kanondan):
// kuzatuv/xatolar.ts — foydalanuvchida yuz bergan xatolarni yig'ish
// (21/#19): Sentry SDK'siz. @sentry/react 11.6.0 o'lchandi — +29.6 KB
// gzip, CSP 0 (natijalar/tajriba/kutubxonalar-21.txt), lekin DSN
// (hisob) kerak; bizga kerak bo'lgani — 4 manba va bitta POST(Izoh qatorlari telefonga sig'ishi uchun qayta bo'lingan.)
7.2 Yozuv: xato-yozuvi.ts
Bu «Jonli: yozuvni yasaymiz» dagi buildEvent ning asli. shaxsiyniYashir va yolniUmumlashtir — analitika bilan umumiy maxfiylik.ts dan:
export type XatoManbai = "window" | "promise" | "react" | "chegara";
/** Har qanday throw qilingan qiymatdan yozuv (sof — node:test) */
export function xatoYozuvi(
xato: unknown,
manba: XatoManbai,
muhit: {
yol: string;
til: string;
reliz: string;
iz: readonly string[];
},
): XatoYozuvi {
const e =
xato instanceof Error
? xato
: new Error(
typeof xato === "string" ? xato : JSON.stringify(xato),
);
return {
nom: e.name,
xabar: shaxsiyniYashir(e.message).slice(0, 300),
stack: shaxsiyniYashir(
(e.stack ?? "").split("\n").slice(0, 8).join("\n"),
),
manba,
yol: yolniUmumlashtir(muhit.yol),
til: muhit.til,
reliz: muhit.reliz,
iz: [...muhit.iz],
vaqt: Date.now(),
};
}XatoManbai — literal turlar birlashmasi: noto'g'ri manba nomi tsc xatosi beradi. Stack 8 qator bilan cheklangan — yetarli va yozuv kichik.
7.3 Yuborish: xatolar.ts
const MANZIL = import.meta.env.VITE_XATO_URL;
const MAKS = 10;
const IZ_UZUNLIGI = 10;
const iz: string[] = [];
const korilgan = new Set<string>();
/** Sahifa o'tishi — izga (main.tsx router.subscribe) */
export function izQoldir(yol: string): void {
iz.push(yolniUmumlashtir(yol));
if (iz.length > IZ_UZUNLIGI) {
iz.shift();
}
}
/** Xatoni yuborish (manzil bo'lsa).
* Takror va ortiqchasi — tashlanadi */
export function xatoniYubor(xato: unknown, manba: XatoManbai): void {
if (MANZIL === undefined || korilgan.size >= MAKS) {
return;
}
const yozuv = xatoYozuvi(xato, manba, {
yol: location.pathname,
til: document.documentElement.lang,
reliz: __RELIZ__,
iz,
});
const kalit = `${yozuv.nom}:${yozuv.xabar}:${yozuv.stack.split("\n")[1] ?? ""}`;
if (korilgan.has(kalit)) {
return;
}
korilgan.add(kalit);
const tana = new Blob([JSON.stringify(yozuv)], {
type: "application/json",
});
// sendBeacon — sahifa yiqilib yopilayotgan bo'lsa ham ketadi
if (!navigator.sendBeacon(MANZIL, tana)) {
void fetch(MANZIL, {
method: "POST",
body: tana,
keepalive: true,
}).catch(() => {});
}
}
if (MANZIL !== undefined) {
addEventListener("error", (event) => {
xatoniYubor(event.error ?? event.message, "window");
});
addEventListener("unhandledrejection", (event) => {
xatoniYubor(event.reason, "promise");
});
}Darsdagi hamma g'oya shu yerda:
MANZIL === undefined— production'daVITE_XATO_URLyo'q, demak hech narsa yuborilmaydi va tinglovchilar ham ulanmaydi. Dev'da manzil — Vite plagini (skriptlar/kuzatuv.js): yozuvlar terminalda ko'rinadi.kalit— nom + xabar + stack'ning birinchiatqatori. Bir xil xato bir marta.navigator.sendBeacon— analitika dagidek: sahifa yopilayotganda ham ketadi. Brauzer rad etsa (hajm katta) —fetch+keepalive.event.error ?? event.message— begona domendagi skript xatosida brauzerevent.errorni bermaydi (pastda — «Ko'p uchraydigan xatolar»).
Kodning qolgan qismi — faqat dev'da (import.meta.env.DEV): window.__xatoSinovi — e2e sinov uchun xato chiqaruvchi. Kanon izohi: puppeteer'ning evaluate idan otilgan xato brauzerga «Script error.» bo'lib kelgan, shuning uchun xato ilovaning o'z modulidan otiladi. Production build'da bu blok yo'q.
7.4 main.tsx va reliz
React manbalari — 17/#45 va 18/#08 dagi ilgaklarda bittadan qator:
onCaughtError(xato, malumot) {
+ xatoniYubor(xato, "chegara"); // 21/#19
console.error(
...
onUncaughtError(xato, malumot) {
+ xatoniYubor(xato, "react"); // 21/#19Iz — analitika uchun yozilgan router.subscribe ga bitta chaqiruv: izQoldir(oxirgiYol).
Reliz nomi — vite.config.ts dagi define:
// 21/#19: reliz nomi xato yozuvlariga (kuzatuv/xatolar.ts):
// Actions'da commit SHA, lokal build'da "lokal"
define: {
__RELIZ__: JSON.stringify(
process.env.GITHUB_SHA?.slice(0, 7) ?? "lokal",
),
},define — build paytida __RELIZ__ so'zini satr bilan almashtiradi. TypeScript uni bilishi uchun vite-env.d.ts ga declare const __RELIZ__: string;, Vitest uchun esa vitest.config.ts ga __RELIZ__: JSON.stringify("test").
7.5 Natija
Kanon tekshiruvi (tekshiruv-19.txt): build, node:test 238 (+2: yuqoridagi Error va Error bo'lmagan qiymat holatlari), Vitest 65, lint, format, tip, knip — toza. Birinchi yuklash JS (gzip): 223 286 → 223 376 bayt — +90 bayt. Production'da MANZIL yo'q: xatoniYubor darhol qaytadi, tinglovchilar ulanmaydi.
Dev server'da (natija-dev-19.txt, Chrome 155):
--- 21/#19 xatolar
✅ 2 xato (3 ta bir xili — bitta), manba, yashirilgan email: [["window","Error","sinov xatosi: [email]"],["promise","Error","va'da rad etildi"]]
✅ yozuvda: yo'l, reliz, iz (oxirgi sahifalar), stack: ["/statistika","lokal",["/","/statistika"],true]Sinov uchta bir xil xato (ichida aziz@example.com) va bitta rad etilgan Promise chiqardi. Keldi — ikkita: takror bir marta, email yashirilgan, iz va reliz bor. Production build'da (natija-21-19.txt):
--- 21/#19 xatolar (production)
✅ build: dev sinov ilgagi (__xatoSinovi) va qabul qiluvchi yo'q: [false,false]
✅ production: xato yuborilmadi (VITE_XATO_URL yo'q): []Halol chegara (TEXNIK-QARZ 65): production'da xatolar yig'ilmaydi — manzil yo'q. Source map bilan stack'ni ochuvchi server ham yo'q. O'z serverimiz 25-qismda (HTTP, Express va REST API) keladi: shu JSON shaklini qabul qiladigan bitta marshrut.
7.6 Commit
git add -A
git commit -m "feat: xatolarni yig'ish — window, promise, React; reliz, iz, maxfiylik"8. Hujumchi nigohi
1. Soxta xatolar bilan to'ldirish. DSN (yoki bizning VITE_XATO_URL) — maxfiy emas: u build'da, har kim ko'radi. Hujumchi skript bilan sekundiga minglab soxta xato yuboradi. Natija: Sentry oylik limiti tugaydi va haqiqiy xatolar yozilmay qoladi. Yoki ro'yxat axlat bilan to'lib, haqiqiy xato ko'rinmaydi.
Himoya — server tomonda: Sentry loyihasida ruxsat etilgan domenlar (Allowed Domains), so'rovlar chegarasi va kiruvchi filtrlar bor. O'z serveringizda — rate limit va yozuv hajmi chegarasi. Brauzerdagi MAKS = 10 faqat halol ilovani to'xtatadi, hujumchini emas.
2. Maxfiy ma'lumot sizib chiqishi. Xato xabarlari va iz — sizib chiqish uchun eng qulay joy: so'rov qatoridagi token, formadagi telefon, JSON.stringify(user). Himoya — «Xato yozuvi» bo'limidagi uch qatlam: xabarga ma'lumot yozmaslik, beforeSend/shaxsiyniYashir, sendDefaultPii: false. Replay'da — maskAllText ni o'chirmang.
3. Ochiq source map. Yopiq kodli loyihada sourcemap: true bilan deploy — asl kod, izohlar va ichki API manzillari ochiq. Himoya — "hidden" va yuklagandan keyin o'chirish («Map kimda bo'lishi kerak»).
4. Foydalanuvchiga ichki xato matni. {error.stack} ni ekranga chiqarish — hujumchiga kutubxonalar versiyasi va fayl tuzilmasini beradi. Foydalanuvchiga — umumiy xabar ("Nimadir buzildi, qayta urinib ko'ring"), batafsil — faqat xato serveriga.
9. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
Ro'yxatda faqat Script error. |
skript boshqa domendan, brauzer xato tafsilotini yashiradi | <script crossorigin> + serverda CORS, yoki skriptni o'z domeningizdan bering |
Stack: Kt (index-….js:1:48211) |
map yuklanmagan yoki reliz nomi mos emas | reliz bir xil bo'lsin: build'da va yuklashda |
| Map'lar saytda ochiq | sourcemap: true, o'chirilmagan |
"hidden" + filesToDeleteAfterUpload |
| Birinchi xatolar yo'q | init ilova importlaridan keyin |
import "./instrument" — birinchi qator |
| Ushlangan xatolar hech qayerda yo'q | catch ichida faqat toast |
captureException(error) |
| Ro'yxat dev xatolari bilan to'lgan | dev'da ham DSN bor | dev'da DSN bermang yoki environment bilan ajrating |
| Xato matnida email | throw new Error(\${email} …`)` |
xabarga ma'lumot yozmang + beforeSend |
Failed to set … violates connect-src |
DSN hosti CSP'da yo'q | connect-src ga *.ingest.sentry.io (ongli qaror) |
10. Mashqlar
Mashqlar kurs/mashqlar/21/19-xato-kuzatuvi/ da. Jonli yechimlarni «Ishga tushir» bilan sinang.
1-mashq (oson): bo'sh joylarni to'ldiring
- Rad etilgan va ushlanmagan Promise'ni ushlaydigan
windowhodisasi: - Error boundary ushlagan xatoni beradigan
createRootopsiyasi: - Map yasaladi, lekin JS faylga havola yozilmaydigan Vite sozlamasi:
sourcemap: "" - Xatodan oldingi harakatlar ro'yxati (Sentry nomi, inglizcha):
- Sentry'da yozuvni yuborishdan oldin o'zgartiradigan yoki tashlaydigan ilgak:
Yechim
unhandledrejection — .catch siz qolgan Promise. onCaughtError — boundary ushlagani, onUncaughtError esa ushlanmagani (ilova yiqilgan). "hidden" — map bor, lekin sourceMappingURL qatori yo'q: brauzer uni qidirmaydi. breadcrumbs — kanonda "iz". beforeSend null qaytarsa — yozuv yuborilmaydi.
2-mashq (o'rta): scrub ni kengaytiring
«Xato yozuvi» bo'limidagi scrub ga ikki qoida qo'shing:
- Telegram username (
@bilan boshlanadigan, kamida 5 belgili:@ali_karimov) →[username]. Email'dagi@ga tegmasin — email qoidasi birinchi ishlasin. - JWT ko'rinishidagi token (
eyJbilan boshlanadi, nuqta bilan ajralgan uch bo'lak) →[token].
Sinov — to'rt satr:
"Buyurtma @ali_karimov uchun topilmadi"
"aziz@example.com yozdi"
"Bearer eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MX0.abc-DEF_1 eskirgan"
"+998 90 000 00 00 raqamiga SMS ketmadi"Ishora: regex'lar tartibi muhim — replace zanjiri yuqoridan pastga ishlaydi (regex almashtirish). Username uchun @[\w]{5,}, token uchun eyJ[\w-]+\.[\w-]+\.[\w-]+.
Yechim
function scrub(text) {
return text
.replace(/[\w.+-]+@[\w-]+\.[\w.]+/g, "[email]")
.replace(/eyJ[\w-]+\.[\w-]+\.[\w-]+/g, "[token]")
.replace(/@\w{5,}/g, "[username]")
.replace(/\+?\d[\d\s()-]{7,}\d/g, "[raqam]")
.replace(/\?[^\s)'"]*/g, "?…");
}
for (const text of [
"Buyurtma @ali_karimov uchun topilmadi",
"aziz@example.com yozdi",
"Bearer eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MX0.abc-DEF_1 eskirgan",
"+998 90 000 00 00 raqamiga SMS ketmadi",
]) {
console.log(scrub(text));
}Konsolda:
Buyurtma [username] uchun topilmadi
[email] yozdi
Bearer [token] eskirgan
[raqam] raqamiga SMS ketmadiEmail birinchi: aks holda aziz@example.com dagi @example username deb olinib, aziz[username].com bo'lardi. Token raqam qoidasidan oldin: tokenda raqamlar ketma-ketligi uchrasa, u bo'linib ketmasin.
3-mashq (qiyin): boundary + "Qayta urinish" + yuborish
«Bahor» narxlar bo'limi uchun komponent yozing:
Pricesserver tayyor bo'lmaguncha yiqiladi (TypeError), tayyor bo'lgach «Osh: 35 000 so'm» ni chizadi. "Server tayyor" — komponentdan tashqaridagiserverReadyo'zgaruvchisi, «Qayta urinish» unitrueqiladi.- Boundary zaxira UI: «Narxlar yuklanmadi» va «Qayta urinish» tugmasi. Tugma boundary'ni tozalaydi (
setState({ failed: false })). - Xato
createRootningonCaughtErrororqalicapturega tushsin.capturekonsolgayuborildi: TypeError | iz: …chiqarsin. Iz — boundary'dagi har «Qayta urinish» bosilishi ("qayta").
Sinov: «Qayta urinish» ni bosing — narx chiqsin, konsolda bitta yozuv.
Yechim
import { Component } from "react";
import { createRoot } from "react-dom/client";
const crumbs = [];
let serverReady = false; // birinchi so'rov — muvaffaqiyatsiz
function capture(error) {
console.log(
`yuborildi: ${error.name} | iz: ${crumbs.join(", ") || "-"}`,
);
}
class Boundary extends Component {
state = { failed: false };
static getDerivedStateFromError() {
return { failed: true };
}
retry = () => {
crumbs.push("qayta");
serverReady = true; // ikkinchi so'rov — javob keldi
this.setState({ failed: false });
};
render() {
if (!this.state.failed) return this.props.children;
return (
<p>
Narxlar yuklanmadi{" "}
<button onClick={this.retry}>Qayta urinish</button>
</p>
);
}
}
function Prices() {
const menu = serverReady ? { osh: "35 000" } : undefined;
return <p>Osh: {menu.osh} so'm</p>;
}
createRoot(document.getElementById("root"), {
onCaughtError: (error) => capture(error),
}).render(
<Boundary>
<Prices />
</Boundary>,
);Konsolda:
yuborildi: TypeError | iz: -Ekranda:
Osh: 35 000 so'mXato bir marta yuborildi — birinchi chizishda, iz hali bo'sh. «Qayta urinish» boundary'ni tozaladi, Prices qayta chizildi va endi yiqilmadi. Nega sanoq (renders === 1) emas, serverReady? React render xatosidan keyin komponentni yana bir marta chizib ko'radi — sanoq ikkinchi urinishda 2 bo'lib, xato yo'qolib qolardi (biz shunday yozib ko'rdik: zaxira UI umuman chiqmadi). Agar ikkinchi chizish ham yiqilganda, onCaughtError yana chaqirilardi va iz qayta ni ko'rsatardi — dasturchi "foydalanuvchi qayta urindi, baribir buzildi" deb biladi.
4-mashq: Vazifalar qadami
feat/xato-kuzatuvi branch'ida «Vazifalar qadami» ni bajaring: kuzatuv/xato-yozuvi.ts, kuzatuv/xatolar.ts, main.tsx dagi ikki ilgak va izQoldir, __RELIZ__ (vite.config.ts, vitest.config.ts, vite-env.d.ts), ikki test. npm run check toza bo'lsin. Keyin:
npm run devda ilovani oching, DevTools konsolida__xatoSinovi("sinov ali@example.com")ni uch marta chaqiring. Terminalda nechta[xatolar]qatori chiqdi?npm run buildvadist/assets/da.mapfayllar bormi? JS faylning oxirgi qatori nima?npm run previewda xuddi shu__xatoSinovini chaqirib ko'ring.
Yechim
- Bitta: takror bir marta yuboriladi, email o'rnida
[email]. - Bor — kanon build'i map yasaydi (
map: 3,660.08 kB). JS fayl oxirida//# sourceMappingURL=…qatori bor:sourcemap: true,"hidden"emas. Bu ataylab — repo ochiq, map yangi sir ochmaydi. Yopiq loyihada va map'ni xato serveriga yuklash bosqichi qo'shilganda (25/34-qism)"hidden"+ o'chirish. __xatoSinovi is not defined— production build'da bu ilgak yo'q (import.meta.env.DEV). Manzil ham yo'q: xato faqat konsolda.
11. Real ishda
- Deyarli har production loyihada xato kuzatuvi bor — Sentry yoki muqobili. Yangi ishga kirganda birinchi haftada "Sentry'dagi eng ko'p xatoni tuzating" topshirig'i odatiy.
- Reliz va deploy bog'liq: CI har deploy'da reliz yaratadi va map yuklaydi. Xato "qaysi relizdan boshlandi" — orqaga qaytarish (rollback) qarorining asosi (34-qism).
- Ogohlantirishlar: "yangi xato turi paydo bo'ldi" yoki "xatolar soni 5 daqiqada 100 dan oshdi" — Telegram yoki Slack'ga. Navbatchi dasturchi shu bilan uyg'onadi.
- Intervyu savollari: "Error boundary qaysi xatolarni ushlamaydi?", "Source map'ni production'da qanday saqlaysiz?", "Reliz nima uchun kerak?", "Xato kuzatuvida qanday ma'lumot yubormaslik kerak?", "Sampling nima?".
Xulosa
- Xato kuzatuvi — foydalanuvchi brauzeridagi xatoni serverga yig'ish: to'rt manba —
error,unhandledrejection, ReactonCaughtError/onUncaughtError, qo'ldacatch. - Yaxshi yozuv: nom, xabar, stack, reliz, muhit, umumlashtirilgan sahifa va iz; email, telefon, token — yashiriladi; takror — bir marta, sessiyada chegara.
- Minify qilingan stack source map bilan ochiladi: map
"hidden", xato serveriga yuklanadi, saytdan o'chiriladi; reliz nomi ikki joyda bir xil. @sentry/react:initbirinchi import,reactErrorHandler,ErrorBoundary,captureException,beforeSend, tracing va replay — namuna olish bilan; +29.6 KB, DSN = hisob; GlitchTip — o'sha SDK bilan o'z serveringiz.vazifalar: o'z kuzatuvi (+90 bayt), dev'da yuboradi, production'da manzil yo'q — halol chegara, server 25-qismda.
Keyingi dars: Feature flag va A/B testlar — yangi funksiyani deploysiz yoqib-o'chiramiz: vazifalar dagi teglar grafigi flag ortiga o'tadi, ?flag= sinovchi havolasi va "miltillash" muammosi.
Manbalar
- Sentry: "React" qo'llanmasi (
init,reactErrorHandler,ErrorBoundary), "Session Replay", "Tracing", "Source Maps — Vite", "Migration v9 → v10" (sendDefaultPii) — docs.sentry.io/platforms/javascript/guides/react - @sentry/vite-plugin — npmjs.com/package/@sentry/vite-plugin
- GlitchTip — glitchtip.com/documentation
- MDN:
Window: error event,unhandledrejection event,Navigator.sendBeacon(), "Script error" (CORS) — developer.mozilla.org - React:
createRoot(onCaughtError,onUncaughtError,onRecoverableError) — react.dev/reference/react-dom/client/createRoot - Kanon:
vazifalarholat 19 (src/kuzatuv/,main.tsx,vite.config.ts),natijalar/tekshiruv-19.txt,brauzer-tekshiruv/natija-dev-19.txt,natija-21-19.txt,natijalar/tajriba/kutubxonalar-21.txt, TEXNIK-QARZ 65 (2026-10-17)
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!