From 6a24b997dd0113271e74ad6f21ea861f72e5609b Mon Sep 17 00:00:00 2001 From: caadiq Date: Tue, 26 May 2026 17:02:53 +0900 Subject: [PATCH] =?UTF-8?q?feat(logs):=20=ED=99=9C=EB=8F=99=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=20=EB=8B=A4=EC=9D=B4=EC=96=BC=EB=A1=9C=EA=B7=B8=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=20=EC=A0=95=EB=B3=B4=20=ED=91=9C=EC=8B=9C=20?= =?UTF-8?q?+=20=EC=97=90=EB=9F=AC=20=EC=A0=95=EB=B3=B4=20=EB=B3=B4?= =?UTF-8?q?=EA=B0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - logs API: longtext로 저장된 details를 JSON 객체로 파싱해 반환 - 응답 스키마에 additionalProperties: true 추가 (fast-json-stringify가 스키마 미정의 키를 제거하던 문제 해결) - scheduler 에러 로그: err.cause / err.code / err.causeCode 함께 기록 (fetch failed 등 모호한 메시지의 진짜 원인 식별 가능) Co-Authored-By: Claude Opus 4.7 --- backend/src/plugins/scheduler.js | 18 ++++++++++++++++-- backend/src/routes/admin/logs.js | 15 +++++++++++++-- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/backend/src/plugins/scheduler.js b/backend/src/plugins/scheduler.js index c759c3a..caa03a9 100644 --- a/backend/src/plugins/scheduler.js +++ b/backend/src/plugins/scheduler.js @@ -9,6 +9,20 @@ const REDIS_PREFIX = 'bot:status:'; const TIMEZONE = 'Asia/Seoul'; const MAX_CONSECUTIVE_ERRORS = 10; +/** + * 에러 객체에서 활동 로그용 details 구성 + * - err.cause(Node fetch failed의 진짜 원인 등), err.code를 함께 포함 + */ +function buildErrorDetails(err) { + const d = { error: err.message }; + if (err.code) d.code = err.code; + if (err.cause) { + d.cause = err.cause.message || String(err.cause); + if (err.cause.code) d.causeCode = err.cause.code; + } + return d; +} + async function schedulerPlugin(fastify, opts) { const tasks = new Map(); const burstTimers = new Map(); // weekly 모드 내부 setInterval 핸들 @@ -269,7 +283,7 @@ async function schedulerPlugin(fastify, opts) { action: 'error', category: 'sync', summary: `${botId} 동기화 오류: ${err.message}`, - details: { error: err.message }, + details: buildErrorDetails(err), }); } if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) { @@ -279,7 +293,7 @@ async function schedulerPlugin(fastify, opts) { action: 'stop', category: 'bot', summary: `${botId} 연속 ${MAX_CONSECUTIVE_ERRORS}회 실패로 자동 정지`, - details: { error: err.message, consecutiveErrors }, + details: { ...buildErrorDetails(err), consecutiveErrors }, }); try { await stopBot(botId); diff --git a/backend/src/routes/admin/logs.js b/backend/src/routes/admin/logs.js index bdd9e19..e0260d6 100644 --- a/backend/src/routes/admin/logs.js +++ b/backend/src/routes/admin/logs.js @@ -77,7 +77,7 @@ export default async function logsRoutes(fastify) { target_type: { type: 'string', nullable: true }, target_id: { type: 'integer', nullable: true }, summary: { type: 'string' }, - details: { type: 'object', nullable: true }, + details: { type: 'object', nullable: true, additionalProperties: true }, created_at: { type: 'string' }, }, }, @@ -147,8 +147,19 @@ export default async function logsRoutes(fastify) { [...params, limit, offset] ); + // details는 longtext(JSON 문자열)로 저장되어 있으므로 객체로 파싱 + const parsedLogs = logs.map(log => { + if (!log.details) return log; + if (typeof log.details === 'object') return log; + try { + return { ...log, details: JSON.parse(log.details) }; + } catch { + return { ...log, details: { raw: log.details } }; + } + }); + return { - logs, + logs: parsedLogs, total, page, limit,