98 lines
2.8 KiB
JavaScript
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 };
|