<?php
declare(strict_types=1);
/*
* This file is part of the SynergyBot project.
*
* For the full copyright and license information,
* please read the LICENSE.md file that was distributed with this source code.
*
* The SymfonyBot project - inspiring people to chat!
*
* Copyright (c) 2022.
*/
namespace App\ApiBundle\EventListeners;
use App\ApiBundle\Exception\ApiException;
use App\ApiBundle\Response\ApiResponse;
use App\ClickHouseBundle\Services\ClickHouseService;
use App\CoreBundle\Util\Rand;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
class KernelListener implements LoggerAwareInterface
{
use LoggerAwareTrait;
protected bool $isApiClient = false;
protected bool $isApiPartner = false;
protected bool $isApi = false;
protected ?\Throwable $exception = null;
public function __construct(
private readonly ClickHouseService $clickhouse)
{
}
public function onKernelRequest(RequestEvent $event)
{
$request = $event->getRequest();
$route = $request->get('_route');
$this->isApiClient = null !== $route && (false !== mb_stripos((string) $route, 'api_client'));
$this->isApiPartner = null !== $route && (false !== mb_stripos((string) $route, 'api_partner'));
$this->isApi = $this->isApiClient || $this->isApiPartner;
if (!$this->isApi) {
return;
}
$request->request->add([
'_request_id' => Rand::getRandomSHA1(),
]);
}
public function onKernelResponse(ResponseEvent $event)
{
if (!$this->isApi) {
return;
}
$request = $event->getRequest();
$requestId = $request->request->get('_request_id');
$request->request->remove('_request_id');
$requestParams = implode(\PHP_EOL, [
'GET: '.http_build_query($request->query->all()),
'POST: '.http_build_query($request->request->all()),
'DATA: '.$request->getContent(),
]);
$response = $event->getResponse();
$currentAt = new \DateTime();
$exceptionText = $this->exception ? $this->exception->__toString() : '';
$tableName = $this->isApiClient ? 'client_api_logs' : 'partner_api_logs';
$this->clickhouse->insert($tableName, [
[
$requestId,
$requestId,
$currentAt->format('Y-m-d'),
$currentAt->format('Y-m-d H:i:s'),
$request->getClientIp(),
$request->getUri(),
$request->getMethod(),
$response->getStatusCode(),
$requestParams,
$response->getContent() ?: '',
$exceptionText,
],
], [
'request_id',
'queue_id',
'created_date',
'created_at',
'ip_addr',
'uri',
'method',
'status',
'request',
'response',
'exception',
]);
$event->getResponse()->setStatusCode(200);
}
public function onKernelException(ExceptionEvent $event)
{
if (!$this->isApi) {
return;
}
$this->exception = $event->getThrowable();
$status = 0;
$message = 'Try again later.';
$httpCode = Response::HTTP_INTERNAL_SERVER_ERROR;
if ($this->exception instanceof ApiException) {
$status = $this->exception->getCode();
$message = $this->exception->getMessage();
$httpCode = $this->exception->getStatusCode();
$this->logger->notice(
'Api exception',
[
'code' => $status,
'message' => $message,
'http_code' => $httpCode,
'exception' => $this->exception,
]
);
} else {
$this->logger->critical(
'Kernel exception',
[
'code' => $this->exception->getCode(),
'message' => $this->exception->getMessage(),
'http_code' => $httpCode,
'exception' => $this->exception,
]
);
}
$event->setResponse(new ApiResponse(
['success' => false],
[
'code' => $status,
'message' => $message,
],
$httpCode
));
$event->getResponse()->setStatusCode($httpCode);
}
}