Fastify و ماجرای یک میلیون خط لاگ در ماه
خلاصهٔ کاملتر
مانوئل اسپیگولون، یکی از نگهدارندههای Fastify، تعریف میکنه که موقع بررسی یه pull request به یه مسئلهٔ آشنا رسیده: ارکستریتور هر ۵ ثانیه /health رو صدا میزنه، یعنی ۱۷٬۲۸۰ درخواست در روز، و Fastify برای هر درخواست دو خط لاگ مینویسه. ماهی بیش از یک میلیون خط که همهشون فقط میگن همهچی مرتبه، و تو داری بابت ذخیره و ایندکس همونها پول میدی. به گفتهٔ نویسنده اون PR اصلاً لازم نبود دست به هستهٔ Fastify بزنه، چون فریمورک از قبل دو تا راه براش داشته.
راه اول دو خطیـه. هر بار که تو Fastify یه پلاگین register میکنی، یه کانتکست کپسولهشده ساخته میشه و هر کانتکست لاگر بچهٔ خودش رو داره. گزینهٔ logLevel سطح همون لاگر بچه رو تعیین میکنه و مقدار silent هر چی هست رو دور میریزه:
app.register(async function healthChecks (app) {
app.get('/health', async () => ({ status: 'ok' }))
app.get('/ready', async () => ({ status: 'ok' }))
}, { logLevel: 'silent' })بیرون از این پلاگین سطح info دستنخورده میمونه. یعنی دو تا هلثچک، صفر خط لاگ.
ایراد silent اینه که همهچی یا هیچیـه: لاگهای خود هندلر و خطاها هم با هم میرن و نمیتونی بگی «فقط شکستها بمونن». راه دوم برای همینه: LogController، کلاسی که مستقیم از fastify بیرون میاد و از نسخهٔ ۵.۱۰.۰ در دسترسه. هر خط لاگ خودکار Fastify یه متد روی این کلاسه: incomingRequest، requestCompleted، defaultErrorLog، routeNotFound، streamError، writeHeadError، serializerError و serviceUnavailable. ازش سابکلاس میسازی، فقط همونهایی که برات مهمن رو override میکنی و بقیه رفتار پیشفرضشون رو نگه میدارن.
با همین میشه اون دو خط رو یکی کرد و دقیقاً فیلدهایی که داشبوردت میخواد رو فرستاد. یه override خالی هم یعنی اون خط کلاً حذف بشه:
class AccessLog extends LogController {
incomingRequest () {}
requestCompleted (error, request, reply) {
reply.log.info({ method: request.method, url: request.url, status: reply.statusCode, ms: Math.round(reply.elapsedTime) }, 'access')
}
}نویسنده تأکید میکنه که وقتی یه متد رو override میکنی، گیت پیشفرض کلاس پایه هم از بین میره، پس باید خودت this.isLogDisabled(request) رو صدا بزنی؛ و اگه constructor خودت رو نوشتی، super(options) یادت نره. با همین الگو میشه یه sampler هم ساخت که مثلاً از هر ده درخواست موفق یکی رو لاگ کنه ولی هیچ خطایی رو از دست نده.
دو تا محدودیت هم هست. logController یه نمونهٔ واحد برای کل سرورـه و برخلاف logLevel کپسوله نیست، پس رفتار هر روت رو باید از خود request دربیاری، مثلاً از request.routeOptions.config. این متدها هم روی مسیر داغ هر درخواست نشستن، پس باید ارزون بمونن: خوندن یه پراپرتی و نوشتن لاگ، نه await و رفتوبرگشت JSON. در ضمن گزینههای disableRequestLogging و requestIdLogLabel بهعنوان آپشن سطح بالای Fastify منسوخ شدن و تو fastify@6 حذف میشن؛ از این به بعد به کنترلر داده میشن.
نکات کلیدی:
- هلثچک هر ۵ ثانیه یعنی ۱۷٬۲۸۰ درخواست در روز و بیش از یک میلیون خط لاگ در ماه
- register(plugin, { logLevel: 'silent' }) کل کانتکست رو ساکت میکنه، حتی لاگهای خود هندلر
- LogController از Fastify v5.10.0 در دسترسه، بدون نیاز به نسخهٔ alpha یا RC
- override کردن یه متد، گیت isLogDisabled رو هم برمیداره؛ باید دستی صداش بزنی
- disableRequestLogging و requestIdLogLabel منسوخ شدن و تو fastify@6 حذف میشن
- logController کپسوله نیست: یه نمونه برای کل سرور، پس تصمیم هر روت باید از request بیاد




