<?php
namespace App\EventListener;
use App\Entity\DailyErrorLog;
use App\Service\Logger;
use App\Service\Slack;
use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Console\ConsoleEvents;
use Symfony\Component\Console\Event\ConsoleErrorEvent;
use Symfony\Component\Console\Event\ConsoleTerminateEvent;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Contracts\EventDispatcher\Event;
class FatalErrorListener implements EventSubscriberInterface
{
public function __construct(
private EntityManagerInterface $entityManager,
private Slack $slackService,
private Logger $logger,
)
{
}
public static function getSubscribedEvents(): array
{
// Specify the event and method to be called
return [
KernelEvents::EXCEPTION => 'onKernelException',
ConsoleEvents::ERROR => 'onConsoleError',
];
}
public function onKernelException(ExceptionEvent $event): void
{
$request = $event->getRequest();
$date = new DateTime();
$errorInfo = $this->getErrorInfo($event);
$logContent = $errorInfo['logContent']
. 'Request: ==========================================================' . PHP_EOL
. 'Method: ' . $request->getMethod() . PHP_EOL
. 'URI: ' . $request->getRequestUri() . PHP_EOL
. 'ClientIP: ' . $request->getClientIp() . PHP_EOL
. 'Host: ' . $request->getHost() . PHP_EOL
. 'Query-JSON: ' . json_encode($request->query->all()) . PHP_EOL
. 'Headers-JSON: ' . json_encode($request->headers->all()) . PHP_EOL
. 'Body-JSON: ' . json_encode($request->request->all()) . PHP_EOL
. 'EndOfError: ===============================================' . PHP_EOL;
$this->writeLog($date, $logContent, $errorInfo['sendMessage']);
}
public function onConsoleError(ConsoleErrorEvent $event): void
{
$input = $event->getInput();
$date = new DateTime();
$errorInfo = $this->getErrorInfo($event);
$logContent = $errorInfo['logContent'];
$command = $event->getCommand();
$logContent .= 'Command: ==========================================================' . PHP_EOL;
if (null != $command) {
$logContent .= 'Command Name: ' . $command->getName() . PHP_EOL
. 'Options-JSON: ' . json_encode($input->getOptions()) . PHP_EOL
. 'Arguments-JSON: ' . json_encode($input->getArguments()) . PHP_EOL;
} else {
$logContent .= 'Command not found.' . PHP_EOL;
}
$logContent .= 'EndOfError: ===============================================' . PHP_EOL . PHP_EOL;
$this->writeLog($date, $logContent, $errorInfo['sendMessage']);
}
private function writeLog(DateTime $date, string $logContent, bool $sendMessage): void
{
$file = "error_{$date->format('d')}_" . '.log';
$this->logger->writeLog($logContent, $file);
if ($sendMessage) {
$this->slackService->sendMessage($logContent);
}
}
private function getErrorInfo(Event $event): array
{
$exception = method_exists($event, 'getError') ? $event->getError()
: (method_exists($event, 'getThrowable') ? $event->getThrowable() : null);
$date = new DateTime();
$environment = getenv('APP_ENV');
$result = [
'sendMessage' => false,
'logContent' => PHP_EOL
. '================== ' . $date->format('Y-m-d H:i:s') . '======================' . PHP_EOL
. "Environment: {$environment}" . PHP_EOL,
];
if (null === $exception) {
$result['logContent'] .= "class: " . get_class($event) . PHP_EOL
. "class: " . json_encode(get_class_methods($event)) . PHP_EOL
. "No error information available!" . PHP_EOL;
return $result;
}
$result['logContent'] .= 'TimeZone: ' . date_default_timezone_get() . PHP_EOL
. 'Error: ============================================================' . PHP_EOL
. 'Message: ' . $exception->getMessage() . PHP_EOL
. 'File: ' . $exception->getFile() . PHP_EOL
. 'Line: ' . $exception->getLine() . PHP_EOL
. 'Code: ' . $exception->getCode() . PHP_EOL
. 'Trace-JSON: ' . json_encode($exception->getTrace()) . PHP_EOL;
$result['sendMessage'] = $this->addError($exception, $date);
return $result;
}
private function addError($exception, DateTime $dateTime): bool
{
$repository = $this->entityManager->getRepository(DailyErrorLog::class);
$loggedError = $repository->findBy(
[
'file' => $exception->getFile(),
'code' => $exception->getCode(),
'line' => $exception->getLine(),
'appear' => $dateTime,
]
);
if (!$loggedError) {
$loggedError = new DailyErrorLog();
$loggedError->setCode($exception->getCode());
$loggedError->setFile($exception->getFile());
$loggedError->setLine($exception->getLine());
$loggedError->setAppear($dateTime);
$loggedError->setMessage($exception->getMessage());
$this->entityManager->persist($loggedError);
$this->entityManager->flush();
return true;
}
return false;
}
}