[log] display logs
This commit is contained in:
@@ -151,6 +151,12 @@ func (a *APIHandler) getHandler(c *gin.Context) {
|
||||
case "onlines":
|
||||
onlines, err := a.StatsService.GetOnlines()
|
||||
jsonObj(c, onlines, err)
|
||||
case "logs":
|
||||
service := c.Query("s")
|
||||
count := c.Query("c")
|
||||
level := c.Query("l")
|
||||
logs := a.ServerService.GetLogs(service, count, level)
|
||||
jsonObj(c, logs, nil)
|
||||
default:
|
||||
jsonMsg(c, "API call", nil)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"s-ui/config"
|
||||
"s-ui/logger"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/shirou/gopsutil/v3/cpu"
|
||||
@@ -135,3 +138,24 @@ func (s *ServerService) GetSystemInfo() map[string]interface{} {
|
||||
|
||||
return info
|
||||
}
|
||||
|
||||
func (s *ServerService) GetLogs(service string, count string, level string) []string {
|
||||
c, _ := strconv.Atoi(count)
|
||||
var lines []string
|
||||
if service == "sing-box" {
|
||||
cmdArgs := []string{"journalctl", "-u", service, "--no-pager", "-n", count, "-p", level}
|
||||
// Run the command
|
||||
cmd := exec.Command(cmdArgs[0], cmdArgs[1:]...)
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return []string{"Failed to run journalctl command!"}
|
||||
}
|
||||
lines = strings.Split(out.String(), "\n")
|
||||
} else {
|
||||
lines = logger.GetLogs(c, level)
|
||||
}
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
<template>
|
||||
<LogVue
|
||||
v-model="logModal.visible"
|
||||
:visible="logModal.visible"
|
||||
:logType="logModal.logType"
|
||||
@close="closeLogs"
|
||||
/>
|
||||
<v-container class="fill-height">
|
||||
<v-responsive :class="reloadItems.length>0 ? 'fill-height text-center' : 'align-center'" >
|
||||
<v-row class="d-flex align-center justify-center">
|
||||
@@ -45,7 +51,7 @@
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="3" v-for="i in reloadItems" :key="i">
|
||||
<v-card class="rounded-lg" variant="outlined" height="200px"
|
||||
<v-card class="rounded-lg" variant="outlined" height="210px"
|
||||
:title="menuItems.flatMap(cat => cat.value).find(m => m.value == i)?.title">
|
||||
<v-card-text style="padding: 0 16px;" align="center" justify="center">
|
||||
<Gauge :tilesData="tilesData" :type="i" v-if="i.charAt(0) == 'g'" />
|
||||
@@ -80,13 +86,19 @@
|
||||
</v-col>
|
||||
<v-col cols="3">S-UI</v-col>
|
||||
<v-col cols="9">
|
||||
<v-chip density="compact" color="primary" variant="flat">
|
||||
<v-chip density="compact" color="blue">
|
||||
<v-tooltip activator="parent" location="top">
|
||||
{{ $t('main.info.threads') }}: {{ tilesData.sys?.appThreads }}<br />
|
||||
{{ $t('main.info.memory') }}: {{ HumanReadable.sizeFormat(tilesData.sys?.appMem) }}
|
||||
</v-tooltip>
|
||||
v{{ tilesData.sys?.appVersion }}
|
||||
</v-chip>
|
||||
<v-chip density="compact" color="transparent" style="cursor: pointer;" @click="openLogs('s-ui')">
|
||||
<v-tooltip activator="parent" location="top">
|
||||
S-UI Logs
|
||||
</v-tooltip>
|
||||
<v-icon icon="mdi-list-box-outline" color="blue" />
|
||||
</v-chip>
|
||||
</v-col>
|
||||
<v-col cols="3">{{ $t('main.info.uptime') }}</v-col>
|
||||
<v-col cols="9">{{ HumanReadable.formatSecond(tilesData.uptime) }}</v-col>
|
||||
@@ -98,6 +110,12 @@
|
||||
<v-col cols="8">
|
||||
<v-chip density="compact" color="success" variant="flat" v-if="tilesData.sbd?.running">{{ $t('yes') }}</v-chip>
|
||||
<v-chip density="compact" color="error" variant="flat" v-else>{{ $t('no') }}</v-chip>
|
||||
<v-chip density="compact" color="transparent" style="cursor: pointer;" @click="openLogs('sing-box')">
|
||||
<v-tooltip activator="parent" location="top">
|
||||
Sing-Box Logs
|
||||
</v-tooltip>
|
||||
<v-icon icon="mdi-list-box-outline" :color="tilesData.sbd?.running ? 'success': 'error'" />
|
||||
</v-chip>
|
||||
</v-col>
|
||||
<v-col cols="4">{{ $t('main.info.memory') }}</v-col>
|
||||
<v-col cols="8">
|
||||
@@ -148,6 +166,7 @@ import Gauge from '@/components/tiles/Gauge.vue'
|
||||
import History from '@/components/tiles/History.vue'
|
||||
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import { i18n } from '@/locales'
|
||||
import LogVue from '@/layouts/modals/Logs.vue'
|
||||
|
||||
const menu = ref(false)
|
||||
const menuItems = [
|
||||
@@ -215,4 +234,19 @@ onMounted(() => {
|
||||
onBeforeUnmount(() => {
|
||||
stopTimer()
|
||||
})
|
||||
|
||||
const logModal = ref({
|
||||
visible: false,
|
||||
logType: "s-ui"
|
||||
})
|
||||
|
||||
const openLogs = (logType: string) => {
|
||||
logModal.value.logType = logType
|
||||
logModal.value.visible = true
|
||||
}
|
||||
|
||||
const closeLogs = () => {
|
||||
logModal.value.logType = "s-ui"
|
||||
logModal.value.visible = false
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<v-dialog transition="dialog-bottom-transition" width="90%" max-width="1200" :loading="loading">
|
||||
<v-card class="rounded-lg">
|
||||
<v-card-title>
|
||||
{{ (logType == 's-ui'? "S-UI" : "Sing-Box") + " logs" }}
|
||||
</v-card-title>
|
||||
<v-divider></v-divider>
|
||||
<v-card-text>
|
||||
<v-row>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-select
|
||||
hide-details
|
||||
label="Level"
|
||||
:items="logLevels"
|
||||
v-model="logLevel"
|
||||
@update:model-value="loadData">
|
||||
</v-select>
|
||||
</v-col>
|
||||
<v-col cols="12" sm="6" md="4">
|
||||
<v-select
|
||||
hide-details
|
||||
label="Count"
|
||||
:items="[10,20,30,50,100]"
|
||||
v-model.number="logCount"
|
||||
@update:model-value="loadData">
|
||||
</v-select>
|
||||
</v-col>
|
||||
<v-col cols="auto" align="center" justify="center">
|
||||
<v-btn
|
||||
icon="mdi-refresh"
|
||||
variant="tonal"
|
||||
:loading="loading"
|
||||
@click="loadData">
|
||||
<v-icon />
|
||||
</v-btn>
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-card style="background-color: background" dir="ltr" v-html="lines.join('<br />')"></v-card>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn
|
||||
color="blue-darken-1"
|
||||
variant="outlined"
|
||||
@click="$emit('close')"
|
||||
>
|
||||
{{ $t('actions.close') }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import HttpUtils from '@/plugins/httputil';
|
||||
|
||||
export default {
|
||||
props: ['logType', 'visible'],
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
lines: [],
|
||||
logLevel: 'info',
|
||||
logLevels: [
|
||||
{ title: 'DEBUG', value: 'debug' },
|
||||
{ title: 'INFO', value: 'info' },
|
||||
{ title: 'WARNING', value: 'warn' },
|
||||
{ title: 'ERROR', value: 'error' },
|
||||
],
|
||||
logCount: 10,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async loadData() {
|
||||
this.loading = true
|
||||
const data = await HttpUtils.get('api/logs',{ s: this.$props.logType, c: this.logCount, l: this.logLevel })
|
||||
if (data.success) {
|
||||
this.lines = data.obj?? []
|
||||
this.loading = false
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
visible(newValue) {
|
||||
this.lines = []
|
||||
this.logLevel = 'info'
|
||||
this.logCount = 10
|
||||
if (newValue) {
|
||||
this.loadData()
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user