ব্লগে ফিরে যান
১৩ আগস্ট, ২০২৬Sergei Solod15 মিনিট পড়া

সব ব্রাউজার এররেই অ্যালার্ট দিচ্ছিলাম: সেই শোরকে কীভাবে কাজে লাগার প্রোডাকশন মনিটরিংয়ে বদলেছি

শুরুর ফ্রন্টএন্ড রিপোর্টার বিজ্ঞাপন ব্লক হওয়া, GTM ব্যর্থতা, AbortError, অস্পষ্ট Script error এবং আসল Next.js চাঙ্ক ব্যর্থতাকে একইভাবে দেখত। পরে মালিকানা, ব্যবহারকারীর ওপর প্রভাব, প্রমাণের মান, ঘটনাগুলোর সহসম্পর্ক এবং রিকভারিকে ভিত্তি করে মনিটরিং নতুন করে সাজিয়েছি।

ব্রাউজার এরর মনিটরিংফ্রন্টএন্ড অবজারভেবিলিটিJavaScript এররপ্রোডাকশন মনিটরিংNext.jsওয়েব পারফরম্যান্স

আমি ক্লায়েন্ট-সাইড এরর রিপোর্টিং বানিয়েছিলাম কারণ বাস্তব ব্যবহারকারীরা যে সমস্যায় পড়ছেন, বিশেষ করে যেগুলো লোকালে নির্ভরযোগ্যভাবে পুনরায় তৈরি করা কঠিন, সেগুলো দেখতে চেয়েছিলাম। রিপোর্টার ঠিক আমার বলা কাজটাই করছিল: ত্রুটি ধরো, আমাকে পাঠাও।

সমস্যা ছিল, প্রায় সবকিছুই সমান জরুরি দেখাত।

কোনো বাইরের অ্যানালিটিক্স স্ক্রিপ্ট লোড হলো না? লাল অ্যালার্ট। বিজ্ঞাপনের স্ক্রিপ্ট ব্লক হলো? লাল অ্যালার্ট। কোনো ক্রলার Google Analytics লোড করতে পারল না? লাল অ্যালার্ট। ভিডিও প্রিভিউ play() চালিয়ে Promise শেষ হওয়ার আগেই থেমে গেল? লাল অ্যালার্ট। কাজে লাগার মতো উৎস বা স্ট্যাক ছাড়া Script error. এল? আবার লাল অ্যালার্ট।

একই ধারায় এমন ব্যর্থতাও ছিল যেগুলো সত্যিই ব্যবস্থা নেওয়ার মতো: আমার অ্যাপের URL ভুল করে https://example.comhttps://example.com/... হয়ে গিয়েছিল, আর ব্রাউজার /_next/static/chunks/... এর নিচে আমার অ্যাপের একটি Next.js ফাইল লোড করতে পারেনি।

সংগ্রহ স্তর কাজ করছিল। মনিটরিং স্তর করছিল না।

এই পার্থক্য ফ্রন্টএন্ড অবজারভেবিলিটি নিয়ে আমার ভাবনা বদলে দেয়। একটি এরর ইভেন্ট শুধু প্রমাণ করে যে কিছু ঘটেছে। সেটি এখনও রোগনির্ণয় নয়, গুরুত্বের স্তর নয়, এমনকি একটি ইনসিডেন্টও নয়।

প্রথম ভুল ছিল “এরর” মানেই “জরুরি” ধরে নেওয়া

আমার প্রথম মানসিক মডেল ছিল প্রায় এমন:

browser error report করে
        ↓
CLIENT ERROR পাঠাও
        ↓
developer-এর এখনই দেখা দরকার

এই মডেলটি অনেক আলাদা প্রশ্নকে একসঙ্গে মিশিয়ে দিয়েছিল। ত্রুটিটা কি আমার কোডে? সক্রিয় রুট সত্যিই ভেঙেছে? এটা কি প্রত্যাশিত বাতিল? ব্রাউজারের কাছে উৎস শনাক্ত করার মতো তথ্য আছে? অ্যাপ কি রিকভার করেছে? দশটি বার্তা মানে দশটি ইনসিডেন্ট, নাকি একটি ইনসিডেন্টের দশটি লক্ষণ?

এই প্রশ্নগুলোর উত্তর না পাওয়া পর্যন্ত কোনো ইভেন্ট স্বয়ংক্রিয়ভাবে অ্যালার্ট হওয়া উচিত নয়।

একটি প্রাথমিক পর্যালোচনায় আমার সামনে প্রায় আঠারোটি বার্তা ছিল। বেশিরভাগই বাইরের সেবা বা স্বাভাবিক লাইফসাইকেলের শোর। দুটি ঘটনা স্পষ্টভাবে আলাদা ছিল: আমার অ্যাপের বিকৃত URL নিশ্চিত বাগ, আর আমার অ্যাপের JavaScript চাঙ্ক লোড না হওয়া পেজকে প্রয়োজনীয় কোড থেকে বঞ্চিত করতে পারত। তবুও রিপোর্টার প্রায় সবকিছুকে ব্লক হওয়া বিজ্ঞাপনের মতোই জরুরি দেখাচ্ছিল।

