NestJS — Node.js للمؤسسات

التسجيل وفحوصات الصحة والمراقبة

16 دقيقة الدرس 45 من 48

التسجيل وفحوصات الصحة والمراقبة

يجب أن تُجيب التطبيقات الجاهزة للإنتاج عن ثلاثة أسئلة في أي لحظة: ماذا يحدث؟ (التسجيل)، هل الخدمة حيّة؟ (فحوصات الصحة)، كيف أتتبّع طلبًا من البداية إلى النهاية؟ (معرّفات الارتباط / التتبّع الموزَّع). يُشحَن NestJS بمسجِّل مدمَج ويتكامل بسهولة مع مسجِّلات منظَّمة كـ Pino أو Winston ومكتبة فحوصات الصحة @nestjs/terminus.

المسجِّل المدمَج

يوفّر NestJS الفئة Logger من @nestjs/common. هيّئها بسلسلة سياق (اسم الفئة) لإضافة علامة لكلّ سطر تسجيل:

import { Injectable, Logger } from '@nestjs/common'; @Injectable() export class OrdersService { private readonly logger = new Logger(OrdersService.name); async create(dto: CreateOrderDto) { this.logger.log(`Creating order for user ${dto.userId}`); try { const order = await this.ordersRepo.save(dto); this.logger.verbose(`Order saved: ${order.id}`); return order; } catch (err) { this.logger.error('Failed to save order', err.stack); throw err; } } }

مستويات التسجيل تصاعديًّا حسب الخطورة: verbosedebuglogwarnerrorfatal. تحكَّم في المستويات النشطة عبر app.useLogger() أو خيار logger في NestFactory.create().

استبدال المسجِّل بـ Pino (JSON منظَّم)

سجلّات النصّ العادي يصعب الاستعلام عنها في الإنتاج. تستبدل حزمة nestjs-pino المسجِّل الافتراضي بـ Pino الذي يُصدر JSON محدودًا بالأسطر الجديدة تحلّله منصّات تجميع السجلّات (Loki وCloudWatch وDatadog) أصليًّا:

// app.module.ts import { LoggerModule } from 'nestjs-pino'; @Module({ imports: [ LoggerModule.forRoot({ pinoHttp: { level: process.env.NODE_ENV === 'production' ? 'info' : 'debug', transport: process.env.NODE_ENV !== 'production' ? { target: 'pino-pretty' } // قابل للقراءة في التطوير : undefined, }, }), ], }) export class AppModule {} // main.ts import { Logger } from 'nestjs-pino'; const app = await NestFactory.create(AppModule, { bufferLogs: true }); app.useLogger(app.get(Logger)); await app.listen(3000);
bufferLogs: true يحتجز رسائل التشغيل المبكّر حتى يصبح Pino جاهزًا لئلّا يضيع شيء. اقرنها دائمًا بـ app.useLogger(app.get(Logger)).

فحوصات الصحة مع @nestjs/terminus

تكشف @nestjs/terminus نقطة نهاية /health تنسّق مؤشّرات متعدّدة (قاعدة البيانات، والقرص، والذاكرة، والاعتماديات HTTP). تستدعي مِسبارات liveness وreadiness في Kubernetes هذه النقطة لتقرير ما إذا كان يجب توجيه الحركة إلى حاوية (pod).

// health.controller.ts import { Controller, Get } from '@nestjs/common'; import { HealthCheckService, HealthCheck, TypeOrmHealthIndicator, MemoryHealthIndicator, DiskHealthIndicator, } from '@nestjs/terminus'; @Controller('health') export class HealthController { constructor( private health: HealthCheckService, private db: TypeOrmHealthIndicator, private mem: MemoryHealthIndicator, private disk: DiskHealthIndicator, ) {} @Get() @HealthCheck() check() { return this.health.check([ () => this.db.pingCheck('database'), () => this.mem.checkHeap('memory_heap', 300 * 1024 * 1024), // 300 ميغابايت () => this.disk.checkStorage('storage', { path: '/', thresholdPercent: 0.9 }), ]); } }

يحتوي JSON الناتج على حقل status في المستوى الأعلى ("ok" أو "error") وتفصيل لكلّ مؤشّر. HTTP 200 = سليم؛ HTTP 503 = متدهور.

تتبّع الطلبات بمعرّفات الارتباط

معرّف الارتباط (Correlation ID) هو UUID يُولَد (أو يُمرَّر من المُستدعي) عند نقطة الدخول لكلّ طلب ويُرفَق بجميع السجلّات واستدعاءات HTTP الصادرة. يتيح لك ذلك البحث عن معرّف واحد عبر الخدمات المصغَّرة لإعادة بناء التتبّع الكامل:

// correlation.middleware.ts import { Injectable, NestMiddleware } from '@nestjs/common'; import { Request, Response, NextFunction } from 'express'; import { randomUUID } from 'crypto'; @Injectable() export class CorrelationMiddleware implements NestMiddleware { use(req: Request, res: Response, next: NextFunction) { const id = (req.headers['x-correlation-id'] as string) ?? randomUUID(); req.headers['x-correlation-id'] = id; res.setHeader('x-correlation-id', id); next(); } }
مرّر معرّف الارتباط إلى الخدمات الأخرى. حين تستدعي خدمتك خدمةً HTTP أخرى (عبر HttpService / Axios)، مرّر رأس x-correlation-id ذاته. في Pino، ادرجه عبر pinoHttp.customProps حتى يحمله كلّ سطر تسجيل تلقائيًّا.
لا تُسجّل بيانات حسّاسة أبدًا. معرّفات الارتباط ومسارات الطلبات وتوقيتاتها آمنة. كلمات المرور والرموز والبيانات الشخصية وأرقام بطاقات الائتمان يجب ألّا تظهر في السجلّات مطلقًا — حتى في التطوير. طبّق استراتيجية تنقيّة للسجلّات (يدعم Pino مسارات redact أصليًّا).

الخلاصة

استخدم Logger المدمَج في NestJS للسيناريوهات البسيطة، واستبدله بـ nestjs-pino للحصول على سجلّات JSON منظَّمة في الإنتاج. أضف مؤشّرات فحص الصحة من @nestjs/terminus لقاعدة البيانات والذاكرة والقرص حتى تتمكّن Kubernetes (أو أيّ نظام فحص صحة) من مراقبة جاهزية الخدمة. احقن معرّفات الارتباط عبر وسيط (middleware) لربط جميع سطور السجلّات الخاصّة بطلب واحد — عبر الخدمات. هذه الركائز الثلاث (التسجيل وفحص الصحة والتتبّع) تشكّل أساس المراقبة الإنتاجية.