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

1
.idea/LogLib2.iml generated
View file

@ -4,6 +4,7 @@
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" /> <sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/examples" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/build" /> <excludeFolder url="file://$MODULE_DIR$/build" />
</content> </content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />

5
.idea/php.xml generated
View file

@ -10,6 +10,11 @@
<option name="highlightLevel" value="WARNING" /> <option name="highlightLevel" value="WARNING" />
<option name="transferred" value="true" /> <option name="transferred" value="true" />
</component> </component>
<component name="PhpIncludePathManager">
<include_path>
<path value="/var/ncc/packages/net.nosial.optslib=1.1.2" />
</include_path>
</component>
<component name="PhpProjectSharedConfiguration" php_language_level="8.3" /> <component name="PhpProjectSharedConfiguration" php_language_level="8.3" />
<component name="PhpStanOptionsConfiguration"> <component name="PhpStanOptionsConfiguration">
<option name="transferred" value="true" /> <option name="transferred" value="true" />

14
LICENSE Normal file
View file

@ -0,0 +1,14 @@
Copyright 2022-2025 Nosial All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of
the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

713
README.md Normal file
View file

@ -0,0 +1,713 @@
# LogLib2
LogLib2 is a lightweight logging library for php/ncc projects. This is the successor of the original LogLib library, due
to the many changes in the project structure, it is recommended to use this library instead of the original one.
With LogLib2 you can log events from your application to several different handlers built into the library such as
- Console Logging: Logs events to `stdout` and `stderr` streams.
- Descriptor Logging: Logs events to a file descriptor.
- File Logging: Logs events to a file using a file locking mechanism.
- HTTP Logging: Logs events to a remote server using HTTP POST requests.
- TCP Logging: Logs events to a remote TCP server
- UDP Logging: Logs events to a remote UDP server
Aside from Console logging, all other handlers supports up to 5 different log formats, which are:
- JSON Lines
- CSV
- TXT
- XML
- HTML
LogLib2 is designed to be silent-failing, this means that if an error occurs while logging an event, the library will
silently fail and continue to log events, this is to prevent the application from crashing due to a logging error.
## Table of Contents
<!-- TOC -->
* [LogLib2](#loglib2)
* [Table of Contents](#table-of-contents)
* [Installation](#installation)
* [Compiling](#compiling)
* [Documentation](#documentation)
* [Environment Variables](#environment-variables)
* [Console Variables](#console-variables)
* [Descriptor Variables](#descriptor-variables)
* [File Variables](#file-variables)
* [HTTP Variables](#http-variables)
* [TCP Variables](#tcp-variables)
* [UDP Variables](#udp-variables)
* [Log Levels](#log-levels)
* [Log Filtering](#log-filtering)
* [Command-Line Override](#command-line-override)
* [Environment Variable Override](#environment-variable-override)
* [Creating a Logger](#creating-a-logger)
* [Logging Events](#logging-events)
* [Changing Default Configuration](#changing-default-configuration)
* [Changing Logger Configuration](#changing-logger-configuration)
* [TCP/UDP Logging Server (python)](#tcpudp-logging-server-python)
* [Usage:](#usage)
* [Formatters](#formatters)
* [AnsiFormat](#ansiformat)
* [LogFormat](#logformat)
* [TimestampFormat](#timestampformat)
* [TraceFormat](#traceformat)
* [Object Types](#object-types)
* [Event](#event)
* [StackTrace](#stacktrace)
* [ExceptionDetails](#exceptiondetails)
* [CallTypes](#calltypes)
* [Log Formats](#log-formats)
* [JSONL](#jsonl)
* [CSV](#csv)
* [TXT](#txt)
* [XML](#xml)
* [License](#license)
<!-- TOC -->
## Installation
To install LogLib2, you can add the project as a dependency to your project.json file, for example
From the n64 repository
```json
{
"name": "net.nosial.loglib2",
"version": "latest",
"source": "nosial/libs.log2=latest@n64"
}
```
From the github repository
```json
{
"name": "net.nosial.loglib2",
"version": "latest",
"source": "nosial/loglib2=latest@github"
}
```
To install the library from the command line, you can use the following command, use the appropriate source for the
repository you want to install from, you may also use the `--build-source` flag to force ncc to build the package from
source rather than downloading a pre-built package if one is available.
```bash
ncc package install --package=nosial/loglib2=latest@github
```
## Compiling
To compile the library, you can use the Makefile provided in the project, the Makefile provides several targets for
compiling the library, the default target is `all` which compiles both the `release` and `debug` versions of the library.
To compile the library, you can use the following command
```bash
make all
```
To compile the library in `release` mode, you can use the following command
```bash
make release
```
You may also compile manually using the following commands
```bash
ncc build --config=release
```
To install the library, you can use the following command
```bash
ncc package install --package=build/release/net.nosial.loglib2.ncc --skip-dependencies --build-source --reinstall -y
```
------------------------------------------------------------------------------------------------------------------------
# Documentation
The LogLib2 library provides a simple and easy-to-use logging interface for logging events from your application to
several different handlers built into the library such as Console Logging, Descriptor Logging, File Logging, HTTP Logging,
TCP Logging, and UDP Logging. This documentation will provide an overview of the library and how to use it in your
application.
## Environment Variables
Environment Variables can be used to configure the default configuration properties of the library, this is useful when
you want to configure the library without needing to declare the default properties at the start of your application.
### Console Variables
The following environment variables can be used to configure the console logging handler, these variables are used to
configure the console logging handler for all logger instances.
| Variable Name | Excepted Value | Default Value | Description |
|--------------------------------|-------------------------|---------------|----------------------------------------------------------------------|
| `LOGLIB_CONSOLE_ENABLED` | `true`, `false` | `true` | Enable or disable console logging |
| `LOGLIB_CONSOLE_DISPLAY_NAME` | `true`, `false` | `true` | Enable or disable display name of the application in console logging |
| `LOGLIB_CONSOLE_DISPLAY_LEVEL` | `true`, `false` | `true` | Enable or disable display level of the log event in console logging |
| `LOGLIB_CONSOLE_ANSI_FORMAT` | `basic`, `none` | `basic` | Enable or disable ANSI formatting in console logging |
| `LOGLIB_CONSOLE_TRACE_FORMAT` | `none`, `basic`, `full` | `basic` | Enable or disable trace formatting |
### Descriptor Variables
The following environment variables can be used to configure the descriptor logging handler, these variables are used to
configure the descriptor logging handler for all logger instances.
| Variable Name | Excepted Value | Default Value | Description |
|--------------------------------------|---------------------------------------------------------------------------------------------------------|----------------|--------------------------------------------------------|
| `LOGLIB_DESCRIPTOR_ENABLED` | `true`, `false` | `false` | Enable or disable descriptor logging |
| `LOGLIB_DESCRIPTOR_PATH` | `string` | `php://stdout` | The path to the descriptor file |
| `LOGLIB_DESCRIPTOR_APPEND_NEWLINE` | `true`, `false` | `true` | Enable or disable appending a newline to the log entry |
| `LOGLIB_DESCRIPTOR_LOG_FORMAT` | `jsonl`, `csv`, `txt`, `xml`, `html` | `jsonl` | The format of the log entry |
| `LOGLIB_DESCRIPTOR_TIMESTAMP_FORMAT` | `none`, `time_only`, `time_only_millis`, `date_only`, `date_time`. `date_time_millis`, `unix_timestamp` | `date_time` | The format of the timestamp |
| `LOGLIB_DESCRIPTOR_TRACE_FORMAT` | `none`, `basic`, `full` | `basic` | Enable or disable trace formatting |
### File Variables
The following environment variables can be used to configure the file logging handler, these variables are used to
configure the file logging handler for all logger instances.
| Variable Name | Excepted Value | Default Value | Description |
|-----------------------------------|---------------------------------------------------------------------------------------------------------|---------------|--------------------------------------------------------|
| `LOGLIB_FILE_ENABLED` | `true`, `false` | `false` | Enable or disable file logging |
| `LOGLIB_FILE_DEFAULT_PERMISSIONS` | `octal` | `0777` | The default permissions for the log file |
| `LOGLIB_FILE_PATH` | `string` | `(tmp)` | The path to the log file |
| `LOGLIB_FILE_APPEND_NEWLINE` | `true`, `false` | `true` | Enable or disable appending a newline to the log entry |
| `LOGLIB_FILE_LOG_FORMAT` | `jsonl`, `csv`, `txt`, `xml`, `html` | `jsonl` | The format of the log entry |
| `LOGLIB_FILE_TIMESTAMP_FORMAT` | `none`, `time_only`, `time_only_millis`, `date_only`, `date_time`. `date_time_millis`, `unix_timestamp` | `date_time` | The format of the timestamp |
| `LOGLIB_FILE_TRACE_FORMAT` | `none`, `basic`, `full` | `basic` | Enable or disable trace formatting |
### HTTP Variables
The following environment variables can be used to configure the HTTP logging handler, these variables are used to
configure the HTTP logging handler for all logger instances.
| Variable Name | Excepted Value | Default Value | Description |
|--------------------------------|---------------------------------------------------------------------------------------------------------|-----------------------|--------------------------------------------------------|
| `LOGLIB_HTTP_ENABLED` | `true`, `false` | `false` | Enable or disable HTTP logging |
| `LOGLIB_HTTP_ENDPOINT` | `string` | `http://0.0.0.0:5131` | The URL to the HTTP endpoint |
| `LOGLIB_HTTP_APPEND_NEWLINE` | `true`, `false` | `true` | Enable or disable appending a newline to the log entry |
| `LOGLIB_HTTP_LOG_FORMAT` | `jsonl`, `csv`, `txt`, `xml`, `html` | `jsonl` | The format of the log entry |
| `LOGLIB_HTTP_TIMESTAMP_FORMAT` | `none`, `time_only`, `time_only_millis`, `date_only`, `date_time`. `date_time_millis`, `unix_timestamp` | `date_time` | The format of the timestamp |
| `LOGLIB_HTTP_TRACE_FORMAT` | `none`, `basic`, `full` | `basic` | Enable or disable trace formatting |
### TCP Variables
The following environment variables can be used to configure the TCP logging handler, these variables are used to
configure the TCP logging handler for all logger instances.
| Variable Name | Excepted Value | Default Value | Description |
|-------------------------------|---------------------------------------------------------------------------------------------------------|---------------|--------------------------------------------------------|
| `LOGLIB_TCP_ENABLED` | `true`, `false` | `false` | Enable or disable TCP logging |
| `LOGLIB_TCP_HOST` | `string` | ` | |
| `LOGLIB_TCP_PORT` | `integer` | `5131` | The port to the TCP server |
| `LOGLIB_TCP_APPEND_NEWLINE` | `true`, `false` | `true` | Enable or disable appending a newline to the log entry |
| `LOGLIB_TCP_LOG_FORMAT` | `jsonl`, `csv`, `txt`, `xml`, `html` | `jsonl` | The format of the log entry |
| `LOGLIB_TCP_TIMESTAMP_FORMAT` | `none`, `time_only`, `time_only_millis`, `date_only`, `date_time`. `date_time_millis`, `unix_timestamp` | `date_time` | The format of the timestamp |
| `LOGLIB_TCP_TRACE_FORMAT` | `none`, `basic`, `full` | `basic` | Enable or disable trace formatting |
### UDP Variables
The following environment variables can be used to configure the UDP logging handler, these variables are used to
configure the UDP logging handler for all logger instances.
| Variable Name | Excepted Value | Default Value | Description |
|-------------------------------|---------------------------------------------------------------------------------------------------------|---------------|--------------------------------------------------------|
| `LOGLIB_UDP_ENABLED` | `true`, `false` | `false` | Enable or disable UDP logging |
| `LOGLIB_UDP_HOST` | `string` | ` | |
| `LOGLIB_UDP_PORT` | `integer` | `5131` | The port to the UDP server |
| `LOGLIB_UDP_APPEND_NEWLINE` | `true`, `false` | `true` | Enable or disable appending a newline to the log entry |
| `LOGLIB_UDP_LOG_FORMAT` | `jsonl`, `csv`, `txt`, `xml`, `html` | `jsonl` | The format of the log entry |
| `LOGLIB_UDP_TIMESTAMP_FORMAT` | `none`, `time_only`, `time_only_millis`, `date_only`, `date_time`. `date_time_millis`, `unix_timestamp` | `date_time` | The format of the timestamp |
| `LOGLIB_UDP_TRACE_FORMAT` | `none`, `basic`, `full` | `basic` | Enable or disable trace formatting |
## Log Levels
The LogLevel enumeration in the LogLib2 namespace provides five distinct log levels for logging events, each log level
Depending on the log level, the log entry may be handled differently by the logging handler and or extra information may
be added to the log event, for example, the `WARNING`, `ERROR`, and `CRITICAL` log levels may include an exception object
in the log event.
| Level | Standard Value | Possible Values | Description | Contains Exception |
|----------|----------------|-----------------------------------|----------------------------------------------------------------------------------------------------------|--------------------|
| DEBUG | `DBG` | `debug`, `dbg`, `d`, 0 | A debugging event, reserved for being extra verbose about states and events happening in the application | No |
| VERBOSE | `VRB` | `verbose`, `verb`, `vrb`, `v`, 1 | A verbose event, reserved for being verbose about states and events happening in the application | No |
| INFO | `INF` | `info`, `inf`, `i`, 2 | An informational event, reserved for logging general information about the application | No |
| WARNING | `WRN` | `warning`, `warn`, `wrn`, `w`, 3 | A warning event, reserved for logging events that may indicate a potential problem in the application | Yes |
| ERROR | `ERR` | `error`, `err`, `e`, 4 | An error event, reserved for logging events that indicate an error in the application | Yes |
| CRITICAL | `CRT` | `critical`, `crit`, `crt`, `c`, 5 | A critical event, reserved for logging events that indicate a critical error in the application | Yes |
The values are used to allow the user to specify the log level in a more human-readable format, for example, if you were
to use command-line arguments or environment variables to specify the log level to `debug`, the values `debug`, `dbg`, `d`,
and `0` would all be valid values to specify the log level.
### Log Filtering
By default application's log level filter is set to `INFO`, this means that only log events with a log level of `INFO` or
higher will be processed but everything below `INFO` will be ignored. The filter can easily be overridden by the
command-line arguments or environment variables to enforce all loggers to use a specific log level.
#### Command-Line Override
The log level filter can be overridden by the command-line arguments, for example, if LogLib2 can access the command-line
arguments, it will check for the `--log-level` argument, if the argument is present, it will override the default log
level filter with the value provided in the argument.
```bash
./myapp --log-level=debug
```
#### Environment Variable Override
The log level filter can also be overridden by the environment variable `LOG_LEVEL`, if the environment variable is set,
it will override the default log level filter with the value provided in the environment variable.
```bash
export LOG_LEVEL=debug
./myapp
```
## Creating a Logger
To create a logger, you can use the `Logger` class in the LogLib2 namespace, all loggers require an application name to
be specified, this is used to identify the application that generated the log event.
```php
$logger = new \LogLib2\Logger('com.example.myapp');
// Or specify the log level filter
$logger = new \LogLib2\Logger('com.example.myapp', \LogLib2\LogLevel::DEBUG);
```
The logger instance is designed to be statically defined, this means that you can define the logger instance as a static
variable in your application and use it throughout your application, this is to prevent the need to create a new logger
instance every time you want to log an event.
```php
class MyApp {
private static ?\LogLib2\Logger $logger=null;
public static function getLogger() {
if (self::$logger === null) {
self::$logger = new \LogLib2\Logger('com.example.myapp');
}
return self::$logger;
}
}
```
## Logging Events
To log an event, you can use the `log` method on the logger instance, you can specify the method to use by using the
appropriate method on the logger instance, for example, to log a debug event, you can use the `debug` method, to log a
verbose event, you can use the `verbose` method, and so on.
`warning`, `error`, and `critical` log levels may include an exception object in the log event, this is to provide more
information about the error that occurred, for example, the exception object may contain the exception name, message,
and stack trace.
```php
$logger = new \LogLib2\Logger('com.example.myapp');
$exception = new \Exception('An error occurred');
$logger->debug('A debugging event occurred');
$logger->verbose('A verbose event occurred');
$logger->info('An informational event occurred');
$logger->warning('A warning event occurred', $exception);
$logger->error('An error event occurred', $exception);
$logger->critical('A critical event occurred', $exception);
```
## Changing Default Configuration
The default configuration can be altered before the rest of the code-base begins to utilize the library, for example
while by default UDP logging is disabled, it can be enabled by altering the default configuration before any logging is
initiated, when libraries/applications begin to initialize their own logging instances, they would inherit the default
configuration that was altered unless they are explicitly configured to be overridden by their own configurations.
```php
\LogLib2\Logger::getDefaultUdpConfiguration()->setEnabled(true);
\LogLib2\Logger::getDefaultUdpConfiguration()->setHost('0.0.0.0');
\LogLib2\Logger::getDefaultUdpConfiguration()->setPort(5131);
\LogLib2\Logger::getDefaultTcpConfiguration()->setEnabled(true);
\LogLib2\Logger::getDefaultTcpConfiguration()->setHost('0.0.0.0');
\LogLib2\Logger::getDefaultTcpConfiguration()->setPort(5131);
\LogLib2\Logger::getDefaultHttpConfiguration()->setEnabled(true);
\LogLib2\Logger::getDefaultHttpConfiguration()->setUrl('http://0.0.0.0/log');
// This will use the default configuration that was altered above
$logger = new \LogLib2\Logger('com.example.myapp');
$logger->info('An informational event occurred');
```
> Note: The recommended way to alter the default configuration is to do so before any logging is initiated, or
> alternatively you can use environment variables to skip the need to alter the default configuration manually.
## Changing Logger Configuration
The logger configuration can be altered by the logger instance, this is useful when you want to override the default
configuration for a specific logger instance, for example, you may want to enable UDP logging for a specific logger
instance but not for the rest of the application.
```php
$logger = new \LogLib2\Logger('com.example.myapp');
$logger->getUdpConfiguration()->setEnabled(true);
$logger->getUdpConfiguration()->setHost('0.0.0.0');
$logger->getUdpConfiguration()->setPort(5131);
$logger->info('An informational event occurred');
```
> Note: This would override the default configuration for the logger instance only, other logger instances would still
> use the default configuration unless they are explicitly configured to be overridden by their own configurations.
## TCP/UDP Logging Server (python)
While no fully-fledged logging server is provided with the library, a simple TCP/UDP logging server is provided
with this project which is written in Python without requiring pip dependencies, the server is listens on both
TCP & UDP connections and is only designed to receive JSON formatted log entries with the Unix Timestamp being the
format timestamp. Contributions to improve this server are welcomed.
> See [server.py](server.py) for the server implementation.
### Usage:
The server can be started by using python to run [server.py](server.py), by default the server listens on port 5131 and
writes log entries to the current working directory under the 'logs' directory. This can be configured using command-line
arguments
```bash
python server.py --port=5131 --working-directory=/path/to/logs
```
Once running, you can configure the UDP configuration of your loggers to use this server, or alternatively use
environment variables to configure the server for all loggers.
```bash
export LOGLIB_UDP_ENABLED=true
export LOGLIB_UDP_HOST=0.0.0.0
export LOGLIB_UDP_PORT=5131
```
And once any logging events have been fired, you should see the server receiving the log entries in real-time. If for
any reason that LogLib fails to send these entries to the server, it will fail silently and re-try next time.
## Formatters
Formatters are used in LogLib to specify the type of format for a log entry property or log format entirely. Some
formats are only applicable to some handlers for example AnsiFormat is only applicable to ConsoleHandler
### AnsiFormat
The AnsiFormat enumeration in the LogLib2 namespace provides two distinct formats for ANSI formatting in console logging,
these formats are used to specify the type of ANSI formatting to use in console logging.
| Format Name | Value | Description |
|-------------|---------|---------------------------------------------------------------------------------|
| `NONE` | `none` | (Default) Displays regular console output without any ANSI formatting or colors |
| `BASIC` | `basic` | Displays regular console output with ANSI formatting and colors |
### LogFormat
The LogFormat enumeration allows you to dictate the log output format, some formats has different behaviors but
they all try to follow a compatible format.
| Format Name | Value | |
|-------------|-----------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `JSONL` | `json`, `jsonl` | JSON Lines format, each log entry is JSON encoded intended to be used for JSONL files or storage mediums |
| `CSV` | `csv` | CSV Format, when using a File handler; if the file doesn't exist a new one will be created with CSV headers included, in other cases CSV rows are simply used instead |
| `TXT` | `txt` | TXT Format, a human-readable format that is intended to be used for TXT files or storage mediums |
| `XML` | `xml` | XML Format, intended to be used for XML files or storage mediums |
| `HTML` | `html` | HTML Format, intended to be used for HTML files or storage mediums |
### TimestampFormat
The TimestampFormat enumeration allows you to dictate the timestamp format for the log entry, this is used to specify the
type of format to use for the timestamp in the log entry.
| Format Name | Value | Description |
|--------------------|-------------------------|-------------------------------------------------------------------------------------|
| `NONE` | `none`, `0` | (Default) No timestamp is included in the log entry |
| `TIME_ONLY` | `time_only`, `1` | Only the time is included in the log entry (`H:i:s`) |
| `TIME_ONLY_MILLIS` | `time_only_millis`, `2` | Only the time is included in the log entry with milliseconds (`H:i:s.u`) |
| `DATE_ONLY` | `date_only`, `3` | Only the date is included in the log entry (`Y-m-d`) |
| `DATE_TIME` | `date_time`, `4` | The date and time are included in the log entry (`Y-m-d H:i:s`) |
| `DATE_TIME_MILLIS` | `date_time_millis`, `5` | The date and time are included in the log entry with milliseconds (`Y-m-d H:i:s.u`) |
| `UNIX_TIMESTAMP` | `unix_timestamp`, `6` | The Unix timestamp is included in the log entry (`U`) |
### TraceFormat
The TraceFormat enumeration allows you to dictate the trace format for the log entry, this is used to specify the type of
format to use for the trace in the log
| Format Name | Value | Description |
|-------------|---------|-----------------------------------------------------------------------------|
| `NONE` | `none` | (Default) No trace is included in the log entry |
| `BASIC` | `basic` | Only the class and method name are included in the log entry trace |
| `FULL` | `full` | The full trace is included in the log entry, including the class and method |
## Object Types
The LogLib2 library provides simplified object types for logging events, exceptions and stack traces. These object types
are designed to be used for serialization for log formats, for example JSON would show an object representation of the
log entry, while TXT would show a human-readable representation of the log entry.
### Event
The Event object type is used to represent a log event, it contains the following properties:
| Property Name | Value Type | Optional | Description |
|------------------|------------------------------------------------|----------|-------------------------------------------------------------------------------------|
| application_name | `string` | No | The name of the application that created the event |
| timestamp | `string` | No | The timestamp of the event, formatted using the [TimestampFormat](#timestampformat) |
| level | [`LogLevel`](#log-levels) (See standard value) | No | The level of the logging event, eg; `INF` |
| message | `string` | No | The message of the event |
| trace | `string` | Yes | The trace of the executed caller that created the event |
| stack_trace | [`StackTrace[]`](#stacktrace) | Yes | The stack traces of the executed caller that created the event |
| exception | [`ExceptionDetails`](#exceptiondetails) | Yes | The exception object of the event |
### StackTrace
The StackTrace object type is used to represent a stack trace, it contains the following properties:
| Property Name | Value Type | Optional | Description |
|---------------|-------------------------|----------|-----------------------------------------|
| file | `string` | Yes | The executing file of the caller |
| line | `integer` | Yes | The executing line of the caller |
| function | `string` | Yes | The executing function of the caller |
| args | `mixed[]` | Yes | The arguments of the executing function |
| class | `string` | Yes | The executing class of the caller |
| call_type | [`CallType`](#calltype) | Yes | The call type of the caller |
### ExceptionDetails
The ExceptionDetails object type is used to represent an exception object, it contains the following properties:
| Property Name | Value Type | Optional | Description |
|---------------|-----------------------------------------|----------|-----------------------------------------|
| name | `string` | No | The name of the exception |
| message | `string` | No | The message of the exception |
| code | `integer` | Yes | The code of the exception |
| file | `string` | Yes | The file of the exception |
| line | `integer` | Yes | The line of the exception |
| trace | [`StackTrace[]`](#stacktrace) | Yes | The stack trace of the exception |
| previous | [`ExceptionDetails`](#exceptiondetails) | Yes | The previous exception of the exception |
## CallTypes
CallTypes are used to specify the type of call that was made when the log event was created, this is used to provide more
information about the call that was made when the log event was created.
| Call Type | Value | Description |
|-----------------|----------|--------------------------------------------------------------|
| `METHOD_CALL` | `->` | A method call was made when the log event was created |
| `STATIC_CALL` | `::` | A static method call was made when the log event was created |
| `FUNCTION_CALL` | `()` | A function >call was made when the log event was created |
| `LAMBDA_CALL` | `λ` | A lambda call was made when the log event was created |
| `EVAL_CALL` | `eval()` | An eval call was made when the log event was created |
## Log Formats
The LogFormat enumeration in the LogLib2 namespace provides five distinct formats for serializing log entries.
Each format is designed to suit different logging needs and use cases, such as machine-readable formats (JSONL, XML),
human-readable formats (TXT, HTML), or structured formats (CSV).
These log formats are designed to be used for the following handlers:
- Descriptor Logging
- File Logging
- HTTP Logging
- TCP Logging
- UDP Logging
But note the common behavior of each handler when serializing log entries:
- File Logging: Appends a newline (\n) to the serialized log entry automatically.
- HTTP, UDP, TCP, Descriptor Logging: Does not append a newline (\n) to the serialized log entry.
### JSONL
Produces a single-line JSON representation of the log entry. This format is highly efficient for processing large
volumes of logs as each log entry occupies a single line.
```json
{"timestamp":"16:00:23","level":"ERROR","message":"An error occurred","trace":"Some trace details","exception":{"name":"ExceptionName","message":"Error details"}}
```
JSON Objects follows the same object structure format as the [Event](#event) object type.
### CSV
Produces a CSV representation of the log entry. This format is useful for exporting log entries to a spreadsheet or
database for further analysis.
> Note: Only the File handler will use CSV headers, this only happens when trying to create a .csv file that doesn't
> exist, LogLib will automatically create the file with the headers included.
The CSV format is as follows:
```csv
timestamp,level,message,trace,exception
16:00:23,ERROR,An error occurred,<trace>,<json encoded exception details>
```
- `timestamp`: The timestamp of the log entry, formatted using the [TimestampFormat](#timestampformat).
- `level`: The level of the log entry, eg; `INF`.
- `message`: The message of the log entry.
- `trace`: The trace of the event, formatted using the [TraceFormat](#traceformat).
- `exception`: JSON encoded exception details, formatted using the [ExceptionDetails](#exceptiondetails) object type.
### TXT
Produces a human-readable text representation of the log entry. This format is useful for viewing log entries in a
text editor or terminal.
The TXT format is as follows:
```
05:45:38 [INFO] This is an example log message.
05:45:38 [INFO] This is an example log message.
05:45:38 [INFO] This is an example log message.
05:45:38 [INFO] This is an example log message.
05:45:38 [INFO] This is an example log message.
05:45:38 [INFO] This is an example log message.
05:45:38 [INFO] This is an example log message.
05:45:38 [INFO] This is an example log message.
05:45:39 [INFO] This is an example log message.
05:45:39 [INFO] This is an example log message.
05:45:39 [ERR] test
Exception: This is an example exception. (0)
File: LogLib2/examples/example1.php:22
05:45:39 [INFO] ExampleClass::sleepExample Sleeping for 5 seconds...
05:45:44 [INFO] ExampleClass::sleepExample Finished sleeping for 5 seconds.
05:45:39 [WRN] LogLib2\Logger::LogLib2\{closure} Undefined array key "foo"
Runtime: Undefined array key "foo" (2)
File: LogLib2/examples/example1.php:20
05:45:44 [ERR] LogLib2\Logger::LogLib2\{closure} this is a new exception
Exception: this is a new exception (0)
File: LogLib2/examples/example_class.php:32
ExampleClassthrowDoubleException (LogLib2/examples/example1.php:29)
Exception: This is an example exception. (0)
File: LogLib2/examples/example_class.php:22
ExampleClassthrowException (LogLib2/examples/example_class.php:28)
ExampleClassthrowDoubleException (LogLib2/examples/example1.php:29)
```
### XML
Produces an XML representation of the log entry. This format is useful for exporting log entries to an XML file or
The XML structure follows the same object structure as [Event](#event)
```xml
<event>
<application_name>Runtime</application_name>
<timestamp>05:52:58</timestamp>
<level>WRN</level>
<message>WRN</message>
<trace>LogLib2\Logger::LogLib2\{closure}</trace>
<stack_trace>
<trace>
<file>LogLib2/examples/example1.php</file>
<line>20</line>
<function>LogLib2\{closure}</function>
<class>LogLib2\Logger</class>
<arguments>
<argument>"2"</argument>
<argument>"Undefined array key \"foo\""</argument>
<argument>"LogLib2/examples/example1.php"</argument>
<argument>"20"</argument>
</arguments>
</trace>
</stack_trace>
<exception>
<name>Runtime</name>
<message>Undefined array key "foo"</message>
<code>2</code>
<file>LogLib2/examples/example1.php</file>
<line>20</line>
</exception>
</event>
```
```xml
<event>
<application_name>Runtime</application_name>
<timestamp>05:53:03</timestamp>
<level>ERR</level>
<message>ERR</message>
<trace>LogLib2\Logger::LogLib2\{closure}</trace>
<stack_trace>
<trace>
<function>LogLib2\{closure}</function>
<class>LogLib2\Logger</class>
<arguments>
<argument>"[OBJECT]"</argument>
</arguments>
</trace>
</stack_trace>
<exception>
<name>Exception</name>
<message>this is a new exception</message>
<code>0</code>
<file>LogLib2/examples/example_class.php</file>
<line>32</line>
<stack_trace>
<trace>
<file>LogLib2/examples/example1.php</file>
<line>29</line>
<function>throwDoubleException</function>
<class>ExampleClass</class>
</trace>
</stack_trace>
<previous>
<name>Exception</name>
<message>This is an example exception.</message>
<code>0</code>
<file>LogLib2/examples/example_class.php</file>
<line>22</line>
<stack_trace>
<trace>
<file>LogLib2/examples/example_class.php</file>
<line>28</line>
<function>throwException</function>
<class>ExampleClass</class>
</trace>
<trace>
<file>LogLib2/examples/example1.php</file>
<line>29</line>
<function>throwDoubleException</function>
<class>ExampleClass</class>
</trace>
</stack_trace>
</previous>
</exception>
</event>
```
------------------------------------------------------------------------------------------------------------------------
# License
The LogLib2 library is licensed under the MIT License, see the [LICENSE](LICENSE) file for more information.

29
examples/example1.php Normal file
View file

@ -0,0 +1,29 @@
<?php
require 'ncc';
require 'example_class.php';
import('net.nosial.loglib2');
\LogLib2\Logger::setBacktraceLevel(3);
\LogLib2\Logger::registerHandlers();
$logger = new \LogLib2\Logger('Example');
// Iterate 10 times
for($i = 0; $i < 10; $i++)
{
// Log a message with a random log level
$logger->info('This is an example log message.');
}
$a = [];
$b = $a['foo']; // <-- This will throw a notice that will be caught by the logger
$exception = new \Exception('This is an example exception.');
$logger->error("test", $exception);
$example = new ExampleClass($logger);
$example->sleepExample(5);
$example->throwDoubleException();

View file

@ -0,0 +1,39 @@
<?php
class ExampleClass {
private \LogLib2\Logger $logger;
public function __construct(\LogLib2\Logger $logger) {
$this->logger = $logger;
}
public function getLogger(): \LogLib2\Logger {
return $this->logger;
}
public function sleepExample(int $seconds): void {
$this->logger->info("Sleeping for $seconds seconds...");
sleep($seconds);
$this->logger->info("Finished sleeping for $seconds seconds.");
}
public function throwException(): void {
throw new \Exception("This is an example exception.");
}
public function throwDoubleException(): void {
try
{
$this->throwException();
}
catch(Exception $e)
{
throw new Exception("this is a new exception", 0, $e);
}
}
public function warningExceptionExample(): void {
$this->logger->warning("Throwing a warning exception...", new \Exception("This is an example warning exception."));
}
}

View file

@ -4,9 +4,6 @@
"extension": "php", "extension": "php",
"minimum_version": "8.0", "minimum_version": "8.0",
"maximum_version": "8.2" "maximum_version": "8.2"
},
"options": {
"create_symlink": true
} }
}, },
"assembly": { "assembly": {
@ -24,6 +21,13 @@
"ASSEMBLY_VERSION": "%ASSEMBLY.VERSION%", "ASSEMBLY_VERSION": "%ASSEMBLY.VERSION%",
"ASSEMBLY_UID": "%ASSEMBLY.UID%" "ASSEMBLY_UID": "%ASSEMBLY.UID%"
}, },
"dependencies": [
{
"name": "net.nosial.optslib",
"version": "latest",
"source": "nosial/libs.opts=latest@n64"
}
],
"configurations": [ "configurations": [
{ {
"name": "release", "name": "release",

358
server.py Normal file
View file

@ -0,0 +1,358 @@
# This is a simple server that listens on both TCP and UDP ports and logs incoming messages to a file & console.
# To use this with LogLib, simply start the server and configure the LogLib client to send logs to the server.
# The following environment variables can be used to configure LogLib to send logs to the server:
#
# - LOGLIB_UDP_ENABLED=true (enable UDP logging)
# - LOGLIB_UDP_HOST=0.0.0.0 (UDP host)
# - LOGLIB_UDP_PORT=5131 (UDP port, default 5131)
#
# This server is designed to only accept JSON-formatted log messages using the UnixTimestamp for the Timestamps,
# TraceLevels can be anything.
#
# After configuring the environment variables, start the server & run your program. Logging events should be
# sent to the server and logged to the console and a file in the specified working directory.
import argparse
import json
import logging
import os
import socket
import threading
from datetime import datetime
from queue import Queue, Empty
from enum import Enum
from typing import Optional, Dict, Any, List
from dataclasses import dataclass
import colorama
from colorama import Fore, Back, Style
colorama.init()
class LogLevel(str, Enum):
DEBUG = "DBG"
VERBOSE = "VRB"
INFO = "INFO"
WARNING = "WRN"
ERROR = "ERR"
CRITICAL = "CRT"
@classmethod
def to_python_level(cls, level: str) -> int:
return {
cls.DEBUG: logging.DEBUG,
cls.VERBOSE: logging.DEBUG,
cls.INFO: logging.INFO,
cls.WARNING: logging.WARNING,
cls.ERROR: logging.ERROR,
cls.CRITICAL: logging.CRITICAL
}.get(level, logging.INFO)
@classmethod
def get_color(cls, level: str) -> str:
return {
cls.DEBUG: Fore.CYAN,
cls.VERBOSE: Fore.BLUE,
cls.INFO: Fore.GREEN,
cls.WARNING: Fore.YELLOW,
cls.ERROR: Fore.RED,
cls.CRITICAL: Fore.RED + Back.WHITE
}.get(level, Fore.WHITE)
@dataclass
class StackFrame:
file: Optional[str]
line: Optional[int]
function: Optional[str]
args: Optional[List[Any]]
class_name: Optional[str]
call_type: str = 'static'
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> 'StackFrame':
return cls(
file=str(data.get('file')) if data.get('file') else None,
line=int(data['line']) if data.get('line') is not None else None,
function=str(data.get('function')) if data.get('function') else None,
args=data.get('args'),
class_name=str(data.get('class')) if data.get('class') else None,
call_type=str(data.get('callType', 'static'))
)
def format(self) -> str:
location = f"{self.file or '?'}:{self.line or '?'}"
if self.class_name:
call = f"{self.class_name}{self.call_type}{self.function or ''}"
else:
call = self.function or ''
args_str = ""
if self.args:
args_str = f"({', '.join(str(arg) for arg in self.args)})"
return f"{Fore.BLUE}{call}{Style.RESET_ALL}{args_str} in {Fore.CYAN}{location}{Style.RESET_ALL}"
class ExceptionDetails:
def __init__(self, name: str, message: str, code: Optional[int],
file: Optional[str], line: Optional[int],
trace: List[StackFrame], previous: Optional['ExceptionDetails']):
self.name = name
self.message = message
self.code = code
self.file = file
self.line = line
self.trace = trace
self.previous = previous
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> Optional['ExceptionDetails']:
if not data:
return None
trace = []
if 'trace' in data and isinstance(data['trace'], list):
trace = [StackFrame.from_dict(frame) for frame in data['trace']
if isinstance(frame, dict)]
previous = None
if 'previous' in data and isinstance(data['previous'], dict):
previous = cls.from_dict(data['previous'])
return cls(
name=str(data.get('name', '')),
message=str(data.get('message', '')),
code=int(data['code']) if data.get('code') is not None else None,
file=str(data.get('file')) if data.get('file') else None,
line=int(data['line']) if data.get('line') is not None else None,
trace=trace,
previous=previous
)
def format(self, level: int = 0) -> str:
indent = " " * level
parts = []
# Exception header
header = f"{indent}{Fore.RED}{self.name}"
if self.code is not None and 0:
header += f" {Fore.YELLOW}:{self.code}{Style.RESET_ALL}"
# Message
header += f"{Fore.WHITE}:{Style.RESET_ALL} {self.message}"
# Location
if self.file and self.line:
header += f"{Fore.WHITE} at {Style.RESET_ALL}{self.file}:{self.line}"
parts.append(header)
# Stack trace
if self.trace:
parts.append(f"{indent}{Fore.WHITE}Stack trace:{Style.RESET_ALL}")
for frame in self.trace:
parts.append(f"{indent}{frame.format()}")
# Previous exception
if self.previous:
parts.append(f"{indent}{Fore.YELLOW}Caused by:{Style.RESET_ALL}")
parts.append(self.previous.format(level + 1))
return "\n".join(parts)
class ColoredLogger(logging.Logger):
def __init__(self, name: str):
super().__init__(name)
self.formatter = logging.Formatter(
f'%(asctime)s {Fore.WHITE}[%(levelname)s]{Style.RESET_ALL} %(message)s',
datefmt='%Y-%m-%d %H:%M:%S'
)
console_handler = logging.StreamHandler()
console_handler.setFormatter(self.formatter)
self.addHandler(console_handler)
class MultiProtocolServer:
def __init__(self, host: str, port: int, working_directory: str):
self.host = host
self.port = port
self.working_directory = working_directory
self.log_queue: Queue = Queue()
self.current_date = datetime.now().strftime('%Y-%m-%d')
self.log_file = None
self.stop_event = threading.Event()
os.makedirs(self.working_directory, exist_ok=True)
# Set up colored logging
logging.setLoggerClass(ColoredLogger)
self.logger = logging.getLogger("MultiProtocolServer")
self.logger.setLevel(logging.DEBUG)
def _handle_log_event(self, data: Dict[str, Any], address: tuple) -> None:
"""Process and format a structured log event with colors and proper formatting."""
try:
app_name = data.get('application_name', 'Unknown')
timestamp = data.get('timestamp')
if timestamp:
try:
timestamp = datetime.fromtimestamp(int(timestamp))
timestamp = timestamp.strftime('%Y-%m-%d %H:%M:%S')
except (ValueError, TypeError):
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
else:
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
level = data.get('level', 'INFO')
message = data.get('message', '')
# Format the log message with colors
color = LogLevel.get_color(level)
log_message = f"{color}[{app_name}]{Style.RESET_ALL} {message}"
# Handle exception if present
exception_data = data.get('exception')
if exception_data:
exception = ExceptionDetails.from_dict(exception_data)
if exception:
log_message += f"\n{exception.format()}"
# Log with appropriate level
python_level = LogLevel.to_python_level(level)
self.logger.log(python_level, log_message)
# Add to log queue for file logging
self.log_queue.put({
"timestamp": timestamp,
"address": address,
"data": data
})
except Exception as e:
self.logger.error(f"Error processing log event: {e}", exc_info=True)
def _handle_data(self, data: bytes, address: tuple) -> None:
"""Process incoming data and attempt to parse as JSON."""
try:
decoded_data = data.decode('utf-8').strip()
try:
json_data = json.loads(decoded_data)
# Handle structured log event
self._handle_log_event(json_data, address)
except json.JSONDecodeError:
# Log raw data if not valid JSON
self.logger.info(f"Received non-JSON data from {address}: {decoded_data}")
self.log_queue.put({
"timestamp": datetime.now().isoformat(),
"address": address,
"data": decoded_data
})
except Exception as e:
self.logger.error(f"Data handling error: {e}")
# Rest of the class remains the same...
def _get_log_file(self):
date = datetime.now().strftime('%Y-%m-%d')
if date != self.current_date or self.log_file is None:
if self.log_file:
self.log_file.close()
self.current_date = date
filename = os.path.join(self.working_directory, f"log{date}.jsonl")
self.log_file = open(filename, 'a')
return self.log_file
def _log_writer(self):
while not self.stop_event.is_set() or not self.log_queue.empty():
try:
data = self.log_queue.get(timeout=1)
log_file = self._get_log_file()
json.dump(data, log_file)
log_file.write('\n')
log_file.flush()
except Empty:
continue
except Exception as e:
self.logger.error(f"Error writing to log file: {e}")
def _handle_tcp_client(self, client_socket, address):
self.logger.info(f"TCP connection established from {address}")
try:
with client_socket:
while True:
data = client_socket.recv(4096)
if not data:
break
self._handle_data(data, address)
except Exception as e:
self.logger.error(f"TCP client error: {e}")
self.logger.info(f"TCP connection closed from {address}")
def _handle_udp_client(self, data, address):
try:
self._handle_data(data, address)
except Exception as e:
self.logger.error(f"UDP client error: {e}")
def _start_tcp_server(self):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as tcp_socket:
tcp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
tcp_socket.bind((self.host, self.port))
tcp_socket.listen(5)
self.logger.debug(f"TCP server running on {self.host}:{self.port}")
while not self.stop_event.is_set():
try:
client_socket, address = tcp_socket.accept()
threading.Thread(target=self._handle_tcp_client,
args=(client_socket, address),
daemon=True).start()
except Exception as e:
self.logger.error(f"TCP server error: {e}")
def _start_udp_server(self):
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as udp_socket:
udp_socket.bind((self.host, self.port))
self.logger.debug(f"UDP server running on {self.host}:{self.port}")
while not self.stop_event.is_set():
try:
data, address = udp_socket.recvfrom(4096)
self._handle_udp_client(data, address)
except Exception as e:
self.logger.error(f"UDP server error: {e}")
def start(self):
self.logger.info("Starting MultiProtocolServer...")
threading.Thread(target=self._log_writer, daemon=True).start()
tcp_thread = threading.Thread(target=self._start_tcp_server, daemon=True)
udp_thread = threading.Thread(target=self._start_udp_server, daemon=True)
tcp_thread.start()
udp_thread.start()
try:
tcp_thread.join()
udp_thread.join()
except KeyboardInterrupt:
self.stop()
def stop(self):
self.logger.info("Stopping MultiProtocolServer...")
self.stop_event.set()
if self.log_file:
self.log_file.close()
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="MultiProtocol Server")
parser.add_argument("-p", "--port", type=int, default=5131,
help="Port to listen on")
parser.add_argument("-w", "--working-directory", type=str,
default="./logs", help="Directory to store log files")
args = parser.parse_args()
server = MultiProtocolServer("0.0.0.0", args.port, args.working_directory)
server.start()

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';