সেখানেই “সব ব্রাউজার এরর সংগ্রহ করা” আর “প্রোডাকশন মনিটরিং বানানো” আমার কাছে আলাদা সমস্যা হয়ে যায়। সংগ্রহের কাজ প্রমাণ রাখা; মনিটরিংয়ের কাজ সেই প্রমাণকে সিদ্ধান্তে রূপ দেওয়া।

ব্রাউজারের এমন একটিমাত্র এরর চ্যানেল নেই যার অর্থ সব ব্যর্থতার জন্য একই

ক্লায়েন্টের বিভিন্ন ব্যর্থতা একই অর্থ নিয়ে আসে না।

window-এর error ইভেন্ট সিঙ্ক্রোনাস স্ক্রিপ্ট এররের জন্য ব্যবহৃত হয় এবং রিসোর্স লোড ব্যর্থতার ক্ষেত্রেও ভূমিকা রাখে। হ্যান্ডল না হওয়া Promise rejection অন্য পথে যায়: ব্রাউজার unhandledrejection পাঠায়। স্ক্রিপ্ট, ছবি বা মিডিয়া লোড করা এলিমেন্ট নিজেদের error ইভেন্টও দিতে পারে। React ও Next.js এর ওপর ফ্রেমওয়ার্ক-স্তরের এরর বাউন্ডারি সিগন্যাল যোগ করে।

window.error
→ synchronous script error বাইরে বেরিয়ে থাকতে পারে

unhandledrejection
→ rejected Promise তখন handle হয়নি

element error
→ resource load বা use করা যায়নি

framework boundary
→ rendering বা execution error boundary-তে পৌঁছেছে

গ্লোবাল হুক সিস্টেমের সীমানায় দেখা একটি লক্ষণ দেখে; পুরো কারণ-শৃঙ্খল তার জানা নাও থাকতে পারে।

এটা মেনে নেওয়ার পর আমি সব ইভেন্টকে সঙ্গে সঙ্গে এক সাধারণ Error ও একই গুরুত্বে নামিয়ে আনা বন্ধ করি।

মালিকানা প্রথম কার্যকর ফিল্টার

শোর কমানোর প্রথম কার্যকর ভাগ ছিল—যে কোড বা রিসোর্স ব্যর্থ হয়েছে, সেটি কার?

/_next/static/chunks/app/... লোড ব্যর্থ হওয়া আর অন্য অরিজিনে থাকা বিজ্ঞাপন SDK ব্যর্থ হওয়া এক নয়। আমার URL বিল্ডার ভুল URL বানানো আর ব্লক হওয়া অ্যানালিটিক্স রিকোয়েস্টও এক নয়। ব্রাউজার এক্সটেনশনের ত্রুটি আরেক শ্রেণি।

  • নিজের অ্যাপ: আমার JavaScript, CSS, API, মিডিয়া এবং কোডে তৈরি URL;
  • ফ্রেমওয়ার্ক ও রানটাইম: অ্যাপের এক্সিকিউশন পথে থাকা Next.js বা React;
  • বাইরের ইন্টিগ্রেশন: অ্যানালিটিক্স, বিজ্ঞাপন, উইজেট ও বাইরের SDK;
  • পরিবেশ: ব্রাউজার এক্সটেনশন, ক্রলার, নেটওয়ার্ক অবস্থা, গোপনীয়তা টুল এবং ব্রাউজার-নির্দিষ্ট আচরণ।

এর মানে বাইরের সেবা গুরুত্বহীন নয়। পেমেন্ট বা অথেন্টিকেশন প্রোভাইডার খুব গুরুত্বপূর্ণ হতে পারে, আর বিজ্ঞাপন ব্যর্থ হলে আয়ে প্রভাব পড়তে পারে। কিন্তু ইন্টিগ্রেশন অসুস্থ মানেই অ্যাপ ক্র্যাশ নয়।

সবকিছু একই জরুরি অ্যালার্ট চ্যানেলে পাঠালে সেই চ্যানেলের অর্থই নষ্ট হয়ে যায়।

নিজের অ্যাপের দুটি ব্যর্থতা আমাকে শিখিয়েছে “ব্যবস্থা নেওয়ার মতো” সিগন্যাল কী

ভুলভাবে তৈরি URL-এর ঘটনাটি সহজ ছিল:

https://example.comhttps://example.com/resource

এই ক্ষেত্রে AdBlock, VPN বা ব্রাউজারের নীতি নিয়ে অনুমান করার দরকার ছিল না। URL-টাই ভুল। কোথাও কোড এমন একটি মানের আগে অরিজিন জুড়েছিল, যেটি আগে থেকেই পূর্ণ URL ছিল।

