Added initial codebase

This commit is contained in:
netkas 2025-01-22 01:02:17 -05:00
parent 493a86bc9b
commit 80a6af74ed
41 changed files with 5676 additions and 29 deletions

View file

@ -0,0 +1,133 @@
<?php
namespace LogLib2\Classes;
use LogLib2\Exceptions\IOException;
class FileLock
{
private $fileHandle;
private int $permissions;
private string $filePath;
private int $retryInterval; // in microseconds
private int $confirmationInterval; // in microseconds
/**
* Constructor for FileLock.
*
* @param string $filePath Path to the file.
* @param int $permissions
* @param int $retryInterval Time to wait between retries (in microseconds).
* @param int $confirmationInterval Time to wait before double confirmation (in microseconds).
* @throws IOException if unable to create the file or set the permissions.
*/
public function __construct(string $filePath, int $permissions, int $retryInterval=100000, int $confirmationInterval=50000)
{
$this->filePath = $filePath;
$this->permissions = $permissions;
$this->retryInterval = $retryInterval;
$this->confirmationInterval = $confirmationInterval;
// Create the file if it doesn't exist
if (!file_exists($filePath))
{
// Create the file
if(!@touch($filePath))
{
throw new IOException("Unable to create the file: " . $filePath);
}
if(!@chmod($filePath, $this->permissions))
{
throw new IOException("Unable to set the file permissions: " . $filePath);
}
}
}
/**
* Locks the file.
*
* @throws IOException if unable to open or lock the file.
*/
private function lock(): bool
{
$this->fileHandle = @fopen($this->filePath, 'a');
if ($this->fileHandle === false)
{
return false;
}
// Keep trying to acquire the lock until it succeeds
while (!flock($this->fileHandle, LOCK_EX))
{
usleep($this->retryInterval); // Wait for the specified interval before trying again
}
// Double confirmation
usleep($this->confirmationInterval); // Wait for the specified confirmation interval
if (!flock($this->fileHandle, LOCK_EX | LOCK_NB))
{
// If the lock cannot be re-acquired, release the current lock and retry
flock($this->fileHandle, LOCK_UN);
$this->lock();
}
return true;
}
/**
* Unlocks the file after performing write operations.
*/
private function unlock(): void
{
if ($this->fileHandle !== null)
{
flock($this->fileHandle, LOCK_UN); // Release the lock
fclose($this->fileHandle); // Close the file handle
$this->fileHandle = null; // Reset the file handle
// Check if write permissions have changed
if (!is_writable($this->filePath))
{
// Set the file permissions to the default
chmod($this->filePath, $this->permissions);
}
}
}
/**
* Appends data to the file.
*
* @param string $data Data to append.
* @throws IOException if unable to write to the file.
*/
public function append(string $data): void
{
if(!$this->lock())
{
// Do not proceed if the file cannot be locked
return;
}
if ($this->fileHandle !== false)
{
if (fwrite($this->fileHandle, $data) === false)
{
throw new IOException("Unable to write to the file: " . $this->filePath);
}
}
$this->unlock();
}
/**
* Destructor to ensure the file handle is closed.
*/
public function __destruct()
{
if ($this->fileHandle)
{
fclose($this->fileHandle);
}
}
}

View file

@ -0,0 +1,309 @@
<?php
namespace LogLib2\Classes\LogHandlers;
use LogLib2\Enums\AnsiFormat;
use LogLib2\Enums\ConsoleColor;
use LogLib2\Enums\LogLevel;
use LogLib2\Enums\TimestampFormat;
use LogLib2\Enums\TraceFormat;
use LogLib2\Interfaces\LogHandlerInterface;
use LogLib2\Objects\Application;
use LogLib2\Objects\Event;
use LogLib2\Objects\ExceptionDetails;
use Random\RandomException;
class ConsoleHandler implements LogHandlerInterface
{
private static array $applicationColors = [];
/**
* Checks if the current PHP environment is available for execution in CLI mode.
*
* @return bool True if the PHP environment is running in CLI mode, false otherwise.
*/
public static function isAvailable(Application $application): bool
{
return php_sapi_name() === 'cli';
}
/**
* @inheritDoc
*/
public static function handleEvent(Application $application, Event $event): void
{
// Output the event to the console based on the ANSI style.
$output = match($application->getConsoleConfiguration()->getAnsiFormat())
{
AnsiFormat::NONE => self::noAnsi($application, $event),
AnsiFormat::BASIC => self::basicOutput($application, $event),
};
// Output the event to the appropriate console stream based on the LogLevel.
switch($event->getLevel())
{
case LogLevel::DEBUG:
case LogLevel::VERBOSE:
case LogLevel::INFO:
fwrite(STDOUT, $output);
break;
case LogLevel::WARNING:
case LogLevel::ERROR:
case LogLevel::CRITICAL:
fwrite(STDERR, $output);
break;
}
}
/**
* Outputs the event to the console.
*
* @param Application $application The application that generated the event.
* @param Event $event The event to output.
*/
private static function noAnsi(Application $application, Event $event): string
{
$output = (string)null;
if($application->getConsoleConfiguration()->getTimestampFormat() !== TimestampFormat::NONE)
{
$output .= $application->getConsoleConfiguration()->getTimestampFormat()->format($event->getTimestamp());
}
if($application->getConsoleConfiguration()->isDisplayName())
{
if($output !== (string)null)
{
$output .= ' ';
}
$output .= $application->getName();
}
if($application->getConsoleConfiguration()->isDisplayLevel())
{
if($output !== (string)null)
{
$output .= ' ';
}
$output .= sprintf('[%s]', $event->getLevel()->value);
}
if($application->getConsoleConfiguration()->getTraceFormat() !== TraceFormat::NONE && $event->getFirstTrace() !== null)
{
if($output !== (string)null)
{
$output .= ' ';
}
$output .= $application->getConsoleConfiguration()->getTraceFormat()->format($event->getFirstTrace());
}
if($output !== (string)null)
{
$output .= $event->getMessage();
}
if($event->getException() !== null)
{
$output .= self::noAnsiException($event->getException());
}
return $output . "\n";
}
/**
* Outputs the exception details in a basic format.
*
* @param ExceptionDetails $exception The exception details to output.
* @param bool $previous If this is a previous exception in the chain.
* @return string The formatted exception details.
*/
private static function noAnsiException(ExceptionDetails $exception, bool $previous=false): string
{
if($previous)
{
$output = sprintf("%s", $exception->getName());
}
else
{
$output = sprintf("\n%s", $exception->getName());
}
if($exception->getCode() !== 0 && $exception->getCode() !== null)
{
$output .= sprintf(" (%d)", $exception->getCode());
}
if($exception->getMessage() !== null)
{
$output .= sprintf(": %s", $exception->getMessage());
}
if($exception->getFile() !== null)
{
$output .= sprintf(" File: %s", $exception->getFile());
if($exception->getLine() !== null && $exception->getLine() !== 0)
{
$output .= sprintf(":%d", $exception->getLine());
}
}
if($exception->getTrace() !== null)
{
$output .= "\n Stack Trace:\n";
foreach($exception->getTrace() as $trace)
{
$output .= sprintf(" - %s\n", TraceFormat::FULL->format($trace));
}
}
if($exception->getPrevious() !== null)
{
$output .= self::noAnsiException($exception->getPrevious(), true);
}
return $output;
}
/**
* Outputs the exception details in a basic format.
*
* @param Application $application The application that generated the event.
* @param Event $event The event to output.
* @return string The formatted exception details.
* @throws RandomException Thrown when the random_int function fails to generate a random integer.
*/
private static function basicOutput(Application $application, Event $event): string
{
$output = (string)null;
if($application->getConsoleConfiguration()->getTimestampFormat() !== TimestampFormat::NONE)
{
$output .= ConsoleColor::DEFAULT->formatBold($application->getConsoleConfiguration()->getTimestampFormat()->format($event->getTimestamp()));
}
if($application->getConsoleConfiguration()->isDisplayName())
{
if($output !== (string)null)
{
$output .= ' ';
}
$output .= self::getApplicationColor($application)->formatBold($application->getName());
}
if($application->getConsoleConfiguration()->isDisplayLevel())
{
if($output !== (string)null)
{
$output .= ' ';
}
$output .= sprintf('[%s]', ConsoleColor::DEFAULT->formatBold($event->getLevel()->value));
}
if($application->getConsoleConfiguration()->getTraceFormat() !== TraceFormat::NONE && $event->getFirstTrace() !== null)
{
if($output !== (string)null)
{
$output .= ' ';
}
$output .= $application->getConsoleConfiguration()->getTraceFormat()->format($event->getFirstTrace());
}
if($output !== (string)null)
{
$output .= ' ';
}
$output .= $event->getMessage();
if($event->getException() !== null)
{
$output .= self::basicOutputException($event->getException());
}
return $output . "\n";
}
/**
* Outputs the exception details in a basic format.
*
* @param ExceptionDetails $exception The exception details to output.
* @param bool $previous If this is a previous exception in the chain.
* @return string The formatted exception details.
*/
private static function basicOutputException(ExceptionDetails $exception, bool $previous=false): string
{
if($previous)
{
$output = sprintf("%s", ConsoleColor::RED->formatBold($exception->getName()));
}
else
{
$output = sprintf("\n%s", ConsoleColor::RED->formatBold($exception->getName()));
}
if($exception->getCode() !== 0 && $exception->getCode() !== null)
{
$output .= sprintf(" (%d)", ConsoleColor::DEFAULT->formatBold($exception->getCode()));
}
if($exception->getMessage() !== null)
{
$output .= sprintf(": %s", $exception->getMessage());
}
if($exception->getFile() !== null)
{
$output .= sprintf("\n File: %s", $exception->getFile());
if($exception->getLine() !== null && $exception->getLine() !== 0)
{
$output .= sprintf(":%d", ConsoleColor::DEFAULT->formatBold($exception->getLine()));
}
}
if($exception->getTrace() !== null && count($exception->getTrace()) > 0)
{
$output .= "\n Stack Trace:\n";
foreach($exception->getTrace() as $trace)
{
$output .= sprintf(" - %s\n", ConsoleColor::DEFAULT->formatLight(TraceFormat::FULL->format($trace)));
}
}
if($exception->getPrevious() !== null)
{
$output .= self::basicOutputException($exception->getPrevious(), true);
}
return $output;
}
/**
* Retrieves the color for the given application.
*
* @param Application $application The application to retrieve the color for.
* @return ConsoleColor The color for the given application.
* @throws RandomException Thrown when the random_int function fails to generate a random integer.
*/
private static function getApplicationColor(Application $application): ConsoleColor
{
if(!isset(self::$applicationColors[$application->getName()]))
{
self::$applicationColors[$application->getName()] = ConsoleColor::getRandomColor([
ConsoleColor::BLACK, ConsoleColor::DEFAULT
]);
}
return self::$applicationColors[$application->getName()];
}
}

View file

@ -0,0 +1,58 @@
<?php
namespace LogLib2\Classes\LogHandlers;
use LogLib2\Interfaces\LogHandlerInterface;
use LogLib2\Objects\Application;
use LogLib2\Objects\Event;
class DescriptorHandler implements LogHandlerInterface
{
private static array $resources = [];
/**
* @inheritDoc
*/
public static function isAvailable(Application $application): bool
{
// Check if the descriptor exists
if(!file_exists($application->getDescriptorConfiguration()->getDescriptor()))
{
return false;
}
// If the file lock does not exist, create it to allow for file locking & writing.
if(!isset(self::$resources[$application->getName()]))
{
self::$resources[$application->getName()] = @fopen($application->getDescriptorConfiguration()->getDescriptor(), 'a');
}
return true;
}
/**
* @inheritDoc
*/
public static function handleEvent(Application $application, Event $event): void
{
$message = $application->getDescriptorConfiguration()->getLogFormat()->format(
$application->getDescriptorConfiguration()->getTimestampFormat(), $application->getDescriptorConfiguration()->getTraceFormat(), $event
);
if($application->getDescriptorConfiguration()->isAppendNewline())
{
$message .= PHP_EOL;
}
// Write the event to the descriptor.
$result = @fwrite(self::$resources[$application->getName()], $message);
if($result === false)
{
@fclose(self::$resources[$application->getName()]);
unset(self::$resources[$application->getName()]);
self::$resources[$application->getName()] = @fopen($application->getDescriptorConfiguration()->getDescriptor(), 'a');
@fwrite(self::$resources[$application->getName()], $message);
}
}
}

View file

@ -0,0 +1,96 @@
<?php
namespace LogLib2\Classes\LogHandlers;
use DateTime;
use LogLib2\Classes\FileLock;
use LogLib2\Classes\Utilities;
use LogLib2\Enums\LogFormat;
use LogLib2\Interfaces\LogHandlerInterface;
use LogLib2\Objects\Application;
use LogLib2\Objects\Event;
class FileHandler implements LogHandlerInterface
{
private const string CSV_HEADERS = "timestamp,level,message,trace,exception";
private static array $fileLocks = [];
/**
* @inheritDoc
*/
public static function isAvailable(Application $application): bool
{
$logPath = $application->getFileConfiguration()->getLogPath();
$filePath = self::getLogFilePath($application);
// If the log path is not writable nor a dir, return false.
if(!is_writable($logPath) || !is_dir($logPath))
{
return false;
}
// If the log file does not exist, create it.
if(!file_exists($filePath))
{
if(!@touch($filePath) || !@chmod($filePath, $application->getFileConfiguration()->getDefaultPermissions()))
{
return false;
}
// Create the headers for the log file if required.
if($application->getFileConfiguration()->getLogFormat() == LogFormat::CSV)
{
$temporaryLock = new FileLock($filePath, $application->getFileConfiguration()->getDefaultPermissions());
$temporaryLock->append(self::CSV_HEADERS . PHP_EOL);
unset($temporaryLock);
}
}
// If the file lock does not exist, create it to allow for file locking & writing.
if(!isset(self::$fileLocks[$application->getName()]))
{
self::$fileLocks[$application->getName()] = new FileLock($filePath, $application->getFileConfiguration()->getDefaultPermissions());
}
return true;
}
/**
* @inheritDoc
*/
public static function handleEvent(Application $application, Event $event): void
{
$message = $application->getFileConfiguration()->getLogFormat()->format(
$application->getFileConfiguration()->getTimestampFormat(), $application->getFileConfiguration()->getTraceFormat(), $event
);
if($application->getFileConfiguration()->isAppendNewline())
{
$message .= PHP_EOL;
}
self::$fileLocks[$application->getName()]->append($message);
}
/**
* Retrieves the log file path for the given application.
*
* @param Application $application The application to retrieve the log file path for.
*
* @return string The log file path for the given application.
*/
private static function getLogFilePath(Application $application): string
{
$extension = match($application->getFileConfiguration()->getLogFormat())
{
LogFormat::JSONL => 'jsonl',
LogFormat::CSV => 'csv',
LogFormat::TXT => 'txt',
LogFormat::XML => 'xml',
LogFormat::HTML => 'html',
};
return Utilities::getEnvironmentLogPath($application) . DIRECTORY_SEPARATOR .
sprintf('%s-%s.%s', Utilities::sanitizeFileName($application->getName()), (new DateTime())->format('Y-m-d'), $extension);
}
}

