blanc-logger v1.1.0
Blanc Logger
Advanced Winston logger with TypeORM support & structured logging.
Blanc Logger는 NestJS 및 TypeORM 애플리케이션을 위한 고급 로깅 라이브러리입니다.
이 라이브러리는 Winston을 기반으로 콘솔 및 파일 로깅을 지원하며,
자동 구조화된 JSON 포맷, SQL 쿼리 하이라이팅, 모듈 기반 로그 추적 등 다양한 기능을 제공합니다.
Installation
npm install blanc-logger
Usage
1. NestJS 전역 Logger 적용 (main.ts
)
Blanc Logger를 전역 로거로 적용하면, 애플리케이션 전체에서 동일한 로깅 설정을 사용할 수 있습니다.
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { blancLogger, BlancLoggerMiddleware } from 'blanc-logger';
async function bootstrap() {
const app = await NestFactory.create(AppModule, {
logger: blancLogger, // 전역 Logger 적용
});
// 미들웨어를 적용하여 요청 시 모듈명 등 컨텍스트 정보를 추가
app.use(new BlancLoggerMiddleware().use);
await app.listen(3000);
}
bootstrap();
2. TypeORM Logger 적용 (AppModule
)
TypeORM 설정에서 Blanc Logger의 전용 로거를 사용하여, SQL 쿼리 및 데이터베이스 관련 로그를 효과적으로 기록할 수 있습니다.
import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { TypeOrmBlancLogger } from 'blanc-logger';
@Module({ imports: TypeOrmModule.forRoot({ // ... DB 설정 logging: true, logger: new TypeOrmBlancLogger(), // TypeORM용 로거 사용 }), // 다른 모듈들... , }) export class AppModule {}
## 기존 Logger 대체 방법
> _Blanc Logger를 사용하여 기존의 NestJS 내장 로거를 대체할 수 있습니다._
### 1. 기존 코드
```typescript
// 기존 NestJS 내장 로거 사용:
this.logger.error('Error message', error.stack);
this.logger.log('Log message');
2. Blanc Logger 사용
// 에러 발생 시, 스택 정보와 함께 기록
blancLogger.error('Error message', { moduleName: 'ModuleName', stack: error.stack });
blancLogger.log('Log message', { moduleName: 'ModuleName' });
blancLogger.warn('Warn message', { moduleName: 'ModuleName' });
blancLogger.verbose('Verbose message', { moduleName: 'ModuleName' });
blancLogger.debug('Debug message', { moduleName: 'ModuleName' });
3. Logs 파일
로그 파일이 자동으로 날짜별로 생성되어 logs 폴더 아래에 다음과 같이 쌓입니다.
logs/
├── combined-2025-03-03.log # 모든 로그 메시지
├── error-2025-03-03.log # error 레벨 로그
├── exceptions-2025-03-03.log # 처리되지 않은 예외 로그
└── rejections-2025-03-03.log # 미처리된 Promise 거부 로그
예시 로그 (error-2025-03-03.log 파일):
{"level":"error","message":"HTTP Exception: DUPLICATION","stack":[{"moduleName":"user","path":"/api/user/profile","stack":"ConflictException: DUPLICATION\n at UserService.getProfile (/path/to/src/user/user.service.ts:60:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)"}],"timestamp":"2025-03-03 14:29:51"}
Overview
Feature | Description |
---|---|
다중 전송 로깅 (Multi-Transport Logging) | - 콘솔 출력과 파일 저장을 동시에 지원- 일정 크기(예: 20MB) 또는 일정 주기(예: 15일)에 따라 자동으로 로그 파일 회전- 로그 파일 예시: combined-YYYY-MM-DD.log , error-YYYY-MM-DD.log , exceptions-YYYY-MM-DD.log , rejections-YYYY-MM-DD.log |
구조화된 로그 출력 (Structured Logging) | - UUIDv5를 사용해 고유 LogID 생성- {timestamp, level, message} 형식의 표준 JSON 포맷으로 출력되어 외부 시스템 연동 용이 |
모듈별 컨텍스트 추적 (Contextual Tracing) | - BlancLoggerMiddleware 를 활용하여 HTTP 요청에서 모듈명 자동 추출- [UserService → AuthModule → Subsystem] 과 같이 계층 구조로 표시 |
동적 로그 레벨 관리 (Dynamic Log Levels) | - 개발 및 운영 환경에 따라 debug, verbose, info, warn, error 로그 레벨을 실시간으로 조절 가능- 필요한 정보만 선별 기록하여 로그의 가독성과 관리 효율성을 향상 |
SQL 구문 하이라이팅 (SQL Syntax Highlighting) | - SQL 쿼리의 키워드, 값, 테이블명을 색상 강조 및 들여쓰기로 구분- 파라미터 값 컨텍스트 인식 강조 |
쿼리 성능 분석 (Query Analysis) | - SQL 성능 저하 유발 패턴 자동 감지 (SELECT * , JOIN 조건 누락 등) |
에러 진단 및 스택 추적 (Error Diagnostics) | - 에러 발생 시 다중 스택 레이어와 추가 메타데이터를 포함하여 상세한 에러 로그 기록 |
성능 모니터링 (Performance Monitoring) | - HTTP 요청 처리 시간 및 Slow Query (예: 500ms) 경고 로그 생성- 실행 계획(Explain Plan) 리포트 시각화 |
커스터마이징 (Customization) | - logger-config.yaml 파일을 통해 로그 저장 경로, 로그 레벨, 파일 크기, 회전 주기 등을 쉽게 조정 가능 |
Configuration
Blanc Logger는 기본 설정을 제공하지만, 필요에 따라 사용자 환경에 맞게 커스터마이징 할 수 있습니다.
설정을 변경하려면프로젝트 루트
에logger-config.yaml
파일을 생성하고 아래와 같이Override
할 수 있습니다.
LOG_DIR: logs # 로그 파일 저장 경로 (기본: 프로젝트 루트/logs)
CONSOLE_LOG_LEVEL: info # 콘솔 출력 로그 레벨 (debug, verbose, info, warn, error)
FILE_LOG_LEVEL: error # 파일 출력 로그 레벨 (debug, verbose, info, warn, error)
ROTATION_DAYS: 30d # 로그 파일 보관 기간 (예: 30일)
MAX_FILE_SIZE: 20m # 단일 파일 최대 크기 (예: 20MB)
Log Output Examples
SQL Query Log Example
╔═ SQL Query ═════════════════════════════════ SELECT "user"."id" AS "userId", "user"."email" AS "userEmail" FROM "user" "user" WHERE "user"."age" > $1 ╠═ Parameters ═══════════════════════════════ [18] ╠═ Analysis ═════════════════════════════════ ⚠️ Avoid SELECT * - specify columns explicitly ╚═════════════════════════════════════════════
Console Output Log Example
Console Error Log Example
Using Examples
1. Logging Interceptor 구현
HTTP 요청 처리 시간을 측정하여 로그로 기록하는 인터셉터 예제입니다.
BlancLoggerMiddleware를 통해 설정된 모듈명 정보가 자동으로 포함됩니다.
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
import { blancLogger } from 'blanc-logger';
import * as chalk from 'chalk';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const startTime = Date.now();
const req = context.switchToHttp().getRequest();
const decodedUrl = decodeURIComponent(req.url);
const moduleName = (req as any).moduleName || 'UnknownModule';
return next.handle().pipe(
tap(() => {
const delay = Date.now() - startTime;
const delayStr =
delay > 500 ? chalk.bold.red(`${delay}ms 🚨`) : chalk.magenta(`${delay}ms`);
const message = `Request processed: ${chalk.yellow(req.method)} ${chalk.green(
decodedUrl,
)} ${delayStr}`;
blancLogger.log(message, moduleName);
}),
);
}
}
전역 인터셉터로 적용하려면 AppModule에 아래와 같이 등록합니다
import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { LoggingInterceptor } from './commons/interceptors/logging.interceptor';
@Module({
providers: [
{
provide: APP_INTERCEPTOR,
useClass: LoggingInterceptor, // 전역 인터셉터로 등록
},
],
})
export class AppModule {}
Console Output Log Example
- 응답 시간 (예: 500ms) 초과 시 강조
2. Global Exception Filter 구현
Blanc Logger를 사용하여 예외 발생 시 상세한 에러 정보를 로그로 기록하는 전역 익셉션 필터 예제입니다.
BlancLoggerMiddleware를 통해 설정된 모듈명 정보가 자동으로 포함됩니다.
import {
ArgumentsHost,
Catch,
ExceptionFilter,
HttpException,
InternalServerErrorException,
} from '@nestjs/common';
import { blancLogger } from 'blanc-logger';
import { Request, Response } from 'express';
interface ExceptionResponse {
status: number;
message: string;
stack?: string;
}
/** 예외 객체를 처리하여 상태, 메시지, 스택을 반환하는 함수 */
const handleException = (exception: unknown, _request: Request): ExceptionResponse => {
if (exception instanceof HttpException) {
const status = exception.getStatus();
const res = exception.getResponse();
const message =
typeof res === 'object' && res !== null
? (res as any).message ?? exception.message
: exception.message;
return {
status,
message: `HTTP Exception: ${message}`,
stack: exception instanceof Error ? exception.stack : '',
};
}
if (exception instanceof Error) {
const status = new InternalServerErrorException().getStatus();
return {
status,
message: `Unhandled exception: ${exception.message}`,
stack: exception.stack,
};
}
return { status: 500, message: 'Unknown error' };
};
@Catch()
export class GlobalExceptionFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost): void {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const moduleName = (request as any)?.moduleName ?? 'Global';
const { status, message, stack } = handleException(exception, request);
blancLogger.error(message, {
moduleName,
path: request.url,
stack,
});
response.status(status).json({
statusCode: status,
status: message,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}
전역 필터로 적용하려면 AppModule에 아래와 같이 등록합니다
import { Module } from '@nestjs/common';
import { APP_FILTER } from '@nestjs/core';
import { GlobalExceptionFilter } from './commons/filters/global-exception.filter';
@Module({
providers: [
{
provide: APP_FILTER,
useClass: GlobalExceptionFilter, // 전역 필터로 등록
},
],
})
export class AppModule {}
전역 익셉션 필터를 적용한 후, 다음과 같이 예외를 던질 경우
throw new ConflictException('DUPLICATION');
Console Output Log Example
또한, 동일하게 로그 파일도 생성됩니다.
{"level":"error","message":"HTTP Exception: DUPLICATION","stack":[{"moduleName":"user","path":"/api/user/profile","stack":"ConflictException: DUPLICATION\n at UserService.getProfile (/path/to/src/user/user.service.ts:60:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)"}],"timestamp":"2025-03-03 14:29:51"}
License
이 프로젝트는 MIT License에 따라 배포 및 사용이 가능합니다.