<startdow>*</startdow>
<canbedisabled>0</canbedisabled>
</cronjob>
+ <cronjob name="com.woltlab.wcf.exceptionMailer">
+ <classname>wcf\system\cronjob\ExceptionMailerCronjob</classname>
+ <description>Sends out notifications about new entries in the error log.</description>
+ <description language="de">Versendet Benachrichtigungen über neue Einträge im Fehlerprotokoll.</description>
+ <startminute>0</startminute>
+ <starthour>5</starthour>
+ <startdom>*</startdom>
+ <startmonth>*</startmonth>
+ <startdow>*</startdow>
+ </cronjob>
</import>
</data>
--- /dev/null
+<?php
+namespace wcf\system\cronjob;
+use wcf\data\cronjob\Cronjob;
+use wcf\system\email\mime\PlainTextMimePart;
+use wcf\system\email\Email;
+use wcf\system\email\Mailbox;
+use wcf\system\language\LanguageFactory;
+use wcf\system\registry\RegistryHandler;
+use wcf\util\ExceptionLogUtil;
+use wcf\util\StringUtil;
+
+/**
+ * Mails an Exception summary.
+ *
+ * @author Tim Duesterhus
+ * @copyright 2001-2019 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Cronjob
+ * @since 5.2
+ */
+class ExceptionMailerCronjob extends AbstractCronjob {
+ /**
+ * @inheritDoc
+ */
+ public function execute(Cronjob $cronjob) {
+ parent::execute($cronjob);
+
+ $timestamp = RegistryHandler::getInstance()->get('com.woltlab.wcf', 'exceptionMailerTimestamp');
+ $timestamp = max($timestamp, TIME_NOW - 86400 * 3);
+ for ($it = $timestamp; $it < TIME_NOW; $it += 86400) {
+ $files[gmdate('Y-m-d', $it)] = [];
+ }
+ $files[gmdate('Y-m-d', TIME_NOW)] = [];
+
+ $seen = [];
+ foreach ($files as $file => $value) {
+ $path = WCF_DIR.'log/'.$file.'.txt';
+ if (!file_exists($path)) {
+ unset($files[$file]);
+ continue;
+ }
+ // check log size (2MiB) to prevent resource exhaustion
+ if (filesize($path) > 2 * (1 << 20)) {
+ $files[$file] = [
+ 'verdict' => 'huge'
+ ];
+ continue;
+ }
+ try {
+ $exceptions = ExceptionLogUtil::splitLog(file_get_contents($path));
+ }
+ catch (\Exception $e) {
+ $files[$file] = [
+ 'verdict' => 'corrupt'
+ ];
+ continue;
+ }
+
+ $count = 0;
+ $files[$file]['messages'] = [];
+ foreach ($exceptions as $exception) {
+ $exception = ExceptionLogUtil::parseException($exception);
+ $message = $exception['message'];
+ if ($exception['date'] < $timestamp) continue;
+
+ $count++;
+ if (!isset($seen[$message]) && count($files[$file]['messages']) < 3) {
+ $files[$file]['messages'][] = StringUtil::truncate(preg_replace('/\s+/', ' ', $message), 140);
+ $seen[$message] = true;
+ }
+ }
+ if ($count == 0) {
+ unset($files[$file]);
+ continue;
+ }
+
+ $files[$file]['count'] = $count;
+ }
+
+ if (empty($files)) return;
+
+ $language = LanguageFactory::getInstance()->getDefaultLanguage();
+
+ $email = new Email();
+ $email->addRecipient(new Mailbox(MAIL_ADMIN_ADDRESS, null, $language));
+ $email->setSubject($language->getDynamicVariable('wcf.acp.exceptionLog.email.subject', [
+ 'date' => $timestamp
+ ]));
+ $email->setBody(new PlainTextMimePart($language->getDynamicVariable('wcf.acp.exceptionLog.email.body', [
+ 'date' => $timestamp,
+ 'files' => $files
+ ])));
+ $email->send();
+ RegistryHandler::getInstance()->set('com.woltlab.wcf', 'exceptionMailerTimestamp', TIME_NOW);
+ }
+}
<item name="wcf.acp.exceptionLog.exception.date"><![CDATA[Datum]]></item>
<item name="wcf.acp.exceptionLog.exception.userAgent"><![CDATA[Browser]]></item>
<item name="wcf.acp.exceptionLog.exception.memory"><![CDATA[Arbeitsspeicher]]></item>
+ <item name="wcf.acp.exceptionLog.email.subject"><![CDATA[Fehlermeldungen seit {$date|plainTime}]]></item>
+ <item name="wcf.acp.exceptionLog.email.body"><![CDATA[Hallo Administrator,
+
+die folgenden Fehlermeldungen auf Ihrer Website {@PAGE_TITLE|language} wurden seit dem {$date|plainTime} protokolliert:
+
+{foreach from=$files key=file item=data}
+log/{$file}.txt {* this line ends with a space *}
+==================
+
+{if $data[verdict]|isset}
+{if $data[verdict] == 'corrupt'}
+Das Fehlerprotokoll ist defekt. Bitte prüfen Sie das Protokoll händisch: {'WCF_DIR'|constant}log/{$file}.txt
+{elseif $data[verdict] == 'huge'}
+Das Fehlerprotokoll ist sehr groß. Bitte prüfen Sie das Protokoll händisch: {'WCF_DIR'|constant}log/{$file}.txt
+{/if}
+{else}
+Das Fehlerprotokoll enthält {$data[count]} neue Einträge. Die ersten drei, in dieser E-Mail noch nicht genannten, Fehlermeldungen lauten:
+{foreach from=$data[messages] item=message}
+- {@$message} {* this line ends with a space *}
+{/foreach}
+{/if}
+{* this line is intentionally left empty *}
+{/foreach}]]></item>
</category>
<category name="wcf.acp.group">
<item name="wcf.acp.group.add"><![CDATA[Benutzergruppe hinzufügen]]></item>
<item name="wcf.acp.exceptionLog.exception.date"><![CDATA[Time]]></item>
<item name="wcf.acp.exceptionLog.exception.userAgent"><![CDATA[User Agent]]></item>
<item name="wcf.acp.exceptionLog.exception.memory"><![CDATA[Memory]]></item>
+ <item name="wcf.acp.exceptionLog.email.subject"><![CDATA[Logged Errors since {$date|plainTime}]]></item>
+ <item name="wcf.acp.exceptionLog.email.body"><![CDATA[Dear Administrator,
+
+the following errors have been logged on your website {@PAGE_TITLE|language} since {$date|plainTime}:
+
+{foreach from=$files key=file item=data}
+log/{$file}.txt {* this line ends with a space *}
+==================
+
+{if $data[verdict]|isset}
+{if $data[verdict] == 'corrupt'}
+This protocol file is corrupted. Please check the protocol manually: {'WCF_DIR'|constant}log/{$file}.txt
+{elseif $data[verdict] == 'huge'}
+This protocol file is very large. Please check the protocol manually: {'WCF_DIR'|constant}log/{$file}.txt
+{/if}
+{else}
+This protocol file contains {$data[count]} new entries. The first three error messages that are not yet listed in this email are:
+{foreach from=$data[messages] item=message}
+- {@$message} {* this line ends with a space *}
+{/foreach}
+{/if}
+{* this line is intentionally left empty *}
+{/foreach}]]></item>
</category>
<category name="wcf.acp.group">
<item name="wcf.acp.group.add"><![CDATA[Add User Group]]></item>