From 54b3776313cd52fe480f2840d0be110aaafb68e9 Mon Sep 17 00:00:00 2001 From: Patrick Niebeling Date: Thu, 10 Mar 2022 09:36:09 +0100 Subject: [PATCH] Update Signed-off-by: Patrick Niebeling --- LICENSE | 21 ++++ Logger.php | 349 +++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 113 +++++++++++++++++ 3 files changed, 483 insertions(+) create mode 100644 LICENSE create mode 100644 Logger.php create mode 100644 README.md diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..89bfb9f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Lars + +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. diff --git a/Logger.php b/Logger.php new file mode 100644 index 0000000..be32dfa --- /dev/null +++ b/Logger.php @@ -0,0 +1,349 @@ + 'd-M-Y', + 'logFormat' => 'H:i:s d-M-Y', + 'logFile' => '/logs/log-{$time}.txt' + ]; + + private static $instance; + + /** + * Create the log file + * @param string $log_file - path and filename of log + * @param array $params - settable options + */ + public static function createLogFile() + { + $time = date(static::$options['dateFormat']); + static::$log_file = __DIR__ . static::$options['logFile']; + + + //Check if directory /logs exists + if (!file_exists(__DIR__ . '/logs')) { + mkdir(__DIR__ . '/logs', 0777, true); + } + + //Create log file if it doesn't exist. + if (!file_exists(static::$log_file)) { + fopen(static::$log_file, 'w') or exit("Can't create {static::log_file}!"); + } + + //Check permissions of file. + if (!is_writable(static::$log_file)) { + //throw exception if not writable + throw new Exception("ERROR: Unable to write to file!", 1); + } + } + + /** + * Set logging options (optional) + * @param array $options Array of settable options + * + * Options: + * [ + * 'dateFormat' => 'value of the date format the .txt file should be saved int' + * 'logFormat' => 'value of the date format each log event should be saved int' + * ] + */ + public static function setOptions($options = []) + { + static::$options = array_merge(static::$options, $options); + } + + /** + * Info method (write info message) + * + * Used for e.g.: "The user example123 has created a post". + * + * @param string $message Descriptive text of the debug + * @param string $context Array to expend the message's meaning + * @return void + */ + public static function info($message, array $context = []) + { + // grab the line and file path where the log method has been executed ( for troubleshooting ) + $bt = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1); + + //execute the writeLog method with passing the arguments + static::writeLog([ + 'message' => $message, + 'bt' => $bt, + 'severity' => 'INFO', + 'context' => $context + ]); + } + + /** + * Notice method (write notice message) + * + * Used for e.g.: "The user example123 has created a post". + * + * @param string $message Descriptive text of the debug + * @param string $context Array to expend the message's meaning + * @return void + */ + public static function notice($message, array $context = []) + { + // grab the line and file path where the log method has been executed ( for troubleshooting ) + $bt = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1); + + //execute the writeLog method with passing the arguments + static::writeLog([ + 'message' => $message, + 'bt' => $bt, + 'severity' => 'NOTICE', + 'context' => $context + ]); + } + + /** + * Debug method (write debug message) + * + * Used for debugging, could be used instead of echo'ing values + * + * @param string $message Descriptive text of the debug + * @param string $context Array to expend the message's meaning + * @return void + */ + public static function debug($message, array $context = []) + { + + // grab the line and file path where the log method has been executed ( for troubleshooting ) + $bt = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1); + + //execute the writeLog method with passing the arguments + static::writeLog([ + 'message' => $message, + 'bt' => $bt, + 'severity' => 'DEBUG', + 'context' => $context + ]); + } + + /** + * Warning method (write warning message) + * + * Used for warnings which is not fatal to the current operation + * + * @param string $message Descriptive text of the warning + * @param string $context Array to expend the message's meaning + * @return void + */ + public static function warning($message, array $context = []) + { + // grab the line and file path where the log method has been executed ( for troubleshooting ) + $bt = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1); + + //execute the writeLog method with passing the arguments + static::writeLog([ + 'message' => $message, + 'bt' => $bt, + 'severity' => 'WARNING', + 'context' => $context + ]); + } + + /** + * Error method (write error message) + * + * Used for e.g. file not found,... + * + * @param string $message Descriptive text of the error + * @param string $context Array to expend the message's meaning + * @return void + */ + public static function error($message, array $context = []) + { + // grab the line and file path where the log method has been executed ( for troubleshooting ) + $bt = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1); + + //execute the writeLog method with passing the arguments + static::writeLog([ + 'message' => $message, + 'bt' => $bt, + 'severity' => 'ERROR', + 'context' => $context + ]); + } + + /** + * Fatal method (write fatal message) + * + * Used for e.g. database unavailable, system shutdown + * + * @param string $message Descriptive text of the error + * @param string $context Array to expend the message's meaning + * @return void + */ + public static function fatal($message, array $context = []) + { + // grab the line and file path where the log method has been executed ( for troubleshooting ) + $bt = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 1); + + //execute the writeLog method with passing the arguments + static::writeLog([ + 'message' => $message, + 'bt' => $bt, + 'severity' => 'FATAL', + 'context' => $context + ]); + } + + /** + * Write to log file + * @param array $args Array of message (for log file), line (of log method execution), severity (for log file) and displayMessage (to display on frontend for the used) + * @return void + */ + // public function writeLog($message, $line = null, $displayMessage = null, $severity) + public static function writeLog($args = []) + { + //Create the log file + static::createLogFile(); + + // open log file + if (!is_resource(static::$file)) { + static::openLog(); + } + + // // grab the url path + // $path = $_SERVER["SERVER_NAME"] . $_SERVER["REQUEST_URI"]; + + //Grab time - based on the time format + $time = date(static::$options['logFormat']); + + // Convert context to json + $context = json_encode($args['context']); + + $caller = array_shift($args['bt']); + $btLine = $caller['line']; + $btPath = $caller['file']; + + // Convert absolute path to relative path (using UNIX directory seperators) + $path = static::absToRelPath($btPath); + + // Create log variable = value pairs + $timeLog = is_null($time) ? "[N/A] " : "[{$time}] "; + $pathLog = is_null($path) ? "[N/A] " : "[{$path}] "; + $lineLog = is_null($btLine) ? "[N/A] " : "[{$btLine}] "; + $severityLog = is_null($args['severity']) ? "[N/A]" : "[{$args['severity']}]"; + $messageLog = is_null($args['message']) ? "N/A" : "{$args['message']}"; + $contextLog = empty($args['context']) ? "" : "{$context}"; + + // Write time, url, & message to end of file + fwrite(static::$file, "{$timeLog}{$pathLog}{$lineLog}: {$severityLog} - {$messageLog} {$contextLog}" . PHP_EOL); + + // Close file stream + static::closeFile(); + } + + /** + * Open log file + * @return void + */ + private static function openLog() + { + $openFile = static::$log_file; + // 'a' option = place pointer at end of file + static::$file = fopen($openFile, 'a') or exit("Can't open $openFile!"); + } + + /** + * Close file stream + */ + public static function closeFile() + { + if (static::$file) { + fclose(static::$file); + } + } + + /** + * Convert absolute path to relative url (using UNIX directory seperators) + * + * E.g.: + * Input: D:\development\htdocs\public\todo-list\index.php + * Output: localhost/todo-list/index.php + * + * @param string Absolute directory/path of file which should be converted to a relative (url) path + * @return string Relative path + */ + public static function absToRelPath($pathToConvert) + { + $pathAbs = str_replace(['/', '\\'], '/', $pathToConvert); + $documentRoot = str_replace(['/', '\\'], '/', $_SERVER['DOCUMENT_ROOT']); + return $_SERVER['SERVER_NAME'] . str_replace($documentRoot, '', $pathAbs); + } + + /** + * The Singleton's constructor should always be private to prevent direct + * construction calls with the `new` operator. + */ + protected function __construct() + { } + + /** + * Singletons should not be cloneable. + */ + protected function __clone() + { } + + /** + * Singletons should not be restorable from strings. + */ + public function __wakeup() + { + throw new \Exception("Cannot unserialize a singleton."); + } + + public static function getInstance() + { + if (is_null(self::$instance)) { + self::$instance = new self(); + } + return self::$instance; + } + + /** + * The Singleton's constructor should always be private to prevent direct + * construction calls with the `new` operator. + */ + private function __destruct() + { } +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..48b8aba --- /dev/null +++ b/README.md @@ -0,0 +1,113 @@ +# :floppy_disk: Simple PHP Logger :floppy_disk: + +*Simple php logger* is a single file PHP log writer which writes logs into a .txt file using one line of code, e.g. +```php +Logger::info("I'm an info message"); +``` +outputs inside a *log-28-nov-2019.txt*: +``` +[22:50:35 28-Nov-2019] [localhost/test.php] [1] : [INFO] - I'm an info message +``` + +#### Features +- single file +- singleton pattern +- six log levels (info, notice, debug, warning, error, fatal) +- logs the line where the `Logger` method is executed (good for troubleshooting) +- logs the relative filepath of the source file, not the required one (good for troubleshooting) + +### :wrench: Motivation +There are many PHP Logger's out there, but most of them are either very heavy or too basic. Sometimes, you simply want something in the middle, suitable for every situation and project size. Therefore, I've decided to create a simple PHP Logger, inspired by several other logging libraries. + +### :white_check_mark: Installation +Simply download the Logger.php file and require it wherever you need it. +You can also insert it into a *Log* directory and add a namespace to it, for those who want to use it within an OOP project +Make sure that the `logs/` directory exists and is writable, else the logger throws an error that it cant read/write/create the log files. + +### :mag_right: Log levels +The simple php logger uses six log levels: + +|Level |Description | Example | +|---|---|---| +| INFO | Used for general informations | "The server has been running X hours" | +| NOTICE | Used for interesting events | "The user $userName has logged in." | +| DEBUG | Used for debugging | Could be used instead of echo'ing values | +| WARNING | Used for anything that might/ might not be a problem. | "The image XX is 30MB big. This can cause slow web performance" | +| ERROR | Any error which is fatal to the operation, but not shutting down the application| Can't open a required file, missing data, etc. | +| FATAL | Any error which is shutting down the application| Database unavailable | + +### :books: How to use + +#### Basic example +Here's a basic example how you could use simple php logger +```php +getMessage()]); // <- Log a fatal error with details + + die('Oh snap, looks like something didn't work. Please retry again later'); // <- UX friendly die() message + } +} +``` + +#### Logging methods +```php + 'Y-M-d H:i:s' +]); +``` + +**Logger outputs** +```codes +[15:45:33 27-Nov-2019] [localhost/test.php] [4] : [INFO]- The article XX has YY comments +[15:45:33 27-Nov-2019] [localhost/test.php] [8] : [NOTICE]- The user XX has created the YY article +[15:45:33 27-Nov-2019] [localhost/test.php] [12] : [DEBUG]- This is where the code breaks +[15:45:33 27-Nov-2019] [localhost/test.php] [16] : [WARNING]- The file XX is 100GB big +[15:45:33 27-Nov-2019] [localhost/test.php] [19] : [ERROR]- The file XX is missing +[15:45:33 27-Nov-2019] [localhost/test.php] [23] : [FATAL]- Database connection failed ["Very bad database","I didnt feed him"] +``` + +##### Note +This logger is not a replacement for a PSR-3 compliant logger library such as [Monolog](https://github.com/Seldaek/monolog), [KLogger](https://github.com/katzgrau/KLogger) or [Analog](https://github.com/jbroadway/analog). This Logger should simply be used to quickly implement a simple logger with several methods.