CakePHP 3.x:日志为序列化数组

CakePHP 3.x:日志为序列化数组,cakephp,cakephp-3.x,Cakephp,Cakephp 3.x,我正在为CakePHP编写自己的解析器日志 我只需要一件事:它不是写一条日志“消息”(作为字符串),而是写一个带有各种日志信息(日期、类型、行、堆栈跟踪等)的序列化数组 但是我不明白我应该重写什么方法/类,尽管我已经查阅了API。你能帮我吗 编辑: 现在我做的是相反的:我读取日志(已经写入),然后用正则表达式将它们转换成一个数组 我的代码: $logs = array_map(function($log) { preg_match('/^'. '([\d\-]+\s[\d

我正在为CakePHP编写自己的解析器日志

我只需要一件事:它不是写一条日志“消息”(作为字符串),而是写一个带有各种日志信息(日期、类型、行、堆栈跟踪等)的序列化数组

但是我不明白我应该重写什么方法/类,尽管我已经查阅了API。你能帮我吗

编辑:
现在我做的是相反的:我读取日志(已经写入),然后用正则表达式将它们转换成一个数组

我的代码:

$logs = array_map(function($log) {
    preg_match('/^'.
        '([\d\-]+\s[\d:]+)\s(Error: Fatal Error|Error|Notice: Notice|Warning: Warning)(\s\(\d+\))?:\s([^\n]+)\n'.
        '(Exception Attributes:\s((.(?!Request|Referer|Stack|Trace))+)\n)?'.
        '(Request URL:\s([^\n]+)\n)?'.
        '(Referer URL:\s([^\n]+)\n)?'.
        '(Stack Trace:\n(.+))?'.
        '(Trace:\n(.+))?(.+)?'.
    '/si', $log, $matches);

    switch($matches[2]) {
        case 'Error: Fatal Error':
            $type = 'fatal';
            break;
        case 'Error':
            $type = 'error';
            break;
        case 'Notice: Notice':
            $type = 'notice';
            break;
        case 'Warning: Warning':
            $type = 'warning';
            break;
        default:
            $type = 'unknown';
            break;
    }

    return (object) af([
        'datetime'      => \Cake\I18n\FrozenTime::parse($matches[1]),
        'type'          => $type,
        'error'         => $matches[4],
        'attributes'    => empty($matches[6]) ? NULL : $matches[6],
        'url'           => empty($matches[9]) ? NULL : $matches[9],
        'referer'       => empty($matches[11]) ? NULL : $matches[11],
        'stack_trace'   => empty($matches[13]) ? (empty($matches[16]) ? NULL : $matches[16]) : $matches[13],
        'trace'         => empty($matches[15]) ? NULL : $matches[15]
    ]);
}, af(preg_split('/[\r\n]{2,}/', $logs)));
现在我做的是相反的:我读取日志(已经写入),然后用正则表达式将它们转换成数组


问题是这太贵了。相反,最好是以序列化数组的形式直接写入日志。

我认为您要做的是编写自己的日志。 您只需创建一个类ArrayLog(扩展BaseLog),如文档中所述,并配置cakePHP来使用它。在log函数中,可以将$level、$message和$context等信息作为数组附加到文件中。这将生成一个包含多个阵列的日志文件,然后可以拆分这些阵列

也就是说,我建议登录到数据库并读取它,而不是解析。

好的,就是这样

(注意,这段代码完全是实验性的,我还没有对它进行适当的测试)

我想做的一件有趣的事情是:对于每个日志,写入序列化文件,同时写入计划文件。 这使我可以将日志作为纯文本文件读取,也可以使用序列化文件对其进行操作

use Cake\Log\Engine\FileLog;

class SerializedLog extends FileLog {
    protected function _getLogAsArray($level, $message) {       
        $serialized['level'] = $level;
        $serialized['datetime'] = date('Y-m-d H:i:s');

        //Sets exception type and message
        if(preg_match('/^(\[([^\]]+)\]\s)?(.+)/', $message, $matches)) {                
            if(!empty($matches[2]))
                $serialized['exception'] = $matches[2];

            $serialized['message'] = $matches[3];
        }

        //Sets the exception attributes
        if(preg_match('/Exception Attributes:\s((.(?!Request URL|Referer URL|Stack Trace|Trace))+)/is', $message, $matches)) {
            $serialized['attributes'] = $matches[1];
        }

        //Sets the request URL
        if(preg_match('/^Request URL:\s(.+)$/mi', $message, $matches)) {
            $serialized['request'] = $matches[1];
        }

        //Sets the referer URL
        if(preg_match('/^Referer URL:\s(.+)$/mi', $message, $matches)) {
            $serialized['referer'] = $matches[1];
        }

        //Sets the trace
        if(preg_match('/(Stack )?Trace:\n(.+)$/is', $message, $matches)) {
            $serialized['trace'] = $matches[2];
        }

        $serialized['full'] = date('Y-m-d H:i:s').' '.ucfirst($level).': '.$message;

        return (object) $serialized;
    }


    public function log($level, $message, array $context = []) {
        $message = $this->_format(trim($message), $context);

        $filename = $this->_getFilename($level);
        if (!empty($this->_size)) {
            $this->_rotateFile($filename);
        }

        $pathname = $this->_path . $filename;
        $mask = $this->_config['mask'];

        //Gets the content of the existing logs and unserializes
        $logs = @unserialize(@file_get_contents($pathname));

        if(empty($logs) || !is_array($logs))
            $logs = [];

        //Adds the current log
        $logs[] = $this->_getLogAsArray($level, $message);

        //Serializes logs
        $output = serialize($logs);

        if (empty($mask)) {
            return file_put_contents($pathname, $output);
        }

        $exists = file_exists($pathname);
        $result = file_put_contents($pathname, $output);
        static $selfError = false;

        if (!$selfError && !$exists && !chmod($pathname, (int)$mask)) {
            $selfError = true;
            trigger_error(vsprintf(
                'Could not apply permission mask "%s" on log file "%s"',
                [$mask, $pathname]
            ), E_USER_WARNING);
            $selfError = false;
        }

        return $result;
    }
}

什么是“解析器日志”?这里的上下文是什么?您希望以特定格式记录哪些内容?@ndm,请参阅我的编辑。很简单:日志被写成字符串,其中包含各种信息(错误类型、日期和时间、请求的引用者和url、消息等)。但是我想成为一个序列化的数组,包含这些信息,我已经得到了这些信息,但是这些数据到底在哪里呢?您是在谈论默认的调试/错误日志吗?也就是说,每个日志都应该序列化吗?或者仅仅是错误?您是否正在使用一个自定义记录器,该记录器应该是唯一受影响的记录器?您确实需要更具体一些。@nmn这段代码在哪里并不重要。它可以是控制器,但也可以是CakePHP的外部脚本。但是,在CakePHP编写日志时,这一点很重要。您看到的代码读取默认记录器生成的CakePHP日志文件。我试图用这种方式更好地解释:现在日志是按日期排序的。如果我想按请求URL或生成它们的文件名对它们进行排序,该怎么办?因此,我发布的代码读取日志文件,正则表达式提取信息。如果CakePHP以序列化数组的形式编写日志,所有这些都会变得更容易。你没有理解我的意思,如果我不知道哪些日志应该以序列化的方式编写,我就不能给你一个正确的答案!谢谢@DIDoS,我已经想到了这一点。问题是,在本例中,
$message
参数已经包含了最后一个字符串,即各种连接信息。当然,在这里划分总是更好的(再次使用正则表达式),但我想知道是否可以在上游操作。您认为这可能吗?我相信受影响的代码是
\u getMessage()
\u logError()
\u logException()
方法,它们来自
BaseErrorHandler
。在这里,对于错误和异常,信息被连接成一个字符串。一旦它们是字符串,它们将被发送到日志。所以,也许我们需要编写一个LogAdapter和一个ErrorHandler。你认为正确吗?