Installation
composer require qandidate/stack-request-id
Note: While the package is Symfony-focused, Laravel can leverage it via middleware adaptation.
First Use Case: Request ID Middleware
Create a custom Laravel middleware to wrap the Symfony RequestId logic:
// app/Http/Middleware/RequestIdMiddleware.php
namespace App\Http\Middleware;
use Closure;
use Qandidate\Stack\RequestId;
use Qandidate\Stack\UuidRequestIdGenerator;
use Symfony\Component\HttpFoundation\Request;
class RequestIdMiddleware
{
public function handle($request, Closure $next)
{
$kernel = new \App\Http\Kernel(); // Laravel's kernel
$generator = new UuidRequestIdGenerator(1337);
$stack = new RequestId($kernel, $generator);
// Override the request with the modified one
$modifiedRequest = $stack->handle(Request::createFromGlobals());
$request->headers->set('X-Request-ID', $modifiedRequest->headers->get('X-Request-ID'));
return $next($request);
}
}
Register it in app/Http/Kernel.php:
protected $middleware = [
\App\Http\Middleware\RequestIdMiddleware::class,
];
Verify Request ID Access the ID via:
$requestId = request()->headers->get('X-Request-ID');
Request ID Generation
UuidRequestIdGenerator for UUIDs (default) or implement RequestIdGeneratorInterface for custom logic (e.g., sequential IDs).class CustomRequestIdGenerator implements RequestIdGeneratorInterface
{
public function generate(): string
{
return uniqid('CUST_', true);
}
}
Middleware Integration
RequestId in a Laravel middleware (as above) to inject the header and modify the request object.RequestId class in your kernel (as per README).Logging Integration
MonologProcessor to prepend request IDs to logs:
// config/logging.php
'processors' => [
\Qandidate\Stack\RequestId\MonologProcessor::class,
],
MonologProcessor to work with Laravel’s log channels:
use Illuminate\Support\Facades\Log;
use Monolog\Logger;
class LaravelMonologProcessor
{
public function __invoke(array $record): array
{
$record['extra']['request_id'] = request()->header('X-Request-ID');
return $record;
}
}
Database Tracing
// app/Providers/AppServiceProvider.php
public function boot()
{
\DB::listen(function ($query) {
$query->bindValue(':request_id', request()->header('X-Request-ID'), 'string');
});
}
API Responses
return response()->json([
'data' => $data,
'meta' => [
'request_id' => request()->header('X-Request-ID'),
],
]);
Symfony vs. Laravel Compatibility
Request ID Overrides
X-Request-ID header, the middleware may overwrite it.if (!$request->headers->has('X-Request-ID')) {
$modifiedRequest = $stack->handle(Request::createFromGlobals());
$request->headers->set('X-Request-ID', $modifiedRequest->headers->get('X-Request-ID'));
}
Thread Safety
UuidRequestIdGenerator uses a seed (e.g., 1337). In multi-threaded environments, this could lead to collisions.random_int() or omit the seed:
$generator = new UuidRequestIdGenerator(); // No seed = random UUIDs
Monolog Processor Timing
MonologProcessor listens to kernel.request, which may not capture all log entries (e.g., those before the request is fully processed).Monolog setup) or listen to monolog.record:
// config/logging.php
'processors' => [
function ($record) {
$record['extra']['request_id'] = request()->header('X-Request-ID') ?? 'N/A';
return $record;
},
],
Consistent Naming
X-Request-ID as the header name to align with industry standards (e.g., AWS X-Ray, Datadog).Testing
$request = Request::create('/test', 'GET', [], [], [], ['HTTP_X_REQUEST_ID' => 'TEST_123']);
$response = $this->app->handle($request);
$this->assertEquals('TEST_123', $response->headers->get('X-Request-ID'));
Performance
uniqid()).Extending Functionality
RequestId class to support additional headers (e.g., X-Request-Trace).\DB::table('request_logs')->insert([
'id' => request()->header('X-Request-ID'),
'created_at' => now(),
]);
Debugging
if (!$request->header('X-Request-ID')) {
\Log::warning('Request ID missing for URL: ' . $request->url());
}
How can I help you explore Laravel packages today?