خطاهای JavaScript؛ چرا اطلاعات خطا در JSON و log ناپدید می‌شود؟

چرا مهم است

اگر برای بررسی خرابی به log تکیه می‌کنی، خروجی خالی ممکن است گمراهت کند؛ اطلاعات خطا هنوز وجود دارد، اما آن را ثبت نمی‌کند. حتی هم الزاماً محل پرتاب خطا را نشان نمی‌دهد.

قبل از خواندن، این‌ها را بدان۴ مفهوم
Serialization

تبدیل داده به قالبی قابل ذخیره یا انتقال، مانند JSON؛ اطلاعات باقی‌مانده به قواعد این تبدیل بستگی دارد.

بیشتر: Serialization

Stack trace

فهرستی از مسیر فراخوانی تابع‌ها که برای بررسی خطا استفاده می‌شود؛ قالب و جزئیات آن به محیط اجرا بستگی دارد.

در حال نگارش: Stack trace

Cause chain

زنجیره ارتباط میان یک خطا و خطاهای قبلی که به‌عنوان علت آن نگه داشته شده‌اند؛ این ارتباط می‌تواند حلقه هم داشته باشد.

در حال نگارش: Cause chain

Enumerable property

ویژگی یک object که در پیمایش‌هایی مانند Object.keys ظاهر می‌شود. ویژگی‌های فاقد این مشخصه ممکن است وجود داشته باشند، اما در چنین پیمایش‌هایی دیده نشوند.

در حال نگارش: Enumerable property

ممکن است یک Error واقعی، همراه با message و cause،؜ در log فقط به شکل {} دیده شود. مقاله «Your JS Error Is Lying to You» توضیح می‌دهد که این خروجی لزوماً نشانه خالی بودن خطا نیست؛ روش خواندن و ثبت اطلاعات تعیین می‌کند چه چیزی باقی بماند.

دلیل اصلی در JSON.stringify این است که message و cause از نوع Enumerable property نیستند. بنابراین Object.keys،؜ عملگر spread و Object.assign هم آن‌ها را نمی‌بینند. در مقابل، Object.getOwnPropertyNames می‌تواند این نام‌ها را نشان دهد. در Node.js نیز console.log اطلاعات بیشتری نمایش می‌دهد، چون مشخصاً message،؜ stack و cause را می‌خواند.

خود مقدار دریافتی در catch هم الزاماً Error نیست. JavaScript اجازه می‌دهد هر مقداری، از رشته تا null یا یک object معمولی، پرتاب شود. دسترسی مستقیم به message روی null می‌تواند داخل همان بخش مدیریت خطا، TypeError تازه‌ای ایجاد کند. مقاله این موضوع را به رفتار TypeScript در حالت strict پیوند می‌دهد: با گزینه useUnknownInCatchVariables،؜ مقدار catch از نوع unknown است و پیش از استفاده باید بررسی شود.

از سوی دیگر، instanceof Error همیشه کافی نیست. نمونه مقاله نشان می‌دهد Error ساخته‌شده در محیط جداگانه node:vm از این بررسی عبور نمی‌کند؛ Error.isError در Node.js 24.2.0 همان خطا را تشخیص می‌دهد.

Stack trace نیز بخشی از تعریف استاندارد زبان نیست. در V8 محل ساخته‌شدن خطا را ثبت می‌کند، نه محل پرتاب آن؛ عمق پیش‌فرض آن ۱۰ frame است و کد می‌تواند آن را بازنویسی کند. هنگام ساختن خطای تازه از روی خطای قبلی، ندادن cause ارتباط با شکست اولیه را از بین می‌برد. حتی با حفظ Cause chain،؜ خواندن همه علت‌ها نیازمند پیمایش است و این ارتباط ممکن است حلقه داشته باشد.

مقاله تأکید می‌کند که Serialization،؜ انتقال میان محیط‌های اجرا و تنظیمات Pino یا Winston،؜ هرکدام می‌توانند بخش متفاوتی از اطلاعات را نگه دارند. پیشنهاد آن حفظ cause،؜ تصمیم‌گیری درباره تلاش دوباره براساس کد خطا، و ساخت رکورد log با تابعی تحت کنترل برنامه است؛ تابعی که ارتباط خطاها را دنبال کند، فقط فیلدهای مشخص را ثبت کند و محدودیت عمق داشته باشد. برای پاسخ HTTP نیز اطلاعات مختصر مطابق الگویی مانند RFC 9457 پیشنهاد می‌شود، نه جزئیات داخلی خطا.

نکته کلیدی

هنگام ساختن خطای تازه، خطای قبلی را با cause نگه دار. برای ثبت اطلاعات هم به تبدیل خودکار اکتفا نکن؛ فیلدها را صریح انتخاب کن و دنبال‌کردن علت‌های خطا را به عمق مشخصی محدود کن.

خواندن مقاله اصلی (در زبانه‌ی جدید باز می‌شود)
برچسب‌ها (۴)