diff --git a/.gitignore b/.gitignore index bbffc8a..0ebef99 100644 --- a/.gitignore +++ b/.gitignore @@ -45,4 +45,8 @@ next-env.d.ts bun.lockb pnpm-lock.yaml -pnpm-workspace.yaml \ No newline at end of file +pnpm-workspace.yaml + +# logs +logs/ +*.log diff --git a/next.config.ts b/next.config.ts index 58db81e..6733d06 100644 --- a/next.config.ts +++ b/next.config.ts @@ -6,7 +6,12 @@ const nextConfig: NextConfig = { sassOptions: { includePaths: [path.join(__dirname, './src/styles')], }, - serverExternalPackages: ['@react-pdf/renderer'], + serverExternalPackages: ['@react-pdf/renderer', 'pino'], + logging: { + fetches: { + fullUrl: true, + }, + }, async rewrites() { return [ { diff --git a/package.json b/package.json index 439c5df..e309ff6 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "next": "15.2.4", "nodemailer": "^7.0.3", "pdf-lib": "^1.17.1", + "pino": "^9.7.0", "react": "^19.0.0", "react-dom": "^19.0.0", "react-to-pdf": "^2.0.0", diff --git a/src/libs/logger.ts b/src/libs/logger.ts new file mode 100644 index 0000000..5901429 --- /dev/null +++ b/src/libs/logger.ts @@ -0,0 +1,51 @@ +import { NextRequest } from 'next/server' +import { join } from 'path' +import pino from 'pino' + +/* 로그 파일 경로 */ +const logFilePath = join('.', 'logs', 'onsite-survey.log') + +/* 로거 생성 함수 */ +const createLogger = (): pino.Logger => { + try { + return pino({ + level: 'info', + timestamp: pino.stdTimeFunctions.isoTime, + transport: { + targets: [ + { + target: 'pino/file', + options: { + destination: logFilePath, + mkdir: true, + sync: false, + }, + }, + ], + }, + }) + } catch (error) { + console.error('Pino transport 생성 실패, 기본 로거 사용:', error) + return pino({ + level: 'info', + timestamp: pino.stdTimeFunctions.isoTime, + }) + } +} + +/* 로거 인스턴스 */ +export const logger = createLogger() + +/* 로그 기록 함수 */ +export const writeLog = async (request: NextRequest, responseStatus: number): Promise => { + const logData = { + timestamp: new Date().toISOString(), + status: responseStatus, + method: request.method, + url: request.url, + // headers: Object.fromEntries(request.headers), + query: Object.fromEntries(new URL(request.url).searchParams), + body: request.body ? await request.clone().text() : undefined, + } + logger.info(logData, 'API Request') +}