View file

@ -0,0 +1,63 @@
<?php
namespace LogLib2\Classes\LogHandlers;
use LogLib2\Enums\LogFormat;
use LogLib2\Interfaces\LogHandlerInterface;
use LogLib2\Objects\Application;
use LogLib2\Objects\Event;
class HttpHandler implements LogHandlerInterface
{
/**
* Checks if the current PHP environment is available for execution in CLI mode.
*
* @return bool True if the PHP environment is running in CLI mode, false otherwise.
*/
public static function isAvailable(Application $application): bool
{
if(!function_exists('curl_init'))
{
return false;
}
if(!filter_var($application->getHttpConfiguration()->getEndpoint(), FILTER_VALIDATE_URL))
{
return false;
}
return true;
}
/**
* @inheritDoc
*/
public static function handleEvent(Application $application, Event $event): void
{
$header = match($application->getHttpConfiguration()->getLogFormat())
{
LogFormat::JSONL => 'Content-Type: application/json',
LogFormat::CSV => 'Content-Type: text/csv',
LogFormat::TXT => 'Content-Type: text/plain',
LogFormat::XML => 'Content-Type: text/xml',
LogFormat::HTML => 'Content-Type: text/html',
};
$message = $application->getHttpConfiguration()->getLogFormat()->format(
$application->getHttpConfiguration()->getTimestampFormat(), $application->getHttpConfiguration()->getTraceFormat(), $event
);
if($application->getHttpConfiguration()->isAppendNewline())
{
$message .= PHP_EOL;
}
// Note, no exception handling is done here. If the HTTP request fails, it will fail silently.
$ch = curl_init($application->getHttpConfiguration()->getEndpoint());
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_POSTFIELDS, $message);
curl_setopt($ch, CURLOPT_HTTPHEADER, [$header]);
curl_exec($ch);
curl_close($ch);
}
}

View file

@ -0,0 +1,100 @@
<?php
namespace LogLib2\Classes\LogHandlers;
use LogLib2\Interfaces\LogHandlerInterface;
use LogLib2\Objects\Application;
use LogLib2\Objects\Event;
class TcpHandler implements LogHandlerInterface
{
private static array $sockets = [];
/**
* @inheritDoc
*/
public static function isAvailable(Application $application): bool
{
// Check if the TCP configuration is valid.
if(!filter_var($application->getTcpConfiguration()->getHost(), FILTER_VALIDATE_IP))
{
return false;
}
if($application->getTcpConfiguration()->getPort() < 1 || $application->getTcpConfiguration()->getPort() > 65535)
{
return false;
}
$socketKey = $application->getTcpConfiguration()->getHost() . ':' . $application->getTcpConfiguration()->getPort();
if(!isset(self::$sockets[$socketKey]))
{
self::$sockets[$socketKey] = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if(self::$sockets[$socketKey] === false)
{
unset(self::$sockets[$socketKey]);
return false;
}
if(!@socket_connect(self::$sockets[$socketKey], $application->getTcpConfiguration()->getHost(), $application->getTcpConfiguration()->getPort()))
{
unset(self::$sockets[$socketKey]);
return false;
}
return true;
}
return true;
}
/**
* @inheritDoc
*/
public static function handleEvent(Application $application, Event $event): void
{
$message = $application->getTcpConfiguration()->getLogFormat()->format(
$application->getTcpConfiguration()->getTimestampFormat(), $application->getTcpConfiguration()->getTraceFormat(), $event
);
if($application->getTcpConfiguration()->isAppendNewline())
{
$message .= PHP_EOL;
}
// If the message is too long, fail silently.
if(strlen($message) > 65535)
{
return;
}
$socketKey = $application->getTcpConfiguration()->getHost() . ':' . $application->getTcpConfiguration()->getPort();
if(!isset(self::$sockets[$socketKey]))
{
self::$sockets[$socketKey] = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if(self::$sockets[$socketKey] === false)
{
unset(self::$sockets[$socketKey]);
return;
}
if(!@socket_connect(self::$sockets[$socketKey], $application->getTcpConfiguration()->getHost(), $application->getTcpConfiguration()->getPort()))
{
unset(self::$sockets[$socketKey]);
return;
}
}
// If the request fails, try to reconnect and send the message again. if it fails again, fail silently.
if(@socket_send(self::$sockets[$socketKey], $message, strlen($message), 0) === false)
{
if(!@socket_connect(self::$sockets[$socketKey], $application->getTcpConfiguration()->getHost(), $application->getTcpConfiguration()->getPort()))
{
unset(self::$sockets[$socketKey]);
return;
}
@socket_send(self::$sockets[$socketKey], $message, strlen($message), 0);
}
}
}

View file

@ -0,0 +1,86 @@
<?php
namespace LogLib2\Classes\LogHandlers;
use LogLib2\Interfaces\LogHandlerInterface;
use LogLib2\Objects\Application;
use LogLib2\Objects\Event;
class UdpHandler implements LogHandlerInterface
{
private static array $sockets = [];
/**
* @inheritDoc
*/
public static function isAvailable(Application $application): bool
{
// Check if the UDP configuration is valid.
if(!filter_var($application->getUdpConfiguration()->getHost(), FILTER_VALIDATE_IP))
{
return false;
}
if($application->getUdpConfiguration()->getPort() < 1 || $application->getUdpConfiguration()->getPort() > 65535)
{
return false;
}
// If the socket does not exist, create it.
$socketKey = $application->getUdpConfiguration()->getHost() . ':' . $application->getUdpConfiguration()->getPort();
if(!isset(self::$sockets[$socketKey]))
{
self::$sockets[$application->getName()] = @socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
if(self::$sockets[$application->getName()] === false)
{
unset(self::$sockets[$socketKey]);
return false;
}
}
return true;
}
/**
* @inheritDoc
*/
public static function handleEvent(Application $application, Event $event): void
{
$message = $application->getUdpConfiguration()->getLogFormat()->format(
$application->getUdpConfiguration()->getTimestampFormat(), $application->getUdpConfiguration()->getTraceFormat(), $event
);
if($application->getUdpConfiguration()->isAppendNewline())
{
$message .= PHP_EOL;
}
// If the message is too long, fail silently.
if(strlen($message) > 65535)
{
return;
}
$socketKey = $application->getUdpConfiguration()->getHost() . ':' . $application->getUdpConfiguration()->getPort();
if(!isset(self::$sockets[$socketKey]))
{
self::$sockets[$socketKey] = @socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
if(self::$sockets[$socketKey] === false)
{
unset(self::$sockets[$socketKey]);
return;
}
}
// If the request fails, try to reconnect and send the message again. if it fails again, fail silently.
if(@socket_sendto(self::$sockets[$socketKey], $message, strlen($message), 0, $application->getUdpConfiguration()->getHost(), $application->getUdpConfiguration()->getPort()) === false)
{
if(!@socket_connect(self::$sockets[$socketKey], $application->getUdpConfiguration()->getHost(), $application->getUdpConfiguration()->getPort()))
{
unset(self::$sockets[$socketKey]);
}
@socket_sendto(self::$sockets[$socketKey], $message, strlen($message), 0, $application->getUdpConfiguration()->getHost(), $application->getUdpConfiguration()->getPort());
}
}
}

View file

@ -0,0 +1,184 @@
<?php
namespace LogLib2\Classes;
use ErrorException;
use LogLib2\Enums\LogLevel;
use LogLib2\Objects\Application;
use LogLib2\Objects\ExceptionDetails;
use LogLib2\Objects\StackTrace;
use OptsLib\Parse;
use Throwable;
class Utilities
{
private static ?array $cachedOptions = null;
/**
* Retrieves the logging level from various sources, including environment variables,
* CLI arguments, or from the Application object. Defaults to INFO if no specific
* logging level is found.
*
* @return LogLevel Returns the determined logging level based on the available sources.
*/
public static function getEnvironmentLogLevel(): LogLevel
{
// Parse from environment if a variable is set.
if(getenv('LOG_LEVEL'))
{
return LogLevel::parseFrom(getenv('LOG_LEVEL'));
}
// Parse from CLI arguments if the script is running in CLI mode.
if(php_sapi_name() === 'cli')
{
if(self::$cachedOptions === null)
{
self::$cachedOptions = Parse::getArguments();
}
if(isset(self::$cachedOptions['log-level']))
{
return LogLevel::parseFrom(self::$cachedOptions['log-level']);
}
}
return LogLevel::INFO;
}
/**
* Determines the log path for the application from the environment, CLI arguments, or application defaults.
*
* The method checks if a log path is defined in environment variables, provided as a command-line argument,
* or set in the application instance. If no path is specified, it defaults to '/tmp/logs'.
*
* @param Application|null $application Optional application instance to retrieve a default log path.
* @return string The log path determined from the environment, CLI arguments, application instance, or default value.
*/
public static function getEnvironmentLogPath(?Application $application=null): string
{
// Parse from CLI arguments if the script is running in CLI mode.
if(php_sapi_name() === 'cli')
{
if(self::$cachedOptions === null)
{
self::$cachedOptions = Parse::getArguments();
}
if(isset(self::$cachedOptions['log-path']))
{
return rtrim(self::$cachedOptions['log-path'], DIRECTORY_SEPARATOR);
}
}
if($application?->getFileConfiguration()?->getLogPath() !== null)
{
return rtrim($application->getFileConfiguration()->getLogPath(), DIRECTORY_SEPARATOR);
}
return DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR . 'logs';
}
/**
* Processes an input value and returns a safely formatted string representation.
* Converts arrays and other unsupported data types into a structured string format.
*
* @param mixed $input The input value to be processed. It can be of any type including arrays, strings, integers, etc.
*
* @return string A safe and structured string representation of the input value.
*/
public static function getSafeValue(mixed $input): string
{
if(is_array($input))
{
if(array_is_list($input))
{
$output = [];
foreach($input as $value)
{
$output[] = self::getSafeValue($value);
}
return sprintf('[%s]', implode(', ', $output));
}
else
{
$output = [];
foreach($input as $key => $value)
{
$output[] = sprintf('%s: %s', self::getSafeValue($key), self::getSafeValue($value));
}
return sprintf('[%s]', implode(', ', $output));
}
}
return match(strtolower(gettype($input)))
{
'boolean', 'integer', 'double', 'float', 'string', 'null' => $input,
default => sprintf('[%s]', strtoupper(gettype($input))),
};
}
/**
* Retrieves a backtrace from the current execution point.
*
* @param int $level The number of stack frames to skip before starting the trace.
* @return StackTrace[] An array of StackTrace objects representing the backtrace.
*/
public static function getBackTrace(int $level=3): array
{
if(!function_exists('debug_backtrace'))
{
return [];
}
$debugBacktrace = debug_backtrace();
$results = [];
foreach($debugBacktrace as $trace)
{
$stackTrace = StackTrace::fromTrace($trace);
if($stackTrace->isEmpty())
{
continue;
}
$results[] = $stackTrace;
}
return array_slice($results, $level);
}
/**
* Sanitizes a file name by replacing invalid characters with underscores.
*
* @param string $name The file name to sanitize.
* @return string Returns the sanitized file name.
*/
public static function sanitizeFileName(string $name): string
{
return preg_replace('/[\/:*?"<>|.]/', '_', str_replace(' ', '-', $name));
}
/**
* Converts an Error instance into an ErrorException instance.
*
* @param int $errno The error number.
* @param string $errstr The error message.
* @param string $errfile The file in which the error occurred.
* @param int $errline The line number in which the error occurred.
* @return ExceptionDetails Returns the converted ErrorException instance.
*/
public static function detailsFromError(int|string $errno, string $errstr, string $errfile, int $errline): ExceptionDetails
{
if(is_string($errno))
{
$errno = 0;
$errstr = sprintf('%s: %s', $errno, $errstr);
}
return new ExceptionDetails('Runtime', $errstr, $errno, $errfile, $errline);
}
}

View file

