diff --git a/src/LogLib/Classes/Utilities.php b/src/LogLib/Classes/Utilities.php index 3cced33..a9aeeac 100644 --- a/src/LogLib/Classes/Utilities.php +++ b/src/LogLib/Classes/Utilities.php @@ -80,11 +80,9 @@ * - SILENT (0) * If no log level is configured or the configured level is not recognized, the INFO level (4) will be returned by default. */ - public static function getLogLevel(): LogLevel + private static function parseLogLevel(string $logLevel): LogLevel { - $args = Parse::getArguments(); - - switch(strtolower(($args['log'] ?? $args['log-level'] ?? (getenv('LOG_LEVEL') ?: 'info') ?? 'info'))) + switch(strtolower($logLevel)) { case LogLevel::DEBUG: case 'debug': @@ -154,13 +152,12 @@ * Returns a string representation of the backtrace for the given event. * * @param Event $event The event object for which to generate the backtrace string. - * @param bool $ansi Determines whether the output should include ANSI escape codes for colored output. Default is false. * @return string|null A string representation of the backtrace for the event, or null if the event has no backtrace. * The output format is: ClassName::methodName() or functionName() depending on the type of call. * 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=false): ?string + public static function getTraceString(Event $event): ?string { if($event->getBacktrace() === null || count($event->getBacktrace()) === 0) { @@ -174,7 +171,7 @@ { if(isset($backtrace['file'])) { - return ($ansi ? "\033[1;37m" : '') . basename($backtrace['file']) . ($ansi ? "\033[0m" : ''); + return "\033[1;37m" . basename($backtrace['file']) . "\033[0m"; } return basename($backtrace['file']); @@ -184,7 +181,7 @@ { if(isset($backtrace['file'])) { - return ($ansi ? "\033[1;37m" : '') . basename($backtrace['file']) . ($ansi ? "\033[0m" : '') . CallType::STATIC_CALL->value . CallType::LAMBDA_CALL->value; + return "\033[1;37m" . basename($backtrace['file']) . "\033[0m" . CallType::STATIC_CALL->value . CallType::LAMBDA_CALL->value; } return basename($backtrace['file']) . CallType::STATIC_CALL->value . CallType::LAMBDA_CALL->value; @@ -194,26 +191,18 @@ { if(isset($backtrace['file'])) { - return ($ansi ? "\033[1;37m" : '') . basename($backtrace['file']) . ($ansi ? "\033[0m" : '') . CallType::STATIC_CALL->value . CallType::EVAL_CALL->value; + 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; } - if($ansi) - { - $function = sprintf("\033[1;37m%s\033[0m", $backtrace['function']); - $class = null; + $function = sprintf("\033[1;37m%s\033[0m", $backtrace['function']); + $class = null; - if(isset($backtrace["class"])) - { - $class = sprintf("\033[1;37m%s\033[0m", $backtrace['class']); - } - } - else + if(isset($backtrace["class"])) { - $function = $backtrace['function']; - $class = $backtrace['class'] ?? null; + $class = sprintf("\033[1;37m%s\033[0m", $backtrace['class']); } if($class === null) @@ -253,4 +242,94 @@ ]; } + public static function getLogDirectory(): string + { + $args = Parse::getArguments(); + $log_directory = ($args['log-directory'] ?? getenv('LOG_DIRECTORY') ?? null); + + if($log_directory === null) + { + return sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'logs'; + } + + return $log_directory; + } + + + public static function getConsoleLoggingEnabled(): bool + { + if(isset(Parse::getArguments()['log-level'])) + { + return true; + } + + if(self::runningInCli()) + { + return true; + } + + if(getenv('CLI_LOGGING') === 'true' || getenv('CLI_LOGGING') === '1') + { + return true; + } + + return false; + } + + public static function getConsoleLoggingLevel(): LogLevel + { + return self::parseLogLevel(($args['log'] ?? $args['log-level'] ?? (getenv('LOG_LEVEL') ?: 'info') ?? 'info')); + } + + public static function getFileLoggingEnabled(): bool + { + if(isset(Parse::getArguments()['log-directory'])) + { + return true; + } + + if(getenv('LOG_DIRECTORY') !== null) + { + return true; + } + + if(!is_writable(self::getLogDirectory())) + { + return false; + } + + return false; + } + + public static function getFileLoggingLevel(): LogLevel + { + if(getenv('FILE_LOG_LEVEL') !== null || isset(Parse::getArguments()['file-log-level'])) + { + return self::parseLogLevel(getenv('FILE_LOG_LEVEL')); + } + + return LogLevel::WARNING; + } + + public static function getFileLoggingDirectory(): string + { + $args = Parse::getArguments(); + $log_directory = ($args['log-directory'] ?? getenv('LOG_DIRECTORY') ?? null); + + if($log_directory === null) + { + return sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'logs'; + } + + return $log_directory; + } + + public static function sanitizeFileName(string $name): string + { + // Replace spaces with hyphens + $name = str_replace(' ', '-', $name); + + // Remove illegal characters and replace escapable characters with underscores + return preg_replace('/[\/:*?"<>|.]/', '_', $name); + } } \ No newline at end of file diff --git a/src/LogLib/Log.php b/src/LogLib/Log.php index efc972f..fb78a23 100644 --- a/src/LogLib/Log.php +++ b/src/LogLib/Log.php @@ -5,36 +5,29 @@ namespace LogLib; use InvalidArgumentException; - use LogLib\Classes\Console; use LogLib\Classes\Utilities; - use LogLib\Classes\Validate; use LogLib\Enums\LogLevel; + use LogLib\Exceptions\LoggingException; + use LogLib\Objects\Application; use LogLib\Objects\Event; - use LogLib\Objects\Options; - use LogLib\Objects\RuntimeOptions; use Throwable; class Log { /** - * @var Options[]|null + * @var Application[]|null */ private static $applications; - /** - * @var RuntimeOptions|null - */ - private static $runtime_options; - /** * Registers a new application logger * - * @param Options $options The options for the application + * @param Application $application The options for the application * @return bool */ - public static function register(Options $options): bool + public static function register(Application $application): bool { - if(self::isRegistered($options->getApplicationName())) + if(self::isRegistered($application->getApplicationName())) { return false; } @@ -44,7 +37,7 @@ self::$applications = []; } - self::$applications[$options->getApplicationName()] = $options; + self::$applications[$application->getApplicationName()] = $application; return true; } @@ -79,33 +72,27 @@ } /** - * @param string $application - * @return Options + * Retrieves the application options. If the application is not registered, it optionally creates and registers a new one. + * + * @param string $application The name of the application. + * @param bool $create (Optional) Whether to create the application if it is not registered. Default is true. + * @return Application The options for the specified application. */ - public static function getApplication(string $application): Options + public static function getApplication(string $application, bool $create=true): Application { if(!self::isRegistered($application)) { - throw new InvalidArgumentException("The application '$application' is not registered"); + if(!$create) + { + throw new InvalidArgumentException("The application '$application' is not registered"); + } + + self::register(new Application($application)); } return self::$applications[$application]; } - /** - * @param string $application_name The name of the application - * @return Options The options for the application - */ - public static function getOptions(string $application_name): Options - { - if(!self::isRegistered($application_name)) - { - self::register(new Options($application_name)); - } - - return self::$applications[$application_name]; - } - /** * Logs a message with a specified application name, level, optional message, and optional throwable. * @@ -114,22 +101,11 @@ * @param string|null $message The message of the log event * @param Throwable|null $throwable The exception that was thrown, if any * @return void + * @throws LoggingException */ private static function log(?string $application_name, LogLevel $level=LogLevel::INFO, ?string $message=null, ?Throwable $throwable=null): void { - if($application_name === null) - { - $options = self::getRuntimeOptions(); - } - else - { - $options = self::getOptions($application_name); - } - - if(!Validate::checkLevelType($level, $options->getLoglevel())) - { - return; - } + $application = self::getApplication($application_name); if($message === null) { @@ -143,9 +119,14 @@ $event->setBacktrace(Utilities::getBacktrace()); } - if($options->isConsoleOutput()) + if($application->isConsoleLoggingEnabled()) { - Console::out($options, $event); + $application->getConsoleLoggingHandler()::handle($application, $event); + } + + if($application->isFileLoggingEnabled()) + { + $application->getFileLoggingHandler()::handle($application, $event); } } @@ -153,6 +134,7 @@ * @param string $application_name The name of the application * @param string $message The message of the event * @return void + * @throws LoggingException */ public static function info(string $application_name, string $message): void { @@ -163,6 +145,7 @@ * @param string $application_name The name of the application * @param string $message The message of the event * @return void + * @throws LoggingException */ public static function verbose(string $application_name, string $message): void { @@ -173,6 +156,7 @@ * @param string $application_name The name of the application * @param string $message The message of the event * @return void + * @throws LoggingException */ public static function debug(string $application_name, string $message): void { @@ -186,6 +170,7 @@ * @param string $message The warning message to log. * @param Throwable|null $throwable (Optional) The throwable object associated with the warning. * @return void + * @throws LoggingException */ public static function warning(string $application_name, string $message, ?Throwable $throwable=null): void { @@ -199,7 +184,8 @@ * @param string $message The error message. * @param Throwable|null $throwable The optional throwable object associated with the error. * @return void - **/ + * @throws LoggingException + */ public static function error(string $application_name, string $message, ?Throwable $throwable=null): void { self::log($application_name, LogLevel::ERROR, $message, $throwable); @@ -212,6 +198,7 @@ * @param string $message The fatal message to log. * @param Throwable|null $throwable (Optional) The throwable object associated with the fatal message. * @return void + * @throws LoggingException */ public static function fatal(string $application_name, string $message, ?Throwable $throwable=null): void { @@ -227,7 +214,14 @@ { set_exception_handler(static function(Throwable $throwable) { - self::error('Runtime', $throwable->getMessage(), $throwable); + try + { + self::error('Runtime', $throwable->getMessage(), $throwable); + } + catch(LoggingException) + { + return; + } }); } @@ -241,19 +235,4 @@ set_exception_handler(null); } - /** - * Gets the runtime options. - * - * @return RuntimeOptions The runtime options. - */ - public static function getRuntimeOptions(): RuntimeOptions - { - if(self::$runtime_options === null) - { - self::$runtime_options = new RuntimeOptions(); - } - - return self::$runtime_options; - } - } \ No newline at end of file