Adding a unique request id using middleware


In our event-sourced applications built using Broadway we have a full log of all events that happened in our application. We also log all commands using monolog. We did however miss some traceability between the commands and the actual events: we logged both the commands and the events but could not determine which commands led to which events.

In order to achieve this we decided to add a request id to each of our requests. We can now add the request id to the command log and add the request id to the metadata of our DomainMessages using the MetadataEnricherInterface

There are some webservers that provide a solution for this problem. One of these is an nginx module, and there is also an apache module. However, we wanted our solution to be webserver-independent. The result: we decided to tackle our problem the way we know best; in PHP.

We created a middleware for Symfony that adds a request id header to the request's headers. Check out the source code. You can add it to your application by changing a few lines of code in your app.php file. Just have a look at these 'before' and 'after' codeblocks:

Before:

use Symfony\Component\HttpFoundation\Request;

$kernel = new AppKernel($env, $debug);
$kernel->loadClassCache();

$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);

After:

use Qandidate\Stack\RequestId;
use Qandidate\Stack\UuidRequestIdGenerator;
use Symfony\Component\HttpFoundation\Request;

$kernel = new AppKernel($env, $debug);

$generator = new UuidRequestIdGenerator();
$stack = new RequestId($kernel, $generator);

$kernel->loadClassCache();
$request = Request::createFromGlobals();
$response = $stack->handle($request);
$response->send();
$kernel->terminate($request, $response);

Adding the request id to your monolog

It's great that each request can now be identified, but we still have to log the actual request id in our logs. We created a Monolog Processor that does just that. Add the following to your services.xml file and everything works out of the box:

<service id="qandidate.stack.request_id.monolog_processor" class="Qandidate\Stack\RequestId\MonologProcessor">
    <tag name="kernel.event_listener" event="kernel.request" method="onKernelRequest" priority="255" />
    <tag name="monolog.processor" />
</service>

Note that it works out of the box due to MonologBundle - be sure you have it enabled!

Combining with Nginx or Apache

If you prefer to use the nginx or apache modules in your production environment adding stack-request-id still has its benefits. The middleware only adds a request id if it isn't yet available, and you can customize the header that should be used to store the request id in. If you use one of the nginx/apache modules the request id from that module will be used, else we will generate one for you. This way you don't have to bother with enabling the modules in your development environment.

Happy logging!