好吧,我试图解析PHP错误日志。因此我构建了以下类:
<?php
/**
* Created by PhpStorm.
* User: toton
* Date: 2/29/2016
* Time: 8:16 AM
*/
final class error_parser
{
private $log_file_path;
private $current_line;
private $recent;
/**
* error_parser constructor.
* Takes in the path of the error log file of PHP ERROR LOG FILE.
* And another param for checking to get the direction to traverse the file.
* @param string $log_file_path
* @param bool $recent
*/
public function __construct($log_file_path, $recent = true)
{
$this->log_file_path = $log_file_path;
$this->recent = $recent;
$this->_parse();
return true;
}
/**
* Parses the PHP ERROR LOG, and pushes an array with the following structure:
* array(
* "date" => {DATE},
* "severity" => {SEVERITY},
* "message" => {message},
* "stack_trace" => array(each({STACK_TRACE})) || false;
* );
* to the main array.
* !!!! IMPORTANT !!!!
* STACK TRACE IS NOT SUPPORTED AT THIS MOMENT
* TODO: IMPLEMENT STACK TRACE
* MILESTONE: NEXT_MAJOR RELEASE
*/
private function _parse() {
$contents = file_get_contents($this->log_file_path);
if(!$contents){
throw new Exception("Log file does not exist.", 2);
}
$lines = explode("\n", $contents);
if($this->recent) {
$lines = array_reverse($lines);
}
for($this->current_line = 0; $this->current_line < count($lines); $this->current_line++) {
parse_loop:
$current_line = trim($lines[$this->current_line]);
if(strlen($current_line) == 0) {
//If the line is empty throw it to the dustbin.
// SORRY, FOR THE GOTO.
// GOD PLEASE FORGIVE ME!
$this->current_line = $this->current_line + 1;
goto parse_loop;
}
if($current_line[0] != "[") {
// NOT SUPPORTING STACK TRACES AT THE MOMENT
$this->current_line = $this->current_line + 1;
goto parse_loop;
}
$dateArr = array();
preg_match('~^\[(.*?)\]~', $current_line, $dateArr);
$date = array(
"date" => explode(" ", $dateArr[1])[0],
"time" => explode(" ", $dateArr[1])[1]
);
$severity = "";
if(strpos($current_line, "PHP Warning")) {
$severity = "WARNING";
} elseif(strpos($current_line, "PHP Notice")) {
$severity = "NOTICE";
} elseif(strpos($current_line, "PHP Fatal error")) {
$severity = "FATAL";
} elseif(strpos($current_line, "PHP Parse error")) {
$severity = "SYNTAX_ERROR";
} else {
$severity = "UNIDENTIFIED_ERROR";
}
}
}
}
(好吧,代码中可能有一些不良做法:-P)
例如,一个错误可能是这个 - [28-Dec-2015 07:51:31 UTC] PHP Warning: PHP Startup: Unable to load dynamic library 'C:\xampp\php\ext\php_pspell.dll' - The specified module could not be found.
无论如何,我能够提取错误的日期和类型。但我找不到解析错误信息的方法。
所以我的问题是,如何从PHP错误日志中解析错误消息?
先谢谢,干杯!
答案 0 :(得分:2)
我有点迟了但这是我的解决方案(它也支持堆栈跟踪):
<?php
use DateTime;
use DateTimeZone;
class ErrorLog {
private $logFilePath;
/**
* ErrorLog constructor.
*
* @param string $logFilePath
*/
public function __construct(string $logFilePath) {
$this->logFilePath = $logFilePath;
}
/**
* Parses the PHP error log to an array.
*
* @return \Generator
*/
public function getParsedLogFile(): \Generator {
$parsedLogs = [];
$logFileHandle = fopen($this->logFilePath, 'rb');
while (!feof($logFileHandle)) {
$currentLine = str_replace(PHP_EOL, '', fgets($logFileHandle));
// Normal error log line starts with the date & time in []
if ('[' === $currentLine[0]) {
if (10000 === \count($parsedLogs)) {
yield $parsedLogs;
$parsedLogs = [];
}
// Get the datetime when the error occurred and convert it to berlin timezone
try {
$dateArr = [];
preg_match('~^\[(.*?)\]~', $currentLine, $dateArr);
$currentLine = str_replace($dateArr[0], '', $currentLine);
$currentLine = trim($currentLine);
$errorDateTime = new DateTime($dateArr[1]);
$errorDateTime->setTimezone(new DateTimeZone('Europe/Berlin'));
$errorDateTime = $errorDateTime->format('Y-m-d H:i:s');
} catch (\Exception $e) {
$errorDateTime = '';
}
// Get the type of the error
if (false !== strpos($currentLine, 'PHP Warning')) {
$currentLine = str_replace('PHP Warning:', '', $currentLine);
$currentLine = trim($currentLine);
$errorType = 'WARNING';
} else if (false !== strpos($currentLine, 'PHP Notice')) {
$currentLine = str_replace('PHP Notice:', '', $currentLine);
$currentLine = trim($currentLine);
$errorType = 'NOTICE';
} else if (false !== strpos($currentLine, 'PHP Fatal error')) {
$currentLine = str_replace('PHP Fatal error:', '', $currentLine);
$currentLine = trim($currentLine);
$errorType = 'FATAL';
} else if (false !== strpos($currentLine, 'PHP Parse error')) {
$currentLine = str_replace('PHP Parse error:', '', $currentLine);
$currentLine = trim($currentLine);
$errorType = 'SYNTAX';
} else if (false !== strpos($currentLine, 'PHP Exception')) {
$currentLine = str_replace('PHP Exception:', '', $currentLine);
$currentLine = trim($currentLine);
$errorType = 'EXCEPTION';
} else {
$errorType = 'UNKNOWN';
}
if (false !== strpos($currentLine, ' on line ')) {
$errorLine = explode(' on line ', $currentLine);
$errorLine = trim($errorLine[1]);
$currentLine = str_replace(' on line ' . $errorLine, '', $currentLine);
} else {
$errorLine = substr($currentLine, strrpos($currentLine, ':') + 1);
$currentLine = str_replace(':' . $errorLine, '', $currentLine);
}
$errorFile = explode(' in /', $currentLine);
$errorFile = '/' . trim($errorFile[1]);
$currentLine = str_replace(' in ' . $errorFile, '', $currentLine);
// The message of the error
$errorMessage = trim($currentLine);
$parsedLogs[] = [
'dateTime' => $errorDateTime,
'type' => $errorType,
'file' => $errorFile,
'line' => (int)$errorLine,
'message' => $errorMessage,
'stackTrace' => []
];
} // Stack trace beginning line
else if ('Stack trace:' === $currentLine) {
$stackTraceLineNumber = 0;
while (!feof($logFileHandle)) {
$currentLine = str_replace(PHP_EOL, '', fgets($logFileHandle));
// If the current line is a stack trace line
if ('#' === $currentLine[0]) {
$parsedLogsLastKey = key($parsedLogs);
$currentLine = str_replace('#' . $stackTraceLineNumber, '', $currentLine);
$parsedLogs[$parsedLogsLastKey]['stackTrace'][] = trim($currentLine);
$stackTraceLineNumber++;
} // If the current line is the last stack trace ('thrown in...')
else {
break;
}
}
}
}
yield $parsedLogs;
}
}
答案 1 :(得分:1)
您可以在PHP中定义自己的异常和错误处理程序,这样用户就不会看到错误,而是可以为他们提供一条很好的错误消息,通知他们出现了问题。
//Exception Handler
function my_exception_handler($exception) {
echo "<p>App Exception {" , $exception->getMessage(), "}</p>\n";
}
\set_exception_handler('my_exception_handler');
//Error handler
function my_error_handler($errno, $errstr, $errfile, $errline){
echo '<p style="color:darkred">App Error {'.$errno.', '.$errstr.'}</p>'."\n";
}
\set_error_handler('my_error_handler');
如果您没有使用任何名称空间,可以在set_error_ ..函数的开头删除“\”,或者您可以保留它,它可以确保您的代码在自定义名称空间中运行。
自定义函数内部的echo语句,进行DB保存,或者只是使用PHP的error_log函数来保存错误。
答案 2 :(得分:0)
好吧,奇怪的是我找到了一种方法来做到这一点,我不知道这是否是最好的方法......但重要的是它有效......
所以我做的是删除字符串的部分,比如日期和ERROR_TYPE
,一旦我解析并存储它们......最后我们将留下错误信息...
更新
*添加了返回错误的json和xml对象
所以这是我的最后一堂课:
<?php
/**
* Created by PhpStorm.
* User: toton
* Date: 2/29/2016
* Time: 8:16 AM
*/
/**
* Class error_parser
* Members:
* @param $log_file_path - private
* @param $current_line - private
* @param $recent - private
* @param $errors_array - private
*
* Methods:
* @method \__construct() - public
* @method \_parse() - private
*
* Function:
* TO parse the PHP ERROR LOG and provide a readable and a programmable array, consisting of the errors!
*/
final class error_parser
{
private $log_file_path;
private $current_line;
private $recent;
private $errors_array = array();
/**
* error_parser constructor.
* Takes in the path of the error log file of PHP ERROR LOG FILE.
* And another param for checking to get the direction to traverse the file.
* @param string $log_file_path
* @param bool $recent
*/
public function __construct($log_file_path, $recent = true)
{
$this->log_file_path = $log_file_path;
$this->recent = $recent;
$this->_parse();
return true;
}
/**
* Parses the PHP ERROR LOG, and pushes an array with the following structure:
* array(
* "date" => {DATE},
* "severity" => {SEVERITY},
* "message" => {message},
* "stack_trace" => array(each({STACK_TRACE})) || false;
* );
* to the main array.
* !!!! IMPORTANT !!!!
* STACK TRACE IS NOT SUPPORTED AT THIS MOMENT
* TODO: IMPLEMENT STACK TRACE
* MILESTONE: NEXT_MAJOR RELEASE
*/
private function _parse() {
$contents = file_get_contents($this->log_file_path);
if(!$contents){
throw new Exception("Log file does not exist.", 2);
}
$lines = explode("\n", $contents);
if($this->recent) {
$lines = array_reverse($lines);
}
for($this->current_line = 0; $this->current_line < count($lines); $this->current_line++) {
parse_loop:
$current_line = trim($lines[$this->current_line]);
if(strlen($current_line) == 0) {
//If the line is empty throw it to the dustbin.
// SORRY, FOR THE GOTO.
// GOD PLEASE FORGIVE ME!
$this->current_line = $this->current_line + 1;
goto parse_loop;
}
if($current_line[0] != "[") {
// NOT SUPPORTING STACK TRACES AT THE MOMENT
$this->current_line = $this->current_line + 1;
goto parse_loop;
}
$dateArr = array();
preg_match('~^\[(.*?)\]~', $current_line, $dateArr);
$current_line = str_replace($dateArr[0], "", $current_line);
$current_line = trim($current_line);
$date = array(
"date" => explode(" ", $dateArr[1])[0],
"time" => explode(" ", $dateArr[1])[1]
);
$severity = "";
if(strpos($current_line, "PHP Warning") !== false) {
$current_line = str_replace("PHP Warning:", "", $current_line);
$current_line = trim($current_line);
$severity = "WARNING";
} elseif(strpos($current_line, "PHP Notice") !== false) {
$current_line = str_replace("PHP Notice:", "", $current_line);
$current_line = trim($current_line);
$severity = "NOTICE";
} elseif(strpos($current_line, "PHP Fatal error") !== false) {
$current_line = str_replace("PHP Fatal error:", "", $current_line);
$current_line = trim($current_line);
$severity = "FATAL";
} elseif(strpos($current_line, "PHP Parse error") !== false) {
$current_line = str_replace("PHP Parse error:", "", $current_line);
$current_line = trim($current_line);
$severity = "SYNTAX_ERROR";
} else {
$severity = "UNIDENTIFIED_ERROR";
}
$message = $current_line;
/* Final Array *//* Add nodes while creating them */
$finalArray = array(
"date" => $date,
"severity" => $severity,
"message" => $message
);
array_push($this->errors_array, $finalArray);
}
}
/**
* Function for returning the the error things in JSON format.
* @return string <JSON STRING>
*/
public function returnJson() {
return json_encode($this->errors_array);
}
public function returnXml() {
var_dump($this->errors_array);
$xml = new SimpleXMLElement("<?xml version=\"1.0\"?><errors></errors>");
$this->_array_to_xml($xml, $this->errors_array);
// By this time we would get a xml "STRING"
return $xml->asXML();
}
/* Factory Function */
/**
* Converts an array to an xml-dcument
* @param $data
* @param $xml_data <SimpleXMLObject>
*/
private function _array_to_xml(&$xml_data, $data) {
foreach( $data as $key => $value ) {
if( is_array($value) ) {
if(is_numeric($key)){
$key = 'item' . $key;
}
$subnode = $xml_data->addChild($key);
$this->_array_to_xml($subnode, $value);
} else {
$xml_data->addChild("$key",htmlspecialchars("$value"));
}
}
}
}