Bug #78849

Show logged records of DatabaseWriter in ext:belog

Added by Oliver Hader over 4 years ago. Updated 10 months ago.

Status:
New
Priority:
Should have
Assignee:
-
Category:
Logging
Target version:
-
Start date:
2016-12-01
Due date:
% Done:

0%

Estimated time:
TYPO3 Version:
7
PHP Version:
7.0
Tags:
Complexity:
Is Regression:
No
Sprint Focus:

Description

Log entries that have been persisted using the logging-framework are not visualized in ext:belog.
The reason is, that the logging-framework uses different field names for the sys_log table that are not considered.

#1

Updated by Steffen Müller over 4 years ago

This has historic reasons: When the Logging API was merged, the dev team's opinion was to rather develop a dedicated BE log module for the Logging API than to squeeze the stuff into the current be log module.

I still believe we should first refactor the current syslog, because there's too much stuff tightly coupled: sys_log + sys_history table, Page->info->history module, be log module + GeneralUtility:sysLog() + various class methods *log() writing more or less directly to sys_log.

Roughly said:
1. separate sys_history and sys_log
2. deprecate GeneralUtility::sysLog() + GeneralUtility::devLog() + any other *log() method in favor of Logging API
3. Show Logging API DatabaseWriter stuff in BE log.
4. Show sys_history in a dedicated module

Currently the be log can filter the following "Actions": database, file, login, cache, settings, errors.

In future this could be decoupled in the following way:
1. put database, file stuff exclusively into sys_history
2. put errors, settings, login, cache into be log.

Just my 2ct.

#2

Updated by Oliver Hader over 4 years ago

@Steffen: Thanks for your feedback and more detailed insight. How would the current "action filter" with error, database, login, ... be reflected with the logging framework? Would that be a new attribute "scope" or "topic", or modeled without changing the schema?

#3

Updated by Frans Saris over 3 years ago

Now sys_log_history is separated. how to proceed here? https://review.typo3.org/#/c/53195/

IMO it should be save to just make sure the current DatabaseWriter uses the sys_log format else is't rather useless.

#4

Updated by Gerrit Code Review over 3 years ago

  • Status changed from New to Under Review

Patch set 1 for branch master of project Packages/TYPO3.CMS has been pushed to the review server.
It is available at https://review.typo3.org/55086

#5

Updated by Gerrit Code Review over 3 years ago

Patch set 2 for branch master of project Packages/TYPO3.CMS has been pushed to the review server.
It is available at https://review.typo3.org/55086

#6

Updated by Markus Klein almost 3 years ago

  • Status changed from Under Review to New
#7

Updated by Guillaume Crico 10 months ago

Here is a minimal implementation.

<?php
namespace Awesome\Extension\Log\Writer;

use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Log\LogLevel;
use TYPO3\CMS\Core\Log\LogRecord;
use TYPO3\CMS\Core\Log\Writer\AbstractWriter;
use TYPO3\CMS\Core\Utility\GeneralUtility;

/**
 * Log writer that populates the TYPO3 sys_log table in a way friendly with the belog extension.
 *
 * This log writer has no options.
 *
 * The name of the class comes from the discussion in https://review.typo3.org/c/Packages/TYPO3.CMS/+/55086/
 */
class Typo3SysLogDatabaseWriter extends AbstractWriter
{
    /**
     * Writes the log record.
     *
     * @param \TYPO3\CMS\Core\Log\LogRecord $record
     * @return \TYPO3\CMS\Core\Log\Writer\WriterInterface $this
     */
    public function writeLog(LogRecord $record)
    {
        // If the Exception handler tries to log an Exeption with this writer,
        // and the DB is still not ready, then there would be a very nasty loop!
        // So, any exception catched here will be rethrown, wrapped as the
        // "previous" of a Typo3SysLogDatabaseWriterException.
        try {
            $recData = $record->getData();
            if (isset($recData['exception']) && $recData['exception'] instanceof Typo3SysLogDatabaseWriterException) {
                // Would be nice to have a fallback logger for this special case!
                // But the "fallback" should be the responsability of the LoggerManager (it's not ready for that yet).
                // We just "pass" for now...
                return $this;
            }

            $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('sys_log');
            $connection->insert('sys_log', $this->createValuesFromRecord($record));

        } catch (\Exception $e) {
            // See comment above...
            throw new Typo3SysLogDatabaseWriterException('Error while logging to typo3 sys_log table.', 0, $e);
        }

        return $this;
    }

    /**
     * Heavily inspired by TYPO3\CMS\Core\Error\ErrorHandler->writeLog().
     *
     * @param \TYPO3\CMS\Core\Log\LogRecord $record
     * @return \TYPO3\CMS\Core\Log\Writer\WriterInterface $this
     */
    private function createValuesFromRecord(LogRecord $record)
    {
        $logMessage = $record->getComponent() . ': ' . $record->getMessage();
        $tstamp = (int) $record->getCreated();
        $userid = 0;
        $workspace = 0;
        $data = [];

        if (empty($logMessage)) {
            $recData = $record->getData();
            if (isset($recData['exception']) && $recData['exception'] instanceof \Exception) {
                $logMessage = $recData['exception']->getMessage();
            }
        }

        $severity = 0; // 0 = OK, 1 = warning, 2 = error
        if ($record->getLevel() <= LogLevel::ERROR) {
            $severity = 2;
        } elseif ($record->getLevel() === LogLevel::WARNING) {
            $severity = 1;
        }

        $backendUser = isset($GLOBALS['BE_USER']) ? $GLOBALS['BE_USER'] : null;

        if (is_object($backendUser)) {
            if (isset($backendUser->user['uid'])) {
                $userid = $backendUser->user['uid'];
            }
            if (isset($backendUser->workspace)) {
                $workspace = $backendUser->workspace;
            }
            if (!empty($backendUser->user['ses_backuserid'])) {
                $data['originalUser'] = $backendUser->user['ses_backuserid'];
            }
        }

        $values = [
            'userid' => $userid,
            'type' => 5,
            'action' => 0,
            'error' => $severity,
            'details_nr' => 0,
            'details' => str_replace('%', '%%', $logMessage),
            'log_data' => (empty($data) ? '' : serialize($data)),
            'IP' => (string) GeneralUtility::getIndpEnv('REMOTE_ADDR'),
            'tstamp' => $tstamp,
            'workspace' => $workspace
        ];

        return $values;
    }
}

/**
 * This exception is thrown by the Typo3SysLogDatabaseWriter, in case the write failed.
 *
 * It is used to try to detect any loop in case the log writing itself throw an Exception
 * and an Exception Handler uses the Typo3SysLogDatabaseWriter.
 */
class Typo3SysLogDatabaseWriterException extends \RuntimeException
{
}

In typo3conf/AdditionalConfiguration.php, one can use:

// Default Log Writer: Typo3SysLogDatabaseWriter
$GLOBALS['TYPO3_CONF_VARS']['LOG']['writerConfiguration'] = [
    \TYPO3\CMS\Core\Log\LogLevel::WARNING => [
        \Awesome\Extension\Log\Writer\Typo3SysLogDatabaseWriter::class => []
    ],
];

Also available in: Atom PDF