@@ -18,113 +18,41 @@
|
||||
</div>
|
||||
|
||||
<el-row :gutter="16" class="overview-grid">
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="4">
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<el-card class="overview-card overview-card--primary" shadow="hover">
|
||||
<div class="overview-label">插件总数</div>
|
||||
<div class="overview-value">{% raw %}{{ plugins.length }}{% endraw %}</div>
|
||||
<div class="overview-note">当前已注册插件模块</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="4">
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<el-card class="overview-card" shadow="hover">
|
||||
<div class="overview-label">运行中</div>
|
||||
<div class="overview-value">{% raw %}{{ runningPluginsCount }}{% endraw %}</div>
|
||||
<div class="overview-note">可正常提供能力的插件</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="4">
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<el-card class="overview-card" shadow="hover">
|
||||
<div class="overview-label">已停用</div>
|
||||
<div class="overview-value">{% raw %}{{ stoppedPluginsCount }}{% endraw %}</div>
|
||||
<div class="overview-note">待启用或排查状态</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="4">
|
||||
<el-col :xs="24" :sm="12" :md="6">
|
||||
<el-card class="overview-card overview-card--soft" shadow="hover">
|
||||
<div class="overview-label">治理告警</div>
|
||||
<div class="overview-value">{% raw %}{{ governanceRiskCount }}{% endraw %}</div>
|
||||
<div class="overview-note">存在配置、依赖或加载风险的插件</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="4">
|
||||
<el-card class="overview-card" shadow="hover">
|
||||
<div class="overview-label">执行异常</div>
|
||||
<div class="overview-value">{% raw %}{{ executionRiskCount }}{% endraw %}</div>
|
||||
<div class="overview-note">最近执行失败、超时或进入熔断的插件</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="12" :md="8" :lg="4">
|
||||
<el-card class="overview-card" shadow="hover">
|
||||
<div class="overview-label">熔断中</div>
|
||||
<div class="overview-value">{% raw %}{{ openCircuitCount }}{% endraw %}</div>
|
||||
<div class="overview-note">当前被保护机制隔离的高风险插件</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="16" class="insight-grid">
|
||||
<el-col :xs="24" :md="12">
|
||||
<el-card class="workspace-card" shadow="hover">
|
||||
<div slot="header" class="workspace-header">
|
||||
<div>
|
||||
<h3>高风险插件</h3>
|
||||
<p>优先排查熔断中、连续失败或最近错误较多的插件。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rank-list">
|
||||
<div v-if="topRiskPlugins.length === 0" class="mobile-empty-state">暂无高风险插件</div>
|
||||
<div v-for="(plugin, index) in topRiskPlugins" :key="`risk-${plugin.module_name}`" class="rank-item">
|
||||
<div class="rank-item__index">{% raw %}{{ index + 1 }}{% endraw %}</div>
|
||||
<div class="rank-item__content">
|
||||
<div class="rank-item__title-row">
|
||||
<div class="rank-item__title">{% raw %}{{ plugin.name }}{% endraw %}</div>
|
||||
<el-tag :type="executionTagType((plugin.execution_summary || {}).status)" size="mini">
|
||||
{% raw %}{{ executionLabel((plugin.execution_summary || {}).status) }}{% endraw %}
|
||||
</el-tag>
|
||||
</div>
|
||||
<div class="rank-item__summary">{% raw %}{{ (plugin.execution_summary || {}).summary || '暂无执行摘要' }}{% endraw %}</div>
|
||||
<div class="rank-item__meta">
|
||||
<span>最近错误:{% raw %}{{ (plugin.execution_summary || {}).last_error_message || '无' }}{% endraw %}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="24" :md="12">
|
||||
<el-card class="workspace-card" shadow="hover">
|
||||
<div slot="header" class="workspace-header">
|
||||
<div>
|
||||
<h3>慢插件排行</h3>
|
||||
<p>基于最近一次执行耗时,快速定位可能影响主链路响应的插件。</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rank-list">
|
||||
<div v-if="slowestPlugins.length === 0" class="mobile-empty-state">暂无执行样本</div>
|
||||
<div v-for="(plugin, index) in slowestPlugins" :key="`slow-${plugin.module_name}`" class="rank-item">
|
||||
<div class="rank-item__index">{% raw %}{{ index + 1 }}{% endraw %}</div>
|
||||
<div class="rank-item__content">
|
||||
<div class="rank-item__title-row">
|
||||
<div class="rank-item__title">{% raw %}{{ plugin.name }}{% endraw %}</div>
|
||||
<div class="rank-item__value">{% raw %}{{ formatDurationMs((plugin.execution_summary || {}).last_process_time_ms) }}{% endraw %}</div>
|
||||
</div>
|
||||
<div class="rank-item__summary">{% raw %}{{ (plugin.execution_summary || {}).summary || '暂无执行摘要' }}{% endraw %}</div>
|
||||
<div class="rank-item__meta">
|
||||
<span>成功率:{% raw %}{{ formatPercent((plugin.execution_summary || {}).success_rate) }}{% endraw %}</span>
|
||||
<span>累计执行:{% raw %}{{ (plugin.execution_summary || {}).total_executions || 0 }}{% endraw %}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-card class="workspace-card" shadow="hover">
|
||||
<div slot="header" class="workspace-header">
|
||||
<div>
|
||||
<h3>插件列表</h3>
|
||||
<p>优先关注状态、执行表现和说明,再进入单个插件详情与配置编辑。</p>
|
||||
<p>优先关注状态和说明,再进入单个插件详情与配置编辑。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -163,23 +91,6 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="执行表现" min-width="220">
|
||||
<template slot-scope="scope">
|
||||
<div class="execution-cell">
|
||||
<div class="execution-cell__head">
|
||||
<el-tag :type="executionTagType((scope.row.execution_summary || {}).status)" size="mini">
|
||||
{% raw %}{{ executionLabel((scope.row.execution_summary || {}).status) }}{% endraw %}
|
||||
</el-tag>
|
||||
<span class="execution-cell__metric">
|
||||
{% raw %}{{ `${formatPercent((scope.row.execution_summary || {}).success_rate)} / ${formatDurationMs((scope.row.execution_summary || {}).last_process_time_ms)}` }}{% endraw %}
|
||||
</span>
|
||||
</div>
|
||||
<div class="execution-cell__summary">
|
||||
{% raw %}{{ (scope.row.execution_summary || {}).summary || '暂无执行摘要' }}{% endraw %}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="能力类型" width="150" align="center">
|
||||
<template slot-scope="scope">
|
||||
<div class="command-tags command-tags--compact">
|
||||
@@ -246,11 +157,6 @@
|
||||
<span>版本:{% raw %}{{ plugin.version || '未知' }}{% endraw %}</span>
|
||||
<span>治理:{% raw %}{{ governanceLabel(plugin.governance_status) }}{% endraw %}</span>
|
||||
</div>
|
||||
<div class="mobile-plugin-card__meta">
|
||||
<span>执行:{% raw %}{{ executionLabel((plugin.execution_summary || {}).status) }}{% endraw %}</span>
|
||||
<span>成功率:{% raw %}{{ formatPercent((plugin.execution_summary || {}).success_rate) }}{% endraw %}</span>
|
||||
<span>耗时:{% raw %}{{ formatDurationMs((plugin.execution_summary || {}).last_process_time_ms) }}{% endraw %}</span>
|
||||
</div>
|
||||
<div class="mobile-plugin-card__desc">
|
||||
{% raw %}{{ plugin.description || '暂无描述' }}{% endraw %}
|
||||
</div>
|
||||
@@ -375,45 +281,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="执行表现" :span="2" v-if="selectedPlugin.execution_summary">
|
||||
<div class="config-overview-grid">
|
||||
<div class="config-overview-item">
|
||||
<span class="config-overview-label">执行状态</span>
|
||||
<span class="config-overview-value">{% raw %}{{ executionLabel(selectedPlugin.execution_summary.status) }}{% endraw %}</span>
|
||||
</div>
|
||||
<div class="config-overview-item">
|
||||
<span class="config-overview-label">累计执行</span>
|
||||
<span class="config-overview-value">{% raw %}{{ selectedPlugin.execution_summary.total_executions || 0 }}{% endraw %}</span>
|
||||
</div>
|
||||
<div class="config-overview-item">
|
||||
<span class="config-overview-label">成功率</span>
|
||||
<span class="config-overview-value">{% raw %}{{ formatPercent(selectedPlugin.execution_summary.success_rate) }}{% endraw %}</span>
|
||||
</div>
|
||||
<div class="config-overview-item">
|
||||
<span class="config-overview-label">超时率</span>
|
||||
<span class="config-overview-value">{% raw %}{{ formatPercent(selectedPlugin.execution_summary.timeout_rate) }}{% endraw %}</span>
|
||||
</div>
|
||||
<div class="config-overview-item">
|
||||
<span class="config-overview-label">最近耗时</span>
|
||||
<span class="config-overview-value">{% raw %}{{ formatDurationMs(selectedPlugin.execution_summary.last_process_time_ms) }}{% endraw %}</span>
|
||||
</div>
|
||||
<div class="config-overview-item">
|
||||
<span class="config-overview-label">最近成功</span>
|
||||
<span class="config-overview-value">{% raw %}{{ selectedPlugin.execution_summary.last_success_at_text || '-' }}{% endraw %}</span>
|
||||
</div>
|
||||
<div class="config-overview-item">
|
||||
<span class="config-overview-label">最近失败</span>
|
||||
<span class="config-overview-value">{% raw %}{{ selectedPlugin.execution_summary.last_failure_at_text || '-' }}{% endraw %}</span>
|
||||
</div>
|
||||
<div class="config-overview-item">
|
||||
<span class="config-overview-label">最近错误</span>
|
||||
<span class="config-overview-value">{% raw %}{{ selectedPlugin.execution_summary.last_error_message || '无' }}{% endraw %}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="entity-subtitle" style="margin-top: 8px;">
|
||||
{% raw %}{{ selectedPlugin.execution_summary.summary || '暂无执行摘要' }}{% endraw %}
|
||||
</div>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="治理诊断" :span="2" v-if="selectedPlugin.governance_diagnostics">
|
||||
<div v-if="selectedPlugin.governance_diagnostics.length > 0" class="diagnostic-list">
|
||||
<div
|
||||
@@ -627,50 +494,6 @@
|
||||
governanceRiskCount() {
|
||||
return (this.plugins || []).filter(plugin => ['warning', 'error'].includes((plugin.governance_status || '').toLowerCase())).length;
|
||||
},
|
||||
executionRiskCount() {
|
||||
// 这里把执行风险单独统计出来,和治理告警区分开:
|
||||
// 治理告警偏配置/依赖/加载问题,执行风险偏运行过程中的失败、超时与熔断。
|
||||
return (this.plugins || []).filter(plugin => ['warning', 'error'].includes((((plugin.execution_summary || {}).status) || '').toLowerCase())).length;
|
||||
},
|
||||
openCircuitCount() {
|
||||
return (this.plugins || []).filter(plugin => ((((plugin.execution_summary || {}).circuit_state) || '').toLowerCase() === 'open')).length;
|
||||
},
|
||||
topRiskPlugins() {
|
||||
// 风险排行优先按熔断状态、执行状态和连续失败次数排序,
|
||||
// 让页面顶部尽量把“最值得先排查”的插件顶上来。
|
||||
const statusPriority = {
|
||||
error: 0,
|
||||
warning: 1,
|
||||
info: 2,
|
||||
healthy: 3
|
||||
};
|
||||
return (this.plugins || [])
|
||||
.filter(plugin => ['warning', 'error'].includes((((plugin.execution_summary || {}).status) || '').toLowerCase()))
|
||||
.slice()
|
||||
.sort((left, right) => {
|
||||
const leftSummary = left.execution_summary || {};
|
||||
const rightSummary = right.execution_summary || {};
|
||||
const leftPriority = statusPriority[(leftSummary.status || 'info').toLowerCase()];
|
||||
const rightPriority = statusPriority[(rightSummary.status || 'info').toLowerCase()];
|
||||
return (
|
||||
(typeof leftPriority === 'number' ? leftPriority : 9) - (typeof rightPriority === 'number' ? rightPriority : 9)
|
||||
|| Number(rightSummary.consecutive_failures || 0) - Number(leftSummary.consecutive_failures || 0)
|
||||
|| Number(rightSummary.failure_count_total || 0) - Number(leftSummary.failure_count_total || 0)
|
||||
|| Number(rightSummary.timeout_count_total || 0) - Number(leftSummary.timeout_count_total || 0)
|
||||
);
|
||||
})
|
||||
.slice(0, 5);
|
||||
},
|
||||
slowestPlugins() {
|
||||
// 慢插件排行只看有执行样本的插件,避免未执行插件把榜单冲掉。
|
||||
return (this.plugins || [])
|
||||
.filter(plugin => Number((plugin.execution_summary || {}).total_executions || 0) > 0)
|
||||
.slice()
|
||||
.sort((left, right) => {
|
||||
return Number((right.execution_summary || {}).last_process_time_ms || 0) - Number((left.execution_summary || {}).last_process_time_ms || 0);
|
||||
})
|
||||
.slice(0, 5);
|
||||
},
|
||||
// 弹窗宽度按视口分级收缩,保证手机上弹窗内容不会贴边或继续触发横向溢出。
|
||||
pluginInfoDialogWidth() {
|
||||
return this.isMobileViewport ? '94%' : '64%';
|
||||
@@ -707,7 +530,7 @@
|
||||
},
|
||||
pluginStatusLabel(plugin) {
|
||||
if (plugin && plugin.status_label) return plugin.status_label;
|
||||
const normalizedStatus = String((plugin && plugin.status) || '').toUpperCase();
|
||||
const normalizedStatus = String(plugin?.status || '').toUpperCase();
|
||||
const mapping = {
|
||||
RUNNING: '运行中',
|
||||
STOPPED: '已停用',
|
||||
@@ -735,27 +558,10 @@
|
||||
};
|
||||
return mapping[normalizedLevel] || '提示';
|
||||
},
|
||||
executionTagType(level) {
|
||||
const normalizedLevel = String(level || '').toLowerCase();
|
||||
if (normalizedLevel === 'error') return 'danger';
|
||||
if (normalizedLevel === 'warning') return 'warning';
|
||||
if (normalizedLevel === 'healthy') return 'success';
|
||||
return 'info';
|
||||
},
|
||||
executionLabel(level) {
|
||||
const normalizedLevel = String(level || '').toLowerCase();
|
||||
const mapping = {
|
||||
healthy: '稳定',
|
||||
warning: '需关注',
|
||||
error: '高风险',
|
||||
info: '暂无样本'
|
||||
};
|
||||
return mapping[normalizedLevel] || '暂无样本';
|
||||
},
|
||||
governanceIssueSummary(plugin) {
|
||||
const errorCount = Number((plugin && plugin.governance_error_count) || 0);
|
||||
const warningCount = Number((plugin && plugin.governance_warning_count) || 0);
|
||||
const infoCount = Number((plugin && plugin.governance_info_count) || 0);
|
||||
const errorCount = Number(plugin?.governance_error_count || 0);
|
||||
const warningCount = Number(plugin?.governance_warning_count || 0);
|
||||
const infoCount = Number(plugin?.governance_info_count || 0);
|
||||
if (errorCount > 0 || warningCount > 0) {
|
||||
return `错误 ${errorCount} / 告警 ${warningCount}`;
|
||||
}
|
||||
@@ -764,16 +570,6 @@
|
||||
}
|
||||
return '暂无治理问题';
|
||||
},
|
||||
formatPercent(value) {
|
||||
const normalizedValue = Number(value || 0);
|
||||
if (!Number.isFinite(normalizedValue)) return '0.00%';
|
||||
return `${normalizedValue.toFixed(2)}%`;
|
||||
},
|
||||
formatDurationMs(value) {
|
||||
const normalizedValue = Number(value || 0);
|
||||
if (!Number.isFinite(normalizedValue) || normalizedValue <= 0) return '-';
|
||||
return `${normalizedValue.toFixed(2)} ms`;
|
||||
},
|
||||
loadPlugins() {
|
||||
this.loading = true;
|
||||
axios.get('/api/plugins')
|
||||
@@ -891,7 +687,7 @@
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('保存配置出错:', error);
|
||||
this.configError = '保存配置出错: ' + (((error.response || {}).data || {}).message || error.message);
|
||||
this.configError = '保存配置出错: ' + (error.response?.data?.message || error.message);
|
||||
});
|
||||
} catch (e) {
|
||||
this.configError = '处理配置时出错: ' + e.message;
|
||||
@@ -1058,7 +854,6 @@
|
||||
font-size: 14px;
|
||||
}
|
||||
.overview-grid .el-col { margin-bottom: 16px; }
|
||||
.insight-grid .el-col { margin-bottom: 16px; }
|
||||
.overview-card { min-height: 112px; }
|
||||
.overview-card--primary {
|
||||
background: linear-gradient(180deg, rgba(79,70,229,0.10), rgba(255,255,255,0.94)) !important;
|
||||
@@ -1074,64 +869,6 @@
|
||||
}
|
||||
.workspace-header h3 { font-size: 18px; margin-bottom: 4px; }
|
||||
.workspace-header p { font-size: 13px; color: #64748b; }
|
||||
.rank-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
.rank-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
padding: 14px;
|
||||
border-radius: 16px;
|
||||
background: rgba(248,250,252,0.82);
|
||||
border: 1px solid rgba(148,163,184,0.12);
|
||||
}
|
||||
.rank-item__index {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 50%;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: rgba(79,70,229,0.10);
|
||||
color: #4f46e5;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.rank-item__content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.rank-item__title-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.rank-item__title,
|
||||
.rank-item__value {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
color: #0f172a;
|
||||
}
|
||||
.rank-item__summary {
|
||||
font-size: 13px;
|
||||
line-height: 1.7;
|
||||
color: #475569;
|
||||
word-break: break-word;
|
||||
}
|
||||
.rank-item__meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px 14px;
|
||||
margin-top: 8px;
|
||||
font-size: 12px;
|
||||
color: #94a3b8;
|
||||
}
|
||||
.entity-cell { display: flex; align-items: center; gap: 12px; }
|
||||
.entity-badge {
|
||||
width: 30px; height: 30px; border-radius: 50%; display: inline-flex; align-items: center;
|
||||
@@ -1166,28 +903,6 @@
|
||||
color: #94a3b8;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.execution-cell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
.execution-cell__head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.execution-cell__metric {
|
||||
font-size: 12px;
|
||||
color: #64748b;
|
||||
font-weight: 600;
|
||||
}
|
||||
.execution-cell__summary {
|
||||
font-size: 12px;
|
||||
color: #94a3b8;
|
||||
line-height: 1.6;
|
||||
word-break: break-word;
|
||||
}
|
||||
.config-overview-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||
@@ -1364,10 +1079,6 @@
|
||||
.mobile-plugin-card__header {
|
||||
flex-direction: column;
|
||||
}
|
||||
.rank-item__title-row {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.mobile-plugin-card__actions .el-button,
|
||||
.mobile-group-card__actions .el-button {
|
||||
flex: 1 1 calc(50% - 8px);
|
||||
|
||||
@@ -602,107 +602,11 @@ class PluginManager:
|
||||
"info_count": level_counts["info"],
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _format_runtime_timestamp(timestamp_value: Any) -> str:
|
||||
"""把运行态中的 unix 时间戳转成后台可读文本。"""
|
||||
try:
|
||||
normalized = float(timestamp_value or 0.0)
|
||||
except (TypeError, ValueError):
|
||||
return ""
|
||||
if normalized <= 0:
|
||||
return ""
|
||||
try:
|
||||
return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(normalized))
|
||||
except (OverflowError, OSError, ValueError):
|
||||
return ""
|
||||
|
||||
@staticmethod
|
||||
def _safe_percent(numerator: Any, denominator: Any) -> float:
|
||||
"""安全计算百分比,避免分母为空时抛异常。"""
|
||||
try:
|
||||
denominator_value = float(denominator or 0.0)
|
||||
if denominator_value <= 0:
|
||||
return 0.0
|
||||
return round((float(numerator or 0.0) / denominator_value) * 100, 2)
|
||||
except (TypeError, ValueError, ZeroDivisionError):
|
||||
return 0.0
|
||||
|
||||
def _build_execution_summary(self, guard_snapshot: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""把执行保护记录转换成更适合后台页面展示的执行摘要。
|
||||
|
||||
设计考虑:
|
||||
1. 原始 execution_guard 更偏底层状态,前端直接消费会充满规则判断;
|
||||
2. 这里统一补出成功率、总执行次数、最近成功/失败时间、最近错误摘要;
|
||||
3. 未来如果还要做“高风险插件排行”“慢插件排行”,也能直接复用该摘要。
|
||||
"""
|
||||
guard_snapshot = dict(guard_snapshot or {})
|
||||
success_count_total = int(guard_snapshot.get("success_count_total", 0) or 0)
|
||||
failure_count_total = int(guard_snapshot.get("failure_count_total", 0) or 0)
|
||||
timeout_count_total = int(guard_snapshot.get("timeout_count_total", 0) or 0)
|
||||
consecutive_failures = int(guard_snapshot.get("consecutive_failures", 0) or 0)
|
||||
consecutive_timeouts = int(guard_snapshot.get("consecutive_timeouts", 0) or 0)
|
||||
last_process_time_ms = round(float(guard_snapshot.get("last_process_time_ms", 0.0) or 0.0), 2)
|
||||
circuit_state = str(guard_snapshot.get("circuit_state", "closed") or "closed").strip().lower()
|
||||
last_error_message = str(guard_snapshot.get("last_error_message") or "").strip()
|
||||
if len(last_error_message) > 240:
|
||||
last_error_message = f"{last_error_message[:237]}..."
|
||||
|
||||
total_executions = success_count_total + failure_count_total
|
||||
success_rate = self._safe_percent(success_count_total, total_executions)
|
||||
timeout_rate = self._safe_percent(timeout_count_total, total_executions)
|
||||
last_success_at_text = self._format_runtime_timestamp(guard_snapshot.get("last_success_at"))
|
||||
last_failure_at_text = self._format_runtime_timestamp(guard_snapshot.get("last_failure_at"))
|
||||
|
||||
status = "info"
|
||||
summary = "暂无执行样本"
|
||||
if total_executions > 0:
|
||||
status = "healthy"
|
||||
summary = (
|
||||
f"累计执行 {total_executions} 次,成功率 {success_rate}%,"
|
||||
f"最近耗时 {last_process_time_ms}ms"
|
||||
)
|
||||
|
||||
# 熔断打开是最明确的高风险信号,应优先标记为 error。
|
||||
if circuit_state == "open":
|
||||
status = "error"
|
||||
summary = (
|
||||
f"插件当前处于熔断中,连续失败 {consecutive_failures} 次,"
|
||||
f"恢复剩余 {int(guard_snapshot.get('open_remaining_seconds', 0) or 0)}s"
|
||||
)
|
||||
elif failure_count_total > 0 or timeout_count_total > 0 or consecutive_failures > 0 or consecutive_timeouts > 0:
|
||||
status = "warning"
|
||||
summary = (
|
||||
f"累计失败 {failure_count_total} 次,超时 {timeout_count_total} 次,"
|
||||
f"成功率 {success_rate}%"
|
||||
)
|
||||
|
||||
return {
|
||||
"status": status,
|
||||
"summary": summary,
|
||||
"total_executions": total_executions,
|
||||
"success_count_total": success_count_total,
|
||||
"failure_count_total": failure_count_total,
|
||||
"timeout_count_total": timeout_count_total,
|
||||
"success_rate": success_rate,
|
||||
"timeout_rate": timeout_rate,
|
||||
"consecutive_failures": consecutive_failures,
|
||||
"consecutive_timeouts": consecutive_timeouts,
|
||||
"last_process_time_ms": last_process_time_ms,
|
||||
"last_success_at_text": last_success_at_text,
|
||||
"last_failure_at_text": last_failure_at_text,
|
||||
"last_error_message": last_error_message,
|
||||
"last_failure_type": str(guard_snapshot.get("last_failure_type") or "").strip(),
|
||||
"last_timeout_seconds": int(guard_snapshot.get("last_timeout_seconds", 0) or 0),
|
||||
"circuit_state": circuit_state,
|
||||
"open_remaining_seconds": int(guard_snapshot.get("open_remaining_seconds", 0) or 0),
|
||||
}
|
||||
|
||||
def _build_plugin_snapshot(self, plugin: PluginInterface) -> Dict[str, Any]:
|
||||
"""为已加载插件生成标准治理快照。"""
|
||||
module_name = self._get_module_name_from_plugin(plugin) or "unknown"
|
||||
runtime_record = self._get_module_runtime_state(module_name)
|
||||
guard_snapshot = self.get_plugin_guard_snapshot(module_name)
|
||||
execution_summary = self._build_execution_summary(guard_snapshot)
|
||||
config_path = plugin.get_config_path()
|
||||
config_overview = self._read_plugin_config_overview(config_path)
|
||||
commands = self._collect_plugin_commands(plugin)
|
||||
@@ -744,14 +648,12 @@ class PluginManager:
|
||||
"runtime_state": runtime_record.get("state", "loaded"),
|
||||
"runtime_message": runtime_record.get("message", ""),
|
||||
"execution_guard": guard_snapshot,
|
||||
"execution_summary": execution_summary,
|
||||
}
|
||||
|
||||
def _build_unloaded_plugin_snapshot(self, module_name: str) -> Dict[str, Any]:
|
||||
"""为未成功加载的插件模块生成治理快照。"""
|
||||
runtime_record = self._get_module_runtime_state(module_name)
|
||||
guard_snapshot = self.get_plugin_guard_snapshot(module_name)
|
||||
execution_summary = self._build_execution_summary(guard_snapshot)
|
||||
config_path = os.path.join(self.plugin_dir, module_name, "config.toml")
|
||||
if not os.path.exists(config_path):
|
||||
config_path = os.path.join(self.plugin_dir, f"{module_name}", "config.toml")
|
||||
@@ -798,7 +700,6 @@ class PluginManager:
|
||||
"runtime_state": runtime_state or "discovered",
|
||||
"runtime_message": runtime_record.get("message", ""),
|
||||
"execution_guard": guard_snapshot,
|
||||
"execution_summary": execution_summary,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
|
||||
@@ -405,7 +405,6 @@
|
||||
- 第一阶段已完成:`PluginManager` 已输出统一插件治理快照,后台不再只展示“加载成功的插件”
|
||||
- 第一阶段已完成:后台插件管理页已补充治理健康、能力类型、Feature Key、依赖与配置概览信息
|
||||
- 第一阶段已完成:插件配置保存前已增加格式校验,避免坏配置直接写回线上文件
|
||||
- 第二阶段已完成:插件管理页已补充执行表现摘要、最近错误信息与高风险/慢插件排行,便于快速定位运行异常插件
|
||||
- 后续可继续补充插件错误历史、性能排名、依赖图与熔断/隔离控制
|
||||
|
||||
建议内容:
|
||||
|
||||
Reference in New Issue
Block a user