Erweitertes Abfangen von Exceptions
Ist ein Projekt abgeschlossen und wird an den Kunden übergeben, beginnt die Zitter-Phase. Erst im Betrieb wird sich zeigen, ob das Programm seinen Aufgaben gewachsen ist. Keine Applikation ist fehlerlos/bugfrei und eigene Tests sind nicht unfehlbar. Was wir dagegen tun: Erweitertes Abfangen von Exceptions im productive Environment.
Symfony selbst baut auf viele hilfreiche Exceptions, die jedoch nur während der Entwicklung angezeigt werden. Wird das Projekt im productive Environment installiert, endet eine hilfreiche Exception nur in einer „Code 500 – Server unavailable“ Seite bzw. der eingerichteten Fehlermeldung. Das führt beim Kunden zu Frust und ist dem Entwickler keinerlei Hilfe, um dem Problem entgegenzuwirken.
Logs nutzen
Auf dem Live-System werden alle wichtigen Ereignisse – und natürlich auch Exceptions – in einer Log-Datei im Projektverzeichnis abgelegt. Unter var/logs/prod*
findet man entsprechende Ausgaben. Hierbei muss natürlich erst einmal kommuniziert werden, dass ein Fehler aufgetreten ist. Oder man entwickelt einen serverseitigen Prozess, der den Entwickler über Einträge in der Log-Datei informiert.
Monolog
Symfony verwendet unter der Haube Monolog, um sämtliche Logs erstellen zu lassen. Das Paket ist äußert komfortabel und kann auch für unsere zwecke eingerichtet werden. Es bringt auch schon alles Notwendige mit, um Exceptions für die Entwickler sichtbar zu machen.
# config/packages/prod/monolog.yaml monolog: handlers: main: type: fingers_crossed action_level: error handler: grouped excluded_http_codes: [403, 404] grouped: type: group members: [nested, mailer] nested: type: rotating_file path: "%kernel.logs_dir%/%kernel.environment%.log" level: debug max_files: 30 mailer: type: deduplication handler: swift_mail swift_mail: type: swift_mailer from_email: 'my-project@my-domain.com' to_email: 'receiver@example.com' subject: 'Exception in Project: %%message%%' level: debug formatter: monolog.formatter.html content_type: text/html console: type: console process_psr_3_messages: false channels: ["!event", "!doctrine"]
Diese Konfiguration erzeugt eine rotierende Log-Datei, also ein Log pro Tag und maximal 30 in einem Ordner. Zusätzlich wird eine E-Mail, die detaillierte Informationen zur Exception enthält, generiert und an receiver@example.com gesendet. Voraussetzung hierfür ist natürlich ein installierter SwiftMailer. Mit diesem Stück Text ist man also schon auf der sicheren Seite, um auf eventuelle Exceptions reagieren zu können.
Exceptions manuell nach Hause telefonieren lassen
Dies ist eine Symfony 3.* Konfiguration.
Sollte kein Monolog vorhanden sein, kann auch Twig eine Zusammenfassung der Exception für uns aufbereiten. Zusätzlich muss man sich jedoch selbst um die Zustellung der Informationen kümmern. Das Twig-Bundle verfügt über einen ExceptionController
, der die Exception Seite im „dev“ Environment rendert. Die Methode showAction()
im Controller kann für unsere Zwecke angepasst werden:
namespace AppBundle\Controller; use ...; /** * Class CustomExceptionController */ class CustomExceptionController extends ExceptionController { /** * Override the existing handle * @param Request $request * @param FlattenException $exception * @param DebugLoggerInterface|null $logger * @return Response */ public function showAction(Request $request, FlattenException $exception, DebugLoggerInterface $logger = null) { // create the response from parent controller $response = parent::showAction($request, $exception, $logger); $code = $exception->getStatusCode(); // do your own handling // ... // continue return $response; } }
Unser Exception-Controller muss nun noch registriert werden.
# app/config/config.yml twig: debug: "%kernel.debug%" strict_variables: "%kernel.debug%" exception_controller: AppBundle\Controller\CustomExceptionController::showAction
Die $exception
enthält den gesamten „StackTrace“ des Fehlschlags. Hier wird einfach nur Platz geschaffen, um den eigenen Code auszuführen. An entsprechender Stelle könnte man z.B. eine E-Mail (mit der Exception) an seine eigene Adresse senden. Oder mit Guzzle an einen anderen Server oder Slack schicken.
Exceptions für den Admin
Dies ist eine Symfony 3.* Konfiguration.
Es kann ebenfalls hilfreich sein, für bestimmte Benutzergruppen Exceptions in prod Environment anzeigen zu lassen. Twig erlaubt das Überschreiben der Error-Seiten (Error-Codes 400, 404, 500, etc.). Hier also noch eine andere Möglichkeit für erweitertes Abfangen von Exceptions:
Wir müssen dafür unsere eigenen Templates in das Verzeichnis app/Resource/TwigBundle/views/Exception/
legen. Erstellt man hier eine error.html.twig
, wird dieser Template für alle Exceptions genutzt.
{% extends '::base.html.twig' %} {% block content %} <h1>Eine Exceptions wurde ausgelöst!</h1> <div>Status Code: {{ status_code }}</div> {% if isLoggedIn %} <div>{{ exception.message }}<br>Error Code: {{ exception.code }}</div> {% if isAdmin %} <div> <ol> {% for trace in exception.trace %} <li> {% if trace.function %} at <strong> <abbr title="{{ trace.class }}">{{ trace.short_class }}</abbr> {{ trace.type ~ trace.function }} </strong> ({{ trace.args|format_args }}) {% endif %} {% if trace.file is defined and trace.file and trace.line is defined and trace.line %} {{ trace.function ? '<br>' : '' }} in {{ trace.file|format_file(trace.line) }} {% endif %} </li> {% endfor %} </ol> </div> {% endif %} {% endif %} {% endblock %}
Eingeloggte Nutzer bekommen hier noch die Exception-Nachricht, Admins sogar den kompletten StackTrace angezeigt. Da dies nur hinter einer Firewall funktioniert, ist das Ergebnis natürlich beschränkt. Außerdem kann es auch unvorteilhaft sein, eine Exception absichtlich auszulösen / zu testen, um den StackTrace einzusehen.