2022-05-31 16:14:49 +02:00
|
|
|
<?php
|
|
|
|
|
2022-11-18 18:06:46 +01:00
|
|
|
namespace Core\Driver\Logger;
|
2022-05-31 16:14:49 +02:00
|
|
|
|
2022-11-18 18:06:46 +01:00
|
|
|
use Core\Driver\SQL\SQL;
|
2022-05-31 16:14:49 +02:00
|
|
|
|
|
|
|
class Logger {
|
|
|
|
|
|
|
|
public const LOG_FILE_DATE_FORMAT = "Y-m-d_H-i-s_v";
|
2023-01-16 21:47:23 +01:00
|
|
|
public const LOG_LEVEL_NONE = -1;
|
2022-11-27 12:33:27 +01:00
|
|
|
public const LOG_LEVEL_DEBUG = 0;
|
|
|
|
public const LOG_LEVEL_INFO = 1;
|
|
|
|
public const LOG_LEVEL_WARNING = 2;
|
|
|
|
public const LOG_LEVEL_ERROR = 3;
|
|
|
|
public const LOG_LEVEL_SEVERE = 4;
|
|
|
|
|
2022-05-31 16:14:49 +02:00
|
|
|
public const LOG_LEVELS = [
|
2022-11-27 12:33:27 +01:00
|
|
|
self::LOG_LEVEL_DEBUG => "debug",
|
|
|
|
self::LOG_LEVEL_INFO => "info",
|
|
|
|
self::LOG_LEVEL_WARNING => "warning",
|
|
|
|
self::LOG_LEVEL_ERROR => "error",
|
|
|
|
self::LOG_LEVEL_SEVERE => "severe"
|
2022-05-31 16:14:49 +02:00
|
|
|
];
|
|
|
|
|
|
|
|
public static Logger $INSTANCE;
|
|
|
|
|
|
|
|
private ?SQL $sql;
|
|
|
|
private string $module;
|
|
|
|
|
2022-06-17 23:36:04 +02:00
|
|
|
// unit tests
|
|
|
|
private bool $unitTestMode;
|
|
|
|
private ?string $lastMessage;
|
|
|
|
private ?string $lastLevel;
|
|
|
|
|
2022-05-31 16:14:49 +02:00
|
|
|
public function __construct(string $module = "Unknown", ?SQL $sql = null) {
|
|
|
|
$this->module = $module;
|
|
|
|
$this->sql = $sql;
|
2022-06-17 23:36:04 +02:00
|
|
|
$this->unitTestMode = false;
|
|
|
|
$this->lastMessage = null;
|
|
|
|
$this->lastLevel = null;
|
2022-05-31 16:14:49 +02:00
|
|
|
}
|
|
|
|
|
2024-05-04 17:06:39 +02:00
|
|
|
protected function getStackTrace(int $pop = 2, ?array $debugTrace = null): string {
|
|
|
|
if ($debugTrace === null) {
|
|
|
|
$debugTrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
|
|
|
|
}
|
|
|
|
|
2022-05-31 16:14:49 +02:00
|
|
|
if ($pop > 0) {
|
|
|
|
array_splice($debugTrace, 0, $pop);
|
|
|
|
}
|
|
|
|
return implode("\n", array_map(function ($trace) {
|
2022-06-20 19:52:31 +02:00
|
|
|
if (isset($trace["file"])) {
|
|
|
|
return $trace["file"] . "#" . $trace["line"] . ": " . $trace["function"] . "()";
|
|
|
|
} else {
|
|
|
|
return $trace["function"] . "()";
|
|
|
|
}
|
2022-05-31 16:14:49 +02:00
|
|
|
}, $debugTrace));
|
|
|
|
}
|
|
|
|
|
2024-05-04 17:06:39 +02:00
|
|
|
public function log(string|\Throwable $logEntry, string $severity, bool $appendStackTrace = true): void {
|
|
|
|
|
|
|
|
$debugTrace = null;
|
|
|
|
$message = $logEntry;
|
|
|
|
if ($message instanceof \Throwable) {
|
|
|
|
$message = $logEntry->getMessage();
|
|
|
|
$debugTrace = $logEntry->getTrace();
|
|
|
|
}
|
2022-05-31 16:14:49 +02:00
|
|
|
|
|
|
|
if ($appendStackTrace) {
|
2024-05-04 17:06:39 +02:00
|
|
|
$message .= "\n" . $this->getStackTrace(2, $debugTrace);
|
2022-05-31 16:14:49 +02:00
|
|
|
}
|
|
|
|
|
2022-11-18 18:06:46 +01:00
|
|
|
$this->lastMessage = $message;
|
|
|
|
$this->lastLevel = $severity;
|
2022-06-17 23:36:04 +02:00
|
|
|
if ($this->unitTestMode) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-11-27 12:33:27 +01:00
|
|
|
if ($severity >= self::LOG_LEVEL_WARNING) {
|
|
|
|
error_log($message);
|
|
|
|
}
|
|
|
|
|
2022-05-31 16:14:49 +02:00
|
|
|
if ($this->sql !== null && $this->sql->isConnected()) {
|
|
|
|
$success = $this->sql->insert("SystemLog", ["module", "message", "severity"])
|
|
|
|
->addRow($this->module, $message, $severity)
|
|
|
|
->execute();
|
|
|
|
if ($success !== false) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// database logging failed, try to log to file
|
|
|
|
$module = preg_replace("/[^a-zA-Z0-9-]/", "-", $this->module);
|
|
|
|
$date = (\DateTime::createFromFormat('U.u', microtime(true)))->format(self::LOG_FILE_DATE_FORMAT);
|
|
|
|
$logFile = implode("_", [$module, $severity, $date]) . ".log";
|
2022-12-04 13:21:40 +01:00
|
|
|
$logPath = implode(DIRECTORY_SEPARATOR, [WEBROOT, "Site", "Logs", $logFile]);
|
2022-05-31 16:14:49 +02:00
|
|
|
@file_put_contents($logPath, $message);
|
|
|
|
}
|
|
|
|
|
2024-05-04 17:06:39 +02:00
|
|
|
public function error(string|\Throwable $message): string {
|
2022-05-31 16:14:49 +02:00
|
|
|
$this->log($message, "error");
|
|
|
|
return $message;
|
|
|
|
}
|
|
|
|
|
2024-05-04 17:06:39 +02:00
|
|
|
public function severe(string|\Throwable $message): string {
|
2022-05-31 16:14:49 +02:00
|
|
|
$this->log($message, "severe");
|
|
|
|
return $message;
|
|
|
|
}
|
|
|
|
|
2024-05-04 17:06:39 +02:00
|
|
|
public function warning(string|\Throwable $message): string {
|
2022-05-31 16:14:49 +02:00
|
|
|
$this->log($message, "warning", false);
|
|
|
|
return $message;
|
|
|
|
}
|
|
|
|
|
2024-05-04 17:06:39 +02:00
|
|
|
public function info(string|\Throwable $message): string {
|
2022-05-31 16:14:49 +02:00
|
|
|
$this->log($message, "info", false);
|
|
|
|
return $message;
|
|
|
|
}
|
|
|
|
|
2024-05-04 17:06:39 +02:00
|
|
|
public function debug(string|\Throwable $message, bool $appendStackTrace = false): string {
|
2022-06-20 19:52:31 +02:00
|
|
|
$this->log($message, "debug", $appendStackTrace);
|
2022-05-31 16:14:49 +02:00
|
|
|
return $message;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function instance(): Logger {
|
|
|
|
if (self::$INSTANCE === null) {
|
|
|
|
self::$INSTANCE = new Logger("Global");
|
|
|
|
}
|
|
|
|
|
|
|
|
return self::$INSTANCE;
|
|
|
|
}
|
2022-06-17 23:36:04 +02:00
|
|
|
|
|
|
|
public function getLastMessage(): ?string {
|
|
|
|
return $this->lastMessage;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getLastLevel(): ?string {
|
|
|
|
return $this->lastLevel;
|
|
|
|
}
|
2022-11-18 18:06:46 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Calling this method will prevent the logger from persisting log messages (writing to database/file),
|
|
|
|
*/
|
2022-12-04 13:21:40 +01:00
|
|
|
public function unitTestMode(): void {
|
2022-11-18 18:06:46 +01:00
|
|
|
$this->unitTestMode = true;
|
|
|
|
}
|
2022-05-31 16:14:49 +02:00
|
|
|
}
|