Files
Hyungi Ahn 2b1c7bfb88 feat: 다수 기능 개선 - 순찰, 출근, 작업분석, 모바일 UI 등
- 순찰/점검 기능 개선 (zone-detail 페이지 추가)
- 출근/근태 시스템 개선 (연차 조회, 근무현황)
- 작업분석 대분류 그룹화 및 마이그레이션 스크립트
- 모바일 네비게이션 UI 추가
- NAS 배포 도구 및 문서 추가

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 14:41:01 +09:00

198 lines
4.2 KiB
JavaScript
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 로깅 유틸리티
*
* 애플리케이션 전체에서 사용하는 통합 로거
*
* @author TK-FB-Project
* @since 2025-12-11
*/
const fs = require('fs');
const path = require('path');
/**
* 로그 레벨 정의
*/
const LogLevel = {
ERROR: 'ERROR',
WARN: 'WARN',
INFO: 'INFO',
DEBUG: 'DEBUG'
};
/**
* 로그 레벨별 이모지
*/
const LogEmoji = {
ERROR: '❌',
WARN: '⚠️',
INFO: '',
DEBUG: '🔍'
};
/**
* 로그 레벨별 색상 (콘솔)
*/
const LogColor = {
ERROR: '\x1b[31m', // Red
WARN: '\x1b[33m', // Yellow
INFO: '\x1b[36m', // Cyan
DEBUG: '\x1b[90m', // Gray
RESET: '\x1b[0m'
};
class Logger {
constructor() {
this.logDir = path.join(__dirname, '../logs');
this.logFile = path.join(this.logDir, 'app.log');
this.errorFile = path.join(this.logDir, 'error.log');
this.ensureLogDirectory();
}
/**
* 로그 디렉토리 생성
*/
ensureLogDirectory() {
if (!fs.existsSync(this.logDir)) {
fs.mkdirSync(this.logDir, { recursive: true });
}
}
/**
* 타임스탬프 생성
*/
getTimestamp() {
return new Date().toISOString();
}
/**
* 로그 포맷팅
*/
formatLog(level, message, context = {}) {
const timestamp = this.getTimestamp();
const emoji = LogEmoji[level] || '';
const contextStr = Object.keys(context).length > 0
? `\n Context: ${JSON.stringify(context, null, 2)}`
: '';
return `[${timestamp}] [${level}] ${emoji} ${message}${contextStr}`;
}
/**
* 콘솔에 컬러 로그 출력
*/
logToConsole(level, message, context = {}) {
const color = LogColor[level] || LogColor.RESET;
const formattedLog = this.formatLog(level, message, context);
if (level === LogLevel.ERROR) {
console.error(`${color}${formattedLog}${LogColor.RESET}`);
} else if (level === LogLevel.WARN) {
console.warn(`${color}${formattedLog}${LogColor.RESET}`);
} else {
console.log(`${color}${formattedLog}${LogColor.RESET}`);
}
}
/**
* 파일에 로그 기록
*/
logToFile(level, message, context = {}) {
const formattedLog = this.formatLog(level, message, context);
const logEntry = `${formattedLog}\n`;
try {
// 모든 로그를 app.log에 기록
fs.appendFileSync(this.logFile, logEntry, 'utf8');
// 에러는 error.log에도 기록
if (level === LogLevel.ERROR) {
fs.appendFileSync(this.errorFile, logEntry, 'utf8');
}
} catch (err) {
console.error('로그 파일 기록 실패:', err);
}
}
/**
* 로그 기록 메인 함수
*/
log(level, message, context = {}) {
// 개발 환경에서는 콘솔에 출력
if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV !== 'production') {
this.logToConsole(level, message, context);
}
// 프로덕션에서는 파일에만 기록
if (process.env.NODE_ENV === 'production') {
this.logToFile(level, message, context);
}
}
/**
* 에러 로그
*/
error(message, context = {}) {
this.log(LogLevel.ERROR, message, context);
}
/**
* 경고 로그
*/
warn(message, context = {}) {
this.log(LogLevel.WARN, message, context);
}
/**
* 정보 로그
*/
info(message, context = {}) {
this.log(LogLevel.INFO, message, context);
}
/**
* 디버그 로그
*/
debug(message, context = {}) {
// DEBUG 로그는 개발 환경에서만 출력
if (process.env.NODE_ENV === 'development') {
this.log(LogLevel.DEBUG, message, context);
}
}
/**
* HTTP 요청 로그
*/
http(method, url, statusCode, duration, user = 'anonymous') {
const level = statusCode >= 400 ? LogLevel.ERROR : LogLevel.INFO;
const message = `${method} ${url} - ${statusCode} (${duration}ms)`;
const context = {
method,
url,
statusCode,
duration,
user
};
this.log(level, message, context);
}
/**
* 데이터베이스 쿼리 로그
*/
query(sql, params = [], duration = 0) {
if (process.env.NODE_ENV === 'development') {
this.debug('DB Query', {
sql,
params,
duration: `${duration}ms`
});
}
}
}
// 싱글톤 인스턴스 생성 및 내보내기
const logger = new Logger();
module.exports = logger;