Skip to content

Commit

Permalink
Add search filter UI (#70)
Browse files Browse the repository at this point in the history
  • Loading branch information
frankdekker authored Mar 17, 2024
1 parent 6b17e55 commit 696d8e5
Show file tree
Hide file tree
Showing 12 changed files with 788 additions and 562 deletions.
1 change: 0 additions & 1 deletion docs/advanced-search-queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ The search query allows for more fine-grained control over the search results. T
| `after:<date>`,`after:"<date>"` | `a` | Show all logs messages that occur after the specified date. |
| `severity:<pipe-separated-string>` | `s` | Show all logs messages that match the given severity/severities. |
| `channel:<pipe-separated-string>` | `c` | Show all logs messages that match the given channel(s). |
| `after:<date>`,`after:"<date>"` | `a` | Show all logs messages that occur after the specified date. |
| `exclude:<word>`,`exclude:"<words>"` | `-` | Exclude the specific sentence from the results. Can be specified multiple times |
| `context:<string>`, `context:<key>=<value>` | | Show all logs messages that match the given context. |
| `extra:<string>`, `extra:<key>=<value>` | | Show all logs messages that match the given extra. |
Expand Down
11 changes: 8 additions & 3 deletions docs/disabling-default-monolog-configuration.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
## Disable the default monolog configuration

If you want to disable the default monolog configuration, add the following to your configuration:

If you want to disable the default monolog configuration, overwrite `log_files` with your own configuration. For example:
```yaml
# config/packages/fd_log_viewer.yaml
fd_log_viewer:
enable_default_monolog: false
log_files:
apache-access:
type: http-access
name: Apache2 access
finder:
in: "/var/log/apache2"
name: "access.log"
```
Binary file modified docs/images/preview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1,032 changes: 549 additions & 483 deletions frontend/package-lock.json

Large diffs are not rendered by default.

118 changes: 118 additions & 0 deletions frontend/src/components/SearchFilter.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<script setup lang="ts">
import {ref} from 'vue';
const expanded = ref(false);
const emit = defineEmits(['add']);
const addFilter = (event: MouseEvent) => {
const target = event.target as HTMLElement;
const filter = target.closest('[data-role=filter]') as HTMLInputElement;
const fields = Array.from(filter.querySelectorAll('input'));
let pattern = String(filter.dataset.pattern);
const strip = filter.dataset.strip;
let replaced = false;
for (const input of fields) {
const key = input.name;
let val = input.value.trim();
if (strip !== undefined) {
val = val.replace(strip, '');
}
const escapeVal = (val.indexOf(' ') === -1 ? val : '"' + val + '"');
const matches = pattern.match('\\{' + key + '(=)?\\}');
if (matches !== null) {
pattern = pattern.replace(matches[0], val === '' ? '' : escapeVal + (matches[1] ?? ''));
replaced = replaced || val !== '';
}
input.value = '';
}
if (replaced) {
emit('add', pattern);
}
}
</script>

<template>
<button ref="filterButton"
class="btn btn-outline-secondary dropdown-toggle"
type="button"
:aria-expanded="expanded"
@click="expanded = !expanded">Filter
</button>
<div class="dropdown-menu slv-dropdown-menu" :class="{'d-block': expanded}">
<div class="px-2">
<div class="input-group mb-1" data-role="filter" data-pattern="before:{value}">
<span class="slv-input-label input-group-text" id="filter-date-start">Before</span>
<input name="value" type="datetime-local" class="form-control" aria-label="Before" aria-describedby="filter-date-start">
<button class="btn btn-outline-primary" type="button" @click="addFilter">Add</button>
</div>
<div class="input-group mb-1" data-role="filter" data-pattern="after:{value}">
<span class="slv-input-label input-group-text" id="filter-date-end">After</span>
<input name="value" type="datetime-local" class="form-control" aria-label="After" aria-describedby="filter-date-end">
<button class="btn btn-outline-primary" type="button" @click="addFilter">Add</button>
</div>
<div class="input-group mb-1" data-role="filter" data-pattern="severity:{value}" data-strip=" ">
<span class="slv-input-label input-group-text" id="filter-severity">Severity</span>
<input name="value"
type="text"
class="form-control"
placeholder="Separate multiple by pipe symbol"
aria-label="Severity"
aria-describedby="filter-severity">
<button class="btn btn-outline-primary" type="button" @click="addFilter">Add</button>
</div>
<div class="input-group mb-1" data-role="filter" data-pattern="channel:{value}" data-strip=" ">
<span class="slv-input-label input-group-text" id="filter-severity">Channels</span>
<input name="value"
type="text"
class="form-control"
placeholder="Separate multiple by pipe symbol"
aria-label="Severity"
aria-describedby="filter-severity">
<button class="btn btn-outline-primary" type="button" @click="addFilter">Add</button>
</div>
<div class="input-group mb-1" data-role="filter" data-pattern="exclude:{value}">
<span class="slv-input-label input-group-text" id="filter-exclude">Exclude</span>
<input name="value" type="text" class="form-control" aria-label="Exclude string" aria-describedby="filter-exclude">
<button class="btn btn-outline-primary" type="button" @click="addFilter">Add</button>
</div>
<div class="input-group mb-1" data-role="filter" data-pattern="context:{key=}{value}">
<span class="slv-input-label input-group-text" id="filter-context">Context</span>
<input name="key"
type="text"
class="form-control"
placeholder="key (optional)"
aria-label="Context key (optional)"
aria-describedby="filter-context">
<input name="value" type="text" class="form-control" placeholder="search" aria-label="Context" aria-describedby="filter-context">
<button class="btn btn-outline-primary" type="button" @click="addFilter">Add</button>
</div>
<div class="input-group mb-1" data-role="filter" data-pattern="extra:{key=}{value}">
<span class="slv-input-label input-group-text" id="filter-extra">Extra</span>
<input name="key"
type="text"
class="form-control"
placeholder="key (optional)"
aria-label="Extra key (optional)"
aria-describedby="filter-extra">
<input name=value type="text" class="form-control" placeholder="search" aria-label="Extra" aria-describedby="filter-extra">
<button class="btn btn-outline-primary" type="button" @click="addFilter">Add</button>
</div>
<div>
<button class="btn btn-sm btn-primary float-end" type="button" @click="expanded = !expanded">Close</button>
</div>
</div>
</div>
</template>

<style scoped>
.slv-dropdown-menu {
top: 37px;
}
.slv-input-label {
width: 100px;
}
</style>
65 changes: 65 additions & 0 deletions frontend/src/components/SearchForm.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<script setup lang="ts">
import SearchFilter from '@/components/SearchFilter.vue';
import {ref} from 'vue';
const searchRef = ref<HTMLInputElement>();
const query = defineModel<string>('query');
const sort = defineModel<string>('sort');
const perPage = defineModel<string>('perPage');
const emit = defineEmits(['navigate']);
const focus = (): void => {
searchRef.value?.focus();
}
defineProps<{
badRequest: boolean,
}>();
defineExpose({focus});
</script>

<template>
<div class="input-group">
<SearchFilter @add="(value) => query = (query === '' ? value : query + ' ' + value)"/>

<input type="text"
class="form-control"
:class="{'is-invalid': badRequest}"
ref="searchRef"
placeholder="Search log entries."
aria-label="Search log entries."
aria-describedby="button-search"
@change="emit('navigate')"
v-model="query">

<select class="slv-menu-sort-direction form-control"
aria-label="Sort direction"
title="Sort direction"
v-model="sort"
@change="emit('navigate')">
<option value="desc">Newest First</option>
<option value="asc">Oldest First</option>
</select>

<select class="slv-menu-page-size form-control"
aria-label="Entries per page"
title="Entries per page"
v-model="perPage"
@change="emit('navigate')">
<option value="50">50</option>
<option value="100">100</option>
<option value="150">150</option>
<option value="200">200</option>
<option value="250">250</option>
<option value="300">300</option>
</select>

<button class="slv-log-search-btn btn btn-outline-primary" type="button" id="button-search" @click="emit('navigate')">Search</button>
</div>
</template>

<style scoped>
.slv-menu-sort-direction, .slv-menu-page-size, .slv-log-search-btn {
max-width: fit-content;
}
</style>
49 changes: 9 additions & 40 deletions frontend/src/views/LogView.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script setup lang="ts">
import LogRecord from '@/components/LogRecord.vue';
import PerformanceDetails from '@/components/PerformanceDetails.vue';
import SearchForm from '@/components/SearchForm.vue';
import ParameterBag from '@/models/ParameterBag';
import {useHostsStore} from '@/stores/hosts';
import {useLogRecordStore} from '@/stores/log_records';
Expand All @@ -14,7 +15,7 @@ const logRecordStore = useLogRecordStore();
const hostsStore = useHostsStore();
const searchStore = useSearchStore();
const searchRef = ref<HTMLInputElement>();
const searchRef = ref<InstanceType<typeof SearchForm>>()
const file = ref('');
const offset = ref(0);
const badRequest = ref(false);
Expand Down Expand Up @@ -67,41 +68,13 @@ onMounted(() => {
<template>
<div class="slv-content h-100 overflow-hidden">
<div class="d-flex align-items-stretch pt-1">
<div class="flex-grow-1 input-group">
<input type="text"
class="form-control"
:class="{'is-invalid': badRequest}"
ref="searchRef"
placeholder="Search log entries, Use severity:, channel:, before:, after:, or exclude: to fine-tune the search."
aria-label="Search log entries, Use severity:, channel:, before:, after:, or exclude: to fine-tune the search."
aria-describedby="button-search"
@change="navigate"
v-model="searchStore.query">

<select class="slv-menu-sort-direction form-control"
aria-label="Sort direction"
title="Sort direction"
v-model="searchStore.sort"
@change="navigate">
<option value="desc">Newest First</option>
<option value="asc">Oldest First</option>
</select>

<select class="slv-menu-page-size form-control"
aria-label="Entries per page"
title="Entries per page"
v-model="searchStore.perPage"
@change="navigate">
<option value="50">50</option>
<option value="100">100</option>
<option value="150">150</option>
<option value="200">200</option>
<option value="250">250</option>
<option value="300">300</option>
</select>

<button class="slv-log-search-btn btn btn-outline-primary" type="button" id="button-search" @click="navigate">Search</button>
</div>
<search-form class="flex-grow-1"
ref="searchRef"
:bad-request="badRequest"
v-model:query="searchStore.query"
v-model:sort="searchStore.sort"
v-model:perPage="searchStore.perPage"
@navigate="navigate"></search-form>

<button class="btn btn-dark ms-1 me-1" type="button" aria-label="Refresh" title="Refresh" @click="load">
<i class="bi bi-arrow-clockwise"></i>
Expand Down Expand Up @@ -142,8 +115,4 @@ onMounted(() => {
.slv-entries {
--bs-list-group-border-radius: 0;
}
.slv-menu-sort-direction, .slv-menu-page-size, .slv-log-search-btn {
max-width: fit-content;
}
</style>
4 changes: 2 additions & 2 deletions src/Resources/public/.vite/manifest.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"src/main.ts": {
"file": "assets/main-2He2JVbP.js",
"file": "assets/main-8G18k7Hp.js",
"src": "src/main.ts",
"isEntry": true
},
"style.css": {
"file": "assets/style-2zzsHFJL.css",
"file": "assets/style-BG1K9lyT.css",
"src": "style.css"
}
}
Loading

0 comments on commit 696d8e5

Please sign in to comment.