Cronjob-Fehlerbehandlung und Logging richtig umsetzen
Cronjob-Fehlerbehandlung und Logging richtig umsetzen
~7 Min. Lesezeit Zuletzt aktualisiert am 9. August 2026
Kapitel 30 zeigte, warum ein Observer auf sales_order_place_after niemals eine Exception nach außen dringen lassen darf - sie würde den Checkout-Request des Kunden zerstören. Für ExpirePoints (Kapitel 33) gilt eine strukturell andere, aber ähnlich wichtige Regel: kein Kunde wartet synchron auf einen Cronjob, trotzdem kann ein unbehandelter Fehler hier reale Schäden anrichten - nur auf eine andere Art.
Was passiert bei einer unbehandelten Exception?
Anders als bei einem Checkout-Observer bricht eine Exception in einer Cronjob-Methode nicht den gesamten cron:run-Aufruf ab - Magentos eigener Ausführungsmechanismus (\Magento\Cron\Observer\ProcessCronQueueObserver) fängt sie pro Job ab, markiert die betroffene Zeile in der cron_schedule-Tabelle mit status = 'error', schreibt die Fehlermeldung in die messages-Spalte und fährt mit dem nächsten fälligen Job fort. Andere Gruppen und Jobs laufen unbeeinflusst weiter.
Achtung: Das gilt nur für die äußerste Ebene - die von Magento aufgerufene execute()-Methode selbst. Der try/catch innerhalb der foreach-Schleife in Kapitel 33 ist trotzdem notwendig: ohne ihn würde eine Exception beim zweiten von 500 fälligen Kunden die komplette Restschleife abbrechen - die verbleibenden 498 Kunden würden erst mit dem nächsten Cron-Lauf (laut Kapitel 32 frühestens am nächsten Tag) verarbeitet. Das äußere Auffangen durch Magento selbst schützt nur den Gesamtprozess, nicht die einzelnen Iterationen innerhalb eines Jobs.
Wo Fehler landen
cron_schedule.status/cron_schedule.messages- die kurzfristige, technische Sicht auf einen fehlgeschlagenen Lauf.var/log/system.log/var/log/exception.log- Magentos Standard-Logdateien, sofern kein eigener Logger-Kanal konfiguriert ist (siehe unten).var/log/cron.log- reine Ausführungsprotokolle voncron:runselbst (Start/Ende/Dauer je Job), kein Ersatz für strukturiertes Fehler-Logging.
# Direkt im Container mitlesen (bin/log-Wrapper aus CLAUDE.md)
bin/log cron.log
bin/log system.logEin eigener Log-Kanal für dieses Modul
Ohne Konfiguration landen alle LoggerInterface-Aufrufe in var/log/system.log, vermischt mit jedem anderen Modul. Ein eigener Monolog-Kanal - eine reine di.xml-Konfiguration, keine neue Klasse - schreibt Loyalty-spezifische Log-Zeilen in eine eigene Datei.
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<virtualType name="Mironsoft\Loyalty\Logger\Handler"
type="Magento\Framework\Logger\Handler\Base">
<arguments>
<argument name="fileName" xsi:type="string">var/log/mironsoft_loyalty.log</argument>
</arguments>
</virtualType>
<virtualType name="Mironsoft\Loyalty\Logger\Logger" type="Monolog\Logger">
<arguments>
<argument name="name" xsi:type="string">mironsoftLoyalty</argument>
<argument name="handlers" xsi:type="array">
<item name="system" xsi:type="object">Mironsoft\Loyalty\Logger\Handler</item>
</argument>
</arguments>
</virtualType>
<type name="Mironsoft\Loyalty\Cron\ExpirePoints">
<arguments>
<argument name="logger" xsi:type="object">Mironsoft\Loyalty\Logger\Logger</argument>
</arguments>
</type>
</config>Der <type name="...ExpirePoints">-Block überschreibt nur für diese eine Klasse, welche konkrete LoggerInterface-Implementierung der Konstruktor-Parameter $logger injiziert bekommt - AwardPointsOnOrderPlaced (Kapitel 30) und ReversePointsOnCreditmemoSave (Kapitel 31) ließen sich über dieselbe Technik ebenfalls auf Mironsoft\Loyalty\Logger\Logger umstellen, sind hier aber bewusst beim Standard-Kanal belassen, um die kritische, checkout-nahe Fehlerausgabe nicht in einer Datei zu verstecken, die niemand routinemäßig überwacht.
Der Status "missed"
Neben error kennt cron_schedule auch missed: ein Job wird als verpasst markiert und nicht ausgeführt, wenn seine geplante Zeit um mehr als schedule_lifetime Minuten (Kapitel 32, hier 180) überschritten ist - etwa nach einem längeren Server-Ausfall oder wenn ein vorheriger Lauf derselben Gruppe blockierte. Bei einem täglichen Job mit dreistündigem schedule_lifetime bedeutet das: fällt der Server zur geplanten Ausführungszeit für mehr als drei Stunden aus, läuft der Punkte-Ablauf an diesem Tag gar nicht - er holt es aber am nächsten Tag automatisch nach, weil die Soll-Ist-Berechnung aus Kapitel 33 keinen Unterschied zwischen "gestern fällig geworden" und "heute fällig geworden" macht.
Tipp: Zum lokalen Testen reicht ein manueller Aufruf über den bin/-Wrapper - kein Warten auf den nächsten geplanten Lauf nötig.
bin/magento cron:run --group=mironsoft_loyalty
bin/log mironsoft_loyalty.logMit robuster Fehlerbehandlung auf beiden Seiten - Observer (Kapitel 30/31) und Cronjob (Kapitel 33/34) - wendet sich Kapitel 35 der anderen Richtung zu: dieses Modul selbst als Absender eigener Events für andere Module.