Laravel’de Observability: Request ID ile Uçtan Uca İzlenebilirlik (Log + Response + Job)
Her isteğe tekil bir Request ID ekleyip loglarda, response’ta ve queue job’larında uçtan uca izlenebilirlik kurun.
Üretimde “Bir kullanıcı şunu yaptı, sonra şu hata oldu” sorusunu yanıtlamak çoğu zaman log aramak değil, aynı akışı uçtan uca takip edebilmek meselesi. Laravel’de bunun en pratik yolu: her HTTP isteğine bir Request ID (Correlation ID) üretmek ve bu kimliği loglara, response’a ve arka plan job’larına taşımak.
Hedef: Tek ID ile tüm akışı takip etmek
- Kullanıcı isteği → Controller/Service logları
- Harici API çağrıları (varsa) → isteğe bağlı header
- Queue job’ları → aynı ID ile log
- İstemciye dönüş → response header/body içinde ID
Böylece Sentry/ELK/CloudWatch gibi sistemlerde tek bir ID ile arama yaparak dakikalar kazanırsınız.
1) Middleware ile Request ID üret
app/Http/Middleware/RequestId.php:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
class RequestId
{
public function handle(Request $request, Closure $next)
{
// İstemci zaten gönderiyorsa onu kullan (örn. API gateway)
$id = $request->header('X-Request-Id') ?? (string) Str::uuid();
// Request yaşam döngüsünde erişmek için
app()->instance('request_id', $id);
$response = $next($request);
// İstemciye geri dön
$response->headers->set('X-Request-Id', $id);
return $response;
}
}
app/Http/Kernel.php içine (API grubuna eklemek genelde ideal):
protected $middlewareGroups = [
'api' => [
// ...
\App\Http\Middleware\RequestId::class,
],
];
2) Loglara otomatik ekle (Monolog “processor” mantığıyla)
Laravel 10/11’de log context eklemek için en temiz yöntemlerden biri global context.
App\Providers\AppServiceProvider.php:
use Illuminate\Support\Facades\Log;
public function boot(): void
{
Log::shareContext([
'request_id' => fn () => app()->bound('request_id') ? app('request_id') : null,
]);
}
Artık herhangi bir yerde:
logger()->info('Order created', ['order_id' => $order->id]);
çıktı olarak request_id de loga düşer (formatınıza bağlı).
3) Exception’larda da Request ID göster
Üretimde kullanıcıya “500” dönmek yerine, destek ekibinin arayacağı bir referans verin.
Örnek (API JSON response):
return response()->json([
'message' => 'Beklenmeyen bir hata oluştu.',
'request_id' => app()->bound('request_id') ? app('request_id') : null,
], 500);
Bunu app/Exceptions/Handler.php içinde render() ile merkezi hale getirebilirsiniz.
4) Queue job’larında aynı Request ID’yi koru
HTTP isteği ile başlayan bir işlem job’a devredildiğinde iz sürme genelde kopar. Çözüm: job dispatch ederken request_id’yi payload’a koymak.
SendInvoiceJob::dispatch($invoiceId, app('request_id'));
Job tarafı:
class SendInvoiceJob implements ShouldQueue
{
public function __construct(
public int $invoiceId,
public ?string $requestId = null,
) {}
public function handle(): void
{
if ($this->requestId) {
app()->instance('request_id', $this->requestId);
}
logger()->info('Sending invoice', ['invoice_id' => $this->invoiceId]);
// ...
}
}
Bu küçük taşıma, “job fail oldu” durumunda ilgili HTTP isteğini bulmayı kolaylaştırır.
5) Bonus: Harici servislere giderken header ekle
Laravel HTTP Client kullanıyorsanız:
Http::withHeaders([
'X-Request-Id' => app('request_id'),
])->post($url, $payload);
Karşı servis de aynı ID’yi loglarsa, iki sistem arasında da izlenebilirlik oluşur.
Sonuç
Request ID, küçük bir ekleme gibi görünür ama prod ortamında debug süresini dramatik şekilde azaltır. Laravel’de middleware + log context + job propagation üçlüsüyle, “hangi istek bu hatayı tetikledi?” sorusunu artık tek arama ile yanıtlayabilirsiniz.