Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ read logs from any directory.

### Features

- 📂 **View all the Monolog logs** in your `%kernel.logs_dir%` directory,
- 📂 **View all the Monolog, Apache2 or Nginx logs** in specified directories,
- 🔍 **Search** the logs,
- 🎚 **Filter** by log level (error, info, debug, etc.), by channel, date range or log content,
- 🌑 **Dark mode**,
Expand Down Expand Up @@ -74,15 +74,15 @@ php bin/console assets:install

Once the installation is complete, you will be able to access **Log Viewer** directly in your browser.

By default, the application is available at: `{APP_URL}/log-viewer`.

(for example: `https://my-app.test/log-viewer`)
By default, it is available at: `/log-viewer` on your domain.

## Configuration
- [Adding more monolog directories](docs/adding-more-monolog-directories.md)
- [Modifying monolog configuration](docs/modifying-monolog-configuration.md)
- [Disabling the default monolog configuration](docs/disabling-default-monolog-configuration.md)
- [Adding additional log files](docs/adding-additional-log-files.md)
- [Adding apache logs](docs/configuring-apache-logs.md)
- [Adding nginx logs](docs/configuring-nginx-logs.md)
- [Configuring the back home url](docs/configuring-the-back-home-route.md)
- [Advanced search queries](docs/search-queries.md)
- [Advanced search queries](docs/advanced-search-queries.md)
- [Full configuration reference](docs/configuration-reference.md)
2 changes: 2 additions & 0 deletions dev/compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ version: '3'

services:
nginx:
container_name: log-viewer-nginx
image: nginx:stable-alpine
ports:
- ${NGINX_PORT:-8888}:80
volumes:
- ../.:/app:rw
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
- log-viewer-log:/var/log/nginx:rw

php-fpm:
container_name: log-viewer-php
Expand Down
16 changes: 16 additions & 0 deletions dev/config/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,19 @@ fd_log_viewer:
monolog:
downloadable: true
deletable: true
nginx-access:
type: nginx-access
name: Nginx access
finder:
in: "/var/log/nginx"
name: "nginx.access.log"
downloadable: true
deletable: true
nginx-error:
type: nginx-error
name: Nginx error
finder:
in: "/var/log/nginx"
name: "nginx.error.log"
downloadable: true
deletable: true
2 changes: 1 addition & 1 deletion dev/docker/nodejs/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/bin/bash
set -e

npm install --no-save
npm install --no-save --no-update-notifier --no-fund --no-audit
node node_modules/vite/bin/vite.js build -w --mode development
10 changes: 7 additions & 3 deletions docs/configuration-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,13 @@ This entry allows you to add more log file directories to the Log Viewer. Each e

### log_files.type

**type**: `string` (`enum: monolog`)
**type**: `string` (`enum: monolog|http-access|apache-error|nginx-error`)

This is the type of log file that will be read. Currently only `monolog` is supported.
This is the type of log file that will be read.
- `monolog` is the default type and will read the default monolog log files.
- `http-access` will read the access log files of Apache and Nginx.
- `apache-error` will read the error log files of Apache.
- `nginx-error` will read the error log files of Nginx.
<br><br>

### log_files.name
Expand Down Expand Up @@ -142,7 +146,7 @@ Should the log folders and files be deletable.

**type**: `string|null`.

As log files can contain multiple lines per log entry, this pattern is used to find the start of a log entry. Any lines not matching
As certain log files can contain multiple lines per log entry, this pattern is used to find the start of a log entry. Any lines not matching
the pattern will be appended to the line before.
The default monolog regex pattern is `/^\[\d{4}-\d{2}-\d{2}[^]]*]\s+\S+\.\S+:/` and matches:

Expand Down
40 changes: 40 additions & 0 deletions docs/configuring-apache-logs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Configuring Apache2 logs

To add apache2 logs to the Log Viewer, add the following to your `config/packages/fd_log_viewer.yaml` file:

```yaml
fd_log_viewer:
log_files:
apache-access:
type: http-access
name: Apache2 access
finder:
in: "/var/log/apache2"
name: "access.log"
apache-error:
type: apache-error
name: Apache2 error
finder:
in: "/var/log/apache2"
name: "error.log"
```

## Access logs

The `log_message_pattern`-regex that is used for the **access logs** is:

```regex
/(?P<ip>\S+) (?P<identity>\S+) (?P<remote_user>\S+) \[(?P<date>[^\]]+)\] "(?P<method>\S+) (?P<path>\S+) (?P<http_version>\S+)" (?P<status_code>\S+) (?P<content_length>\S+) "(?P<referrer>[^"]*)" "(?P<user_agent>[^"]*)"/
```

The fields `date`, `method` and `path` are mandatory. The other fields are optional.

