feat: shortened and filename-safe request ids
This commit is contained in:
@@ -1197,8 +1197,8 @@ Persists INFO+ level logs to `LogEntry` entity. Replaces message placeholders wi
|
|||||||
When debug mode is enabled, API requests/responses are dumped to `var/bpn/`:
|
When debug mode is enabled, API requests/responses are dumped to `var/bpn/`:
|
||||||
|
|
||||||
```
|
```
|
||||||
{request_id}_1_request.xml
|
r-00m8z7k58a-8k1pvd9q_1_request.xml
|
||||||
{request_id}_1_response.xml
|
r-00m8z7k58a-8k1pvd9q_1_response.xml
|
||||||
```
|
```
|
||||||
|
|
||||||
View via Admin Panel: Admin → Log → XML Dumps action.
|
View via Admin Panel: Admin → Log → XML Dumps action.
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ use App\BusProNet\Model\Travel;
|
|||||||
use App\BusProNet\XmlParser\ApiResponseParser;
|
use App\BusProNet\XmlParser\ApiResponseParser;
|
||||||
use App\Form\Model\BookingDto;
|
use App\Form\Model\BookingDto;
|
||||||
use App\Form\Model\RegistrationDto;
|
use App\Form\Model\RegistrationDto;
|
||||||
|
use App\Service\RequestIdGenerator;
|
||||||
use League\Flysystem\FilesystemException;
|
use League\Flysystem\FilesystemException;
|
||||||
use League\Flysystem\FilesystemOperator;
|
use League\Flysystem\FilesystemOperator;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
@@ -58,6 +59,7 @@ class ApiClient
|
|||||||
private readonly LoggerInterface $logger,
|
private readonly LoggerInterface $logger,
|
||||||
private readonly BookingDataProcessor $bookingDataProcessor,
|
private readonly BookingDataProcessor $bookingDataProcessor,
|
||||||
private readonly RequestStack $requestStack,
|
private readonly RequestStack $requestStack,
|
||||||
|
private readonly RequestIdGenerator $requestIdGenerator,
|
||||||
array $options,
|
array $options,
|
||||||
) {
|
) {
|
||||||
$this->config = $this->resolveOptions($options);
|
$this->config = $this->resolveOptions($options);
|
||||||
@@ -762,7 +764,7 @@ class ApiClient
|
|||||||
private function getRequestId(): string
|
private function getRequestId(): string
|
||||||
{
|
{
|
||||||
$baseId = $this->requestStack->getMainRequest()?->attributes->get('request_id')
|
$baseId = $this->requestStack->getMainRequest()?->attributes->get('request_id')
|
||||||
?? date(DATE_ATOM);
|
?? $this->requestIdGenerator->generateBaseId();
|
||||||
|
|
||||||
return $baseId.'_'.++$this->requestCounter;
|
return $baseId.'_'.++$this->requestCounter;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App\EventListener;
|
namespace App\EventListener;
|
||||||
|
|
||||||
|
use App\Service\RequestIdGenerator;
|
||||||
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
|
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
|
||||||
use Symfony\Component\HttpKernel\Event\RequestEvent;
|
use Symfony\Component\HttpKernel\Event\RequestEvent;
|
||||||
use Symfony\Component\HttpKernel\KernelEvents;
|
use Symfony\Component\HttpKernel\KernelEvents;
|
||||||
@@ -11,13 +12,18 @@ use Symfony\Component\HttpKernel\KernelEvents;
|
|||||||
#[AsEventListener(event: KernelEvents::REQUEST, priority: 255)]
|
#[AsEventListener(event: KernelEvents::REQUEST, priority: 255)]
|
||||||
class RequestIdListener
|
class RequestIdListener
|
||||||
{
|
{
|
||||||
|
public function __construct(
|
||||||
|
private readonly RequestIdGenerator $requestIdGenerator,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
public function __invoke(RequestEvent $event): void
|
public function __invoke(RequestEvent $event): void
|
||||||
{
|
{
|
||||||
if (false === $event->isMainRequest()) {
|
if (false === $event->isMainRequest()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$requestId = date(DATE_ATOM).uniqid();
|
$requestId = $this->requestIdGenerator->generateBaseId();
|
||||||
$event->getRequest()->attributes->set('request_id', $requestId);
|
$event->getRequest()->attributes->set('request_id', $requestId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Service;
|
||||||
|
|
||||||
|
class RequestIdGenerator
|
||||||
|
{
|
||||||
|
private const TIMESTAMP_LENGTH = 10;
|
||||||
|
private const RANDOM_LENGTH = 8;
|
||||||
|
|
||||||
|
public function generateBaseId(): string
|
||||||
|
{
|
||||||
|
$timestampMs = (int) floor(microtime(true) * 1000);
|
||||||
|
$timestampPart = str_pad(base_convert((string) $timestampMs, 10, 36), self::TIMESTAMP_LENGTH, '0', STR_PAD_LEFT);
|
||||||
|
|
||||||
|
$maxRandomValue = (36 ** self::RANDOM_LENGTH) - 1;
|
||||||
|
$randomPart = str_pad(base_convert((string) random_int(0, $maxRandomValue), 10, 36), self::RANDOM_LENGTH, '0', STR_PAD_LEFT);
|
||||||
|
|
||||||
|
return sprintf('r-%s-%s', $timestampPart, $randomPart);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ use App\BusProNet\ApiClient;
|
|||||||
use App\BusProNet\DataProcessor\BookingDataProcessor;
|
use App\BusProNet\DataProcessor\BookingDataProcessor;
|
||||||
use App\BusProNet\Exception\ImmediateConnectionCloseException;
|
use App\BusProNet\Exception\ImmediateConnectionCloseException;
|
||||||
use App\BusProNet\XmlParser\ApiResponseParser;
|
use App\BusProNet\XmlParser\ApiResponseParser;
|
||||||
|
use App\Service\RequestIdGenerator;
|
||||||
use League\Flysystem\FilesystemOperator;
|
use League\Flysystem\FilesystemOperator;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
@@ -27,6 +28,7 @@ class ApiClientReceiveTest extends TestCase
|
|||||||
$this->createMock(LoggerInterface::class),
|
$this->createMock(LoggerInterface::class),
|
||||||
$this->createMock(BookingDataProcessor::class),
|
$this->createMock(BookingDataProcessor::class),
|
||||||
new RequestStack(),
|
new RequestStack(),
|
||||||
|
new RequestIdGenerator(),
|
||||||
[
|
[
|
||||||
'bpn_username' => 'test',
|
'bpn_username' => 'test',
|
||||||
'bpn_password' => 'test',
|
'bpn_password' => 'test',
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace App\Tests\Service;
|
||||||
|
|
||||||
|
use App\Service\RequestIdGenerator;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class RequestIdGeneratorTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testGeneratesFilenameSafeRequestId(): void
|
||||||
|
{
|
||||||
|
$generator = new RequestIdGenerator();
|
||||||
|
|
||||||
|
$requestId = $generator->generateBaseId();
|
||||||
|
|
||||||
|
self::assertMatchesRegularExpression('/^r-[0-9a-z]{10}-[0-9a-z]{8}$/', $requestId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGeneratedIdsAreSortableByTime(): void
|
||||||
|
{
|
||||||
|
$generator = new RequestIdGenerator();
|
||||||
|
|
||||||
|
$first = $generator->generateBaseId();
|
||||||
|
usleep(2_000);
|
||||||
|
$second = $generator->generateBaseId();
|
||||||
|
usleep(2_000);
|
||||||
|
$third = $generator->generateBaseId();
|
||||||
|
|
||||||
|
self::assertTrue($first < $second);
|
||||||
|
self::assertTrue($second < $third);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user