Alle Artikel
12.09.2023

Symfony Error Monitoring mit Monolog und Elasticsearch

10 Minuten Lesezeit Ein blauer Phoenix mit aufgespannten Flügeln, das Logo von SyntaxPhoenixsebastian.diers
Eine Frau und ein Mann mit nachdenklichem Ausdruck vor einem Laptop. Dieser steht auf einem Schreibtisch mit einem weiteren Monitor, im Hintergrund weitere Personen an Schreibtischen. Auf dem Monitor ist Quelltext geöffnet.
Was Sie in diesem Artikel erwartet:
  • Wie man Elasticsearch und Kibana mit Debian 12 aufsetzt
  • Die Konfiguration von Symfony-Applikationen um an ELK Logs zu übermitteln
  • Die Konfiguration von Index-Patterns in Kibana

Als Web-Entwickler steht man oft vor der Herausforderung, dass Fehler in irgendwelchen Logs untergehen und übersehen werden. Doch gerade um wichtige Platzierungen bei Suchmaschinen zu halten, sollten möglichst keine Fehler das Nutzererlebnis beeinträchtigen.

Viele Fehler können im Vorfeld bereits durch gute Tests identifiziert werden, es kann aber dennoch immer mal vorkommen, dass ein Fehler durch die Überprüfung rutscht. Doch wenn der Fehler erstmal in der produktiven Anwendung ist, ist es umso wichtiger diesen schnell identifizieren zu können. In den nachfolgenden Abschnitten wollen wir Ihnen zeigen, wie Sie ganz einfach ein Error-Monitoring mit monolog und Elasticsearch (genauer dem ELK-Stack) aufbauen können um Ihre Symfony-Anwendung zu überwachen.

Das Paket monolog sollte einem als Symfony-Entwickler bereits bekannt vorkommen. Das Paket ist mit über 140 Mio. Installationen sehr weit verbreitet und wird auch von Symfony für das Logging empfohlen. Elasticsearch bzw. der ELK-Stack (bestehend aus Elasticsearch, Logstash und Kibana) dürften eher den Systemadministratoren bekannt vorkommen.

 

Aufbau des Monitorings

Um das Monitoring überhaupt nutzen zu können müssen wir erstmal den ELK-Stack aufsetzen. Vom ELK-Stack benötigen wir aber nur Elasticsearch und Kibana. Wir werden daher auch nur diese beiden Programme installieren. Eventuell macht es aber Sinn weitere Pakete wie Filebeat und Logstash zu installieren, sollte die Instanz für weitere Dienste abseits von Symfony verwendet werden. In der nachfolgenden Anleitung verwenden wir das Betriebssystem Debian 12 (Bookworm), unter anderen Distributionen oder Versionen kann es sein, dass bestimmte Inhalte nicht vorhanden sind oder Befehle nicht funktionieren. Im Rahmen des Tutorials zeigen wir nur die Installation einer einzelnen Elasticsearch-Node, für größere Installationen und mehr Ausfallsicherheit sollte über ein Cluster nachgedacht werden.

 

System-Voraussetzungen

 

Arbeitsspeicher min. 8GB (je nach System ggf. 64GB und mehr)
CPU min. 4 Kerne
Festplatte min. 50GB (für kleinere Umgebungen), für größere Umgebungen sollten SSDs für bessere I/O Performance verwendet werden

 

Installation und Konfiguration von Elasticsearch

Bevor Sie mit der Installation beginnen können, sollten Sie das System aktualisieren:

apt update
apt upgrade

Um Elasticsearch installieren zu können, müssen wir das Repository von Elastic hinzufügen. Hierfür muss zuerst der GPG-Key von Elasticsearch importiert werden, anschließend muss noch das Repository hinterlegt werden:

apt install curl gnupg2 -y
curl -sL https://artifacts.elastic.co/GPG-KEY-elasticsearch | gpg --dearmor > /etc/apt/trusted.gpg.d/elastic.gpg
echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | tee /etc/apt/sources.list.d/elastic-7.x.list

Nun kann Elasticsearch ganz einfach via apt installiert werden, nachdem mit apt update die Repositories neu geladen wurden:

apt update
apt install elasticsearch

Nach der Installation müssen noch ein paar Konfigurationen vorgenommen werden, damit Daten an Elasticsearch gesendet werden können.

