feat : ajout de la lecture des logs symfony et docker (#3)
Some checks failed
Auto Tag Develop / tag (push) Has been cancelled

Reviewed-on: #3
Co-authored-by: tristan <tristan@yuno.malio.fr>
Co-committed-by: tristan <tristan@yuno.malio.fr>
This commit was merged in pull request #3.
This commit is contained in:
2026-04-07 10:01:01 +00:00
committed by Autin
parent 7e342c9aeb
commit b769abdbe1
14 changed files with 525 additions and 19 deletions

View File

@@ -8,11 +8,34 @@ use Symfony\Component\Process\Process;
final class DockerService
{
private ?bool $dockerAvailable = null;
private function isDockerAvailable(): bool
{
if (null === $this->dockerAvailable) {
$process = new Process(['which', 'docker']);
$process->setTimeout(5);
$process->run();
$this->dockerAvailable = $process->isSuccessful();
}
return $this->dockerAvailable;
}
/**
* @return array{status: string, image: string, version: string, startedAt: string}
*/
public function getContainerStatus(string $containerName): array
{
if (!$this->isDockerAvailable()) {
return [
'status' => 'unavailable',
'image' => '',
'version' => '',
'startedAt' => '',
];
}
$process = new Process([
'docker', 'inspect',
'--format', '{{.State.Status}}||{{.Config.Image}}||{{.State.StartedAt}}',
@@ -60,6 +83,15 @@ final class DockerService
*/
public function getContainerStats(string $containerName): array
{
if (!$this->isDockerAvailable()) {
return [
'cpuPercent' => 0.0,
'memoryUsage' => '',
'memoryLimit' => '',
'memoryPercent' => 0.0,
];
}
$process = new Process([
'docker', 'stats', '--no-stream',
'--format', '{{.CPUPerc}}||{{.MemUsage}}||{{.MemPerc}}',

113
src/Service/LogService.php Normal file
View File

@@ -0,0 +1,113 @@
<?php
declare(strict_types=1);
namespace App\Service;
use Symfony\Component\Process\Process;
final readonly class LogService
{
public function __construct(
private AppPathResolver $pathResolver,
) {}
public function getDockerLogs(string $containerName, int $lines = 100, ?string $since = null): string
{
$check = new Process(['which', 'docker']);
$check->setTimeout(5);
$check->run();
if (!$check->isSuccessful()) {
return 'Docker CLI is not available in this environment.';
}
$args = ['docker', 'logs', '--tail', (string) $lines];
if (null !== $since) {
$args[] = '--since';
$args[] = $since;
}
$args[] = $containerName;
$process = new Process($args);
$process->setTimeout(10);
$process->run();
return $process->getOutput() . $process->getErrorOutput();
}
public function getSymfonyLog(string $relativePath, int $lines = 100, ?string $level = null): string
{
$path = $this->pathResolver->resolve($relativePath);
if (!file_exists($path)) {
return sprintf('Log file not found: %s', $path);
}
// Read more lines than requested to compensate for filtering
$readLines = (null !== $level && '' !== $level) ? $lines * 5 : $lines;
$process = new Process(['tail', '-n', (string) $readLines, $path]);
$process->setTimeout(10);
$process->run();
$rawLines = explode("\n", trim($process->getOutput()));
$formatted = [];
foreach ($rawLines as $line) {
if ('' === $line) {
continue;
}
$parsed = $this->parseSymfonyLogLine($line);
if (null === $parsed) {
continue;
}
// Skip noisy channels
if ('doctrine' === $parsed['channel']) {
continue;
}
if (null !== $level && '' !== $level && !str_contains(strtoupper($parsed['level']), strtoupper($level))) {
continue;
}
$formatted[] = sprintf('[%s] %s.%s: %s', $parsed['date'], $parsed['channel'], $parsed['level'], $parsed['message']);
}
// Keep only the last N lines after filtering
$formatted = \array_slice($formatted, -$lines);
return implode("\n", $formatted);
}
/**
* @return array{date: string, level: string, channel: string, message: string}|null
*/
private function parseSymfonyLogLine(string $line): ?array
{
// Standard Symfony monolog format: [2026-04-03T15:33:19.304937+02:00] channel.LEVEL: message {context} []
if (preg_match('/^\[([^\]]+)\]\s+(\w+)\.(\w+):\s+(.+)$/', $line, $matches)) {
$date = substr($matches[1], 0, 19); // Trim to YYYY-MM-DDTHH:MM:SS
$message = $matches[4];
// Remove JSON context at the end: {"key":"value"} []
$message = preg_replace('/\s*\{.*\}\s*\[\]\s*$/', '', $message) ?? $message;
// Remove trailing []
$message = preg_replace('/\s*\[\]\s*$/', '', $message) ?? $message;
return [
'date' => str_replace('T', ' ', $date),
'level' => $matches[3],
'channel' => $matches[2],
'message' => $message,
];
}
return null;
}
}