Compare commits
11 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
dc9b831534 | ||
![]() |
49bb26a063 | ||
![]() |
9958bdd205 | ||
![]() |
0507fbb9d1 | ||
![]() |
808baec53c | ||
![]() |
284cd4f52a | ||
![]() |
359afdcbe0 | ||
![]() |
e927c7a8ec | ||
![]() |
b6417cba97 | ||
![]() |
ae1c71fec0 | ||
![]() |
0fdf36661e |
10 changed files with 346 additions and 105 deletions
35
CHANGELOG.md
35
CHANGELOG.md
|
@ -5,6 +5,41 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [2.0.7] - 2025-01-13
|
||||
|
||||
This update introduces a minor fix
|
||||
|
||||
### Fixed
|
||||
- Fixed FileLogging issue by setting the write permission to 0666 for the log file if it doesn't exist.
|
||||
|
||||
|
||||
|
||||
## [2.0.6] - 2025-01-10
|
||||
|
||||
This update introduces a minor change
|
||||
|
||||
### Changed
|
||||
- File logging is disabled for web environments due to instability in file locking, until a better solution is found.
|
||||
|
||||
|
||||
## [2.0.5] - 2025-01-09
|
||||
|
||||
This update introduces a minor bug fix
|
||||
|
||||
### Fixed
|
||||
- Refactor file locking to return status and handle failure.
|
||||
|
||||
|
||||
## [2.0.4] - 2024-12-04
|
||||
|
||||
This update introduces a minor bug fix
|
||||
|
||||
|
||||
## [2.0.3] - 2024-11-05
|
||||
|
||||
This update introduces a minor bug fix
|
||||
|
||||
|
||||
## [2.0.2] - 2024-10-30
|
||||
|
||||
This update introduces minor improvements
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
"package": "net.nosial.loglib",
|
||||
"company": "Nosial",
|
||||
"copyright": "Copyright (c) 2022-2023 Nosial",
|
||||
"version": "2.0.2",
|
||||
"version": "2.0.7",
|
||||
"uuid": "de1deca6-7b65-11ed-a8b0-a172264634d8"
|
||||
},
|
||||
"build": {
|
||||
|
|
87
src/LogLib/Classes/BacktraceParser.php
Normal file
87
src/LogLib/Classes/BacktraceParser.php
Normal file
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
namespace LogLib\Classes;
|
||||
|
||||
class BacktraceParser
|
||||
{
|
||||
/**
|
||||
* Determines if the given backtrace originates from the exception handler.
|
||||
*
|
||||
* @param array $backtrace The backtrace array to inspect.
|
||||
* @return bool Returns true if the backtrace originates from the exception handler within LogLib\Runtime class, false otherwise.
|
||||
*/
|
||||
public static function fromExceptionHandler(array $backtrace): bool
|
||||
{
|
||||
/** @var array $trace */
|
||||
foreach($backtrace as $trace)
|
||||
{
|
||||
if(!isset($trace['function']) || $trace['function'] != 'exceptionHandler')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!isset($trace['class']) || $trace['class'] != 'LogLib\Runtime')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the given backtrace originates from the error handler.
|
||||
*
|
||||
* @param array $backtrace The backtrace array to inspect.
|
||||
* @return bool Returns true if the backtrace originates from the error handler within LogLib\Runtime class, false otherwise.
|
||||
*/
|
||||
public static function fromErrorHandler(array $backtrace): bool
|
||||
{
|
||||
/** @var array $trace */
|
||||
foreach($backtrace as $trace)
|
||||
{
|
||||
if(!isset($trace['function']) || $trace['function'] != 'errorHandler')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!isset($trace['class']) || $trace['class'] != 'LogLib\Runtime')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a given backtrace contains a call to the shutdownHandler method in the LogLib\Runtime class.
|
||||
*
|
||||
* @param array $backtrace The backtrace to be analyzed.
|
||||
* @return bool True if the shutdownHandler method in the LogLib\Runtime class is found in the backtrace; otherwise, false.
|
||||
*/
|
||||
public static function fromShutdownHandler(array $backtrace): bool
|
||||
{
|
||||
/** @var array $trace */
|
||||
foreach($backtrace as $trace)
|
||||
{
|
||||
if(!isset($trace['function']) || $trace['function'] != 'shutdownHandler')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!isset($trace['class']) || $trace['class'] != 'LogLib\Runtime')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -32,8 +32,11 @@
|
|||
// Create the file if it doesn't exist
|
||||
if (!file_exists($filePath))
|
||||
{
|
||||
$this->fileHandle = fopen($filePath, 'w');
|
||||
fclose($this->fileHandle);
|
||||
// Create the file
|
||||
touch($filePath);
|
||||
|
||||
// Set the file permissions to 0666
|
||||
chmod($filePath, 0666);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,12 +45,12 @@
|
|||
*
|
||||
* @throws RuntimeException if unable to open or lock the file.
|
||||
*/
|
||||
private function lock(): void
|
||||
private function lock(): bool
|
||||
{
|
||||
$this->fileHandle = fopen($this->filePath, 'a');
|
||||
$this->fileHandle = @fopen($this->filePath, 'a');
|
||||
if ($this->fileHandle === false)
|
||||
{
|
||||
throw new RuntimeException("Unable to open the file: " . $this->filePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Keep trying to acquire the lock until it succeeds
|
||||
|
@ -64,6 +67,8 @@
|
|||
flock($this->fileHandle, LOCK_UN);
|
||||
$this->lock();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -87,7 +92,11 @@
|
|||
*/
|
||||
public function append(string $data): void
|
||||
{
|
||||
$this->lock();
|
||||
if(!$this->lock())
|
||||
{
|
||||
// Do not proceed if the file cannot be locked
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->fileHandle !== false)
|
||||
{
|
||||
|
|
|
@ -157,36 +157,34 @@
|
|||
* If $ansi is true, the output will be colored using ANSI escape codes.
|
||||
* If the event has no backtrace, the constant CallType::LAMBDA_CALL will be returned.
|
||||
*/
|
||||
public static function getTraceString(Event $event, bool $ansi=true): ?string
|
||||
public static function getTraceString(Event $event, bool $ansi = true): ?string
|
||||
{
|
||||
if($event->getBacktrace() === null || count($event->getBacktrace()) === 0)
|
||||
if ($event->getBacktrace() === null || count($event->getBacktrace()) === 0)
|
||||
{
|
||||
return CallType::LAMBDA_CALL->value;
|
||||
}
|
||||
|
||||
$backtrace = $event->getBacktrace()[count($event->getBacktrace()) - 1];
|
||||
|
||||
// Ignore \LogLib namespace
|
||||
if(isset($backtrace['class']) && str_starts_with($backtrace['class'], 'LogLib'))
|
||||
if (isset($backtrace['class']) && str_starts_with($backtrace['class'], 'LogLib'))
|
||||
{
|
||||
if(isset($backtrace['file']))
|
||||
if (isset($backtrace['file']))
|
||||
{
|
||||
if($ansi)
|
||||
if ($ansi)
|
||||
{
|
||||
return "\033[1;37m" . basename($backtrace['file']) . "\033[0m";
|
||||
}
|
||||
|
||||
return basename($backtrace['file']);
|
||||
}
|
||||
|
||||
return basename($backtrace['file']);
|
||||
return self::determineCallType($event->getBacktrace())->value; // Return a placeholder value
|
||||
}
|
||||
|
||||
if($backtrace['function'] === '{closure}')
|
||||
if ($backtrace['function'] === '{closure}')
|
||||
{
|
||||
if(isset($backtrace['file']))
|
||||
if (isset($backtrace['file']))
|
||||
{
|
||||
if($ansi)
|
||||
if ($ansi)
|
||||
{
|
||||
return "\033[1;37m" . basename($backtrace['file']) . "\033[0m" . CallType::STATIC_CALL->value . CallType::LAMBDA_CALL->value;
|
||||
}
|
||||
|
@ -194,25 +192,24 @@
|
|||
return basename($backtrace['file']) . CallType::STATIC_CALL->value . CallType::LAMBDA_CALL->value;
|
||||
}
|
||||
|
||||
return basename($backtrace['file']) . CallType::STATIC_CALL->value . CallType::LAMBDA_CALL->value;
|
||||
return self::determineCallType($event->getBacktrace())->value . CallType::STATIC_CALL->value . CallType::LAMBDA_CALL->value; // Adjusted to handle missing 'file'
|
||||
}
|
||||
|
||||
if($backtrace['function'] === 'eval')
|
||||
if ($backtrace['function'] === 'eval')
|
||||
{
|
||||
if(isset($backtrace['file']))
|
||||
if (isset($backtrace['file']))
|
||||
{
|
||||
if($ansi)
|
||||
if ($ansi)
|
||||
{
|
||||
return "\033[1;37m" . basename($backtrace['file']) . "\033[0m" . CallType::STATIC_CALL->value . CallType::EVAL_CALL->value;
|
||||
}
|
||||
|
||||
return basename($backtrace['file']) . CallType::STATIC_CALL->value . CallType::EVAL_CALL->value;
|
||||
}
|
||||
|
||||
return basename($backtrace['file']) . CallType::STATIC_CALL->value . CallType::EVAL_CALL->value;
|
||||
return self::determineCallType($event->getBacktrace())->value . CallType::STATIC_CALL->value . CallType::EVAL_CALL->value; // Adjusted to handle missing 'file'
|
||||
}
|
||||
|
||||
if($ansi)
|
||||
if ($ansi)
|
||||
{
|
||||
$function = sprintf("\033[1;37m%s\033[0m", $backtrace['function']);
|
||||
}
|
||||
|
@ -223,9 +220,9 @@
|
|||
|
||||
$class = null;
|
||||
|
||||
if(isset($backtrace["class"]))
|
||||
if (isset($backtrace["class"]))
|
||||
{
|
||||
if($ansi)
|
||||
if ($ansi)
|
||||
{
|
||||
$class = sprintf("\033[1;37m%s\033[0m", $backtrace['class']);
|
||||
}
|
||||
|
@ -235,7 +232,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
if($class === null)
|
||||
if ($class === null)
|
||||
{
|
||||
return $function . CallType::FUNCTION_CALL->value;
|
||||
}
|
||||
|
@ -244,6 +241,32 @@
|
|||
return "{$class}{$type->value}{$function}" . CallType::FUNCTION_CALL->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the type of call based on the provided backtrace.
|
||||
*
|
||||
* @param array $backtrace The backtrace information of the calling code.
|
||||
* @return CallType The type of call detected.
|
||||
*/
|
||||
private static function determineCallType(array $backtrace): CallType
|
||||
{
|
||||
if(BacktraceParser::fromErrorHandler($backtrace))
|
||||
{
|
||||
return CallType::ERROR_HANDLER;
|
||||
}
|
||||
|
||||
if(BacktraceParser::fromExceptionHandler($backtrace))
|
||||
{
|
||||
return CallType::EXCEPTION_HANDLER;
|
||||
}
|
||||
|
||||
if(BacktraceParser::fromShutdownHandler($backtrace))
|
||||
{
|
||||
return CallType::SHUTDOWN_HANDLER;
|
||||
}
|
||||
|
||||
return CallType::UNKNOWN_FILE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an exception object to an array representation.
|
||||
*
|
||||
|
|
|
@ -38,4 +38,32 @@
|
|||
* @var string EVAL_CALL
|
||||
*/
|
||||
case EVAL_CALL = 'eval()';
|
||||
|
||||
/**
|
||||
* Represents an unknown file.
|
||||
*
|
||||
* @var string UNKNOWN_FILE
|
||||
*/
|
||||
case UNKNOWN_FILE = '?';
|
||||
|
||||
/**
|
||||
* Represents a runtime error handler.
|
||||
*
|
||||
* @var string ERROR_HANDLER
|
||||
*/
|
||||
case ERROR_HANDLER = 'RUNTIME_ERROR';
|
||||
|
||||
/**
|
||||
* Represents a shutdown handler event.
|
||||
*
|
||||
* @var string SHUTDOWN_HANDLER
|
||||
*/
|
||||
case SHUTDOWN_HANDLER = 'SHUTDOWN_ERROR';
|
||||
|
||||
/**
|
||||
* Represents an exception handler for runtime exceptions.
|
||||
*
|
||||
* @var string EXCEPTION_HANDLER
|
||||
*/
|
||||
case EXCEPTION_HANDLER = 'RUNTIME_EXCEPTION';
|
||||
}
|
|
@ -24,7 +24,6 @@ class ConsoleLogging implements LogHandlerInterface
|
|||
*/
|
||||
public static function handle(Application $application, Event $event): void
|
||||
{
|
||||
// Check if the application is running in a CLI environment, if not, return
|
||||
if(!Utilities::runningInCli())
|
||||
{
|
||||
return;
|
||||
|
@ -207,10 +206,13 @@ class ConsoleLogging implements LogHandlerInterface
|
|||
{
|
||||
print('Stack Trace:' . PHP_EOL);
|
||||
foreach($trace as $item)
|
||||
{
|
||||
if(isset($item['file']) && isset($item['line']))
|
||||
{
|
||||
print( ' - ' . self::color($item['file'], ConsoleColors::RED) . ':' . $item['line'] . PHP_EOL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($exception->getPrevious() !== null)
|
||||
{
|
||||
|
|
|
@ -88,9 +88,9 @@ class FileLogging implements LogHandlerInterface
|
|||
|
||||
$logging_file = $logging_directory . DIRECTORY_SEPARATOR . Utilities::sanitizeFileName($application->getApplicationName()) . '-' . date('Y-m-d') . '.log';
|
||||
|
||||
if(!file_exists($logging_file))
|
||||
if(!file_exists($logging_file) && !@touch($logging_file))
|
||||
{
|
||||
touch($logging_file);
|
||||
throw new RuntimeException(sprintf("Cannot write to %s due to insufficient permissions", $logging_file));
|
||||
}
|
||||
|
||||
return $logging_file;
|
||||
|
@ -132,10 +132,13 @@ class FileLogging implements LogHandlerInterface
|
|||
{
|
||||
$output .= 'Stack Trace:' . PHP_EOL;
|
||||
foreach($trace as $item)
|
||||
{
|
||||
if(isset($item['file']) && isset($item['line']))
|
||||
{
|
||||
$output .= ' - ' . $item['file'] . ':' . $item['line'] . PHP_EOL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($exception->getPrevious() !== null)
|
||||
{
|
||||
|
|
|
@ -213,77 +213,7 @@
|
|||
*/
|
||||
public static function registerExceptionHandler(): void
|
||||
{
|
||||
|
||||
// Handle uncaught exceptions
|
||||
set_exception_handler(static function(Throwable $exception): void
|
||||
{
|
||||
try
|
||||
{
|
||||
self::error('Runtime', $exception->getMessage(), $exception);
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
// Handle warnings and notices
|
||||
set_error_handler(static function(int $errno, string $errstr, string $errfile = '', int $errline = 0): bool
|
||||
{
|
||||
try
|
||||
{
|
||||
// Convert error to exception for consistent handling
|
||||
$exception = new ErrorException($errstr, 0, $errno, $errfile, $errline);
|
||||
|
||||
// Handle different error types
|
||||
switch ($errno)
|
||||
{
|
||||
case E_ERROR:
|
||||
case E_PARSE:
|
||||
case E_CORE_ERROR:
|
||||
case E_COMPILE_ERROR:
|
||||
case E_USER_ERROR:
|
||||
self::error('Runtime', $errstr, $exception);
|
||||
break;
|
||||
|
||||
case E_USER_DEPRECATED:
|
||||
case E_DEPRECATED:
|
||||
case E_USER_NOTICE:
|
||||
case E_NOTICE:
|
||||
case E_USER_WARNING:
|
||||
case E_WARNING:
|
||||
default:
|
||||
self::warning('Runtime', $errstr, $exception);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return true to prevent PHP's internal error handler
|
||||
return true;
|
||||
});
|
||||
|
||||
// Handle fatal errors
|
||||
register_shutdown_function(static function(): void
|
||||
{
|
||||
$error = error_get_last();
|
||||
|
||||
if ($error !== null && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR]))
|
||||
{
|
||||
try
|
||||
{
|
||||
$exception = new ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line']);
|
||||
self::error('Fatal Error', $error['message'], $exception);
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
Runtime::registerExceptionHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -293,6 +223,6 @@
|
|||
*/
|
||||
public static function unregisterExceptionHandler(): void
|
||||
{
|
||||
set_exception_handler(null);
|
||||
Runtime::unregisterExceptionHandler();
|
||||
}
|
||||
}
|
124
src/LogLib/Runtime.php
Normal file
124
src/LogLib/Runtime.php
Normal file
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
|
||||
namespace LogLib;
|
||||
|
||||
use ErrorException;
|
||||
use Exception;
|
||||
use Throwable;
|
||||
|
||||
class Runtime
|
||||
{
|
||||
/**
|
||||
* Registers an exception handler that logs any uncaught exceptions as errors.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function registerExceptionHandler(): void
|
||||
{
|
||||
set_exception_handler([__CLASS__, 'exceptionHandler']);
|
||||
set_error_handler([__CLASS__, 'errorHandler']);
|
||||
register_shutdown_function([__CLASS__, 'shutdownHandler']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles uncaught exceptions by logging them with a fatal error level.
|
||||
*
|
||||
* @param Throwable $throwable The exception or error that was thrown.
|
||||
* @return void
|
||||
*/
|
||||
public static function exceptionHandler(Throwable $throwable): void
|
||||
{
|
||||
try
|
||||
{
|
||||
Log::Fatal('Runtime', $throwable->getMessage(), $throwable);
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles PHP errors by converting them to exceptions and logging appropriately.
|
||||
*
|
||||
* @param int $errno The level of the error raised.
|
||||
* @param string $errstr The error message.
|
||||
* @param string $errfile The filename that the error was raised in.
|
||||
* @param int $errline The line number the error was raised at.
|
||||
* @return bool True to prevent PHP's internal error handler from being invoked.
|
||||
*/
|
||||
public static function errorHandler(int $errno, string $errstr, string $errfile = '', int $errline = 0): bool
|
||||
{
|
||||
try
|
||||
{
|
||||
// Convert error to exception for consistent handling
|
||||
$exception = new ErrorException($errstr, 0, $errno, $errfile, $errline);
|
||||
|
||||
// Handle different error types
|
||||
switch ($errno)
|
||||
{
|
||||
case E_ERROR:
|
||||
case E_PARSE:
|
||||
case E_CORE_ERROR:
|
||||
case E_COMPILE_ERROR:
|
||||
case E_USER_ERROR:
|
||||
Log::error('Runtime', $errstr, $exception);
|
||||
break;
|
||||
|
||||
case E_USER_DEPRECATED:
|
||||
case E_DEPRECATED:
|
||||
case E_USER_NOTICE:
|
||||
case E_NOTICE:
|
||||
case E_USER_WARNING:
|
||||
case E_WARNING:
|
||||
default:
|
||||
Log::warning('Runtime', $errstr, $exception);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return true to prevent PHP's internal error handler
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles script shutdown by checking for any fatal errors and logging them.
|
||||
*
|
||||
* This method is designed to be registered with the `register_shutdown_function`,
|
||||
* and it inspects the last error that occurred using `error_get_last`. If a fatal
|
||||
* error is detected, it logs the error details.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function shutdownHandler(): void
|
||||
{
|
||||
$error = error_get_last();
|
||||
|
||||
if ($error !== null && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR]))
|
||||
{
|
||||
try
|
||||
{
|
||||
$exception = new ErrorException($error['message'], 0, $error['type'], $error['file'], $error['line']);
|
||||
Log::error('Fatal Error', $error['message'], $exception);
|
||||
}
|
||||
catch(Exception)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters the currently registered exception handler.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function unregisterExceptionHandler(): void
|
||||
{
|
||||
set_exception_handler(null);
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue