From 178e542c40181c1f171f9e8140e9abb5c658800f Mon Sep 17 00:00:00 2001
From: Dominik Pfennig <dominik@suma-ev.de>
Date: Wed, 21 Sep 2016 15:03:29 +0200
Subject: [PATCH] =?UTF-8?q?Die=20Daten=20dar=C3=BCber,=20welche=20Suchmasc?=
 =?UTF-8?q?hinen=20abgefragt=20wurden=20und=20wie=20viele=20geantwortet=20?=
 =?UTF-8?q?haben=20werden=20ins=20Redis=20geschrieben.=20Mit=20einem=20ent?=
 =?UTF-8?q?sprechenden=20Cronjob=20werden=20diese=20auch=20in=20eine=20Log?=
 =?UTF-8?q?=20Datei=20geschrieben?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 app/Console/Commands/LogRotate.php | 119 +++++++++++++++++++++++++++++
 app/Console/Kernel.php             |   3 +-
 app/MetaGer.php                    |  38 +++++++--
 app/Models/Searchengine.php        |   1 -
 4 files changed, 152 insertions(+), 9 deletions(-)
 create mode 100644 app/Console/Commands/LogRotate.php

diff --git a/app/Console/Commands/LogRotate.php b/app/Console/Commands/LogRotate.php
new file mode 100644
index 000000000..091e6b77d
--- /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 71c519d32..1ef2005a1 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')->everyMinute();
         // $schedule->command('inspire')
         //          ->hourly();
     }
diff --git a/app/MetaGer.php b/app/MetaGer.php
index 57f6cb295..91b12586d 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 4d8b75fb2..60946a9fd 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);
-- 
GitLab