ইভেন্টটি ব্যবস্থা নেওয়ার মতো ছিল কারণ প্রমাণ নির্দিষ্ট, রিসোর্স আমার, আর সমস্যাটি সরাসরি আমার নিয়ন্ত্রণে থাকা কোড-পথে ইঙ্গিত করছিল।

Next.js চাঙ্ক ব্যর্থতার ঘটনা আলাদা ছিল:

Failed to load script:
/_next/static/chunks/9253.647385b4be0958e4.js

এটিও আমার অ্যাপের এবং পেজ ভাঙতে পারে, কিন্তু ইভেন্টটি কারণ প্রমাণ করে না। পুরোনো ক্লায়েন্ট আগের ডিপ্লয়মেন্টের ফাইল চাইতে পারে; রিকোয়েস্ট টাইমআউট হতে পারে; রিভার্স প্রোক্সি বা CDN ব্যর্থ হতে পারে; সংযোগ কেটে যেতে পারে; অথবা ফাইলটি সত্যিই অনুপস্থিত হতে পারে।

সঠিক প্রতিক্রিয়া ছিল “কারণ জেনে গেছি” নয়, “এই শ্রেণির অগ্রাধিকার বেশি, আরও প্রসঙ্গ দরকার।”

কারণ সম্পর্কে নিশ্চিত না হলেও গুরুত্ব অনেক বেশি হতে পারে।

Script error. একটি সূত্র, স্ট্যাক ট্রেস নয়

Error: Script error.
filename: unknown
line: 0
column: 0

দেখতে ভয়ংকর, কিন্তু কাজে লাগার মতো তথ্য প্রায় নেই।

ব্রাউজার ক্রস-অরিজিন স্ক্রিপ্ট এররের তথ্য ইচ্ছাকৃতভাবে সীমিত করে। MDN বলছে, ঠিক CORS কনফিগারেশন না থাকলে window.onerror সীমিত তথ্য পায়; <script>-এর crossorigin আচরণ পূর্ণ এরর তথ্য পাওয়া যাবে কি না, তাতে সরাসরি প্রভাব ফেলে।

তাই অস্পষ্ট Script error. দেখেই আমি “আমার অ্যাপ ক্র্যাশ করেছে” ধরে নিই না। উৎস হতে পারে আমার কোড, তৃতীয় পক্ষের কোড, ইনজেক্ট করা কোড, অথবা এমন ত্রুটি যার বিস্তারিত ব্রাউজার ক্রস-অরিজিন নিয়মের কারণে প্রকাশ করতে পারে না।

আমি ইভেন্টটি রাখি এবং পেজ, বিল্ড, ব্রাউজার ও কাছাকাছি ইভেন্টের সঙ্গে মিলিয়ে দেখি; কিন্তু একটিমাত্র 0:0 জরুরি অ্যালার্টের জন্য যথেষ্ট নয়। একই স্বাক্ষর যদি একটি নির্দিষ্ট রিলিজ বা রুটকে ঘিরে গুচ্ছ হতে শুরু করে, তখন অগ্রাধিকার বদলায়।

অজানা মানে নিরীহ নয়। আবার গুরুতরও নয়।

AbortError একেবারে বাস্তব ত্রুটি হয়েও স্বাভাবিক লাইফসাইকেলের অংশ হতে পারে

ভিডিও প্রিভিউ এই পার্থক্যের সবচেয়ে পরিষ্কার উদাহরণ দিয়েছে:

AbortError:
The play() request was interrupted by a call to pause()

HTMLMediaElement.play() একটি Promise ফেরায়, এবং সেই Promise প্রত্যাখ্যাত হতে পারে। মিডিয়ার লাইফসাইকেল অপারেশন ইচ্ছাকৃতভাবে অপেক্ষমাণ প্লেব্যাক থামাতে পারে; MDN স্পষ্টভাবে লিখেছে যে load(), অপেক্ষমাণ play() Promise-কে AbortError দিয়ে বাতিল করে

প্রিভিউ গ্রিডে ব্যবহারকারীর চোখে কিছু না ভাঙলেও এটি সহজেই ঘটে। আইটেম viewport-এ আসে, কোড play() চালায়; ব্যবহারকারী স্ক্রল করে, আইটেম বেরিয়ে যায়; প্লেব্যাক পুরোপুরি শুরু হওয়ার আগেই কোড মিডিয়া থামায় বা বদলে দেয়।

Promise প্রত্যাখ্যাত হওয়াটা বাস্তব। কিন্তু ব্যবহারকারীর জন্য কোনো ইনসিডেন্ট নাও থাকতে পারে।

সঠিক সমাধান সাধারণত কলটির কাছেই: Promise সেখানেই হ্যান্ডল করা এবং প্রত্যাশিত বাতিলকে আসল প্লেব্যাক ব্যর্থতা থেকে আলাদা করা। unhandledrejection নিরাপত্তাজাল হিসেবে ভালো, কিন্তু স্বাভাবিক মিডিয়া লাইফসাইকেল প্রথমবার বোঝার জায়গা হওয়া উচিত নয়।

