Keep similar-ai from tripping the global circuit on a lone URL 502, clamp Qdrant search to 100, and add Server-Timing plus slow-request logging. Studio shared props, Academy S3 exists caching, heat chunking, and Redis/scheduler hygiene stay in this rollout.
75 lines
2.4 KiB
PHP
75 lines
2.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Http\Middleware;
|
|
|
|
use App\Support\Http\HttpUriNormalizer;
|
|
use Closure;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Inertia\Support\Header as InertiaHeader;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
|
|
final class LogSlowHttpRequest
|
|
{
|
|
public function __construct(private readonly HttpUriNormalizer $uris)
|
|
{
|
|
}
|
|
|
|
public function handle(Request $request, Closure $next): Response
|
|
{
|
|
if (! (bool) config('http_observability.slow_request.enabled', false)) {
|
|
return $next($request);
|
|
}
|
|
|
|
$started = hrtime(true);
|
|
$response = $next($request);
|
|
$durationMs = (hrtime(true) - $started) / 1_000_000;
|
|
|
|
if (app()->environment('testing')) {
|
|
$override = config('http_observability.slow_request.test_duration_ms');
|
|
if (is_numeric($override)) {
|
|
$durationMs = (float) $override;
|
|
}
|
|
}
|
|
|
|
$threshold = max(1, (int) config('http_observability.slow_request.threshold_ms', 750));
|
|
if ($durationMs < $threshold) {
|
|
return $response;
|
|
}
|
|
|
|
$route = $request->route();
|
|
$routeUri = is_object($route) && method_exists($route, 'uri')
|
|
? (string) $route->uri()
|
|
: $this->uris->normalize($request->path());
|
|
|
|
$payload = [
|
|
'time' => now()->toIso8601String(),
|
|
'method' => $request->getMethod(),
|
|
'route_name' => is_object($route) ? $route->getName() : null,
|
|
'route_uri' => $routeUri,
|
|
'status' => $response->getStatusCode(),
|
|
'duration_ms' => round($durationMs, 1),
|
|
'peak_memory_mb' => round(memory_get_peak_usage(true) / 1048576, 2),
|
|
'authenticated' => $request->user() !== null,
|
|
'inertia' => $this->isInertia($request, $response),
|
|
];
|
|
|
|
Log::channel('slow-http')->info(json_encode($payload, JSON_UNESCAPED_SLASHES));
|
|
|
|
return $response;
|
|
}
|
|
|
|
private function isInertia(Request $request, Response $response): bool
|
|
{
|
|
if ($request->headers->has(InertiaHeader::INERTIA)) {
|
|
return true;
|
|
}
|
|
|
|
return $response->headers->has(InertiaHeader::INERTIA)
|
|
|| $response->headers->get('Vary') === 'X-Inertia'
|
|
|| str_contains((string) $response->headers->get('Vary'), 'X-Inertia');
|
|
}
|
|
}
|