guanghulab/scripts/process-syslog.js

139 lines
5.2 KiB
JavaScript
Raw Permalink Normal View History

// 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}`);