বাইরের সেবার ব্যর্থতার জন্য আলাদা স্বাস্থ্য-মডেল দরকার

আমার প্রাথমিক লগে অ্যানালিটিক্স ও বিজ্ঞাপন ডোমেনের অনেক ব্যর্থতা ছিল। কিছু গোপনীয়তাকেন্দ্রিক ব্রাউজার থেকে, কিছু ক্রলার থেকে। বিশেষ করে মূল্যহীন জরুরি অ্যালার্ট ছিল—একটি ক্রলার Google Analytics লোড করতে পারেনি।

এতে শুধু জানা যায় একটি নেটওয়ার্ক রিকোয়েস্ট ব্যর্থ হয়েছে। একজন মানুষ অ্যাপ ব্যবহার করতে পেরেছেন কি না, সে সম্পর্কে প্রায় কিছুই জানা যায় না।

ইভেন্টটি সংরক্ষণ করা ভুল ছিল না। ভুল ছিল সেটিকে আমার অ্যাপের লোড না হওয়া JavaScript চাঙ্কের সঙ্গে একই ইনসিডেন্ট ধারায় রাখা।

  • অ্যাপটি কি ব্যবহারকারীর জন্য সত্যিই ভেঙে গেছে?
  • বাইরের ইন্টিগ্রেশন কি সুস্থ আছে?

ব্লক হওয়া বিজ্ঞাপন স্ক্রিপ্ট বিজ্ঞাপন ডেলিভারির মেট্রিকে যেতে পারে। অ্যানালিটিক্স ব্যর্থতা অ্যানালিটিক্স কভারেজের মেট্রিক হতে পারে। মূল ফিচার সত্যিই এর ওপর নির্ভর না করলে কোনোটিকেই “ফ্রন্টএন্ড ক্র্যাশ” বলে জরুরি অ্যালার্ট দেওয়া উচিত নয়।

আলাদা রাখলে বাইরের সমস্যাগুলো আরও পরিষ্কার দেখা যায়: প্রোভাইডার, ব্রাউজার ও অঞ্চল অনুযায়ী গুচ্ছ করা যায়, এলোমেলো লাল শোর হিসেবে নয়।

navigator.onLine প্রসঙ্গ দেয়, সংযোগের প্রমাণ নয়

আমি ব্রাউজার নিজেকে অনলাইন মনে করছে কি না সেটিও লগ করা শুরু করি। তথ্যটি কাজে লাগে, কিন্তু কেবল একটি ইঙ্গিত হিসেবে।

কিছু ইভেন্টকে অতিরিক্ত লজিক নেটওয়ার্ক ব্যর্থতা বলেছিল, অথচ একই লগে লেখা ছিল:

Online: true

এটি কোনো বিরোধ নয়। MDN স্পষ্টভাবে সতর্ক করে যে navigator.onLine ব্রাউজার ও অপারেটিং সিস্টেমের অনুমানের ওপর নির্ভর করে। মেশিন লোকাল নেটওয়ার্কে যুক্ত থেকেও আমার অরিজিনে পৌঁছাতে নাও পারে। VPN, ফায়ারওয়াল, DNS এবং আংশিক নেটওয়ার্ক বিভ্রাট ছবিটাকে আরও জটিল করে।

online === false
→ environment problem-এর শক্ত signal

online === true
→ origin বা resource reachable ছিল তার proof নয়

এই ছোট পার্থক্য মনিটরিং সিস্টেমকে কোনো ইঙ্গিতকে আত্মবিশ্বাসী কিন্তু ভুল রোগনির্ণয়ে বদলে ফেলতে দেয় না।

একটি মূল ব্যর্থতা একাধিক ব্রাউজার ইভেন্ট তৈরি করতে পারে

সংগ্রহ উন্নত করার পর আরেক ধরনের শোর স্পষ্ট হলো: একটি ইনসিডেন্ট একাধিক বার্তা তৈরি করতে পারে।

একটি JavaScript চাঙ্ক প্রথমে resource.error দিতে পারে, এরপর মডিউল লোডার ChunkLoadError ছুড়তে পারে, React বা Next.js এরর বাউন্ডারি পার করতে পারে, তারপর রিকভারি লজিক রিলোড নির্ধারণ করতে পারে। প্রতিটি স্তর আলাদা অ্যালার্ট পাঠালে একটি ব্যবহারকারী কাজকে একাধিক আলাদা প্রোডাকশন ব্যর্থতা মনে হয়।

পাঁচটি বার্তা মানসিকভাবে পাঁচজন ক্ষতিগ্রস্ত ব্যবহারকারীর মতো লাগে, অথচ সবগুলো একটি সেশন ও একটি রিসোর্স থেকে আসতে পারে।

শুধু বার্তার টেক্সট মিলিয়ে ডুপ্লিকেট বাদ দেওয়া যথেষ্ট নয়। ইনসিডেন্ট-স্তরের সহসম্পর্ক দরকার:

session
+ ছোট time window
+ normalized error class
+ first-party resource
+ client build
+ route

