Laravel: Generate a unique request ID for each request and put it into logs

Search for a command to run...

No comments yet. Be the first to comment.
In this series/category, I would share with you guys all of my exp and best practices, to build the awesome products
Hey guys, I'm super excited to share with you all about my latest work - RenderPDF.io 😎 We all know that rendering HTML to PDF is not so fun and game. You have to research a bunch of things, then try out a lot of CSS styling, and even have to instal...
Hey guys, An update after I posted the “Laravel Excel: How to append rows to an existing Excel file“ a while back. This post will give you another solution and probably a better way to export that fits every case. Problems Previously, when trying to ...

Hey guys, An easy topic for today! Make something a default (e.g., category, product, campaign, etc). Let’s go with the categories table for the examples. Let’s say we only want one category to be the default one. And find out which is the optimal wa...
Hey guys, Probably a chapter 2 of this https://sethphat.dev/why-is-wordpress-still-an-awesome-cms blog post, haha. Here was the story: I’ve been spending like 2 days figuring out which CMS I want to use for my new blog (for my new hobby lol) Let’s se...

Hey guys, Well damn, it has been a while since my last post lol, lazy & busy. But here I am, back to write useful tips for y’all 🥹 Let us do some exporting tasks for today, using Laravel Excel to export XLSX (yeah CSV is so 2010 lol). But, append mo...

Hey guys, Recently, I just bought Herd PRO (a yearly subscription). I’ve been using the basic Herd since the first release. So, my latest work required interaction with S3 storage, there are several ways to achieve that in the local env: Use Cloud (...

Hey guys,
Today I'll share with you a tip that will skyrocket your production debugging workflow 😎
We're going to generate a unique ID for each request, which we can use the request ID to search for every related log of the given request ID.
Thus, if any issue happens, we can try to find the request ID (or customers give us), and then jump immediately to the logs and do the surgery 👀.
Pretty handy, isn't it? 💪
Let's jump to the topic & the implementation.
You'll need Laravel 11 because we are going to use the Context feature.
If you haven't learned about Context, visit this page of mine:
Laravel's "context" capabilities enable you to capture, retrieve, and share information throughout requests, jobs, and commands executing within your application. This captured information is also included in logs written by your application, giving you deeper insight into the surrounding code execution history that occurred before a log entry was written and allowing you to trace execution flows throughout a distributed system.
Read more at: Laravel Context
In short terms:
So if a context presents in the request's lifecycle, Laravel will automatically log it every time we write a log using Log facade
Context is only available for each request, and queue job (Octane is covered too). There won't be any conflict or race condition.
Context::add('hello', 'world');
Log::info('Hello'); // you will see the [datetime] Hello { "contexts": { "hello": "world" } } in the log record
We'll have 2 scenarios:
If there is a X-REQUEST-ID header with a valid ID, we'll continue to use that ID
This will help trace ALL REQUESTS of a particular user's lifecycle.
I'll use X-REQUEST-ID, you can use any key that you like 🥹.
If not, we'll generate a new unique ID
Let's hit the command to create a new middleware:
php artisan make:middleware UseRequestId
And the implementation:
<?php
namespace App\Http\Middleware;
use Illuminate\Support\Str;
class UseRequestId
{
public function handle(Request $request, Closure $next): Response
{
// get existing id or generate new one
$requestId = $request->header('X-REQUEST-ID')
?? (string) Str::ulid();
// add to context
Context::add('requestId', $requestId);
return $next($request);
}
}
Since my application is an API service, I'll apply the middleware to the api group.
Open bootstrap/app.php and register it:
use App\Http\Middleware\UseRequestId;
->withMiddleware(function (Middleware $middleware) {
$middleware->prependToGroup(UseRequestId::class);
})
Using prependToGroup to ensure this middleware will be resolved at the top (global => this middleware => other middleware of api group)
Add a sample API
// api.php
Route::get('/ping', function () {
Log::info('pong');
return response()->json([]);
});
Open the log file to check (laravel.log for the basic driver or laravel-{date}.log for daily driver).
We should see something like:
[2024-04-28 13:22:59] local.INFO: Pong {"requestId":"01HWJDZ12P3CE4JDY3912YSDEA"}
To ensure it will print the same request ID, you can try to log multiple times/multiple files in the same lifecycle. For example:
Log::info('ping');
// do something
Log::info('pong');
// do something more
Log::info('end');
Result:
[2024-04-28 13:26:35] local.INFO: ping {"requestId":"01HWJE5MD8DQ24DBDN5R8TXK7K"}
[2024-04-28 13:26:35] local.INFO: pong {"requestId":"01HWJE5MD8DQ24DBDN5R8TXK7K"}
[2024-04-28 13:26:35] local.INFO: end {"requestId":"01HWJE5MD8DQ24DBDN5R8TXK7K"}
Context already covered & battle-tested from Laravel too 👀
Last part, we need to send the ID to the API consumers, so they can send the same ID or for error reporting purposes.
We'll transform a bit of the middleware handle method:
/** @var \Illuminate\Http\Response $response */
$response = $next($request);
return $response->header('X-REQUEST-ID', $requestId);
When hitting the $next, Laravel will process everything and give us the FINAL RESPONSE (after Error/Exception too).
So we can add the ID into the header (or body if you want) 😉
Now our requests are bound with request IDs. With means:
Easier for us to search for logs of a given ID 😎 Debugging is much faster & better now.
Customers/API consumers can know their "request ID", if it has any issue, they can extract the request ID and report the problem to us 👀
Additionally, you can record more data in the Context, e.g.:
Logged In User ID
URL
Timezone
etc
Well, happy coding & happy debugging guys.
Until the next blog post 😎. Thanks for reading!