diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a879886 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/.idea/ +/.vs/ +/.vscode/ +/vendor/ +/composer.lock \ No newline at end of file diff --git a/README.md b/README.md index fb1df82..e24da33 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,143 @@ -# Logger -InitPHP Logger Library +# InitPHP Logger + +Logger class in accordance with PSR-3 standards + +[![Latest Stable Version](http://poser.pugx.org/initphp/logger/v)](https://packagist.org/packages/initphp/logger) [![Total Downloads](http://poser.pugx.org/initphp/logger/downloads)](https://packagist.org/packages/initphp/logger) [![Latest Unstable Version](http://poser.pugx.org/initphp/logger/v/unstable)](https://packagist.org/packages/initphp/logger) [![License](http://poser.pugx.org/initphp/logger/license)](https://packagist.org/packages/initphp/logger) [![PHP Version Require](http://poser.pugx.org/initphp/logger/require/php)](https://packagist.org/packages/initphp/logger) + +## Features + +- Keeping logs to the database with PDO. +- Printing log records to a file. +- Logging feature with multiple drivers. + +## Requirements + +- PHP 5.6 or higher +- [PSR-3 Interface Package](https://www.php-fig.org/psr/psr-3/) +- PDO Extension (Only `PDOLogger`) + +## Installation + +``` +composer require initphp/logger +``` + +## Using + +### FileLogger + +```php +require_once "vendor/autoload.php"; +use \InitPHP\Logger\Logger; +use \InitPHP\Logger\FileLogger; + +$logFile = __DIR__ . '/logfile.log'; + +$logger = new Logger(new FileLogger($logFile)); +``` + +### PdoLogger + +```php +require_once "vendor/autoload.php"; +use \InitPHP\Logger\Logger; +use \InitPHP\Logger\PDOLogger; + +$table = 'logs'; +$pdo = new \PDO('mysql:dbname=project;host=localhost', 'root', ''); + +$logger = new Logger(new PDOLogger($pdo, $table)); + +$logger->error('User {user} caused an error.', array('user' => 'muhametsafak')); +// INSERT INTO logs (level, message, date) VALUES ('ERROR', 'User muhametsafak caused an error.', '2022-03-11 13:05:45') +``` + +You can use the following SQL statement to create a sample MySQL table. + +```sql +CREATE TABLE `logs` ( + `level` ENUM('EMERGENCY','ALERT','CRITICAL','ERROR','WARNING','NOTICE','INFO','DEBUG') NOT NULL, + `message` TEXT NOT NULL, + `date` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +) ENGINE = InnoDB CHARSET=utf8mb4 COLLATE utf8mb4_general_ci; +``` + +### Multi Logger + +```php +require_once "vendor/autoload.php"; +use \InitPHP\Logger\Logger; +use \InitPHP\Logger\PDOLogger; +use \InitPHP\Logger\FileLogger; + +$logFile = __DIR__ . '/logfile.log'; + +$table = 'logs'; +$pdo = new \PDO('mysql:dbname=project;host=localhost', 'root', ''); + +$logger = new Logger(new FileLogger($logFile), new PDOLogger($pdo, $table)); +``` + +## Methods + +```php +public function emergency(string $msg, array $context = array()): void; + +public function alert(string $msg, array $context = array()): void; + +public function critical(string $msg, array $context = array()): void; + +public function error(string $msg, array $context = array()): void; + +public function warning(string $msg, array $context = array()): void; + +public function notice(string $msg, array $context = array()): void; + +public function info(string $msg, array $context = array()): void; + +public function debug(string $msg, array $context = array()): void; + +public function log(string $level, string $msg, array $context = array()): void; +``` + +All of the above methods are used the same way, except for the `log()` method. You can use the `log()` method for your own custom error levels. + +**Example 1 :** + +```php +$logger->emergency("Something went wrong"); +``` + +It prints an output like this to the log file. + +``` +2021-09-29T13:34:47+02:00 [EMERGENCY] Something went wrong +``` + +**Example 2:** + +```php +$logger->error("User {username} caused an error.", ["username" => "john"]); +``` + +It prints an output like this to the log file. + +``` +2021-09-29T13:34:47+02:00 [ERROR] User john caused an error. +``` + +That is all. + +*** + +## Getting Help + +If you have questions, concerns, bug reports, etc, please file an issue in this repository's Issue Tracker. + +## Credits + +- [Muhammet ŞAFAK](https://www.muhammetsafak.com.tr) + +## License + +Copyright © 2022 [MIT License](./LICENSE) diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..8b4efae --- /dev/null +++ b/composer.json @@ -0,0 +1,24 @@ +{ + "name": "initphp/logger", + "description": "PSR-3 Logger Library", + "type": "library", + "license": "MIT", + "autoload": { + "psr-4": { + "InitPHP\\Logger\\": "src/" + } + }, + "authors": [ + { + "name": "Muhammet ŞAFAK", + "email": "info@muhammetsafak.com.tr", + "role": "Developer", + "homepage": "https://www.muhammetsafak.com.tr" + } + ], + "minimum-stability": "stable", + "require": { + "php": ">=5.6", + "psr/log": "1.1.4" + } +} diff --git a/src/FileLogger.php b/src/FileLogger.php new file mode 100644 index 0000000..ecf0989 --- /dev/null +++ b/src/FileLogger.php @@ -0,0 +1,59 @@ + + * @copyright Copyright © 2022 InitPHP + * @license http://initphp.github.io/license.txt MIT + * @version 1.0 + * @link https://www.muhammetsafak.com.tr + */ + +namespace InitPHP\Logger; + +use \Psr\Log\AbstractLogger; +use \Psr\Log\LoggerInterface; + +use const PHP_EOL; +use const FILE_APPEND; + +use function strtoupper; +use function date; + +class FileLogger extends \Psr\Log\AbstractLogger implements \Psr\Log\LoggerInterface +{ + use HelperTrait; + + /** @var string */ + protected $path; + + /** + * @param string $path + */ + public function __construct($path) + { + $this->path = $this->interpolate($path, array( + 'year' => date('Y'), + 'month' => date('m'), + 'day' => date('d'), + 'hour' => date('H'), + 'minute' => date('i'), + 'second' => date('s') + )); + } + + /** + * @inheritDoc + */ + public function log($level, $message, array $context = array()) + { + $this->logLevelVerify($level); + $msg = PHP_EOL . $this->getDate('c') . ' [' + . strtoupper($level) + . '] ' . $this->interpolate($message, $context); + @file_put_contents($this->path, $msg, FILE_APPEND); + } + +} diff --git a/src/HelperTrait.php b/src/HelperTrait.php new file mode 100644 index 0000000..c9f274b --- /dev/null +++ b/src/HelperTrait.php @@ -0,0 +1,82 @@ + + * @copyright Copyright © 2022 InitPHP + * @license http://initphp.github.io/license.txt MIT + * @version 1.0 + * @link https://www.muhammetsafak.com.tr + */ + +namespace InitPHP\Logger; + +use \DateTime; +use \Psr\Log\InvalidArgumentException; +use \Psr\Log\LogLevel; + +use function strtolower; +use function in_array; +use function implode; +use function is_array; +use function is_object; +use function method_exists; +use function strtr; + +trait HelperTrait +{ + /** @var array */ + private $levels = array( + LogLevel::EMERGENCY, + LogLevel::ALERT, + LogLevel::CRITICAL, + LogLevel::ERROR, + LogLevel::WARNING, + LogLevel::WARNING, + LogLevel::NOTICE, + LogLevel::INFO, + LogLevel::DEBUG + ); + + /** + * @param string $msg + * @param array $context + * @return string + */ + protected function interpolate($msg, array $context = array()) + { + if(empty($context)){ + return $msg; + }else{ + $replace = array(); + foreach ($context as $key => $val) { + if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) { + $replace['{' . $key . '}'] = $val; + } + } + return strtr($msg, $replace); + } + } + + /** + * @param string $format + * @return string + */ + protected function getDate($format = 'c') + { + return (new DateTime('now'))->format($format); + } + + /** + * @param $level + * @return void + */ + protected function logLevelVerify($level) + { + if(in_array(strtolower($level), $this->levels, true) === FALSE){ + throw new InvalidArgumentException('Only ' . implode(', ', $this->levels) . ' levels can be logged.'); + } + } +} diff --git a/src/Logger.php b/src/Logger.php new file mode 100644 index 0000000..8b2af44 --- /dev/null +++ b/src/Logger.php @@ -0,0 +1,50 @@ + + * @copyright Copyright © 2022 InitPHP + * @license http://initphp.github.io/license.txt MIT + * @version 1.0 + * @link https://www.muhammetsafak.com.tr + */ + +namespace InitPHP\Logger; + +use \Psr\Log\LoggerInterface; + +/** + * @method void emergency(string $message, array $context = array()) + * @method void alert(string $message, array $context = array()) + * @method void critical(string $message, array $context = array()) + * @method void error(string $message, array $context = array()) + * @method void warning(string $message, array $context = array()) + * @method void notice(string $message, array $context = array()) + * @method void info(string $message, array $context = array()) + * @method void debug(string $message, array $context = array()) + * @method void log(string $level, string $message, array $context = array()) + */ +class Logger +{ + /** @var LoggerInterface[] */ + protected $loggers = []; + + public function __construct(...$loggers) + { + foreach ($loggers as $log) { + if($log instanceof LoggerInterface){ + $this->loggers[] = $log; + } + } + } + + public function __call($name, $arguments) + { + foreach ($this->loggers as $logger) { + $logger->{$name}(...$arguments); + } + } + +} diff --git a/src/PDOLogger.php b/src/PDOLogger.php new file mode 100644 index 0000000..8800585 --- /dev/null +++ b/src/PDOLogger.php @@ -0,0 +1,64 @@ + + * @copyright Copyright © 2022 InitPHP + * @license http://initphp.github.io/license.txt MIT + * @version 1.0 + * @link https://www.muhammetsafak.com.tr + */ + +namespace InitPHP\Logger; + +use \Psr\Log\AbstractLogger; +use \Psr\Log\InvalidArgumentException; +use \Psr\Log\LoggerInterface; +use \PDO; + +use function strtoupper; + +class PDOLogger extends \Psr\Log\AbstractLogger implements \Psr\Log\LoggerInterface +{ + use HelperTrait; + + /** @var PDO */ + protected $pdo; + + /** @var string */ + protected $table; + + public function __construct($pdo, $table) + { + if(!($pdo instanceof PDO)){ + throw new \InvalidArgumentException('It must be a PDO object.'); + } + if(!is_string($table)){ + throw new \InvalidArgumentException('The name of the table where the logs will be kept must be specified as a string.'); + } + $this->pdo = $pdo; + $this->table = $table; + } + + public function __destruct() + { + $this->pdo = null; + } + + /** + * @inheritDoc + */ + public function log($level, $message, array $context = array()) + { + $this->logLevelVerify($level); + $date = $this->getDate('Y-m-d H:i:s'); + $level = strtoupper($level); + $msg = $this->interpolate($message, $context); + + $sql = "INSERT INTO " . $this->table . " (level, message, date) VALUES (?,?,?)"; + $query = $this->pdo->prepare($sql); + $query->execute(array($level, $msg, $date)); + } +} \ No newline at end of file