Skip to content

Commit 696d8e5

Browse files
authored
Add search filter UI (#70)
1 parent 6b17e55 commit 696d8e5

File tree

12 files changed

+788
-562
lines changed

12 files changed

+788
-562
lines changed

docs/advanced-search-queries.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ The search query allows for more fine-grained control over the search results. T
88
| `after:<date>`,`after:"<date>"` | `a` | Show all logs messages that occur after the specified date. |
99
| `severity:<pipe-separated-string>` | `s` | Show all logs messages that match the given severity/severities. |
1010
| `channel:<pipe-separated-string>` | `c` | Show all logs messages that match the given channel(s). |
11-
| `after:<date>`,`after:"<date>"` | `a` | Show all logs messages that occur after the specified date. |
1211
| `exclude:<word>`,`exclude:"<words>"` | `-` | Exclude the specific sentence from the results. Can be specified multiple times |
1312
| `context:<string>`, `context:<key>=<value>` | | Show all logs messages that match the given context. |
1413
| `extra:<string>`, `extra:<key>=<value>` | | Show all logs messages that match the given extra. |
Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
## Disable the default monolog configuration
22

3-
If you want to disable the default monolog configuration, add the following to your configuration:
4-
3+
If you want to disable the default monolog configuration, overwrite `log_files` with your own configuration. For example:
54
```yaml
65
# config/packages/fd_log_viewer.yaml
76
fd_log_viewer:
8-
enable_default_monolog: false
7+
log_files:
8+
apache-access:
9+
type: http-access
10+
name: Apache2 access
11+
finder:
12+
in: "/var/log/apache2"
13+
name: "access.log"
914
```

docs/images/preview.png

-268 KB
Loading

frontend/package-lock.json

Lines changed: 549 additions & 483 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
<script setup lang="ts">
2+
import {ref} from 'vue';
3+
4+
const expanded = ref(false);
5+
const emit = defineEmits(['add']);
6+
7+
const addFilter = (event: MouseEvent) => {
8+
const target = event.target as HTMLElement;
9+
const filter = target.closest('[data-role=filter]') as HTMLInputElement;
10+
const fields = Array.from(filter.querySelectorAll('input'));
11+
let pattern = String(filter.dataset.pattern);
12+
const strip = filter.dataset.strip;
13+
let replaced = false;
14+
15+
for (const input of fields) {
16+
const key = input.name;
17+
let val = input.value.trim();
18+
if (strip !== undefined) {
19+
val = val.replace(strip, '');
20+
}
21+
22+
const escapeVal = (val.indexOf(' ') === -1 ? val : '"' + val + '"');
23+
const matches = pattern.match('\\{' + key + '(=)?\\}');
24+
if (matches !== null) {
25+
pattern = pattern.replace(matches[0], val === '' ? '' : escapeVal + (matches[1] ?? ''));
26+
replaced = replaced || val !== '';
27+
}
28+
input.value = '';
29+
}
30+
31+
if (replaced) {
32+
emit('add', pattern);
33+
}
34+
}
35+
</script>
36+
37+
<template>
38+
<button ref="filterButton"
39+
class="btn btn-outline-secondary dropdown-toggle"
40+
type="button"
41+
:aria-expanded="expanded"
42+
@click="expanded = !expanded">Filter
43+
</button>
44+
<div class="dropdown-menu slv-dropdown-menu" :class="{'d-block': expanded}">
45+
<div class="px-2">
46+
<div class="input-group mb-1" data-role="filter" data-pattern="before:{value}">
47+
<span class="slv-input-label input-group-text" id="filter-date-start">Before</span>
48+
<input name="value" type="datetime-local" class="form-control" aria-label="Before" aria-describedby="filter-date-start">
49+
<button class="btn btn-outline-primary" type="button" @click="addFilter">Add</button>
50+
</div>
51+
<div class="input-group mb-1" data-role="filter" data-pattern="after:{value}">
52+
<span class="slv-input-label input-group-text" id="filter-date-end">After</span>
53+
<input name="value" type="datetime-local" class="form-control" aria-label="After" aria-describedby="filter-date-end">
54+
<button class="btn btn-outline-primary" type="button" @click="addFilter">Add</button>
55+
</div>
56+
<div class="input-group mb-1" data-role="filter" data-pattern="severity:{value}" data-strip=" ">
57+
<span class="slv-input-label input-group-text" id="filter-severity">Severity</span>
58+
<input name="value"
59+
type="text"
60+
class="form-control"
61+
placeholder="Separate multiple by pipe symbol"
62+
aria-label="Severity"
63+
aria-describedby="filter-severity">
64+
<button class="btn btn-outline-primary" type="button" @click="addFilter">Add</button>
65+
</div>
66+
<div class="input-group mb-1" data-role="filter" data-pattern="channel:{value}" data-strip=" ">
67+
<span class="slv-input-label input-group-text" id="filter-severity">Channels</span>
68+
<input name="value"
69+
type="text"
70+
class="form-control"
71+
placeholder="Separate multiple by pipe symbol"
72+
aria-label="Severity"
73+
aria-describedby="filter-severity">
74+
<button class="btn btn-outline-primary" type="button" @click="addFilter">Add</button>
75+
</div>
76+
<div class="input-group mb-1" data-role="filter" data-pattern="exclude:{value}">
77+
<span class="slv-input-label input-group-text" id="filter-exclude">Exclude</span>
78+
<input name="value" type="text" class="form-control" aria-label="Exclude string" aria-describedby="filter-exclude">
79+
<button class="btn btn-outline-primary" type="button" @click="addFilter">Add</button>
80+
</div>
81+
<div class="input-group mb-1" data-role="filter" data-pattern="context:{key=}{value}">
82+
<span class="slv-input-label input-group-text" id="filter-context">Context</span>
83+
<input name="key"
84+
type="text"
85+
class="form-control"
86+
placeholder="key (optional)"
87+
aria-label="Context key (optional)"
88+
aria-describedby="filter-context">
89+
<input name="value" type="text" class="form-control" placeholder="search" aria-label="Context" aria-describedby="filter-context">
90+
<button class="btn btn-outline-primary" type="button" @click="addFilter">Add</button>
91+
</div>
92+
<div class="input-group mb-1" data-role="filter" data-pattern="extra:{key=}{value}">
93+
<span class="slv-input-label input-group-text" id="filter-extra">Extra</span>
94+
<input name="key"
95+
type="text"
96+
class="form-control"
97+
placeholder="key (optional)"
98+
aria-label="Extra key (optional)"
99+
aria-describedby="filter-extra">
100+
<input name=value type="text" class="form-control" placeholder="search" aria-label="Extra" aria-describedby="filter-extra">
101+
<button class="btn btn-outline-primary" type="button" @click="addFilter">Add</button>
102+
</div>
103+
<div>
104+
<button class="btn btn-sm btn-primary float-end" type="button" @click="expanded = !expanded">Close</button>
105+
</div>
106+
</div>
107+
</div>
108+
</template>
109+
110+
<style scoped>
111+
.slv-dropdown-menu {
112+
top: 37px;
113+
}
114+
115+
.slv-input-label {
116+
width: 100px;
117+
}
118+
</style>
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<script setup lang="ts">
2+
import SearchFilter from '@/components/SearchFilter.vue';
3+
import {ref} from 'vue';
4+
5+
const searchRef = ref<HTMLInputElement>();
6+
const query = defineModel<string>('query');
7+
const sort = defineModel<string>('sort');
8+
const perPage = defineModel<string>('perPage');
9+
const emit = defineEmits(['navigate']);
10+
11+
const focus = (): void => {
12+
searchRef.value?.focus();
13+
}
14+
15+
defineProps<{
16+
badRequest: boolean,
17+
}>();
18+
defineExpose({focus});
19+
</script>
20+
21+
<template>
22+
<div class="input-group">
23+
<SearchFilter @add="(value) => query = (query === '' ? value : query + ' ' + value)"/>
24+
25+
<input type="text"
26+
class="form-control"
27+
:class="{'is-invalid': badRequest}"
28+
ref="searchRef"
29+
placeholder="Search log entries."
30+
aria-label="Search log entries."
31+
aria-describedby="button-search"
32+
@change="emit('navigate')"
33+
v-model="query">
34+
35+
<select class="slv-menu-sort-direction form-control"
36+
aria-label="Sort direction"
37+
title="Sort direction"
38+
v-model="sort"
39+
@change="emit('navigate')">
40+
<option value="desc">Newest First</option>
41+
<option value="asc">Oldest First</option>
42+
</select>
43+
44+
<select class="slv-menu-page-size form-control"
45+
aria-label="Entries per page"
46+
title="Entries per page"
47+
v-model="perPage"
48+
@change="emit('navigate')">
49+
<option value="50">50</option>
50+
<option value="100">100</option>
51+
<option value="150">150</option>
52+
<option value="200">200</option>
53+
<option value="250">250</option>
54+
<option value="300">300</option>
55+
</select>
56+
57+
<button class="slv-log-search-btn btn btn-outline-primary" type="button" id="button-search" @click="emit('navigate')">Search</button>
58+
</div>
59+
</template>
60+
61+
<style scoped>
62+
.slv-menu-sort-direction, .slv-menu-page-size, .slv-log-search-btn {
63+
max-width: fit-content;
64+
}
65+
</style>

frontend/src/views/LogView.vue

Lines changed: 9 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<script setup lang="ts">
22
import LogRecord from '@/components/LogRecord.vue';
33
import PerformanceDetails from '@/components/PerformanceDetails.vue';
4+
import SearchForm from '@/components/SearchForm.vue';
45
import ParameterBag from '@/models/ParameterBag';
56
import {useHostsStore} from '@/stores/hosts';
67
import {useLogRecordStore} from '@/stores/log_records';
@@ -14,7 +15,7 @@ const logRecordStore = useLogRecordStore();
1415
const hostsStore = useHostsStore();
1516
const searchStore = useSearchStore();
1617
17-
const searchRef = ref<HTMLInputElement>();
18+
const searchRef = ref<InstanceType<typeof SearchForm>>()
1819
const file = ref('');
1920
const offset = ref(0);
2021
const badRequest = ref(false);
@@ -67,41 +68,13 @@ onMounted(() => {
6768
<template>
6869
<div class="slv-content h-100 overflow-hidden">
6970
<div class="d-flex align-items-stretch pt-1">
70-
<div class="flex-grow-1 input-group">
71-
<input type="text"
72-
class="form-control"
73-
:class="{'is-invalid': badRequest}"
74-
ref="searchRef"
75-
placeholder="Search log entries, Use severity:, channel:, before:, after:, or exclude: to fine-tune the search."
76-
aria-label="Search log entries, Use severity:, channel:, before:, after:, or exclude: to fine-tune the search."
77-
aria-describedby="button-search"
78-
@change="navigate"
79-
v-model="searchStore.query">
80-
81-
<select class="slv-menu-sort-direction form-control"
82-
aria-label="Sort direction"
83-
title="Sort direction"
84-
v-model="searchStore.sort"
85-
@change="navigate">
86-
<option value="desc">Newest First</option>
87-
<option value="asc">Oldest First</option>
88-
</select>
89-
90-
<select class="slv-menu-page-size form-control"
91-
aria-label="Entries per page"
92-
title="Entries per page"
93-
v-model="searchStore.perPage"
94-
@change="navigate">
95-
<option value="50">50</option>
96-
<option value="100">100</option>
97-
<option value="150">150</option>
98-
<option value="200">200</option>
99-
<option value="250">250</option>
100-
<option value="300">300</option>
101-
</select>
102-
103-
<button class="slv-log-search-btn btn btn-outline-primary" type="button" id="button-search" @click="navigate">Search</button>
104-
</div>
71+
<search-form class="flex-grow-1"
72+
ref="searchRef"
73+
:bad-request="badRequest"
74+
v-model:query="searchStore.query"
75+
v-model:sort="searchStore.sort"
76+
v-model:perPage="searchStore.perPage"
77+
@navigate="navigate"></search-form>
10578

10679
<button class="btn btn-dark ms-1 me-1" type="button" aria-label="Refresh" title="Refresh" @click="load">
10780
<i class="bi bi-arrow-clockwise"></i>
@@ -142,8 +115,4 @@ onMounted(() => {
142115
.slv-entries {
143116
--bs-list-group-border-radius: 0;
144117
}
145-
146-
.slv-menu-sort-direction, .slv-menu-page-size, .slv-log-search-btn {
147-
max-width: fit-content;
148-
}
149118
</style>
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
{
22
"src/main.ts": {
3-
"file": "assets/main-2He2JVbP.js",
3+
"file": "assets/main-8G18k7Hp.js",
44
"src": "src/main.ts",
55
"isEntry": true
66
},
77
"style.css": {
8-
"file": "assets/style-2zzsHFJL.css",
8+
"file": "assets/style-BG1K9lyT.css",
99
"src": "style.css"
1010
}
1111
}

0 commit comments

Comments
 (0)