Skip to content

Commit d18d23c

Browse files
authored
Add search expressions (#26)
1 parent e4e4589 commit d18d23c

File tree

64 files changed

+1500
-114
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+1500
-114
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,5 @@ By default, the application is available at: `{APP_URL}/log-viewer`.
8484
- [Disabling the default monolog configuration](docs/disabling-default-monolog-configuration.md)
8585
- [Adding additional log files](docs/adding-additional-log-files.md)
8686
- [Configuring the back home url](docs/configuring-the-back-home-route.md)
87+
- [Advanced search queries](docs/search-queries.md)
8788
- [Full configuration reference](docs/configuration-reference.md)

docs/advanced-search-queries.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Searching logs
2+
3+
The search query allows for more fine-grained control over the search results. The following operators are supported:
4+
5+
| Operator | Short | Description |
6+
|--------------------------------------|-------|---------------------------------------------------------------------------------|
7+
| `before:<date>`,`before:"<date>"` | `b` | Show all logs messages that occur before the specified date. |
8+
| `after:<date>`,`after:"<date>"` | `a` | Show all logs messages that occur after the specified date. |
9+
| `exclude:<word>`,`exclude:"<words>"` | `-` | Exclude the specific sentence from the results. Can be specified multiple times |
10+
11+
## Example
12+
13+
Search all log entries between `2020-01-01` and `2020-01-31`, excluding all entries that contain the word `"Controller"` and must
14+
include `"Exception"`.
15+
16+
```text
17+
before:2020-01-31 after:2020-01-01 exclude:Controller "Failed to read"
18+
```
19+
20+
### In shorthand
21+
22+
```text
23+
b:2020-01-31 a:2020-01-01 -:Controller "Failed to read"
24+
```
25+
26+
### Multiple exclusions
27+
28+
```text
29+
before:2020-01-31 after:2020-01-01 exclude:Controller exclude:404
30+
```
31+
32+
### Handling whitespace
33+
34+
If you want to search for a sentence that contains whitespace, you can use double quotes to indicate that the words should be treated as a single
35+
word.
36+
37+
```text
38+
before:"2020-01-31 23:59:59" after:"2020-01-01 00:00:00" exclude:"new IndexController" "Failed to read"
39+
```

frontend/src/router/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import ErrorView from '@/views/ErrorView.vue';
12
import FileNotFoundView from '@/views/FileNotFoundView.vue';
23
import HomeView from '@/views/HomeView.vue'
34
import LogView from '@/views/LogView.vue';
@@ -21,6 +22,11 @@ function router(baseUri: string) {
2122
path: '/404',
2223
name: 'file-not-found',
2324
component: FileNotFoundView
25+
},
26+
{
27+
path: '/5XX',
28+
name: 'error',
29+
component: ErrorView
2430
}
2531
]
2632
});

