Logger — NestJS built-in, Winston/Pino integratsiya

~20 daq · NestJS · Fayllar va Xavfsizlik

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 alohida
  • error → warn → log → debug → verbose — daraja ierarxiyasi
  • HttpLoggerMiddleware — har so'rovni avtomatik log
  • Produksiyada JSON format — log tizimlarida qidiriladi
  • logs/ papkasi .gitignore da
  • Parol, token — log ga yozilmaydi

Keyingi dars

37-dars: WebSockets — real-time ilovalar