محمدحسین عباداللهی
محمدحسین عباداللهی
خواندن ۶ دقیقه·۷ سال پیش

روزی که دیگر console.log کافی نیست.

معمولا برای یادگیریِ لاگ گرفتن یا همان Logging زمان زیادی صرف نمی‌کنیم. چون یک console.log نوشتن، آن‌قدرها هم سخت نیست. شاید اگر بخواهید جاوا اسکریپت یاد بگیرید، اولین چیزی که از این زبان عجیب ببینید، یک لاگ ساده باشد:

console.log('Hello World!');

اما حقیقت این است که وقتی اپلیکیشن‌تان بزرگ و بزرگ‌تر می‌شود، این console.log ساده و جذاب تبدیل به ابزاری ناکارآمد می‌شود که لازم می‌شود فکری به حالش کنیم.

از بین بردن باگ‌ها با console.log

فرض کنید تابعی نوشتید که درست کار نمی‌کند یا همان به قول اهل فن، باگ دارد. شاید اولین قدمی که برای حل مشکل یا همان دیباگ کردن این تابع برداریم، لاگ گرفتن از تک‌تک ورودی‌ها و خروجی این تابع است. شاید آن وسط‌ها هم لاگ‌های دیگری قرار دهیم تا بهتر متوجه شویم که برنامه‌ی‌مان چگونه پیش‌روی می‌کند و یا باز به قول اهل فن، flow برنامه را مشاهده کنیم.

خب، لاگ‌ها را نوشتیم. تا به این‌جا همه چیز خوب است، باگ را هم یافتیم و همه چیز به خیر و خوشی تمام شد. حالا با این همه عبارت console.log چه کنیم؟ اضافه و دست و پا گیر هستند. پاک‌شان می‌کنیم و می‌رویم سراغ قابلیت یا حتی باگ بعدی. برای حل این باگ، نیاز است که دوباره با flow برنامه آشنا شویم و ببینیم که بدنه‌ی if اجرا می‌شود یا else؟ آن تابعی که پیش از این دیباگش کردیم، چه خروجی می‌دهد؟ ۵ یا ۱۵؟ در این حالت، مجبوریم دوباره چند console.log بنویسیم. شاید همان‌هایی که چند لحظه‌ی پیش پاک‌شان کردیم.

حالا فرض کنید اپلیکیشن را نوشتیم و قرار است روی سرور مستقر کنیم تا کاربرهای‌مان از این اَپ فوق‌العاده استفاده کنند. چگونه مطمئن می‌شویم همه چیز درست کار می‌کند؟ همه‌ی سرویس‌ها اجرا شده‌اند و برنامه‌ی ما هم به سرویس‌های مورد نظر با موفقیت وصل شده است؟ از کجا متوجه می‌شویم که چه زمانی خطایی رخ داد؟ در کجا؟ وقتی خطا رخ داد، تابع‌مان چه چیزی را ورودی گرفته بود؟


اگر به این پاراگراف رسیدید، فرض می‌کنم Logging برای‌تان مهم است و نمی‌خواهید با صورت سرخ شده‌ی مدیرتان، وقتی که در حال پرس و جو از مشتری هستید که دقیقا چه چیزا را وارد کرد که به خطا خورد، مواجه شدید، در ادامه با من همراه باشید.

یکی از دلایلی که لاگ‌های‌مان را پاک می‌کنیم، برای این است که خروجی واضح‌تری داشته باشیم. وقتی تعداد لاگ‌ها زیاد باشد و هر لاگ هم اطلاعات زیادی را در صفحه، نمایش دهد، عملا نمی‌توانیم لاگ مورد نظرمان را به خوبی ببینیم و این همه لاگ، اذیت کننده خواهند بود.

استفاده از console.log راه حل خوبی برای لاگ گرفتن نیست. چون تنظیماتی ندارد که بخواهید در شرایط مختلف لاگ را کنترل کنید. یا شاید لازم باشد که لاگ‌ها را به سرویس‌هایی که آن‌ها را ذخیره و دسته‌بندی می‌کنند ارسال کنید. console.log هیچ‌کدام از این قابلیت‌ها را ندارد.

وینستون (Winston)، یک ابزار حرفه‌ای برای لاگ گرفتن

با افتخار، معرفی می‌کنم: پکیج Winston، ابزاری حرفه‌ای با قابلیت‌هایی که تمام ضعف‌های console.log را می‌پوشاند. Winston معروف‌ترین پکیج Logging برای Node.js است که به برخی از قابلیت‌هایش اشاره می‌کنم:

  • قابلیت انتقال و یا همان Transport کردن لاگ‌ها به سرویس‌های مختلف
  • قابلیت ایجاد انتقال‌دهنده‌های دلخواه
  • استریم کردن لاگ‌ها
  • قابلیت گشتن و کوئری‌زدن روی لاگ‌ها برای یافتن لاگ‌های خاص
  • لاگ گرفتن در سطوح مختلف، مانند error، warning و یا info
  • قابلیت ایجاد سطوح دلخواه

نصب و راه‌اندازی Winston

وینستون با دستور زیر نصب می‌شود:

npm install winston

حالا فقط کافیست هر جایی که لازم دارید، آن را وارد کنید و لاگ بگیرید:

