با دقت بخونید.
خب بیاید خیلی راحت بهتون یه چیزی رو همین اولش بگم. وقتی یه وب اپی مینویسی که ازش انتظار میره تو محیط production کار بکنه و نیازاتون رو برطرف بکنه نباید لاگ گیری رو یه console.log ساده ببینید.
اطلاعات حساس مثل: رمز عبور، secret key ها، اطلاعات شخصی
برای ماشین ها لاگ بگیر، نه برای انسان ها. چرا؟ چونکه خیلی بهتر از انسان ها عمل میکنن تو حجم بالای اطلاعات. winston لاگ هاشو تو JSON میریزه و اکثر ابزار های دیگه هم از JSON دارن استفاده میکنن.
ارزش لاگ ها تا زمانی که تو یه فایل یا تو ترمینال هستن زیاد نیست. ولی وقتی از ابزاری مثل Retrace استفاده بکنی ارزش لاگ هایی که میگرفتی مشخص میشه (قصد من این نیست که Retrace یا هر ابزار دیگه ای رو بیام توضیح بدم).
یه نکته رو هم که باید بهش توجه بکنی اینه که توی محیط production ارور ها رو یجور دیگه لاگ بکنیم و فرمت بندی هم داریم. میبینید که مسائل داره زیاد میشه. پس باید از یه پکیجی مثل winston استفاده کرد. برای همین موضوع تو میتونی یه کلاس تعریف بکنی به اسم Logger.
چرا؟ چونکه اینکار به تو این توانایی رو میده تا بعدا همون کلاس رو جاهای دیگه استفاده بکنی.
const winston = require('winston'); class Logger { constructor (name) { this.logger = winston.createLogger({ level: 'debug', defaultMeta: { service: name }, transports: [ new winston.transports.Console() ] }); }; debug(log, metadata) { this.logger.debug(log, metadata) }; info(log, metadata) { this.logger.info(log, metadata) }; warn(log, metadata) { this.logger.warn(log, metadata) }; error(log, metadata) { this.logger.error(log, metadata) }; }; module.exports = new Logger(appName); module.exports.getLogger = function(name) { return new Logger(name); };
یه مشته توضیح در مورد این کلاس:
متد createLogger یه مشته تنظیمات رو میگیره و بعدش یه آبجکت برمیگردونه که باهاش میتونی لاگ بگیری. در ضمن از اونجایی که میخواستیم بعدا بتونیم یه logger جدید هم بسازیم با یه اسم جدید اومدیم getLogger رو هم export کردیم. نکته ای که باید بهش توجه بکنی اینه که winston به صورت پیشفرض از JSON استفاده میکنه.
حالا بیاید از این کلاس استفاده بکنیم.
const logger = new require('./logger'); logger.debug('debug log'); logger.info('info log'); logger.warn('warn log'); logger.error('error log1'); logger.error(new Error('error log2'));
در قدم بعدی برای فرمت دادن و رنگ و لعاب دادن باید بیای transports بهش اضافه بکنی. پس این تغییرات رو اعمال بکن:
this.logger = winston.createLogger({ level: 'debug', defaultMeta: { service: name }, transports: [ new winston.transports.Console({ format: winston.format.combine( winston.format.metadata({fillExcept: [ 'password', 'accessKey', 'refreshKey' ]}), winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), winston.format.prettyPrint(), winston.format.colorize(), winston.format.printf(info => `${info.level} **** ${info.message}`), winston.format.simple() ), new winston.transports.File({ filename: path.join(__dirname, '..', 'logs', 'error.log'), level: 'error', }), new winston.transports.File({ filename: path.join(__dirname, '..', 'logs', 'combined.log') }) }) ]
برای مشخص کردن فرمت از winston.format استفاده میکنیم. برای همین منظور توی کلاس Console میایم فرمت رو تعریف میکنیم. یادتون هم باشه که ترتیب این متد های winston.format مهمه. یعنی override میشن وقتی بعد از هم میان.
در ضمن شما در کنار این پکیج میتونید از morgan هم استفاده بکنید.
کانفیگی هم که خودم معمولا استفاده میکنم اینه:
فرض کن نمیخوای لاگ ها رو تو فایل ذخیره بکنی. چکار میکنی؟ از من بپرسی گزینه بهتری از MongoDB سراغ ندارم. با پکیج winston-mongodb به راحتی میتونی لاگ ها رو تو دیتابیس ذخیره بکنی. برای این کار فقط کافیه که یه نگاه سرسری به ریپوزیتوریم تو گیت هاب که براتون با دقت و با توجه به نیاز های یه پروژه واقعی ساختم بندازید.
یه مشته توضیحات در مورد کاری که انجام شده:
پس ما از کلاس Logger میایم instance میگریم و اسم سرویس رو مشخص میکنیم. همون اسم رو با این تنظیمات توی داکیمنت ها هم ذخیره میکنیم تا بدونیم هر لاگ مرتبط با کدوم instance از logger هست. این یه روش خوب برای اینه که بعدا لاگ هاتون رو بتونید از هم تفکیک کنید.