কাঁচা ইভেন্টগুলো আমি রাখতে চাই, কিন্তু মানুষের কাছে সবচেয়ে বেশি তথ্যবহুল ইনসিডেন্টটি পৌঁছানো উচিত। যদি এরর বাউন্ডারিতে ইতিমধ্যে আমার কোডের স্ট্যাক ও চাঙ্কের সঠিক URL থাকে, তার আগে আসা সাধারণ রিসোর্স এররকে দ্বিতীয় জরুরি অ্যালার্ট বানাতে হবে না।

অ্যালার্ট দিন ইনসিডেন্টে। ইভেন্ট সংরক্ষণ করুন।

এররের চারপাশের প্রসঙ্গ পরে এরর স্ট্রিংয়ের চেয়ে বেশি মূল্যবান হলো

পরে টেলিমেট্রি অনেক বেশি কাঠামোবদ্ধ হয়:

clientBuildId
resource URL
resourceResponseStatus
resourceTransferSize
resourceDurationMs
serviceWorkerVersion
serviceWorkerController
serviceWorkerState
chunkRecoveryScheduled
online
stack / component stack

মিডিয়ার জন্য আমি মিডিয়া এরর কোড, আলাদাভাবে দেখা গেলে HTTP স্ট্যাটাস, আসল Content-Type এবং ব্যর্থতাটি HTTP নাকি নেটওয়ার্ক ডেলিভারির, সেটিও রাখতাম।

এই ফিল্ডগুলো এমন প্রশ্ন করতে দিল যেগুলোর উত্তর একটি সাধারণ এক্সেপশন স্ট্রিং দিতে পারে না: একটি নির্দিষ্ট বিল্ডে কি ব্যর্থতা শুরু হয়েছে? ব্রাউজার কি HTTP রেসপন্স পেয়েছে? Service Worker কি পেজ নিয়ন্ত্রণ করছিল? রিকভারি লজিক কি চলেছে? একাধিক ইভেন্ট কি একই রিসোর্সকে দেখাচ্ছে? বর্তমান রুট কি সত্যিই ক্ষতিগ্রস্ত?

Resource Timing API রিসোর্সের সময়কাল, ট্রান্সফার তথ্য এবং যেখানে সমর্থিত ও অনুমোদিত সেখানে রেসপন্স স্ট্যাটাস দিতে পারে। এর সীমা আছে: ক্রস-অরিজিন টাইমিং সীমিত, ক্যাশ করা রিসোর্সে transferSize: 0 হতে পারে, আর responseStatus সব জায়গায় নেই। তাই null0 অর্থপূর্ণ অবস্থাই থাকা উচিত, কৃত্রিম নিশ্চিততা নয়।

ChunkLoadError একটি লক্ষণ, 404 ডিটেক্টর নয়

পরে একটি ইভেন্ট চাঙ্ক ব্যর্থতা বোঝার আমার ধরন বদলে দেয়।

ব্রাউজার একটি Next.js layout চাঙ্কের জন্য ChunkLoadError জানায়, আর উন্নত টেলিমেট্রিতে একই সঙ্গে ছিল:

resourceResponseStatus: 200
resourceDurationMs: 170523
serviceWorkerState: activated
chunkRecoveryScheduled: true

রেকর্ড করা সময় প্রায় 170 সেকেন্ড। সেই ইনসিডেন্টের সঠিক মূল কারণ যা-ই হোক, এই তথ্য অতিরিক্ত সরল একটি নিয়ম বাতিল করার জন্য যথেষ্ট ছিল:

ChunkLoadError === server 404 ফিরিয়েছে

অন্য চাঙ্ক ব্যর্থতায় কোনো দৃশ্যমান রেসপন্স স্ট্যাটাস ছিল না। কিছু টাইমআউট হিসেবে এসেছিল, কিছুতে ট্রান্সফার তথ্য ছিল। এরর শ্রেণি একই; চারপাশের প্রমাণ আলাদা।

Next.js-এ এটি বিশেষ গুরুত্বপূর্ণ, কারণ /_next/static/ এর নিচের ফাইলে সাধারণত কনটেন্ট হ্যাশ থাকে এবং সেগুলো immutable ক্যাশিংয়ের জন্য তৈরি। বর্তমান Next.js self-hosting ডকুমেন্টেশন হ্যাশ করা immutable অ্যাসেটের জন্য দীর্ঘমেয়াদি ক্যাশ হেডার বর্ণনা করে। তাই ChunkLoadError-এর পেছনে ডিপ্লয়মেন্ট অমিল, পুরোনো ক্লায়েন্ট, নেটওয়ার্ক ডেলিভারি, রিভার্স প্রোক্সি, CDN, ক্যাশ, Service Worker বা সত্যিকারের অনুপস্থিত বিল্ড আর্টিফ্যাক্ট থাকতে পারে।

আমি চাই না অ্যালার্টিং স্তর কারণ বানিয়ে নিক। আমি চাই তদন্তের জন্য দরকারি প্রমাণটি রেখে দিক।

