diff --git a/src/LogLib/Handlers/ConsoleLogging.php b/src/LogLib/Handlers/ConsoleLogging.php new file mode 100644 index 0000000..4266ad7 --- /dev/null +++ b/src/LogLib/Handlers/ConsoleLogging.php @@ -0,0 +1,225 @@ +getLevel(), $application->getConsoleLoggingLevel())) + { + return; + } + + if(Validate::checkLevelType(LogLevel::DEBUG, $application->getConsoleLoggingLevel())) + { + $backtrace_output = Utilities::getTraceString($event); + + print(sprintf("[%s] [%s] [%s] %s %s" . PHP_EOL, + self::getTimestamp(), self::formatAppColor($application->getApplicationName()), self::colorize($event), + $backtrace_output, $event->getMessage() + )); + + if($event->getException() !== null) + { + self::outException($event->getException()); + } + + return; + } + + if(Validate::checkLevelType(LogLevel::VERBOSE, $application->getConsoleLoggingLevel())) + { + $backtrace_output = Utilities::getTraceString($event); + + print(sprintf("[%s] [%s] %s %s" . PHP_EOL, + self::formatAppColor($application->getApplicationName()), + self::colorize($event), + $backtrace_output, $event->getMessage() + )); + + if($event->getException() !== null) + { + self::outException($event->getException()); + } + + return; + } + + print(sprintf("[%s] [%s] %s" . PHP_EOL, + self::formatAppColor($application->getApplicationName()), + self::colorize($event), + $event->getMessage() + )); + } + + /** + * @inheritDoc + */ + public static function getType(): LogHandlerType + { + return LogHandlerType::CONSOLE; + } + + /** + * Formats the application name with a color for the console + * + * @param string $application_name The application name + * @return string The formatted application name + * @throws LoggingException If unable to generate a random color for the application + */ + private static function formatAppColor(string $application_name): string + { + if(!isset(self::$application_colors[$application_name])) + { + $colors = ConsoleColors::ALL; + + try + { + $color = $colors[random_int(0, count($colors) - 1)]; + } + catch (Exception $e) + { + throw new LoggingException(sprintf('Unable to generate random color for application "%s"', $application_name), $e->getCode(), $e); + } + + self::$application_colors[$application_name] = $color; + } + + return self::color($application_name, self::$application_colors[$application_name]); + } + + /** + * Applies a specified color to the given text, using ANSI escape sequences. + * + * @param string $text The text to apply the color to. + * @param ConsoleColors $color The ANSI color code to apply to the text. + * @return string The text with the specified color applied. + */ + private static function color(string $text, ConsoleColors $color): string + { + return "\033[" . $color->value . "m" . $text . "\033[0m"; + } + + /** + * Colorizes the log message based on the event level using ANSI escape sequences. + * + * @param Event $event The log event to colorize. + * @return string The colorized log message. + */ + private static function colorize(Event $event): string + { + $color = match($event->getLevel()) + { + LogLevel::DEBUG => ConsoleColors::LIGHT_PURPLE, + LogLevel::VERBOSE => ConsoleColors::LIGHT_CYAN, + LogLevel::INFO => ConsoleColors::WHITE, + LogLevel::WARNING => ConsoleColors::YELLOW, + LogLevel::FATAL => ConsoleColors::RED, + LogLevel::ERROR => ConsoleColors::LIGHT_RED, + default => null, + }; + + if($color === null) + { + return Utilities::levelToString($event->getLevel()); + } + + return self::color(Utilities::levelToString($event->getLevel()), $color); + } + + /** + * Retrieves the current timestamp as a formatted string. + * + * @return string The current timestamp. + */ + private static function getTimestamp(): string + { + $tick_time = (string)microtime(true); + + if(!is_null(self::$largest_tick_length) && strlen($tick_time) > (int)self::$largest_tick_length) + { + self::$largest_tick_length = strlen($tick_time); + } + + if(strlen($tick_time) < self::$largest_tick_length) + { + $tick_time = str_pad($tick_time, (strlen($tick_time) + (self::$largest_tick_length - strlen($tick_time)))); + } + + $fmt_tick = $tick_time; + if(self::$last_tick_time !== null) + { + $timeDiff = microtime(true) - self::$last_tick_time; + + if ($timeDiff > 1.0) + { + $fmt_tick = self::color($tick_time, ConsoleColors::LIGHT_RED); + } + elseif ($timeDiff > 0.5) + { + $fmt_tick = self::color($tick_time, ConsoleColors::YELLOW); + } + } + + self::$last_tick_time = $tick_time; + return $fmt_tick; + } + + /** + * Prints information about the given exception, including the error message, error code, + * and stack trace. + * + * @param Throwable|null $exception The exception to print information about. + * @return void + */ + private static function outException(?Throwable $exception=null): void + { + if($exception === null) + { + return; + } + + $trace_header = self::color($exception->getFile() . ':' . $exception->getLine(), ConsoleColors::PURPLE); + $trace_error = self::color('error: ', ConsoleColors::RED); + + print($trace_header . ' ' . $trace_error . $exception->getMessage() . PHP_EOL); + print(sprintf('Error code: %s', $exception->getCode()) . PHP_EOL); + $trace = $exception->getTrace(); + + if(count($trace) > 1) + { + print('Stack Trace:' . PHP_EOL); + foreach($trace as $item) + { + print( ' - ' . self::color($item['file'], ConsoleColors::RED) . ':' . $item['line'] . PHP_EOL); + } + } + + if($exception->getPrevious() !== null) + { + print('Previous Exception:' . PHP_EOL); + self::outException($exception->getPrevious()); + } + } +} \ No newline at end of file