@racla-dev/node-iris
Version:
TypeScript port of Python irispy-client module for KakaoTalk bot development
242 lines • 10.1 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.winstonLogger = exports.defaultLogger = exports.Logger = void 0;
const lossless_json_1 = require("lossless-json");
const path_1 = __importDefault(require("path"));
const winston_1 = __importDefault(require("winston"));
// 순환 참조를 처리하는 stringify replacer 함수
function getCircularReplacer() {
const seen = new WeakSet();
return (key, value) => {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) {
return '[Circular]';
}
seen.add(value);
}
// 함수는 직렬화하지 않음
if (typeof value === 'function') {
return '[Function]';
}
// BigInt는 문자열로 변환
if (typeof value === 'bigint') {
return value.toString();
}
// undefined는 null로 변환
if (value === undefined) {
return null;
}
return value;
};
}
// 로그 포맷 설정
const logFormat = winston_1.default.format.combine(winston_1.default.format.timestamp({
format: 'YYYY-MM-DD HH:mm:ss',
}), winston_1.default.format.errors({ stack: true }), winston_1.default.format.printf(({ timestamp, level, message, stack, ...meta }) => {
let log = `[${timestamp}] [${level.toUpperCase()}] ${message}`;
// 메타데이터가 있으면 추가 (안전한 stringify 사용)
if (Object.keys(meta).length > 0) {
try {
log += ' ' + (0, lossless_json_1.stringify)(meta, getCircularReplacer());
}
catch (error) {
log += ' [Medata serialization error]';
}
}
// 스택 트레이스가 있으면 추가
if (stack) {
log += '\n' + stack;
}
return log;
}));
// 기본 로거 생성
const logger = winston_1.default.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: logFormat,
transports: [
// 콘솔 출력 - 컬러 포맷 분리
new winston_1.default.transports.Console({
format: winston_1.default.format.combine(winston_1.default.format.timestamp({
format: 'YYYY-MM-DD HH:mm:ss',
}), winston_1.default.format.colorize(), winston_1.default.format.printf(({ timestamp, level, message, stack, ...meta }) => {
let log = `[${timestamp}] [${level}] ${message}`;
// 메타데이터가 있으면 추가 (안전한 stringify 사용)
if (Object.keys(meta).length > 0) {
try {
log += ' ' + (0, lossless_json_1.stringify)(meta, getCircularReplacer());
}
catch (error) {
log += ' [메타데이터 직렬화 오류]';
}
}
// 스택 트레이스가 있으면 추가
if (stack) {
log += '\n' + stack;
}
return log;
})),
}),
// 파일 출력 - 모든 로그 (채팅 로그 제외)
new winston_1.default.transports.File({
filename: path_1.default.join(process.cwd(), 'logs', 'app.log'),
maxsize: 5242880, // 5MB
maxFiles: 5,
format: winston_1.default.format.combine(winston_1.default.format((info) => {
// 채팅 로그는 파일에 저장하지 않음
if (typeof info.message === 'string' &&
info.message.includes('[MSG]')) {
return false;
}
return info;
})(), logFormat),
}),
// 파일 출력 - 에러만
new winston_1.default.transports.File({
filename: path_1.default.join(process.cwd(), 'logs', 'error.log'),
level: 'error',
maxsize: 5242880, // 5MB
maxFiles: 5,
}),
],
// 처리되지 않은 예외 캐치
exceptionHandlers: [
new winston_1.default.transports.File({
filename: path_1.default.join(process.cwd(), 'logs', 'exceptions.log'),
}),
],
// 처리되지 않은 Promise rejection 캐치
rejectionHandlers: [
new winston_1.default.transports.File({
filename: path_1.default.join(process.cwd(), 'logs', 'rejections.log'),
}),
],
});
exports.winstonLogger = logger;
// 로거 래퍼 클래스
class Logger {
constructor(context = 'App', options = {}) {
this.context = context;
this.saveChatLogs = options.saveChatLogs || false;
this.logLevel = options.logLevel || 'info';
// 개별 winston 인스턴스 생성 (logLevel 적용)
this.winstonLogger = winston_1.default.createLogger({
level: this.logLevel,
format: logFormat,
transports: [
// 콘솔 출력 - 컬러 포맷 분리
new winston_1.default.transports.Console({
format: winston_1.default.format.combine(winston_1.default.format.timestamp({
format: 'YYYY-MM-DD HH:mm:ss',
}), winston_1.default.format.colorize(), winston_1.default.format.printf(({ timestamp, level, message, stack, ...meta }) => {
let log = `[${timestamp}] [${level}] ${message}`;
// 메타데이터가 있으면 추가 (안전한 stringify 사용)
if (Object.keys(meta).length > 0) {
try {
log += ' ' + (0, lossless_json_1.stringify)(meta, getCircularReplacer());
}
catch (error) {
log += ' [메타데이터 직렬화 오류]';
}
}
// 스택 트레이스가 있으면 추가
if (stack) {
log += '\n' + stack;
}
return log;
})),
}),
// 파일 출력 - 모든 로그 (채팅 로그 제외)
new winston_1.default.transports.File({
filename: path_1.default.join(process.cwd(), 'logs', 'app.log'),
maxsize: 5242880, // 5MB
maxFiles: 5,
format: winston_1.default.format.combine(winston_1.default.format((info) => {
// 채팅 로그는 파일에 저장하지 않음
if (typeof info.message === 'string' &&
info.message.includes('[MSG]')) {
return false;
}
return info;
})(), logFormat),
}),
// 파일 출력 - 에러만
new winston_1.default.transports.File({
filename: path_1.default.join(process.cwd(), 'logs', 'error.log'),
level: 'error',
maxsize: 5242880, // 5MB
maxFiles: 5,
}),
],
// 개별 로거는 예외 핸들러를 등록하지 않음 (메모리 누수 방지)
silent: false,
});
// 채팅 로그 저장이 활성화된 경우 별도 로거 생성
if (this.saveChatLogs) {
this.chatLogger = winston_1.default.createLogger({
level: this.logLevel,
format: logFormat,
transports: [
new winston_1.default.transports.File({
filename: path_1.default.join(process.cwd(), 'logs', 'chat.log'),
maxsize: 5242880, // 5MB
maxFiles: 5,
}),
],
});
}
}
formatMessage(message) {
return `[${this.context}] ${message}`;
}
error(message, error, meta) {
if (error instanceof Error) {
this.winstonLogger.error(this.formatMessage(message), {
error: error.message,
stack: error.stack,
...meta,
});
}
else if (error) {
this.winstonLogger.error(this.formatMessage(message), { error, ...meta });
}
else {
this.winstonLogger.error(this.formatMessage(message), meta);
}
}
warn(message, meta) {
this.winstonLogger.warn(this.formatMessage(message), meta);
}
info(message, meta) {
this.winstonLogger.info(this.formatMessage(message), meta);
}
debug(message, meta) {
this.winstonLogger.debug(this.formatMessage(message), meta);
}
// 채팅 관련 특별한 로그 메서드들
chat(type, roomName, senderName, message) {
const logMessage = this.formatMessage(`[${type.replace(' | ', '] [')}] [${roomName}] ${senderName}: ${message}`);
// 콘솔에는 항상 출력
this.winstonLogger.info(logMessage);
// 파일 저장은 옵션에 따라
if (this.saveChatLogs && this.chatLogger) {
this.chatLogger.info(logMessage);
}
}
command(roomName, senderName, command) {
this.winstonLogger.info(this.formatMessage(`[CMD] [${roomName}] ${senderName} executed command: ${command}`));
}
newMember(roomName, memberName) {
this.winstonLogger.info(this.formatMessage(`[NEWMEM] [${roomName}]: ${memberName}`));
}
delMember(roomName, memberName) {
this.winstonLogger.info(this.formatMessage(`[DELMEM] [${roomName}]: ${memberName}`));
}
}
exports.Logger = Logger;
// 기본 로거 인스턴스 생성
exports.defaultLogger = new Logger('IrisBot');
exports.default = Logger;
//# sourceMappingURL=logger.js.map