2022-06-01 11:20:24 +02:00
|
|
|
<?php
|
|
|
|
|
2022-11-18 18:06:46 +01:00
|
|
|
namespace Core\API {
|
2022-06-01 11:20:24 +02:00
|
|
|
|
2022-11-18 18:06:46 +01:00
|
|
|
use Core\Objects\Context;
|
2022-06-01 11:20:24 +02:00
|
|
|
|
|
|
|
abstract class LogsAPI extends Request {
|
2022-06-20 19:52:31 +02:00
|
|
|
public function __construct(Context $context, bool $externalCall = false, array $params = array()) {
|
|
|
|
parent::__construct($context, $externalCall, $params);
|
2022-06-01 11:20:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-18 18:06:46 +01:00
|
|
|
namespace Core\API\Logs {
|
2022-06-01 11:20:24 +02:00
|
|
|
|
2022-11-18 18:06:46 +01:00
|
|
|
use Core\API\LogsAPI;
|
|
|
|
use Core\API\Parameter\Parameter;
|
|
|
|
use Core\API\Parameter\StringType;
|
2024-03-25 18:37:08 +01:00
|
|
|
use Core\API\Traits\Pagination;
|
2022-11-18 18:06:46 +01:00
|
|
|
use Core\Driver\Logger\Logger;
|
|
|
|
use Core\Driver\SQL\Column\Column;
|
|
|
|
use Core\Driver\SQL\Condition\Compare;
|
2024-03-25 18:37:08 +01:00
|
|
|
use Core\Driver\SQL\Condition\CondAnd;
|
2022-11-18 18:06:46 +01:00
|
|
|
use Core\Driver\SQL\Condition\CondIn;
|
2024-03-25 18:37:08 +01:00
|
|
|
use Core\Driver\SQL\Condition\CondLike;
|
|
|
|
use Core\Driver\SQL\Condition\CondOr;
|
2022-11-18 18:06:46 +01:00
|
|
|
use Core\Objects\Context;
|
2023-01-16 21:47:23 +01:00
|
|
|
use Core\Objects\DatabaseEntity\Group;
|
2022-11-18 18:06:46 +01:00
|
|
|
use Core\Objects\DatabaseEntity\SystemLog;
|
2022-06-01 11:20:24 +02:00
|
|
|
|
2024-04-10 19:04:37 +02:00
|
|
|
// TODO: how to handle pagination here for log entries stored in files?
|
2022-06-01 11:20:24 +02:00
|
|
|
class Get extends LogsAPI {
|
|
|
|
|
2024-03-25 18:37:08 +01:00
|
|
|
use Pagination;
|
|
|
|
|
2024-03-29 16:37:42 +01:00
|
|
|
protected array $shownLogLevels;
|
|
|
|
protected ?\DateTime $since;
|
|
|
|
|
2022-06-20 19:52:31 +02:00
|
|
|
public function __construct(Context $context, bool $externalCall = false) {
|
2024-03-25 18:37:08 +01:00
|
|
|
$params = self::getPaginationParameters(['id', 'timestamp', "module", "severity"],
|
|
|
|
'timestamp', 'desc');
|
|
|
|
$params["since"] = new Parameter("since", Parameter::TYPE_DATE_TIME, true);
|
2024-03-29 16:37:42 +01:00
|
|
|
$params["severity"] = new StringType("severity", 32, true, "debug", array_values(Logger::LOG_LEVELS));
|
2024-03-25 18:37:08 +01:00
|
|
|
$params["query"] = new StringType("query", 64, true, null);
|
|
|
|
parent::__construct($context, $externalCall, $params);
|
2024-03-29 16:37:42 +01:00
|
|
|
$this->shownLogLevels = Logger::LOG_LEVELS;
|
|
|
|
$this->since = null;
|
2022-06-01 11:20:24 +02:00
|
|
|
}
|
|
|
|
|
2024-03-29 16:37:42 +01:00
|
|
|
protected function getFilter(): CondIn|CondAnd|bool {
|
|
|
|
$this->since = $this->getParam("since");
|
2022-06-01 11:20:24 +02:00
|
|
|
$severity = strtolower(trim($this->getParam("severity")));
|
2024-03-25 18:37:08 +01:00
|
|
|
$query = $this->getParam("query");
|
2022-06-01 11:20:24 +02:00
|
|
|
|
|
|
|
$logLevel = array_search($severity, Logger::LOG_LEVELS, true);
|
|
|
|
if ($logLevel === false) {
|
|
|
|
return $this->createError("Invalid severity. Allowed values: " . implode(",", Logger::LOG_LEVELS));
|
|
|
|
} else if ($logLevel > 0) {
|
2024-03-29 16:37:42 +01:00
|
|
|
$this->shownLogLevels = array_slice(Logger::LOG_LEVELS, $logLevel);
|
2022-06-01 11:20:24 +02:00
|
|
|
}
|
|
|
|
|
2024-03-29 16:37:42 +01:00
|
|
|
$condition = new CondIn(new Column("severity"), $this->shownLogLevels);
|
|
|
|
if ($this->since !== null) {
|
|
|
|
$condition = new CondAnd($condition, new Compare("timestamp", $this->since, ">="));
|
2024-03-25 18:37:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($query) {
|
|
|
|
$condition = new CondAnd($condition, new CondOr(
|
|
|
|
new CondLike(new Column("message"), "%$query%"),
|
|
|
|
new CondLike(new Column("module"), "%$query%"),
|
|
|
|
));
|
2022-06-01 11:20:24 +02:00
|
|
|
}
|
|
|
|
|
2024-03-29 16:37:42 +01:00
|
|
|
return $condition;
|
|
|
|
}
|
2022-06-01 11:20:24 +02:00
|
|
|
|
2024-03-29 16:37:42 +01:00
|
|
|
protected function loadFromFileSystem(array &$logs): void {
|
2022-06-01 11:20:24 +02:00
|
|
|
// get all log entries from filesystem (if database failed)
|
2022-12-04 13:21:40 +01:00
|
|
|
$logPath = realpath(implode(DIRECTORY_SEPARATOR, [WEBROOT, "Site", "Logs"]));
|
2022-06-01 11:20:24 +02:00
|
|
|
if ($logPath) {
|
|
|
|
$index = 1;
|
|
|
|
foreach (scandir($logPath) as $fileName) {
|
|
|
|
$logFile = $logPath . DIRECTORY_SEPARATOR . $fileName;
|
|
|
|
// {module}_{severity}_{date}_{time}_{ms}.log
|
|
|
|
if (preg_match("/^(\w+)_(\w+)_((\d+-\d+-\d+_){2}\d+)\.log$/", $fileName, $matches) && is_file($logFile)) {
|
|
|
|
$content = @file_get_contents($logFile);
|
|
|
|
$date = \DateTime::createFromFormat(Logger::LOG_FILE_DATE_FORMAT, $matches[3]);
|
|
|
|
if ($content && $date) {
|
|
|
|
|
|
|
|
// filter log date
|
2024-03-29 16:37:42 +01:00
|
|
|
if ($this->since !== null && datetimeDiff($date, $this->since) > 0) {
|
2022-06-01 11:20:24 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// filter log level
|
2024-03-29 16:37:42 +01:00
|
|
|
if (!in_array(trim(strtolower($matches[2])), $this->shownLogLevels)) {
|
2022-06-01 11:20:24 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2024-03-29 16:37:42 +01:00
|
|
|
$logs[] = [
|
2022-06-01 11:20:24 +02:00
|
|
|
"id" => "file-" . ($index++),
|
|
|
|
"module" => $matches[1],
|
|
|
|
"severity" => $matches[2],
|
|
|
|
"message" => $content,
|
|
|
|
"timestamp" => $date->format(Parameter::DATE_TIME_FORMAT)
|
|
|
|
];
|
2024-04-11 17:51:50 +02:00
|
|
|
|
|
|
|
$this->result["pagination"]["total"] += 1;
|
2022-06-01 11:20:24 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-03-29 16:37:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
protected function _execute(): bool {
|
|
|
|
$sql = $this->context->getSQL();
|
|
|
|
$condition = $this->getFilter();
|
|
|
|
if (!$this->initPagination($sql, SystemLog::class, $condition)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$query = $this->createPaginationQuery($sql);
|
|
|
|
$logEntries = SystemLog::findBy($query);
|
|
|
|
$this->success = $logEntries !== false;
|
|
|
|
$this->lastError = $sql->getLastError();
|
2022-06-01 11:20:24 +02:00
|
|
|
|
2024-03-29 16:37:42 +01:00
|
|
|
if ($this->success) {
|
|
|
|
$this->result["logs"] = [];
|
|
|
|
foreach ($logEntries as $logEntry) {
|
|
|
|
$this->result["logs"][] = $logEntry->jsonSerialize();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// we couldn't fetch logs from database, return a message and proceed to log files
|
|
|
|
$this->result["logs"] = [
|
|
|
|
[
|
|
|
|
"id" => "fetch-fail",
|
|
|
|
"module" => "LogsAPI",
|
|
|
|
"message" => "Failed retrieving logs from database: " . $this->lastError,
|
|
|
|
"severity" => "error",
|
|
|
|
"timestamp" => (new \DateTime())->format(Parameter::DATE_TIME_FORMAT)
|
|
|
|
]
|
|
|
|
];
|
2024-04-11 17:51:50 +02:00
|
|
|
$this->result["pagination"]["total"] += 1;
|
2024-03-29 16:37:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$this->loadFromFileSystem($this->result["logs"]);
|
2022-06-01 11:20:24 +02:00
|
|
|
return true;
|
|
|
|
}
|
2023-01-16 21:47:23 +01:00
|
|
|
|
2024-04-23 12:14:28 +02:00
|
|
|
public static function getDescription(): string {
|
|
|
|
return "Allows users to fetch system logs";
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function getDefaultPermittedGroups(): array {
|
|
|
|
return [Group::ADMIN];
|
2023-01-16 21:47:23 +01:00
|
|
|
}
|
2022-06-01 11:20:24 +02:00
|
|
|
}
|
|
|
|
}
|