pax_global_header00006660000000000000000000000064142710427520014516gustar00rootroot0000000000000052 comment=36481c69db9305169a2ceadead25c2acaabd567c php-klogger-1.2.2/000077500000000000000000000000001427104275200137375ustar00rootroot00000000000000php-klogger-1.2.2/.gitignore000066400000000000000000000001121427104275200157210ustar00rootroot00000000000000/tests/logs/*.txt /tests/logs/*.log basic-test.php /vendor/ composer.lock php-klogger-1.2.2/README.markdown000066400000000000000000000161231427104275200164430ustar00rootroot00000000000000# KLogger: Simple Logging for PHP A project written by [Kenny Katzgrau](http://twitter.com/katzgrau) and [Dan Horrigan](http://twitter.com/dhrrgn). ## About KLogger is an easy-to-use [PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md) compliant logging class for PHP. It isn't naive about file permissions (which is expected). It was meant to be a class that you could quickly include into a project and have working right away. If you need a logger that supports PHP < 5.3, see [past releases](https://github.com/katzgrau/KLogger/releases) for KLogger versions < 1.0.0. ## Installation ### Composer From the Command Line: ``` composer require katzgrau/klogger:dev-master ``` In your `composer.json`: ``` json { "require": { "katzgrau/klogger": "dev-master" } } ``` ## Basic Usage ``` php 'Kenny Katzgrau', 'username' => 'katzgrau', ], [ 'name' => 'Dan Horrigan', 'username' => 'dhrrgn', ], ]; $logger = new Katzgrau\KLogger\Logger(__DIR__.'/logs'); $logger->info('Returned a million search results'); $logger->error('Oh dear.'); $logger->debug('Got these users from the Database.', $users); ``` ### Output ``` [2014-03-20 3:35:43.762437] [INFO] Returned a million search results [2014-03-20 3:35:43.762578] [ERROR] Oh dear. [2014-03-20 3:35:43.762795] [DEBUG] Got these users from the Database. 0: array( 'name' => 'Kenny Katzgrau', 'username' => 'katzgrau', ) 1: array( 'name' => 'Dan Horrigan', 'username' => 'dhrrgn', ) ``` ## PSR-3 Compliant KLogger is [PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md) compliant. This means it implements the `Psr\Log\LoggerInterface`. [See Here for the interface definition.](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md#3-psrlogloggerinterface) ## Setting the Log Level Threshold You can use the `Psr\Log\LogLevel` constants to set Log Level Threshold, so that any messages below that level, will not be logged. ### Default Level The default level is `DEBUG`, which means everything will be logged. ### Available Levels ``` php error('Uh Oh!'); // Will be logged $logger->info('Something Happened Here'); // Will be NOT logged ``` ### Additional Options KLogger supports additional options via third parameter in the constructor: ``` php 'log', // changes the log file extension )); ``` Here's the full list: | Option | Default | Description | | ------ | ------- | ----------- | | dateFormat | 'Y-m-d G:i:s.u' | The format of the date in the start of the log lone (php formatted) | | extension | 'txt' | The log file extension | | filename | [prefix][date].[extension] | Set the filename for the log file. **This overrides the prefix and extention options.** | | flushFrequency | `false` (disabled) | How many lines to flush the output buffer after | | prefix | 'log_' | The log file prefix | | logFormat | `false` | Format of log entries | | appendContext | `true` | When `false`, don't append context to log entries | ### Log Formatting The `logFormat` option lets you define what each line should look like and can contain parameters representing the date, message, etc. When a string is provided, it will be parsed for variables wrapped in braces (`{` and `}`) and replace them with the appropriate value: | Parameter | Description | | --------- | ----------- | | date | Current date (uses `dateFormat` option) | | level | The PSR log level | | level-padding | The whitespace needed to make this log level line up visually with other log levels in the log file | | priority | Integer value for log level (see `$logLevels`) | | message | The message being logged | | context | JSON-encoded context | #### Tab-separated Same as default format but separates parts with tabs rather than spaces: $logFormat = "[{date}]\t[{level}]\t{message}"; #### Custom variables and static text Inject custom content into log messages: $logFormat = "[{date}] [$var] StaticText {message}"; #### JSON To output pure JSON, set `appendContext` to `false` and provide something like the below as the value of the `logFormat` option: ``` $logFormat = json_encode([ 'datetime' => '{date}', 'logLevel' => '{level}', 'message' => '{message}', 'context' => '{context}', ]); ``` The output will look like: {"datetime":"2015-04-16 10:28:41.186728","logLevel":"INFO","message":"Message content","context":"{"1":"foo","2":"bar"}"} #### Pretty Formatting with Level Padding For the obsessive compulsive $logFormat = "[{date}] [{level}]{level-padding} {message}"; ... or ... $logFormat = "[{date}] [{level}{level-padding}] {message}"; ## Why use KLogger? Why not? Just drop it in and go. If it saves you time and does what you need, go for it! Take a line from the book of our C-code fathers: "`build` upon the work of others". ## Who uses KLogger? Klogger has been used in projects at: * The University of Iowa * The University of Laverne * The New Jersey Institute of Technology * Middlesex Hospital in NJ Additionally, it's been used in numerous projects, both commercial and personal. ## Special Thanks Special thanks to all contributors: * [Dan Horrigan](http://twitter.com/dhrrgn) * [Brian Fenton](http://github.com/fentie) * [Tim Kinnane](http://twitter.com/etherealtim) * [Onno Vos](https://github.com/onno-vos-dev) * [Cameron Will](https://github.com/cwill747) * [Kamil WylegaƂa](https://github.com/kamilwylegala) ## License The MIT License Copyright (c) 2008-2015 Kenny Katzgrau 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. php-klogger-1.2.2/composer.json000066400000000000000000000012031427104275200164550ustar00rootroot00000000000000{ "name": "katzgrau/klogger", "version": "1.2.2", "description": "A Simple Logging Class", "keywords": ["logging"], "require": { "php": ">=5.3", "psr/log": "^1.0.0" }, "require-dev": { "phpunit/phpunit": "^6.0.0" }, "license": "MIT", "authors": [ { "name": "Kenny Katzgrau", "email": "katzgrau@gmail.com" }, { "name": "Dan Horrigan", "email": "dan@dhorrigan.com" } ], "autoload": { "psr-4": { "Katzgrau\\KLogger\\": "src/" }, "classmap": ["src/"] } } php-klogger-1.2.2/phpunit.xml000066400000000000000000000010251427104275200161460ustar00rootroot00000000000000 tests ./vendor php-klogger-1.2.2/src/000077500000000000000000000000001427104275200145265ustar00rootroot00000000000000php-klogger-1.2.2/src/Logger.php000077500000000000000000000244021427104275200164630ustar00rootroot00000000000000info('Returned a million search results'); //Prints to the log file * $log->error('Oh dear.'); //Prints to the log file * $log->debug('x = 5'); //Prints nothing due to current severity threshhold * * @author Kenny Katzgrau * @since July 26, 2008 * @link https://github.com/katzgrau/KLogger * @version 1.0.0 */ /** * Class documentation */ class Logger extends AbstractLogger { /** * KLogger options * Anything options not considered 'core' to the logging library should be * settable view the third parameter in the constructor * * Core options include the log file path and the log threshold * * @var array */ protected $options = array ( 'extension' => 'txt', 'dateFormat' => 'Y-m-d G:i:s.u', 'filename' => false, 'flushFrequency' => false, 'prefix' => 'log_', 'logFormat' => false, 'appendContext' => true, ); /** * Path to the log file * @var string */ private $logFilePath; /** * Current minimum logging threshold * @var integer */ protected $logLevelThreshold = LogLevel::DEBUG; /** * The number of lines logged in this instance's lifetime * @var int */ private $logLineCount = 0; /** * Log Levels * @var array */ protected $logLevels = array( LogLevel::EMERGENCY => 0, LogLevel::ALERT => 1, LogLevel::CRITICAL => 2, LogLevel::ERROR => 3, LogLevel::WARNING => 4, LogLevel::NOTICE => 5, LogLevel::INFO => 6, LogLevel::DEBUG => 7 ); /** * This holds the file handle for this instance's log file * @var resource */ private $fileHandle; /** * This holds the last line logged to the logger * Used for unit tests * @var string */ private $lastLine = ''; /** * Octal notation for default permissions of the log file * @var integer */ private $defaultPermissions = 0777; /** * Class constructor * * @param string $logDirectory File path to the logging directory * @param string $logLevelThreshold The LogLevel Threshold * @param array $options * * @internal param string $logFilePrefix The prefix for the log file name * @internal param string $logFileExt The extension for the log file */ public function __construct($logDirectory, $logLevelThreshold = LogLevel::DEBUG, array $options = array()) { $this->logLevelThreshold = $logLevelThreshold; $this->options = array_merge($this->options, $options); $logDirectory = rtrim($logDirectory, DIRECTORY_SEPARATOR); if ( ! file_exists($logDirectory)) { mkdir($logDirectory, $this->defaultPermissions, true); } if(strpos($logDirectory, 'php://') === 0) { $this->setLogToStdOut($logDirectory); $this->setFileHandle('w+'); } else { $this->setLogFilePath($logDirectory); if(file_exists($this->logFilePath) && !is_writable($this->logFilePath)) { throw new RuntimeException('The file could not be written to. Check that appropriate permissions have been set.'); } $this->setFileHandle('a'); } if ( ! $this->fileHandle) { throw new RuntimeException('The file could not be opened. Check permissions.'); } } /** * @param string $stdOutPath */ public function setLogToStdOut($stdOutPath) { $this->logFilePath = $stdOutPath; } /** * @param string $logDirectory */ public function setLogFilePath($logDirectory) { if ($this->options['filename']) { if (strpos($this->options['filename'], '.log') !== false || strpos($this->options['filename'], '.txt') !== false) { $this->logFilePath = $logDirectory.DIRECTORY_SEPARATOR.$this->options['filename']; } else { $this->logFilePath = $logDirectory.DIRECTORY_SEPARATOR.$this->options['filename'].'.'.$this->options['extension']; } } else { $this->logFilePath = $logDirectory.DIRECTORY_SEPARATOR.$this->options['prefix'].date('Y-m-d').'.'.$this->options['extension']; } } /** * @param $writeMode * * @internal param resource $fileHandle */ public function setFileHandle($writeMode) { $this->fileHandle = fopen($this->logFilePath, $writeMode); } /** * Class destructor */ public function __destruct() { if ($this->fileHandle) { fclose($this->fileHandle); } } /** * Sets the date format used by all instances of KLogger * * @param string $dateFormat Valid format string for date() */ public function setDateFormat($dateFormat) { $this->options['dateFormat'] = $dateFormat; } /** * Sets the Log Level Threshold * * @param string $logLevelThreshold The log level threshold */ public function setLogLevelThreshold($logLevelThreshold) { $this->logLevelThreshold = $logLevelThreshold; } /** * Logs with an arbitrary level. * * @param mixed $level * @param string $message * @param array $context * @return null */ public function log($level, $message, array $context = array()) { if ($this->logLevels[$this->logLevelThreshold] < $this->logLevels[$level]) { return; } $message = $this->formatMessage($level, $message, $context); $this->write($message); } /** * Writes a line to the log without prepending a status or timestamp * * @param string $message Line to write to the log * @return void */ public function write($message) { if (null !== $this->fileHandle) { if (fwrite($this->fileHandle, $message) === false) { throw new RuntimeException('The file could not be written to. Check that appropriate permissions have been set.'); } else { $this->lastLine = trim($message); $this->logLineCount++; if ($this->options['flushFrequency'] && $this->logLineCount % $this->options['flushFrequency'] === 0) { fflush($this->fileHandle); } } } } /** * Get the file path that the log is currently writing to * * @return string */ public function getLogFilePath() { return $this->logFilePath; } /** * Get the last line logged to the log file * * @return string */ public function getLastLogLine() { return $this->lastLine; } /** * Formats the message for logging. * * @param string $level The Log Level of the message * @param string $message The message to log * @param array $context The context * @return string */ protected function formatMessage($level, $message, $context) { if ($this->options['logFormat']) { $parts = array( 'date' => $this->getTimestamp(), 'level' => strtoupper($level), 'level-padding' => str_repeat(' ', 9 - strlen($level)), 'priority' => $this->logLevels[$level], 'message' => $message, 'context' => json_encode($context), ); $message = $this->options['logFormat']; foreach ($parts as $part => $value) { $message = str_replace('{'.$part.'}', $value, $message); } } else { $message = "[{$this->getTimestamp()}] [{$level}] {$message}"; } if ($this->options['appendContext'] && ! empty($context)) { $message .= PHP_EOL.$this->indent($this->contextToString($context)); } return $message.PHP_EOL; } /** * Gets the correctly formatted Date/Time for the log entry. * * PHP DateTime is dump, and you have to resort to trickery to get microseconds * to work correctly, so here it is. * * @return string */ private function getTimestamp() { $originalTime = microtime(true); $micro = sprintf("%06d", ($originalTime - floor($originalTime)) * 1000000); $date = new DateTime(date('Y-m-d H:i:s.'.$micro, (int)$originalTime)); return $date->format($this->options['dateFormat']); } /** * Takes the given context and coverts it to a string. * * @param array $context The Context * @return string */ protected function contextToString($context) { $export = ''; foreach ($context as $key => $value) { $export .= "{$key}: "; $export .= preg_replace(array( '/=>\s+([a-zA-Z])/im', '/array\(\s+\)/im', '/^ |\G /m' ), array( '=> $1', 'array()', ' ' ), str_replace('array (', 'array(', var_export($value, true))); $export .= PHP_EOL; } return str_replace(array('\\\\', '\\\''), array('\\', '\''), rtrim($export)); } /** * Indents the given string with the given indent. * * @param string $string The string to indent * @param string $indent What to use as the indent. * @return string */ protected function indent($string, $indent = ' ') { return $indent.str_replace("\n", "\n".$indent, $string); } } php-klogger-1.2.2/tests/000077500000000000000000000000001427104275200151015ustar00rootroot00000000000000php-klogger-1.2.2/tests/LoggerTest.php000066400000000000000000000046371427104275200177030ustar00rootroot00000000000000logPath = __DIR__.'/logs'; $this->logger = new Logger($this->logPath, LogLevel::DEBUG, array ('flushFrequency' => 1)); $this->errLogger = new Logger($this->logPath, LogLevel::ERROR, array ( 'extension' => 'log', 'prefix' => 'error_', 'flushFrequency' => 1 )); } public function testImplementsPsr3LoggerInterface() { $this->assertInstanceOf('Psr\Log\LoggerInterface', $this->logger); } public function testAcceptsExtension() { $this->assertStringEndsWith('.log', $this->errLogger->getLogFilePath()); } public function testAcceptsPrefix() { $filename = basename($this->errLogger->getLogFilePath()); $this->assertStringStartsWith('error_', $filename); } public function testWritesBasicLogs() { $this->logger->log(LogLevel::DEBUG, 'This is a test'); $this->errLogger->log(LogLevel::ERROR, 'This is a test'); $this->assertTrue(file_exists($this->errLogger->getLogFilePath())); $this->assertTrue(file_exists($this->logger->getLogFilePath())); $this->assertLastLineEquals($this->logger); $this->assertLastLineEquals($this->errLogger); } public function assertLastLineEquals(Logger $logr) { $this->assertEquals($logr->getLastLogLine(), $this->getLastLine($logr->getLogFilePath())); } public function assertLastLineNotEquals(Logger $logr) { $this->assertNotEquals($logr->getLastLogLine(), $this->getLastLine($logr->getLogFilePath())); } private function getLastLine($filename) { $size = filesize($filename); $fp = fopen($filename, 'r'); $pos = -2; // start from second to last char $t = ' '; while ($t != "\n") { fseek($fp, $pos, SEEK_END); $t = fgetc($fp); $pos = $pos - 1; if ($size + $pos < -1) { rewind($fp); break; } } $t = fgets($fp); fclose($fp); return trim($t); } public function tearDown() { #@unlink($this->logger->getLogFilePath()); #@unlink($this->errLogger->getLogFilePath()); } } php-klogger-1.2.2/tests/logs/000077500000000000000000000000001427104275200160455ustar00rootroot00000000000000php-klogger-1.2.2/tests/logs/.gitkeep000066400000000000000000000000001427104275200174640ustar00rootroot00000000000000