summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAsko Nõmm <asko@nth.ee>2024-11-10 22:28:03 +0200
committerAsko Nõmm <asko@nth.ee>2024-11-10 22:28:03 +0200
commit12b6c4b3008c2df545c537943d4e38323cfc174e (patch)
tree11e427a794832ac73657da675f3d79949443a3f6 /src
Initial commit
Diffstat (limited to 'src')
-rw-r--r--src/Driver.php11
-rw-r--r--src/Drivers/FileSystemDriver.php53
-rw-r--r--src/Drivers/OutputDriver.php19
-rw-r--r--src/Format.php135
-rw-r--r--src/Level.php18
-rw-r--r--src/Loggr.php151
-rw-r--r--src/Message.php15
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