Add FileLogging and FileLock classes
This commit is contained in:
parent
f3d0412525
commit
9d0dbad846
2 changed files with 257 additions and 0 deletions
102
src/LogLib/Classes/FileLock.php
Normal file
102
src/LogLib/Classes/FileLock.php
Normal file
|
@ -0,0 +1,102 @@
|
|||
<?php
|
||||
namespace LogLib\Classes;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Class FileLock
|
||||
*
|
||||
* This class provides functionalities to safely work with file locks and ensures
|
||||
* that concurrent write operations do not overwrite each other.
|
||||
* It offers methods to lock, unlock, and append data to a file.
|
||||
*/
|
||||
class FileLock
|
||||
{
|
||||
private $fileHandle;
|
||||
private string $filePath;
|
||||
private int $retryInterval; // in microseconds
|
||||
private int $confirmationInterval; // in microseconds
|
||||
|
||||
/**
|
||||
* Constructor for FileLock.
|
||||
*
|
||||
* @param string $filePath Path to the file.
|
||||
* @param int $retryInterval Time to wait between retries (in microseconds).
|
||||
* @param int $confirmationInterval Time to wait before double confirmation (in microseconds).
|
||||
*/
|
||||
public function __construct(string $filePath, int $retryInterval=100000, int $confirmationInterval=50000)
|
||||
{
|
||||
$this->filePath = $filePath;
|
||||
$this->retryInterval = $retryInterval;
|
||||
$this->confirmationInterval = $confirmationInterval;
|
||||
|
||||
// Create the file if it doesn't exist
|
||||
if (!file_exists($filePath))
|
||||
{
|
||||
$this->fileHandle = fopen($filePath, 'w');
|
||||
fclose($this->fileHandle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks the file.
|
||||
*
|
||||
* @throws RuntimeException if unable to open or lock the file.
|
||||
*/
|
||||
private function lock(): void
|
||||
{
|
||||
$this->fileHandle = fopen($this->filePath, 'a');
|
||||
|
||||
if (!$this->fileHandle)
|
||||
{
|
||||
throw new RuntimeException("Unable to open the file: " . $this->filePath);
|
||||
}
|
||||
|
||||
// Keep trying to acquire the lock until it succeeds
|
||||
while (!flock($this->fileHandle, LOCK_EX))
|
||||
{
|
||||
usleep($this->retryInterval); // Wait for the specified interval before trying again
|
||||
}
|
||||
|
||||
// Double confirmation
|
||||
usleep($this->confirmationInterval); // Wait for the specified confirmation interval
|
||||
if (!flock($this->fileHandle, LOCK_EX | LOCK_NB))
|
||||
{
|
||||
// If the lock cannot be re-acquired, release the current lock and retry
|
||||
flock($this->fileHandle, LOCK_UN);
|
||||
$this->lock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlocks the file after performing write operations.
|
||||
*/
|
||||
private function unlock(): void
|
||||
{
|
||||
flock($this->fileHandle, LOCK_UN); // Release the lock
|
||||
fclose($this->fileHandle); // Close the file handle
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends data to the file.
|
||||
*
|
||||
* @param string $data Data to append.
|
||||
* @throws RuntimeException if unable to write to the file.
|
||||
*/
|
||||
public function append(string $data): void
|
||||
{
|
||||
$this->lock();
|
||||
fwrite($this->fileHandle, $data);
|
||||
$this->unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor to ensure the file handle is closed.
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
if ($this->fileHandle)
|
||||
{
|
||||
fclose($this->fileHandle);
|
||||
}
|
||||
}
|
||||
}
|
155
src/LogLib/Handlers/FileLogging.php
Normal file
155
src/LogLib/Handlers/FileLogging.php
Normal file
|
@ -0,0 +1,155 @@
|
|||
<?php
|
||||
|
||||
namespace LogLib\Handlers;
|
||||
|
||||
use LogLib\Classes\FileLock;
|
||||
use LogLib\Classes\Utilities;
|
||||
use LogLib\Classes\Validate;
|
||||
use LogLib\Enums\LogHandlerType;
|
||||
use LogLib\Enums\LogLevel;
|
||||
use LogLib\Exceptions\LoggingException;
|
||||
use LogLib\Interfaces\LogHandlerInterface;
|
||||
use LogLib\Objects\Application;
|
||||
use LogLib\Objects\Event;
|
||||
use Throwable;
|
||||
|
||||
class FileLogging implements LogHandlerInterface
|
||||
{
|
||||
private static array $application_logs = [];
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public static function handle(Application $application, Event $event): void
|
||||
{
|
||||
if(!Validate::checkLevelType($event->getLevel(), $application->getFileLoggingLevel()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(Validate::checkLevelType(LogLevel::DEBUG, $application->getConsoleLoggingLevel()))
|
||||
{
|
||||
$backtrace_output = Utilities::getTraceString($event);
|
||||
|
||||
$output = sprintf("[%s] [%s] [%s] %s %s" . PHP_EOL,
|
||||
self::getTimestamp(), $application->getApplicationName(), $event->getLevel()->name, $backtrace_output, $event->getMessage()
|
||||
);
|
||||
|
||||
if($event->getException() !== null)
|
||||
{
|
||||
$output .= self::outException($event->getException());
|
||||
}
|
||||
}
|
||||
else if(Validate::checkLevelType(LogLevel::VERBOSE, $application->getConsoleLoggingLevel()))
|
||||
{
|
||||
$backtrace_output = Utilities::getTraceString($event);
|
||||
|
||||
$output = sprintf("[%s] [%s] %s %s" . PHP_EOL, $application->getApplicationName(), $event->getLevel()->name, $backtrace_output, $event->getMessage());
|
||||
|
||||
if($event->getException() !== null)
|
||||
{
|
||||
$output .= self::outException($event->getException());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$output = sprintf("[%s] [%s] %s" . PHP_EOL, $application->getApplicationName(), $event->getLevel()->name, $event->getMessage());
|
||||
}
|
||||
|
||||
self::getLogger($application)->append($output);
|
||||
}
|
||||
|
||||
public static function getType(): LogHandlerType
|
||||
{
|
||||
return LogHandlerType::FILE;
|
||||
}
|
||||
|
||||
private static function getLogger(Application $application): FileLock
|
||||
{
|
||||
if(!isset(self::$application_logs[$application->getApplicationName()]))
|
||||
{
|
||||
self::$application_logs[$application->getApplicationName()] = new FileLock(self::getLogFile($application));
|
||||
}
|
||||
|
||||
return self::$application_logs[$application->getApplicationName()];
|
||||
}
|
||||
|
||||
private static function getLogFile(Application $application): string
|
||||
{
|
||||
$logging_directory = $application->getFileLoggingDirectory();
|
||||
|
||||
if(!is_writable($logging_directory))
|
||||
{
|
||||
throw new LoggingException(sprintf("Cannot write to %s due to insufficient permissions", $logging_directory));
|
||||
}
|
||||
|
||||
if(!file_exists($logging_directory))
|
||||
{
|
||||
mkdir($logging_directory);
|
||||
}
|
||||
|
||||
$logging_file = $logging_directory . DIRECTORY_SEPARATOR . Utilities::sanitizeFileName($application->getApplicationName()) . date('Y-m-d') . '.log';
|
||||
|
||||
if(!file_exists($logging_file))
|
||||
{
|
||||
touch($logging_file);
|
||||
}
|
||||
|
||||
return $logging_file;
|
||||
}
|
||||
|
||||
private static function getExceptionFile(Application $application, \Throwable $e): string
|
||||
{
|
||||
$logging_directory = $application->getFileLoggingDirectory();
|
||||
|
||||
if(!is_writable($logging_directory))
|
||||
{
|
||||
throw new LoggingException(sprintf("Cannot write to %s due to insufficient permissions", $logging_directory));
|
||||
}
|
||||
|
||||
if(!file_exists($logging_directory))
|
||||
{
|
||||
mkdir($logging_directory);
|
||||
}
|
||||
|
||||
return Utilities::sanitizeFileName($application->getApplicationName()) . '-' . Utilities::sanitizeFileName(get_class($e)) . '-' . date('d-m-Y-H-i-s') . '.json';
|
||||
}
|
||||
|
||||
private static function getTimestamp(): string
|
||||
{
|
||||
return date('yd/m/y H:i');
|
||||
}
|
||||
|
||||
private static function outException(?Throwable $exception=null): string
|
||||
{
|
||||
if($exception === null)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
$output = '';
|
||||
$trace_header = $exception->getFile() . ':' . $exception->getLine();
|
||||
$trace_error = 'error: ';
|
||||
|
||||
$output .= $trace_header . ' ' . $trace_error . $exception->getMessage() . PHP_EOL;
|
||||
$output .= sprintf('Error code: %s', $exception->getCode() . PHP_EOL);
|
||||
$trace = $exception->getTrace();
|
||||
|
||||
if(count($trace) > 1)
|
||||
{
|
||||
$output .= 'Stack Trace:' . PHP_EOL;
|
||||
foreach($trace as $item)
|
||||
{
|
||||
$output .= ' - ' . $item['file'] . ':' . $item['line'] . PHP_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
if($exception->getPrevious() !== null)
|
||||
{
|
||||
$output .= 'Previous Exception:' . PHP_EOL;
|
||||
$output .= self::outException($exception->getPrevious());
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue