add python log viewer
This commit is contained in:
@@ -3,7 +3,8 @@ monolog:
|
|||||||
|
|
||||||
handlers:
|
handlers:
|
||||||
agent:
|
agent:
|
||||||
type: stream
|
type: rotating_file
|
||||||
path: '%kernel.logs_dir%/agent.log'
|
path: '%kernel.logs_dir%/agent.log'
|
||||||
level: debug
|
level: debug
|
||||||
channels: ['agent']
|
max_files: 14
|
||||||
|
channels: ['agent']
|
||||||
69
src/Controller/Admin/AdminVectorLogController.php
Normal file
69
src/Controller/Admin/AdminVectorLogController.php
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
<hr class="border-secondary">
|
<hr class="border-secondary">
|
||||||
|
|
||||||
<div class="text-info text-uppercase small mb-2">
|
<div class="text-info text-uppercase small mb-2">
|
||||||
Dokumente & Wissen
|
RAG Dokumente & Wissen
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a class="nav-link text-light {% if route starts with 'admin_document' %}active fw-bold{% endif %}"
|
<a class="nav-link text-light {% if route starts with 'admin_document' %}active fw-bold{% endif %}"
|
||||||
@@ -77,11 +77,6 @@
|
|||||||
Tags
|
Tags
|
||||||
</a>
|
</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 %}"
|
<a class="nav-link text-light {% if route starts with 'admin_system_agent' %}active fw-bold{% endif %}"
|
||||||
href="{{ path('admin_system_agent') }}">
|
href="{{ path('admin_system_agent') }}">
|
||||||
Retrieval Wissensbasis (Chunk-Index)
|
Retrieval Wissensbasis (Chunk-Index)
|
||||||
@@ -90,7 +85,7 @@
|
|||||||
<hr class="border-secondary">
|
<hr class="border-secondary">
|
||||||
|
|
||||||
<div class="text-info text-uppercase small mb-2">
|
<div class="text-info text-uppercase small mb-2">
|
||||||
System-Profile
|
RAG System-Profile
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a class="nav-link text-light {% if route starts with 'admin_system_prompt' %}active fw-bold{% endif %}"
|
<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') }}">
|
href="{{ path('admin_guides_index') }}">
|
||||||
How-To & Leitfäden
|
How-To & Leitfäden
|
||||||
</a>
|
</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>
|
</nav>
|
||||||
</aside>
|
</aside>
|
||||||
|
|||||||
86
templates/admin/vector/log.html.twig
Normal file
86
templates/admin/vector/log.html.twig
Normal 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 %}
|
||||||
Reference in New Issue
Block a user