const winston = require('winston'); winston.info('Hello World!');

و شاید هم لازم داشته باشید وینستون را تنظیم کنید که لاگ‌ها را به فایل خاصی انتقال دهد:

winston.configure({ transports: [ new (winston.transports.File)({ filename: 'myLogs.log' }), ] });

همان‌طور که می‌بینید، transports در واقع یک آرایه است، یعنی می‌توانید هم‌زمان به مقاصد مختلف لاگ‌ها را هدایت کنید. شاید بخواهید هم‌زمان علاوه بر این‌که لاگ‌ها را داخل فایلی ذخیره می‌کنید، آن‌ها را به سرویس‌های Sentry.io و یا Loggly.com هم هدایت کنید.

سطوح مختلف لاگ در Winston

حتما می‌پرسید که وینستون چطور می‌خواهد لاگ‌ها را مدیریت کند؟ وقتی قرار است هیچ لاگی را پاک نکنیم، چون بعدها به درد خواهند خورد.

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

  • سطح Fatal: اپلیکیشن در موقعیت بحرانی قرار گرفته است و قادر به انجام عملیات کنونی نیست. معمولا در چنین شرایطی، برنامه این خطا را در سطح Fatal، لاگ می‌کند و خود را خاموش می‌کند.
  • سطح Error: یک خطای مهم، زمانی که اپلیکیشن در حال انجام عملیاتی بوده، رخ داده است. معمولا بعد از گزارش این خطا، لازم است که کاربر با برنامه بیشتر کار کند تا بتواند مشکل را حل کند. استثناها یا همان Exceptions، معمولا از این دسته هستند، چون تقریبا معنای مشابهی دارند. مثلا وقتی در نظر نگرفته بودیم که ممکن است کاربر در username اش از کاراکترهای غیر مجاز استفاده کند و دیتابیس از این بابت خطایی تولید کند. اگر نمی‌دانید بعد از گذاشتن try/catch، با خطایی که داخل catch دارید باید چه کنید، خیلی راحت می‌توانید در این سطح آن را لاگ کنید و رد شوید.
  • سطح Warning: این نوع لاگ‌ها برای زمانی هستند که خطای رخ داده، چندان تاثیری روی عملیات کنونی نمی‌گذارند و اپلیکیشن در حالت معمول خودش است. معمولا این نوع لاگ‌ها را زمانی بررسی می‌کنیم که برنامه در حالت production مستقر است.
  • سطح Info: این نوع از لاگ‌ها معمولا برای گزارش دادن پیش‌روی اپلیکیشن و وضعیت‌های مختلفی که در آن قرار می‌گیرد استفاده می‌شود. مثلا ممکن است لاگ بگیریم که برنامه با موفقیت به دیتابیس وصل شد، یا مثلا فلان عملیات آغاز شد و یا به پایان رسید. و یا حتی ممکن است لاگ بگیریم که فلان کار، ۱۰ ثانیه طول کشید تا انجام شود.
  • سطح Debug: این نوع لاگ‌ها، برای خطایابی برنامه استفاده می‌شوند که معمولا با توضیحات بیشتری همراه هستند. مثلا ممکن است صدا کردن یک تابع مهم، مقادیر ورودی آن تابع و حتی خروجی‌اش را در این سطح لاگ بگیرید. حتی مقادیر یک‌سری متغیر خاص و مهم را که برای دیباگ کردن لازم می‌شوند را در این سطح لاگ بگیرید.
  • سطح Trace: در این سطح لاگ‌هایی با اطلاعات بسیار زیاد (یا حتی به‌صورت افراطی) را گزارش می‌دهند. لاگ‌هایی در این سطح، قدم به قدم وضعیت برنامه را شرح می‌دهند، حتی کوچک‌ترین اتفاقات، همراه با جزئیات.
توجه داشته باشید که وینستون، به‌جای warning از verbose و به جای trace از عبارت silly استفاده می‌کند. هر چند که می‌توانید حتی سطوح خودتان را هم تعریف کنید.

با استفاده از وینستون، در شرایط مختلف می‌توانید، یک لاگ در سطح مورد نظرتان ایجاد کنید:

winston.error('An error occurred'); winston.info('Connected to database.');

و حالا خیلی راحت، می‌توانید از وینستون بخواهید که لاگ‌های کدام سطح را نمایش دهد:

winston.level = 'warning';

وقتی یک سطح یا level را تعیین می‌کنید، وینستون سعی می‌کند تمام لاگ‌های آن سطح و بالاتر از آن را نمایش دهد. مثلا در نمونه‌ی بالا، چون سطح را روی warning تنظیم کردیم، تمام لاگ‌های مرتبط با warning و error و fatal نمایش داده خواهند شد.

با استفاده از این پکیج و تنظیم کردن آن، کارهای مختلفی می‌توانید انجام دهید. حتی می‌توانید از وینستون بخواهید که لاگ‌ها را در فرمت JSON خروجی دهد و یا برای نمونه، خطاها را به رنگ قرمز نمایش دهد.

بخش‌هایی از این نوشته، با ترجمه‌ی Node Logging Basics شکل گرفته است.

loggingnodejsjavascriptجاوا اسکریپتلاگ
شاید از این پست‌ها خوشتان بیاید