মিডিয়া এরর অন্য দিক থেকে একই শিক্ষা দিয়েছে

কিছু ইভেন্টে মিডিয়া এলিমেন্ট জানিয়েছিল:

MEDIA_ELEMENT_ERROR: Format error

শুধু এই বার্তা দেখলে কোডেক অসামঞ্জস্য মনে হয়।

কিন্তু এমন কিছু ইভেন্টে অতিরিক্ত ডেলিভারি যাচাই দেখিয়েছিল:

HTTP status: 410
Content-Type: text/html; charset=utf-8
failure kind: http

ব্রাউজার ভিডিও চেয়েছিল, কিন্তু HTML-সহ একটি HTTP এরর রেসপন্স পেয়েছিল। মিডিয়া এলিমেন্ট HTML-কে ভিডিও হিসেবে ডিকোড করতে পারেনি, তাই বাইরের লক্ষণ “Format error” হয়েছে। কাজে লাগার মতো রোগনির্ণয় ছিল ডেলিভারি স্তরে।

যে স্তর প্রথম সমস্যা দেখেছে, সমস্যা সৃষ্টির স্তর সেটিই হবে এমন নয়।

“কোডেক ব্যর্থতা”, “নেটওয়ার্ক বিভ্রাট”, “ক্যাশ বাগ” এবং “হারানো চাঙ্ক”—সবই সিদ্ধান্ত। টেলিমেট্রির প্রথম কাজ পর্যবেক্ষণ লেখা।

আমি ব্রাউজার ব্যর্থতাকে পাঁচটি মাত্রায় মূল্যায়ন করি

1. মালিকানা

উৎস কি নিজের অ্যাপ, ফ্রেমওয়ার্ক/রানটাইম, বাইরের ইন্টিগ্রেশন, নাকি পরিবেশ?

2. ব্যবহারকারীর ওপর প্রভাব

সক্রিয় রুট, রেন্ডারিং, অথেন্টিকেশন, চ্যাট, চেকআউট বা অন্য কোনো মূল প্রবাহ কি ভেঙেছে? নাকি শুধু ঐচ্ছিক বিজ্ঞাপন, অ্যানালিটিক্স, প্রিলোড বা প্রিভিউ ব্যর্থ হয়েছে, আর পেজ এখনও ব্যবহারযোগ্য?

3. প্রমাণের মান

আমার কাছে কি নিজের কোডের স্ট্যাক, রিসোর্স URL, HTTP স্ট্যাটাস, বিল্ড ID এবং কম্পোনেন্ট স্ট্যাক আছে? নাকি শুধু 0:0-এ Script error.?

4. পুনরাবৃত্তি ও বিস্তার

এটি কি একটি সেশনের একটি ইভেন্ট, নাকি একই রিলিজের পর আলাদা ব্যবহারকারী, রুট ও ব্রাউজারে একই স্বাক্ষর দেখা যাচ্ছে?

5. রিকভারি

অ্যাপ কি নিজে রিকভার করেছে? চাঙ্ক রিলোড কি নির্ধারিত হয়েছে? ফলব্যাক কাজ করেছে? ব্যবহারকারী এখনও আটকে?

clear first-party
+ high user impact
+ strong evidence
+ multiple sessions
+ no recovery
= urgent incident

third-party
+ optional feature
+ weak evidence
+ isolated
+ user unaffected
= metric বা low priority

কোনো ইভেন্ট বাদ দেওয়ার নীতি সতর্ক হওয়া উচিত

শোর অসহ্য হলে অনেক রেগুলার এক্সপ্রেশন লিখে বিরক্তিকর সবকিছু বাদ দিতে ইচ্ছে করে। এটি বিপজ্জনক।

সব AbortError চেপে দিলে সত্যিকারের বাতিল হওয়া API রিকোয়েস্ট লুকিয়ে যেতে পারে। সব Script error. ফেলে দিলে ব্রাউজার-নির্দিষ্ট এমন গুচ্ছ হারাতে পারি যা সমষ্টির পর গুরুত্বপূর্ণ হয়। সব বাইরের ব্যর্থতা উপেক্ষা করলে ভাঙা পেমেন্ট, অথেন্টিকেশন বা সম্মতি-প্রোভাইডার বাদ যেতে পারে।

ALERT
→ strong incident, action দরকার

RETAIN / AGGREGATE
→ রাখো ও count করো; cluster হলে alert

METRIC / SAMPLE
→ expected বা low-impact noise; trend ও example রাখো

সিস্টেম শান্ত হতে পারে, অন্ধ নয়।

ভালো ক্লাসিফায়ার আসলে কোডে লেখা নীতি

নিচের কোড আমার প্রোডাকশন প্রকল্প থেকে কপি করা নয়। এটি সেই নীতির ছোট উদাহরণ, যেটি দিয়ে শুরু করতে পারলে ভালো হতো:

