98 lines
2.8 KiB
JavaScript

// ═══════════════════════════════════════
// 铸渊开发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 };