mirror of
https://github.com/symfony/http-kernel.git
synced 2026-03-24 01:12:09 +01:00
[HttpKernel][WebProfilerBundle] Add error indicator to profiler list view
This commit is contained in:
@@ -4,6 +4,7 @@ CHANGELOG
|
||||
8.1
|
||||
---
|
||||
|
||||
* Add `hasErrors()` method to `Profile` to track profiles with errors (exceptions or error-level logs)
|
||||
* Validate typed route parameters before calling controllers and return an HTTP error when an invalid value is provided
|
||||
* Add `ControllerAttributeEvent` et al. to dispatch events named after controller attributes
|
||||
* Add support for `UploadedFile` when using `MapRequestPayload`
|
||||
|
||||
@@ -62,7 +62,7 @@ class FileProfilerStorage implements ProfilerStorageInterface
|
||||
continue;
|
||||
}
|
||||
|
||||
[$csvToken, $csvIp, $csvMethod, $csvUrl, $csvTime, $csvParent, $csvStatusCode, $csvVirtualType] = $values + [7 => null];
|
||||
[$csvToken, $csvIp, $csvMethod, $csvUrl, $csvTime, $csvParent, $csvStatusCode, $csvVirtualType, $csvHasErrors] = $values + [7 => null, 8 => null];
|
||||
$csvTime = (int) $csvTime;
|
||||
|
||||
$urlFilter = false;
|
||||
@@ -91,6 +91,7 @@ class FileProfilerStorage implements ProfilerStorageInterface
|
||||
'parent' => $csvParent,
|
||||
'status_code' => $csvStatusCode,
|
||||
'virtual_type' => $csvVirtualType ?: 'request',
|
||||
'has_errors' => (bool) $csvHasErrors,
|
||||
];
|
||||
|
||||
if ($filter && !$filter($profile)) {
|
||||
@@ -159,6 +160,7 @@ class FileProfilerStorage implements ProfilerStorageInterface
|
||||
'time' => $profile->getTime(),
|
||||
'status_code' => $profile->getStatusCode(),
|
||||
'virtual_type' => $profile->getVirtualType() ?? 'request',
|
||||
'has_errors' => $profile->hasErrors(),
|
||||
];
|
||||
|
||||
$data = serialize($data);
|
||||
@@ -186,6 +188,7 @@ class FileProfilerStorage implements ProfilerStorageInterface
|
||||
$profile->getParentToken(),
|
||||
$profile->getStatusCode(),
|
||||
$profile->getVirtualType() ?? 'request',
|
||||
$profile->hasErrors() ? '1' : '0',
|
||||
], ',', '"', '\\');
|
||||
fclose($file);
|
||||
|
||||
@@ -271,6 +274,7 @@ class FileProfilerStorage implements ProfilerStorageInterface
|
||||
$profile->setTime($data['time']);
|
||||
$profile->setStatusCode($data['status_code']);
|
||||
$profile->setVirtualType($data['virtual_type'] ?: 'request');
|
||||
$profile->setHasErrors($data['has_errors'] ?? false);
|
||||
$profile->setCollectors($data['data']);
|
||||
|
||||
if (!$parent && $data['parent']) {
|
||||
|
||||
@@ -32,6 +32,7 @@ class Profile
|
||||
private ?int $statusCode = null;
|
||||
private ?self $parent = null;
|
||||
private ?string $virtualType = null;
|
||||
private bool $hasErrors = false;
|
||||
|
||||
/**
|
||||
* @var Profile[]
|
||||
@@ -155,6 +156,16 @@ class Profile
|
||||
return $this->virtualType;
|
||||
}
|
||||
|
||||
public function hasErrors(): bool
|
||||
{
|
||||
return $this->hasErrors;
|
||||
}
|
||||
|
||||
public function setHasErrors(bool $hasErrors): void
|
||||
{
|
||||
$this->hasErrors = $hasErrors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds children profilers.
|
||||
*
|
||||
@@ -261,6 +272,7 @@ class Profile
|
||||
'time' => $this->time,
|
||||
'statusCode' => $this->statusCode,
|
||||
'virtualType' => $this->virtualType,
|
||||
'hasErrors' => $this->hasErrors,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface;
|
||||
use Symfony\Component\HttpKernel\DataCollector\LateDataCollectorInterface;
|
||||
use Symfony\Component\HttpKernel\DataCollector\LoggerDataCollector;
|
||||
use Symfony\Contracts\Service\ResetInterface;
|
||||
|
||||
/**
|
||||
@@ -94,6 +95,15 @@ class Profiler implements ResetInterface
|
||||
}
|
||||
}
|
||||
|
||||
// Update hasErrors flag to include error-level logs (available after lateCollect)
|
||||
if (!$profile->hasErrors()
|
||||
&& $profile->hasCollector('logger')
|
||||
&& ($logger = $profile->getCollector('logger')) instanceof LoggerDataCollector
|
||||
&& $logger->countErrors() > 0
|
||||
) {
|
||||
$profile->setHasErrors(true);
|
||||
}
|
||||
|
||||
if (!($ret = $this->storage->write($profile)) && null !== $this->logger) {
|
||||
$this->logger->warning('Unable to store the profiler information.', ['configured_storage' => $this->storage::class]);
|
||||
}
|
||||
@@ -148,6 +158,8 @@ class Profiler implements ResetInterface
|
||||
$profile->setVirtualType($request->attributes->get('_virtual_type'));
|
||||
}
|
||||
|
||||
$profile->setHasErrors(null !== $exception);
|
||||
|
||||
if ($prevToken = $response->headers->get('X-Debug-Token')) {
|
||||
$response->headers->set('X-Previous-Debug-Token', $prevToken);
|
||||
}
|
||||
|
||||
@@ -329,6 +329,45 @@ class FileProfilerStorageTest extends TestCase
|
||||
$this->assertContains((int) $tokens[1]['status_code'], [200, 404]);
|
||||
}
|
||||
|
||||
public function testHasErrors()
|
||||
{
|
||||
$profile = new Profile('token_with_errors');
|
||||
$profile->setIp('127.0.0.1');
|
||||
$profile->setUrl('http://foo.bar/error');
|
||||
$profile->setMethod('GET');
|
||||
$profile->setStatusCode(500);
|
||||
$profile->setHasErrors(true);
|
||||
$this->storage->write($profile);
|
||||
|
||||
$profile = new Profile('token_without_errors');
|
||||
$profile->setIp('127.0.0.1');
|
||||
$profile->setUrl('http://foo.bar/success');
|
||||
$profile->setMethod('GET');
|
||||
$profile->setStatusCode(200);
|
||||
$profile->setHasErrors(false);
|
||||
$this->storage->write($profile);
|
||||
|
||||
$loadedProfile = $this->storage->read('token_with_errors');
|
||||
$this->assertTrue($loadedProfile->hasErrors(), '->read() restores hasErrors=true on the Profile object');
|
||||
|
||||
$loadedProfile = $this->storage->read('token_without_errors');
|
||||
$this->assertFalse($loadedProfile->hasErrors(), '->read() restores hasErrors=false on the Profile object');
|
||||
}
|
||||
|
||||
public function testHasErrorsBackwardCompatibility()
|
||||
{
|
||||
// Test backward compatibility with old CSV lines that don't have has_errors field
|
||||
$file = $this->tmpDir.'/index.csv';
|
||||
$time = time();
|
||||
|
||||
// Write an old-format CSV line (8 fields, no has_errors)
|
||||
file_put_contents($file, "old_token,127.0.0.1,GET,http://foo.bar/old,{$time},,200,request\n");
|
||||
|
||||
$tokens = $this->storage->find('', '', 10, '');
|
||||
$this->assertCount(1, $tokens);
|
||||
$this->assertFalse($tokens[0]['has_errors'], '->find() returns has_errors=false for old CSV lines without the field');
|
||||
}
|
||||
|
||||
public function testMultiRowIndexFile()
|
||||
{
|
||||
$iteration = 3;
|
||||
|
||||
Reference in New Issue
Block a user