From 024810abf7fcc08756debef6effbee113c2421b8 Mon Sep 17 00:00:00 2001 From: Daniel Ronkainen Date: Sun, 26 Oct 2025 12:22:57 +0100 Subject: [PATCH 1/2] refactor: clean up and organize codebase --- Interfaces/LoggerAwareInterface.php | 18 --- Interfaces/LoggerInterface.php | 125 ------------------ composer.json | 58 ++++---- AbstractLogger.php => src/AbstractLogger.php | 2 +- .../Handlers}/AbstractHandler.php | 0 {Handlers => src/Handlers}/DBHandler.php | 0 .../Handlers}/ErrorLogHandler.php | 2 +- {Handlers => src/Handlers}/StreamHandler.php | 12 +- .../Interfaces}/HandlerInterface.php | 0 .../InvalidArgumentException.php | 0 LogLevel.php => src/LogLevel.php | 0 Logger.php => src/Logger.php | 2 +- .../LoggerAwareTrait.php | 2 +- LoggerTrait.php => src/LoggerTrait.php | 0 NullLogger.php => src/NullLogger.php | 0 15 files changed, 42 insertions(+), 179 deletions(-) delete mode 100755 Interfaces/LoggerAwareInterface.php delete mode 100755 Interfaces/LoggerInterface.php rename AbstractLogger.php => src/AbstractLogger.php (90%) rename {Handlers => src/Handlers}/AbstractHandler.php (100%) rename {Handlers => src/Handlers}/DBHandler.php (100%) rename {Handlers => src/Handlers}/ErrorLogHandler.php (96%) rename {Handlers => src/Handlers}/StreamHandler.php (89%) rename {Interfaces => src/Interfaces}/HandlerInterface.php (100%) rename InvalidArgumentException.php => src/InvalidArgumentException.php (100%) rename LogLevel.php => src/LogLevel.php (100%) rename Logger.php => src/Logger.php (97%) rename LoggerAwareTrait.php => src/LoggerAwareTrait.php (90%) rename LoggerTrait.php => src/LoggerTrait.php (100%) rename NullLogger.php => src/NullLogger.php (100%) diff --git a/Interfaces/LoggerAwareInterface.php b/Interfaces/LoggerAwareInterface.php deleted file mode 100755 index 8ea8734..0000000 --- a/Interfaces/LoggerAwareInterface.php +++ /dev/null @@ -1,18 +0,0 @@ -=8.0", - "maplephp/http": "^1.0" + "name": "maplephp/log", + "type": "library", + "description": "PHP PSR-3 Logger library, standardized approach to logging messages.", + "keywords": [ + "PSR-3", + "log", + "logger", + "filesystem", + "error log" + ], + "homepage": "https://wazabii.se", + "license": "Apache-2.0", + "authors": [ + { + "name": "Daniel Ronkainen", + "email": "daniel.ronkainen@wazabii.se" }, - "autoload": { - "psr-4": { - "MaplePHP\\Log\\": "" - } - }, - "minimum-stability": "dev" + { + "name": "MaplePHP", + "homepage": "https://wazabii.se" + } + ], + "require": { + "php": ">=8.2", + "psr/log": "^3.0", + "maplephp/http": "^2.0" + }, + "autoload": { + "psr-4": { + "MaplePHP\\Log\\": "src" + } + }, + "minimum-stability": "dev" } \ No newline at end of file diff --git a/AbstractLogger.php b/src/AbstractLogger.php similarity index 90% rename from AbstractLogger.php rename to src/AbstractLogger.php index 2c6af20..d74b3b4 100755 --- a/AbstractLogger.php +++ b/src/AbstractLogger.php @@ -2,7 +2,7 @@ namespace MaplePHP\Log; -use MaplePHP\Log\Interfaces\LoggerInterface; +use Psr\Log\LoggerInterface; /** * This is a simple Logger implementation that other Loggers can inherit from. diff --git a/Handlers/AbstractHandler.php b/src/Handlers/AbstractHandler.php similarity index 100% rename from Handlers/AbstractHandler.php rename to src/Handlers/AbstractHandler.php diff --git a/Handlers/DBHandler.php b/src/Handlers/DBHandler.php similarity index 100% rename from Handlers/DBHandler.php rename to src/Handlers/DBHandler.php diff --git a/Handlers/ErrorLogHandler.php b/src/Handlers/ErrorLogHandler.php similarity index 96% rename from Handlers/ErrorLogHandler.php rename to src/Handlers/ErrorLogHandler.php index a3a6d6f..a1fde95 100755 --- a/Handlers/ErrorLogHandler.php +++ b/src/Handlers/ErrorLogHandler.php @@ -7,7 +7,7 @@ class ErrorLogHandler extends AbstractHandler public function __construct(?string $file = null) { ini_set("log_errors", "1"); - if (!is_null($file)) { + if ($file !== null) { ini_set("error_log", $file); } } diff --git a/Handlers/StreamHandler.php b/src/Handlers/StreamHandler.php similarity index 89% rename from Handlers/StreamHandler.php rename to src/Handlers/StreamHandler.php index ec0c195..f300bea 100755 --- a/Handlers/StreamHandler.php +++ b/src/Handlers/StreamHandler.php @@ -2,8 +2,8 @@ namespace MaplePHP\Log\Handlers; -use MaplePHP\Http\Interfaces\StreamInterface; use MaplePHP\Http\Stream; +use Psr\Http\Message\StreamInterface; class StreamHandler extends AbstractHandler { @@ -21,7 +21,7 @@ public function __construct(string $file, ?int $size = null, ?int $count = null) { $this->file = basename($file); $this->dir = dirname($file) . "/"; - $this->size = !is_null($size) ? ($size * 1024) : $size; + $this->size = $size !== null ? ($size * 1024) : $size; $this->count = $count; } @@ -49,7 +49,7 @@ public function handler(string $level, string $message, array $context, string $ */ protected function stream(): StreamInterface { - if (is_null($this->stream)) { + if ($this->stream === null) { if (!is_writable($this->dir)) { throw new \Exception("The directory \"{$this->dir}\" is not writable!", 1); } @@ -64,7 +64,7 @@ protected function stream(): StreamInterface */ protected function rotate(): void { - if (!is_null($this->size)) { + if ($this->size !== null) { $file = $this->dir . $this->file; $filename = $this->fileInfo("filename"); $extension = $this->fileInfo("extension"); @@ -74,7 +74,7 @@ protected function rotate(): void $count = count($files); sort($files); - if (!is_null($this->count) && ($count >= $this->count)) { + if ($this->count !== null && ($count >= $this->count)) { for ($i = 0; $i < (($count - $this->count) + 1); $i++) { unlink($files[$i]); } @@ -92,7 +92,7 @@ protected function rotate(): void */ private function fileInfo(string $key): ?string { - if (is_null($this->info)) { + if ($this->info === null) { $this->info = pathinfo($this->file); } return ($this->info[$key] ?? null); diff --git a/Interfaces/HandlerInterface.php b/src/Interfaces/HandlerInterface.php similarity index 100% rename from Interfaces/HandlerInterface.php rename to src/Interfaces/HandlerInterface.php diff --git a/InvalidArgumentException.php b/src/InvalidArgumentException.php similarity index 100% rename from InvalidArgumentException.php rename to src/InvalidArgumentException.php diff --git a/LogLevel.php b/src/LogLevel.php similarity index 100% rename from LogLevel.php rename to src/LogLevel.php diff --git a/Logger.php b/src/Logger.php similarity index 97% rename from Logger.php rename to src/Logger.php index a797b50..bac3a14 100755 --- a/Logger.php +++ b/src/Logger.php @@ -69,7 +69,7 @@ public function getContext(): array */ protected function getDate(): string { - if (is_null($this->dateTime)) { + if ($this->dateTime === null) { $this->dateTime = new \DateTime("now"); } return $this->dateTime->format(static::DATETIME_FORMAT); diff --git a/LoggerAwareTrait.php b/src/LoggerAwareTrait.php similarity index 90% rename from LoggerAwareTrait.php rename to src/LoggerAwareTrait.php index d08bc7e..1023eee 100755 --- a/LoggerAwareTrait.php +++ b/src/LoggerAwareTrait.php @@ -2,7 +2,7 @@ namespace MaplePHP\Log; -use MaplePHP\Log\Interfaces\LoggerInterface; +use Psr\Log\LoggerInterface; /** * Basic Implementation of LoggerAwareInterface. diff --git a/LoggerTrait.php b/src/LoggerTrait.php similarity index 100% rename from LoggerTrait.php rename to src/LoggerTrait.php diff --git a/NullLogger.php b/src/NullLogger.php similarity index 100% rename from NullLogger.php rename to src/NullLogger.php From e0e9e41d657f602a5c432ef4b16959622362d355 Mon Sep 17 00:00:00 2001 From: Daniel Ronkainen Date: Mon, 12 Jan 2026 20:35:53 +0100 Subject: [PATCH 2/2] refactor: file structure --- .gitignore | 3 + LICENSE | 201 ++++++++++++++++++++++++++++ README.md | 50 +++++++ composer.json | 35 +++++ src/AbstractLogger.php | 17 +++ src/Handlers/AbstractHandler.php | 34 +++++ src/Handlers/DBHandler.php | 87 ++++++++++++ src/Handlers/ErrorLogHandler.php | 29 ++++ src/Handlers/StreamHandler.php | 100 ++++++++++++++ src/Interfaces/HandlerInterface.php | 8 ++ src/InvalidArgumentException.php | 7 + src/LogLevel.php | 18 +++ src/Logger.php | 77 +++++++++++ src/LoggerAwareTrait.php | 28 ++++ src/LoggerTrait.php | 142 ++++++++++++++++++++ src/NullLogger.php | 32 +++++ 16 files changed, 868 insertions(+) create mode 100755 .gitignore create mode 100755 LICENSE create mode 100755 README.md create mode 100755 composer.json create mode 100755 src/AbstractLogger.php create mode 100755 src/Handlers/AbstractHandler.php create mode 100755 src/Handlers/DBHandler.php create mode 100755 src/Handlers/ErrorLogHandler.php create mode 100755 src/Handlers/StreamHandler.php create mode 100755 src/Interfaces/HandlerInterface.php create mode 100755 src/InvalidArgumentException.php create mode 100755 src/LogLevel.php create mode 100755 src/Logger.php create mode 100755 src/LoggerAwareTrait.php create mode 100755 src/LoggerTrait.php create mode 100755 src/NullLogger.php diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..e9ec7e7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +._* +.DS_Store +.sass-cache \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100755 index 0000000..91bf91b --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ + + +# MaplePHP - PSR-3 Logger +PHP PSR-3 Logger library – your reliable companion for efficient logging in PHP applications. This library adheres to the PSR-3 standard, providing a seamless and standardized approach to logging messages across different components of your application. + + +## Log-levels + +1. **emergency:** System is unusable +2. **alert:** Action must be taken immediately +3. **critical:** Critical conditions +4. **error:** Runtime errors that do not require immediate action but usually logged and monitored. +5. **warning:** Exceptional occurrences that are not errors. +6. **notice:** Normal but significant events. +7. **info:** Interesting events (User logs in, SQL logs.) +8. **debug:** Detailed debug information. +9. **log:** Logs with an arbitrary level. + + +## Stream/file handler + +#### Add namespaces +```php +use MaplePHP\Log\Logger; +use MaplePHP\Log\Handlers\StreamHandler; +``` +#### Create simple stream logger +```php +$log = new Logger(new StreamHandler("/path/to/logger.log")); +$log->warning("The user {firstname} has been added.", ["firstname" => "John", "lastname" => "Doe"]); +``` +#### Rotatable log files +Create simple stream rotatables loggers. Will create a new log file if size is more than MAX_SIZE (5000 KB) and remove log files if total file count is more than MAX_COUNT 10. +```php +$log = new Logger(new StreamHandler("/path/to/logger.log", StreamHandler::MAX_SIZE, StreamHandler::MAX_COUNT)); +$log->warning("The user {firstname} has been added.", ["firstname" => "John", "lastname" => "Doe"]); +``` + +## PHP error log handler (error_log()) +You can (not required) specify a log file location in ErrorLogHandler. If argument is empty, then server default location. + +#### Add namespaces +```php +use MaplePHP\Log\Logger; +use MaplePHP\Log\Handlers\ErrorLogHandler; +``` +```php +$log = new Logger(new ErrorLogHandler("/path/to/logger.log")); +$log->warning("The user {firstname} has been added.", ["firstname" => "John", "lastname" => "Doe", "data" => ["city" => "Stockholm", "coor" => "122,1212"]]); +``` diff --git a/composer.json b/composer.json new file mode 100755 index 0000000..2227a0a --- /dev/null +++ b/composer.json @@ -0,0 +1,35 @@ +{ + "name": "maplephp/log", + "type": "library", + "description": "PHP PSR-3 Logger library, standardized approach to logging messages.", + "keywords": [ + "PSR-3", + "log", + "logger", + "filesystem", + "error log" + ], + "homepage": "https://wazabii.se", + "license": "Apache-2.0", + "authors": [ + { + "name": "Daniel Ronkainen", + "email": "daniel.ronkainen@wazabii.se" + }, + { + "name": "MaplePHP", + "homepage": "https://wazabii.se" + } + ], + "require": { + "php": ">=8.2", + "psr/log": "^3.0", + "maplephp/http": "^2.0" + }, + "autoload": { + "psr-4": { + "MaplePHP\\Log\\": "src" + } + }, + "minimum-stability": "dev" +} \ No newline at end of file diff --git a/src/AbstractLogger.php b/src/AbstractLogger.php new file mode 100755 index 0000000..d74b3b4 --- /dev/null +++ b/src/AbstractLogger.php @@ -0,0 +1,17 @@ + $val) { + if (is_array($val)) { + $replace['{' . $key . '}'] = json_encode($val); + } elseif ((!is_object($val) || method_exists($val, '__toString'))) { + $replace['{' . $key . '}'] = $val; + } + } + return strtr($message, $replace); + } +} diff --git a/src/Handlers/DBHandler.php b/src/Handlers/DBHandler.php new file mode 100755 index 0000000..6f5c784 --- /dev/null +++ b/src/Handlers/DBHandler.php @@ -0,0 +1,87 @@ +args = $args; + } + + /** + * Stream handler + * @param string $level + * @param string $message + * @param array $context + * @param string $date + * @return void + */ + public function handler(string $level, string $message, array $context, string $date): void + { + $set = array_merge([ + "level" => $level, + "user_id" => ($context['user_id'] ?? 0), + "message" => $message, + "data" => json_encode($context), + "date" => $date + ], $this->args); + + $insert = DB::insert(static::TABLE)->set($set); + $insert->execute(); + } + + /** + * Execute method bellow once an it will automatically create your table! + * @return mixed + */ + public function create(): mixed + { + + $mig = new Create(static::TABLE, Connect::prefix()); + $mig->auto(); + + // Add/alter columns + $mig->column("id", [ + "type" => "int", + "length" => 11, + "attr" => "unsigned", + "index" => "primary", + "ai" => true + + ])->column("user_id", [ + "type" => "int", + "length" => 11, + "index" => "index", + "default" => "0" + + ])->column("level", [ + "type" => "varchar", + "collate" => true, + "length" => 30, + + ])->column("message", [ + "type" => "text", + "collate" => true + + ])->column("data", [ + "type" => "text", + "collate" => true, + "null" => true + + ])->column("date", [ + "type" => "datetime", + "index" => "index" + ]); + + return $mig->execute(); + } +} diff --git a/src/Handlers/ErrorLogHandler.php b/src/Handlers/ErrorLogHandler.php new file mode 100755 index 0000000..a1fde95 --- /dev/null +++ b/src/Handlers/ErrorLogHandler.php @@ -0,0 +1,29 @@ +file = basename($file); + $this->dir = dirname($file) . "/"; + $this->size = $size !== null ? ($size * 1024) : $size; + $this->count = $count; + } + + /** + * Stream handler + * @param string $level + * @param string $message + * @param array $context + * @param string $date + * @return void + */ + public function handler(string $level, string $message, array $context, string $date): void + { + $encode = json_encode($context); + $message = sprintf($message, $encode); + $this->rotate(); + $this->stream()->seek(0); + $this->stream()->write("[{$date}] [{$level}] {$message} {$encode}"); + $this->stream()->write("\n"); + } + + /** + * Create stream + * @return StreamInterface + */ + protected function stream(): StreamInterface + { + if ($this->stream === null) { + if (!is_writable($this->dir)) { + throw new \Exception("The directory \"{$this->dir}\" is not writable!", 1); + } + $this->stream = new Stream($this->dir . $this->file, "a"); + } + return $this->stream; + } + + /** + * File rotation + * @return void + */ + protected function rotate(): void + { + if ($this->size !== null) { + $file = $this->dir . $this->file; + $filename = $this->fileInfo("filename"); + $extension = $this->fileInfo("extension"); + + if (is_file($file) && (filesize($file) > $this->size)) { + $files = glob($this->dir . "{$filename}*[0-9].{$extension}"); + $count = count($files); + sort($files); + + if ($this->count !== null && ($count >= $this->count)) { + for ($i = 0; $i < (($count - $this->count) + 1); $i++) { + unlink($files[$i]); + } + } + $date = time(); + rename($file, $this->dir . $filename . "-{$date}.{$extension}"); + } + } + } + + /** + * Get file information + * @param string $key + * @return string|null + */ + private function fileInfo(string $key): ?string + { + if ($this->info === null) { + $this->info = pathinfo($this->file); + } + return ($this->info[$key] ?? null); + } +} diff --git a/src/Interfaces/HandlerInterface.php b/src/Interfaces/HandlerInterface.php new file mode 100755 index 0000000..c3e2201 --- /dev/null +++ b/src/Interfaces/HandlerInterface.php @@ -0,0 +1,8 @@ +handler = $handler; + $this->dateTime = $dateTime; + } + + /** + * Log Value + * @param mixed $level + * @param string|\Stringable $message + * @param array $context + * @return void + */ + public function log(mixed $level, string|\Stringable $message, array $context = []): void + { + $this->level = strtoupper($level); + if (!defined(LogLevel::class . '::' . $this->level)) { + throw new InvalidArgumentException("The log level \"{$this->level}\" does not exist.", 1); + } + + $this->message = $message; + $this->context = $context; + $this->handler->handler( + $this->level, + $this->handler->interpolate((string)$this->message, $this->context), + $this->context, + $this->getDate() + ); + } + + public function getLevel(): mixed + { + return $this->level; + } + + public function getMessage(): string + { + return (string)$this->message; + } + + public function getContext(): array + { + return $this->context; + } + + /** + * Get formated date + * @return string + */ + protected function getDate(): string + { + if ($this->dateTime === null) { + $this->dateTime = new \DateTime("now"); + } + return $this->dateTime->format(static::DATETIME_FORMAT); + } +} diff --git a/src/LoggerAwareTrait.php b/src/LoggerAwareTrait.php new file mode 100755 index 0000000..1023eee --- /dev/null +++ b/src/LoggerAwareTrait.php @@ -0,0 +1,28 @@ +logger = $logger; + } +} diff --git a/src/LoggerTrait.php b/src/LoggerTrait.php new file mode 100755 index 0000000..4694fcb --- /dev/null +++ b/src/LoggerTrait.php @@ -0,0 +1,142 @@ +log(LogLevel::EMERGENCY, $message, $context); + } + + /** + * Action must be taken immediately. + * + * Example: Entire website down, database unavailable, etc. This should + * trigger the SMS alerts and wake you up. + * + * @param string|\Stringable $message + * @param array $context + * + * @return void + */ + public function alert(string|\Stringable $message, array $context = []): void + { + $this->log(LogLevel::ALERT, $message, $context); + } + + /** + * Critical conditions. + * + * Example: Application component unavailable, unexpected exception. + * + * @param string|\Stringable $message + * @param array $context + * + * @return void + */ + public function critical(string|\Stringable $message, array $context = []): void + { + $this->log(LogLevel::CRITICAL, $message, $context); + } + + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * + * @param string|\Stringable $message + * @param array $context + * + * @return void + */ + public function error(string|\Stringable $message, array $context = []): void + { + $this->log(LogLevel::ERROR, $message, $context); + } + + /** + * Exceptional occurrences that are not errors. + * + * Example: Use of deprecated APIs, poor use of an API, undesirable things + * that are not necessarily wrong. + * + * @param string|\Stringable $message + * @param array $context + * + * @return void + */ + public function warning(string|\Stringable $message, array $context = []): void + { + $this->log(LogLevel::WARNING, $message, $context); + } + + /** + * Normal but significant events. + * + * @param string|\Stringable $message + * @param array $context + * + * @return void + */ + public function notice(string|\Stringable $message, array $context = []): void + { + $this->log(LogLevel::NOTICE, $message, $context); + } + + /** + * Interesting events. + * + * Example: User logs in, SQL logs. + * + * @param string|\Stringable $message + * @param array $context + * + * @return void + */ + public function info(string|\Stringable $message, array $context = []): void + { + $this->log(LogLevel::INFO, $message, $context); + } + + /** + * Detailed debug information. + * + * @param string|\Stringable $message + * @param array $context + * + * @return void + */ + public function debug(string|\Stringable $message, array $context = []): void + { + $this->log(LogLevel::DEBUG, $message, $context); + } + + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string|\Stringable $message + * @param array $context + * + * @return void + * + * @throws \Psr\Log\InvalidArgumentException + */ + abstract public function log($level, string|\Stringable $message, array $context = []): void; +} diff --git a/src/NullLogger.php b/src/NullLogger.php new file mode 100755 index 0000000..5a07ee8 --- /dev/null +++ b/src/NullLogger.php @@ -0,0 +1,32 @@ +logger) { }` + * blocks. + */ +class NullLogger extends AbstractLogger +{ + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string|\Stringable $message + * @param array $context + * + * @return void + * + * @throws \Psr\Log\InvalidArgumentException + */ + public function log($level, string|\Stringable $message, array $context = []): void + { + // noop + } +}