Logger — NestJS built-in, Winston/Pino integratsiya
Muammodan boshlaylik
Produksiyada xato yuz berdi. console.log lar bor — lekin qaysi faylda, qaysi vaqtda, qaysi foydalanuvchi so'rovida? Hech narsa ko'rinmaydi. Xatoni topish uchun soat sarf qilindi.
Professional logging: vaqt tamg'asi, daraja (error/warn/info), context (qaysi service), va mashinalar o'qiy oladigan format (JSON) — log tizimlariga yuboriladi, qidiriladi, alertlar o'rnatiladi.
Loyiha tuzilishi
src/
├── main.ts ← Logger ulash
├── app.module.ts
│
├── logger/
│ ├── logger.module.ts ← global module
│ ├── logger.service.ts ← Winston wrapper
│ └── http-logger.middleware.ts ← har HTTP so'rovni log
│
└── users/
└── users.service.ts ← Logger inject
1. NestJS built-in Logger
Kichik loyihalar uchun yetarli:
// src/users/users.service.ts
import { Injectable, Logger, NotFoundException } from '@nestjs/common';
@Injectable()
export class UsersService {
// Har class uchun alohida Logger — context ko'rinadi
private readonly logger = new Logger(UsersService.name);
async findOne(id: string) {
this.logger.log(`Foydalanuvchi qidirilmoqda: ${id}`);
const user = await this.userModel.findById(id).exec();
if (!user) {
this.logger.warn(`Foydalanuvchi topilmadi: ${id}`);
throw new NotFoundException('Topilmadi');
}
this.logger.debug(`Topildi: ${user.email}`);
return user;
}
async create(dto: any) {
try {
const user = await new this.userModel(dto).save();
this.logger.log(`Yangi foydalanuvchi: ${user.email}`);
return user;
} catch (err) {
this.logger.error(`Yaratishda xato: ${err.message}`, err.stack);
throw err;
}
}
}
Log darajalari:
logger.error() — Xatolar (produksiyada har doim)
logger.warn() — Ogohlantirishlar
logger.log() — Muhim hodisalar (info)
logger.debug() — Batafsil (dev da)
logger.verbose() — Juda batafsil
2. Winston — professional logging
npm install winston nest-winston
// src/logger/logger.service.ts
import { Injectable, LoggerService } from '@nestjs/common';
import * as winston from 'winston';
@Injectable()
export class AppLogger implements LoggerService {
private readonly winston: winston.Logger;
constructor() {
this.winston = winston.createLogger({
level: process.env.NODE_ENV === 'production' ? 'warn' : 'debug',
format: winston.format.combine(
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
winston.format.errors({ stack: true }),
// Dev — rangli, o'qilishi oson
// Prod — JSON, log tizimi uchun
process.env.NODE_ENV === 'production'
? winston.format.json()
: winston.format.combine(
winston.format.colorize(),
winston.format.printf(({ timestamp, level, message, context, stack }) =>
`${timestamp} [${context}] ${level}: ${message}${stack ? '\n' + stack : ''}`
),
),
),
transports: [
new winston.transports.Console(),
// Produksiyada fayllarga yozish
...(process.env.NODE_ENV === 'production'
? [
new winston.transports.File({
filename: 'logs/error.log',
level: 'error',
maxsize: 10 * 1024 * 1024, // 10MB
maxFiles: 5, // 5 ta fayl, keyin eski o'chadi
}),
new winston.transports.File({
filename: 'logs/combined.log',
maxsize: 20 * 1024 * 1024,
maxFiles: 10,
}),
]
: []),
],
});
}
log(message: string, context?: string) {
this.winston.info(message, { context });
}
error(message: string, trace?: string, context?: string) {
this.winston.error(message, { context, stack: trace });
}
warn(message: string, context?: string) {
this.winston.warn(message, { context });
}
debug(message: string, context?: string) {
this.winston.debug(message, { context });
}
verbose(message: string, context?: string) {
this.winston.verbose(message, { context });
}
}
// src/logger/logger.module.ts
import { Global, Module } from '@nestjs/common';
import { AppLogger } from './logger.service';
@Global() // barcha modullarda import qilmasdan ishlatish
@Module({
providers: [AppLogger],
exports: [AppLogger],
})
export class LoggerModule {}
// src/main.ts
import { AppLogger } from './logger/logger.service';
async function bootstrap() {
const app = await NestFactory.create(AppModule, {
bufferLogs: true, // ← bootstrap log lari ham saqlansin
});
const logger = app.get(AppLogger);
app.useLogger(logger); // ← NestJS built-in → Winston ga o'tkazish
await app.listen(3000);
logger.log(`Server 3000-portda ishga tushdi`, 'Bootstrap');
}
HTTP so'rovlarni log qilish — Middleware
Har HTTP so'rovni avtomatik log qilish:
// src/logger/http-logger.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { AppLogger } from './logger.service';
@Injectable()
export class HttpLoggerMiddleware implements NestMiddleware {
constructor(private logger: AppLogger) {}
use(req: Request, res: Response, next: NextFunction) {
const { method, originalUrl, ip } = req;
const userAgent = req.get('user-agent') || '';
const start = Date.now();
res.on('finish', () => {
const { statusCode } = res;
const duration = Date.now() - start;
const message = `${method} ${originalUrl} ${statusCode} ${duration}ms`;
const context = 'HTTP';
if (statusCode >= 500) {
this.logger.error(message, '', context);
} else if (statusCode >= 400) {
this.logger.warn(message, context);
} else {
this.logger.log(message, context);
}
});
next();
}
}
// src/app.module.ts
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { HttpLoggerMiddleware } from './logger/http-logger.middleware';
import { LoggerModule } from './logger/logger.module';
@Module({
imports: [LoggerModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(HttpLoggerMiddleware)
.forRoutes('*'); // barcha route lar
}
}
Natija (dev):
2024-01-15 14:23:01 [HTTP] info: POST /auth/login 200 45ms
2024-01-15 14:23:15 [HTTP] warn: GET /users/invalid-id 404 12ms
2024-01-15 14:23:45 [HTTP] error: POST /products 500 234ms
Service da Logger ishlatish
// src/users/users.service.ts
import { Injectable } from '@nestjs/common';
import { AppLogger } from '../logger/logger.service';
@Injectable()
export class UsersService {
constructor(
@InjectModel(User.name) private userModel: Model<UserDocument>,
private logger: AppLogger, // ← inject
) {}
async create(dto: CreateUserDto) {
try {
const user = await new this.userModel(dto).save();
this.logger.log(`Yangi foydalanuvchi ro'yxatdan o'tdi: ${user.email}`, UsersService.name);
return user;
} catch (err) {
this.logger.error(
`Foydalanuvchi yaratishda xato: ${dto.email}`,
err.stack,
UsersService.name,
);
throw err;
}
}
}
Tez-tez uchraydigan xatolar
console.log ni loggerga almashtirishni unutish
// ❌ console.log — vaqt tamg'asi yo'q, darajasi yo'q, JSON emas
console.log('Foydalanuvchi yaratildi:', user);
// ✅ Logger — tuzilgan, daraja bilan
this.logger.log(`Foydalanuvchi yaratildi: ${user.email}`, UsersService.name);
Muhim ma'lumotlarni log ga yozish
// ❌ Hech qachon
this.logger.log(`Login: email=${dto.email} parol=${dto.password}`);
// ✅ Faqat identifikatorlar
this.logger.log(`Login urinishi: ${dto.email}`, 'AuthService');
logs/ papkasini git da qoldirish
# .gitignore
logs/
*.log
mkdir logs
touch logs/.gitkeep
Xulosa
Logger — produksiyada muammolarni tezkor aniqlash asosi. Built-in Logger — oddiy loyihalar. Winston — professional, faylga yozish, darajalar.
Eslab qoling:
new Logger(ClassName.name)— har class uchun alohidaerror→warn→log→debug→verbose— daraja ierarxiyasiHttpLoggerMiddleware— har so'rovni avtomatik log- Produksiyada JSON format — log tizimlarida qidiriladi
logs/papkasi.gitignoreda- Parol, token — log ga yozilmaydi
Keyingi dars
37-dars: WebSockets — real-time ilovalar