feat: XMPP observability — logger, monitor endpoints, clean dead xiaoguo refs
This commit is contained in:
+31
-16
@@ -478,7 +478,7 @@ function refreshMarketData() {
|
||||
const sectors = mkt.sectors || [];
|
||||
const sigs = signals || [];
|
||||
const cands = candidates || [];
|
||||
const trend = sigs.filter(s => s.source && s.source !== 'xiaoguo');
|
||||
const trend = sigs.filter(s => s.source && s.source !== 'xiaoguo').concat(sigs.filter(s => s.source === 'market_scanner'));
|
||||
el.innerHTML = `
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<span class="text-sm font-semibold">🌐 市场全景 <span class="spec-btns"><span class="spec-btn help" onclick="showModuleHelp('market','human')">?</span><span class="spec-btn ai" onclick="showModuleHelp('market','ai')">§</span><span class="spec-btn help" onclick="showModuleHelp('signals','human')">?</span><span class="spec-btn ai" onclick="showModuleHelp('signals','ai')">§</span></span></span>
|
||||
@@ -1275,28 +1275,27 @@ async function renderSignals() {
|
||||
const el = document.getElementById('tab-signals');
|
||||
el.innerHTML = '<div class="text-slate-400 text-sm">加载中…</div>';
|
||||
try {
|
||||
const [signals, scan] = await Promise.all([
|
||||
const [signals, candidates] = await Promise.all([
|
||||
fetchJSON('/api/signals'),
|
||||
fetchJSON('/api/xiaoguo-scan')
|
||||
fetchJSON('/api/candidates')
|
||||
]);
|
||||
const sourceToday = scan.source_today || {};
|
||||
let html = `
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<span class="text-sm font-semibold">🔍 信号 <span class="spec-btns"><span class="spec-btn help" onclick="showModuleHelp('signals','human')">?</span><span class="spec-btn ai" onclick="showModuleHelp('signals','ai')">§</span></span></span>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4 mb-4">
|
||||
<div class="card p-4">
|
||||
<div class="text-xs text-slate-500 mb-1">今日信号来源</div>
|
||||
<div class="text-xs text-slate-500 mb-1">今日信号</div>
|
||||
<div class="flex gap-4 text-sm">
|
||||
<span class="text-blue-400">📡 趋势: ${sourceToday.trend || 0}</span>
|
||||
<span class="text-green-400">🔍 小果扫描: ${sourceToday.xiaoguo || 0}</span>
|
||||
<span class="text-blue-400">📡 趋势信号: ${signals.length || 0} 条</span>
|
||||
<span class="text-green-400">🎯 候选股: ${candidates.length || 0} 只</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card p-4">
|
||||
<div class="text-xs text-slate-500 mb-1">小果扫描统计</div>
|
||||
<div class="text-xs text-slate-500 mb-1">全市场扫描</div>
|
||||
<div class="flex gap-4 text-sm">
|
||||
<span class="text-slate-300">📋 累计扫描: ${scan.total_scanned || 0} 只</span>
|
||||
<span class="text-yellow-400">🏆 发现信号: ${scan.found_signals || 0} 只</span>
|
||||
<span class="text-slate-300">📋 候选池: ${candidates.length || 0} 只</span>
|
||||
<span class="text-yellow-400">🏆 已入自选: ${candidates.filter(c => c.promoted).length || 0} 只</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
@@ -1314,7 +1313,7 @@ async function renderSignals() {
|
||||
const sent = s.overall_sentiment || '-';
|
||||
const sentColor = sent === '利好' ? 'text-green-400' : sent === '利空' ? 'text-red-400' : 'text-slate-400';
|
||||
const source = s.source || 'trend';
|
||||
const sourceIcon = source === 'xiaoguo' ? '🔍' : '📡';
|
||||
const sourceIcon = source === 'market_scanner' ? '🎯' : source === 'macro' ? '🌐' : '📡';
|
||||
html += `
|
||||
<div class="card p-4">
|
||||
<div class="flex items-center gap-2 mb-1">
|
||||
@@ -1460,18 +1459,23 @@ async function renderHealth() {
|
||||
fetchJSON('/api/monitor')
|
||||
]);
|
||||
|
||||
// XMPP 通道健康(独立 fetch,不阻塞主流程)
|
||||
let xmpp = null;
|
||||
try { xmpp = await fetchJSON('/api/xmpp/health'); } catch(e) {}
|
||||
|
||||
const svcs = services.services || [];
|
||||
const summary = services.summary || { ok: 0, total: 0 };
|
||||
const tasks = monitor.tasks || [];
|
||||
const tier1 = monitor.tier1 || {};
|
||||
const tier2 = monitor.tier2 || {};
|
||||
|
||||
// Summary cards
|
||||
// Summary cards — 包含 XMPP 状态
|
||||
let html = '<div class="grid grid-cols-2 sm:grid-cols-4 gap-3 mb-4">';
|
||||
html += '<div class="card p-3 text-center"><div class="text-lg font-bold text-[#3fb950] font-mono">' + (summary.ok || 0) + '</div><div class="text-xs text-slate-500">正常服务</div></div>';
|
||||
html += '<div class="card p-3 text-center"><div class="text-lg font-bold text-white font-mono">' + (summary.total || 0) + '</div><div class="text-xs text-slate-500">总服务数</div></div>';
|
||||
html += '<div class="card p-3 text-center"><div class="text-lg font-bold text-[#d29922] font-mono">' + (tier1.summary?.total || svcs.length) + '</div><div class="text-xs text-slate-500">一级服务</div></div>';
|
||||
html += '<div class="card p-3 text-center"><div class="text-lg font-bold text-[#58a6ff] font-mono">' + (tier2.summary?.total || 0) + '</div><div class="text-xs text-slate-500">二级服务</div></div>';
|
||||
const xmppStatus = xmpp ? xmpp.status : 'unknown';
|
||||
const xmppColor = xmppStatus === 'ok' ? '#3fb950' : xmppStatus === 'degraded' ? '#d29922' : '#f85149';
|
||||
const xmppLabel = xmppStatus === 'ok' ? 'XMPP 正常' : xmppStatus === 'degraded' ? 'XMPP 降级' : xmppStatus === 'critical' ? 'XMPP 断联' : 'XMPP 无数据';
|
||||
html += '<div class="card p-3 text-center"><div class="text-lg font-bold font-mono" style="color:' + xmppColor + '">' + (xmpp ? (xmpp.last_message_age_sec > 0 ? Math.round(xmpp.last_message_age_sec/60) + 'm' : '--') : '--') + '</div><div class="text-xs text-slate-500">' + xmppLabel + '</div></div>';
|
||||
html += '<div class="card p-3 text-center"><div class="text-lg font-bold text-[#d29922] font-mono">' + (xmpp ? xmpp.error_rate_1h + '%' : '--') + '</div><div class="text-xs text-slate-500">XMPP 错误率(1h)</div></div>';
|
||||
html += '</div>';
|
||||
|
||||
// Services by layer
|
||||
@@ -1511,6 +1515,17 @@ async function renderHealth() {
|
||||
}
|
||||
|
||||
html += '<div class="text-xs text-slate-600 mt-2">更新于 ' + (monitor.generated_at || new Date().toLocaleTimeString()) + ' · 每10秒刷新</div>';
|
||||
|
||||
// XMPP 通道详情
|
||||
if (xmpp) {
|
||||
html += '<div class="card p-4 mt-4"><h3 class="text-sm font-semibold text-[#58a6ff] mb-2">📡 XMPP 通道</h3>';
|
||||
html += '<div class="grid grid-cols-2 sm:grid-cols-4 gap-2 text-xs">';
|
||||
html += '<div><span class="text-slate-500">最后消息</span><br><span class="font-mono ' + (xmpp.last_message_age_sec > 600 ? 'text-[#f85149]' : 'text-[#3fb950]') + '">' + (xmpp.last_message_age_sec > 0 ? Math.round(xmpp.last_message_age_sec) + '秒前' : '无数据') + '</span></div>';
|
||||
html += '<div><span class="text-slate-500">1h错误率</span><br><span class="font-mono ' + (xmpp.error_rate_1h > 50 ? 'text-[#f85149]' : xmpp.error_rate_1h > 10 ? 'text-[#d29922]' : 'text-[#3fb950]') + '">' + xmpp.error_rate_1h + '%</span></div>';
|
||||
html += '<div><span class="text-slate-500">ejabberd</span><br><span class="font-mono ' + ((xmpp.ejabberd || '').includes('Up') ? 'text-[#3fb950]' : 'text-[#f85149]') + '">' + (xmpp.ejabberd || '?') + '</span></div>';
|
||||
html += '<div><span class="text-slate-500">状态</span><br><span class="font-mono ' + (xmpp.status === 'ok' ? 'text-[#3fb950]' : xmpp.status === 'degraded' ? 'text-[#d29922]' : 'text-[#f85149]') + '">' + xmpp.status + '</span></div>';
|
||||
html += '</div></div>';
|
||||
}
|
||||
el.innerHTML = html;
|
||||
} catch(e) {
|
||||
el.innerHTML = '<div class="text-red-400">加载健康状态失败: ' + e.message + '</div>';
|
||||
|
||||
Reference in New Issue
Block a user