## Error logs

The `log_message_pattern`-regex that is used for the **error logs** is:

```regex
/^\[(?<date>.*?)\] \[(?:(?<module>.*?):)?(?<severity>.*?)\] \[pid\s(?<pid>\d*)\](?: (?<error_status>[^\]]*?))?(?: \[client (?<ip>.*):(?<port>\d+)\]) (?<message>.*?)(?:, referer: (?<referer>\S*?))?$/
```

The fields `date`, `severity`, and `message` are mandatory. The other fields are optional.
40 changes: 40 additions & 0 deletions docs/configuring-nginx-logs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Configuring Nginx logs

To add nginx logs to the Log Viewer, add the following to your `config/packages/fd_log_viewer.yaml` file:

```yaml
fd_log_viewer:
log_files:
nginx-access:
type: http-access
name: Nginx access
finder:
in: "/var/log/nginx"
name: "nginx.access.log"
nginx-error:
type: nginx-error
name: Nginx error
finder:
in: "/var/log/nginx"
name: "nginx.error.log"
```

## Access logs

The `log_message_pattern`-regex that is used for the **access logs** is:

```regex
/(?P<ip>\S+) (?P<identity>\S+) (?P<remote_user>\S+) \[(?P<date>[^\]]+)\] "(?P<method>\S+) (?P<path>\S+) (?P<http_version>\S+)" (?P<status_code>\S+) (?P<content_length>\S+) "(?P<referrer>[^"]*)" "(?P<user_agent>[^"]*)"/
```

The fields `date`, `method` and `path` are mandatory. The other fields are optional.

## Error logs

The `log_message_pattern`-regex that is used for the **error logs** is:

```regex
/^(?P<date>[\d+/ :]+) \[(?P<severity>.+)] .*?: (?P<message>.+?)(?:, client: (?P<ip>.+?))?(?:, server: (?P<server>.*?))?(?:, request: "?(?P<request>.+?)"?)?(?:, upstream: "?(?P<upstream>.+?)"?)?(?:, host: "?(?P<host>.+?)"?)?$/
```

The fields `date`, `severity`, and `message` are mandatory. The other fields are optional.
14 changes: 3 additions & 11 deletions frontend/src/components/FileTree.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@
import LogFolder from '@/components/LogFolder.vue';
import bus from '@/services/EventBus';
import {useFolderStore} from '@/stores/folders';
import {ref} from 'vue'

const selectedFolder = ref(0);
const folderStore = useFolderStore();
const folderStore = useFolderStore();

bus.on('file-deleted', () => folderStore.update());
bus.on('folder-deleted', () => folderStore.update());
Expand All @@ -15,9 +13,7 @@ bus.on('folder-deleted', () => folderStore.update());
<!-- FileTree -->
<div class="p-1 pe-2 overflow-auto">
<div class="slv-control-layout m-0">
<div>
<!-- Host: Local-->
</div>
<div><!-- Host: Local --></div>
<div></div>
<div>
<select class="form-control p-0 border-0" v-model="folderStore.direction" v-on:change="folderStore.update">
Expand All @@ -28,11 +24,7 @@ bus.on('folder-deleted', () => folderStore.update());
</div>

<div class="slv-loadable" v-bind:class="{ 'slv-loading': folderStore.loading }">
<log-folder :folder="folder"
:expanded="index === selectedFolder"
:key="index"
v-for="(folder, index) in folderStore.folders"
@expand="selectedFolder = index"/>
<log-folder :folder="folder" :expand="true" :key="index" v-for="(folder, index) in folderStore.folders"/>
</div>
</div>
</template>
Expand Down
13 changes: 8 additions & 5 deletions frontend/src/components/LogFolder.vue
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
<script setup lang="ts">
import LogFile from '@/components/LogFile.vue';
import ButtonGroup from '@/components/ButtonGroup.vue';
import LogFile from '@/components/LogFile.vue';
import type LogFolder from '@/models/LogFolder';
import bus from '@/services/EventBus';
import axios from 'axios';
import {ref} from 'vue';
import {onMounted, ref} from 'vue';
import {useRouter} from 'vue-router';

const toggleRef = ref();
const baseUri = axios.defaults.baseURL;
const router = useRouter();
const expanded = ref(false);

defineProps<{
expanded: boolean,
const props = defineProps<{
expand: boolean,
folder: LogFolder
}>()

Expand All @@ -24,14 +25,16 @@ const deleteFile = (identifier: string) => {
});
}

onMounted(() => expanded.value = props.expand);

</script>

