diff --git a/app/Console/Commands/LogRotate.php b/app/Console/Commands/LogRotate.php new file mode 100644 index 0000000000000000000000000000000000000000..091e6b77d9569653bac1d4b029635e206fabce81 --- /dev/null +++ b/app/Console/Commands/LogRotate.php @@ -0,0 +1,119 @@ +<?php + +namespace App\Console\Commands; + +use Illuminate\Console\Command; +use Redis; + +class LogRotate extends Command +{ + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'log:rotate'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Dieses Kommando bezieht alle Log-Einträge aus dem Redis System und exportiert diese in entsprechende Dateien.'; + + /** + * Create a new command instance. + * + * @return void + */ + public function __construct() + { + parent::__construct(); + } + + /** + * Execute the console command. + * + * @return mixed + */ + public function handle() + { + # Wir extrahieren die Logs mit den Daten der einzelnen Suchmaschinen: + # In den Daten ist festgehalten, wie oft eine jede Suchmaschine abgefragt wurde + # und wie häufig diese geantwortet hat: + $this->extractSearchEngineLogs(); + } + + private function extractSearchEngineLogs() + { + $redis = Redis::connection('redisLogs'); + # Hier legen wir das Ergebnis ab: + # [ + # 'fastbot' => [ + # '' + # ] + # ] + $searchEngineLogs = ['recent' => [], 'overall' => []]; + try { + # Wir benötigen zunächst die Namen aller Suchmaschinen: + $sumasFile = config_path() . "/sumas.xml"; + $xml = simplexml_load_file($sumasFile); + $xml = $xml->xpath("suma"); + $sumaNames = []; + foreach ($xml as $suma) { + $searchEngineLogs['recent'][$suma["name"]->__toString()] = ["requests" => 0, "answered" => 0]; + } + #Auslesen/hinzufügen der Daten: + foreach ($searchEngineLogs['recent'] as $name => $values) { + $searchEngineLogs['recent'][$name]["requests"] = $redis->getset('logs.engines.requests.' . $name, 0); + $searchEngineLogs['recent'][$name]["answered"] = $redis->getset('logs.engines.answered.' . $name, 0); + } + + $filePath = "/var/log/metager/"; + # Wir haben nun die aktuellen daten und müssen diese noch mit den aktuellen Kombinieren: + # Hierbei behalten wir die Daten, seid dem letzten Rotate so wie sie sind und verknüpfen diese mit einer gesamt Statistik. + if (file_exists($filePath . "engine.log") && is_readable($filePath . "engine.log")) { + $oldData = file_get_contents($filePath . "engine.log"); + # JSON Decode + $oldData = json_decode($oldData, true); + if (isset($oldData['overall'])) { + $searchEngineLogs['overall'] = $oldData['overall']; + } + } + + # Jetzt fügen wir zu den Gesamtdaten die zuletzt erfassten hinzu: + foreach ($searchEngineLogs['recent'] as $name => $values) { + if (isset($searchEngineLogs['overall'][$name]["requests"])) { + $searchEngineLogs['overall'][$name]["requests"] += $values["requests"]; + } else { + $searchEngineLogs['overall'][$name]["requests"] = $values["requests"]; + } + if (isset($searchEngineLogs['overall'][$name]["answered"])) { + $searchEngineLogs['overall'][$name]["answered"] += $values["answered"]; + } else { + $searchEngineLogs['overall'][$name]["answered"] = $values["answered"]; + } + } + + # Ins JSON Format umwandeln: + $searchEngineLogs = json_encode($searchEngineLogs, JSON_PRETTY_PRINT); + + # Und in die Datei sichern + if (((file_exists($filePath . "engine.log") && is_writable($filePath . "engine.log")) || (!file_exists($filePath . "engine.log") && is_writable($filePath))) && file_put_contents($filePath . "engine.log", $searchEngineLogs) !== false) { + $this->info("Logs der Suchmaschinen erfolgreich exportiert."); + return; + } else { + # Der Schreibvorgang war nicht erfolgreich. Wir packen die Daten zurück + foreach (json_decode($searchEngineLogs, true) as $name => $values) { + $redis->incrby('logs.engines.requests.' . $name, $values["requests"]); + $redis->incrby('logs.engines.answered.' . $name, $values["answered"]); + } + $this->info("Konnte die Datei \"$filePath" . "engine.log\" nicht erstellen. Keine Rechte."); + return; + } + + } catch (\ErrorException $e) { + return; + } + } +} diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 71c519d32770449e73d225687093d1e41a9ce554..fba95bb3997d9e7aceb9531b6b8229807ce67724 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -13,7 +13,7 @@ class Kernel extends ConsoleKernel * @var array */ protected $commands = [ - // Commands\Inspire::class, + Commands\LogRotate::class, ]; /** @@ -24,6 +24,7 @@ class Kernel extends ConsoleKernel */ protected function schedule(Schedule $schedule) { + $schedule->command('log:rotate')->everyTenMinutes(); // $schedule->command('inspire') // ->hourly(); } diff --git a/app/Http/Controllers/AdminInterface.php b/app/Http/Controllers/AdminInterface.php index c7cd810f083ee94c55fa40ded411396d53ea5694..959b2ca2fed451eb5f4d9b718519c90b66417f2a 100644 --- a/app/Http/Controllers/AdminInterface.php +++ b/app/Http/Controllers/AdminInterface.php @@ -96,4 +96,37 @@ class AdminInterface extends Controller ->with('title', 'Wer sucht was? - MetaGer') ->with('q', $q); } + + public function engines() + { + # Wir laden den Inhalt der Log Datei und übergeben ihn an den view + $file = "/var/log/metager/engine.log"; + if (file_exists($file) && is_readable($file)) { + $engineStats = file_get_contents($file); + # Daten vom JSON Format dekodieren + $engineStats = json_decode($engineStats, true); + + # Eine Sortierung wäre nicht das verkehrteste + uasort($engineStats["recent"], function ($a, $b) { + if ($a["requests"] == $b["requests"]) { + return 0; + } + + return ($a["requests"] < $b["requests"]) ? 1 : -1; + }); + + uasort($engineStats["overall"], function ($a, $b) { + if ($a["requests"] == $b["requests"]) { + return 0; + } + + return ($a["requests"] < $b["requests"]) ? 1 : -1; + }); + return view('admin.engines') + ->with('engineStats', $engineStats) + ->with('title', "Suchmaschinenstatus - MetaGer"); + } else { + return redirect(url('admin')); + } + } } diff --git a/app/Http/routes.php b/app/Http/routes.php index 04074ed736c0486d83457bce14b3e25e2f61bf00..96423a88fa1b8f6c5c4d4c217890f28f94b4ed52 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -144,6 +144,7 @@ Route::group( Route::get('admin', 'AdminInterface@index'); Route::get('admin/count', 'AdminInterface@count'); Route::get('admin/check', 'AdminInterface@check'); + Route::get('admin/engines', 'AdminInterface@engines'); Route::get('settings', 'StartpageController@loadSettings'); diff --git a/app/MetaGer.php b/app/MetaGer.php index 57f6cb295348ab6a8f4fabfea13e4da06beaef01..91b12586d321f8c9051e0a7021963ee42ffce683 100644 --- a/app/MetaGer.php +++ b/app/MetaGer.php @@ -524,19 +524,20 @@ class MetaGer */ # Wir zählen die Suchmaschinen, die durch den Cache beantwortet wurden: - $enginesToLoad = 0; + $enginesToLoad = []; $canBreak = false; foreach ($engines as $engine) { if ($engine->cached) { - $enginesToLoad--; if ($overtureEnabled && ($engine->name === "overture" || $engine->name === "overtureAds")) { $canBreak = true; } + } else { + # Das Array zählt einerseits die Suchmaschinen, auf die wir warten und andererseits + # welche Suchmaschinen nicht rechtzeitig geantwortet haben. + $enginesToLoad[$engine->name] = false; } } - $enginesToLoad += count($engines); - $this->waitForResults($enginesToLoad, $overtureEnabled, $canBreak); $this->retrieveResults($engines); @@ -616,6 +617,10 @@ class MetaGer { $loadedEngines = 0; $timeStart = microtime(true); + + # Auf wie viele Suchmaschinen warten wir? + $engineCount = count($enginesToLoad); + while (true) { $time = (microtime(true) - $timeStart) * 1000; $loadedEngines = intval(Redis::hlen('search.' . $this->getHashCode())); @@ -625,12 +630,12 @@ class MetaGer # Abbruchbedingung if ($time < 500) { - if (($enginesToLoad === 0 || $loadedEngines >= $enginesToLoad) && $canBreak) { + if (($engineCount === 0 || $loadedEngines >= $engineCount) && $canBreak) { break; } } elseif ($time >= 500 && $time < $this->time) { - if (($enginesToLoad === 0 || ($loadedEngines / ($enginesToLoad * 1.0)) >= 0.8) && $canBreak) { + if (($engineCount === 0 || ($loadedEngines / ($engineCount * 1.0)) >= 0.8) && $canBreak) { break; } @@ -639,6 +644,13 @@ class MetaGer } usleep(50000); } + + # Wir haben nun so lange wie möglich gewartet. Wir registrieren nun noch die Suchmaschinen, die geanwortet haben. + $answered = Redis::hgetall('search.' . $this->getHashCode()); + foreach ($answered as $key => $value) { + $enginesToLoad[$key] = true; + } + $this->enginesToLoad = $enginesToLoad; } public function retrieveResults($engines) @@ -940,7 +952,19 @@ class MetaGer $useragent = str_replace(" ", "", $useragent); $logEntry .= " time=" . round((microtime(true) - $this->starttime), 2) . " serv=" . $this->fokus; $logEntry .= " search=" . $this->eingabe; - $redis->rpush('logs.search', $logEntry); + + # 2 Arten von Logs in einem wird die Anzahl der Abfragen an eine Suchmaschine gespeichert und in der anderen + # die Anzahl, wie häufig diese Ergebnisse geliefert hat. + $enginesToLoad = $this->enginesToLoad; + $redis->pipeline(function ($pipe) use ($enginesToLoad, $logEntry) { + $pipe->rpush('logs.search', $logEntry); + foreach ($this->enginesToLoad as $name => $answered) { + $pipe->incr('logs.engines.requests.' . $name); + if ($answered) { + $pipe->incr('logs.engines.answered.' . $name); + } + } + }); } catch (\Exception $e) { return; } diff --git a/app/Models/Searchengine.php b/app/Models/Searchengine.php index 4d8b75fb285c128457f4202e60bbd058f7aebb4c..60946a9fd9c2138b2fd01fa9af092d86309a91a1 100644 --- a/app/Models/Searchengine.php +++ b/app/Models/Searchengine.php @@ -178,7 +178,6 @@ abstract class Searchengine } } - if ($body !== "") { $this->loadResults($body); $this->getNext($metager, $body); diff --git a/resources/views/admin/engines.blade.php b/resources/views/admin/engines.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..43ad731e9ff502a9952ea24e7fef7f5326c793f7 --- /dev/null +++ b/resources/views/admin/engines.blade.php @@ -0,0 +1,54 @@ +@extends('layouts.subPages') + +@section('title', $title ) + +@section('content') +<h1>Suchmaschinenübersicht</h1> +<p>Diese Übersicht gibt Aufschluss darüber, welche Suchmaschinen wie oft abgefragt wurden und zusätzlich wie oft diese innerhalb unseres Timeouts geantwortet haben</p> +<table class="table table-bordered"> + <caption>Daten der letzten 10 Minuten</caption> + <thead> + <tr> + <th>Name</th> + <th>Anzahl der gesamten Abfragen</th> + <th>Davon tatsächlich beantwortet</th> + <th>Prozent</th> + </tr> + </thead> + <tbody> + @foreach($engineStats["recent"] as $name => $values) + @if($values["requests"] > 0) + <tr @if($values["requests"] === $values["answered"]) class="success" @else class="danger" @endif> + <td>{{$name}}</td> + <td>{{$values["requests"]}}</td> + <td>{{$values["answered"]}}</td> + <td>{{ floor(($values["answered"] / $values["requests"]) * 100) }}%</td> + </tr> + @endif + @endforeach + </tbody> +</table> +<table class="table table-bordered"> + <caption>Daten insgesamt</caption> + <thead> + <tr> + <th>Name</th> + <th>Anzahl der gesamten Abfragen</th> + <th>Davon tatsächlich beantwortet</th> + <th>Prozent</th> + </tr> + </thead> + <tbody> + @foreach($engineStats["overall"] as $name => $values) + @if($values["requests"] > 0) + <tr @if($values["requests"] === $values["answered"]) class="success" @else class="danger" @endif> + <td>{{$name}}</td> + <td>{{$values["requests"]}}</td> + <td>{{$values["answered"]}}</td> + <td>{{ floor(($values["answered"] / $values["requests"]) * 100) }}%</td> + </tr> + @endif + @endforeach + </tbody> +</table> +@endsection