تسجيل الدخول في تطبيقات TypeScript: أفضل الممارسات التطبيقية
تسجيل الدخول هو جزء أساسي من أي تطبيق إنتاجي. فهو لا يسمح فقط بتتبع تنفيذ الكود والعثور على الأخطاء، بل يتيح أيضًا إجراء تدقيق أمني، وتحليل الأداء، وتصحيح السيناريوهات المعقدة. في TypeScript، مع نظام الأنواع القوي الخاص به، يصبح نهج تسجيل الدخول أكثر مرونة وموثوقية. في هذه المقالة، سنستعرض أفضل ممارسات تسجيل الدخول من خلال أمثلة محددة.
1. اختيار المكتبة وإعداد المسجل
أول ما يجب البدء به هو اختيار المكتبة المناسبة. من بين الحلول الشائعة نجد winston و pino. كلاهما يدعم TypeScript بشكل افتراضي، لكن لهما أولويات مختلفة: winston يوفر المرونة والوظائف الغنية، بينما pino يركز على الأداء الأقصى.
لنلقِ نظرة على مثال لإعداد winston مع تحديد الأنواع:
import winston from 'winston';
const logger = winston.createLogger({ level: process.env.LOG_LEVEL || 'info', format: winston.format.combine( winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), winston.format.errors({ stack: true }), winston.format.json() ), defaultMeta: { service: 'user-service' }, transports: [ new winston.transports.Console({ format: winston.format.combine( winston.format.colorize(), winston.format.simple() ), }), new winston.transports.File({ filename: 'logs/error.log', level: 'error' }), new winston.transports.File({ filename: 'logs/combined.log' }), ],});
export default logger;أما بالنسبة لـ pino، فسيكون التكوين أكثر إيجازًا ولكنه بنفس الفعالية:
import pino from 'pino';
const logger = pino({ level: process.env.LOG_LEVEL || 'info', transport: { target: 'pino-pretty', options: { colorize: true, translateTime: 'SYS:standard', }, },});
export default logger;نصيحة: قم دائمًا بتخزين إعداد مستوى التسجيل في متغيرات البيئة (LOG_LEVEL). في بيئة الإنتاج، يُستخدم عادةً info أو warn، بينما في بيئة التطوير يُستخدم debug.
2. التسجيل المنظم والسياق
أحد الأخطاء الرئيسية هو تسجيل نصوص بسيطة. بدلاً من ذلك، استخدم كائنات منظمة. فهي تسمح بتصفية وتجميع السجلات بسهولة في أنظمة مثل ELK أو Grafana أو Datadog.
مثال على تسجيل سيء:
logger.info('User logged in successfully');مثال على تسجيل جيد مع السياق:
logger.info({ message: 'User logged in', userId: user.id, email: user.email, loginMethod: 'oauth', ip: request.ip, duration: Date.now() - startTime,});لتسهيل الأمر، يمكنك إنشاء غلاف محدد الأنواع:
interface LogContext { requestId?: string; userId?: string; action?: string; [key: string]: unknown;}
function logWithContext(message: string, context: LogContext = {}): void { logger.info({ message, ...context });}
// الاستخدامlogWithContext('Order processed', { orderId: '12345', userId: 'user_678', total: 99.99 });يضمن هذا النهج أن كل سجل يحتوي على المعلومات الأساسية للتحليل.
3. تسجيل الأخطاء والاستثناءات
معالجة الأخطاء هي الجزء الأهم في تسجيل الدخول. قم دائمًا بتسجيل كامل مكدس الخطأ والسياق الذي حدث فيه. لا تستخدم console.error لأنه لا يوفر مرونة في التدوير والتنسيق.
النمط الصحيح مع TypeScript:
import logger from './logger';
async function fetchUserData(userId: string): Promise<UserData> { try { const response = await api.get(`/users/${userId}`); ret