像尾一样反向日志

Reverse log like tail

我想撤销我的系统日志。

我的系统日志是这样的:

[ 2016-03-17T15:52:00+08:00 ] 0.0.0.0 /Pwebshell/index.php/Log/GetLog
INFO: [ route_check ] --START--
INFO: CheckRoute Behavior ::run [ RunTime:0.001000s ]
INFO: [ route_check ] --END-- [ RunTime:0.001000s ]
INFO: [ app_begin ] --START--
INFO: ReadHtmlCache Behavior ::run [ RunTime:0.001000s ]
INFO: [ app_begin ] --END-- [ RunTime:0.001000s ]

[ 2016-03-17T15:52:16+08:00 ] 0.0.0.0 /Pwebshell/index.php/Log/GetLog
INFO: [ route_check ] --START--
INFO: CheckRoute Behavior ::run [ RunTime:0.000000s ]
INFO: [ route_check ] --END-- [ RunTime:0.001000s ]
INFO: [ app_begin ] --START--
INFO: ReadHtmlCache Behavior ::run [ RunTime:0.000000s ]
INFO: [ app_begin ] --END-- [ RunTime:0.000000s ]

我想这样打印:

[ 2016-03-17T15:52:16+08:00 ] 0.0.0.0 /Pwebshell/index.php/Log/GetLog
INFO: [ route_check ] --START--
INFO: CheckRoute Behavior ::run [ RunTime:0.000000s ]
INFO: [ route_check ] --END-- [ RunTime:0.001000s ]
INFO: [ app_begin ] --START--
INFO: ReadHtmlCache Behavior ::run [ RunTime:0.000000s ]
INFO: [ app_begin ] --END-- [ RunTime:0.000000s ]

[ 2016-03-17T15:52:00+08:00 ] 0.0.0.0 /Pwebshell/index.php/Log/GetLog
INFO: [ route_check ] --START--
INFO: CheckRoute Behavior ::run [ RunTime:0.001000s ]
INFO: [ route_check ] --END-- [ RunTime:0.001000s ]
INFO: [ app_begin ] --START--
INFO: ReadHtmlCache Behavior ::run [ RunTime:0.001000s ]
INFO: [ app_begin ] --END-- [ RunTime:0.001000s ]

到目前为止我尝试过的:

1 首先,我使用 file_get_contents

阅读日志
$content = file_get_contents('log');

2 然后我使用正则表达式 select 条目。

preg_match_all('#(\[\s+\d{4}\-\d{2}\-\d{2}.*\].*?)\[\s+\d{4}\-\d{2}\-\d{2}#s', $content, $matches);

3我会用array_reverse反转日志

但是 $matches 没有被正则表达式 select 正确编辑。

正则表达式存在三个不同的问题:

  • 您需要第一个 .* 不贪心,否则它将匹配文件中的最后一个 ]
  • 您的正则表达式与最后一个条目不匹配,因为它后面没有另一个条目。
  • 每个匹配 "captures" 匹配的文本,包括以下 \[\s+\d{4}\-\d{2}\-\d{2}(即它捕获下一个条目的开始)。这意味着只会返回部分匹配项。要纠正这个问题,我们需要使用 (?=...).
  • 将最后一部分转换为前瞻断言

像下面这样的东西应该可以解决这三个问题。

preg_match_all('#(\[\s+\d{4}\-\d{2}\-\d{2}.*?\].*?)(?=\[\s+\d{4}\-\d{2}\-\d{2}|$)#s', $content, $matches);

但是您的条目似乎被双行分隔。为什么不只是拆分这些日志?假设您不会在日志数据中自然地获得双换行,这将更易于阅读、更快且更不容易出错。

以下内容适用于 Linux:

$lines = explode("\n\n", $content);
$lines = array_reverse($lines);
echo implode("\n\n", $lines);

在 Windows 上,您可能需要将 \n\n 替换为 \r\n\r\n

一种改进的解决方案,无论换行符的类型如何,都可以使用,允许在换行符之间使用白色 space,并且使用系统默认的行尾输出可能如下所示:

$lines = preg_split("#\r?\n\s*\n#", $content);
$lines = array_reverse($lines);
echo implode(PHP_EOL . PHP_EOL, $lines);

这还将处理这样一个事实,即您的日志内容有时似乎包含两个以上的换行分隔条目。