add python log viewer

This commit is contained in:
team2
2026-02-28 19:35:39 +01:00
parent 03e7cfdeef
commit 7b777b0f27
4 changed files with 172 additions and 9 deletions

View File

@@ -3,7 +3,8 @@ monolog:
handlers:
agent:
type: stream
type: rotating_file
path: '%kernel.logs_dir%/agent.log'
level: debug
channels: ['agent']
max_files: 14
channels: ['agent']

View File

@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
#[Route('/admin/vector')]
final class AdminVectorLogController extends AbstractController
{
#[Route('/log', name: 'admin_vector_log')]
public function view(): Response
{
$this->denyAccessUnlessGranted('ROLE_SUPER_ADMIN');
// ⚠️ Pfad ggf. anpassen falls bei dir anders konfiguriert
$logFile = \dirname(__DIR__, 3) . '/var/log/vector_service.log';
if (!\file_exists($logFile)) {
return $this->render('admin/vector/log.html.twig', [
'content' => 'vector_service.log not found.',
'fileExists' => false,
]);
}
$content = $this->readLastLines($logFile, 500);
return $this->render('admin/vector/log.html.twig', [
'content' => $content,
'fileExists' => true,
]);
}
/**
* Read last N lines safely (memory-friendly)
*/
private function readLastLines(string $file, int $lines = 500): string
{
$handle = \fopen($file, 'rb');
if (!$handle) {
return 'Unable to open log file.';
}
$buffer = '';
$chunkSize = 4096;
$lineCount = 0;
$pos = -1;
\fseek($handle, 0, SEEK_END);
$fileSize = \ftell($handle);
while ($lineCount < $lines && -$pos < $fileSize) {
$step = \min($chunkSize, $fileSize + $pos);
\fseek($handle, $pos - $step, SEEK_END);
$chunk = \fread($handle, $step);
$buffer = $chunk . $buffer;
$lineCount += \substr_count($chunk, "\n");
$pos -= $step;
}
\fclose($handle);
$linesArray = \explode("\n", $buffer);
return \implode("\n", \array_slice($linesArray, -$lines));
}
}

View File

@@ -61,7 +61,7 @@
<hr class="border-secondary">
<div class="text-info text-uppercase small mb-2">
Dokumente & Wissen
RAG Dokumente & Wissen
</div>
<a class="nav-link text-light {% if route starts with 'admin_document' %}active fw-bold{% endif %}"
@@ -77,11 +77,6 @@
Tags
</a>
<a class="nav-link text-light {% if route starts with 'admin_job' %}active fw-bold{% endif %}"
href="{{ path('admin_jobs') }}">
Indexierungs-Log (Ingest Jobs)
</a>
<a class="nav-link text-light {% if route starts with 'admin_system_agent' %}active fw-bold{% endif %}"
href="{{ path('admin_system_agent') }}">
Retrieval Wissensbasis (Chunk-Index)
@@ -90,7 +85,7 @@
<hr class="border-secondary">
<div class="text-info text-uppercase small mb-2">
System-Profile
RAG System-Profile
</div>
<a class="nav-link text-light {% if route starts with 'admin_system_prompt' %}active fw-bold{% endif %}"
@@ -122,6 +117,18 @@
href="{{ path('admin_guides_index') }}">
How-To & Leitfäden
</a>
<hr class="border-secondary">
<div class="text-info text-uppercase small mb-2">
Logs
</div>
<a class="nav-link text-light {% if route starts with 'admin_job' %}active fw-bold{% endif %}"
href="{{ path('admin_jobs') }}">
Indexierungs-Log (Ingest Jobs)
</a>
<a class="nav-link text-light {% if route starts with 'admin_job' %}active fw-bold{% endif %}"
href="{{ path('admin_vector_log') }}">
Vector-Logs Python
</a>
</nav>
</aside>

View File

@@ -0,0 +1,86 @@
{% extends 'admin/base.html.twig' %}
{% block title %}Vector Service Log{% endblock %}
{% block body %}
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="h3 mb-0">Vector Service Log Python</h1>
{% if fileExists %}
<input
type="text"
id="logSearch"
class="form-control form-control-sm"
placeholder="Search in log..."
style="max-width:300px;"
>
{% endif %}
</div>
<div class="card bg-black border-secondary">
<div class="card-body p-0">
{% if not fileExists %}
<div class="p-3 text-warning">
{{ content }}
</div>
{% else %}
<pre
id="logContent"
style="
margin:0;
padding:15px;
background:#000;
color:#00ff88;
font-size:12px;
line-height:1.4;
max-height:70vh;
overflow:auto;
white-space:pre-wrap;
"
>{{ content }}</pre>
{% endif %}
</div>
</div>
{% if fileExists %}
<script>
(function () {
const input = document.getElementById('logSearch');
const logElement = document.getElementById('logContent');
if (!input || !logElement) {
return;
}
const originalText = logElement.textContent;
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
function highlight(term) {
if (!term) {
logElement.innerHTML = originalText;
return;
}
const safeTerm = escapeRegExp(term);
const regex = new RegExp('(' + safeTerm + ')', 'gi');
const highlighted = originalText.replace(regex, function(match) {
return '<span style="background:#ffcc00;color:#000;">' + match + '</span>';
});
logElement.innerHTML = highlighted;
}
input.addEventListener('input', function () {
highlight(this.value.trim());
});
})();
</script>
{% endif %}
{% endblock %}