Ship production optimization M1-M12.5A: queues, metrics, HTTP observability, and vector search reliability.
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.
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Artwork;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
function slowHttpLogPath(): string
|
||||
{
|
||||
return storage_path('logs/slow-http-test.log');
|
||||
}
|
||||
|
||||
function configureSlowHttpLog(): void
|
||||
{
|
||||
config([
|
||||
'http_observability.slow_request.enabled' => true,
|
||||
'http_observability.slow_request.threshold_ms' => 750,
|
||||
'logging.channels.slow-http.driver' => 'single',
|
||||
'logging.channels.slow-http.path' => slowHttpLogPath(),
|
||||
'logging.channels.slow-http.level' => 'info',
|
||||
]);
|
||||
Log::forgetChannel('slow-http');
|
||||
File::delete(slowHttpLogPath());
|
||||
}
|
||||
|
||||
afterEach(function (): void {
|
||||
File::delete(slowHttpLogPath());
|
||||
});
|
||||
|
||||
it('does not write a slow-http record for a fast request', function () {
|
||||
configureSlowHttpLog();
|
||||
config(['http_observability.slow_request.test_duration_ms' => 120]);
|
||||
|
||||
$this->get('/leaderboard')->assertOk();
|
||||
|
||||
expect(File::exists(slowHttpLogPath()))->toBeFalse();
|
||||
});
|
||||
|
||||
it('writes one structured slow-http record using the route template', function () {
|
||||
configureSlowHttpLog();
|
||||
config(['http_observability.slow_request.test_duration_ms' => 900]);
|
||||
|
||||
$response = $this->get('/leaderboard');
|
||||
$response->assertOk();
|
||||
expect($response->headers->get('Server-Timing'))->toContain('app;desc="Laravel"');
|
||||
|
||||
$raw = File::get(slowHttpLogPath());
|
||||
expect(substr_count(trim($raw), "\n"))->toBe(0);
|
||||
|
||||
$line = trim($raw);
|
||||
preg_match('/\{.*\}\s*$/', $line, $matches);
|
||||
expect($matches[0] ?? null)->not->toBeNull();
|
||||
$json = json_decode($matches[0], true, flags: JSON_THROW_ON_ERROR);
|
||||
|
||||
expect($json['method'])->toBe('GET')
|
||||
->and($json['status'])->toBe(200)
|
||||
->and($json['duration_ms'])->toEqual(900)
|
||||
->and($json['route_uri'])->not->toContain('?')
|
||||
->and($json['route_uri'])->not->toContain('leaderboard/')
|
||||
->and($json)->not->toHaveKey('query')
|
||||
->and($json)->not->toHaveKey('ip')
|
||||
->and($json)->not->toHaveKey('email')
|
||||
->and($json)->not->toHaveKey('user_id')
|
||||
->and($json['authenticated'])->toBeFalse();
|
||||
});
|
||||
|
||||
it('does not log when slow-request logging is disabled', function () {
|
||||
configureSlowHttpLog();
|
||||
config([
|
||||
'http_observability.slow_request.enabled' => false,
|
||||
'http_observability.slow_request.test_duration_ms' => 5000,
|
||||
]);
|
||||
|
||||
$this->get('/leaderboard')->assertOk()->assertHeader('Server-Timing');
|
||||
|
||||
expect(File::exists(slowHttpLogPath()))->toBeFalse();
|
||||
});
|
||||
|
||||
it('does not break a missing-file download response and still sends Server-Timing', function () {
|
||||
configureSlowHttpLog();
|
||||
config(['http_observability.slow_request.test_duration_ms' => 100]);
|
||||
|
||||
$artwork = Artwork::factory()->create([
|
||||
'is_public' => true,
|
||||
'is_approved' => true,
|
||||
'published_at' => now()->subDay(),
|
||||
'file_ext' => 'jpg',
|
||||
'hash' => str_repeat('cd', 32),
|
||||
]);
|
||||
|
||||
$this->get('/download/artwork/'.$artwork->id)
|
||||
->assertNotFound()
|
||||
->assertHeader('Server-Timing');
|
||||
});
|
||||
|
||||
it('does not log query strings or user identifiers on an authenticated slow request', function () {
|
||||
configureSlowHttpLog();
|
||||
config(['http_observability.slow_request.test_duration_ms' => 800]);
|
||||
|
||||
$user = User::factory()->create([
|
||||
'email' => '[email protected]',
|
||||
'username' => 'slowlogsecret',
|
||||
]);
|
||||
|
||||
$this->actingAs($user)->get('/leaderboard?period=weekly&debug=1')->assertOk();
|
||||
|
||||
$raw = File::get(slowHttpLogPath());
|
||||
expect($raw)->not->toContain('[email protected]')
|
||||
->and($raw)->not->toContain('period=weekly')
|
||||
->and($raw)->not->toContain('debug=1')
|
||||
->and($raw)->not->toContain('"user_id"')
|
||||
->and($raw)->not->toContain($user->email);
|
||||
});
|
||||
Reference in New Issue
Block a user