Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

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 von cron:run selbst (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.log

Ein 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.

app/code/Mironsoft/Loyalty/etc/di.xml
<?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.log

Mit 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.