@ -0,0 +1,24 @@
<?php
namespace LogLib2\Enums;
enum AnsiFormat
{
case NONE;
case BASIC;
/**
* Parses the input string into an AnsiFormat enum.
*
* @param string $input The input string to parse.
* @return AnsiFormat The parsed AnsiFormat enum.
*/
public static function parseFrom(string $input): AnsiFormat
{
return match(strtolower($input))
{
'basic', '1' => AnsiFormat::BASIC,
default => AnsiFormat::NONE,
};
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace LogLib2\Enums;
enum CallType : string
{
/**
* Represents a method call.
*
* @var string METHOD_CALL
*/
case METHOD_CALL = '->';
/**
* Represents a static method call.
*
* @var string STATIC_CALL
*/
case STATIC_CALL = '::';
/**
* Represents a function call.
*
* @var string FUNCTION_CALL
*/
case FUNCTION_CALL = '()';
/**
* Represents a lambda function call.
*
* @var string LAMBDA_CALL
*/
case LAMBDA_CALL = 'λ';
}

View file

@ -0,0 +1,152 @@
<?php
namespace LogLib2\Enums;
use Random\RandomException;
enum ConsoleColor
{
case DEFAULT;
case BLACK;
case RED;
case GREEN;
case YELLOW;
case BLUE;
case MAGENTA;
case CYAN;
case WHITE;
/**
* Formats the given input string with the color of the current ConsoleColor.
*
* @param string $input The input string to format.
* @param bool $revertToDefault Whether to revert the color back to the default color after the input string.
* @return string The formatted input string.
*/
public function format(string $input, bool $revertToDefault=true): string
{
$colorCode = match($this)
{
self::DEFAULT => 39,
self::BLACK => 30,
self::RED => 31,
self::GREEN => 32,
self::YELLOW => 33,
self::BLUE => 34,
self::MAGENTA => 35,
self::CYAN => 36,
self::WHITE => 37,
};
return "\033[" . $colorCode . "m" . $input . ($revertToDefault ? "\033[39m" : '');
}
/**
* Formats the given input string with the light color of the current ConsoleColor.
*
* @param string $input The input string to format.
* @param bool $revertToDefault Whether to revert the color back to the default color after the input string.
* @return string The formatted input string.
*/
public function formatLight(string $input, bool $revertToDefault=true): string
{
$colorCode = match($this)
{
self::DEFAULT => 39,
self::BLACK => 90,
self::RED => 91,
self::GREEN => 92,
self::YELLOW => 93,
self::BLUE => 94,
self::MAGENTA => 95,
self::CYAN => 96,
self::WHITE => 97,
};
return "\033[" . $colorCode . "m" . $input . ($revertToDefault ? "\033[39m" : '');
}
/**
* Formats the given input string with the bold color of the current ConsoleColor.
*
* @param string $input The input string to format.
* @param bool $revertToDefault Whether to revert the color back to the default color after the input string.
* @return string The formatted input string.
*/
public function formatBold(string $input, bool $revertToDefault=true): string
{
$colorCode = match($this)
{
self::DEFAULT => 39,
self::BLACK => 30,
self::RED => 31,
self::GREEN => 32,
self::YELLOW => 33,
self::BLUE => 34,
self::MAGENTA => 35,
self::CYAN => 36,
self::WHITE => 37,
};
return "\033[1;" . $colorCode . "m" . $input . ($revertToDefault ? "\033[0m" : '');
}
/**
* Formats the given input string with the background color of the current ConsoleColor.
*
* @param string $input The input string to format.
* @param ConsoleColor $foreground The foreground color to use.
* @param bool $revertToDefault Whether to revert the color back to the default color after the input string.
* @return string The formatted input string.
*/
public function formatBackground(string $input, ConsoleColor $foreground, bool $revertToDefault=true): string
{
$colorCode = match($this)
{
self::DEFAULT => 49,
self::BLACK => 40,
self::RED => 41,
self::GREEN => 42,
self::YELLOW => 43,
self::BLUE => 44,
self::MAGENTA => 45,
self::CYAN => 46,
self::WHITE => 47,
};
return "\033[" . $colorCode . "m" . $foreground->format($input, false) . ($revertToDefault ? "\033[49m" : '');
}
/**
* Formats the given input string with the light background color of the current ConsoleColor.
*
* @return ConsoleColor The formatted input string.
* @throws RandomException Thrown when the random_int function fails to generate a random integer.
*/
public static function getRandomColor(array $disallow=[]): ConsoleColor
{
$colors = [
self::BLACK,
self::RED,
self::GREEN,
self::YELLOW,
self::BLUE,
self::MAGENTA,
self::CYAN,
self::WHITE,
];
// Convert disallowed colors to strings
$disallow = array_map(fn($color) => $color->name, $disallow);
// Filter out disallowed colors
$colors = array_filter($colors, fn($color) => !in_array($color->name, $disallow));
if (empty($colors))
{
throw new RandomException('No colors available to choose from.');
}
return $colors[array_rand($colors)];
}
}

View file

@ -0,0 +1,437 @@
<?php
namespace LogLib2\Enums;
use LogLib2\Objects\Event;
use LogLib2\Objects\ExceptionDetails;
use LogLib2\Objects\StackTrace;
use SimpleXMLElement;
enum LogFormat
{
case JSONL;
case CSV;
case TXT;
case XML;
case HTML;
/**
* Formats the log entry.
*
* @param TimestampFormat $timestampType The timestamp type to use.
* @param TraceFormat $traceType The trace type to use.
* @param Event $event The event to format.
* @return string The formatted log entry.
*/
public function format(TimestampFormat $timestampType, TraceFormat $traceType, Event $event): string
{
return match($this)
{
self::JSONL => self::formatJson($timestampType, $traceType, $event),
self::CSV => self::formatCsv($timestampType, $traceType, $event),
self::TXT => self::formatTxt($timestampType, $traceType, $event),
self::XML => self::formatXml($timestampType, $traceType, $event),
self::HTML => self::formatHtml($timestampType, $traceType, $event),
};
}
/**
* Parses a log format from a string.
*
* @param string $input The input to parse.
* @return LogFormat The parsed log format.
*/
public static function parseFrom(string $input): LogFormat
{
return match(strtolower($input))
{
'csv', '1' => self::CSV,
'txt', '2' => self::TXT,
'xml', '3' => self::XML,
'html', '4' => self::HTML,
default => self::JSONL
};
}
/**
* Formats the log entry as a JSON string.
*
* @param TimestampFormat $timestampFormat The timestamp format to use.
* @param TraceFormat $traceFormat The trace format to use.
* @param Event $event The event to format as a JSON string.
* @return string The log entry as a JSON string.
*/
private static function formatJson(TimestampFormat $timestampFormat, TraceFormat $traceFormat, Event $event): string
{
return json_encode($event->toStandardArray($timestampFormat, $traceFormat), JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
}
/**
* Formats the log entry as a CSV string.
*
* @param TimestampFormat $timestampFormat The timestamp format to use.
* @param TraceFormat $traceFormat The trace format to use.
* @param Event $event The event to format as a CSV string.
* @return string The log entry as a CSV string.
*/
private static function formatCsv(TimestampFormat $timestampFormat, TraceFormat $traceFormat, Event $event): string
{
$output = self::sanitizeCsv($timestampFormat->format($event->getTimestamp())) . ',';
$output .= self::sanitizeCsv($event->getLevel()->value) . ',';
$output .= self::sanitizeCsv($event->getMessage()) . ',';
if($traceFormat === TraceFormat::NONE || $event->getFirstTrace() === null)
{
$output .= '-,';
}
else
{
$output .= self::sanitizeCsv($traceFormat->format($event->getFirstTrace())) . ',';
}
if ($event->getException() === null)
{
$output .= '-,';
}
else
{
$output .= self::sanitizeCsv(json_encode($event->getException()->toArray(), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)) . ',';
}
return $output;
}
/**
* Sanitizes a CSV value.
*
* @param string $value The value to sanitize.
* @return string The sanitized value.
*/
private static function sanitizeCsv(string $value): string
{
$escapedValue = str_replace('"', '""', $value);
if (str_contains($escapedValue, ',') || str_contains($escapedValue, '\n') || str_contains($escapedValue, '"'))
{
$escapedValue = '"' . $escapedValue . '"';
}
return $escapedValue;
}
/**
* Formats the log entry as a plain text string.
*
* @param TimestampFormat $timestampType The timestamp type to use.
* @param TraceFormat $traceType The trace type to use.
* @param Event $event The event to format as a plain text string.
* @return string The log entry as a plain text string.
*/
private static function formatTxt(TimestampFormat $timestampType, TraceFormat $traceType, Event $event): string
{
$output = '';
if ($timestampType !== TimestampFormat::NONE)
{
$output .= $timestampType->format($event->getTimestamp()) . ' ';
}
$output .= sprintf('[%s] ', $event->getLevel()->value);
if ($traceType !== TraceFormat::NONE && $event->getFirstTrace() !== null)
{
$output .= $traceType->format($event->getFirstTrace()) . ' ';
}
$output .= $event->getMessage();
if ($event->getException() !== null)
{
$output .= self::exceptionToString($event->getException());
}
return $output;
}
/**
* Formats the log entry as an XML string.
*
* @param Event $event The event to format as an XML string.
* @return string The log entry as an XML string.
*/
private static function formatXml(TimestampFormat $timestampType, TraceFormat $traceType, Event $event): string
{
$xml = new SimpleXMLElement('<event/>');
$xml->addChild('application_name', $event->getApplicationName());
$xml->addChild('timestamp', $timestampType->format($event->getTimestamp()));
$xml->addChild('level', $event->getLevel()->value);
$xml->addChild('message', $event->getLevel()->value);
if($traceType !== TraceFormat::NONE && $event->getFirstTrace() !== null)
{
$xml->addChild('trace', $traceType->format($event->getFirstTrace()));
}
if(count($event->getTraces()) > 0)
{
$tracesElement = $xml->addChild('stack_trace');
foreach($event->getTraces() as $trace)
{
$traceElement = $tracesElement->addChild('trace');
self::traceToXml($trace, $traceElement);
}
}
if ($event->getException() !== null)
{
self::exceptionToXml($event->getException(), $xml->addChild('exception'));
}
$dom = dom_import_simplexml($xml)->ownerDocument;
$dom->formatOutput = true;
return $dom->saveXML($dom->documentElement);
}
/**
* Converts an exception to an XML element.
*
* @param ExceptionDetails $exception The exception to convert.
* @param SimpleXMLElement $xml The XML element to append the exception to.
*/
private static function exceptionToXml(ExceptionDetails $exception, SimpleXMLElement $xml): void
{
$xml->addChild('name', htmlspecialchars($exception->getName(), ENT_XML1, 'UTF-8'));
$xml->addChild('message', htmlspecialchars($exception->getMessage(), ENT_XML1, 'UTF-8'));
if ($exception->getCode() !== null)
{
$xml->addChild('code', (string)$exception->getCode());
}
if ($exception->getFile() !== null)
{
$xml->addChild('file', htmlspecialchars($exception->getFile(), ENT_XML1, 'UTF-8'));
}
if ($exception->getLine() !== null)
{
$xml->addChild('line', (string)$exception->getLine());
}
if ($exception->getTrace() !== null)
{
$tracesElement = $xml->addChild('stack_trace');
foreach ($exception->getTrace() as $trace)
{
$traceElement = $tracesElement->addChild('trace');
self::traceToXml($trace, $traceElement);
}
}
if ($exception->getPrevious() !== null)
{
self::exceptionToXml($exception->getPrevious(), $xml->addChild('previous'));
}
}
/**
* Converts a stack trace to an XML element.
*
* @param StackTrace $trace The stack trace to convert.
* @param SimpleXMLElement $xml The XML element to append the stack trace to.
*/
private static function traceToXml(StackTrace $trace, SimpleXMLElement $xml): void
{
if ($trace->getFile() !== null)
{
$xml->addChild('file', htmlspecialchars($trace->getFile(), ENT_XML1, 'UTF-8'));
}
if ($trace->getLine() !== null)
{
$xml->addChild('line', (string)$trace->getLine());
}
if ($trace->getFunction() !== null)
{
$xml->addChild('function', htmlspecialchars($trace->getFunction(), ENT_XML1, 'UTF-8'));
}
if ($trace->getClass() !== null)
{
$xml->addChild('class', htmlspecialchars($trace->getClass(), ENT_XML1, 'UTF-8'));
}
if ($trace->getCallType() !== null)
{
$xml->addChild('call_type', htmlspecialchars($trace->getCallType()->value, ENT_XML1, 'UTF-8'));
}
if ($trace->getArgs() !== null)
{
$argsElement = $xml->addChild('arguments');
foreach ($trace->getArgs() as $arg)
{
$argsElement->addChild('argument', htmlspecialchars(json_encode($arg, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), ENT_XML1, 'UTF-8'));
}
}
}
/**
* Formats the log entry as an HTML string.
*
* @param Event $event The event to format as an HTML string.
* @return string The log entry as an HTML string.
*/
private static function formatHtml(TimestampFormat $timestampType, TraceFormat $traceType, Event $event): string
{
$html = '<div class="log-entry">';
$html .= sprintf('<p><strong>Timestamp:</strong> %s</p>', $timestampType->format($event->getTimestamp()));
$html .= sprintf('<p><strong>Level:</strong> %s</p>', $event->getLevel()->value);
$html .= sprintf('<p><strong>Message:</strong> %s</p>', htmlspecialchars($event->getMessage(), ENT_QUOTES, 'UTF-8'));
if($traceType !== TraceFormat::NONE && $event->getFirstTrace() !== null)
{
$html .= sprintf('<p><strong>Backtrace:</strong> %s</p>', $traceType->format($event->getFirstTrace()));
}
if ($event->getException() !== null)
{
$html .= '<p><strong>Exception Details:</strong></p>';
$html .= self::exceptionToHtml($event->getException());
}
$html .= '</div>';
return $html;
}
/**
* Converts an exception to an HTML string.
*
* @param ExceptionDetails $exception The exception to convert.
* @return string The exception as an HTML string.
*/
private static function exceptionToHtml(ExceptionDetails $exception): string
{
$html = '<div class="exception-details">';
$html .= sprintf('<p><strong>%s:</strong> %s (Code: %s)</p>',
htmlspecialchars($exception->getName(), ENT_QUOTES, 'UTF-8'),
htmlspecialchars($exception->getMessage(), ENT_QUOTES, 'UTF-8'),
$exception->getCode() !== null ? htmlspecialchars((string)$exception->getCode(), ENT_QUOTES, 'UTF-8') : 'N/A'
);
if ($exception->getFile() !== null)
{
$html .= sprintf('<p><strong>File:</strong> %s</p>', htmlspecialchars($exception->getFile(), ENT_QUOTES, 'UTF-8'));
if ($exception->getLine() !== null)
{
$html .= sprintf('<p><strong>Line:</strong> %d</p>', $exception->getLine());
}
}
if ($exception->getTrace() !== null)
{
$html .= '<p><strong>Stack Trace:</strong></p><ul>';
foreach ($exception->getTrace() as $trace)
{
$html .= '<li>';
$html .= self::traceToHtml($trace);
$html .= '</li>';
}
$html .= '</ul>';
}
if ($exception->getPrevious() !== null)
{
$html .= '<p><strong>Caused by:</strong></p>';
$html .= self::exceptionToHtml($exception->getPrevious());
}
$html .= '</div>';
return $html;
}
/**
* Converts a stack trace to an HTML string.
*
* @param StackTrace $trace The stack trace to convert.
* @return string The stack trace as an HTML string.
*/
private static function traceToHtml(StackTrace $trace): string
{
$output = '<div class="stack-trace">';
if ($trace->getFile() !== null)
{
$output .= sprintf('<p><strong>File:</strong> %s</p>', htmlspecialchars($trace->getFile(), ENT_QUOTES, 'UTF-8'));
}
if ($trace->getLine() !== null)
{
$output .= sprintf('<p><strong>Line:</strong> %d</p>', $trace->getLine());
}
if ($trace->getFunction() !== null)
{
$output .= sprintf('<p><strong>Function:</strong> %s</p>', htmlspecialchars($trace->getFunction(), ENT_QUOTES, 'UTF-8'));
}
if ($trace->getClass() !== null)
{
$output .= sprintf('<p><strong>Class:</strong> %s</p>', htmlspecialchars($trace->getClass(), ENT_QUOTES, 'UTF-8'));
}
if ($trace->getCallType() !== null)
{
$output .= sprintf('<p><strong>Call Type:</strong> %s</p>', htmlspecialchars($trace->getCallType()->value, ENT_QUOTES, 'UTF-8'));
}
if ($trace->getArgs() !== null)
{
$output .= '<p><strong>Arguments:</strong></p><ul>';
foreach ($trace->getArgs() as $arg)
{
$output .= sprintf('<li>%s</li>', htmlspecialchars(json_encode($arg, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), ENT_QUOTES, 'UTF-8'));
}
$output .= '</ul>';
}
$output .= '</div>';
return $output;
}
/**
* Converts an exception to a string.
*
* @param ExceptionDetails $exception The exception to convert.
* @return string The exception as a string.
*/
private static function exceptionToString(ExceptionDetails $exception): string
{
$output = sprintf("\n%s: %s (%s)", $exception->getName(), $exception->getMessage(), $exception->getCode() ?? 0);
if ($exception->getFile() !== null)
{
$output .= sprintf("\nFile: %s", $exception->getFile());
if ($exception->getLine() !== null)
{
$output .= sprintf(":%d", $exception->getLine());
}
}
if ($exception->getTrace() !== null)
{
$output .= "\n";
foreach ($exception->getTrace() as $trace)
{
$output .= sprintf(" %s\n", TraceFormat::FULL->format($trace));
}
}
if ($exception->getPrevious() !== null)
{
$output .= self::exceptionToString($exception->getPrevious());
}
return $output;
}
}

View file

@ -0,0 +1,12 @@
<?php
namespace LogLib2\Enums;
enum LogHandlers : string
{
case CONSOLE = 'console';
case FILE = 'file';
case TCP = 'tcp';
case UDP = 'udp';
case HTTP = 'http';
}

View file

@ -0,0 +1,90 @@
<?php
namespace LogLib2\Enums;
enum LogLevel : string
{
/**
* Represents a debug level constant.
*/
case DEBUG = 'DBG';
/**
* Represents a verbose level constant.
*/
case VERBOSE = 'VRB';
/**
* Represents an information level constant.
*/
case INFO = 'INFO';
/**
* Represents a warning level constant.
*/
case WARNING = 'WRN';
/**
* Represents an error level constant.
*/
case ERROR = 'ERR';
/**
* Represents a critical level constant.
*/
case CRITICAL = 'CRT';
/**
* Retrieves the levels of severity corresponding to the current level.
*
* @return array An array of levels applicable to the current instance.
*/
public function getLevels(): array
{
return match ($this)
{
self::DEBUG => [self::DEBUG, self::VERBOSE, self::INFO, self::WARNING, self::ERROR, self::CRITICAL],
self::VERBOSE => [self::VERBOSE, self::INFO, self::WARNING, self::ERROR, self::CRITICAL],
self::INFO => [self::INFO, self::WARNING, self::ERROR, self::CRITICAL],
self::WARNING => [self::WARNING, self::ERROR, self::CRITICAL],
self::ERROR => [self::ERROR, self::CRITICAL],
self::CRITICAL => [self::CRITICAL],
};
}
/**
* Checks if the provided log level is allowed based on the current instance's levels.
*
* @param LogLevel $level The log level to check against the allowed levels.
* @return bool True if the log level is allowed, false otherwise.
*/
public function levelAllowed(LogLevel $level): bool
{
return in_array($level, $this->getLevels());
}
/**
* Parses the given value and returns the corresponding log level.
*
* @param int|string $value The value to parse, which can be an integer or a string representation of the log level.
* @return LogLevel The log level matching the provided value, or the default log level if no match is found.
*/
public static function parseFrom(int|string $value): LogLevel
{
if(is_string($value))
{
$value = strtolower($value);
}
return match($value)
{
'debug', 'dbg', 'd', 0 => self::DEBUG,
'verbose', 'verb', 'vrb', 'v', 1 => self::VERBOSE,
'information', 'info', 'inf', 'i', 2 => self::INFO,
'warning', 'warn', 'wrn', 'w', 3 => self::WARNING,
'error', 'err', 'e', 4 => self::ERROR,
'critical', 'crit', 'crt', 'c', 5 => self::CRITICAL,
default => self::INFO
};
}
}

View file

@ -0,0 +1,63 @@
<?php
namespace LogLib2\Enums;
use DateTime;
enum TimestampFormat
{
case NONE;
case TIME_ONLY;
case TIME_ONLY_MILLIS;
case DATE_ONLY;
case DATE_TIME;
case DATE_TIME_MILLIS;
case UNIX_TIMESTAMP;
/**
* Retrieves the format string for the timestamp type.
*
* @param int|null $time The time to format, or null to use the current time.
* @return string Returns the format string for the timestamp type.
*/
public function format(?int $time=null): string
{
$format = match($this)
{
self::NONE => '',
self::TIME_ONLY => 'H:i:s',
self::TIME_ONLY_MILLIS => 'H:i:s.u',
self::DATE_ONLY => 'Y-m-d',
self::DATE_TIME => 'Y-m-d H:i:s',
self::DATE_TIME_MILLIS => 'Y-m-d H:i:s.u',
self::UNIX_TIMESTAMP => 'U',
};
if($time === null)
{
$time = time();
}
return (new DateTime())->setTimestamp($time)->format($format);
}
/**
* Parses the input string into a TimestampFormat enum.
*
* @param string $input The input string to parse.
* @return TimestampFormat The parsed TimestampFormat enum.
*/
public static function parseFrom(string $input): TimestampFormat
{
return match(strtolower($input))
{
'none', '0' => TimestampFormat::NONE,
'time_only_millis', '2' => TimestampFormat::TIME_ONLY_MILLIS,
'date_only', '3' => TimestampFormat::DATE_ONLY,
'date_time', '4' => TimestampFormat::DATE_TIME,
'date_time_millis', '5' => TimestampFormat::DATE_TIME_MILLIS,
'unix_timestamp', '6' => TimestampFormat::UNIX_TIMESTAMP,
default => TimestampFormat::TIME_ONLY,
};
}
}

View file

@ -0,0 +1,138 @@
<?php
namespace LogLib2\Enums;
use LogLib2\Objects\StackTrace;
enum TraceFormat
{
case NONE;
case BASIC;
case FULL;
/**
* Formats the stack trace based on the type of trace.
*
* @param StackTrace $stackTrace The stack trace to format.
* @return string The formatted stack trace.
*/
public function format(StackTrace $stackTrace): string
{
if($this === TraceFormat::BASIC)
{
return self::formatBasic($stackTrace);
}
if($this === TraceFormat::FULL)
{
return self::formatFull($stackTrace);
}
return (string)null;
}
/**
* Parses the input string into a TraceFormat enum.
*
* @param string $input The input string to parse.
* @return TraceFormat The parsed TraceFormat enum.
*/
public static function parseFrom(string $input): TraceFormat
{
return match(strtolower($input))
{
'none', '0' => TraceFormat::NONE,
'basic', '1' => TraceFormat::BASIC,
'full', '2' => TraceFormat::FULL,
default => TraceFormat::BASIC,
};
}
/**
* Formats the stack trace as a basic string.
*
* @param StackTrace $stackTrace The stack trace to format.
* @return string The formatted stack trace.
*/
private static function formatBasic(StackTrace $stackTrace): string
{
if($stackTrace->getFunction() === null)
{
if($stackTrace->getClass() !== null)
{
return $stackTrace->getClass();
}
if($stackTrace->getFile() !== null)
{
if($stackTrace->getLine() !== null)
{
return $stackTrace->getFile() . ':' . $stackTrace->getLine();
}
else
{
return $stackTrace->getFile();
}
}
}
if($stackTrace->getClass() !== null)
{
return $stackTrace->getClass() . ($stackTrace->getCallType()?->value ?? CallType::STATIC_CALL->value) . $stackTrace->getFunction();
}
if($stackTrace->getFile() !== null)
{
if($stackTrace->getLine() !== null)
{
return $stackTrace->getFile() . ':' . $stackTrace->getLine() . ' ' . ($stackTrace->getCallType()?->value ?? CallType::STATIC_CALL->value) . $stackTrace->getFunction();
}
else
{
return $stackTrace->getFile() . ' ' . ($stackTrace->getCallType()?->value ?? CallType::STATIC_CALL->value) . $stackTrace->getFunction();
}
}
return $stackTrace->getFunction();
}
/**
* Formats the stack trace as a full string.
*
* @param StackTrace $stackTrace The stack trace to format.
* @return string The formatted stack trace.
*/
private static function formatFull(StackTrace $stackTrace): string
{
$output = '';
if($stackTrace->getClass() !== null)
{
$output .= $stackTrace->getClass();
}
if($stackTrace->getCallType() !== null)
{
$output .= $stackTrace->getCallType()->value;
}
if($stackTrace->getFunction() !== null)
{
$output .= $stackTrace->getFunction();
}
if($stackTrace->getFile() !== null)
{
$output .= ' (' . $stackTrace->getFile();
if($stackTrace->getLine() !== null)
{
$output .= ':' . $stackTrace->getLine();
}
$output .= ')';
}
return $output;
}
}

View file

@ -0,0 +1,16 @@
<?php
namespace LogLib2\Exceptions;
use Throwable;
class IOException extends \Exception
{
/**
* @inheritDoc
*/
public function __construct(string $message = "", int $code=0, ?Throwable $previous=null)
{
parent::__construct($message, $code, $previous);
}
}

View file

@ -0,0 +1,27 @@
<?php
namespace LogLib2\Interfaces;
use LogLib2\Objects\Application;
use LogLib2\Objects\Event;
interface LogHandlerInterface
{
/**
* Determines if the requested resource or functionality is available.
*
* @param Application $application The application's instance
*
* @return bool True if available, false otherwise
*/
public static function isAvailable(Application $application): bool;
/**
* Handles the logging event for a log handler that implements this class
*
* @param Application $application The application's instance
* @param Event $event The event to log
* @return void
*/
public static function handleEvent(Application $application, Event $event): void;
}

View file

@ -0,0 +1,22 @@
<?php
namespace LogLib2\Interfaces;
interface SerializableInterface
{
/**
* Returns an array representation of the object
*
* @return array The array representation of the object
*/
public function toArray(): array;
/**
* Constructs the object from an array representation
*
* @param array|null $data The array representation of the object
* @throws \InvalidArgumentException If one or more data entries are invalid
* @return SerializableInterface The constructed class that implements SerializableInterface
*/
public static function fromArray(?array $data=null): SerializableInterface;
}

View file

@ -1,8 +0,0 @@
<?php
namespace LogLib2;
class LogLib2
{
}

617
src/LogLib2/Logger.php Normal file
View file

@ -0,0 +1,617 @@
<?php
namespace LogLib2;
require __DIR__ . DIRECTORY_SEPARATOR . 'autoload_patch.php';
use LogLib2\Classes\LogHandlers\ConsoleHandler;
use LogLib2\Classes\LogHandlers\DescriptorHandler;
use LogLib2\Classes\LogHandlers\FileHandler;
use LogLib2\Classes\LogHandlers\HttpHandler;
use LogLib2\Classes\LogHandlers\TcpHandler;
use LogLib2\Classes\LogHandlers\UdpHandler;
use LogLib2\Classes\Utilities;
use LogLib2\Enums\AnsiFormat;
use LogLib2\Enums\LogFormat;
use LogLib2\Enums\LogLevel;
use LogLib2\Enums\TimestampFormat;
use LogLib2\Enums\TraceFormat;
use LogLib2\Objects\Application;
use LogLib2\Objects\Configurations\ConsoleConfiguration;
use LogLib2\Objects\Configurations\DescriptorConfiguration;
use LogLib2\Objects\Configurations\FileConfiguration;
use LogLib2\Objects\Configurations\HttpConfiguration;
use LogLib2\Objects\Configurations\TcpConfiguration;
use LogLib2\Objects\Configurations\UdpConfiguration;
use LogLib2\Objects\Event;
use LogLib2\Objects\ExceptionDetails;
use Throwable;
class Logger
{
private static ?ConsoleConfiguration $defaultConsoleConfiguration=null;
private static ?DescriptorConfiguration $defaultDescriptorConfiguration=null;
private static ?FileConfiguration $defaultFileConfiguration=null;
private static ?HttpConfiguration $defaultHttpConfiguration=null;
private static ?TcpConfiguration $defaultTcpConfiguration=null;
private static ?UdpConfiguration $defaultUdpConfiguration=null;
private static bool $handlersRegistered=false;
private static ?Logger $runtimeLogger=null;
private static int $backtraceLevel=3;
private Application $application;
/**
* Constructs a new instance with the provided application name.
*
* @param string $application The application name
*/
public function __construct(string $application)
{
$this->application = new Application($application);
$this->application->setConsoleConfiguration(self::getDefaultConsoleConfiguration());
$this->application->setDescriptorConfiguration(self::getDefaultDescriptorConfiguration());
$this->application->setFileConfiguration(self::getDefaultFileConfiguration());
$this->application->setHttpConfiguration(self::getDefaultHttpConfiguration());
$this->application->setTcpConfiguration(self::getDefaultTcpConfiguration());
$this->application->setUdpConfiguration(self::getDefaultUdpConfiguration());
}
/**
* Retrieves the Application instance used by the Logger.
*
* @return Application The Application instance used by the Logger.
*/
public function getApplication(): Application
{
return $this->application;
}
/**
* Retrieves the Application instance used by the Logger.
*
* @return Application The Application instance used by the Logger.
*/
public function debug(string $message): void
{
$this->handleEvent($this->createEvent(LogLevel::DEBUG, $message));
}
/**
* Logs a verbose message with the provided message.
*
* @param string $message The message to log.
*/
public function verbose(string $message): void
{
$this->handleEvent($this->createEvent(LogLevel::VERBOSE, $message));
}
/**
* Logs an informational message with the provided message.
*
* @param string $message The message to log.
*/
public function info(string $message): void
{
$this->handleEvent($this->createEvent(LogLevel::INFO, $message));
}
/**
* Logs a warning message with the provided message.
*
* @param string $message The message to log.
*/
public function warning(string $message, null|ExceptionDetails|Throwable $e=null): void
{
$this->handleEvent($this->createEvent(LogLevel::WARNING, $message, $e));
}
/**
* Logs an error message with the provided message.
*
* @param string $message The message to log.
*/
public function error(string $message, null|ExceptionDetails|Throwable $e=null): void
{
$this->handleEvent($this->createEvent(LogLevel::ERROR, $message, $e));
}
/**
* Logs a critical message with the provided message.
*
* @param string $message The message to log.
*/
public function critical(string $message, null|ExceptionDetails|Throwable $e=null): void
{
$this->handleEvent($this->createEvent(LogLevel::CRITICAL, $message, $e));
}
/**
* Logs an alert message with the provided message.
*
* @param string $message The message to log.
*/
private function createEvent(LogLevel $level, string $message, null|ExceptionDetails|Throwable $e=null): Event
{
return new Event($this->application->getName(), $level, $message, Utilities::getBackTrace(Logger::getBacktraceLevel()), time(), $e);
}
/**
* Handles the provided event by passing it to the appropriate log handlers.
*
* @param Event $event The event to handle.
*/
private function handleEvent(Event $event): void
{
// Return early if the given LogLevel not allowed to be processed with the application's given log level.
if(!Utilities::getEnvironmentLogLevel()->levelAllowed($event->getLevel()))
{
return;
}
// Handle the event with the appropriate log handlers.
if($this->application->getConsoleConfiguration()->isEnabled() && ConsoleHandler::isAvailable($this->application))
{
ConsoleHandler::handleEvent($this->application, $event);
}
if($this->application->getDescriptorConfiguration()->isEnabled() && DescriptorHandler::isAvailable($this->application))
{
DescriptorHandler::handleEvent($this->application, $event);
}
if($this->application->getFileConfiguration()->isEnabled() && FileHandler::isAvailable($this->application))
{
FileHandler::handleEvent($this->application, $event);
}
if($this->application->getHttpConfiguration()->isEnabled() && HttpHandler::isAvailable($this->application))
{
HttpHandler::handleEvent($this->application, $event);
}
if($this->application->getTcpConfiguration()->isEnabled() && TcpHandler::isAvailable($this->application))
{
TcpHandler::handleEvent($this->application, $event);
}
if($this->application->getUdpConfiguration()->isEnabled() && UdpHandler::isAvailable($this->application))
{
UdpHandler::handleEvent($this->application, $event);
}
}
/**
* Retrieves the availability of the log handlers.
*
* @return array The availability of the log handlers.
*/
public function getAvailability(): array
{
return [
ConsoleHandler::class => ConsoleHandler::isAvailable($this->application),
DescriptorConfiguration::class => DescriptorHandler::isAvailable($this->application),
FileHandler::class => FileHandler::isAvailable($this->application),
HttpHandler::class => HttpHandler::isAvailable($this->application),
TcpHandler::class => TcpHandler::isAvailable($this->application),
UdpHandler::class => UdpHandler::isAvailable($this->application)
];
}
/**
* Retrieves the default ConsoleConfiguration instance.
*
* @return ConsoleConfiguration The default ConsoleConfiguration instance.
*/
public static function getDefaultConsoleConfiguration(): ConsoleConfiguration
{
if(self::$defaultConsoleConfiguration === null)
{
self::$defaultConsoleConfiguration = new ConsoleConfiguration();
// Apply environment variables to the default ConsoleConfiguration instance.
if(getenv('LOGLIB_CONSOLE_ENABLED') !== false)
{
self::$defaultConsoleConfiguration->setEnabled(filter_var(getenv('LOG_CONSOLE_ENABLED'), FILTER_VALIDATE_BOOLEAN));
}
if(getenv('LOGLIB_CONSOLE_DISPLAY_NAME') !== false)
{
self::$defaultConsoleConfiguration->setDisplayName(filter_var(getenv('LOG_CONSOLE_DISPLAY_NAME'), FILTER_VALIDATE_BOOLEAN));
}
if(getenv('LOGLIB_CONSOLE_DISPLAY_LEVEL') !== false)
{
self::$defaultConsoleConfiguration->setDisplayLevel(filter_var(getenv('LOG_CONSOLE_DISPLAY_LEVEL'), FILTER_VALIDATE_BOOLEAN));
}
if(getenv('LOGLIB_CONSOLE_ANSI_FORMAT') !== false)
{
self::$defaultConsoleConfiguration->setAnsiFormat(AnsiFormat::parseFrom(getenv('LOGLIB_CONSOLE_ANSI_FORMAT')));
}
if(getenv('LOGLIB_CONSOLE_TRACE_FORMAT') !== false)
{
self::$defaultConsoleConfiguration->setTraceFormat(TraceFormat::parseFrom(getenv('LOGLIB_CONSOLE_TRACE_FORMAT')));
}
if(getenv('LOGLIB_CONSOLE_TIMESTAMP_FORMAT') !== false)
{
self::$defaultConsoleConfiguration->setTimestampFormat(TimestampFormat::parseFrom(getenv('LOGLIB_CONSOLE_TIMESTAMP_FORMAT')));
}
}
return self::$defaultConsoleConfiguration;
}
/**
* Retrieves the default DescriptorConfiguration instance.
*
* @return DescriptorConfiguration The default DescriptorConfiguration instance.
*/
public static function getDefaultDescriptorConfiguration(): DescriptorConfiguration
{
if(self::$defaultDescriptorConfiguration === null)
{
self::$defaultDescriptorConfiguration = new DescriptorConfiguration();
// Apply environment variables to the default DescriptorConfiguration instance.
if(getenv('LOGLIB_DESCRIPTOR_ENABLED') !== false)
{
self::$defaultDescriptorConfiguration->setEnabled(filter_var(getenv('LOGLIB_DESCRIPTOR_ENABLED'), FILTER_VALIDATE_BOOLEAN));
}
if(getenv('LOGLIB_DESCRIPTOR_PATH') !== false)
{
self::$defaultDescriptorConfiguration->setDescriptor(getenv('LOGLIB_DESCRIPTOR_PATH'));
}
if(getenv('LOGLIB_DESCRIPTOR_APPEND_NEWLINE') !== false)
{
self::$defaultDescriptorConfiguration->setAppendNewline(filter_var(getenv('LOGLIB_DESCRIPTOR_APPEND_NEWLINE'), FILTER_VALIDATE_BOOLEAN));
}
if(getenv('LOGLIB_DESCRIPTOR_LOG_FORMAT') !== false)
{
self::$defaultDescriptorConfiguration->setLogFormat(LogFormat::parseFrom(getenv('LOGLIB_DESCRIPTOR_LOG_FORMAT')));
}
if(getenv('LOGLIB_DESCRIPTOR_TIMESTAMP_FORMAT') !== false)
{
self::$defaultDescriptorConfiguration->setTimestampFormat(TimestampFormat::parseFrom(getenv('LOGLIB_DESCRIPTOR_TIMESTAMP_FORMAT')));
}
if(getenv('LOGLIB_DESCRIPTOR_TRACE_FORMAT') !== false)
{
self::$defaultDescriptorConfiguration->setTraceFormat(TraceFormat::parseFrom(getenv('LOGLIB_DESCRIPTOR_TRACE_FORMAT')));
}
}
return self::$defaultDescriptorConfiguration;
}
/**
* Retrieves the default FileConfiguration instance.
*
* @return FileConfiguration The default FileConfiguration instance.
*/
public static function getDefaultFileConfiguration(): FileConfiguration
{
if(self::$defaultFileConfiguration === null)
{
self::$defaultFileConfiguration = new FileConfiguration();
// Apply environment variables to the default FileConfiguration instance.
if(getenv('LOGLIB_FILE_ENABLED') !== false)
{
self::$defaultFileConfiguration->setEnabled(filter_var(getenv('LOGLIB_FILE_ENABLED'), FILTER_VALIDATE_BOOLEAN));
}
if(getenv('LOGLIB_FILE_DEFAULT_PERMISSIONS') !== false)
{
$permissions = octdec(filter_var(getenv('LOGLIB_FILE_DEFAULT_PERMISSIONS'), FILTER_VALIDATE_INT));
if($permissions !== false)
{
self::$defaultFileConfiguration->setDefaultPermissions($permissions);
}
}
if(getenv('LOGLIB_FILE_PATH') !== false)
{
// Parse magic constants in the file path.
$path = getenv('LOGLIB_FILE_PATH');
$path = str_ireplace('%CWD%', getcwd(), $path);
$path = str_ireplace('%HOME%', getenv('HOME') ?? sys_get_temp_dir(), $path);
$path = str_ireplace('%TMP%', sys_get_temp_dir(), $path);
$path = str_ireplace('%TEMP%', sys_get_temp_dir(), $path);
if(!is_dir($path))
{
@mkdir($path, self::$defaultFileConfiguration->getDefaultPermissions(), true);
}
self::$defaultFileConfiguration->setLogPath($path);
}
if(getenv('LOGLIB_FILE_APPEND_NEWLINE') !== false)
{
self::$defaultFileConfiguration->setAppendNewline(filter_var(getenv('LOGLIB_FILE_APPEND_NEWLINE'), FILTER_VALIDATE_BOOLEAN));
}
if(getenv('LOGLIB_FILE_LOG_FORMAT') !== false)
{
self::$defaultFileConfiguration->setLogFormat(LogFormat::parseFrom(getenv('LOGLIB_FILE_LOG_FORMAT')));
}
if(getenv('LOGLIB_FILE_TIMESTAMP_FORMAT') !== false)
{
self::$defaultFileConfiguration->setTimestampFormat(TimestampFormat::parseFrom(getenv('LOGLIB_FILE_TIMESTAMP_FORMAT')));
}
if(getenv('LOGLIB_FILE_TRACE_FORMAT') !== false)
{
self::$defaultFileConfiguration->setTraceFormat(TraceFormat::parseFrom(getenv('LOGLIB_FILE_TRACE_FORMAT')));
}
}
return self::$defaultFileConfiguration;
}
/**
* Retrieves the default HttpConfiguration instance.
*
* @return HttpConfiguration The default HttpConfiguration instance.
*/
public static function getDefaultHttpConfiguration(): HttpConfiguration
{
if(self::$defaultHttpConfiguration === null)
{
self::$defaultHttpConfiguration = new HttpConfiguration();
// Apply environment variables to the default HttpConfiguration instance.
if(getenv('LOGLIB_HTTP_ENABLED') !== false)
{
self::$defaultHttpConfiguration->setEnabled(filter_var(getenv('LOGLIB_HTTP_ENABLED'), FILTER_VALIDATE_BOOLEAN));
}
if(getenv('LOGLIB_HTTP_ENDPOINT') !== false)
{
self::$defaultHttpConfiguration->setEndpoint(getenv('LOGLIB_HTTP_ENDPOINT'));
}
if(getenv('LOGLIB_HTTP_LOG_FORMAT') !== false)
{
self::$defaultHttpConfiguration->setLogFormat(LogFormat::parseFrom(getenv('LOGLIB_HTTP_LOG_FORMAT')));
}
if(getenv('LOGLIB_HTTP_TIMESTAMP_FORMAT') !== false)
{
self::$defaultHttpConfiguration->setTimestampFormat(TimestampFormat::parseFrom(getenv('LOGLIB_HTTP_TIMESTAMP_FORMAT')));
}
if(getenv('LOGLIB_HTTP_TRACE_FORMAT') !== false)
{
self::$defaultHttpConfiguration->setTraceFormat(TraceFormat::parseFrom(getenv('LOGLIB_HTTP_TRACE_FORMAT')));
}
}
return self::$defaultHttpConfiguration;
}
/**
* Retrieves the default TcpConfiguration instance.
*
* @return TcpConfiguration The default TcpConfiguration instance.
*/
public static function getDefaultTcpConfiguration(): TcpConfiguration
{
if(self::$defaultTcpConfiguration === null)
{
self::$defaultTcpConfiguration = new TcpConfiguration();
// Apply environment variables to the default TcpConfiguration instance.
if(getenv('LOGLIB_TCP_ENABLED') !== false)
{
self::$defaultTcpConfiguration->setEnabled(filter_var(getenv('LOGLIB_TCP_ENABLED'), FILTER_VALIDATE_BOOLEAN));
}
if(getenv('LOGLIB_TCP_HOST') !== false)
{
self::$defaultTcpConfiguration->setHost(getenv('LOGLIB_TCP_HOST'));
}
if(getenv('LOGLIB_TCP_PORT') !== false)
{
self::$defaultTcpConfiguration->setPort((int)getenv('LOGLIB_TCP_PORT'));
}
if(getenv('LOGLIB_TCP_APPEND_NEWLINE') !== false)
{
self::$defaultTcpConfiguration->setAppendNewline(filter_var(getenv('LOGLIB_TCP_APPEND_NEWLINE'), FILTER_VALIDATE_BOOLEAN));
}
if(getenv('LOGLIB_TCP_LOG_FORMAT') !== false)
{
self::$defaultTcpConfiguration->setLogFormat(LogFormat::parseFrom(getenv('LOGLIB_TCP_LOG_FORMAT')));
}
if(getenv('LOGLIB_TCP_TIMESTAMP_FORMAT') !== false)
{
self::$defaultTcpConfiguration->setTimestampFormat(TimestampFormat::parseFrom(getenv('LOGLIB_TCP_TIMESTAMP_FORMAT')));
}
}
return self::$defaultTcpConfiguration;
}
/**
* Retrieves the default UdpConfiguration instance.
*
* @return UdpConfiguration The default UdpConfiguration instance.
*/
public static function getDefaultUdpConfiguration(): UdpConfiguration
{
if(self::$defaultUdpConfiguration === null)
{
self::$defaultUdpConfiguration = new UdpConfiguration();
// Apply environment variables to the default UdpConfiguration instance.
if(getenv('LOGLIB_UDP_ENABLED') !== false)
{
self::$defaultUdpConfiguration->setEnabled(filter_var(getenv('LOGLIB_UDP_ENABLED'), FILTER_VALIDATE_BOOLEAN));
}
if(getenv('LOGLIB_UDP_HOST') !== false)
{
self::$defaultUdpConfiguration->setHost(getenv('LOGLIB_UDP_HOST'));
}
if(getenv('LOGLIB_UDP_PORT') !== false)
{
self::$defaultUdpConfiguration->setPort((int)getenv('LOGLIB_UDP_PORT'));
}
if(getenv('LOGLIB_UDP_APPEND_NEWLINE') !== false)
{
self::$defaultUdpConfiguration->setAppendNewline(filter_var(getenv('LOGLIB_UDP_APPEND_NEWLINE'), FILTER_VALIDATE_BOOLEAN));
}
if(getenv('LOGLIB_UDP_LOG_FORMAT') !== false)
{
self::$defaultUdpConfiguration->setLogFormat(LogFormat::parseFrom(getenv('LOGLIB_UDP_LOG_FORMAT')));
}
if(getenv('LOGLIB_UDP_TIMESTAMP_FORMAT') !== false)
{
self::$defaultUdpConfiguration->setTimestampFormat(TimestampFormat::parseFrom(getenv('LOGLIB_UDP_TIMESTAMP_FORMAT')));
}
if(getenv('LOGLIB_UDP_TRACE_FORMAT') !== false)
{
self::$defaultUdpConfiguration->setTraceFormat(TraceFormat::parseFrom(getenv('LOGLIB_UDP_TRACE_FORMAT')));
}
}
return self::$defaultUdpConfiguration;
}
/**
* Retrieves the backtrace level.
*
* @return int The backtrace level.
*/
public static function getBacktraceLevel(): int
{
return self::$backtraceLevel;
}
/**
* Sets the backtrace level.
*
* @param int $backtraceLevel The backtrace level.
*/
public static function setBacktraceLevel(int $backtraceLevel): void
{
self::$backtraceLevel = $backtraceLevel;
}
/**
* Retrieves the runtime logger instance.
*
* @return Logger The runtime logger instance.
*/
public static function getRuntimeLogger(): Logger
{
if(self::$runtimeLogger === null)
{
self::$runtimeLogger = new Logger('Runtime');
}
return self::$runtimeLogger;
}
/**
* Registers the log handlers.
*/
public static function registerHandlers(): void
{
if(self::$handlersRegistered)
{
return;
}
$logger = self::getRuntimeLogger();
// Register to catch all PHP errors & warnings.
set_error_handler(function($errno, $errstr, $errfile, $errline) use ($logger)
{
switch($errno)
{
case E_ERROR:
case E_CORE_ERROR:
case E_COMPILE_ERROR:
case E_USER_ERROR:
case E_RECOVERABLE_ERROR:
case E_CORE_WARNING:
case E_COMPILE_WARNING:
case E_PARSE:
$logger->critical($errstr, Utilities::detailsFromError($errno, $errstr, $errfile, $errline));
break;
case E_WARNING:
case E_USER_WARNING:
case E_DEPRECATED:
case E_USER_DEPRECATED:
case E_NOTICE:
case E_USER_NOTICE:
case E_STRICT:
$logger->warning($errstr, Utilities::detailsFromError($errno, $errstr, $errfile, $errline));
break;
default:
$logger->error($errstr, Utilities::detailsFromError($errno, $errstr, $errfile, $errline));
break;
}
});
// Register to catch all uncaught exceptions.
set_exception_handler(function(Throwable $e) use ($logger)
{
$logger->error($e->getMessage(), $e);
});
// Register to catch fatal errors.
register_shutdown_function(function() use ($logger)
{
$error = error_get_last();
if($error !== null)
{
$logger->critical($error['message'], Utilities::detailsFromError($error['type'], $error['message'], $error['file'], $error['line']));
}
});
self::$handlersRegistered = true;
}
/**
* Unregisters the log handlers.
*/
public static function unregisterHandlers(): void
{
if(!self::$handlersRegistered)
{
return;
}
restore_error_handler();
restore_exception_handler();
self::$handlersRegistered = false;
}
/**
* Retrieves the registration status of the log handlers.
*
* @return bool Returns true if the log handlers are registered, false otherwise.
*/
public static function isHandlersRegistered(): bool
{
return self::$handlersRegistered;
}
}

View file

@ -0,0 +1,189 @@
<?php
namespace LogLib2\Objects;
use LogLib2\Objects\Configurations\ConsoleConfiguration;
use LogLib2\Objects\Configurations\DescriptorConfiguration;
use LogLib2\Objects\Configurations\FileConfiguration;
use LogLib2\Objects\Configurations\HttpConfiguration;
use LogLib2\Objects\Configurations\TcpConfiguration;
use LogLib2\Objects\Configurations\UdpConfiguration;
class Application
{
private string $name;
private ConsoleConfiguration $consoleConfiguration;
private DescriptorConfiguration $descriptorConfiguration;
private FileConfiguration $fileConfiguration;
private HttpConfiguration $httpConfiguration;
private TcpConfiguration $tcpConfiguration;
private UdpConfiguration $udpConfiguration;
/**
* Constructs a new instance with the provided application name and log level.
*
* @param string $name The application name.
*/
public function __construct(string $name)
{
$this->name = $name;
$this->consoleConfiguration = new ConsoleConfiguration();
$this->descriptorConfiguration = new DescriptorConfiguration();
$this->fileConfiguration = new FileConfiguration();
$this->httpConfiguration = new HttpConfiguration();
$this->tcpConfiguration = new TcpConfiguration();
$this->udpConfiguration = new UdpConfiguration();
}
/**
* Retrieves the name of the application.
*
* @return string The name of the application.
*/
public function getName(): string
{
return $this->name;
}
/**
* Retrieves the ConsoleConfiguration instance for the application.
*
* @return ConsoleConfiguration The ConsoleConfiguration instance for the application.
*/
public function getConsoleConfiguration(): ConsoleConfiguration
{
return $this->consoleConfiguration;
}
/**
* Sets the ConsoleConfiguration instance for the application.
*
* @param ConsoleConfiguration $configuration The ConsoleConfiguration instance for the application.
* @return Application Returns the current instance for method chaining.
*/
public function setConsoleConfiguration(ConsoleConfiguration $configuration): Application
{
$this->consoleConfiguration = $configuration;
return $this;
}
/**
* Retrieves the DescriptorConfiguration instance for the application.
*
* @return DescriptorConfiguration The DescriptorConfiguration instance for the application.
*/
public function getDescriptorConfiguration(): DescriptorConfiguration
{
return $this->descriptorConfiguration;
}
/**
* Sets the DescriptorConfiguration instance for the application.
*
* @param DescriptorConfiguration $configuration The DescriptorConfiguration instance for the application.
* @return Application Returns the current instance for method chaining.
*/
public function setDescriptorConfiguration(DescriptorConfiguration $configuration): Application
{
$this->descriptorConfiguration = $configuration;
return $this;
}
/**
* Retrieves the FileConfiguration instance for the application.
*
* @return FileConfiguration The FileConfiguration instance for the application.
*/
public function getFileConfiguration(): FileConfiguration
{
return $this->fileConfiguration;
}
/**
* Sets the FileConfiguration instance for the application.
*
* @param FileConfiguration $configuration The FileConfiguration instance for the application.
* @return Application Returns the current instance for method chaining.
*/
public function setFileConfiguration(FileConfiguration $configuration): Application
{
$this->fileConfiguration = $configuration;
return $this;
}
/**
* Retrieves the HttpConfiguration instance for the application.
*
* @return HttpConfiguration The HttpConfiguration instance for the application.
*/
public function getHttpConfiguration(): HttpConfiguration
{
return $this->httpConfiguration;
}
/**
* Sets the HttpConfiguration instance for the application.
*
* @param HttpConfiguration $configuration The HttpConfiguration instance for the application.
* @return Application Returns the current instance for method chaining.
*/
public function setHttpConfiguration(HttpConfiguration $configuration): Application
{
$this->httpConfiguration = $configuration;
return $this;
}
/**
* Retrieves the TcpConfiguration instance for the application.
*
* @return TcpConfiguration The TcpConfiguration instance for the application.
*/
public function getTcpConfiguration(): TcpConfiguration
{
return $this->tcpConfiguration;
}
/**
* Sets the TcpConfiguration instance for the application.
*
* @param TcpConfiguration $configuration The TcpConfiguration instance for the application.
* @return Application Returns the current instance for method chaining.
*/
public function setTcpConfiguration(TcpConfiguration $configuration): Application
{
$this->tcpConfiguration = $configuration;
return $this;
}
/**
* Retrieves the UdpConfiguration instance for the application.
*
* @return UdpConfiguration The UdpConfiguration instance for the application.
*/
public function getUdpConfiguration(): UdpConfiguration
{
return $this->udpConfiguration;
}
/**
* Sets the UdpConfiguration instance for the application.
*
* @param UdpConfiguration $configuration The UdpConfiguration instance for the application.
* @return Application Returns the current instance for method chaining.
*/
public function setUdpConfiguration(UdpConfiguration $configuration): Application
{
$this->udpConfiguration = $configuration;
return $this;
}
/**
* Retrieves the string representation of the application.
*
* @return string The string representation of the application.
*/
public function __toString(): string
{
return $this->name;
}
}

View file

@ -0,0 +1,162 @@
<?php
namespace LogLib2\Objects\Configurations;
use LogLib2\Enums\AnsiFormat;
use LogLib2\Enums\TimestampFormat;
use LogLib2\Enums\TraceFormat;
class ConsoleConfiguration
{
private bool $enabled;
private bool $displayName;
private bool $displayLevel;
private AnsiFormat $ansiFormat;
private TraceFormat $traceFormat;
private TimestampFormat $timestampFormat;
/**
* ConsoleConfiguration constructor.
*/
public function __construct()
{
$this->enabled = true;
$this->displayName = true;
$this->displayLevel = true;
$this->ansiFormat = AnsiFormat::BASIC;
$this->traceFormat = TraceFormat::BASIC;
$this->timestampFormat = TimestampFormat::TIME_ONLY;
}
/**
* Retrieves the enabled status of the console configuration.
*
* @return bool Returns true if the console configuration is enabled, false otherwise.
*/
public function isEnabled(): bool
{
return $this->enabled;
}
/**
* Sets the enabled status of the console configuration.
*
* @param bool $enabled The enabled status to set.
* @return ConsoleConfiguration Returns the current instance.
*/
public function setEnabled(bool $enabled): ConsoleConfiguration
{
$this->enabled = $enabled;
return $this;
}
/**
* Retrieves the display name status of the console configuration.
*
* @return bool Returns true if the display name is enabled, false otherwise.
*/
public function isDisplayName(): bool
{
return $this->displayName;
}
/**
* Sets the display name status of the console configuration.
*
* @param bool $displayName The display name status to set.
* @return ConsoleConfiguration Returns the current instance.
*/
public function setDisplayName(bool $displayName): ConsoleConfiguration
{
$this->displayName = $displayName;
return $this;
}
/**
* Retrieves the display level status of the console configuration.
*
* @return bool Returns true if the display level is enabled, false otherwise.
*/
public function isDisplayLevel(): bool
{
return $this->displayLevel;
}
/**
* Sets the display level status of the console configuration.
*
* @param bool $displayLevel The display level status to set.
* @return ConsoleConfiguration Returns the current instance.
*/
public function setDisplayLevel(bool $displayLevel): ConsoleConfiguration
{
$this->displayLevel = $displayLevel;
return $this;
}
/**
* Retrieves the ANSI format of the console configuration.
*
* @return AnsiFormat Returns the ANSI format as an AnsiForamt.
*/
public function getAnsiFormat(): AnsiFormat
{
return $this->ansiFormat;
}
/**
* Sets the ANSI format of the console configuration.
*
* @param AnsiFormat $ansiFormat The ANSI format to set.
* @return ConsoleConfiguration Returns the current instance.
*/
public function setAnsiFormat(AnsiFormat $ansiFormat): ConsoleConfiguration
{
$this->ansiFormat = $ansiFormat;
return $this;
}
/**
* Retrieves the trace format of the console configuration.
*
* @return TraceFormat Returns the trace format as a TraceFormat.
*/
public function getTraceFormat(): TraceFormat
{
return $this->traceFormat;
}
/**
* Sets the trace format of the console configuration.
*
* @param TraceFormat $traceFormat The trace format to set.
* @return ConsoleConfiguration Returns the current instance.
*/
public function setTraceFormat(TraceFormat $traceFormat): ConsoleConfiguration
{
$this->traceFormat = $traceFormat;
return $this;
}
/**
* Retrieves the timestamp format of the console configuration.
*
* @return TimestampFormat Returns the timestamp format as a TimestampFormat.
*/
public function getTimestampFormat(): TimestampFormat
{
return $this->timestampFormat;
}
/**
* Sets the timestamp format of the console configuration.
*
* @param TimestampFormat $timestampFormat The timestamp format to set.
* @return ConsoleConfiguration Returns the current instance.
*/
public function setTimestampFormat(TimestampFormat $timestampFormat): ConsoleConfiguration
{
$this->timestampFormat = $timestampFormat;
return $this;
}
}

View file

@ -0,0 +1,162 @@
<?php
namespace LogLib2\Objects\Configurations;
use LogLib2\Enums\LogFormat;
use LogLib2\Enums\TimestampFormat;
use LogLib2\Enums\TraceFormat;
class DescriptorConfiguration
{
private bool $enabled;
private string $descriptor;
private bool $appendNewline;
private LogFormat $logFormat;
private TimestampFormat $timestampFormat;
private TraceFormat $traceFormat;
/**
* FileConfiguration constructor.
*/
public function __construct()
{
$this->enabled = false;
$this->descriptor = DIRECTORY_SEPARATOR . 'dev' . DIRECTORY_SEPARATOR . 'null';
$this->appendNewline = false;
$this->logFormat = LogFormat::JSONL;
$this->timestampFormat = TimestampFormat::UNIX_TIMESTAMP;
$this->traceFormat = TraceFormat::FULL;
}
/**
* Retrieves the enabled status of the descriptor configuration.
*
* @return bool Returns true if the descriptor configuration is enabled, false otherwise.
*/
public function isEnabled(): bool
{
return $this->enabled;
}
/**
* Sets the enabled status of the descriptor configuration.
*
* @param bool $enabled The enabled status to set.
* @return DescriptorConfiguration Returns the current instance.
*/
public function setEnabled(bool $enabled): DescriptorConfiguration
{
$this->enabled = $enabled;
return $this;
}
/**
* Retrieves the descriptor of the descriptor configuration.
*
* @return string Returns the descriptor as a string.
*/
public function getDescriptor(): string
{
return $this->descriptor;
}
/**
* Sets the descriptor of the descriptor configuration.
*
* @param string $descriptor The descriptor to set.
* @return DescriptorConfiguration Returns the current instance.
*/
public function setDescriptor(string $descriptor): DescriptorConfiguration
{
$this->descriptor = $descriptor;
return $this;
}
/**
* Retrieves the append newline status of the descriptor configuration.
*
* @return bool Returns true if the descriptor configuration appends a newline, false otherwise.
*/
public function isAppendNewline(): bool
{
return $this->appendNewline;
}
/**
* Sets the append newline status of the descriptor configuration.
*
* @param bool $appendNewline The append newline status to set.
* @return DescriptorConfiguration Returns the current instance.
*/
public function setAppendNewline(bool $appendNewline): DescriptorConfiguration
{
$this->appendNewline = $appendNewline;
return $this;
}
/**
* Retrieves the log type of the descriptor configuration.
*
* @return LogFormat Returns the log type.
*/
public function getLogFormat(): LogFormat
{
return $this->logFormat;
}
/**
* Sets the log type of the descriptor configuration.
*
* @param LogFormat $logFormat The log type to set.
* @return DescriptorConfiguration Returns the current instance.
*/
public function setLogFormat(LogFormat $logFormat): DescriptorConfiguration
{
$this->logFormat = $logFormat;
return $this;
}
/**
* Retrieves the timestamp type of the descriptor configuration.
*
* @return TimestampFormat Returns the timestamp type.
*/
public function getTimestampFormat(): TimestampFormat
{
return $this->timestampFormat;
}
/**
* Sets the timestamp type of the descriptor configuration.
*
* @param TimestampFormat $timestampFormat The timestamp type to set.
* @return DescriptorConfiguration Returns the current instance.
*/
public function setTimestampFormat(TimestampFormat $timestampFormat): DescriptorConfiguration
{
$this->timestampFormat = $timestampFormat;
return $this;
}
/**
* Retrieves the trace format of the descriptor configuration.
*
* @return TraceFormat Returns the trace format.
*/
public function getTraceFormat(): TraceFormat
{
return $this->traceFormat;
}
/**
* Sets the trace format of the descriptor configuration.
*
* @param TraceFormat $traceFormat The trace format to set.
* @return DescriptorConfiguration Returns the current instance.
*/
public function setTraceFormat(TraceFormat $traceFormat): DescriptorConfiguration
{
$this->traceFormat = $traceFormat;
return $this;
}
}

View file

@ -0,0 +1,210 @@
<?php
namespace LogLib2\Objects\Configurations;
use LogLib2\Classes\Utilities;
use LogLib2\Enums\LogFormat;
use LogLib2\Enums\LogLevel;
use LogLib2\Enums\TimestampFormat;
use LogLib2\Enums\TraceFormat;
class FileConfiguration
{
private bool $enabled;
private LogLevel $logLevel;
private string $logPath;
private int $defaultPermissions;
private bool $appendNewline;
private LogFormat $logFormat;
private TimestampFormat $timestampFormat;
private TraceFormat $traceFormat;
/**
* FileConfiguration constructor.
*/
public function __construct()
{
$this->enabled = true;
$this->logPath = Utilities::getEnvironmentLogPath();
$this->defaultPermissions = 0777;
$this->appendNewline = true;
$this->logFormat = LogFormat::TXT;
$this->timestampFormat = TimestampFormat::TIME_ONLY;
$this->traceFormat = TraceFormat::BASIC;
}
/**
* Retrieves the enabled status of the file configuration.
*
* @return bool Returns true if the file configuration is enabled, false otherwise.
*/
public function isEnabled(): bool
{
return $this->enabled;
}
/**
* Sets the enabled status of the file configuration.
*
* @param bool $enabled The enabled status to set.
* @return FileConfiguration Returns the current instance.
*/
public function setEnabled(bool $enabled): FileConfiguration
{
$this->enabled = $enabled;
return $this;
}
/**
* Retrieves the log level of the file configuration.
*
* @return LogLevel Returns the log level as a LogLevel.
*/
public function getLogLevel(): LogLevel
{
return $this->logLevel;
}
/**
* Sets the log level of the file configuration.
*
* @param LogLevel $logLevel The log level to set.
* @return FileConfiguration Returns the current instance.
*/
public function setLogLevel(LogLevel $logLevel): FileConfiguration
{
$this->logLevel = $logLevel;
return $this;
}
/**
* Retrieves the log path of the file configuration.
*
* @return string Returns the log path as a string.
*/
public function getLogPath(): string
{
return $this->logPath;
}
/**
* Sets the log path of the file configuration.
*
* @param string $logPath The log path to set.
* @return FileConfiguration Returns the current instance.
*/
public function setLogPath(string $logPath): FileConfiguration
{
$this->logPath = $logPath;
return $this;
}
/**
* Retrieves the default permissions of the file configuration.
*
* @return int Returns the default permissions as an integer.
*/
public function getDefaultPermissions(): int
{
return $this->defaultPermissions;
}
/**
* @param int $defaultPermissions
* @return FileConfiguration
*/
public function setDefaultPermissions(int $defaultPermissions): FileConfiguration
{
$this->defaultPermissions = $defaultPermissions;
return $this;
}
/**
* Retrieves the append newline status of the file configuration.
*
* @return bool Returns true if the file configuration appends a newline, false otherwise.
*/
public function isAppendNewline(): bool
{
return $this->appendNewline;
}
/**
* Sets the append newline status of the file configuration.
*
* @param bool $appendNewline The append newline status to set.
* @return FileConfiguration Returns the current instance.
*/
public function setAppendNewline(bool $appendNewline): FileConfiguration
{
$this->appendNewline = $appendNewline;
return $this;
}
/**
* Retrieves the log type of the file configuration.
*
* @return LogFormat Returns the log type.
*/
public function getLogFormat(): LogFormat
{
return $this->logFormat;
}
/**
* Sets the log type of the file configuration.
*
* @param LogFormat $logFormat The log type to set.
* @return FileConfiguration Returns the current instance.
*/
public function setLogFormat(LogFormat $logFormat): FileConfiguration
{
$this->logFormat = $logFormat;
return $this;
}
/**
* Retrieves the timestamp format of the file configuration.
*
* @return TimestampFormat Returns the timestamp format as a TimestampFormat.
*/
public function getTimestampFormat(): TimestampFormat
{
return $this->timestampFormat;
}
/**
* Sets the timestamp format of the file configuration.
*
* @param TimestampFormat $timestampFormat The timestamp format to set.
* @return FileConfiguration Returns the current instance.
*/
public function setTimestampFormat(TimestampFormat $timestampFormat): FileConfiguration
{
$this->timestampFormat = $timestampFormat;
return $this;
}
/**
* Retrieves the trace format of the file configuration.
*
* @return TraceFormat Returns the trace format as a TraceFormat.
*/
public function getTraceFormat(): TraceFormat
{
return $this->traceFormat;
}
/**
* Sets the trace format of the file configuration.
*
* @param TraceFormat $traceFormat The trace format to set.
* @return FileConfiguration Returns the current instance.
*/
public function setTraceFormat(TraceFormat $traceFormat): FileConfiguration
{
$this->traceFormat = $traceFormat;
return $this;
}
}

View file

@ -0,0 +1,162 @@
<?php
namespace LogLib2\Objects\Configurations;
use LogLib2\Enums\LogFormat;
use LogLib2\Enums\TimestampFormat;
use LogLib2\Enums\TraceFormat;
class HttpConfiguration
{
private bool $enabled;
private string $endpoint;
private bool $appendNewline;
private LogFormat $logFormat;
private TimestampFormat $timestampFormat;
private TraceFormat $traceFormat;
/**
* HttpConfiguration constructor.
*/
public function __construct()
{
$this->enabled = false;
$this->endpoint = 'http://0.0.0.0:5131';
$this->appendNewline = false;
$this->logFormat = LogFormat::JSONL;
$this->timestampFormat = TimestampFormat::UNIX_TIMESTAMP;
$this->traceFormat = TraceFormat::FULL;
}
/**
* Retrieves the enabled status of the HTTP configuration.
*
* @return bool Returns true if the HTTP configuration is enabled, false otherwise.
*/
public function isEnabled(): bool
{
return $this->enabled;
}
/**
* Sets the enabled status of the HTTP configuration.
*
* @param bool $enabled The enabled status to set.
* @return HttpConfiguration Returns the current instance.
*/
public function setEnabled(bool $enabled): HttpConfiguration
{
$this->enabled = $enabled;
return $this;
}
/**
* Retrieves the endpoint of the HTTP configuration.
*
* @return string Returns the endpoint as a string.
*/
public function getEndpoint(): string
{
return $this->endpoint;
}
/**
* Sets the endpoint of the HTTP configuration.
*
* @param string $endpoint The endpoint to set.
* @return HttpConfiguration Returns the current instance.
*/
public function setEndpoint(string $endpoint): HttpConfiguration
{
$this->endpoint = $endpoint;
return $this;
}
/**
* Retrieves the append newline status of the HTTP configuration.
*
* @return bool Returns true if the HTTP configuration appends a newline, false otherwise.
*/
public function isAppendNewline(): bool
{
return $this->appendNewline;
}
/**
* Sets the append newline status of the HTTP configuration.
*
* @param bool $appendNewline The append newline status to set.
* @return HttpConfiguration Returns the current instance.
*/
public function setAppendNewline(bool $appendNewline): HttpConfiguration
{
$this->appendNewline = $appendNewline;
return $this;
}
/**
* Retrieves the log format of the HTTP configuration.
*
* @return LogFormat Returns the log format.
*/
public function getLogFormat(): LogFormat
{
return $this->logFormat;
}
/**
* Sets the log format of the HTTP configuration.
*
* @param LogFormat $logFormat The log format to set.
* @return HttpConfiguration Returns the current instance.
*/
public function setLogFormat(LogFormat $logFormat): HttpConfiguration
{
$this->logFormat = $logFormat;
return $this;
}
/**
* Retrieves the timestamp format of the HTTP configuration.
*
* @return TimestampFormat Returns the timestamp format.
*/
public function getTimestampFormat(): TimestampFormat
{
return $this->timestampFormat;
}
/**
* Sets the timestamp format of the HTTP configuration.
*
* @param TimestampFormat $timestampFormat The timestamp format to set.
* @return HttpConfiguration Returns the current instance.
*/
public function setTimestampFormat(TimestampFormat $timestampFormat): HttpConfiguration
{
$this->timestampFormat = $timestampFormat;
return $this;
}
/**
* Retrieves the trace format of the HTTP configuration.
*
* @return TraceFormat Returns the trace format.
*/
public function getTraceFormat(): TraceFormat
{
return $this->traceFormat;
}
/**
* Sets the trace format of the HTTP configuration.
*
* @param TraceFormat $traceFormat The trace format to set.
* @return HttpConfiguration Returns the current instance.
*/
public function setTraceFormat(TraceFormat $traceFormat): HttpConfiguration
{
$this->traceFormat = $traceFormat;
return $this;
}
}

View file

@ -0,0 +1,174 @@
<?php
namespace LogLib2\Objects\Configurations;
use LogLib2\Enums\LogFormat;
use LogLib2\Enums\TimestampFormat;
use LogLib2\Enums\TraceFormat;
class TcpConfiguration
{
private bool $enabled;
private string $host;
private int $port;
private bool $appendNewline;
private LogFormat $logFormat;
private TimestampFormat $timestampFormat;
private TraceFormat $traceFormat;
/**
* TcpConfiguration constructor.
*/
public function __construct()
{
$this->enabled = false;
$this->host = '0.0.0.0';
$this->port = 5131;
$this->appendNewline = false;
$this->logFormat = LogFormat::JSONL;
$this->timestampFormat = TimestampFormat::UNIX_TIMESTAMP;
$this->traceFormat = TraceFormat::FULL;
}
/**
* Retrieves the enabled status of the TCP configuration.
*
* @return bool Returns true if the TCP configuration is enabled, false otherwise.
*/
public function isEnabled(): bool
{
return $this->enabled;
}
/**
* Sets the enabled status of the TCP configuration.
*
* @param bool $enabled The enabled status to set.
* @return TcpConfiguration Returns the current instance.
*/
public function setEnabled(bool $enabled): TcpConfiguration
{
$this->enabled = $enabled;
return $this;
}
/**
* Retrieves the host of the TCP configuration.
*
* @return string Returns the host as a string.
*/
public function getHost(): string
{
return $this->host;
}
/**
* Sets the host of the TCP configuration.
*
* @param string $host The host to set.
* @return TcpConfiguration Returns the current instance.
*/
public function setHost(string $host): TcpConfiguration
{
$this->host = $host;
return $this;
}
/**
* Retrieves the port of the TCP configuration.
*
* @return int Returns the port as an integer.
*/
public function getPort(): int
{
return $this->port;
}
/**
* Sets the port of the TCP configuration.
*
* @param int $port The port to set.
* @return TcpConfiguration Returns the current instance.
*/
public function setPort(int $port): TcpConfiguration
{
$this->port = $port;
return $this;
}
/**
* Retrieves the append newline status of the TCP configuration.
*
* @return bool Returns true if the TCP configuration appends a newline, false otherwise.
*/
public function isAppendNewline(): bool
{
return $this->appendNewline;
}
/**
* Sets the append newline status of the TCP configuration.
*
* @param bool $appendNewline The append newline status to set.
* @return TcpConfiguration Returns the current instance.
*/
public function setAppendNewline(bool $appendNewline): TcpConfiguration
{
$this->appendNewline = $appendNewline;
return $this;
}
/**
* Retrieves the log format of the TCP configuration.
*
* @return LogFormat Returns the log format.
*/
public function getLogFormat(): LogFormat
{
return $this->logFormat;
}
/**
* Sets the log format of the TCP configuration.
*
* @param LogFormat $logFormat The log format to set.
* @return TcpConfiguration Returns the current instance.
*/
public function setLogFormat(LogFormat $logFormat): TcpConfiguration
{
$this->logFormat = $logFormat;
return $this;
}
/**
* Retrieves the timestamp format of the TCP configuration.
*
* @return TimestampFormat Returns the timestamp format.
*/
public function getTimestampFormat(): TimestampFormat
{
return $this->timestampFormat;
}
/**
* Sets the timestamp format of the TCP configuration.
*
* @param TimestampFormat $timestampFormat The timestamp format to set.
* @return TcpConfiguration Returns the current instance.
*/
public function setTimestampFormat(TimestampFormat $timestampFormat): TcpConfiguration
{
$this->timestampFormat = $timestampFormat;
return $this;
}
/**
* Retrieves the trace format of the TCP configuration.
*
* @return TraceFormat Returns the trace format.
*/
public function getTraceFormat(): TraceFormat
{
return $this->traceFormat;
}
}

View file

@ -0,0 +1,186 @@
<?php
namespace LogLib2\Objects\Configurations;
use LogLib2\Enums\LogFormat;
use LogLib2\Enums\TimestampFormat;
use LogLib2\Enums\TraceFormat;
class UdpConfiguration
{
private bool $enabled;
private string $host;
private int $port;
private bool $appendNewline;
private LogFormat $logFormat;
private TimestampFormat $timestampFormat;
private TraceFormat $traceFormat;
/**
* TcpConfiguration constructor.
*/
public function __construct()
{
$this->enabled = false;
$this->host = '0.0.0.0';
$this->port = 5131;
$this->appendNewline = false;
$this->logFormat = LogFormat::JSONL;
$this->timestampFormat = TimestampFormat::UNIX_TIMESTAMP;
$this->traceFormat = TraceFormat::FULL;
}
/**
* Retrieves the enabled status of the TCP configuration.
*
* @return bool Returns true if the TCP configuration is enabled, false otherwise.
*/
public function isEnabled(): bool
{
return $this->enabled;
}
/**
* Sets the enabled status of the TCP configuration.
*
* @param bool $enabled The enabled status to set.
* @return UdpConfiguration Returns the current instance.
*/
public function setEnabled(bool $enabled): UdpConfiguration
{
$this->enabled = $enabled;
return $this;
}
/**
* Retrieves the host of the TCP configuration.
*
* @return string Returns the host as a string.
*/
public function getHost(): string
{
return $this->host;
}
/**
* Sets the host of the TCP configuration.
*
* @param string $host The host to set.
* @return UdpConfiguration Returns the current instance.
*/
public function setHost(string $host): UdpConfiguration
{
$this->host = $host;
return $this;
}
/**
* Retrieves the port of the TCP configuration.
*
* @return int Returns the port as an integer.
*/
public function getPort(): int
{
return $this->port;
}
/**
* Sets the port of the TCP configuration.
*
* @param int $port The port to set.
* @return UdpConfiguration Returns the current instance.
*/
public function setPort(int $port): UdpConfiguration
{
$this->port = $port;
return $this;
}
/**
* Retrieves the append newline status of the TCP configuration.
*
* @return bool Returns true if the TCP configuration appends a newline, false otherwise.
*/
public function isAppendNewline(): bool
{
return $this->appendNewline;
}
/**
* Sets the append newline status of the TCP configuration.
*
* @param bool $appendNewline The append newline status to set.
* @return UdpConfiguration Returns the current instance.
*/
public function setAppendNewline(bool $appendNewline): UdpConfiguration
{
$this->appendNewline = $appendNewline;
return $this;
}
/**
* Retrieves the log format of the TCP configuration.
*
* @return LogFormat Returns the log format.
*/
public function getLogFormat(): LogFormat
{
return $this->logFormat;
}
/**
* Sets the log format of the TCP configuration.
*
* @param LogFormat $logFormat The log format to set.
* @return UdpConfiguration Returns the current instance.
*/
public function setLogFormat(LogFormat $logFormat): UdpConfiguration
{
$this->logFormat = $logFormat;
return $this;
}
/**
* Retrieves the timestamp format of the TCP configuration.
*
* @return TimestampFormat Returns the timestamp format.
*/
public function getTimestampFormat(): TimestampFormat
{
return $this->timestampFormat;
}
/**
* Sets the timestamp format of the TCP configuration.
*
* @param TimestampFormat $timestampFormat The timestamp format to set.
* @return UdpConfiguration Returns the current instance.
*/
public function setTimestampFormat(TimestampFormat $timestampFormat): UdpConfiguration
{
$this->timestampFormat = $timestampFormat;
return $this;
}
/**
* Retrieves the trace format of the TCP configuration.
*
* @return TraceFormat Returns the trace format.
*/
public function getTraceFormat(): TraceFormat
{
return $this->traceFormat;
}
/**
* Sets the trace format of the TCP configuration.
*
* @param TraceFormat $traceFormat The trace format to set.
* @return UdpConfiguration Returns the current instance.
*/
public function setTraceFormat(TraceFormat $traceFormat): UdpConfiguration
{
$this->traceFormat = $traceFormat;
return $this;
}
}

View file

@ -0,0 +1,215 @@
<?php
namespace LogLib2\Objects;
use LogLib2\Enums\LogLevel;
use LogLib2\Enums\TimestampFormat;
use LogLib2\Enums\TraceFormat;
use LogLib2\Interfaces\SerializableInterface;
use Throwable;
class Event implements SerializableInterface
{
private string $applicationName;
private int $timestamp;
private LogLevel $level;
private string $message;
private ?ExceptionDetails $exception;
/**
* @var StackTrace[]
*/
private array $traces;
/**
* Constructs a new instance with the provided parameters.
*
* @param string $applicationName The name of the application that generated the event.
* @param LogLevel $level The log level of the event.
* @param string $message The message of the event.
* @param array $backtrace The array of StackTrace instances.
* @param int|null $timestamp The timestamp of the event, or null if not specified (defaults to the current time).
* @param ExceptionDetails|Throwable|null $exceptionDetails The exception details, or null if not specified.
*/
public function __construct(string $applicationName, LogLevel $level, string $message, array $backtrace, ?int $timestamp=null, ExceptionDetails|Throwable|null $exceptionDetails=null)
{
if($exceptionDetails instanceof Throwable)
{
$exceptionDetails = ExceptionDetails::fromThrowable($exceptionDetails);
}
$this->applicationName = $applicationName;
if($timestamp === null)
{
$timestamp = time();
}
$this->timestamp = $timestamp;
$this->level = $level;
$this->message = $message;
$this->exception = $exceptionDetails;
$this->traces = $backtrace;
}
/**
* Retrieves the application name.
*
* @return string Returns the application name as a string.
*/
public function getApplicationName(): string
{
return $this->applicationName;
}
/**
* Retrieves the timestamp.
*
* @return int Returns the timestamp as an integer.
*/
public function getTimestamp(): int
{
return $this->timestamp;
}
/**
* Retrieves the log level.
*
* @return LogLevel Returns the log level as a LogLevel instance.
*/
public function getLevel(): LogLevel
{
return $this->level;
}
/**
* Retrieves the message.
*
* @return string Returns the message as a string.
*/
public function getMessage(): string
{
return $this->message;
}
/**
* Retrieves the exception details.
*
* @return ExceptionDetails|null Returns the exception details, or null if not set.
*/
public function getException(): ?ExceptionDetails
{
return $this->exception;
}
/**
* Retrieves the backtrace.
*
* @return StackTrace[] Returns the backtrace as an array of StackTrace instances.
*/
public function getTraces(): array
{
return $this->traces;
}
/**
* Retrieves the first trace.
*
* @return StackTrace|null Returns the first trace or null if no traces are set.
*/
public function getFirstTrace(): ?StackTrace
{
if(count($this->traces) > 0)
{
return $this->traces[0];
}
return null;
}
/**
* Returns a standard array representation of the event.
*
* @return array The standard array representation of the event.
*/
public function toStandardArray(TimestampFormat $timestampFormat, TraceFormat $traceFormat): array
{
$result = $this->toArray();
/// Rename the traces to stack_trace
$result['stack_trace'] = $result['traces'];
unset($result['traces']);
// Format the timestamp
if($timestampFormat === TimestampFormat::NONE)
{
$result['timestamp'] = TimestampFormat::UNIX_TIMESTAMP->format($result['timestamp']);
}
else
{
$result['timestamp'] = $timestampFormat->format($result['timestamp']);
}
// Format the trace
if(count($result['stack_trace']) > 0)
{
$result['trace'] = $traceFormat->format($this->getFirstTrace());
}
return $result;
}
/**
* @inheritDoc
*/
public function toArray(): array
{
$result = [
'application_name' => $this->applicationName,
'timestamp' => $this->timestamp,
'level' => $this->level->value,
'message' => $this->message,
'traces' => [],
'exception' => null,
];
foreach($this->traces as $trace)
{
$result['traces'][] = $trace->toArray();
}
if($this->exception !== null)
{
$result['exception'] = $this->exception->toArray();
}
return $result;
}
/**
* @inheritDoc
*/
public static function fromArray(?array $data=null): Event
{
$traces = [];
if(isset($data['traces']))
{
foreach($data['traces'] as $traceData)
{
$traces[] = StackTrace::fromArray($traceData);
}
}
$exceptionDetails = null;
if(isset($data['exception']))
{
$exceptionDetails = ExceptionDetails::fromArray($data['exception']);
}
return new Event(
$data['application_name'],
LogLevel::from($data['level']),
$data['message'],
$traces,
$data['timestamp'],
$exceptionDetails
);
}
}

View file

@ -0,0 +1,171 @@
<?php
namespace LogLib2\Objects;
use LogLib2\Interfaces\SerializableInterface;
use Throwable;
class ExceptionDetails implements SerializableInterface
{
private string $name;
private string $message;
private ?int $code;
private ?string $file;
private ?int $line;
/**
* @var StackTrace[]|null
*/
private ?array $trace;
private ?ExceptionDetails $previous;
/**
* Constructs a new instance with the provided parameters.
*
* @param string $name The name of the exception.
* @param string $message The exception message.
* @param int|null $code The exception code, or null if not specified.
* @param string|null $file The file name, or null if not specified.
* @param int|null $line The line number, or null if not specified.
* @param StackTrace[]|null $trace The array of StackTrace instances, or null if not provided.
* @param ExceptionDetails|null $previous The previous exception, or null if not specified.
*/
public function __construct(string $name, string $message, ?int $code=null, ?string $file=null, ?int $line=null, ?array $trace=null, ?ExceptionDetails $previous=null)
{
$this->name = $name;
$this->message = $message;
$this->code = $code;
$this->file = $file;
$this->line = $line;
$this->trace = $trace;
$this->previous = $previous;
}
public function getName(): string
{
return $this->name;
}
public function getMessage(): string
{
return $this->message;
}
public function getCode(): ?int
{
return $this->code;
}
public function getFile(): ?string
{
return $this->file;
}
public function getLine(): ?int
{
return $this->line;
}
public function getTrace(): ?array
{
return $this->trace;
}
public function getPrevious(): ?ExceptionDetails
{
return $this->previous;
}
/**
* @inheritDoc
*/
public function toArray(): array
{
$result = [
'name' => $this->name,
'message' => $this->message,
'code' => $this->code,
'file' => $this->file,
'line' => $this->line,
'trace' => [],
'previous' => null,
];
if($this->trace !== null)
{
foreach($this->trace as $trace)
{
$result['trace'][] = $trace->toArray();
}
}
if($this->previous !== null)
{
$result['previous'] = $this->previous->toArray();
}
return $result;
}
/**
* @inheritDoc
*/
public static function fromArray(?array $data=null): SerializableInterface
{
$trace = [];
if(isset($data['trace']))
{
foreach($data['trace'] as $traceData)
{
$trace[] = StackTrace::fromArray($traceData);
}
}
$previous = null;
if(isset($data['previous']))
{
$previous = self::fromArray($data['previous']);
}
return new ExceptionDetails(
$data['name'] ?? '',
$data['message'] ?? '',
$data['code'] ?? null,
$data['file'] ?? null,
$data['line'] ?? null,
$trace,
$previous
);
}
/**
* Creates a new instance from the provided Throwable instance.
*
* @param Throwable $e The Throwable instance to create the ExceptionDetails instance from.
* @return ExceptionDetails The created ExceptionDetails instance.
*/
public static function fromThrowable(Throwable $e): ExceptionDetails
{
$trace = [];
foreach($e->getTrace() as $traceData)
{
$trace[] = StackTrace::fromTrace($traceData);
}
$previous = null;
if($e->getPrevious() !== null)
{
$previous = self::fromThrowable($e->getPrevious());
}
return new ExceptionDetails(
get_class($e),
$e->getMessage(),
$e->getCode(),
$e->getFile(),
$e->getLine(),
$trace,
$previous
);
}
}

View file

@ -0,0 +1,207 @@
<?php
namespace LogLib2\Objects;
use LogLib2\Classes\Utilities;
use LogLib2\Enums\CallType;
use LogLib2\Interfaces\SerializableInterface;
class StackTrace implements SerializableInterface
{
private ?string $file;
private ?int $line;
private ?string $function;
private ?array $args;
private ?string $class;
private ?CallType $callType;
/**
* Constructs a new instance with the provided parameters.
*
* @param string|null $file The file name, or null if not specified.
* @param int|null $line The line number, or null if not specified.
* @param string|null $function The function name, or null if not specified.
* @param array|null $args The array of arguments, or null if not provided.
* @param string|null $class The class name, or null if not specified.
* @param CallType|null $callType The type of the call, or null if not specified.
*
* @return void
*/
public function __construct(?string $file=null, ?int $line=null, ?string $function=null, ?array $args=null, ?string $class=null, ?CallType $callType=null)
{
$this->file = $file;
$this->line = $line;
$this->function = $function;
if($args !== null && !empty($args))
{
$this->args = $args;
}
else
{
$this->args = null;
}
$this->class = $class;
$this->callType = $callType;
}
/**
* Retrieves the file name.
*
* @return string|null Returns the file as a string, or null if no file is set.
*/
public function getFile(): ?string
{
return $this->file;
}
/**
* Retrieves the line number.
*
* @return int|null Returns the line number or null if not set.
*/
public function getLine(): ?int
{
return $this->line;
}
/**
* Retrieves the function name.
*
* @return string|null The function name or null if not set.
*/
public function getFunction(): ?string
{
return $this->function;
}
/**
* Retrieves the arguments.
*
* @return array|null Returns an array of arguments or null if no arguments are set.
*/
public function getArgs(): ?array
{
return $this->args;
}
/**
*
* @return string|null The class name or null if not set.
*/
public function getClass(): ?string
{
return $this->class;
}
/**
* Retrieves the call type.
*
* @return CallType|null The call type or null if not set.
*/
public function getCallType(): ?CallType
{
return $this->callType;
}
/**
* Determines whether the current object contains no data.
*
* @return bool True if all properties are null, false otherwise.
*/
public function isEmpty(): bool
{
return
$this->file === null &&
$this->line === null &&
$this->function === null &&
$this->args === null &&
$this->class === null &&
$this->callType === null;
}
/**
* @inheritDoc
*/
public function toArray(): array
{
return [
'file' => $this->file,
'line' => $this->line,
'function' => $this->function,
'args' => $this->args,
'class' => $this->class,
'call_type' => $this->callType?->value ?? CallType::STATIC_CALL->value,
];
}
/**
* @inheritDoc
*/
public static function fromArray(?array $data=null): StackTrace
{
$callType = null;
if(isset($data['call_type']))
{
$callType = CallType::tryFrom($data['call_type']);
}
return new StackTrace(
$data['file'] ?? null,
$data['line'] ?? null,
$data['function'] ?? null,
$data['args'] ?? null,
$data['class'] ?? null,
$callType
);
}
/**
* Creates a new instance from the provided trace data.
*
* @param array $trace The trace data to be used.
* @return StackTrace The new instance created from the trace data.
*/
public static function fromTrace(array $trace): StackTrace
{
$parsedTrace = [
'file' => $trace['file'] ?? null,
'function' => $trace['function'] ?? null,
'class' => $trace['class'] ?? null,
'call' => $trace['call'] ?? null,
];
if(isset($trace['line']))
{
$parsedTrace['line'] = (int) $trace['line'];
}
else
{
$parsedTrace['line'] = null;
}
if(isset($trace['args']))
{
$result = [];
if(array_is_list($trace['args']))
{
foreach($trace['args'] as $arg)
{
$result[] = Utilities::getSafeValue($arg);
}
}
else
{
foreach($trace['args'] as $key => $arg)
{
$result[$key] = Utilities::getSafeValue($arg);
}
}
$parsedTrace['args'] = $result;
}
return StackTrace::fromArray($parsedTrace);
}
}

View file

@ -1,18 +0,0 @@
<?php
namespace LogLib2;
class Program
{
/**
* LogLib2 main entry point
*
* @param string[] $args Command-line arguments
* @return int Exit code
*/
public static function main(array $args): int
{
print("Hello World from net.nosial.loglib2!" . PHP_EOL);
return 0;
}
}

View file

@ -0,0 +1,11 @@
<?php
/**
* This file is used to patch the autoloader, ncc will load this file first before evaluating the package's class
* files. Without this file, ncc will run into a recursive dependency issue where classes are trying to load each
* other before they are defined.
*/
require __DIR__ . DIRECTORY_SEPARATOR . 'Objects' . DIRECTORY_SEPARATOR . 'Event.php';
require __DIR__ . DIRECTORY_SEPARATOR . 'Objects' . DIRECTORY_SEPARATOR . 'ExceptionDetails.php';
require __DIR__ . DIRECTORY_SEPARATOR . 'Objects' . DIRECTORY_SEPARATOR . 'StackTrace.php';