139 lines
5.2 KiB
JavaScript
139 lines
5.2 KiB
JavaScript
|
|
// scripts/process-syslog.js
|
|||
|
|
// 铸渊 SYSLOG Pipeline
|
|||
|
|
// 读取 syslog-inbox/ 下的日志条目,处理后归档到 syslog-processed/
|
|||
|
|
// 触发:syslog-inbox/ 目录有新文件 push 到 main 分支
|
|||
|
|
|
|||
|
|
'use strict';
|
|||
|
|
|
|||
|
|
const fs = require('fs');
|
|||
|
|
const path = require('path');
|
|||
|
|
|
|||
|
|
const INBOX_DIR = 'syslog-inbox';
|
|||
|
|
const ARCHIVE_DIR = 'syslog-processed';
|
|||
|
|
const BRAIN_DIR = '.github/brain';
|
|||
|
|
const MEMORY_PATH = path.join(BRAIN_DIR, 'memory.json');
|
|||
|
|
const DEV_STATUS_PATH = '.github/persona-brain/dev-status.json';
|
|||
|
|
|
|||
|
|
// ─── 加载大脑 ────────────────────────────────────────────
|
|||
|
|
let memory = {};
|
|||
|
|
try {
|
|||
|
|
memory = JSON.parse(fs.readFileSync(MEMORY_PATH, 'utf8'));
|
|||
|
|
} catch (_) {
|
|||
|
|
memory = { events: [], stats: { broadcasts_processed: 0 } };
|
|||
|
|
}
|
|||
|
|
if (!memory.events) memory.events = [];
|
|||
|
|
if (!memory.stats) memory.stats = {};
|
|||
|
|
if (!memory.stats.broadcasts_processed) memory.stats.broadcasts_processed = 0;
|
|||
|
|
if (!memory.stats.syslog_processed) memory.stats.syslog_processed = 0;
|
|||
|
|
|
|||
|
|
// ─── 扫描 inbox ──────────────────────────────────────────
|
|||
|
|
if (!fs.existsSync(INBOX_DIR)) {
|
|||
|
|
console.log('📭 syslog-inbox/ 目录不存在,跳过');
|
|||
|
|
process.exit(0);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const files = fs.readdirSync(INBOX_DIR)
|
|||
|
|
.filter(f => f.endsWith('.json') && f !== '.gitkeep');
|
|||
|
|
|
|||
|
|
if (files.length === 0) {
|
|||
|
|
console.log('📭 syslog-inbox/ 无待处理条目');
|
|||
|
|
process.exit(0);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log(`📥 发现 ${files.length} 条 syslog 条目,开始处理...\n`);
|
|||
|
|
|
|||
|
|
// ─── 按月归档路径 ─────────────────────────────────────────
|
|||
|
|
function archiveDir(timestamp) {
|
|||
|
|
const month = (timestamp || new Date().toISOString()).slice(0, 7); // "2026-03"
|
|||
|
|
const dir = path.join(ARCHIVE_DIR, month);
|
|||
|
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|||
|
|
return dir;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ─── 处理每条 syslog ──────────────────────────────────────
|
|||
|
|
files.forEach(file => {
|
|||
|
|
const fullPath = path.join(INBOX_DIR, file);
|
|||
|
|
let entry;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
entry = JSON.parse(fs.readFileSync(fullPath, 'utf8'));
|
|||
|
|
} catch (e) {
|
|||
|
|
console.error(`❌ [INVALID JSON] ${file}: ${e.message},跳过`);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log(`📋 处理: ${file}`);
|
|||
|
|
console.log(` 类型: ${entry.type || '未知'} · 来自: ${entry.from || '未知'} · 标题: ${entry.title || ''}`);
|
|||
|
|
|
|||
|
|
const event = {
|
|||
|
|
timestamp: entry.timestamp || new Date().toISOString(),
|
|||
|
|
type: 'syslog_' + (entry.type || 'unknown'),
|
|||
|
|
syslog_id: entry.syslog_id || file,
|
|||
|
|
title: entry.title || '(无标题)',
|
|||
|
|
from: entry.from || '未知',
|
|||
|
|
file,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// ── 根据类型分发处理 ──
|
|||
|
|
switch (entry.type) {
|
|||
|
|
|
|||
|
|
case 'broadcast': {
|
|||
|
|
// 广播类:追加到 brain/memory.json events,与现有 broadcast pipeline 互补
|
|||
|
|
memory.stats.broadcasts_processed += 1;
|
|||
|
|
event.content_preview = (entry.content || '').slice(0, 80);
|
|||
|
|
console.log(' ✅ 广播已记录到大脑记忆');
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
case 'auth': {
|
|||
|
|
// 授权类:记录授权事件,写入 memory
|
|||
|
|
event.target_dev_id = entry.target_dev_id;
|
|||
|
|
event.target_name = entry.target_name;
|
|||
|
|
event.permission_level = entry.permission_level;
|
|||
|
|
event.authorized_by = entry.authorized_by || '冰朔';
|
|||
|
|
event.valid_until = entry.valid_until;
|
|||
|
|
console.log(` ✅ 授权记录:${entry.target_name}(${entry.target_dev_id})· 权限:${entry.permission_level}`);
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
case 'inspect': {
|
|||
|
|
// 巡检类:记录巡检结果
|
|||
|
|
event.inspect_result = entry.result || 'unknown';
|
|||
|
|
event.issues_found = entry.issues_found || 0;
|
|||
|
|
console.log(` ✅ 巡检记录:结果 ${event.inspect_result} · 发现问题 ${event.issues_found} 个`);
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
case 'alert': {
|
|||
|
|
// 告警类
|
|||
|
|
event.severity = entry.priority || 'normal';
|
|||
|
|
console.log(` ⚠️ 告警记录:${entry.title} · 优先级 ${event.severity}`);
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
default:
|
|||
|
|
console.log(` ℹ️ 未知类型 "${entry.type}",已记录原始内容`);
|
|||
|
|
event.raw = entry;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 写入大脑记忆
|
|||
|
|
memory.events.push(event);
|
|||
|
|
memory.stats.syslog_processed = (memory.stats.syslog_processed || 0) + 1;
|
|||
|
|
memory.last_updated = new Date().toISOString();
|
|||
|
|
|
|||
|
|
// 归档文件
|
|||
|
|
const destDir = archiveDir(entry.timestamp);
|
|||
|
|
const destPath = path.join(destDir, file);
|
|||
|
|
fs.renameSync(fullPath, destPath);
|
|||
|
|
console.log(` 📦 已归档到 ${destPath}\n`);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// ─── 保存大脑 ─────────────────────────────────────────────
|
|||
|
|
// 只保留最近 100 条事件,防止 memory.json 无限膨胀
|
|||
|
|
if (memory.events.length > 100) {
|
|||
|
|
memory.events = memory.events.slice(-100);
|
|||
|
|
}
|
|||
|
|
fs.writeFileSync(MEMORY_PATH, JSON.stringify(memory, null, 2));
|
|||
|
|
|
|||
|
|
console.log(`✅ SYSLOG Pipeline 完成 · 累计处理 ${memory.stats.syslog_processed} 条`);
|