diff options
| author | Asko Nõmm <asko@nth.ee> | 2024-11-10 22:28:03 +0200 |
|---|---|---|
| committer | Asko Nõmm <asko@nth.ee> | 2024-11-10 22:28:03 +0200 |
| commit | 12b6c4b3008c2df545c537943d4e38323cfc174e (patch) | |
| tree | 11e427a794832ac73657da675f3d79949443a3f6 /src | |
Initial commit
Diffstat (limited to 'src')
| -rw-r--r-- | src/Driver.php | 11 | ||||
| -rw-r--r-- | src/Drivers/FileSystemDriver.php | 53 | ||||
| -rw-r--r-- | src/Drivers/OutputDriver.php | 19 | ||||
| -rw-r--r-- | src/Format.php | 135 | ||||
| -rw-r--r-- | src/Level.php | 18 | ||||
| -rw-r--r-- | src/Loggr.php | 151 | ||||
| -rw-r--r-- | src/Message.php | 15 |
7 files changed, 402 insertions, 0 deletions
diff --git a/src/Driver.php b/src/Driver.php new file mode 100644 index 0000000..2bc6cb8 --- /dev/null +++ b/src/Driver.php @@ -0,0 +1,11 @@ +<?php + +namespace Asko\Loggr; + +/** + * @author Asko Nõmm <asko@faultd.com> + */ +interface Driver +{ + public function log(string $serializedMessage): void; +}
\ No newline at end of file diff --git a/src/Drivers/FileSystemDriver.php b/src/Drivers/FileSystemDriver.php new file mode 100644 index 0000000..16c1461 --- /dev/null +++ b/src/Drivers/FileSystemDriver.php @@ -0,0 +1,53 @@ +<?php + +namespace Asko\Loggr\Drivers; + +use Asko\Loggr\Driver; +use DateTime; + +/** + * @author Asko Nõmm <asko@faultd.com> + */ +readonly class FileSystemDriver implements Driver +{ + public function __construct( + private string $directory, + ) {} + + /** + * Logs a serialized message to a file. The log file is created on a daily basis. + * If the required directory or log file does not exist, they are created. + * Ensures that the log file is writable before attempting to write the message. + * + * @param string $serializedMessage The message to be logged, serialized as a string. + * @return void + * @throws \RuntimeException If the log file or directory cannot be created, or if the log file is not writable. + */ + public function log(string $serializedMessage): void + { + // If there's no parent directory, try creating one + if (!is_dir($this->directory)) { + mkdir($this->directory, 0600, true); + } + + $date = new DateTime(); + $file_name = "{$date->format('Y-m-d')}.log"; + $path = $this->directory . DIRECTORY_SEPARATOR . $file_name; + + // If the log file does not exist, try creating one + if (!file_exists($path) && !touch($path)) { + throw new \RuntimeException('Log file could not be created'); + } + + // We managed to get this far, but still can't write to the log file + if (!is_writeable($path)) { + throw new \RuntimeException('Log file is not writeable'); + } + + file_put_contents( + filename: $path, + data: $serializedMessage . PHP_EOL, + flags: FILE_APPEND + ); + } +}
\ No newline at end of file diff --git a/src/Drivers/OutputDriver.php b/src/Drivers/OutputDriver.php new file mode 100644 index 0000000..93c97ba --- /dev/null +++ b/src/Drivers/OutputDriver.php @@ -0,0 +1,19 @@ +<?php + +namespace Asko\Loggr\Drivers; + +use Asko\Loggr\Driver; + +class OutputDriver implements Driver +{ + /** + * Writes the provided serialized message to the standard output stream. + * + * @param string $serializedMessage The message to be logged, in a serialized format. + * @return void + */ + public function log(string $serializedMessage): void + { + file_put_contents('php://output', $serializedMessage); + } +}
\ No newline at end of file diff --git a/src/Format.php b/src/Format.php new file mode 100644 index 0000000..5f914b7 --- /dev/null +++ b/src/Format.php @@ -0,0 +1,135 @@ +<?php + +namespace Asko\Loggr; + +use DateTime; + +/** + * @author Asko Nõmm <asko@faultd.com> + */ +enum Format +{ + case JSON; + case IntelliJ; + case Laravel; + case Symfony; + + /** + * Serializes the given message into a format based on the type of serialization. + * + * @param Message $message The message to be serialized. + * @return string The serialized message. + */ + public function serialize(Message $message): string + { + return match($this) { + self::JSON => $this->serializeJson($message), + self::IntelliJ => $this->serializeIntelliJ($message), + self::Laravel => $this->serializeLaravel($message), + self::Symfony => $this->serializeSymfony($message), + }; + } + + /** + * Serializes the given message into JSON format. + * + * @param Message $message The message to be serialized. + * @return string The JSON-encoded message. + */ + private function serializeJson(Message $message): string + { + return json_encode([ + 'date' => (new DateTime)->format('Y-m-d H:i:s'), + 'level' => $message->level->value, + 'context' => $message->context, + 'trace' => [ + 'file' => $message->trace['file'], + 'line' => $message->trace['line'], + ], + ]); + } + + /** + * Serializes the given message into a Laravel log format. + * + * @param Message $message The message to be serialized. + * @return string The serialized message. + */ + private function serializeLaravel(Message $message): string + { + // Date + $date = (new DateTime)->format('Y-m-d H:i:s'); + $line = "[$date] "; + + // File name and level + $filename = pathinfo($message->trace['file'], PATHINFO_FILENAME); + $line .= "{$filename}.{$message->level->value}: "; + + // Context + if (is_array($message->context) || is_object($message->context)) { + $line .= json_encode($message->context); + } else { + $line .= $message->context; + } + + return $line; + } + + /** + * Serialized the given message into a Symfony log format. + * + * @param Message $message The message object that contains log details. + * @return string The formatted log string. + */ + private function serializeSymfony(Message $message): string + { + // Date + $date = (new DateTime)->format('Y-m-d\TH:i:s.uP'); + $line = "[$date] "; + + // File name and level + $filename = pathinfo($message->trace['file'], PATHINFO_FILENAME); + $line .= "{$filename}.{$message->level->value}: "; + + // Context + if (is_array($message->context) || is_object($message->context)) { + $line .= json_encode($message->context); + } else { + $line .= $message->context; + } + + return $line; + } + + /** + * Serializes the given message into a IntelliJ log format. + * + * @param Message $message The message object that contains log details. + * @return string The formatted log string. + */ + private function serializeIntelliJ(Message $message): string + { + // Date + $date = (new DateTime)->format('Y-m-d H:i:s'); + $line = "{$date} "; + + // Line number + $line .= "[{$message->trace['line']}] "; + + // Level + $line .= "{$message->level->value} "; + + // Filename + $filename = pathinfo($message->trace['file'], PATHINFO_FILENAME); + $line .= "- {$filename} - "; + + // Context + if (is_array($message->context) || is_object($message->context)) { + $line .= json_encode($message->context); + } else { + $line .= $message->context; + } + + return $line; + } +} diff --git a/src/Level.php b/src/Level.php new file mode 100644 index 0000000..5e573bc --- /dev/null +++ b/src/Level.php @@ -0,0 +1,18 @@ +<?php + +namespace Asko\Loggr; + +/** + * @author Asko Nõmm <asko@faultd.com> + */ +enum Level: string +{ + case Emergency = "EMERGENCY"; + case Alert = "ALERT"; + case Critical = "CRITICAL"; + case Error = "ERROR"; + case Warning = "WARNING"; + case Notice = "NOTICE"; + case Info = "INFO"; + case Debug = "DEBUG"; +} diff --git a/src/Loggr.php b/src/Loggr.php new file mode 100644 index 0000000..9b52f1d --- /dev/null +++ b/src/Loggr.php @@ -0,0 +1,151 @@ +<?php + +namespace Asko\Loggr; + +/** + * Loggr is an extendable logging utility class brought to you by the frustration of + * every logging class always having its own unique format, making debugging difficult. + * + * Instead of having its own yet-another-format that no tool supports, Loggr attempts to match + * many already existing formats, allowing you to use whichever you prefer most. + * + * @author Asko Nõmm <asko@faultd.com> + */ +class Loggr +{ + private array $trace; + + public function __construct( + public ?Driver $driver = null, + public ?Format $format = Format::Laravel, + ){} + + /** + * Logs a message at a specified level with optional context. + * + * @param Level $level The severity level of the log message. Defaults to Level::Info. + * @param mixed $context Additional data or context to include with the log message. Optional. + * @return void + * @throws \Exception + */ + private function log(Level $level = Level::Info, mixed $context = null): void + { + if (!$this->driver) { + throw new \Exception("No driver has been set."); + } + + if (!$this->format) { + throw new \Exception("No format has been set."); + } + + $this->driver->log($this->format->serialize(new Message( + level: $level, + trace: $this->trace, + context: $context, + ))); + } + + /** + * Logs an emergency level message with optional context. + * + * @param mixed $context Additional data or context to include with the emergency message. Optional. + * @return void + * @throws \Exception + */ + public function emergency(mixed $context = null): void + { + $this->trace = debug_backtrace()[0]; + $this->log(Level::Emergency, $context); + } + + /** + * Sends an alert-level log message with optional context. + * + * @param mixed $context Additional data or context to include with the log message. Optional. + * @return void + * @throws \Exception + */ + public function alert(mixed $context = null): void + { + $this->trace = debug_backtrace()[0]; + $this->log(Level::Alert, $context); + } + + /** + * Logs a critical level message with optional context. + * + * @param mixed $context Additional data or context to include with the log message. Optional. + * @return void + * @throws \Exception + */ + public function critical( mixed $context = null): void + { + $this->trace = debug_backtrace()[0]; + $this->log(Level::Critical, $context); + } + + /** + * Logs an error message with optional context. + * + * @param mixed $context Additional data or context to include with the error message. Optional. + * @return void + * @throws \Exception + */ + public function error(mixed $context = null): void + { + $this->trace = debug_backtrace()[0]; + $this->log(Level::Error, $context); + } + + /** + * Logs a warning message with the specified context. + * + * @param mixed $context Optional context information to include in the log. + * @return void + * @throws \Exception + */ + public function warning(mixed $context = null): void + { + $this->trace = debug_backtrace()[0]; + $this->log(Level::Warning, $context); + } + + /** + * Logs a notice message with the specified context. + * + * @param mixed $context Optional context information to include in the log. + * @return void + * @throws \Exception + */ + public function notice(mixed $context = null): void + { + $this->trace = debug_backtrace()[0]; + $this->log(Level::Notice, $context); + } + + /** + * Logs an informational message with the specified context. + * + * @param mixed $context Optional context information to include in the log. + * @return void + * @throws \Exception + */ + public function info(mixed $context = null): void + { + $this->trace = debug_backtrace()[0]; + $this->log(Level::Info, $context); + } + + /** + * Logs a debug message with the specified context. + * + * @param mixed $context Optional context information to include in the log. + * @return void + * @throws \Exception + */ + public function debug(mixed $context = null): void + { + $this->trace = debug_backtrace()[0]; + $this->log(Level::Debug, $context); + } +}
\ No newline at end of file diff --git a/src/Message.php b/src/Message.php new file mode 100644 index 0000000..4af49e7 --- /dev/null +++ b/src/Message.php @@ -0,0 +1,15 @@ +<?php + +namespace Asko\Loggr; + +/** + * @author Asko Nõmm <asko@faultd.com> + */ +readonly class Message +{ + public function __construct( + public Level $level, + public array $trace = [], + public mixed $context = null, + ) {} +}
\ No newline at end of file |
