diff --git a/project.json b/project.json new file mode 100644 index 0000000..393e3d2 --- /dev/null +++ b/project.json @@ -0,0 +1,44 @@ +{ + "project": { + "compiler": { + "extension": "php", + "minimum_version": "8.0", + "maximum_version": "8.2" + }, + "options": [] + }, + "assembly": { + "name": "LogLib", + "package": "net.nosial.loglib", + "company": "Nosial", + "copyright": "Copyright (c) 2022 Nosial", + "version": "1.0.0", + "uuid": "de1deca6-7b65-11ed-a8b0-a172264634d8" + }, + "build": { + "source_path": "src", + "default_configuration": "release", + "define_constants": { + "ASSEMBLY_NAME": "%ASSEMBLY.NAME%", + "ASSEMBLY_PACKAGE": "%ASSEMBLY.PACKAGE%", + "ASSEMBLY_VERSION": "%ASSEMBLY.VERSION%", + "ASSEMBLY_UID": "%ASSEMBLY.UID%" + }, + "configurations": [ + { + "name": "debug", + "output_path": "build/debug", + "define_constants": { + "DEBUG": "1" + } + }, + { + "name": "release", + "output_path": "build/release", + "define_constants": { + "DEBUG": "0" + } + } + ] + } +} \ No newline at end of file diff --git a/src/LogLib/Abstracts/CallType.php b/src/LogLib/Abstracts/CallType.php new file mode 100644 index 0000000..f3e5345 --- /dev/null +++ b/src/LogLib/Abstracts/CallType.php @@ -0,0 +1,12 @@ +'; + + const StaticCall = '::'; + + const FunctionCall = ' '; + } \ No newline at end of file diff --git a/src/LogLib/Abstracts/ConsoleColors.php b/src/LogLib/Abstracts/ConsoleColors.php new file mode 100644 index 0000000..c5db91f --- /dev/null +++ b/src/LogLib/Abstracts/ConsoleColors.php @@ -0,0 +1,43 @@ +Level) + { + case LevelType::Debug: + $color = ConsoleColors::LightPurple; + break; + case LevelType::Verbose: + $color = ConsoleColors::LightCyan; + break; + case LevelType::Info: + $color = ConsoleColors::White; + break; + case LevelType::Warning: + $color = ConsoleColors::Yellow; + break; + case LevelType::Fatal: + $color = ConsoleColors::Red; + break; + case LevelType::Error: + $color = ConsoleColors::LightRed; + break; + } + + if($color == null) + return Utilities::levelToString($text); + + return self::color(Utilities::levelToString($text), $color); + } + + /** + * Returns the formatted backtrace + * + * @param Event $event + * @return string|null + */ + private static function parseBacktrace(Event $event): ?string + { + $backtrace = null; + if($event->Backtrace !== null && count($event->Backtrace) > 0) + { + foreach($event->Backtrace as $item) + { + if($item->Class !== 'LogLib\\Log') + { + $backtrace = $item; + break; + } + } + } + + $backtrace_output = null; + if($backtrace !== null) + { + if($backtrace->Class !== null) + { + $backtrace_output = $backtrace->Class . $backtrace->Type . $backtrace->Function . '()'; + } + else + { + $backtrace_output = $backtrace->Function . '()'; + } + + if($backtrace->Line !== null) + $backtrace_output .= ':' . $backtrace->Line; + } + + return $backtrace_output; + } + + /** + * Regular console output for the event object + * + * @param Options $options + * @param Event $event + * @return void + */ + public static function out(Options $options, Event $event): void + { + // If the current level is verbose or higher, then we need to output the backtrace + if(Validate::checkLevelType(LevelType::Verbose, $options->getOutputLevel())) + { + $backtrace_output = self::parseBacktrace($event); + + if($options->isConsoleAnsiColors()) + { + print(sprintf( + "%s [%s] [%s] (%s) - %s" . PHP_EOL, + $event->getTimestamp(), + self::formatAppColor($options->getApplicationName()), + self::colorize($event, $event->Level), + $backtrace_output !== null ? $backtrace_output : 'λ', + $event->Message + )); + } + else + { + print(sprintf( + "%s [%s] [%s] - %s - %s" . PHP_EOL, + $event->getTimestamp(), $options->getApplicationName(), $event->Level, + $backtrace_output !== null ? $backtrace_output : 'lambda', + $event->Message + )); + } + + if($event->Exception !== null) + self::outException($event->Exception); + } + elseif(!Validate::checkLevelType(LevelType::Fatal, $options->getOutputLevel())) + { + if($options->isConsoleAnsiColors()) + { + print(sprintf( + "%s [%s] [%s] - %s" . PHP_EOL, + $event->getTimestamp(), + self::formatAppColor($options->getApplicationName()), + self::colorize($event, $event->Level), + $event->Message + )); + } + else + { + print(sprintf( + "%s [%s] [%s] - %s" . PHP_EOL, + $event->getTimestamp(), $options->getApplicationName(), $event->Level, + $event->Message + )); + } + + if($event->Exception !== null) + self::outException($event->Exception); + } + + + } + + /** + * Prints out the exception details + * + * @param array $exception + * @return void + */ + private static function outException(array $exception): void + { + $trace_header = self::color($exception['file'] . ':' . $exception['line'], ConsoleColors::Purple); + $trace_error = self::color('error: ', ConsoleColors::Red); + print($trace_header . ' ' . $trace_error . $exception['message'] . PHP_EOL); + print(sprintf('Error code: %s', $exception['code']) . PHP_EOL); + $trace = $exception['trace']; + 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['previous'] !== null) + { + print('Previous Exception:' . PHP_EOL); + self::outException($exception['previous']); + } + } + } \ No newline at end of file diff --git a/src/LogLib/Classes/Utilities.php b/src/LogLib/Classes/Utilities.php new file mode 100644 index 0000000..c8f7a22 --- /dev/null +++ b/src/LogLib/Classes/Utilities.php @@ -0,0 +1,74 @@ + $e->getMessage(), + 'code' => $e->getCode(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'trace' => $e->getTrace(), + ]; + + if($e->getPrevious() !== null) + { + $results['previous'] = self::exceptionToArray($e->getPrevious()); + } + + return $results; + } + + /** + * Returns the current level type as a string + * + * @param int $level + * @return string + */ + public static function levelToString(int $level): string + { + return match ($level) + { + LevelType::Debug => 'DBG', + LevelType::Verbose => 'VRB', + LevelType::Info => 'INF', + LevelType::Warning => 'WRN', + LevelType::Fatal => 'CRT', + LevelType::Error => 'ERR', + default => 'UNK', + }; + } + + } \ No newline at end of file diff --git a/src/LogLib/Classes/Validate.php b/src/LogLib/Classes/Validate.php new file mode 100644 index 0000000..9e1c134 --- /dev/null +++ b/src/LogLib/Classes/Validate.php @@ -0,0 +1,109 @@ +" is returned. + * If a static method call, "::" is returned. If a function call, + * nothing is returned. + * + * @see CallType + * @var string|null + */ + public $Type; + + /** + * If inside a function, this lists the functions arguments. If inside + * an included file, this lists the included file name(s). + * + * @var array|null + */ + public $Args; + + /** + * Returns an array representation of the backtrace + * + * @return array + */ + public function toArray(): array + { + return [ + 'function' => $this->Function, + 'line' => $this->Line, + 'file' => $this->File, + 'class' => $this->Class, + 'type' => $this->Type, + 'args' => $this->Args + ]; + } + + /** + * Constructs a new DebugBacktrace object from an array representation + * + * @param array $array + * @return Backtrace + */ + public static function fromArray(array $array): Backtrace + { + $backtrace = new Backtrace(); + $backtrace->Function = ($array['function'] ?? null); + $backtrace->Line = ($array['line'] ?? null); + $backtrace->File = ($array['file'] ?? null); + $backtrace->Class = ($array['class'] ?? null); + $backtrace->Type = ($array['type'] ?? null); + $backtrace->Args = ($array['args'] ?? null); + return $backtrace; + } + } \ No newline at end of file diff --git a/src/LogLib/Objects/Event.php b/src/LogLib/Objects/Event.php new file mode 100644 index 0000000..ea24c1a --- /dev/null +++ b/src/LogLib/Objects/Event.php @@ -0,0 +1,106 @@ +Timestamp = date('Y-m-dTH:i:s.v') . (date('p') == 'Z' ? 'Z' : 'L'); + } + + /** + * Sets an exception to the event + * + * @param Throwable $e + * @return void + */ + public function setException(Throwable $e): void + { + $this->Exception = Utilities::exceptionToArray($e); + } + + /** + * Returns an array representation of the event + * + * @return array + */ + public function toArray(): array + { + return [ + 'level' => ($this->Level ?? null), + 'timestamp' => ($this->Timestamp ?? null), + 'backtrace' => $this->Backtrace, + 'exception' => $this->Exception, + 'message' => ($this->Message ?? null) + ]; + } + + /** + * Constructs a new event from an array representation + * + * @param array $data + * @return Event + */ + public static function fromArray(array $data): Event + { + $event = new Event(); + $event->Level = ($data['level'] ?? null); + $event->Timestamp = ($data['timestamp'] ?? null); + $event->Backtrace = ($data['backtrace'] ?? null); + $event->Exception = ($data['exception'] ?? null); + $event->Message = ($data['message'] ?? null); + return $event; + } + + /** + * @return string + */ + public function getTimestamp(): string + { + return $this->Timestamp; + } + + } \ No newline at end of file diff --git a/src/LogLib/Objects/Options.php b/src/LogLib/Objects/Options.php new file mode 100644 index 0000000..a2acc05 --- /dev/null +++ b/src/LogLib/Objects/Options.php @@ -0,0 +1,252 @@ +ApplicationName = $application_name; + $this->WriteToPackageData = true; + $this->SplitFiles = true; + $this->MaxFileSize = 1073741824; // 1GB + $this->OutputLevel = LevelType::Info; + $this->ConsoleOutput = true; + $this->ConsoleAnsiColors = true; + $this->Handlers = []; + } + + /** + * @return string|null + */ + public function getPackageName(): ?string + { + return $this->PackageName; + } + + /** + * @param string|null $PackageName + */ + public function setPackageName(?string $PackageName): void + { + $this->PackageName = $PackageName; + } + + /** + * @return string + */ + public function getOutputLevel(): string + { + return $this->OutputLevel; + } + + /** + * @param string $OutputLevel + */ + public function setOutputLevel(string $OutputLevel): void + { + if(!in_array($OutputLevel, LevelType::All)) + throw new InvalidArgumentException("Invalid output level provided"); + $this->OutputLevel = $OutputLevel; + } + + /** + * @return bool + */ + public function isConsoleOutput(): bool + { + return $this->ConsoleOutput; + } + + /** + * @param bool $ConsoleOutput + */ + public function setConsoleOutput(bool $ConsoleOutput): void + { + $this->ConsoleOutput = $ConsoleOutput; + } + + /** + * @return bool + */ + public function isConsoleAnsiColors(): bool + { + return $this->ConsoleAnsiColors; + } + + /** + * @param bool $ConsoleAnsiColors + */ + public function setConsoleAnsiColors(bool $ConsoleAnsiColors): void + { + $this->ConsoleAnsiColors = $ConsoleAnsiColors; + } + + /** + * @return bool + */ + public function isWriteToPackageData(): bool + { + return $this->WriteToPackageData; + } + + /** + * @param bool $WriteToPackageData + */ + public function setWriteToPackageData(bool $WriteToPackageData): void + { + $this->WriteToPackageData = $WriteToPackageData; + } + + /** + * @return bool + */ + public function isSplitFiles(): bool + { + return $this->SplitFiles; + } + + /** + * @param bool $SplitFiles + */ + public function setSplitFiles(bool $SplitFiles): void + { + $this->SplitFiles = $SplitFiles; + } + + /** + * @return int + */ + public function getMaxFileSize(): int + { + return $this->MaxFileSize; + } + + /** + * @param int $MaxFileSize + */ + public function setMaxFileSize(int $MaxFileSize): void + { + if($MaxFileSize < 1) + throw new InvalidArgumentException("Max file size must be greater than 0"); + + $this->MaxFileSize = $MaxFileSize; + } + + /** + * @return HandlerInterface[] + */ + public function getHandlers(): array + { + return $this->Handlers; + } + + /** + * @param string $level + * @param HandlerInterface $handler + */ + public function setHandler(string $level, HandlerInterface $handler): void + { + if(!Validate::LevelType($level)) + throw new InvalidArgumentException("Invalid level provided"); + + if(!isset($this->Handlers[$level])) + $this->Handlers[$level] = []; + + $this->Handlers[$level][] = $handler; + } + + /** + * @return void + */ + public function clearHandlers(): void + { + $this->Handlers = []; + } + + /** + * @return string + */ + public function getApplicationName(): string + { + return $this->ApplicationName; + } + } \ No newline at end of file