feat(logs): replace API polling with WebSocket for real-time updates
Implement WebSocket connection to receive live log updates instead of periodic API polling. The changes include: - Add WebSocket connection management with token authentication - Implement computed property for log filtering - Maintain only the latest 100 logs in memory - Handle WebSocket events and errors appropriately
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
<div class="page-header">
|
<div class="page-header">
|
||||||
<h2>System Logs</h2>
|
<h2>System Logs</h2>
|
||||||
<div class="filter-controls">
|
<div class="filter-controls">
|
||||||
<select v-model="selectedLevel" @change="loadLogs">
|
<select v-model="selectedLevel" @change="filterLogs">
|
||||||
<option value="">All Levels</option>
|
<option value="">All Levels</option>
|
||||||
<option value="DEBUG">DEBUG</option>
|
<option value="DEBUG">DEBUG</option>
|
||||||
<option value="INFO">INFO</option>
|
<option value="INFO">INFO</option>
|
||||||
@@ -14,8 +14,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="logs-container">
|
<div class="logs-container">
|
||||||
<div v-if="logs.length > 0" class="log-list">
|
<div v-if="filteredLogs.length > 0" class="log-list">
|
||||||
<div v-for="(log, index) in logs" :key="index" class="log-item">
|
<div v-for="(log, index) in filteredLogs" :key="index" class="log-item">
|
||||||
<span class="log-time">{{ log.time }}</span>
|
<span class="log-time">{{ log.time }}</span>
|
||||||
<span :class="['log-level', log.level]">{{ log.level }}</span>
|
<span :class="['log-level', log.level]">{{ log.level }}</span>
|
||||||
<span class="log-message">{{ log.message }}</span>
|
<span class="log-message">{{ log.message }}</span>
|
||||||
@@ -29,37 +29,93 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { ref, onMounted } from 'vue';
|
import { ref, onMounted, onUnmounted, computed } from 'vue';
|
||||||
import { logApi } from '../api/index.js';
|
import { showNotification, formatDate, getCookie } from '../utils/functions.js';
|
||||||
import { showNotification, formatDate } from '../utils/functions.js';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Logs',
|
name: 'Logs',
|
||||||
setup() {
|
setup() {
|
||||||
const logs = ref([]);
|
const logs = ref([]);
|
||||||
const selectedLevel = ref('');
|
const selectedLevel = ref('');
|
||||||
|
const socket = ref(null);
|
||||||
|
|
||||||
const loadLogs = async () => {
|
const levelMap = {
|
||||||
try {
|
0: 'DEBUG',
|
||||||
const result = await logApi.getLogs(selectedLevel.value);
|
1: 'INFO',
|
||||||
logs.value = result.data.map(log => ({
|
2: 'WARN',
|
||||||
time: formatDate(log.timestamp),
|
3: 'ERROR',
|
||||||
level: log.level,
|
4: 'FATAL'
|
||||||
message: log.message
|
};
|
||||||
}));
|
|
||||||
} catch (error) {
|
const filteredLogs = computed(() => {
|
||||||
showNotification('Failed to load logs', 'error');
|
if (!selectedLevel.value) {
|
||||||
|
return logs.value;
|
||||||
|
}
|
||||||
|
return logs.value.filter(log => log.level === selectedLevel.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
const filterLogs = () => {
|
||||||
|
// Filtering is handled by computed property
|
||||||
|
};
|
||||||
|
|
||||||
|
const connectWebSocket = () => {
|
||||||
|
const token = getCookie('token');
|
||||||
|
const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||||
|
const wsUrl = `${wsProtocol}//localhost:8080/system/getLogs?token=${token}`;
|
||||||
|
|
||||||
|
socket.value = new WebSocket(wsUrl);
|
||||||
|
|
||||||
|
socket.value.onopen = () => {
|
||||||
|
console.log('Connected to log server');
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.value.onmessage = (event) => {
|
||||||
|
try {
|
||||||
|
const log = JSON.parse(event.data);
|
||||||
|
logs.value.push({
|
||||||
|
time: log.timestamp,
|
||||||
|
level: levelMap[log.level],
|
||||||
|
message: log.content
|
||||||
|
});
|
||||||
|
// Keep only the latest 100 logs
|
||||||
|
if (logs.value.length > 100) {
|
||||||
|
logs.value.shift();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error parsing log message:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.value.onclose = () => {
|
||||||
|
console.log('Disconnected from log server');
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.value.onerror = (error) => {
|
||||||
|
console.error('WebSocket error:', error);
|
||||||
|
showNotification('Failed to connect to log server', 'error');
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const disconnectWebSocket = () => {
|
||||||
|
if (socket.value) {
|
||||||
|
socket.value.close();
|
||||||
|
socket.value = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadLogs();
|
connectWebSocket();
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
disconnectWebSocket();
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
logs,
|
logs,
|
||||||
|
filteredLogs,
|
||||||
selectedLevel,
|
selectedLevel,
|
||||||
loadLogs
|
filterLogs
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user