AppExceptionHandler.php 2.88 KB
<?php

declare(strict_types=1);

namespace Meibuyu\Micro\Exceptions\Handler;

use Exception;
use Hyperf\Amqp\Producer;
use Hyperf\Contract\ConfigInterface;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\Logger\LoggerFactory;
use Meibuyu\Micro\Amqp\Producer\ExceptionLogProducer;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerInterface;
use Throwable;

class AppExceptionHandler extends ExceptionHandler
{

    /**
     * @var ContainerInterface
     */
    protected $container;

    /**
     * @var StdoutLoggerInterface
     */
    protected $stdoutLogger;

    /**
     * @var LoggerInterface
     */
    protected $logger;

    /**
     * @var ConfigInterface
     */
    protected $config;

    /**
     * @var RequestInterface
     */
    protected $request;

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
        $this->stdoutLogger = $container->get(StdoutLoggerInterface::class);
        $this->logger = $container->get(LoggerFactory::class)->get('Uncaught Exception');
        $this->config = $container->get(ConfigInterface::class);
        $this->request = $container->get(RequestInterface::class);
    }

    public function handle(Throwable $throwable, ResponseInterface $response)
    {
        // 捕获所有未捕获的异常
        $this->stopPropagation();
        $api = sprintf('%s(%s)', $this->request->getUri(), $this->request->getMethod());
        $exceptionClass = get_class($throwable);
        $message = $throwable->getMessage();
        $line = $throwable->getLine();
        $file = $throwable->getFile();
        $code = $throwable->getCode();
        $trace = $throwable->getTraceAsString();
        $data = [
            'api' => $api,
            'server' => $this->config->get('app_name'),
            'file' => $file,
            'line' => $line,
            'message' => $message,
            'trace' => $trace,
            'code' => $code,
            'created_at' => now(),
        ];
        try {
            $exceptionLogProducer = new ExceptionLogProducer($data);
            $producer = $this->container->get(Producer::class);
            $producer->produce($exceptionLogProducer);
        } catch (Exception $e) {
            put_log('异常日志失败; ' . $e->getMessage(), 'ExceptionLogProducer.log');
        }
        $msg = sprintf('%s: %s(%s) in %s:%s', $exceptionClass, $message, $code, $file, $line);
        $error = sprintf("API: %s\n%s\nStack trace:\n%s", $api, $msg, $trace);
        $this->logger->error($error);
        $this->stdoutLogger->error($error);
        return $response->withStatus(500)->withBody(new SwooleStream($msg));
    }

    public function isValid(Throwable $throwable): bool
    {
        return true;
    }
}