Bicycle/System/Classes/FileLogReader.php
Egor Isaev 858e574e60 dev
2026-08-06 15:45:09 +03:00

99 lines
2.8 KiB
PHP

<?php
/**
* @package Bicycle
* @author Egor Isaev
* @description FileLogReader.php
* @copyright (c) 24/06/2026
*/
namespace System\Classes;
/**
* Чтение логов из файлов {channel}-{Y-m-d}.log в каталоге Log::directory().
* Парсит строки формата "[Y-m-d H:i:s] LEVEL: message".
*/
class FileLogReader implements LogReader
{
/** @var int Максимум записей в выдаче */
public int $limit = 1000;
/** Регулярка строки лога: дата-время, уровень, сообщение */
private const LINE = '/^\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\] (\w+): (.*)$/';
/**
* {@inheritDoc}
*/
public function read(array $filters = []): array
{
$date = $filters['date'] ?? date('Y-m-d');
$channel = $filters['channel'] ?? 'action';
$level = strtolower((string)($filters['level'] ?? ''));
$q = (string)($filters['q'] ?? '');
$channels = $channel === 'all' ? ['action', 'error'] : [$channel];
$entries = [];
foreach ($channels as $ch) {
$file = Log::directory() . '/' . $ch . '-' . $date . '.log';
if (!is_file($file)) {
continue;
}
foreach (file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) as $line) {
if (!preg_match(self::LINE, $line, $m)) {
continue;
}
$entry = [
'datetime' => $m[1],
'level' => strtolower($m[2]),
'message' => $m[3],
'channel' => $ch,
];
if ($level !== '' && $entry['level'] !== $level) {
continue;
}
if ($q !== '' && stripos($line, $q) === false) {
continue;
}
$entries[] = $entry;
}
}
// Новые сверху (datetime сравним лексикографически).
usort($entries, static fn($a, $b) => strcmp($b['datetime'], $a['datetime']));
return array_slice($entries, 0, $this->limit);
}
/**
* {@inheritDoc}
*/
public function dates(): array
{
$dir = Log::directory();
if (!is_dir($dir)) {
return [];
}
$files = array_merge(
glob($dir . '/action-*.log') ?: [],
glob($dir . '/error-*.log') ?: []
);
$dates = [];
foreach ($files as $file) {
if (preg_match('/-(\d{4}-\d{2}-\d{2})\.log$/', $file, $m)) {
$dates[$m[1]] = true;
}
}
$dates = array_keys($dates);
rsort($dates); // новые даты сверху
return $dates;
}
}