如何在 Zend Expressive 中将 PHP 错误记录到 PHP 的错误文件中?
How to log PHP errors into PHP's error file in Zend Expressive?
使用 Zend Expressive 2.0.5,我想将 PHP 错误记录到 PHP 的错误日志文件本身,我经历了 https://docs.zendframework.com/zend-expressive/features/error-handling/#handling-exceptions-and-errors
我也看到了这个post:Zend expressive - php error reporting。这为我清除了很多东西,但仍然没有解决提出的问题。
我所做的事情:定义了我自己的 ErrorHandlerFactory
,我将两个监听器附加到 Zend-stratigility's ErrorHandler
First Listener 使用 Zend Log 登录我的应用程序日志
文件。(只是觉得我的错误会很好
application.log 也是。)
在第二个监听器中,我想登录到PHP的错误日志文件,所以我使用了php中的error_log()
方法。
问题:
error_log() 打印日志的方式与 php 的错误处理程序打印日志的方式不同。我的意思是:
当 php 的错误处理程序打印错误时,它看起来像这样:
[08-Feb-2018 08:22:51 US/Central] PHP Warning: array_push() expects
at least 2 parameters, 1 given in
C:\webserver\webroot\myapi\src\App\src\Action\PageAction.php on
line 38
当我使用 error_log()
打印日志时,它看起来像这样:
[08-Feb-2018 09:03:49 US/Central] array_push() expects at least 2
parameters, 1 given in
C:\webserver\webroot\myapi\src\App\src\Action\PageAction.php on
line 38
我在这里缺少的是 PHP 的错误类型:PHP 警告,这是错误代码吗?我得到的错误代码是一个整数,我该如何解析该代码?我应该将错误代码映射到日志中出现的 PHP 错误常量,例如:WARNING、NOTICE 等 ,我什至可以这样做,但问题是:当 php 的错误处理程序打印 a WARNING 和 Fatal error 日志时,我得到了相同的错误代码 0。
这样在PHP的错误日志文件中记录错误是否正确?应该
我负责 PHP 的错误处理程序?错误处理程序可以做很多事情,例如:记录一些错误的错误消息,但对于另一个错误也记录堆栈跟踪。 如果这不对,我还能如何将错误发送到 PHP 的 error_handler?
据我了解:
My own Error Handler prevents users to look for exceptions and stack
traces but rather returns a
generic message. This also means that the Error Handler consumes the error and doesn't throw it further outside, i.e. will not throw it
to the PHP's error handler.
回答问题1:
我几乎可以模拟 PHP 错误处理程序记录 PHP 错误的方式。
我做过的事:
- 完成了 docs and this SO 问题。使用这些,我能够将侦听器附加到 Zend-stratigility 的 ErrorHandler
- 浏览了 PHP 的 Error Constants and set_error_handler(),这给了我一些关于如何找出发生哪种类型的错误或异常的想法。
下面是我的 ErrorHandlerFactory 的代码,我在其中附加了侦听器。
<?php
// TODO: PHP 7.0.8 is giving strict erros eben if this directive is not enabled. And that too, it should be enabled per file from my understanding.
//declare(strict_types = 1);
namespace App\Factories;
use Interop\Container\ContainerInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Zend\Log\Logger as ZendLogger;
use Throwable;
use Zend\Diactoros\Response;
use Zend\Expressive\Middleware\ErrorResponseGenerator;
use Zend\Stratigility\Middleware\ErrorHandler;
class ErrorHandlerFactory
{
/**
* @param ContainerInterface $container
* @return ErrorHandler
* @throws \Psr\Container\ContainerExceptionInterface
* @throws \Psr\Container\NotFoundExceptionInterface
*/
public function __invoke(ContainerInterface $container)
{
$generator = $container->has(ErrorResponseGenerator::class)
? $container->get(ErrorResponseGenerator::class)
: null;
$errorHandler = new ErrorHandler(new Response(), $generator);
// attaching a listener for logging into application's log file.
if ($container->has(ZendLogger::class)) {
/** @var ZendLogger $logger */
$logger = $container->get(ZendLogger::class);
$errorHandler->attachListener(function (
Throwable $throwable,
RequestInterface $request,
ResponseInterface $response
) use ($logger) {
$logger->err(NULL, [
'method' => $request->getMethod(),
'uri' => (string) $request->getUri(),
'message' => $throwable->getMessage(),
'file' => $throwable->getFile(),
'line' => $throwable->getLine(),
]);
});
}
// Attaching second listener for logging the errors into the PHP's error log
$errorHandler->attachListener(function (
Throwable $throwable,
RequestInterface $request,
ResponseInterface $response
) {
// Default Error type, when PHP Error occurs.
$errorType = sprintf("Fatal error: Uncaught %s", get_class($throwable));
if (get_class($throwable) === "ErrorException") {
// this is an Exception
/** @noinspection PhpUndefinedMethodInspection */
$severity = $throwable->getSeverity();
switch($severity) {
case E_ERROR:
case E_USER_ERROR:
$errorType = 'Fatal error';
break;
case E_USER_WARNING:
case E_WARNING:
$errorType = 'Warning';
break;
case E_USER_NOTICE:
case E_NOTICE:
case E_STRICT:
$errorType = 'Notice';
break;
case E_RECOVERABLE_ERROR:
$errorType = 'Catchable fatal error';
break;
case E_USER_DEPRECATED:
case E_DEPRECATED:
$errorType = "Deprecated";
break;
default:
$errorType = 'Unknown error';
}
error_log(sprintf("PHP %s: %s in %s on line %d", $errorType, $throwable->getMessage(), $throwable->getFile(), $throwable->getLine()), 0);
}
else {
// this is an Error.
error_log(sprintf("PHP %s: %s in %s on line %d \nStack trace:\n%s", $errorType, $throwable->getMessage(), $throwable->getFile(), $throwable->getLine(), $throwable->getTraceAsString()), 0);
}
});
return $errorHandler;
}
}
除此之外,还需要将这个Factory添加到依赖中。
在文件中:
dependencies.global.php
,在factories
数组中:
替换
Zend\Stratigility\Middleware\ErrorHandler::class => Container\ErrorHandlerFactory::class,
与
Zend\Stratigility\Middleware\ErrorHandler::class => \App\Factories\ErrorHandlerFactory::class
这几乎可以模拟 php 错误处理程序的日志记录行为。
回答问题 2:
我认为这样做很好,因为 PHP 本身提供 set_error_handler()
,无论如何我们必须自己处理错误,而不是将其传递给 PHP错误处理程序。如果我们的 ErrorHandler(listener) 可以复制消息并使用 error_log()
登录到 PHP 的错误日志,那么就可以了。
使用 Zend Expressive 2.0.5,我想将 PHP 错误记录到 PHP 的错误日志文件本身,我经历了 https://docs.zendframework.com/zend-expressive/features/error-handling/#handling-exceptions-and-errors
我也看到了这个post:Zend expressive - php error reporting。这为我清除了很多东西,但仍然没有解决提出的问题。
我所做的事情:定义了我自己的 ErrorHandlerFactory
,我将两个监听器附加到 Zend-stratigility's ErrorHandler
First Listener 使用 Zend Log 登录我的应用程序日志 文件。(只是觉得我的错误会很好 application.log 也是。)
在第二个监听器中,我想登录到PHP的错误日志文件,所以我使用了php中的
error_log()
方法。
问题:
error_log() 打印日志的方式与 php 的错误处理程序打印日志的方式不同。我的意思是:
当 php 的错误处理程序打印错误时,它看起来像这样:
[08-Feb-2018 08:22:51 US/Central] PHP Warning: array_push() expects at least 2 parameters, 1 given in C:\webserver\webroot\myapi\src\App\src\Action\PageAction.php on line 38
当我使用
error_log()
打印日志时,它看起来像这样:[08-Feb-2018 09:03:49 US/Central] array_push() expects at least 2 parameters, 1 given in C:\webserver\webroot\myapi\src\App\src\Action\PageAction.php on line 38
我在这里缺少的是 PHP 的错误类型:PHP 警告,这是错误代码吗?我得到的错误代码是一个整数,我该如何解析该代码?我应该将错误代码映射到日志中出现的 PHP 错误常量,例如:WARNING、NOTICE 等 ,我什至可以这样做,但问题是:当 php 的错误处理程序打印 a WARNING 和 Fatal error 日志时,我得到了相同的错误代码 0。
这样在PHP的错误日志文件中记录错误是否正确?应该 我负责 PHP 的错误处理程序?错误处理程序可以做很多事情,例如:记录一些错误的错误消息,但对于另一个错误也记录堆栈跟踪。 如果这不对,我还能如何将错误发送到 PHP 的 error_handler?
据我了解:
My own Error Handler prevents users to look for exceptions and stack traces but rather returns a generic message. This also means that the Error Handler consumes the error and doesn't throw it further outside, i.e. will not throw it to the PHP's error handler.
回答问题1:
我几乎可以模拟 PHP 错误处理程序记录 PHP 错误的方式。 我做过的事:
- 完成了 docs and this SO 问题。使用这些,我能够将侦听器附加到 Zend-stratigility 的 ErrorHandler
- 浏览了 PHP 的 Error Constants and set_error_handler(),这给了我一些关于如何找出发生哪种类型的错误或异常的想法。
下面是我的 ErrorHandlerFactory 的代码,我在其中附加了侦听器。
<?php
// TODO: PHP 7.0.8 is giving strict erros eben if this directive is not enabled. And that too, it should be enabled per file from my understanding.
//declare(strict_types = 1);
namespace App\Factories;
use Interop\Container\ContainerInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Zend\Log\Logger as ZendLogger;
use Throwable;
use Zend\Diactoros\Response;
use Zend\Expressive\Middleware\ErrorResponseGenerator;
use Zend\Stratigility\Middleware\ErrorHandler;
class ErrorHandlerFactory
{
/**
* @param ContainerInterface $container
* @return ErrorHandler
* @throws \Psr\Container\ContainerExceptionInterface
* @throws \Psr\Container\NotFoundExceptionInterface
*/
public function __invoke(ContainerInterface $container)
{
$generator = $container->has(ErrorResponseGenerator::class)
? $container->get(ErrorResponseGenerator::class)
: null;
$errorHandler = new ErrorHandler(new Response(), $generator);
// attaching a listener for logging into application's log file.
if ($container->has(ZendLogger::class)) {
/** @var ZendLogger $logger */
$logger = $container->get(ZendLogger::class);
$errorHandler->attachListener(function (
Throwable $throwable,
RequestInterface $request,
ResponseInterface $response
) use ($logger) {
$logger->err(NULL, [
'method' => $request->getMethod(),
'uri' => (string) $request->getUri(),
'message' => $throwable->getMessage(),
'file' => $throwable->getFile(),
'line' => $throwable->getLine(),
]);
});
}
// Attaching second listener for logging the errors into the PHP's error log
$errorHandler->attachListener(function (
Throwable $throwable,
RequestInterface $request,
ResponseInterface $response
) {
// Default Error type, when PHP Error occurs.
$errorType = sprintf("Fatal error: Uncaught %s", get_class($throwable));
if (get_class($throwable) === "ErrorException") {
// this is an Exception
/** @noinspection PhpUndefinedMethodInspection */
$severity = $throwable->getSeverity();
switch($severity) {
case E_ERROR:
case E_USER_ERROR:
$errorType = 'Fatal error';
break;
case E_USER_WARNING:
case E_WARNING:
$errorType = 'Warning';
break;
case E_USER_NOTICE:
case E_NOTICE:
case E_STRICT:
$errorType = 'Notice';
break;
case E_RECOVERABLE_ERROR:
$errorType = 'Catchable fatal error';
break;
case E_USER_DEPRECATED:
case E_DEPRECATED:
$errorType = "Deprecated";
break;
default:
$errorType = 'Unknown error';
}
error_log(sprintf("PHP %s: %s in %s on line %d", $errorType, $throwable->getMessage(), $throwable->getFile(), $throwable->getLine()), 0);
}
else {
// this is an Error.
error_log(sprintf("PHP %s: %s in %s on line %d \nStack trace:\n%s", $errorType, $throwable->getMessage(), $throwable->getFile(), $throwable->getLine(), $throwable->getTraceAsString()), 0);
}
});
return $errorHandler;
}
}
除此之外,还需要将这个Factory添加到依赖中。
在文件中:
dependencies.global.php
,在factories
数组中:
替换
Zend\Stratigility\Middleware\ErrorHandler::class => Container\ErrorHandlerFactory::class,
与
Zend\Stratigility\Middleware\ErrorHandler::class => \App\Factories\ErrorHandlerFactory::class
这几乎可以模拟 php 错误处理程序的日志记录行为。
回答问题 2:
我认为这样做很好,因为 PHP 本身提供 set_error_handler()
,无论如何我们必须自己处理错误,而不是将其传递给 PHP错误处理程序。如果我们的 ErrorHandler(listener) 可以复制消息并使用 error_log()
登录到 PHP 的错误日志,那么就可以了。