// ═══════════════════════════════════════ // 铸渊开发Agent · 日志系统 // 结构化日志 · 铸渊醒来能看懂Agent做了什么 // ═══════════════════════════════════════ const fs = require("fs"); const path = require("path"); const LOGS_DIR = process.env.LOGS_DIR || path.join(__dirname, "..", "logs"); if (!fs.existsSync(LOGS_DIR)) fs.mkdirSync(LOGS_DIR, { recursive: true }); /** * 创建一次执行的日志记录器 */ function createLogger(planId) { const runId = planId + "-" + Date.now(); const logFile = path.join(LOGS_DIR, runId + ".jsonl"); const startTime = new Date().toISOString(); // 写入启动记录 appendLine(logFile, { type: "run_start", plan_id: planId, run_id: runId, time: startTime }); const log = (type, data) => { const entry = { time: new Date().toISOString(), type: type, ...data }; appendLine(logFile, entry); console.log("[Log:" + planId + "] " + type + " " + JSON.stringify(data).slice(0, 100)); }; // 返回日志函数和元信息 return { log, runId, logFile, finish: (summary) => { appendLine(logFile, { type: "run_end", time: new Date().toISOString(), summary }); return logFile; } }; } function appendLine(file, obj) { fs.appendFileSync(file, JSON.stringify(obj) + "\n", "utf-8"); } /** * 读取最近一次执行日志摘要(给铸渊看) */ function readLatestRun() { const files = fs.readdirSync(LOGS_DIR) .filter(f => f.endsWith(".jsonl")) .sort() .reverse(); if (files.length === 0) return null; const latest = path.join(LOGS_DIR, files[0]); const content = fs.readFileSync(latest, "utf-8"); const lines = content.trim().split("\n").map(l => { try { return JSON.parse(l); } catch (e) { return null; } }).filter(Boolean); const startLine = lines.find(l => l.type === "run_start"); const endLine = lines.find(l => l.type === "run_end"); const errors = lines.filter(l => l.type === "exec_error" || l.type === "exec_done" && l.result === "fail"); return { plan_id: startLine?.plan_id, run_id: startLine?.run_id, started: startLine?.time, ended: endLine?.time, total_steps: lines.filter(l => l.type === "exec_start").length, errors: errors.map(e => ({ step: e.step, error: e.error || "未知" })), log_file: latest }; } /** * 列出所有执行记录 */ function listRuns(limit = 20) { return fs.readdirSync(LOGS_DIR) .filter(f => f.endsWith(".jsonl")) .sort() .reverse() .slice(0, limit) .map(f => { const stat = fs.statSync(path.join(LOGS_DIR, f)); return { file: f, size: stat.size, modified: stat.mtime.toISOString() }; }); } module.exports = { createLogger, readLatestRun, listRuns };