webapp: Add additional filter possibilities to console view

This commit is contained in:
Thomas Basler
2025-04-26 00:19:42 +02:00
parent 95106e4488
commit 4099347a43
8 changed files with 131 additions and 26 deletions

View File

@@ -384,7 +384,10 @@
"EnableAutoScroll": "Ενεργοποίηση αυτόματης κύλισης", "EnableAutoScroll": "Ενεργοποίηση αυτόματης κύλισης",
"ClearConsole": "Εκκαθάριση κονσόλας", "ClearConsole": "Εκκαθάριση κονσόλας",
"CopyToClipboard": "Αντιγραφή στο πρόχειρο", "CopyToClipboard": "Αντιγραφή στο πρόχειρο",
"Download": "Download" "Download": "Download",
"RegularExpression": "Regular Expression",
"Lines": "{lines} Lines",
"LogLevel": "Log Level ({cnt})"
}, },
"inverterchannelinfo": { "inverterchannelinfo": {
"String": "Είσοδος {num}", "String": "Είσοδος {num}",

View File

@@ -384,7 +384,10 @@
"EnableAutoScroll": "Habilitar Desplazamiento Automático", "EnableAutoScroll": "Habilitar Desplazamiento Automático",
"ClearConsole": "Limpiar Consola", "ClearConsole": "Limpiar Consola",
"CopyToClipboard": "Copiar al Portapapeles", "CopyToClipboard": "Copiar al Portapapeles",
"Download": "Download" "Download": "Download",
"RegularExpression": "Regular Expression",
"Lines": "{lines} Lines",
"LogLevel": "Log Level ({cnt})"
}, },
"inverterchannelinfo": { "inverterchannelinfo": {
"String": "Cadena {num}", "String": "Cadena {num}",

View File

@@ -384,7 +384,10 @@
"EnableAutoScroll": "Abilita AutoScroll", "EnableAutoScroll": "Abilita AutoScroll",
"ClearConsole": "Pulisci Console", "ClearConsole": "Pulisci Console",
"CopyToClipboard": "Copia nella clipboard", "CopyToClipboard": "Copia nella clipboard",
"Download": "Download" "Download": "Download",
"RegularExpression": "Regular Expression",
"Lines": "{lines} Lines",
"LogLevel": "Log Level ({cnt})"
}, },
"inverterchannelinfo": { "inverterchannelinfo": {
"String": "Stringa {num}", "String": "Stringa {num}",

View File

@@ -384,7 +384,10 @@
"EnableAutoScroll": "Włącz automatyczne przewijani", "EnableAutoScroll": "Włącz automatyczne przewijani",
"ClearConsole": "Wyczyść konsolę", "ClearConsole": "Wyczyść konsolę",
"CopyToClipboard": "Kopiuj do schowka", "CopyToClipboard": "Kopiuj do schowka",
"Download": "Download" "Download": "Download",
"RegularExpression": "Regular Expression",
"Lines": "{lines} Lines",
"LogLevel": "Log Level ({cnt})"
}, },
"inverterchannelinfo": { "inverterchannelinfo": {
"String": "Numer panelu {num}", "String": "Numer panelu {num}",

View File

@@ -369,7 +369,10 @@
"EnableAutoScroll": "Automatisches Scrollen aktivieren", "EnableAutoScroll": "Automatisches Scrollen aktivieren",
"ClearConsole": "Konsole leeren", "ClearConsole": "Konsole leeren",
"CopyToClipboard": "In die Zwischenablage kopieren", "CopyToClipboard": "In die Zwischenablage kopieren",
"Download": "Herunterladen" "Download": "Herunterladen",
"RegularExpression": "Regulärer Ausdruck",
"Lines": "{lines} Zeilen",
"LogLevel": "Protokollierungsstufe ({cnt})"
}, },
"inverterchannelinfo": { "inverterchannelinfo": {
"String": "String {num}", "String": "String {num}",

View File

@@ -369,7 +369,10 @@
"EnableAutoScroll": "Enable Auto Scroll", "EnableAutoScroll": "Enable Auto Scroll",
"ClearConsole": "Clear Console", "ClearConsole": "Clear Console",
"CopyToClipboard": "Copy to clipboard", "CopyToClipboard": "Copy to clipboard",
"Download": "Download" "Download": "Download",
"RegularExpression": "Regular Expression",
"Lines": "{lines} Lines",
"LogLevel": "Log Level ({cnt})"
}, },
"inverterchannelinfo": { "inverterchannelinfo": {
"String": "String {num}", "String": "String {num}",

View File

@@ -351,7 +351,10 @@
"EnableAutoScroll": "Activer le défilement automatique", "EnableAutoScroll": "Activer le défilement automatique",
"ClearConsole": "Vider la console", "ClearConsole": "Vider la console",
"CopyToClipboard": "Copier dans le presse-papiers", "CopyToClipboard": "Copier dans le presse-papiers",
"Download": "Download" "Download": "Download",
"RegularExpression": "Regular Expression",
"Lines": "{lines} Lines",
"LogLevel": "Log Level ({cnt})"
}, },
"inverterchannelinfo": { "inverterchannelinfo": {
"String": "Ligne {num}", "String": "Ligne {num}",

View File

@@ -1,8 +1,85 @@
<template> <template>
<BasePage :title="$t('console.Console')" :isLoading="dataLoading"> <BasePage :title="$t('console.Console')" :isLoading="dataLoading">
<CardElement :text="$t('console.VirtualDebugConsole')" textVariant="text-bg-primary"> <CardElement :text="$t('console.VirtualDebugConsole')" textVariant="text-bg-primary">
<div class="row align-items-center mb-3"> <div class="btn-toolbar mb-3" role="toolbar">
<div class="col-auto mt-2"> <div class="input-group me-2">
<div class="input-group-text" id="btnSearch">
<BIconSearch />
</div>
<input
v-model="searchText"
type="text"
class="form-control"
:placeholder="$t('console.RegularExpression')"
aria-label="Search"
aria-describedby="btnSearch"
/>
</div>
<div class="btn-group me-2" role="group" aria-label="Filter by level">
<div class="dropdown">
<button
class="btn btn-outline-secondary dropdown-toggle"
type="button"
id="logLevelDropdown"
data-bs-toggle="dropdown"
aria-expanded="false"
>
<BIconFilter /> {{ $t('console.LogLevel', { cnt: activeLevels.length }) }}
</button>
<ul
class="dropdown-menu dropdown-menu-dark p-2"
aria-labelledby="logLevelDropdown"
style="min-width: 200px"
>
<li v-for="(label, tag) in levelMap" :key="tag">
<div class="form-check">
<input
class="form-check-input"
type="checkbox"
:id="`dropdown-filter-${label}`"
v-model="activeLevels"
:value="levelMap[tag]"
/>
<label class="form-check-label" :for="`dropdown-filter-${label}`">
{{ label.toUpperCase() }}
</label>
</div>
</li>
</ul>
</div>
</div>
<div class="btn-group me-2" role="group">
<button
type="button"
class="btn btn-primary"
:onClick="clearConsole"
:title="$t('console.ClearConsole')"
>
<BIconTrash />
</button>
</div>
<div class="btn-group me-2" role="group">
<button
type="button"
class="btn btn-secondary"
:onClick="copyConsole"
:title="$t('console.CopyToClipboard')"
>
<BIconClipboard />
</button>
<button
type="button"
class="btn btn-secondary"
:onClick="exportConsole"
:title="$t('console.Download')"
>
<BIconDownload />
</button>
</div>
<div class="col-auto ms-auto mt-2">
<div class="form-check form-switch"> <div class="form-check form-switch">
<input <input
class="form-check-input" class="form-check-input"
@@ -16,27 +93,15 @@
</label> </label>
</div> </div>
</div> </div>
<div class="col-auto ms-auto">
<div class="btn-group" role="group">
<button type="button" class="btn btn-primary" :onClick="clearConsole">
{{ $t('console.ClearConsole') }}
</button>
<button type="button" class="btn btn-secondary" :onClick="copyConsole">
{{ $t('console.CopyToClipboard') }}
</button>
<button type="button" class="btn btn-secondary" :onClick="exportConsole">
{{ $t('console.Download') }}
</button>
</div>
</div>
</div> </div>
<div id="log" ref="logRef" class="log-output" @scroll="handleScroll"> <div id="log" ref="logRef" class="log-output" @scroll="handleScroll">
<div v-for="(line, index) in lines" :key="index" class="log-line" :class="getLineClass(line.text)"> <div v-for="(line, index) in filteredLines" :key="index" class="log-line" :class="line.level">
<span class="timestamp">[{{ formatTimestamp(line.timestamp) }}]</span> <span class="timestamp">[{{ formatTimestamp(line.timestamp) }}]</span>
{{ line.text }} {{ line.text }}
</div> </div>
</div> </div>
{{ $t('console.Lines', { lines: $n(filteredLines.length) }) }}
</CardElement> </CardElement>
</BasePage> </BasePage>
</template> </template>
@@ -45,17 +110,24 @@
import BasePage from '@/components/BasePage.vue'; import BasePage from '@/components/BasePage.vue';
import CardElement from '@/components/CardElement.vue'; import CardElement from '@/components/CardElement.vue';
import { authUrl } from '@/utils/authentication'; import { authUrl } from '@/utils/authentication';
import { BIconClipboard, BIconDownload, BIconFilter, BIconSearch, BIconTrash } from 'bootstrap-icons-vue';
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
interface LogLine { interface LogLine {
text: string; text: string;
timestamp: Date; timestamp: Date;
level: string;
} }
export default defineComponent({ export default defineComponent({
components: { components: {
BasePage, BasePage,
CardElement, CardElement,
BIconDownload,
BIconClipboard,
BIconTrash,
BIconFilter,
BIconSearch,
}, },
data() { data() {
return { return {
@@ -63,6 +135,8 @@ export default defineComponent({
heartInterval: 0, heartInterval: 0,
dataLoading: true, dataLoading: true,
autoScroll: true, autoScroll: true,
activeLevels: ['error', 'warning', 'info', 'debug', 'verbose'],
searchText: '',
lines: [] as LogLine[], lines: [] as LogLine[],
buffer: '', buffer: '',
levelMap: { levelMap: {
@@ -81,6 +155,15 @@ export default defineComponent({
unmounted() { unmounted() {
this.closeSocket(); this.closeSocket();
}, },
computed: {
filteredLines(): LogLine[] {
return this.lines.filter((line) => {
const regexp = new RegExp(this.searchText, 'i');
const found = regexp.test(line.text);
return this.activeLevels.includes(line.level) && found;
});
},
},
methods: { methods: {
initSocket() { initSocket() {
console.log('Starting connection to WebSocket Server'); console.log('Starting connection to WebSocket Server');
@@ -104,6 +187,7 @@ export default defineComponent({
this.lines.push({ this.lines.push({
text: line, text: line,
timestamp: new Date(), // assign time of message arrival timestamp: new Date(), // assign time of message arrival
level: this.getLineClass(line),
}); });
this.$nextTick(() => { this.$nextTick(() => {
if (this.autoScroll) { if (this.autoScroll) {
@@ -189,7 +273,7 @@ export default defineComponent({
this.buffer = ''; this.buffer = '';
}, },
copyConsole() { copyConsole() {
const content = this.lines const content = this.filteredLines
.map((line) => `[${this.formatTimestamp(line.timestamp)}] ${line.text}`) .map((line) => `[${this.formatTimestamp(line.timestamp)}] ${line.text}`)
.join('\n'); .join('\n');
navigator.clipboard navigator.clipboard
@@ -202,7 +286,7 @@ export default defineComponent({
}); });
}, },
exportConsole() { exportConsole() {
const content = this.lines const content = this.filteredLines
.map((line) => `[${this.formatTimestamp(line.timestamp)}] ${line.text}`) .map((line) => `[${this.formatTimestamp(line.timestamp)}] ${line.text}`)
.join('\n'); .join('\n');
const timestamp = this.getFileTimestamp(); const timestamp = this.getFileTimestamp();
@@ -234,7 +318,7 @@ export default defineComponent({
}); });
</script> </script>
<style> <style scoped>
.log-output { .log-output {
height: 500px; height: 500px;
max-height: 500px; max-height: 500px;