<template>
<!-- LogFolder -->
<div class="folder-group mt-1" :aria-expanded="expanded">
<button-group ref="toggleRef" alignment="right" :split="folder.can_download || folder.can_delete" :hide-on-selected="true">
<template v-slot:btn_left>
<button type="button" class="btn btn-outline-primary text-start w-100" @click="$emit('expand')">
<button type="button" class="btn btn-outline-primary text-start w-100" @click="expanded = !expanded">
<i class="slv-indicator bi bi-chevron-right me-2"></i>
<span class="text-nowrap">{{ folder.path }}</span>
</button>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/LogRecord.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ defineProps<{
<div class="slv-list-link" :class="{ 'text-nowrap': !expanded, 'overflow-hidden': !expanded }" @click="expanded = !expanded">
<i class="slv-indicator bi bi-chevron-right me-1"></i>
<span class="pe-2 text-secondary">{{ logRecord.datetime }}</span>
<span class="text-primary pe-2">{{ logRecord.channel }}</span>
<span class="text-primary pe-2" v-if="logRecord.channel.length > 0">{{ logRecord.channel }}</span>
<span :class="['pe-2', logRecord.level_class ]">{{ logRecord.level_name }}</span>
<span>{{ logRecord.text }}</span>
</div>
Expand Down
4 changes: 2 additions & 2 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public function getConfigTreeBuilder(): TreeBuilder
->scalarNode('home_route')
->info("The name of the route to redirect to when clicking the back button")
->end()
->scalarNode('name')->info("The pretty name to show for these log files")->defaultNull()->end()
->scalarNode('name')->info("The pretty name to show for these log files")->end()
->arrayNode('finder')
->children()
->scalarNode('in')
Expand All @@ -47,7 +47,7 @@ public function getConfigTreeBuilder(): TreeBuilder
->defaultNull()
->end()
->scalarNode('depth')
->info("The symfony/finder directory depth to search files for. Example: > 0")
->info("The symfony/finder directory depth to search files for. Example: '> 0'")
->defaultNull()
->end()
->scalarNode('ignoreUnreadableDirs')
Expand Down
11 changes: 8 additions & 3 deletions src/Resources/config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,15 @@
use FD\LogViewer\Reader\Stream\StreamReaderFactory;
use FD\LogViewer\Routing\RouteLoader;
use FD\LogViewer\Routing\RouteService;
use FD\LogViewer\Service\File\Apache\ApacheErrorFileParser;
use FD\LogViewer\Service\File\Http\HttpAccessFileParser;
use FD\LogViewer\Service\File\LogFileParserProvider;
use FD\LogViewer\Service\File\LogFileService;
use FD\LogViewer\Service\File\LogParser;
use FD\LogViewer\Service\File\LogQueryDtoFactory;
use FD\LogViewer\Service\File\LogRecordsOutputProvider;
use FD\LogViewer\Service\File\Monolog\MonologFileParser;
use FD\LogViewer\Service\File\Nginx\NginxErrorFileParser;
use FD\LogViewer\Service\FinderFactory;
use FD\LogViewer\Service\Folder\LogFolderFactory;
use FD\LogViewer\Service\Folder\LogFolderOutputFactory;
Expand Down Expand Up @@ -91,11 +94,13 @@
$services->set(LogRecordsOutputProvider::class);
$services->set(LogParser::class)->arg('$clock', inline_service(Clock::class));
$services->set(LogFileParserProvider::class)
->arg('$logParsers', tagged_iterator('fd.symfony.log.viewer.monolog_file_parser', 'name'));
->arg('$logParsers', tagged_iterator('fd.symfony.log.viewer.log_file_parser', 'name'));
$services->set(LogQueryDtoFactory::class);
$services->set(MonologFileParser::class)
->tag('fd.symfony.log.viewer.monolog_file_parser', ['name' => 'monolog'])
$services->set(MonologFileParser::class)->tag('fd.symfony.log.viewer.log_file_parser', ['name' => 'monolog'])
->arg('$loggerLocator', tagged_iterator('fd.symfony.log.viewer.logger'));
$services->set(HttpAccessFileParser::class)->tag('fd.symfony.log.viewer.log_file_parser', ['name' => 'http-access']);
$services->set(NginxErrorFileParser::class)->tag('fd.symfony.log.viewer.log_file_parser', ['name' => 'nginx-error']);
$services->set(ApacheErrorFileParser::class)->tag('fd.symfony.log.viewer.log_file_parser', ['name' => 'apache-error']);
$services->set(PerformanceService::class);
$services->set(StreamReaderFactory::class);
$services->set(VersionService::class);
Expand Down
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-pRVgv0ys.js",
"file": "assets/main-mTIZXVgV.js",
"isEntry": true,
"src": "src/main.ts"
},
"style.css": {
"file": "assets/style-skI77787.css",
"file": "assets/style-Ggw-MUUa.css",
"src": "style.css"
}
}
Loading