frontend/src/stores/log_records.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,14 @@ export const useLogRecordStore = defineStore('log_records', () => {
3737
const response = await axios.get<LogRecords>('/api/logs', {params: params});
3838
records.value = response.data;
3939
} catch (e) {
40+
if (e instanceof AxiosError && e.response?.status === 400) {
41+
throw new Error('bad-request');
42+
}
4043
if (e instanceof AxiosError && e.response?.status === 404) {
41-
throw e;
44+
throw new Error('file-not-found');
45+
}
46+
if (e instanceof AxiosError && [500, 501, 502, 503, 504].includes(Number(e.response?.status))) {
47+
throw new Error('error');
4248
}
4349
console.error(e);
4450
records.value = defaultData;

frontend/src/views/ErrorView.vue

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<template>
2+
<div class="failure">
3+
<div class="alert alert-danger label">
4+
An error occurred while reading the log file.
5+
</div>
6+
</div>
7+
</template>
8+
9+
<style scoped>
10+
.failure {
11+
position: relative;
12+
13+
.label {
14+
position: absolute;
15+
top: 50%;
16+
left: 50%;
17+
transform: translate(-50%, -50%);
18+
}
19+
}
20+
</style>
21+
<script setup lang="ts"></script>

frontend/src/views/LogView.vue

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,14 @@ const router = useRouter();
1414
const route = useRoute();
1515
const logRecordStore = useLogRecordStore();
1616
17-
const file = ref('');
18-
const query = ref('');
19-
const levels = ref<Checklist>({choices: {}, selected: []});
20-
const channels = ref<Checklist>({choices: {}, selected: []});
21-
const perPage = ref('50');
22-
const sort = ref('desc');
23-
const offset = ref(0);
17+
const file = ref('');
18+
const query = ref('');
19+
const levels = ref<Checklist>({choices: {}, selected: []});
20+
const channels = ref<Checklist>({choices: {}, selected: []});
21+
const perPage = ref('50');
22+
const sort = ref('desc');
23+
const offset = ref(0);
24+
const badRequest = ref(false);
2425
2526
const navigate = () => {
2627
const fileOffset = offset.value > 0 && logRecordStore.records.paginator?.direction !== sort.value ? 0 : offset.value;
@@ -38,13 +39,21 @@ const navigate = () => {
3839
}
3940
4041
const load = () => {
42+
badRequest.value = false;
4143
logRecordStore
4244
.fetch(file.value, levels.value.selected, channels.value.selected, sort.value, perPage.value, query.value, offset.value)
4345
.then(() => {
4446
levels.value = logRecordStore.records.levels;
4547
channels.value = logRecordStore.records.channels;
4648
})
47-
.catch(() => router.push({name: 'file-not-found'}));
49+
.catch((error: Error) => {
50+
if (error.message === 'bad-request') {
51+
badRequest.value = true;
52+
return;
53+
}
54+
55+
router.push({name: error.message});
56+
});
4857
}
4958
5059
onMounted(() => {
@@ -68,6 +77,7 @@ onMounted(() => {
6877
<div class="flex-grow-1 input-group">
6978
<input type="text"
7079
class="form-control"
80+
:class="{'is-invalid': badRequest}"
7181
placeholder="Search log entries"
7282
aria-label="Search log entries"
7383
aria-describedby="button-search"

package-lock.json

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Controller/LogRecordsController.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@
33

44
namespace FD\LogViewer\Controller;
55

6+
use Exception;
67
use FD\LogViewer\Service\File\LogFileService;
78
use FD\LogViewer\Service\File\LogQueryDtoFactory;
89
use FD\LogViewer\Service\File\LogRecordsOutputProvider;
10+
use FD\LogViewer\Service\Parser\InvalidDateTimeException;
911
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
1012
use Symfony\Component\HttpFoundation\JsonResponse;
1113
use Symfony\Component\HttpFoundation\Request;
14+
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
1215
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
1316

1417
class LogRecordsController extends AbstractController
@@ -20,10 +23,18 @@ public function __construct(
2023
) {
2124
}
2225

26+
/**
27+
* @throws Exception
28+
*/
2329
public function __invoke(Request $request): JsonResponse
2430
{
25-
$logQuery = $this->queryDtoFactory->create($request);
26-
$file = $this->fileService->findFileByIdentifier($logQuery->fileIdentifier);
31+
try {
32+
$logQuery = $this->queryDtoFactory->create($request);
33+
} catch (InvalidDateTimeException $exception) {
34+
throw new BadRequestHttpException('Invalid date.', $exception);
35+
}
36+
37+
$file = $this->fileService->findFileByIdentifier($logQuery->fileIdentifier);
2738
if ($file === null) {
2839
throw new NotFoundHttpException(sprintf('Log file with id `%s` not found.', $logQuery->fileIdentifier));
2940
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace FD\LogViewer\Entity\Expression;
5+
6+
use DateTimeImmutable;
7+
8+
class DateAfterTerm implements TermInterface
9+
{
10+
/**
11+
* @codeCoverageIgnore Simple DTO
12+
*/
13+
public function __construct(public readonly DateTimeImmutable $date)
14+
{
15+
}
16+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace FD\LogViewer\Entity\Expression;
5+
6+
use DateTimeImmutable;
7+
8+
class DateBeforeTerm implements TermInterface
9+
{
10+
/**
11+
* @codeCoverageIgnore Simple DTO
12+
*/
13+
public function __construct(public readonly DateTimeImmutable $date)
14+
{
15+
}
16+
}

0 commit comments

Comments
 (0)