function classifyClientEvent(event) {
  const owner = classifyOwner(event);

  if (isExpectedMediaCancellation(event)) {
    return { severity: "metric", reason: "expected-cancellation" };
  }

  if (owner === "first-party" && breaksActiveRoute(event)) {
    return { severity: "alert", reason: "first-party-user-impact" };
  }

  if (isActiveFirstPartyChunkFailure(event)) {
    return { severity: "alert", reason: "application-chunk" };
  }

  if (owner === "third-party") {
    return { severity: "aggregate", reason: "integration-health" };
  }

  if (isOpaqueScriptError(event)) {
    return { severity: "aggregate", reason: "insufficient-evidence" };
  }

  return { severity: "aggregate", reason: "needs-correlation" };
}

আসল কঠিন কাজ breaksActiveRoute()-এর মতো ফাংশনের ভেতরে। শুধু এক্সেপশনের নাম যথেষ্ট নয়; রুটের প্রসঙ্গ, রিসোর্সের মালিকানা, এরর বাউন্ডারি ডেটা এবং কখনও পণ্য-নির্দিষ্ট জ্ঞান দরকার।

ফিঙ্গারপ্রিন্টকে ইনসিডেন্ট অনুসরণ করতে হবে, বার্তার টেক্সট নয়

পুরো বার্তা সমান কি না দেখে ডুপ্লিকেট বাদ দেওয়া দুর্বল কৌশল। মিনিফাই করা স্ট্যাক অফসেট বিল্ডে বদলায়, চাঙ্ক হ্যাশ বদলায়, URL-এ ডায়নামিক আইডেন্টিফায়ার থাকে এবং ব্রাউজারের শব্দচয়ন আলাদা।

{
  errorClass,
  normalizedFirstPartyResource,
  routeFamily,
  clientBuild,
  sessionId,
  shortTimeBucket
}

পুরো অ্যাপ ক্র্যাশে নিজের কোডের সর্বোচ্চ স্ট্যাক ফ্রেম কাজে লাগতে পারে। চাঙ্ক ব্যর্থতায় স্বাভাবিকীকৃত চাঙ্ক রিসোর্স বেশি দরকারি। মিডিয়া ডেলিভারি ইনসিডেন্টে HTTP ব্যর্থতার শ্রেণি ও মিডিয়া রুট ব্রাউজারের বাইরের এরর বার্তার চেয়ে বেশি মূল্যবান হতে পারে।

resource.error
→ ChunkLoadError
→ framework boundary
→ recovery scheduled

তখন সম্পর্কিত ইভেন্টের একটি ধারা চারটি পর্যবেক্ষণসহ এক ইনসিডেন্ট হতে পারে, চারটি আলাদা জরুরি ব্যর্থতা নয়।

জরুরি অ্যালার্ট চ্যানেলের কাজ অনেক বেশি সংকীর্ণ হওয়া উচিত

আমি তাৎক্ষণিক অ্যালার্ট রাখব এমন ক্ষেত্রে: নিজের অ্যাপের রানটাইম এরর যার উপকারী স্ট্যাক সক্রিয় রুট ভাঙে; React/Next.js এরর বাউন্ডারি যা বাস্তব ইন্টারঅ্যাকশন ক্ষতিগ্রস্ত করে; বর্তমান রুটের জন্য দরকারি নিজের JavaScript বা CSS লোড না হওয়া; একাধিক সেশন বা বিল্ডে পুনরাবৃত্ত ChunkLoadError; মূল API বা ডেটা ব্যর্থতা যাতে ব্যবহারকারী আটকে যায়; বা নিজের কোডের স্পষ্ট নিয়ম ভাঙা, যেমন বিকৃত URL তৈরি হওয়া।

একটি একাকী অস্পষ্ট Script error., সফল রিকভারি থাকা নিজের রিসোর্স ব্যর্থতা, মূল স্তর এখনও অজানা মিডিয়া এরর, বা গুচ্ছ করার আগে বোঝা যায় না এমন ব্রাউজার-নির্দিষ্ট অস্বাভাবিকতা—এসব রাখব, কিন্তু সঙ্গে সঙ্গে অ্যালার্ট করব না।

সাধারণত মেট্রিক বা নমুনা ডায়াগনস্টিকে পাঠাব: পরিচিত বিজ্ঞাপন/অ্যানালিটিক্স রিসোর্স ব্যর্থতা; প্রত্যাশিত মিডিয়া AbortError; শুধু ক্রলারে দেখা বাইরের ব্যর্থতা; শক্তিশালী অফলাইন ইঙ্গিতসহ ব্যর্থতা; এবং সক্রিয় রুটে প্রভাব না ফেলা ঐচ্ছিক অনুমানভিত্তিক রিসোর্স।

জরুরি অ্যালার্টের অর্থ হওয়া উচিত ব্যবহারকারীর ওপর ব্যবস্থা নেওয়ার মতো প্রভাব, ব্রাউজারের অভিযোগের কাঁচা সংখ্যা নয়।

