All files / src/common/services logger.service.ts

69.56% Statements 16/23
66.66% Branches 12/18
45.45% Functions 5/11
66.66% Lines 14/21

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 8967x 67x 67x 67x 67x     67x         14x 14x 14x                               13x                                                   13x               32x                                 14x   14x          
import { Injectable, Scope, OnModuleDestroy } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { createLogger, format, transports, Logger } from 'winston';
import { v4 as uuidv4 } from 'uuid';
import R2Transport from '@app/common/transports/r2.transport';
 
@Injectable({ scope: Scope.TRANSIENT })
export class LoggerService implements OnModuleDestroy {
  private context?: string;
  private logger: Logger;
  private requestId: string;
 
  constructor(private configService: ConfigService) {
    this.requestId = uuidv4();
    this.logger = createLogger({
      level: this.configService.get('LOG_LEVEL', 'info'),
      format: format.combine(
        format.timestamp(),
        format.errors({ stack: true }),
        format.json(),
      ),
      defaultMeta: {
        service: '1uy-backend',
        environment: this.configService.get('NODE_ENV', 'development'),
      },
      transports: [
        new transports.Console({
          format: format.combine(
            format.colorize(),
            format.printf(({ timestamp, level, message, ...meta }) => {
              return `${timestamp} [${level}] [${this.requestId}] ${this.context ? `[${this.context}]` : ''} ${message} ${Object.keys(meta).length ? JSON.stringify(meta) : ''}`;
            }),
          ),
        }),
        ...(this.configService.get<string>('R2_LOGS_BUCKET') || this.configService.get<string>('R2_BUCKET')
          ? [
              new R2Transport({
                bucket: (this.configService.get<string>('R2_LOGS_BUCKET') || this.configService.get<string>('R2_BUCKET'))!,
                prefix: this.configService.get<string>('R2_LOGS_PREFIX') || 'logs/',
                flushIntervalMs: Number(this.configService.get<string>('R2_LOGS_FLUSH_MS') || '30000'),
              }),
            ]
          : []),
      ],
    });
  }
 
  setContext(context: string) {
    this.context = context;
  }
 
  setRequestId(requestId: string) {
    this.requestId = requestId;
  }
 
  error(message: string, meta?: Record<string, any>) {
    this.logger.error(message, { ...meta, requestId: this.requestId, context: this.context });
  }
 
  warn(message: string, meta?: Record<string, any>) {
    this.logger.warn(message, { ...meta, requestId: this.requestId, context: this.context });
  }
 
  info(message: string, meta?: Record<string, any>) {
    this.logger.info(message, { ...meta, requestId: this.requestId, context: this.context });
  }
 
  log(message: string, meta?: Record<string, any>) {
    // Alias for info level to support common logger API expectations
    this.info(message, meta);
  }
 
  debug(message: string, meta?: Record<string, any>) {
    this.logger.debug(message, { ...meta, requestId: this.requestId, context: this.context });
  }
 
  verbose(message: string, meta?: Record<string, any>) {
    this.logger.verbose(message, { ...meta, requestId: this.requestId, context: this.context });
  }
 
  async onModuleDestroy() {
    try {
      // Close all Winston transports
      this.logger.close();
    } catch (error) {
      console.error('Error closing logger transports:', error);
    }
  }
}