Bisher kam man an die Ausgabe von solarManager.py, dem Runner und den uebrigen Dauerprozessen nur per SSH heran. Die neue Seite unter "Protokolle" zeigt sie: links die fuenf Dateien mit Groesse und Alter - eine, die seit Tagen nicht gewachsen ist, faellt so auf, ohne dass man sie oeffnet -, rechts die Zeilen mit Volltextsuche, Level-Filter und optionalem Nachladen. Nicht in die Datenbank geschrieben, obwohl das naheliegt: ein Log muss genau dann noch funktionieren, wenn die Datenbank es nicht tut. Ein Handler, der nach MySQL schreibt, verschluckt ausgerechnet die Meldung "Datenbank nicht erreichbar" - also die, wegen der man nachsieht. Was strukturiert ausgewertet werden soll, steht ohnehin in automation_log. Gelesen wird rueckwaerts in Bloecken: 5000 Zeilen aus der 329 MB grossen Datei der Wallbox kosten 46 ms, weil nur das Ende angefasst wird. Sucht man ins Leere, bricht der Server nach 4 MB ab und sagt es auch. Die beiden Prozesse formatieren unterschiedlich - der eine stellt das Level voran, der andere den Zeitstempel -, deshalb wird beides gesucht statt an fester Stelle erwartet. Zeilen ohne eigenes Level erben das der Zeile darueber, sonst filterte "nur Fehler" die Tracebacks weg, die den Fehler erklaeren. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
126 lines
4.7 KiB
PHP
126 lines
4.7 KiB
PHP
<?php
|
|
/**
|
|
* Logseite: hineinsehen, was die Hintergrundprozesse melden.
|
|
*
|
|
* Links die Dateien mit ihrem Zustand, rechts die Zeilen. Der Zustand ist
|
|
* mehr als Schmuck: ein Prozess, dessen Datei seit Tagen nicht gewachsen
|
|
* ist, faellt hier auf, ohne dass man sie oeffnet.
|
|
*
|
|
* Gelesen wird ueber ajax/logs.php, gezeichnet in js/solar/logs.js. Die
|
|
* erste Liste steht schon im Dokument, damit die Seite nicht leer aufgeht
|
|
* und man beim langsamen Zugriff von unterwegs sofort sieht, was es gibt.
|
|
*/
|
|
require_once(__DIR__ . "/logdateien.php");
|
|
|
|
$dateien = [];
|
|
$fehler = "";
|
|
try {
|
|
foreach (array_keys(logDateien()) as $key) {
|
|
$dateien[] = logZustand($key);
|
|
}
|
|
} catch (Throwable $e) {
|
|
$fehler = $e->getMessage();
|
|
}
|
|
?>
|
|
<!--begin::App Main-->
|
|
<main class="app-main">
|
|
<!--begin::App Content Header-->
|
|
<div class="app-content-header">
|
|
<div class="container-fluid">
|
|
<div class="row">
|
|
<div class="col-sm-12">
|
|
<h3 class="mb-0">Protokolle</h3>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!--end::App Content Header-->
|
|
<!--begin::App Content-->
|
|
<div class="app-content">
|
|
<div class="container-fluid">
|
|
<?php if ($fehler): ?>
|
|
<div class="alert alert-danger">Die Protokolle sind nicht erreichbar: <?= htmlspecialchars($fehler) ?></div>
|
|
<?php endif; ?>
|
|
<div class="row">
|
|
<!-- Dateiliste -->
|
|
<div class="col-12 col-lg-3">
|
|
<div class="card mb-4">
|
|
<div class="card-header pb-2 pt-2">
|
|
<h3 class="card-title"><i class="bi bi-journal-text me-2"></i>Prozesse</h3>
|
|
<div class="card-tools">
|
|
<button type="button" class="btn btn-tool log-zustand-neu" title="Zustand neu einlesen">
|
|
<i class="bi bi-arrow-clockwise"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<div class="list-group list-group-flush log-liste"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Zeilen -->
|
|
<div class="col-12 col-lg-9">
|
|
<div class="card mb-4">
|
|
<div class="card-header pb-2 pt-2">
|
|
<h3 class="card-title">
|
|
<i class="bi bi-terminal me-2"></i><span class="log-titel">Protokoll</span>
|
|
</h3>
|
|
<div class="card-tools">
|
|
<span class="log-stand small text-secondary me-2"></span>
|
|
<button type="button" class="btn btn-tool log-neu" title="Neu laden">
|
|
<i class="bi bi-arrow-clockwise"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card-body pb-2">
|
|
<div class="row g-2 align-items-center mb-3">
|
|
<div class="col-12 col-md-4">
|
|
<div class="input-group input-group-sm">
|
|
<span class="input-group-text"><i class="bi bi-search"></i></span>
|
|
<input type="search" class="form-control log-suche" placeholder="Text enthält …">
|
|
</div>
|
|
</div>
|
|
<div class="col-6 col-md-3">
|
|
<select class="form-select form-select-sm log-level">
|
|
<option value="">alle Meldungen</option>
|
|
<option value="INFO">ab Info</option>
|
|
<option value="WARNING">ab Warnung</option>
|
|
<option value="ERROR">nur Fehler</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-6 col-md-2">
|
|
<select class="form-select form-select-sm log-anzahl">
|
|
<option value="100">100 Zeilen</option>
|
|
<option value="300" selected>300 Zeilen</option>
|
|
<option value="1000">1000 Zeilen</option>
|
|
<option value="5000">5000 Zeilen</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-12 col-md-3">
|
|
<div class="form-check form-switch mb-0">
|
|
<input class="form-check-input log-folgen" type="checkbox" id="logFolgen">
|
|
<label class="form-check-label small" for="logFolgen">alle 10 s nachladen</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="log-hinweis small text-secondary mb-2"></div>
|
|
<div class="log-ausgabe"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!--end::App Content-->
|
|
</main>
|
|
<!--end::App Main-->
|
|
<script>
|
|
// Zustand der Dateien beim Seitenaufbau. Die Zeilen selbst holt das Skript
|
|
// nach - sie sind das Grosse, und welche Datei zuerst gezeigt wird, weiss
|
|
// erst der Browser (er merkt sich die zuletzt gewaehlte).
|
|
const logDateienStand = <?= json_encode($dateien, JSON_UNESCAPED_UNICODE) ?>;
|
|
</script>
|