로그로그
This commit is contained in:
@@ -9,11 +9,7 @@ import {
|
||||
import { Request, Response } from 'express';
|
||||
|
||||
/**
|
||||
* 모든 예외 필터
|
||||
*
|
||||
* @description
|
||||
* HTTP 예외뿐만 아니라 모든 예외를 잡아서 처리합니다.
|
||||
* 예상치 못한 에러도 일관된 형식으로 응답합니다.
|
||||
* 모든 예외 필터 - Docker 환경에서 로그 출력 보장
|
||||
*/
|
||||
@Catch()
|
||||
export class AllExceptionsFilter implements ExceptionFilter {
|
||||
@@ -40,11 +36,7 @@ export class AllExceptionsFilter implements ExceptionFilter {
|
||||
}
|
||||
} else {
|
||||
status = HttpStatus.INTERNAL_SERVER_ERROR;
|
||||
message = '서버 내부 오류가 발생했습니다.';
|
||||
|
||||
if (exception instanceof Error) {
|
||||
message = exception.message;
|
||||
}
|
||||
message = exception instanceof Error ? exception.message : '서버 내부 오류가 발생했습니다.';
|
||||
}
|
||||
|
||||
const errorResponse = {
|
||||
@@ -61,32 +53,41 @@ export class AllExceptionsFilter implements ExceptionFilter {
|
||||
(errorResponse as any).stack = exception.stack;
|
||||
}
|
||||
|
||||
// ========== 상세 로깅 ==========
|
||||
this.logger.error(
|
||||
`========== EXCEPTION ==========`
|
||||
);
|
||||
this.logger.error(
|
||||
`[${request.method}] ${request.url} - Status: ${status}`
|
||||
);
|
||||
this.logger.error(
|
||||
`Message: ${JSON.stringify(message)}`
|
||||
);
|
||||
|
||||
// ========== 상세 로깅 (stdout으로 즉시 출력) ==========
|
||||
const logMessage = [
|
||||
'',
|
||||
'╔══════════════════════════════════════════════════════════════╗',
|
||||
'║ EXCEPTION OCCURRED ║',
|
||||
'╚══════════════════════════════════════════════════════════════╝',
|
||||
` Timestamp : ${errorResponse.timestamp}`,
|
||||
` Method : ${request.method}`,
|
||||
` Path : ${request.url}`,
|
||||
` Status : ${status}`,
|
||||
` Message : ${JSON.stringify(message)}`,
|
||||
];
|
||||
|
||||
if (exception instanceof Error) {
|
||||
this.logger.error(`Error Name: ${exception.name}`);
|
||||
this.logger.error(`Error Message: ${exception.message}`);
|
||||
this.logger.error(`Stack Trace:\n${exception.stack}`);
|
||||
} else {
|
||||
this.logger.error(`Exception: ${JSON.stringify(exception)}`);
|
||||
logMessage.push(` Error Name: ${exception.name}`);
|
||||
logMessage.push(` Stack :`);
|
||||
logMessage.push(exception.stack || 'No stack trace');
|
||||
}
|
||||
|
||||
// Request Body 로깅 (민감정보 마스킹)
|
||||
const safeBody = { ...request.body };
|
||||
if (safeBody.password) safeBody.password = '****';
|
||||
if (safeBody.token) safeBody.token = '****';
|
||||
this.logger.error(`Request Body: ${JSON.stringify(safeBody)}`);
|
||||
|
||||
this.logger.error(`===============================`);
|
||||
logMessage.push(` Body : ${JSON.stringify(safeBody)}`);
|
||||
logMessage.push('══════════════════════════════════════════════════════════════════');
|
||||
logMessage.push('');
|
||||
|
||||
// NestJS Logger 사용 (Docker stdout으로 출력)
|
||||
this.logger.error(logMessage.join('\n'));
|
||||
|
||||
// 추가로 console.error도 출력 (백업)
|
||||
console.error(logMessage.join('\n'));
|
||||
|
||||
// process.stdout으로 직접 출력 (버퍼링 우회)
|
||||
process.stdout.write(logMessage.join('\n') + '\n');
|
||||
|
||||
response.status(status).json(errorResponse);
|
||||
}
|
||||
|
||||
@@ -3,50 +3,42 @@ import {
|
||||
NestInterceptor,
|
||||
ExecutionContext,
|
||||
CallHandler,
|
||||
Logger,
|
||||
} from '@nestjs/common';
|
||||
import { Observable } from 'rxjs';
|
||||
import { tap } from 'rxjs/operators';
|
||||
import { Request } from 'express';
|
||||
|
||||
/**
|
||||
* 로깅 인터셉터
|
||||
*
|
||||
* @description
|
||||
* API 요청/응답을 로깅하고 실행 시간을 측정합니다.
|
||||
*
|
||||
* @example
|
||||
* // main.ts에서 전역 적용
|
||||
* app.useGlobalInterceptors(new LoggingInterceptor());
|
||||
*
|
||||
* @export
|
||||
* @class LoggingInterceptor
|
||||
* @implements {NestInterceptor}
|
||||
* 로깅 인터셉터 - Docker 환경에서 로그 출력 보장
|
||||
*/
|
||||
@Injectable()
|
||||
export class LoggingInterceptor implements NestInterceptor {
|
||||
private readonly logger = new Logger('HTTP');
|
||||
|
||||
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
|
||||
const request = context.switchToHttp().getRequest<Request>();
|
||||
const { method, url, ip } = request;
|
||||
const userAgent = request.get('user-agent') || '';
|
||||
const now = Date.now();
|
||||
|
||||
console.log(
|
||||
`[${new Date().toISOString()}] Incoming Request: ${method} ${url} - ${ip} - ${userAgent}`,
|
||||
);
|
||||
const incomingLog = `[REQUEST] ${method} ${url} - IP: ${ip}`;
|
||||
this.logger.log(incomingLog);
|
||||
process.stdout.write(`${new Date().toISOString()} - ${incomingLog}\n`);
|
||||
|
||||
return next.handle().pipe(
|
||||
tap({
|
||||
next: (data) => {
|
||||
next: () => {
|
||||
const responseTime = Date.now() - now;
|
||||
console.log(
|
||||
`[${new Date().toISOString()}] Response: ${method} ${url} - ${responseTime}ms`,
|
||||
);
|
||||
const successLog = `[RESPONSE] ${method} ${url} - ${responseTime}ms - SUCCESS`;
|
||||
this.logger.log(successLog);
|
||||
process.stdout.write(`${new Date().toISOString()} - ${successLog}\n`);
|
||||
},
|
||||
error: (error) => {
|
||||
const responseTime = Date.now() - now;
|
||||
console.error(
|
||||
`[${new Date().toISOString()}] Error Response: ${method} ${url} - ${responseTime}ms - ${error.message}`,
|
||||
);
|
||||
const errorLog = `[RESPONSE] ${method} ${url} - ${responseTime}ms - ERROR: ${error.message}`;
|
||||
this.logger.error(errorLog);
|
||||
process.stdout.write(`${new Date().toISOString()} - ${errorLog}\n`);
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user