Blaubart: Error-Logs und Email-Benachrichtigung

Mahlzeit.

Um mögliche Fehlermeldungen in meinen Webprojekten nicht direkt dem Seitenbesucher zu präsentieren, verwende ich einen eigenen Error Handler, der neben einer entsprechenden Fehlerseite auch einen Eintrag in einem Logfile erstellt.

Das läuft schon seit einer Weile so, und kürzlich dachte ich mir, daß eine automatische Benachrichtigung per E-Mail im Fehlerfal dem Admin das regelmäßige Überprüfen der Fehlerlogs ("auf Verdacht" sozusagen) abnehmen könnte. Um dabei zu vermeiden, daß bei permanenten Fehlern oder böswilligen Besuchern gleich 200 E-Mails pro Minute beim Admin landen, wäre eine Prüfung praktisch, ob der aktuelle zu meldende Fehler in den letzten x Sekunden/Minuten/Stunden bereits gemeldet wurde.

Viele Ansätze, die ich dazu fand, benutzen für diese Logik im Hintergrund auch wieder die Datenbank. Keine besonders glückliche Wahl, wie ich finde, falls der auftretende Fehler z.B. von der Datenbank selber herrührte. Alternativ kann ich natürlich auch ein ähnliches System auf der Basis von ganz einfachen Textdateien "nachbauen". Mich würde interessieren, wie ihr so etwas handhabt. Was hat sich bei euch bewährt?

  1. Hi,

    Das läuft schon seit einer Weile so, und kürzlich dachte ich mir, daß eine automatische Benachrichtigung per E-Mail im Fehlerfal dem Admin das regelmäßige Überprüfen der Fehlerlogs ("auf Verdacht" sozusagen) abnehmen könnte. Um dabei zu vermeiden, daß bei permanenten Fehlern oder böswilligen Besuchern gleich 200 E-Mails pro Minute beim Admin landen, wäre eine Prüfung praktisch, ob der aktuelle zu meldende Fehler in den letzten x Sekunden/Minuten/Stunden bereits gemeldet wurde.

    ich habe für unsere Firma ein Tool geschrieben, daß Webserver prüft. Dies tut es (z.B.) alle 5 Minuten. Wenn ein Server ausfällt, läuft ein Prozess über mehrere Eskalationsstufen los:
    Zunächst gibt es eine Warnung (per Mail und bei bestimmten Servern per SMS). Gleichzeitig wird der Server jetzt jede Minute geprüft. Ein Zähler wird für diesen Server raufgezählt (z.B. bis max Wert=3). Wenn der Server wieder online ist, zählt der Wert runter, erst dann gibt es eine Warnung und die Prüfzeit wird wieder heraufgesetzt.

    Hinter dem Mechanismus verbergen sich zwei Ideen:

    • ein Kind, das lieb ist, muß nicht dauern gegängelt und überwacht werden, erst wenn bekannt ist, daß es oft Blödsinn macht und man es einfach nicht aus den Augen lassen kann, sollte man mehr drauf achten. Deshalb das Herauf- und Heruntersetzen der Prüfzeit.

    • der Zähler soll mehrfachen Warn- und Entwarnungen vorbeugen. Würde das passieren, könnte man den Status nicht wirklich gut beurteilen bzw. würde das nicht ernst nehmen. In etwa hat das die Funktion eines Schmitt-Triggers.

    Viele Grüße,
    Reiner

    1. [...]Wenn der Server wieder online ist, zählt der Wert runter, erst dann gibt es eine Warnung und die Prüfzeit wird wieder heraufgesetzt.

      "dann gibt es eine ENTwarnung" sollte es natürlich heißen!

      Viele Grüße,
      Reiner

    2. Moin!

      ich habe für unsere Firma ein Tool geschrieben, daß Webserver prüft. Dies tut es (z.B.) alle 5 Minuten. Wenn ein Server ausfällt, läuft ein Prozess über mehrere Eskalationsstufen los:

      Es gibt auch Open Source für diese Aufgabe: Nagios.

      Wobei die Software tendentiell eine eierlegende Wollmilchsau ist, d.h. sie ist für zeitlich wiederkehrende Prüfungen beliebiger Zustände gedacht, deren Status sich in "ok", "Warnstufe" oder "Alarm" ausdrücken läßt. Mitgeliefert werden zahlreiche Modulchen, die PING, HTTP, SMTP, POP3 etc. über das Netz prüfen können, sowie auch lokale Werte (Füllstand der Festplatte z.B.).

      Nagios kann auch remote arbeiten. Sowohl kann ein laufendes Nagios an entfernte Nagiosse lokale Testergebnisse übermitteln, als auch auf entfernt laufenden Subinstallationen Testergebnisse anfordern.

      Ein ausgeklügeltes Benachrichtigungs- und Eskalationssystem, welches hauptsächlich Mailbenachrichtigung, aber u.a. auch ein nett anzuschauendes Browser-Tool enthält, runden die Sache ab.

      - Sven Rautenberg

      --
      My sssignature, my preciousssss!
      1. hi,

        Es gibt auch Open Source für diese Aufgabe: Nagios.

        Wobei die Software tendentiell eine eierlegende Wollmilchsau ist, [...]

        ich kenne das. Eben diese ...sau will man manchmal nicht bzw. selbermachen, um was zu verstehen, kann auch interessant sein.
        Mein Gerät ist sehr einfach gemacht, funktioniert für das was ich will perfekt. Ich wollte auch eben nur beschreiben, wie ich das umgesetzt habe und was ich mir dabei gedacht habe.

        Hier wird doch immer das SELF... hochgehalten. Daß es Software gibt, die schon alles toll kann, ist sicher meist so. Aber es führt nicht wirklich zum Verstehen, oder? ;-)

        Gruß
        Reiner

        1. Moin!

          Hier wird doch immer das SELF... hochgehalten. Daß es Software gibt, die schon alles toll kann, ist sicher meist so. Aber es führt nicht wirklich zum Verstehen, oder? ;-)

          Nagios SELF zu konfigurieren ist eigentlich schon Abenteuer genug, finde ich.

          Nein, man muß nicht alle Software, die man benutzt, selbst geschrieben haben, um dem SELF-Gedanken vollkommen zu genügen. Aber man sollte selbst die Initiative ergreifen, entweder etwas selbst zu schreiben oder selbst etwas existierendes zu suchen und einzusetzen.

          - Sven Rautenberg

          --
          My sssignature, my preciousssss!
    3. 'Nabend Reiner.

      ich habe für unsere Firma ein Tool geschrieben, daß Webserver prüft.

      Du beschreibst da ein Programm, welches aktiv den Webserver auf seine Funktionstüchtigkeit überprüft. Was ich meinte, waren aber eher Fehlermeldungen, die ein auf dem Webserver laufendes Programm erzeugt (z.B. weil mitten in einer Aktion das Datenbankhandle ungültig geworden ist). Also kein "Funktioniert's noch?", sondern eher ein "onError"-Handler. Unerwartete Fehler im laufenden Programm, die nicht direkt mit der Hardware zusammenhängen, kann ich durch einen von dir beschriebenen Check auch nur schlecht finden...

      Da sich bei mir aber kein externes System um das Loggen der Fehler und die Entscheidung wann nun eine Nachricht wie oft an wen gesendet wird, kümmert, bleibt meine ursprüngliche Frage bestehen.

      • der Zähler soll mehrfachen Warn- und Entwarnungen vorbeugen. Würde das passieren, könnte man den Status nicht wirklich gut beurteilen bzw. würde das nicht ernst nehmen. In etwa hat das die Funktion eines Schmitt-Triggers.

      Jau, mit Schmitt-Triggern kenn ich mich aus. :)

      Genau so etwas hatte ich auch vor (Stichwort 200 Emails pro Minute). Mir ging es vor allem um die Umsetzung.

      Da ich ja kein externes Programm verwende, stellt sich die Frage, wie die Logik für das ganze Error-Handling am besten umgesetzt werden soll. Am besten so einfach wie möglich, um nicht zur Berichterstellung usw. auf Resourcen zugreifen zu müssen, die den Fehler überhaupt verursacht haben.

  2. gudn tach!

    Was hat sich bei euch bewährt?

    fehlermeldungen werden bei mir immer in ein logfile auf dem server geschrieben.
    zusaetzlich lasse ich mir bei einem fehler eine e-mail schicken und setze direkt danach ein flag (in form einer datei).
    bei jeder (weiteren) fehlermeldung wird ueberprueft, ob das flag gesetzt wurde. falls es gesetzt wurde, wird die e-mail-benachrichtigung nicht durchgefuehrt.
    in einem admin-web-interface, mit hilfe dessen ich alle moeglichen admin-taetitigkeiten ausfuehre, lasse ich ebenfalls bei gesetztem flag ebenfalls eine nachricht a la "neue fehler!" erscheinen, (sonst "keine neuen fehler."). und wenn man sich via webinterface das aktuelle error-log-file anschaut, wird automatisch das flag wieder zurueckgesetzt.

    link zum thema bzgl. php: http://www.php.net/manual/en/ref.errorfunc.php (das beispiel "Example 1. Using error handling in a script" ist toll.)

    fuer perl habe ich auf die schnelle nur das hier gefunden. kennt jemand einen richtig guten artikel fuer gescheites error-handling in perl?

    prost
    seth

    1. fuer perl habe ich auf die schnelle nur das hier gefunden. kennt jemand einen richtig guten artikel fuer gescheites error-handling in perl?

      Ich verwende sowas:

      $SIG{__DIE__}  = sub
      {
      tu_was_im_fehlerfall(@_);
      }

      gibt's auch für warn:
      $SIG{__WARN__} = ....

      Struppi.

    2. Hallo seth.

      fehlermeldungen werden bei mir immer in ein logfile auf dem server geschrieben.
      zusaetzlich lasse ich mir bei einem fehler eine e-mail schicken und setze direkt danach ein flag (in form einer datei).
      bei jeder (weiteren) fehlermeldung wird ueberprueft, ob das flag gesetzt wurde. falls es gesetzt wurde, wird die e-mail-benachrichtigung nicht durchgefuehrt.

      Und ich nehme an, du loggst trotzdem in der Zwischenzeit auftretende Fehler ohne Aussortieren; allerdings ohne Email-Benachrichtigung. Das spart zumindest schonmal einen komplizierten Unterbau, um z.B. vor dem Loggen/Mailsenden zu überprüfen, ob _dieser_ Fehler in den letzten x Sekunden schon einmal auftrat. Klingt ganz praktisch. :-)

      link zum thema bzgl. php: http://www.php.net/manual/en/ref.errorfunc.php (das beispiel "Example 1. Using error handling in a script" ist toll.)

      Wenn ich das richtig sehe allerdings komplett ohne einen Mechanismus, der Massenmails an den Admin verhindert, falls ein kritischer Fehler bestehen bleibt.

      fuer perl habe ich auf die schnelle nur das hier gefunden.

      Da geht es ja eher um die Architektur des Programms, um mit Exceptions den Fehler bis zur geeigneten Stelle "durchzureichen", nicht um die eigentliche Fehlerbehandlung.

      Danke dir.

      1. gudn tach!

        fehlermeldungen werden bei mir immer in ein logfile auf dem server geschrieben.
        zusaetzlich lasse ich mir bei einem fehler eine e-mail schicken und setze direkt danach ein flag (in form einer datei).
        bei jeder (weiteren) fehlermeldung wird ueberprueft, ob das flag gesetzt wurde. falls es gesetzt wurde, wird die e-mail-benachrichtigung nicht durchgefuehrt.

        Und ich nehme an, du loggst trotzdem in der Zwischenzeit auftretende Fehler ohne Aussortieren; allerdings ohne Email-Benachrichtigung.

        ja, das wollte ich eigentlich damit ausdruecken. sorry, dass es nicht klar rueberkam.

        Das spart zumindest schonmal einen komplizierten Unterbau, um z.B. vor dem Loggen/Mailsenden zu überprüfen, ob _dieser_ Fehler in den letzten x Sekunden schon einmal auftrat.

        ja. allerdings wird ohne diese pruefung das error-log-file bei gewissen attacken ziemlich schnell ziemlich gross. insofern kann eine solche abfrage trotzdem noch sinnvoll sein.

        Klingt ganz praktisch. :-)

        fuer meine beduerfnisse ist es das bisher auch.

        link zum thema bzgl. php: http://www.php.net/manual/en/ref.errorfunc.php (das beispiel "Example 1. Using error handling in a script" ist toll.)

        Wenn ich das richtig sehe allerdings komplett ohne einen Mechanismus, der Massenmails an den Admin verhindert, falls ein kritischer Fehler bestehen bleibt.

        ja, schon. aber die umbauten, die ich im vorigen posting beschrieben hatte, bauen darauf auf und sind dann nicht mehr viele. ich haenge mal diesem posting scriptauszuege an, die ich in einem projekt verwende.

        fuer perl habe ich auf die schnelle nur das hier gefunden.

        Da geht es ja eher um die Architektur des Programms, um mit Exceptions den Fehler bis zur geeigneten Stelle "durchzureichen", nicht um die eigentliche Fehlerbehandlung.

        ja.

        so, und nun das error-handling-zeugs fuer php 4 und 5. vielleicht hat ja noch jemand verbesserungsvorschlaege oder findet sogar luecken/fehler:

        jede datei des projektes beginnt mit

        <?php  
        require('foodir/error_handling.php');
        

        (je nach projektstruktur besser: require_once())

        ausser die datei "error_handling.php", welche sich in irgendeinem verzeichnis "foodir" befindet.

        englische kommentare sollen erlaeutern, was passiert. deutsche kommentare beschreiben, was an der jeweiligen stelle getan werden sollte, was ich jedoch nicht explizit hinschreibe, z.b. weil es das script hier unuebersichtlich machen wuerde.

        <?php  
        // error_handling.php  
          
        // setze $debugging_mode=1, falls scripts (auf lokalem server) auf fehler untersucht werden sollen, sonst 0;  
          
        // set error reporting  
        if($debugging_mode){  
          error_reporting(E_ALL | (defined('E_STRICT')? E_STRICT : 0));  
        }else error_reporting(0);  
          
        // own error handling  
        function userErrorHandler($errno, $errmsg, $filename, $linenum, $vars){  
          // timestamp for the error entry  
          $errortime=date('Y-m-d\TH:i:s');  
          // define an assoc array of error strings  
          $errortype=array(  
            E_ERROR           => 'error',  
            E_WARNING         => 'warning',  
            E_PARSE           => 'parsing error',  
            E_NOTICE          => 'notice',  
            E_CORE_ERROR      => 'core error',  
            E_CORE_WARNING    => 'core warning',  
            E_COMPILE_ERROR   => 'compile error',  
            E_COMPILE_WARNING => 'compile warning',  
            E_USER_ERROR      => 'user error',  
            E_USER_WARNING    => 'user warning',  
            E_USER_NOTICE     => 'user notice');  
          if(defined('E_STRICT')) // because in php prior to version 5 not defined  
            $errortype[E_STRICT]='runtime notice';  
          // table head of logged data  
          $err_h_arr=array('errortime', 'errortype', 'filename', 'linenum', 'errmsg',  
            'REMOTE_ADDR:REMOTE_PORT', 'HTTP_USER_AGENT', 'REQUEST_URI', 'STATUS',  
            'HTTP_REFERER', 'REQUEST_METHOD', '_GET', '_POST');  
          // data to log  
          $err_array=array($errortime, $errortype[$errno], $filename, $linenum, $errmsg);  
          // fuelle $err_array mit dem rest $err_array[]=... (siehe tabellenkopf $err_h_arr)  
          $err='"'.implode('"; "', $err_array).'"'."\n";  
          
          // write error information to error log and set new_error flag  
          $err_file_path='foodir/error.log';  
          // first line = table head  
          if(!file_exists($err_file_path) || filesize($err_file_path)===0){  
            $file_handle=fopen($err_file_path, 'w');  
              fwrite($file_handle, '"'.implode('"; "', $err_h_arr).'"'."\n");  
            fclose($file_handle);  
          }  
          // write errormessage to file  
          error_log($err, 3, $err_file_path);  
          // set new_error flag  
          $flag=0;  
          $flag_file_path='foodir/new_error.flag';  
          if(file_exists($flag_file_path)){  
            $file_handle=fopen($flag_file_path, 'r');  
              $flag=fgetc($file_handle); // read '0' or '1' or false  
            fclose($file_handle);  
          }  
          $file_handle=fopen($flag_file_path, 'w');  
            fwrite($file_handle, '1');  
          fclose($file_handle);  
          
          // send error information by mail  
          if(!$debugging_mode && $flag!=='1'){  
            $headers='From: err_handling_script@'.$_SERVER['SERVER_NAME']."\r\n".  
              'X-Mailer: PHP/'.phpversion();  
            error_log($err, 1, $meine_adresse, $headers);  
          }  
          
          if($errno & (E_USER_ERROR | E_ERROR)){  
            echo '<div>ein fehler ist aufgetreten</div>';  
            echo '<div>der administrator wurde benachrichtigt</div>';  
            die;  
          }  
        }  
          
        $old_error_handler = set_error_handler('userErrorHandler');  
        //eof
        

        und das viele-gleiche-errors-gar-nicht-erst-mitloggen, koennte man nun bewerkstelligen, indem man prueft, ob die aktuelle fehler-ip-adresse und die des letzten eintrages in error.log gleich sind, ob die fehlermeldung die gleiche ist und ob auch der fehler in der gleichen datei an der gleichen stelle aufgetreten ist.

        falls allerdings abwechselnd irgendwelche warnings gefolgt von errors erscheinen koennen, kann man ja z.b. die letzten 10 eintraege durchforsten, wenn sie nicht aelter als z.b. 5 minuten sind.

        prost
        seth