একটি মোট “এরর কাউন্ট”-এর বদলে আমি ইনসিডেন্ট মাপতে চাই

  • প্রতি 1,000 সেশনে নিজের অ্যাপের ইনসিডেন্ট;
  • বিল্ড ID অনুযায়ী প্রভাবিত সেশন;
  • রুট অনুযায়ী এরর-বাউন্ডারি ইনসিডেন্ট;
  • রিসোর্স ও ডিপ্লয়মেন্ট অনুযায়ী চাঙ্ক ব্যর্থতা;
  • প্রোভাইডার অনুযায়ী বাইরের ইন্টিগ্রেশনের ব্যর্থতার হার;
  • প্রত্যাশিত বাতিলের পরিমাণ, যাতে হঠাৎ বৃদ্ধি তবু দেখা যায়;
  • রিকভারির সাফল্যের হার;
  • অনন্য ইনসিডেন্টের সংখ্যা এবং কাঁচা ইভেন্টের সংখ্যা আলাদা।

“একটি ইভেন্ট ঘটেছে” খুব কমই ভালো প্রোডাকশন অ্যালার্ট সীমা। “নতুন বিল্ডে নিজের অ্যাপের একই ইনসিডেন্ট এখন একাধিক স্বাধীন সেশনকে প্রভাবিত করছে এবং রিকভারিও ব্যর্থ”—এটি অনেক বেশি কার্যকর সংকেত।

ক্লায়েন্ট মনিটরিং একা মূল কারণ প্রমাণ করতে পারে না

ব্রাউজার টেলিমেট্রির স্পষ্ট সীমা আছে।

HTTP স্ট্যাটাস না পাওয়ার অর্থ হতে পারে API তথ্য দেয়নি, ব্রাউজার ফিল্ডটি সমর্থন করে না, ক্রস-অরিজিন বাধা আছে, রিকোয়েস্ট বাতিল হয়েছে, অথবা অবজারভেবিলিটিতে অন্য ফাঁক আছে। Service Worker সক্রিয়—এতে প্রমাণ হয় না যে পুরোনো রিসোর্স সেটিই দিয়েছে। ডিপ্লয়মেন্টের পর ChunkLoadError এসেছে—এতে সংস্করণ অমিল প্রমাণ হয় না। online: true থেকেও অরিজিন পৌঁছানো যাচ্ছিল প্রমাণ হয় না।

ক্লায়েন্ট মনিটরিং সম্ভাব্য কারণের পরিধি ছোট করে। আসল কারণ প্রমাণে সার্ভার লগ, রিভার্স-প্রোক্সি লগ, ডিপ্লয়মেন্ট ম্যানিফেস্ট, ক্যাশ অবস্থা এবং বাস্তব পুনরুত্পাদন এখনও দরকার হতে পারে।

অবজারভেবিলিটিকে সীমাহীন ব্যবহারকারী ডেটা সংগ্রহেও পরিণত করতে চাই না। প্রতিটি ফিল্ডের থাকা উচিত কারণ সেটি ব্যর্থতার ধরন আলাদা করতে সত্যিই সাহায্য করে।

ভালো টেলিমেট্রি মানে বেশি টেলিমেট্রি নয়; বেশি পার্থক্য করতে পারে এমন টেলিমেট্রি।

আমার নিয়ম এখন: ইভেন্ট সংগ্রহ করো, ইনসিডেন্ট তদন্ত করো, প্রভাবের ওপর অ্যালার্ট দাও

শুরুতে আমি চেয়েছিলাম রিপোর্টার শুধু একটি প্রশ্নের উত্তর দিক: “কিছু ভেঙেছে?” প্রোডাকশনে প্রশ্নটি এত বিস্তৃত যে কাজে আসে না। কোথাও না কোথাও ক্রলার অ্যানালিটিক্সে পৌঁছায় না, গোপনীয়তা টুল বিজ্ঞাপন ব্লক করে, মিডিয়া Promise ইচ্ছাকৃতভাবে বাতিল হয়, ব্যবহারকারী নেটওয়ার্ক হারান, বা বাইরের SDK অস্বাভাবিক আচরণ করে।

এটা কি আমাদের?
User কি functionality হারিয়েছে?
Evidence কতটা শক্ত?
Repeat হচ্ছে?
Application recover করেছে?
এগুলো অনেক event, নাকি একটি incident?

এই সংকীর্ণ প্রশ্নগুলোকে কেন্দ্র করে মনিটরিং সাজানোর পর লাল বার্তার স্রোত শোর থেকে প্রকৌশল টুলে বদলে গেল।

ব্রাউজার এরর একটি পর্যবেক্ষণ। ইনসিডেন্ট হলো ব্যবহারকারীর ওপর প্রভাবের সঙ্গে সম্পর্কিত ব্যাখ্যা। অ্যালার্ট হলো সিদ্ধান্ত—এখন একজন মানুষের ব্যবস্থা নেওয়া উচিত।

এই তিনটি জিনিসকে আর এক মনে করতে চাই না।