619 lines
21 KiB
Vue

<template>
<div>
<LayoutsBreadcrumb />
<!-- Header Section -->
<rs-card class="mb-6">
<template #header>
<div class="flex items-center justify-between">
<div class="flex items-center">
<Icon class="mr-2 text-primary" name="ic:outline-speed"></Icon>
<h1 class="text-xl font-bold text-primary">Performance Monitoring</h1>
</div>
<div class="flex items-center gap-3">
<div class="flex items-center">
<div class="w-2 h-2 bg-green-500 rounded-full mr-2 animate-pulse"></div>
<span class="text-sm text-gray-600">Real-time Metrics</span>
</div>
<rs-button variant="outline" size="sm" @click="refreshMetrics">
<Icon class="mr-1" name="ic:outline-refresh"></Icon>
Refresh
</rs-button>
</div>
</div>
</template>
<template #body>
<p class="text-gray-600">
Monitor system performance metrics including throughput, uptime, and scaling status to ensure
the system meets the 10,000 notifications per minute requirement with 99.9% uptime.
</p>
</template>
</rs-card>
<!-- Key Performance Indicators -->
<div class="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-4 gap-6 mb-6">
<rs-card
v-for="(kpi, index) in keyMetrics"
:key="index"
class="transition-all duration-300 hover:shadow-lg"
:class="kpi.status === 'critical' ? 'border-red-500' : kpi.status === 'warning' ? 'border-yellow-500' : 'border-green-500'"
>
<div class="pt-5 pb-3 px-5 flex items-center gap-4">
<div
class="p-4 flex justify-center items-center rounded-2xl"
:class="kpi.bgColor"
>
<Icon class="text-2xl" :class="kpi.iconColor" :name="kpi.icon"></Icon>
</div>
<div class="flex-1 truncate">
<span class="block font-bold text-2xl leading-tight" :class="kpi.valueColor">
{{ kpi.value }}
</span>
<span class="text-sm font-medium text-gray-600">
{{ kpi.title }}
</span>
<div class="flex items-center mt-1">
<Icon
class="text-xs mr-1"
:class="kpi.trend === 'up' ? 'text-green-500' : kpi.trend === 'down' ? 'text-red-500' : 'text-gray-500'"
:name="kpi.trend === 'up' ? 'ic:outline-trending-up' : kpi.trend === 'down' ? 'ic:outline-trending-down' : 'ic:outline-trending-flat'"
></Icon>
<span class="text-xs" :class="kpi.trend === 'up' ? 'text-green-500' : kpi.trend === 'down' ? 'text-red-500' : 'text-gray-500'">
{{ kpi.change }}
</span>
</div>
</div>
</div>
</rs-card>
</div>
<!-- Performance Charts -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
<!-- Throughput Chart -->
<rs-card>
<template #header>
<div class="flex items-center justify-between">
<div class="flex items-center">
<Icon class="mr-2 text-primary" name="ic:outline-show-chart"></Icon>
<h3 class="text-lg font-semibold text-primary">Throughput (Last 24 Hours)</h3>
</div>
<div class="flex items-center gap-2">
<span class="text-sm text-gray-600">Target: 10,000/min</span>
<div class="w-3 h-3 bg-green-500 rounded-full"></div>
</div>
</div>
</template>
<template #body>
<div class="h-64 flex items-center justify-center bg-gray-50 rounded-lg">
<div class="text-center">
<Icon class="text-4xl text-gray-400 mb-2" name="ic:outline-show-chart"></Icon>
<p class="text-gray-600">Throughput Chart</p>
<p class="text-sm text-gray-500">Real-time notifications per minute</p>
</div>
</div>
<div class="mt-4 grid grid-cols-3 gap-4 text-center">
<div>
<p class="text-sm text-gray-600">Current</p>
<p class="font-bold text-lg text-green-600">{{ currentThroughput }}/min</p>
</div>
<div>
<p class="text-sm text-gray-600">Peak Today</p>
<p class="font-bold text-lg text-blue-600">{{ peakThroughput }}/min</p>
</div>
<div>
<p class="text-sm text-gray-600">Average</p>
<p class="font-bold text-lg text-purple-600">{{ avgThroughput }}/min</p>
</div>
</div>
</template>
</rs-card>
<!-- System Uptime -->
<rs-card>
<template #header>
<div class="flex items-center justify-between">
<div class="flex items-center">
<Icon class="mr-2 text-primary" name="ic:outline-timeline"></Icon>
<h3 class="text-lg font-semibold text-primary">System Uptime</h3>
</div>
<div class="flex items-center gap-2">
<span class="text-sm text-gray-600">Target: 99.9%</span>
<div class="w-3 h-3 bg-green-500 rounded-full"></div>
</div>
</div>
</template>
<template #body>
<div class="space-y-4">
<!-- Current Uptime -->
<div class="text-center p-6 bg-green-50 rounded-lg">
<div class="text-4xl font-bold text-green-600 mb-2">{{ currentUptime }}%</div>
<p class="text-green-700 font-medium">Current Uptime</p>
<p class="text-sm text-green-600 mt-1">{{ uptimeDuration }} continuous operation</p>
</div>
<!-- Uptime History -->
<div class="grid grid-cols-3 gap-4">
<div class="text-center">
<p class="text-sm text-gray-600">Last 24h</p>
<p class="font-bold text-lg">{{ uptime24h }}%</p>
</div>
<div class="text-center">
<p class="text-sm text-gray-600">Last 7 days</p>
<p class="font-bold text-lg">{{ uptime7d }}%</p>
</div>
<div class="text-center">
<p class="text-sm text-gray-600">Last 30 days</p>
<p class="font-bold text-lg">{{ uptime30d }}%</p>
</div>
</div>
<!-- Recent Incidents -->
<div class="border-t pt-4">
<h4 class="font-medium text-gray-700 mb-2">Recent Incidents</h4>
<div class="space-y-2">
<div
v-for="(incident, index) in recentIncidents"
:key="index"
class="flex items-center justify-between p-2 bg-gray-50 rounded"
>
<div class="flex items-center">
<div class="w-2 h-2 bg-red-500 rounded-full mr-2"></div>
<span class="text-sm">{{ incident.description }}</span>
</div>
<span class="text-xs text-gray-500">{{ incident.duration }}</span>
</div>
</div>
</div>
</div>
</template>
</rs-card>
</div>
<!-- Worker Scaling Status -->
<rs-card class="mb-6">
<template #header>
<div class="flex items-center justify-between">
<div class="flex items-center">
<Icon class="mr-2 text-primary" name="ic:outline-dns"></Icon>
<h3 class="text-lg font-semibold text-primary">Horizontal Scaling Status</h3>
</div>
<rs-button variant="outline" size="sm" @click="showScalingModal = true">
<Icon class="mr-1" name="ic:outline-settings"></Icon>
Configure
</rs-button>
</div>
</template>
<template #body>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Active Workers -->
<div class="text-center p-4 bg-blue-50 rounded-lg">
<Icon class="text-3xl text-blue-600 mb-2" name="ic:outline-memory"></Icon>
<div class="text-2xl font-bold text-blue-600">{{ activeWorkers }}</div>
<p class="text-blue-700 font-medium">Active Workers</p>
<p class="text-sm text-blue-600 mt-1">Processing notifications</p>
</div>
<!-- Auto-scaling Status -->
<div class="text-center p-4 bg-green-50 rounded-lg">
<Icon class="text-3xl text-green-600 mb-2" name="ic:outline-auto-awesome"></Icon>
<div class="text-lg font-bold text-green-600">{{ autoScalingEnabled ? 'Enabled' : 'Disabled' }}</div>
<p class="text-green-700 font-medium">Auto-scaling</p>
<p class="text-sm text-green-600 mt-1">{{ autoScalingEnabled ? 'Automatically adjusting' : 'Manual scaling only' }}</p>
</div>
<!-- Queue Load -->
<div class="text-center p-4 bg-purple-50 rounded-lg">
<Icon class="text-3xl text-purple-600 mb-2" name="ic:outline-queue"></Icon>
<div class="text-2xl font-bold text-purple-600">{{ queueLoad }}%</div>
<p class="text-purple-700 font-medium">Queue Load</p>
<p class="text-sm text-purple-600 mt-1">Current capacity usage</p>
</div>
</div>
<!-- Worker Details -->
<div class="mt-6">
<h4 class="font-medium text-gray-700 mb-4">Worker Instances</h4>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Worker ID
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Status
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
CPU Usage
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Memory Usage
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Jobs Processed
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Last Heartbeat
</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<tr v-for="(worker, index) in workers" :key="index">
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
{{ worker.id }}
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span
class="inline-flex px-2 py-1 text-xs font-semibold rounded-full"
:class="{
'bg-green-100 text-green-800': worker.status === 'healthy',
'bg-yellow-100 text-yellow-800': worker.status === 'warning',
'bg-red-100 text-red-800': worker.status === 'error'
}"
>
{{ worker.status }}
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{{ worker.cpuUsage }}%
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{{ worker.memoryUsage }}%
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{{ worker.jobsProcessed.toLocaleString() }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{{ worker.lastHeartbeat }}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
</rs-card>
<!-- Performance Alerts -->
<rs-card>
<template #header>
<div class="flex items-center justify-between">
<div class="flex items-center">
<Icon class="mr-2 text-primary" name="ic:outline-warning"></Icon>
<h3 class="text-lg font-semibold text-primary">Performance Alerts</h3>
</div>
<rs-button variant="outline" size="sm" @click="showAlertsConfig = true">
<Icon class="mr-1" name="ic:outline-settings"></Icon>
Configure Alerts
</rs-button>
</div>
</template>
<template #body>
<div class="space-y-3">
<div
v-for="(alert, index) in performanceAlerts"
:key="index"
class="flex items-center justify-between p-4 rounded-lg"
:class="{
'bg-red-50 border border-red-200': alert.severity === 'critical',
'bg-yellow-50 border border-yellow-200': alert.severity === 'warning',
'bg-blue-50 border border-blue-200': alert.severity === 'info'
}"
>
<div class="flex items-center">
<Icon
class="mr-3 text-xl"
:class="{
'text-red-500': alert.severity === 'critical',
'text-yellow-500': alert.severity === 'warning',
'text-blue-500': alert.severity === 'info'
}"
:name="alert.icon"
></Icon>
<div>
<p class="font-medium" :class="{
'text-red-800': alert.severity === 'critical',
'text-yellow-800': alert.severity === 'warning',
'text-blue-800': alert.severity === 'info'
}">
{{ alert.title }}
</p>
<p class="text-sm" :class="{
'text-red-600': alert.severity === 'critical',
'text-yellow-600': alert.severity === 'warning',
'text-blue-600': alert.severity === 'info'
}">
{{ alert.description }}
</p>
</div>
</div>
<div class="text-right">
<p class="text-xs text-gray-500">{{ alert.timestamp }}</p>
<rs-button
size="sm"
variant="outline"
@click="acknowledgeAlert(index)"
>
Acknowledge
</rs-button>
</div>
</div>
</div>
</template>
</rs-card>
<!-- Scaling Configuration Modal -->
<rs-modal v-model="showScalingModal" title="Horizontal Scaling Configuration">
<div class="space-y-6">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Auto-scaling</label>
<div class="flex items-center">
<input
type="checkbox"
v-model="scalingConfig.autoScalingEnabled"
class="mr-2"
>
<span class="text-sm text-gray-600">Enable automatic worker scaling</span>
</div>
</div>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Min Workers</label>
<input
type="number"
v-model="scalingConfig.minWorkers"
class="w-full p-2 border border-gray-300 rounded-md"
min="1"
>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Max Workers</label>
<input
type="number"
v-model="scalingConfig.maxWorkers"
class="w-full p-2 border border-gray-300 rounded-md"
min="1"
>
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Scale Up Threshold (%)</label>
<input
type="number"
v-model="scalingConfig.scaleUpThreshold"
class="w-full p-2 border border-gray-300 rounded-md"
min="1"
max="100"
>
<p class="text-xs text-gray-500 mt-1">Scale up when queue load exceeds this percentage</p>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Scale Down Threshold (%)</label>
<input
type="number"
v-model="scalingConfig.scaleDownThreshold"
class="w-full p-2 border border-gray-300 rounded-md"
min="1"
max="100"
>
<p class="text-xs text-gray-500 mt-1">Scale down when queue load falls below this percentage</p>
</div>
</div>
<template #footer>
<div class="flex justify-end gap-3">
<rs-button variant="outline" @click="showScalingModal = false">
Cancel
</rs-button>
<rs-button @click="saveScalingConfig">
Save Configuration
</rs-button>
</div>
</template>
</rs-modal>
</div>
</template>
<script setup>
definePageMeta({
title: "Performance Monitoring",
middleware: ["auth"],
requiresAuth: true,
breadcrumb: [
{
name: "Notification",
path: "/notification",
},
{
name: "Queue & Scheduler",
path: "/notification/queue-scheduler",
},
{
name: "Performance",
path: "/notification/queue-scheduler/performance",
},
],
});
// Reactive data
const showScalingModal = ref(false);
const showAlertsConfig = ref(false);
// Key Performance Metrics
const keyMetrics = ref([
{
title: "Throughput",
value: "12,847/min",
icon: "ic:outline-speed",
bgColor: "bg-green-100",
iconColor: "text-green-600",
valueColor: "text-green-600",
status: "healthy",
trend: "up",
change: "+15.3%"
},
{
title: "System Uptime",
value: "99.97%",
icon: "ic:outline-timeline",
bgColor: "bg-blue-100",
iconColor: "text-blue-600",
valueColor: "text-blue-600",
status: "healthy",
trend: "stable",
change: "0.02%"
},
{
title: "Active Workers",
value: "8",
icon: "ic:outline-memory",
bgColor: "bg-purple-100",
iconColor: "text-purple-600",
valueColor: "text-purple-600",
status: "healthy",
trend: "up",
change: "+2"
},
{
title: "Queue Load",
value: "67%",
icon: "ic:outline-queue",
bgColor: "bg-yellow-100",
iconColor: "text-yellow-600",
valueColor: "text-yellow-600",
status: "warning",
trend: "up",
change: "+12%"
}
]);
// Throughput metrics
const currentThroughput = ref("12,847");
const peakThroughput = ref("15,234");
const avgThroughput = ref("9,876");
// Uptime metrics
const currentUptime = ref("99.97");
const uptimeDuration = ref("47 days, 12 hours");
const uptime24h = ref("100.00");
const uptime7d = ref("99.95");
const uptime30d = ref("99.92");
// Recent incidents
const recentIncidents = ref([
{
description: "Database connection timeout",
duration: "2m 15s",
timestamp: "2 days ago"
},
{
description: "Worker node restart",
duration: "45s",
timestamp: "5 days ago"
}
]);
// Scaling configuration
const activeWorkers = ref(8);
const autoScalingEnabled = ref(true);
const queueLoad = ref(67);
const scalingConfig = ref({
autoScalingEnabled: true,
minWorkers: 2,
maxWorkers: 20,
scaleUpThreshold: 80,
scaleDownThreshold: 30
});
// Worker instances
const workers = ref([
{
id: "worker-001",
status: "healthy",
cpuUsage: 45,
memoryUsage: 62,
jobsProcessed: 15847,
lastHeartbeat: "2 seconds ago"
},
{
id: "worker-002",
status: "healthy",
cpuUsage: 52,
memoryUsage: 58,
jobsProcessed: 14923,
lastHeartbeat: "1 second ago"
},
{
id: "worker-003",
status: "warning",
cpuUsage: 78,
memoryUsage: 85,
jobsProcessed: 12456,
lastHeartbeat: "5 seconds ago"
},
{
id: "worker-004",
status: "healthy",
cpuUsage: 41,
memoryUsage: 55,
jobsProcessed: 16234,
lastHeartbeat: "1 second ago"
}
]);
// Performance alerts
const performanceAlerts = ref([
{
title: "High Queue Load",
description: "Queue load has exceeded 80% for the last 10 minutes",
severity: "warning",
icon: "ic:outline-warning",
timestamp: "5 minutes ago"
},
{
title: "Worker High CPU Usage",
description: "Worker-003 CPU usage is at 78%",
severity: "warning",
icon: "ic:outline-memory",
timestamp: "2 minutes ago"
},
{
title: "Throughput Target Met",
description: "Successfully processing 12,847 notifications per minute",
severity: "info",
icon: "ic:outline-check-circle",
timestamp: "1 minute ago"
}
]);
// Methods
const refreshMetrics = () => {
console.log('Refreshing performance metrics...');
// Simulate data refresh
};
const acknowledgeAlert = (index) => {
performanceAlerts.value.splice(index, 1);
};
const saveScalingConfig = () => {
console.log('Saving scaling configuration:', scalingConfig.value);
showScalingModal.value = false;
// Here you would typically send the config to your backend
};
// Auto-refresh metrics every 30 seconds
onMounted(() => {
const interval = setInterval(refreshMetrics, 30000);
onUnmounted(() => {
clearInterval(interval);
});
});
</script>
<style lang="scss" scoped></style>