1
0
mirror of https://github.com/garraflavatra/rolens.git synced 2025-07-20 22:48:02 +00:00

Add ability to see host logs (#54)

Squashed commit of the following:

commit 93b2d67cef
Author: Romein van Buren <romein@vburen.nl>
Date:   Sat Jul 1 20:07:33 2023 +0200

    Add filter functionality

commit 30b65a198f
Author: Romein van Buren <romein@vburen.nl>
Date:   Sat Jul 1 19:27:20 2023 +0200

    Renamed `form-row` class to `formrow`

commit 21afb01ea1
Author: Romein van Buren <romein@vburen.nl>
Date:   Sat Jul 1 19:26:04 2023 +0200

    Hide object types in object grid

commit 037d5432a4
Author: Romein van Buren <romein@vburen.nl>
Date:   Sat Jul 1 19:21:54 2023 +0200

    Make auto reload interval input smaller

commit 49d5022027
Author: Romein van Buren <romein@vburen.nl>
Date:   Sat Jul 1 15:08:00 2023 +0200

    Implement logs autoreload

commit 1f8984766b
Author: Romein van Buren <romein@vburen.nl>
Date:   Sat Jul 1 15:04:00 2023 +0200

    Return on error

commit 9c85259964
Author: Romein van Buren <romein@vburen.nl>
Date:   Sat Jul 1 15:03:37 2023 +0200

    Add log error handling

commit 7a98a63866
Author: Romein van Buren <romein@vburen.nl>
Date:   Sat Jul 1 14:46:39 2023 +0200

    Update log tab icon

commit f30827ae2e
Author: Romein van Buren <romein@vburen.nl>
Date:   Sat Jul 1 14:41:59 2023 +0200

    Add host log panel
This commit is contained in:
2023-07-01 20:30:43 +02:00
parent 0b9f23365b
commit 24b0df95df
20 changed files with 343 additions and 32 deletions

View File

@ -0,0 +1,194 @@
<script>
import Grid from '$components/grid.svelte';
import Icon from '$components/icon.svelte';
import ObjectViewer from '$components/objectviewer.svelte';
import input from '$lib/actions/input';
import { logComponents, logLevels } from '$lib/mongo';
import { BrowserOpenURL } from '$wails/runtime/runtime';
import { onDestroy } from 'svelte';
export let host;
const autoReloadIntervals = [ 1, 2, 5, 10, 30, 60 ];
let filter = 'global';
let severityFilter = '';
let componentFilter = '';
let logs;
let total = 0;
let error = '';
let copySucceeded = false;
let autoReloadInterval = 0;
let objectViewerData;
let interval;
$: (filter || severityFilter || componentFilter) && refresh();
$: busy = !logs && !error && 'Requesting logs…';
$: if (autoReloadInterval) {
if (interval) {
clearInterval(interval);
}
interval = setInterval(refresh, autoReloadInterval * 1000);
}
async function refresh() {
let _logs = [];
({ logs: _logs, total, error } = await host.getLogs(filter));
logs = [];
for (let index = 0; index < _logs.length; index++) {
const log = JSON.parse(_logs[index]);
const matchesLevel = severityFilter ? log.s?.startsWith(severityFilter) : true;
const matchesComponent = componentFilter ? (componentFilter === log.c?.toUpperCase()) : true;
if (matchesLevel && matchesComponent) {
log._index = index;
log.s = logLevels[log.s] || log.s;
logs = [ ...logs, log ];
}
}
}
function openFilterDocs() {
BrowserOpenURL('https://www.mongodb.com/docs/manual/reference/command/getLog/#command-fields');
}
function openLogDetail(event) {
objectViewerData = logs[event.detail.index];
}
async function copy() {
const json = JSON.stringify(host.status, undefined, '\t');
await navigator.clipboard.writeText(json);
copySucceeded = true;
setTimeout(() => copySucceeded = false, 1500);
}
onDestroy(() => {
if (interval) {
clearInterval(interval);
}
});
</script>
<div class="stats">
<div class="formrow">
<label class="field">
<span class="label">Auto reload (seconds)</span>
<input type="number" class="autoreloadinput" bind:value={autoReloadInterval} list="autoreloadintervals" use:input />
</label>
<label class="field">
<span class="label">Log type</span>
<select bind:value={filter}>
<option value="global">Global</option>
<option value="startupWarnings">Startup warnings</option>
</select>
<button class="button secondary" on:click={openFilterDocs} title="Documentation">
<Icon name="?" />
</button>
</label>
</div>
<div class="formrow">
<label class="field">
<span class="label">Severity</span>
<select bind:value={severityFilter}>
<option value="">All</option>
{#each Object.entries(logLevels) as [ value, name ]}
<option {value}>{value} ({name})</option>
{/each}
</select>
</label>
<label class="field">
<span class="label">Component</span>
<select bind:value={componentFilter}>
<option value="">All</option>
{#each logComponents as value}
<option {value}>{value}</option>
{/each}
</select>
</label>
</div>
<div class="grid">
<Grid
items={logs || []}
columns={[
{ title: 'Date', key: 't.$date' },
{ title: 'Severity', key: 's' },
{ title: 'ID', key: 'id' },
{ title: 'Component', key: 'c' },
{ title: 'Context', key: 'ctx' },
{ title: 'Message', key: 'msg' },
]}
key="_index"
showHeaders
errorTitle={error ? 'Error fetching server status' : ''}
errorDescription={error}
on:trigger={openLogDetail}
{busy}
/>
</div>
<div class="controls">
<button class="button" on:click={refresh}>
<Icon name="reload" spin={busy} /> Reload
</button>
<button class="button secondary" on:click={copy} disabled={!host.status}>
<Icon name={copySucceeded ? 'check' : 'clipboard'} />
Copy JSON
</button>
{#if total}
<div class="total">
Total: {total}
</div>
{/if}
</div>
</div>
{#if objectViewerData}
<ObjectViewer bind:data={objectViewerData} readonly />
{/if}
<datalist id="autoreloadintervals">
{#each autoReloadIntervals as value}
<option {value} />
{/each}
</datalist>
<style>
.stats {
display: grid;
gap: 0.5rem;
grid-template: auto auto 1fr auto / 1fr;
}
.formrow {
display: grid;
gap: 0.5rem;
grid-template: 1fr / 1fr 1fr;
}
.grid {
overflow: auto;
min-height: 0;
min-width: 0;
border: 1px solid #ccc;
}
.controls {
display: flex;
align-items: center;
gap: 0.2rem;
}
.total {
margin-left: auto;
}
.autoreloadinput {
width: 1.5rem;
}
</style>