diff --git a/src/LogLib/Classes/FileLock.php b/src/LogLib/Classes/FileLock.php new file mode 100644 index 0000000..ba8d29d --- /dev/null +++ b/src/LogLib/Classes/FileLock.php @@ -0,0 +1,102 @@ +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); + } + } + } \ No newline at end of file diff --git a/src/LogLib/Handlers/FileLogging.php b/src/LogLib/Handlers/FileLogging.php new file mode 100644 index 0000000..bc0157f --- /dev/null +++ b/src/LogLib/Handlers/FileLogging.php @@ -0,0 +1,155 @@ +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; + } +} \ No newline at end of file