Hierzu kann in der Datei  /etc/elasticsearch/elasticsearch.yml folgende Konfiguration übernommen werden:

cluster.name: <Clustername der Applikation>
network.host: <Host-Adresse des Servers>
http.port: 9200
bootstrap.memory_lock: true
discovery.type: single-node

Eventuell sind Teile dieser Konfiguration bereits vorhanden, in diesem Fall können Sie die Zeilen einfach überschreiben.

Doch was genau bedeuten die einzelnen Werte überhaupt?

  • cluster.name: Der Name des Clusters (In unserem Fall unserer Instanz) als Kennung (Sie haben die freie Wahl bei der Benennung)
  • network.host: Die Netzwerkadresse des Servers, damit Elasticsearch über diese erreichbar ist. Im Normalfall start Elasticsearch ansonsten nur auf dem Lokalhost (127.0.0.1)
  • http.port: Mit dem expliziten setzen des Ports verhindern wir, dass Elasticsearch sich den ersten freien Port ab 9200 auswählt. So ist der Port zuverlässig auf 9200.
  • bootstrap.memory_lock: Verhindert, dass Elasticsearch SWAP verwendet und dadurch langsamer wird
  • discovery.type: Setzt in unserem Fall das Cluster auf den Typ single-node, da wir keine zusätzlichen Server in das Cluster einbinden wollen

 

Anschließend können wir Elasticsearch als Service aktivieren und startklar machen:

service enable --now elasticsearch

 

Um zu überprüfen, ob Elasticsearch läuft, können wir den Status mit service status elasticsearch abfragen.

Um gänzlich zu überprüfen, können wir nun zur Schnittstelle unter http://IP-Adresse:9200 navigieren. Die Ausgabe sollte dort ähnlich wie hier aussehen:

{
  "name" : "debian12",
  "cluster_name" : "syntaxphoenix-demo",
  "cluster_uuid" : "HJhGJdjbTAWXku4prm2bZQ",
  "version" : {
    "number" : "7.14.0",
    "build_flavor" : "default",
    "build_type" : "deb",
    "build_hash" : "dd5a0a2acaa2045ff9624f3729fc8a6f40835aa1",
    "build_date" : "2021-07-29T20:49:32.864135063Z",
    "build_snapshot" : false,
    "lucene_version" : "8.9.0",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}

 

Installation und Konfiguration von Kibana

Um unsere gesammelten Fehler auch ansehen zu können, benötigen wir Kibana. In unserem Beispiel installieren wir Kibana auf dem selben Server wir Elasticsearch. Grundsätzlich können Sie Kibana aber auch auf einer eigenen Instanz installieren.

Um Kibana nun zu installieren müssen wir zuerst das Paket über apt mit folgendem Befehl installieren:

apt install kibana

 

Standardmäßig läuft Kibana auf 127.0.0.1:5601,

daher stellen wir die Adresse auf unsere erreichbare Adresse des Servers um. Für beide Dienste empfiehlt es sich aber einen nginx oder haproxy als proxy davor zu setzen. Auch um den Traffic per SSL zu verschlüsseln.

 

Für Kibana nehmen wir daher noch folgende Änderungen in /etc/kibana/kibana.yml vor:

server.host: "<Host-Adresse des Servers>"
elasticsearch.hosts: ["http://<Host-Adresse des Servers>:9200"]

 

Kibana muss nun nurnoch wie Elasticsearch als Service aktiviert werden, hierzu müssen wir folgenden Befehl verwenden:

service enable --now kibana

 

Sollte noch eine Firewall wie z.B. ufw installiert sein, müssen die Ports für Elasticsearch und Kibana entsprechend geöffnet werden.

 

Anschließend sollten wir, wenn wir http://<Adresse unseres Servers>:5601 aufrufen, folgendes sehen:

Ansicht der Startseite von Kibana

 

Nun ist unser Server mit Elasticsearch und Kibana erfolgreich aufgesetzt und wir können entsprechend mit der Konfiguration innerhalb unserer Symfony-Anwendung starten.

Konfiguration der Symfony-Anwendung

Innerhalb der Symfony-Anwendung müssen wir nur wenig Anpassen, damit wir mit Elasticsearch kommunizieren können.

Zuerst müssen wir hierfür sicherstellen, dass monolog bzw. das monolog-bundle installiert ist und es ansonsten mit composer installieren:

composer install "symfony/monolog-bundle"

 

Anschließend können wir den ElasticsearchLogstashHandler konfigurieren. Dieser sendet unsere logs direkt zu Elasticsearch und formattiert diese wie Logstash.

Hierzu müssen wir den Service zuerst in der services.yaml unserer Applikation hinterlegen:

    Symfony\Bridge\Monolog\Handler\ElasticsearchLogstashHandler:
        arguments:
            $endpoint: '%env(ELK_ENDPOINT)%'
            $index: "<Name unserer Applikation>"
            $client: null
            $bubble: true

 

Als Index hinterlegen wir den Namen unserer Applikation, sodass wir diese später suchen können. Bei uns z.B. syntaxphoenix-homepage. Den Endpunkt setzen wir auf die Variable ELK_ENDPOINT, welche konfigurierbar über die .env-Datei ist. Dies ist hilfreich um auf Test-Servern eine andere ELK-Stack-Instanz verwenden zu können, sodass die Production-Logs nicht mit mit Test-Logs gemischt werden.

In der .env-Datei würde die Zeile dann wie folgt aussehen:

ELK_ENDPOINT=<Adresse unserer Elasticsearch-Instanz, inklusive Protokoll>

Die Adresse könnte dann z.B. https://elk-prod.yourwebsite.com sein.

 

Um nun auch monolog richtig zu konfigurieren müssen wir dort den ELK-Handler hinterlegen. Im Normalfall sollten wir nur Logs speichern wollen, wenn wir die Anwendung produktiv gebaut haben (Für Test-Server und Produktiv-Server). Daher erstellen wir die monolog.yaml im Verzeichnis /config/packages/prod. Um zu verhindern, dass die Anwendung durch die Anfragen verlangsamt wird und damit nur einmal pro Anfrage alle Daten an Elasticsearch gesendet werden, sollte ein Finger-Crossed-Handler oder ein Buffer-Handler vor dem ElasticsearchLogstashHandler verwendet werden. Wir verwenden hier ersteres:

monolog:
    handlers:
        logfile:
            type:  rotating_file
            path: "%kernel.logs_dir%/%kernel.environment%.log"
            level: debug
            max_files: 14
        main:
            type: fingers_crossed
            level: debug
            channels: ["!event"]
            handler: elklogger
            action_level: error
            excluded_http_codes: [404, 405]
            buffer_size: 50
        elklogger:
            type: service
            id: Symfony\Bridge\Monolog\Handler\ElasticsearchLogstashHandler

 

In unserer Konfiguration setzen wir zusätzlich auf eine rotierende Logfile, damit logs im Notfall auch über die Verzeichnisse des Servers erreichbar sind, sollte es Probleme mit Elasticsearch geben. Zusätzlich senden wir nur alle Logs einer Anfrage, wenn ein Log-Eintrag mit Level ERROR vorhanden ist. Dies kann unter action_level im fingers_crossed-Handler angepasst werden. Alle normalen Anfragen ohne das entsprechende Action-Level werden aber weiterhin in den Logfiles gespeichert.

 

Die Konfiguration unserer Applikation für Elasticsearch ist nun abgeschlossen. Nun können wir in Kibana über das Burger-Menu auf der linken-Seite zur Seite Discover (Unter dem Reiter Analytics) navigieren. Die Seite sollte wie folgt aussehen:

Discover-Bereich von Kibana

 

Damit wir nun auch Einträge finden können, müssen wir sogenannte Index-Patterns angeben. Diese entsprechen dem Wert, welchen wir in unserer services.yaml für $index beim ElasticsearchLogstashHandler angegeben haben. Index-Patterns können wir konfigurieren unter <Kibana-Url>/app/management/kibana/indexPatterns. Hier können wir über den Button Create index pattern ein Pattern anlegen. Alle Pattern sollten mit einem Sternchen (*) enden. Als Timestamp-Field kann das Feld @timestamp ausgewählt werden. Sollte das Pattern noch nicht existieren, aber Daten dafür vorhanden sein, kann dies nun angelegt werden.

Index-Pattern in Kibana erstellen

 

Nun können wir uns unsere Logs über Discover ansehen. Als Index-Pattern können wir nun unser gewähltes Index-Pattern auswählen. Sollten alle Anwendungen gleichzeitig angezeigt werden, so kann man dies realisieren, indem man das Index-Pattern * anlegt. So werden alle Index-Pattern via Wildcard geladen.