From 1da57156fcd9741ae75e9db333fe99a169c9023f Mon Sep 17 00:00:00 2001 From: Mihail Binev Date: Tue, 24 Sep 2019 09:08:18 +0200 Subject: [PATCH 01/28] Adds the licence --- Processors/Cli.php | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/Processors/Cli.php b/Processors/Cli.php index 094875d..786e9b3 100644 --- a/Processors/Cli.php +++ b/Processors/Cli.php @@ -1,5 +1,15 @@ + * + * Please view the LICENSE distributed with this source code + * for the full copyright and license information. + * + */ + namespace Koded\Logging\Processors; /** @@ -8,34 +18,22 @@ */ class Cli extends Processor { - - /** - * @var string Message format - */ + /** @var string Message format */ protected $format = '> [timestamp][levelname] - message'; - /** - * @var resource STDOUT - */ + /** @var bool */ private $buffer; public function __construct(array $settings) { parent::__construct($settings); - defined('STDOUT') and $this->buffer = STDOUT; + $this->buffer = defined('STDOUT'); } - public function update(array $messages) + protected function parse(array $message): void { if ($this->buffer) { - fflush($this->buffer); - parent::update($messages); + fwrite(STDOUT, strtr($this->format, $message) . PHP_EOL); } } - - protected function parse(array $message) - { - $message['levelname'] = str_pad($message['levelname'], 11, ' ', STR_PAD_BOTH); - fwrite($this->buffer, strtr($this->format, $message) . PHP_EOL); - } } From e69a0bafc9b1e54298fc013b1227be25b58b0c5b Mon Sep 17 00:00:00 2001 From: Mihail Binev Date: Tue, 24 Sep 2019 09:09:27 +0200 Subject: [PATCH 02/28] - adds return type - adds the licence --- Processors/ErrorLog.php | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Processors/ErrorLog.php b/Processors/ErrorLog.php index e6c3ba5..f34d8e0 100644 --- a/Processors/ErrorLog.php +++ b/Processors/ErrorLog.php @@ -1,16 +1,25 @@ + * + * Please view the LICENSE distributed with this source code + * for the full copyright and license information. + * + */ + namespace Koded\Logging\Processors; /** - * ErrorLog sends the log message to PHP's system logger. + * ErrorLog sends the log message to PHP system logger. * */ class ErrorLog extends Processor { - - protected function parse(array $message) + protected function parse(array $message): void { error_log(strtr($this->format, $message), 0); } -} \ No newline at end of file +} From 5408a5895c4e0c8ded1c20c85ead50ef9568e392 Mon Sep 17 00:00:00 2001 From: Mihail Binev Date: Tue, 24 Sep 2019 09:12:19 +0200 Subject: [PATCH 03/28] - fixes the filename creation - moved initialization in the constructor - adds method return type - exception class refactor - adds the licence --- Processors/File.php | 96 +++++++++++++++++++++++++-------------------- 1 file changed, 53 insertions(+), 43 deletions(-) diff --git a/Processors/File.php b/Processors/File.php index 948947f..935c8e1 100644 --- a/Processors/File.php +++ b/Processors/File.php @@ -1,7 +1,18 @@ + * + * Please view the LICENSE distributed with this source code + * for the full copyright and license information. + * + */ + namespace Koded\Logging\Processors; +use Exception; use Koded\Exceptions\KodedException; /** @@ -13,23 +24,15 @@ * The directory for the log files. Must exist and be writable by the PHP. * * NOTE: the log filename is calculated from of the current - * YEAR/MONTH/DATE and appended to this directory path + * YEAR/MONTH/DATE and appended to this directory path (ex: /path/to/logs/2000/01/01.log) * * - extension (string), default: .log * The log file extension. - * */ class File extends Processor { - - const E_DIRECTORY_DOES_NOT_EXIST = 1; - const E_DIRECTORY_NOT_WRITABLE = 2; - const E_DIRECTORY_NOT_CREATED = 3; - - /** - * @var string The log filename. - */ - protected $filename = ''; + private $dir = ''; + private $ext = ''; /** * {@inheritdoc} @@ -37,53 +40,60 @@ class File extends Processor public function __construct(array $settings) { parent::__construct($settings); - $this->initialize($settings); - } - /** - * Prepares the directory and the log filename. - * - * @param array $settings - * - * @throws FileProcessorException - */ - protected function initialize(array $settings) - { umask(umask() | 0002); + $this->ext = (string)($settings['extension'] ?? '.log'); + $this->dir = rtrim((string)$settings['dir'], '/') . '/'; - $cwd = pathinfo($_SERVER['SCRIPT_FILENAME'] ?? '/tmp', PATHINFO_DIRNAME); - $dir = rtrim($settings['dir'] ?? $cwd . DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; - - if (!is_dir($dir)) { - throw new FileProcessorException(self::E_DIRECTORY_DOES_NOT_EXIST, [':dir' => $dir]); + if (false === is_dir($this->dir)) { + throw FileProcessorException::directoryDoesNotExist($this->dir); } - if (!is_writable($dir)) { - throw new FileProcessorException(self::E_DIRECTORY_NOT_WRITABLE, [':dir' => $dir]); + if (false === is_writable($this->dir)) { + throw FileProcessorException::directoryIsNotWritable($this->dir); } + } - $dir = sprintf('%s%s%s', $dir, date('Y/m'), DIRECTORY_SEPARATOR); + protected function parse(array $message): void + { + try { + // The filename should be calculated at the moment of writing + $dir = $this->dir . date('Y/m'); + is_dir($dir) || mkdir($dir, 0775, true); + + file_put_contents( + $dir . '/' . date('d') . $this->ext, + strtr($this->format, $message) . PHP_EOL, + FILE_APPEND + ); - if (!is_dir($dir) && false === mkdir($dir, 0775, true)) { // @codeCoverageIgnoreStart - throw new FileProcessorException(self::E_DIRECTORY_NOT_CREATED, [':dir' => $dir]); + } catch (Exception $e) { + \error_log(__METHOD__, $e->getMessage(), null); // @codeCoverageIgnoreEnd } - - $this->filename = sprintf('%s%s%s', $dir, date('d'), $settings['extension'] ?? '.log'); - } - - protected function parse(array $message) - { - file_put_contents($this->filename, strtr($this->format, $message) . PHP_EOL, FILE_APPEND); } } + class FileProcessorException extends KodedException { + private const + E_DIRECTORY_DOES_NOT_EXIST = 1, + E_DIRECTORY_NOT_WRITABLE = 2; + protected $messages = [ - File::E_DIRECTORY_DOES_NOT_EXIST => 'Log directory ":dir" must exist.', - File::E_DIRECTORY_NOT_WRITABLE => 'Log directory ":dir" must be writable.', - File::E_DIRECTORY_NOT_CREATED => 'Failed to create a log directory ":dir".', + self::E_DIRECTORY_DOES_NOT_EXIST => 'Log directory ":dir" must exist', + self::E_DIRECTORY_NOT_WRITABLE => 'Log directory ":dir" must be writable', ]; -} \ No newline at end of file + + public static function directoryDoesNotExist(string $directory): self + { + return new self(self::E_DIRECTORY_DOES_NOT_EXIST, [':dir' => $directory]); + } + + public static function directoryIsNotWritable(string $directory): self + { + return new self(self::E_DIRECTORY_NOT_WRITABLE, [':dir' => $directory]); + } +} From 657d1eb8576cb60b8e41ce5b8becfe2cfb7d5e7c Mon Sep 17 00:00:00 2001 From: Mihail Binev Date: Tue, 24 Sep 2019 09:12:45 +0200 Subject: [PATCH 04/28] - adds return type - adds the licence --- Processors/Memory.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Processors/Memory.php b/Processors/Memory.php index 2cafbff..4161221 100644 --- a/Processors/Memory.php +++ b/Processors/Memory.php @@ -1,5 +1,15 @@ + * + * Please view the LICENSE distributed with this source code + * for the full copyright and license information. + * + */ + namespace Koded\Logging\Processors; /** @@ -8,8 +18,7 @@ */ class Memory extends Processor { - - protected function parse(array $message) + protected function parse(array $message): void { $this->formatted .= PHP_EOL . strtr($this->format, $message); } From d89404d82d102caf5d4dca89467620882d0203ce Mon Sep 17 00:00:00 2001 From: Mihail Binev Date: Tue, 24 Sep 2019 09:13:24 +0200 Subject: [PATCH 05/28] - adds return type - log message updated - adds the licence --- Processors/Processor.php | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Processors/Processor.php b/Processors/Processor.php index 1b07510..5cb974b 100644 --- a/Processors/Processor.php +++ b/Processors/Processor.php @@ -1,5 +1,15 @@ + * + * Please view the LICENSE distributed with this source code + * for the full copyright and license information. + * + */ + namespace Koded\Logging\Processors; /** @@ -8,7 +18,6 @@ */ abstract class Processor { - /** * @var int Packed integer for all log levels. If not specified, all levels * are included (by default) @@ -18,7 +27,7 @@ abstract class Processor /** * @var string The log message format. */ - protected $format = '[timestamp] levelname: message'; + protected $format = 'timestamp [levelname]: message'; /** * @var string Keeps all formatted log messages in this property. @@ -42,7 +51,7 @@ public function __construct(array $settings) * * @return void */ - public function update(array $messages) + public function update(array $messages): void { foreach ($messages as $message) { if ($message['level'] & $this->levels) { @@ -59,7 +68,7 @@ public function update(array $messages) * * @return void */ - abstract protected function parse(array $message); + abstract protected function parse(array $message): void; /** * Returns all enabled log levels for the processor object. From 190268f0f123b19980c77705235bef3b6c40d4df Mon Sep 17 00:00:00 2001 From: Mihail Binev Date: Tue, 24 Sep 2019 09:16:45 +0200 Subject: [PATCH 06/28] - messages parsing refactor - adds return type - adds the licence --- Processors/Syslog.php | 43 ++++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/Processors/Syslog.php b/Processors/Syslog.php index eaef888..f4682c2 100644 --- a/Processors/Syslog.php +++ b/Processors/Syslog.php @@ -1,7 +1,19 @@ + * + * Please view the LICENSE distributed with this source code + * for the full copyright and license information. + * + */ + namespace Koded\Logging\Processors; +use Koded\Logging\Logger; + /** * System log. * @@ -9,25 +21,26 @@ */ class Syslog extends Processor { - - /** - * The syslog ident string added to each message. - */ - const IDENT = 'KODED'; - protected $format = '[levelname] message'; - public function update(array $messages) + protected function parse(array $message): void { - if (count($messages)) { - openlog(self::IDENT, LOG_PID | LOG_CONS, LOG_USER); - parent::update($messages); + $levels = [ + Logger::DEBUG => LOG_DEBUG, + Logger::INFO => LOG_INFO, + Logger::NOTICE => LOG_NOTICE, + Logger::WARNING => LOG_WARNING, + Logger::ERROR => LOG_ERR, + Logger::CRITICAL => LOG_CRIT, + Logger::ALERT => LOG_ALERT, + Logger::EMERGENCY => LOG_EMERG + ]; + + try { + openlog(null, LOG_CONS, LOG_USER); + syslog($levels[$message['level']] ?? LOG_DEBUG, strtr($this->format, $message)); + } finally { closelog(); } } - - protected function parse(array $message) - { - syslog($message['level'], strtr($this->format, $message)); - } } From d8f1f6f118b13d3f330f7db6f77ed3c8b5eb067e Mon Sep 17 00:00:00 2001 From: Mihail Binev Date: Tue, 24 Sep 2019 09:17:35 +0200 Subject: [PATCH 07/28] Added test for `deferred` config directive --- Tests/LogDeferredTest.php | 68 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 Tests/LogDeferredTest.php diff --git a/Tests/LogDeferredTest.php b/Tests/LogDeferredTest.php new file mode 100644 index 0000000..c42488b --- /dev/null +++ b/Tests/LogDeferredTest.php @@ -0,0 +1,68 @@ +SUT->alert('Hello, {you}', ['you' => 'awesome person']); + $message = $this->getMessages()[0]; + + $this->assertSame(Log::ALERT, $message['level']); + $this->assertSame('ALERT', $message['levelname']); + $this->assertSame('Hello, awesome person', $message['message']); + $this->assertSame('string', gettype($message['timestamp'])); + } + + public function test_unsupported_level_should_pass_to_default_level() + { + $this->SUT->log('', ''); + $message = $this->getMessages()[0]; + + $this->assertSame(-1, $message['level']); + $this->assertSame('LOG', $message['levelname']); + } + + public function test_log_suppression() + { + $processor = new Memory([ + 'levels' => 0 // suppress this logger completely + ]); + + $processor->update([ + [ + 'level' => -1, // this is ignored + 'levelname' => 'DEBUG', + 'message' => 'Hello', + 'timestamp' => 1234567890 + ] + ]); + + $this->assertSame('', $processor->formatted()); + } + + protected function setUp() + { + $this->SUT = new Log([ + 'deferred' => true, + ]); + } + + private function getMessages(): array + { + $reflected = new ReflectionProperty($this->SUT, 'messages'); + $reflected->setAccessible(true); + + return $reflected->getValue($this->SUT); + } +} From 9deca9ef0c84f179fa1ef04c1391908716039fa1 Mon Sep 17 00:00:00 2001 From: Mihail Binev Date: Tue, 24 Sep 2019 09:18:43 +0200 Subject: [PATCH 08/28] Updates the unit tests --- Tests/LogConstructorTest.php | 6 +- Tests/LogTest.php | 64 +++++-------------- Tests/Processors/CliTest.php | 8 ++- .../DefaultProcessorPropertiesTest.php | 5 +- Tests/Processors/ErrorLogTest.php | 10 ++- Tests/Processors/FileTest.php | 62 +++++++----------- Tests/Processors/MemoryTest.php | 9 ++- Tests/Processors/SyslogTest.php | 8 ++- Tests/Processors/VoidedTest.php | 18 ------ 9 files changed, 74 insertions(+), 116 deletions(-) delete mode 100644 Tests/Processors/VoidedTest.php diff --git a/Tests/LogConstructorTest.php b/Tests/LogConstructorTest.php index db08065..2436a49 100644 --- a/Tests/LogConstructorTest.php +++ b/Tests/LogConstructorTest.php @@ -25,9 +25,9 @@ public function test_construction_with_config_array() public function test_construction_without_config() { $log = new Log([]); - $this->assertAttributeSame('d/m/Y H:i:s', 'dateFormat', $log); - $this->assertAttributeSame('UTC', 'timezone', $log); - $log->register(); + $this->assertAttributeSame(false, 'deferred', $log); + $this->assertAttributeSame('d/m/Y H:i:s.u', 'dateFormat', $log); + $this->assertAttributeSame('UTC', 'timezone', $log); } } diff --git a/Tests/LogTest.php b/Tests/LogTest.php index 7c9c6dc..5375372 100644 --- a/Tests/LogTest.php +++ b/Tests/LogTest.php @@ -8,12 +8,20 @@ class LogTest extends TestCase { - /** * @var Log */ private $SUT; + public function test_default_setup() + { + $this->assertAttributeSame(false, 'deferred', $this->SUT); + $this->assertAttributeSame('d/m/Y H:i:s.u', 'dateFormat', $this->SUT); + $this->assertAttributeSame('UTC', 'timezone', $this->SUT); + $this->assertAttributeEmpty('processors', $this->SUT); + $this->assertAttributeEmpty('messages', $this->SUT); + } + public function test_attach_and_detach() { $processor = new Memory([]); @@ -26,40 +34,6 @@ public function test_attach_and_detach() $this->assertAttributeCount(0, 'processors', $this->SUT); } - public function test_messages_stack() - { - $this->assertAttributeCount(0, 'messages', $this->SUT); - $this->SUT->alert('Hello'); - $this->assertAttributeCount(1, 'messages', $this->SUT); - } - - public function test_message_block() - { - $this->SUT->alert('Hello {you}', ['you' => 'the most awesome person in the universe']); - $message = $this->getMessages()[0]; - - $this->assertSame(Log::ALERT, $message['level']); - $this->assertSame('ALERT', $message['levelname']); - $this->assertSame('Hello the most awesome person in the universe', $message['message']); - $this->assertInternalType('string', $message['timestamp']); - } - - public function test_unsupported_level_should_pass_to_default_level() - { - $this->SUT->log('', ''); - $message = $this->getMessages()[0]; - - $this->assertSame(-1, $message['level']); - $this->assertSame('LOG', $message['levelname']); - } - - public function test_exception_attribute() - { - $processor = new Memory([]); - $this->SUT->exception(new Exception('The message', 1), $processor); - $this->assertAttributeContains('ALERT: The message', 'formatted', $processor); - } - public function test_log_suppression() { $processor = new Memory([ @@ -69,7 +43,7 @@ public function test_log_suppression() $processor->update([ [ 'level' => -1, // this is ignored - 'levelname' => 'TEST', + 'levelname' => 'DEBUG', 'message' => 'Hello', 'timestamp' => 1234567890 ] @@ -78,25 +52,17 @@ public function test_log_suppression() $this->assertSame('', $processor->formatted()); } - public function test_register() + public function test_exception() { - $mock = $this - ->getMockBuilder(Log::class) - ->disableOriginalConstructor() - ->getMock(); + $processor = new Memory([]); + $this->SUT->exception(new Exception('The message', 1), $processor); - $this->assertNull($mock->register()); + $this->assertAttributeContains('[CRITICAL]', 'formatted', $processor); + $this->assertAttributeContains('The message', 'formatted', $processor); } protected function setUp() { $this->SUT = new Log([]); } - - private function getMessages(): array - { - $reflected = new \ReflectionProperty($this->SUT, 'messages'); - $reflected->setAccessible(true); - return $reflected->getValue($this->SUT); - } } diff --git a/Tests/Processors/CliTest.php b/Tests/Processors/CliTest.php index 6ed6fd3..4a19b5a 100644 --- a/Tests/Processors/CliTest.php +++ b/Tests/Processors/CliTest.php @@ -16,8 +16,14 @@ public function test_formatting() [ 'level' => Logger::DEBUG, 'levelname' => 'TEST', - 'message' => 'Hello', + 'message' => 'Cli 1', 'timestamp' => 1234567890 + ], + [ + 'level' => Logger::DEBUG, + 'levelname' => 'TEST', + 'message' => 'Cli 2', + 'timestamp' => 1234567891 ] ]); diff --git a/Tests/Processors/DefaultProcessorPropertiesTest.php b/Tests/Processors/DefaultProcessorPropertiesTest.php index 9487e0d..d717b19 100644 --- a/Tests/Processors/DefaultProcessorPropertiesTest.php +++ b/Tests/Processors/DefaultProcessorPropertiesTest.php @@ -10,9 +10,10 @@ class DefaultProcessorPropertiesTest extends TestCase public function test_defaults() { - $processor = new Voided([]); + $processor = new Memory([]); + $this->assertAttributeSame(-1, 'levels', $processor); - $this->assertAttributeSame('[timestamp] levelname: message', 'format', $processor); + $this->assertAttributeSame('timestamp [levelname]: message', 'format', $processor); $this->assertAttributeSame('', 'formatted', $processor); } diff --git a/Tests/Processors/ErrorLogTest.php b/Tests/Processors/ErrorLogTest.php index cfd0d08..6030cab 100644 --- a/Tests/Processors/ErrorLogTest.php +++ b/Tests/Processors/ErrorLogTest.php @@ -15,9 +15,15 @@ public function test_formatting() $processor->update([ [ 'level' => Logger::DEBUG, - 'levelname' => 'TEST', - 'message' => 'Hello', + 'levelname' => 'INFO', + 'message' => 'ErrorLog 1', 'timestamp' => 1234567890 + ], + [ + 'level' => Logger::DEBUG, + 'levelname' => 'WARN', + 'message' => 'ErrorLog 2', + 'timestamp' => 1234567891 ] ]); diff --git a/Tests/Processors/FileTest.php b/Tests/Processors/FileTest.php index 284582b..9a78d12 100644 --- a/Tests/Processors/FileTest.php +++ b/Tests/Processors/FileTest.php @@ -2,9 +2,7 @@ namespace Koded\Logging\Processors; -use org\bovigo\vfs\vfsStream; -use org\bovigo\vfs\vfsStreamDirectory; -use org\bovigo\vfs\vfsStreamWrapper; +use org\bovigo\vfs\{vfsStream, vfsStreamDirectory, vfsStreamWrapper}; use PHPUnit\Framework\TestCase; class FileTest extends TestCase @@ -15,44 +13,46 @@ class FileTest extends TestCase */ private $dir; - /** - * @dataProvider dataMessage - * @param $message - */ - public function test_formatting($message) + public function test_update() { $subdirectory = date('Y/m'); $file = date('d') . '.log'; $processor = new File(['dir' => $this->dir->url()]); - $processor->update([$message]); + $processor->update([ + [ + 'level' => -1, + 'levelname' => 'DEBUG', + 'message' => 'Test 1', + 'timestamp' => 1234567890 + ], + [ + 'level' => -1, + 'levelname' => 'DEBUG', + 'message' => 'Test 2', + 'timestamp' => 1234567891 + ] + ]); + $this->assertSame('', $processor->formatted()); $this->assertTrue($this->dir->hasChild($subdirectory)); $content = $this->dir->getChild($subdirectory . DIRECTORY_SEPARATOR . $file)->getContent(); - $this->assertContains('[1234567890] DEBUG: Hello', $content); + $this->assertContains("1234567891 [DEBUG]: Test 2\n", $content); } - /** - * @dataProvider dataMessage - * @param $message - */ - public function test_when_directory_does_not_exist($message) + public function test_when_directory_does_not_exist() { - $dir = $this->dir->url() . '/nonexistent'; + $dir = $this->dir->url() . '/nonexistent/'; $this->expectException(FileProcessorException::class); - $this->expectExceptionMessage('Log directory "' . $dir . '/" must exist'); + $this->expectExceptionMessage('Log directory "' . $dir . '" must exist'); $processor = new File(['dir' => $dir]); - $processor->update([$message]); + $processor->update([]); } - /** - * @dataProvider dataMessage - * @param $message - */ - public function test_when_directory_is_not_writable($message) + public function test_when_directory_is_not_writable() { $dir = $this->dir->url(); @@ -61,21 +61,7 @@ public function test_when_directory_is_not_writable($message) vfsStreamWrapper::getRoot()->chmod(0400); $processor = new File(['dir' => $dir]); - $processor->update([$message]); - } - - public function dataMessage() - { - return [ - [ - [ - 'level' => -1, - 'levelname' => 'DEBUG', - 'message' => 'Hello', - 'timestamp' => 1234567890 - ] - ] - ]; + $processor->update([]); } protected function setUp() diff --git a/Tests/Processors/MemoryTest.php b/Tests/Processors/MemoryTest.php index 22deb91..16e9ee3 100644 --- a/Tests/Processors/MemoryTest.php +++ b/Tests/Processors/MemoryTest.php @@ -2,6 +2,7 @@ namespace Koded\Logging\Processors; +use function Koded\Stdlib\dump; use PHPUnit\Framework\TestCase; class MemoryTest extends TestCase @@ -17,9 +18,15 @@ public function test_formatting() 'levelname' => 'TEST', 'message' => 'Hello', 'timestamp' => 1234567890 + ], + [ + 'level' => -1, + 'levelname' => 'TEST', + 'message' => 'World', + 'timestamp' => 1234567891 ] ]); - $this->assertContains('[1234567890] TEST: Hello', $processor->formatted()); + $this->assertContains("1234567890 [TEST]: Hello\n1234567891 [TEST]: World", $processor->formatted()); } } diff --git a/Tests/Processors/SyslogTest.php b/Tests/Processors/SyslogTest.php index b743619..38e2477 100644 --- a/Tests/Processors/SyslogTest.php +++ b/Tests/Processors/SyslogTest.php @@ -15,9 +15,13 @@ public function test_formatting() $processor->update([ [ 'level' => Logger::DEBUG, - 'levelname' => 'TEST', - 'message' => 'Hello', + 'message' => 'Syslog 1', 'timestamp' => 1234567890 + ], + [ + 'level' => Logger::DEBUG, + 'message' => 'Syslog 2', + 'timestamp' => 1234567891 ] ]); diff --git a/Tests/Processors/VoidedTest.php b/Tests/Processors/VoidedTest.php deleted file mode 100644 index 4c736c3..0000000 --- a/Tests/Processors/VoidedTest.php +++ /dev/null @@ -1,18 +0,0 @@ -assertSame(0, $processor->levels()); - $this->assertSame('', $processor->formatted()); - $this->assertNull($processor->update([])); - } -} From c037a771d126d7e40bf0e53f01a5fc52cefc16e9 Mon Sep 17 00:00:00 2001 From: Mihail Binev Date: Tue, 24 Sep 2019 09:19:29 +0200 Subject: [PATCH 09/28] Version bump --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 8428158..359a5b9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.1.2 \ No newline at end of file +2.0.0 \ No newline at end of file From ca059cc0ba1b3f502d716455932b5be90bcc7de2 Mon Sep 17 00:00:00 2001 From: Mihail Binev Date: Tue, 24 Sep 2019 09:20:11 +0200 Subject: [PATCH 10/28] Updates --- phpunit.xml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/phpunit.xml b/phpunit.xml index aed039f..73a750a 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -1,16 +1,16 @@ - Tests + Tests - ./ @@ -22,11 +22,6 @@ - - - - - \ No newline at end of file From f045ac207c5a96ef2cee5271263c43e9082cd6c1 Mon Sep 17 00:00:00 2001 From: Mihail Binev Date: Tue, 24 Sep 2019 09:20:31 +0200 Subject: [PATCH 11/28] Adds Scrutinizer --- .scrutinizer.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .scrutinizer.yml diff --git a/.scrutinizer.yml b/.scrutinizer.yml new file mode 100644 index 0000000..b44a99f --- /dev/null +++ b/.scrutinizer.yml @@ -0,0 +1,22 @@ +build: + nodes: + analysis: + tests: + stop_on_failure: true + override: + - php-scrutinizer-run + environment: + php: + version: '7.2' + dependencies: + override: + - composer install --no-interaction --prefer-source + +filter: + excluded_paths: + - 'Tests/' + - 'vendor/' + +tools: + php_analyzer: true + external_code_coverage: true From f39ce0b1b69d43ea10df9be64d851548fc3eaad5 Mon Sep 17 00:00:00 2001 From: Mihail Binev Date: Tue, 24 Sep 2019 09:20:57 +0200 Subject: [PATCH 12/28] Updates to use Scrutinizer --- .travis.yml | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 436cd9e..c4d3cc3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,30 @@ language: php -sudo: false + +php: + - 7.1 + - 7.2 + - 7.3 + - 7.4snapshot + matrix: - include: - - php: 7.1 fast_finish: true -before_script: - - mkdir -p build/logs - - composer update -o --prefer-dist + allow_failures: + - php: 7.4snapshot + +install: + - travis_retry composer update -o --no-interaction --prefer-source + script: - - phpunit + - vendor/bin/phpunit --coverage-clover build/coverage/clover.xml + after_success: - - travis_retry php vendor/bin/coveralls + - travis_retry vendor/bin/ocular code-coverage:upload --format=php-clover build/coverage/clover.xml + +sudo: false + +notifications: + email: false + +cache: + directories: + - $HOME/.composer/cache From 92cdb320c116bdc42eb2587d977e157898b063df Mon Sep 17 00:00:00 2001 From: Mihail Binev Date: Tue, 24 Sep 2019 09:47:19 +0200 Subject: [PATCH 13/28] License updated --- LICENSE | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/LICENSE b/LICENSE index 01a30f3..8d306d8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,22 +1,21 @@ BSD 3-Clause License -Koded - Logging -Copyright (c) 2018, Mihail Binev +Copyright (c) 2019, Mihail Binev All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. -* Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE From 8f9052743a0c327af364097c7a974ddcb7664a72 Mon Sep 17 00:00:00 2001 From: Mihail Binev Date: Tue, 24 Sep 2019 19:10:22 +0200 Subject: [PATCH 14/28] README updated --- README.md | 93 ++++++++++++++++++++++++------------------------------- 1 file changed, 41 insertions(+), 52 deletions(-) diff --git a/README.md b/README.md index bf8ea98..44edc53 100644 --- a/README.md +++ b/README.md @@ -5,26 +5,29 @@ A simple message logging library that implements [PSR-3][psr-3] with several log processors. It supports multiple log writers that can be set separately and process messages based on the log level. -[![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%207.0-8892BF.svg)](https://php.net/) -[![Build Status](https://travis-ci.org/kodedphp/logging.svg?branch=master)](https://travis-ci.org/kodedphp/logging) -[![Coverage Status](https://coveralls.io/repos/github/kodedphp/logging/badge.svg?branch=master)](https://coveralls.io/github/kodedphp/logging?branch=master) [![Latest Stable Version](https://img.shields.io/packagist/v/koded/logging.svg)](https://packagist.org/packages/koded/logging) +[![Build Status](https://travis-ci.org/kodedphp/logging.svg?branch=master)](https://travis-ci.org/kodedphp/logging) +[![Codacy Badge](https://api.codacy.com/project/badge/Coverage/81ffd9cf1725485d8f6fb836617d002d)](https://www.codacy.com/app/kodeart/logging) +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/81ffd9cf1725485d8f6fb836617d002d)](https://www.codacy.com/app/kodeart/logging) +[![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%207.1-8892BF.svg)](https://php.net/) [![Software license](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](LICENSE) Installation ------------ -Use [composer][composer] because it's awesome. Run `composer require koded/logging`, -or set it manually +Use [composer][composer] and run +> `composer require koded/logging` +or add it manually in your current `composer.json` ```json { "require": { - "koded/logging": "~1" + "koded/logging": "~2" } } ``` + Usage ----- @@ -32,91 +35,77 @@ Usage false, 'loggers' => [ - ['class' => ErrorLog::class, 'levels' => Log::ERROR], + ['class' => Cli::class, 'levels' => Log::ERROR], ['class' => File::class, 'levels' => Log::INFO] ] ]; $log = new Log($settings); -// This message is processed by ErrorLog and File +// This message is processed by Cli and File $log->alert('The message with {variable}', ['variable' => 'useful info']); -// This message won't be processed by ErrorLog because the level is below ERROR -// but *File* will handle it +// This message won't be processed by Cli +// because it's level is below ERROR, +// but File will handle it $log->warning("You don't see anything"); - ``` Configuration ------------- -There are two ways of throwing the log messages -- automatically at the shutdown phase by using the `Log::process()` method somewhere in your bootstrap -(at the very beginning of your project). -**You should do this only once**. +@TODO Fix the text for `register()` and `deferred` flag - ```php - register(); - - // or - register_shutdown_function([$logger, 'process']); - - // or - register_shutdown_function([new Log($settings), 'process']); - ``` +| Param | Type | Required | Default | Description | +|-----------:|:------:|:--------:|:----------------|:------------| +| loggers | array | yes | (empty) | An array of log processors. Every processor is defined in array with it's own configuration parameters. See [processor directives](processor-default-directives) | +| dateformat | string | no | "d/m/Y H:i:s.u" | The date format for the log message. Microseconds are prepended by default | +| timezone | string | no | "UTC" | The desired timezone for the log message | +| deferred | bool | no | false | A flag to set the Log instance how to dump messages. Set to TRUE if you want to process all accumulated messages at shutdown time. Otherwise, the default behavior is to process the message immediately after the LoggerInterface method is called | -- calling directly `$logger->process()` if you want immediate log messages. -**This call will dump all accumulated messages and clear the stack** -(after that the logger will be empty) +### Processor default directives -### Log and Processor default directives - -Every log processor has it's own set of configuration directives. +Every log processor has it's own set of configuration directives. The table shows log parameters in the classes. -| Param | Type | Required | Default | Description -|:-----------|:--------|:--------:|:------------|:----------- -| class | string | yes | | The name of the log processor class -| levels | integer | no | -1 | Packed integer for bitwise comparison. See the constants in Logger class -| dateformat | string | no | d/m/Y H:i:s | The datetime format for the log message -| timezone | string | no | UTC | The desired timezone for the datetime log message +| Param | Type | Required | Default | Description | +|:-----------|:--------|:--------:|:--------------|:------------| +| class | string | yes | | The name of the log processor class | +| levels | integer | no | -1 | Packed integer for bitwise comparison. See the constants in Logger class | ### Levels example -The messages are filtered with bitwise operator (as packed integer). Every processor will filter out the messages as -defined in it's **levels** directive. +The messages are filtered with bitwise operator against the `levels` value. +Every processor will filter out the messages as defined in it's **levels** directive. -For instance, to log only WARNING, INFO and ERROR messages set levels to +For instance, to log only WARNING, INFO and ERROR messages, set levels to ```php - Log::WARN | Log::INFO | Log::ERROR, ...] + Log::WARN | Log::INFO | Log::ERROR, ...]], ``` Tips: - every processor is configured separately -- if you want to process all log levels, skip the `levels` value +- if you want to process all log levels, skip the `levels` value or set it to -1 (by default) - if you want to suppress a specific processor, set it's level to 0 Processors ---------- -| Class name | Description -|-----------:|:----------- -| ErrorLog | uses the [error_log()][error-log] function to send the message to PHP's logger -| SysLog | will open the system logger and send messages using the [syslog()][syslog] function -| Cli | for CLI applications, it can write the messages in the console -| Memory | will store all messages in an array. Useful for unit tests if the logger is involved -| File | saves the messages on a disk. It's a slow one and should be avoided -| Voided | is here for no particular reason and purpose. It's the fastest one tho :) +| Class name | Description | +|-----------:|:-------------------------------------------------------------------------------------| +| ErrorLog | uses the [error_log()][error-log] function to send the message to PHP's logger | +| SysLog | will open the system logger and send messages using the [syslog()][syslog] function | +| Cli | write the messages in the console (with STDOUT) | +| Memory | will store all messages in an array. Useful for unit tests if the logger is involved | +| File | saves the messages on a disk. **It's a slow one and should be avoided** | License From 358037cbcf7ab2f6795dfb955c30c2213ea5abda Mon Sep 17 00:00:00 2001 From: Mihail Binev Date: Tue, 24 Sep 2019 19:11:21 +0200 Subject: [PATCH 15/28] Updates --- .gitattributes | 11 ++++++++--- .gitignore | 13 +++++++------ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/.gitattributes b/.gitattributes index 7e1e42c..93a3744 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,10 @@ *.php diff=php -/Tests export-ignore -/phpunit.* export-ignore -/*.yml export-ignore \ No newline at end of file +/build export-ignore +/Tests export-ignore +/vendor export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore +/composer.lock export-ignore +/*.xml export-ignore +/*.yml export-ignore diff --git a/.gitignore b/.gitignore index c9e01af..8d74a4b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,9 @@ -build -vendor -.DS_Store -.idea +build/ +logs/ +vendor/ +.DS_Store/ +.idea/ composer.lock -phpunit.phar +*.phar *.yml -!.travis.yml \ No newline at end of file +!.scrutinizer.yml \ No newline at end of file From c67a68af2f156a39bc3085977da30fcf71475fec Mon Sep 17 00:00:00 2001 From: Mihail Binev Date: Tue, 24 Sep 2019 19:13:01 +0200 Subject: [PATCH 16/28] Updated dependencies --- composer.json | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index 42cf1a0..12872ab 100644 --- a/composer.json +++ b/composer.json @@ -1,9 +1,16 @@ { "name": "koded/logging", + "description": "A simple standalone logging facility with several log processors", "type": "library", "license": "BSD-3-Clause", - "description": "A simple standalone logging facility with several log processors", - "keywords": ["logging", "log", "logger", "logging-library", "psr-3", "php7"], + "homepage": "https://github.com/kodedphp/logging", + "keywords": [ + "logging", + "log", + "logger", + "logging-library", + "psr-3" + ], "authors": [ { "name": "Mihail Binev", @@ -12,8 +19,8 @@ ], "require": { "php": "^7.1.4", - "psr/log": "^1.0.1", - "koded/stdlib": "~3" + "psr/log": "~1", + "koded/stdlib": "~4" }, "autoload": { "psr-4": { @@ -24,8 +31,13 @@ ] }, "require-dev": { - "phpunit/phpunit": "*", - "mikey179/vfsStream": "~1", - "satooshi/php-coveralls": "~1" + "phpunit/phpunit": "~7", + "mikey179/vfsstream": "~1", + "scrutinizer/ocular": "~1" + }, + "autoload-dev": { + "psr-4": { + "Koded\\Logging\\": "Tests/" + } } -} +} \ No newline at end of file From ed377c595dca9e2dd40b34f38dbc87c4f659c555 Mon Sep 17 00:00:00 2001 From: Mihail Binev Date: Tue, 24 Sep 2019 19:16:17 +0200 Subject: [PATCH 17/28] - added methods to the interface = removed `register()` method - add return types to methods - adds the licence --- Logger.php | 62 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/Logger.php b/Logger.php index fadeda4..2c58485 100644 --- a/Logger.php +++ b/Logger.php @@ -1,5 +1,15 @@ + * + * Please view the LICENSE distributed with this source code + * for the full copyright and license information. + * + */ + namespace Koded\Logging; use Koded\Logging\Processors\Processor; @@ -12,18 +22,38 @@ */ interface Logger extends LoggerInterface { - - /** + /* + * * Log levels + * */ + const EMERGENCY = 1; - const ALERT = 2; - const CRITICAL = 4; - const ERROR = 8; - const WARNING = 16; - const NOTICE = 32; - const INFO = 64; - const DEBUG = 128; + const ALERT = 2; + const CRITICAL = 4; + const ERROR = 8; + const WARNING = 16; + const NOTICE = 32; + const INFO = 64; + const DEBUG = 128; + + /** + * Add a log processor in the stack. + * + * @param Processor $processor Logger processor instance + * + * @return Logger + */ + public function attach(Processor $processor): Logger; + + /** + * Detach a log processor from registered processors. + * + * @param Processor $processor The log processor to detach from the stack. + * + * @return Logger + */ + public function detach(Processor $processor): Logger; /** * @param Throwable $e @@ -31,27 +61,17 @@ interface Logger extends LoggerInterface * * @return void */ - public function exception(Throwable $e, Processor $processor = null); + public function exception(Throwable $e, Processor $processor = null): void; /** * Run all log processors to save the accumulated messages * and clean the message stack (after the method is called, * the message stack is emptied). * - * This method should be registered somewhere in the bootstrap phase with - * $log::register() method. - * * It may be called manually as well to instantly dump all messages * (i.e. for Exception handling). * * @return void */ - public function process(); - - /** - * Registers the process method at the PHP's shutdown phase. - * - * @return void - */ - public function register(); + public function process(): void; } From 1e0dfb472db847a3ac8dcbbd1e1a2b671cad368f Mon Sep 17 00:00:00 2001 From: Mihail Binev Date: Tue, 24 Sep 2019 19:42:54 +0200 Subject: [PATCH 18/28] - uses `deferred` config directive for messages processing control - removes `register()` method - uses Cli processor for exception() method - adds the licence --- Log.php | 160 ++++++++++++++++++++++++++------------------------------ 1 file changed, 73 insertions(+), 87 deletions(-) diff --git a/Log.php b/Log.php index 8cb965d..84ab8ad 100644 --- a/Log.php +++ b/Log.php @@ -1,10 +1,18 @@ + * + * Please view the LICENSE distributed with this source code + * for the full copyright and license information. + * + */ + namespace Koded\Logging; -use DateTime; -use DateTimeZone; -use Koded\Logging\Processors\{ ErrorLog, Processor }; +use Koded\Logging\Processors\{Cli, Processor}; use Psr\Log\LoggerTrait; use Throwable; @@ -16,24 +24,30 @@ * * CONFIGURATION PARAMETERS (Log class) * + * - deferred (bool) [optional], default: false + * A flag to set the Log instance how to dump messages. + * Set to TRUE if you want to process all accumulated messages + * at shutdown time. Otherwise, the default behavior is to process + * the message immediately after the LoggerInterface method is called. + * * - loggers (array) * An array of log processors. Every processor is defined in array with it's own * configuration parameters, but ALL must have the following: * - * - class (string) [required] - * The name of the log processor class. - * Can create multiple same instances with different config - * parameters. + * - class (string) [required] + * The name of the log processor class. + * Can create multiple same instances with different config + * parameters. * - * - levels (integer) [optional], default: -1 (for all levels) - * Packed integer for bitwise comparison. See the constants in this - * class. + * - levels (integer) [optional], default: -1 (for all levels) + * Packed integer for bitwise comparison. See the constants in this + * class. * - * Example: Log::INFO | Log::ERROR | Log::ALERT - * Processor with these log levels will store only - * info, error and warning type messages. + * Example: Log::INFO | Log::ERROR | Log::ALERT + * Processor with these log levels will store only + * info, error and warning type messages. * - * - dateformat (string) [optional], default: d/m/Y H:i:s + * - dateformat (string) [optional], default: d/m/Y H:i:s.u * The date format for the log message. * * - timezone (string) [optional], default: UTC @@ -41,14 +55,18 @@ * * * CONFIGURATION PARAMETERS (Processor class) - * Every processor has it's own specific parameters (with the above directives). + * Every processor may have it's own specific parameters. * */ class Log implements Logger { - use LoggerTrait; + /** + * @var bool Flag to control the messages processing + */ + private $deferred = false; + /** * @var string The date format for the message. */ @@ -76,26 +94,28 @@ class Log implements Logger */ public function __construct(array $settings) { - $this->dateFormat = $settings['dateformat'] ?? 'd/m/Y H:i:s'; - $this->timezone = $settings['timezone'] ?? $this->timezone; + $this->deferred = (bool)($settings['deferred'] ?? false); + $this->dateFormat = (string)($settings['dateformat'] ?? 'd/m/Y H:i:s.u'); + $this->timezone = (string)($settings['timezone'] ?? $this->timezone); - // Build and attach all requested processors - foreach ($settings['loggers'] ?? [] as $processor) { + foreach ((array)($settings['loggers'] ?? []) as $processor) { $this->attach(new $processor['class']($processor)); } + + if ($this->deferred) { + register_shutdown_function([$this, 'process']); + } } - /** - * {@inheritdoc} - */ - public function register() + public function attach(Processor $processor): Logger { - register_shutdown_function([$this, 'process']); + if (0 !== $processor->levels()) { + $this->processors[spl_object_hash($processor)] = $processor; + } + + return $this; } - /** - * {@inheritdoc} - */ public function log($level, $message, array $context = []) { try { @@ -106,38 +126,35 @@ public function log($level, $message, array $context = []) $level = -1; } - $microtime = microtime(true); - $this->messages[] = [ - 'level' => $level, + 'level' => $level, 'levelname' => $levelname, - 'message' => $this->formatMessage($message, $context), - 'timestamp' => ( - (new DateTime(null, new DateTimeZone('UTC'))) - ->setTimestamp($microtime) - ->format($this->dateFormat) - ) . substr(sprintf('%.6F', $microtime), -7) + 'message' => $this->formatMessage($message, $context), + 'timestamp' => date_create_immutable('now', timezone_open($this->timezone))->format($this->dateFormat), ]; + + $this->deferred || $this->process(); } /** - * {@inheritdoc} + * Parses the message as in the interface specification. + * + * @param string|object $message A string or object that implements __toString + * @param array $params [optional] Arbitrary data with key-value pairs replacements + * + * @return string */ - public function exception(Throwable $e, Processor $processor = null) + private function formatMessage($message, array $params = []): string { - $syslog = $processor ?? new ErrorLog([]); - $message = $e->getMessage() . PHP_EOL . ' -- [Trace]: ' . $e->getTraceAsString(); + $replacements = []; + foreach ($params as $k => $v) { + $replacements['{' . $k . '}'] = $v; + } - $this->attach($syslog); - $this->alert($message); - $this->process(); - $this->detach($syslog); + return strtr((string)$message, $replacements); } - /** - * {@inheritdoc} - */ - public function process() + public function process(): void { foreach ($this->processors as $processor) { $processor->update($this->messages); @@ -146,51 +163,20 @@ public function process() $this->messages = []; } - /** - * Add a log processor in the stack. - * - * @param Processor $processor Logger processor instance - * - * @return Log - */ - public function attach(Processor $processor): Log + public function exception(Throwable $e, Processor $processor = null): void { - if (0 !== $processor->levels()) { - $this->processors[spl_object_hash($processor)] = $processor; - } + $logger = $processor ?? new Cli([]); + $message = $e->getMessage() . PHP_EOL . ' -- [Trace]: ' . $e->getTraceAsString(); - return $this; + $this->attach($logger)->critical($message); + $this->process(); + $this->detach($logger); } - /** - * Detach a log processor from registered processors. - * - * @param Processor $processor The log processor to detach from the stack. - * - * @return Log - */ - public function detach(Processor $processor): Log + public function detach(Processor $processor): Logger { unset($this->processors[spl_object_hash($processor)]); return $this; } - - /** - * Parses the message as in the interface specification. - * - * @param string|object $message A string or object that implements __toString - * @param array $context [optional] Arbitrary data with key-value pairs replacements - * - * @return string - */ - private function formatMessage($message, array $context = []): string - { - $replacements = []; - foreach ($context as $k => $v) { - $replacements['{' . $k . '}'] = $v; - } - - return strtr((string)$message, $replacements); - } } From 2519edf82bbfc9625e16f57a23d11e0d96fbd620 Mon Sep 17 00:00:00 2001 From: Mihail Binev Date: Tue, 10 Dec 2019 06:20:40 +0100 Subject: [PATCH 19/28] - added phpbench tests --- Tests/PhpBench/AbstractBench.php | 46 ++++++++++++++++++++++++++++++++ Tests/PhpBench/CliBench.php | 34 +++++++++++++++++++++++ Tests/PhpBench/ErrorLogBench.php | 34 +++++++++++++++++++++++ Tests/PhpBench/FileBench.php | 34 +++++++++++++++++++++++ Tests/PhpBench/MemoryBench.php | 34 +++++++++++++++++++++++ Tests/PhpBench/README.md | 3 +++ Tests/PhpBench/SyslogBench.php | 34 +++++++++++++++++++++++ phpbench.json.dist | 13 +++++++++ 8 files changed, 232 insertions(+) create mode 100644 Tests/PhpBench/AbstractBench.php create mode 100644 Tests/PhpBench/CliBench.php create mode 100644 Tests/PhpBench/ErrorLogBench.php create mode 100644 Tests/PhpBench/FileBench.php create mode 100644 Tests/PhpBench/MemoryBench.php create mode 100644 Tests/PhpBench/README.md create mode 100644 Tests/PhpBench/SyslogBench.php create mode 100644 phpbench.json.dist diff --git a/Tests/PhpBench/AbstractBench.php b/Tests/PhpBench/AbstractBench.php new file mode 100644 index 0000000..84479e2 --- /dev/null +++ b/Tests/PhpBench/AbstractBench.php @@ -0,0 +1,46 @@ +log = new Log($this->getConfig()); + } + + public function tearDown(): void + { + $this->log = null; + } + + abstract protected function getConfig(): array; + + protected function message(): array + { + $messages = [ + ['One morning, when {1} woke from troubled dreams, he found himself {2} in his bed into a {3}.', ['1' => 'Gregor Samsa', '2' => 'transformed', '3' => 'horrible vermin']], + ['He lay on his {back}, and if he lifted his head a little he could see his brown belly, slightly {state} by arches into stiff sections.', ['back' => 'armour-like back', 'state' => 'domed and divided']], + ['The bedding was hardly able to cover it and seemed ready to slide off any moment.', ['' => '', '' => '', '' => '']], + ['His many legs, pitifully thin compared with the size of the rest of him, waved about helplessly as he looked.', ['' => '', '' => '', '' => '']], + ['"What\'s happened to {0}?" he thought. It wasn\'t a {1}.', ['me', 'dream']], + ['His room, a proper {room} although a little too small, lay peacefully {position} familiar walls.', ['room' => 'human room', 'position' => 'between its four']], + ['A collection of textile samples lay spread out on the table - {name} was a travelling salesman - and above it there hung a picture that he had recently cut out of an {object} and housed in a nice, gilded frame.', ['name' => 'Samsa', 'object' => 'illustrated magazine']], + ['It showed a {someone} who sat upright, raising a {something} that covered {somewhere} towards the viewer. ', ['someone' => 'lady fitted out with a fur hat and fur boa', 'something' => 'heavy fur muff', 'somewhere' => 'the whole of her lower arm']], + ]; + + return $messages[rand(0, 7)]; + } +} diff --git a/Tests/PhpBench/CliBench.php b/Tests/PhpBench/CliBench.php new file mode 100644 index 0000000..45e2ced --- /dev/null +++ b/Tests/PhpBench/CliBench.php @@ -0,0 +1,34 @@ +log->debug(...$this->message()); + $this->log->info(...$this->message()); + $this->log->notice(...$this->message()); + $this->log->warning(...$this->message()); + $this->log->error(...$this->message()); + $this->log->critical(...$this->message()); + $this->log->alert(...$this->message()); + $this->log->emergency(...$this->message()); + } + + protected function getConfig(): array + { + return [ + 'deferred' => false, + 'loggers' => [ + ['class' => Cli::class], + ] + ]; + } +} diff --git a/Tests/PhpBench/ErrorLogBench.php b/Tests/PhpBench/ErrorLogBench.php new file mode 100644 index 0000000..60e0580 --- /dev/null +++ b/Tests/PhpBench/ErrorLogBench.php @@ -0,0 +1,34 @@ +log->debug(...$this->message()); + $this->log->info(...$this->message()); + $this->log->notice(...$this->message()); + $this->log->warning(...$this->message()); + $this->log->error(...$this->message()); + $this->log->critical(...$this->message()); + $this->log->alert(...$this->message()); + $this->log->emergency(...$this->message()); + } + + protected function getConfig(): array + { + return [ + 'deferred' => false, + 'loggers' => [ + ['class' => ErrorLog::class], + ] + ]; + } +} diff --git a/Tests/PhpBench/FileBench.php b/Tests/PhpBench/FileBench.php new file mode 100644 index 0000000..c760ab3 --- /dev/null +++ b/Tests/PhpBench/FileBench.php @@ -0,0 +1,34 @@ +log->debug(...$this->message()); + $this->log->info(...$this->message()); + $this->log->notice(...$this->message()); + $this->log->warning(...$this->message()); + $this->log->error(...$this->message()); + $this->log->critical(...$this->message()); + $this->log->alert(...$this->message()); + $this->log->emergency(...$this->message()); + } + + protected function getConfig(): array + { + return [ + 'deferred' => false, + 'loggers' => [ + ['class' => File::class, 'dir' => sys_get_temp_dir()], + ] + ]; + } +} diff --git a/Tests/PhpBench/MemoryBench.php b/Tests/PhpBench/MemoryBench.php new file mode 100644 index 0000000..dc8686f --- /dev/null +++ b/Tests/PhpBench/MemoryBench.php @@ -0,0 +1,34 @@ +log->debug(...$this->message()); + $this->log->info(...$this->message()); + $this->log->notice(...$this->message()); + $this->log->warning(...$this->message()); + $this->log->error(...$this->message()); + $this->log->critical(...$this->message()); + $this->log->alert(...$this->message()); + $this->log->emergency(...$this->message()); + } + + protected function getConfig(): array + { + return [ + 'deferred' => false, + 'loggers' => [ + ['class' => Memory::class], + ] + ]; + } +} diff --git a/Tests/PhpBench/README.md b/Tests/PhpBench/README.md new file mode 100644 index 0000000..ba49a88 --- /dev/null +++ b/Tests/PhpBench/README.md @@ -0,0 +1,3 @@ +```bash +vendor/bin/phpbench run --progress=none --report='generator: "table", cols: ["subject", "mem_peak", "mean", "diff"]' +``` diff --git a/Tests/PhpBench/SyslogBench.php b/Tests/PhpBench/SyslogBench.php new file mode 100644 index 0000000..707dc9d --- /dev/null +++ b/Tests/PhpBench/SyslogBench.php @@ -0,0 +1,34 @@ +log->debug(...$this->message()); + $this->log->info(...$this->message()); + $this->log->notice(...$this->message()); + $this->log->warning(...$this->message()); + $this->log->error(...$this->message()); + $this->log->critical(...$this->message()); + $this->log->alert(...$this->message()); + $this->log->emergency(...$this->message()); + } + + protected function getConfig(): array + { + return [ + 'deferred' => false, + 'loggers' => [ + ['class' => Syslog::class], + ] + ]; + } +} diff --git a/phpbench.json.dist b/phpbench.json.dist new file mode 100644 index 0000000..b6aa51b --- /dev/null +++ b/phpbench.json.dist @@ -0,0 +1,13 @@ +{ + "bootstrap": "./vendor/autoload.php", + "path": "Tests/PhpBench", + "time_unit": "milliseconds", + "php_disable_ini": false, + "reports": [ + { + "default": { + "generator": "table" + } + } + ] +} \ No newline at end of file From 6b47930df30baefa3a3ec9a674dbc736edf64107 Mon Sep 17 00:00:00 2001 From: Mihail Binev Date: Tue, 10 Dec 2019 06:24:00 +0100 Subject: [PATCH 20/28] - unit tests improvements --- Tests/LogConstructorTest.php | 1 - Tests/LogDeferredTest.php | 2 +- Tests/LogTest.php | 10 +++++----- .../DefaultProcessorPropertiesTest.php | 1 - Tests/Processors/FileTest.php | 17 ++++++++--------- Tests/Processors/MemoryTest.php | 10 ++++------ 6 files changed, 18 insertions(+), 23 deletions(-) diff --git a/Tests/LogConstructorTest.php b/Tests/LogConstructorTest.php index 2436a49..5e195f1 100644 --- a/Tests/LogConstructorTest.php +++ b/Tests/LogConstructorTest.php @@ -7,7 +7,6 @@ class LogConstructorTest extends TestCase { - public function test_construction_with_config_array() { $log = new Log([ diff --git a/Tests/LogDeferredTest.php b/Tests/LogDeferredTest.php index c42488b..f8df5c4 100644 --- a/Tests/LogDeferredTest.php +++ b/Tests/LogDeferredTest.php @@ -51,7 +51,7 @@ public function test_log_suppression() $this->assertSame('', $processor->formatted()); } - protected function setUp() + protected function setUp(): void { $this->SUT = new Log([ 'deferred' => true, diff --git a/Tests/LogTest.php b/Tests/LogTest.php index 5375372..b349727 100644 --- a/Tests/LogTest.php +++ b/Tests/LogTest.php @@ -42,9 +42,9 @@ public function test_log_suppression() $processor->update([ [ - 'level' => -1, // this is ignored + 'level' => -1, // this is ignored 'levelname' => 'DEBUG', - 'message' => 'Hello', + 'message' => 'Hello', 'timestamp' => 1234567890 ] ]); @@ -57,11 +57,11 @@ public function test_exception() $processor = new Memory([]); $this->SUT->exception(new Exception('The message', 1), $processor); - $this->assertAttributeContains('[CRITICAL]', 'formatted', $processor); - $this->assertAttributeContains('The message', 'formatted', $processor); + $this->assertAttributeContains('[CRITICAL]', 'formatted', $processor); + $this->assertAttributeContains('The message', 'formatted', $processor); } - protected function setUp() + protected function setUp(): void { $this->SUT = new Log([]); } diff --git a/Tests/Processors/DefaultProcessorPropertiesTest.php b/Tests/Processors/DefaultProcessorPropertiesTest.php index d717b19..26fb99c 100644 --- a/Tests/Processors/DefaultProcessorPropertiesTest.php +++ b/Tests/Processors/DefaultProcessorPropertiesTest.php @@ -7,7 +7,6 @@ class DefaultProcessorPropertiesTest extends TestCase { - public function test_defaults() { $processor = new Memory([]); diff --git a/Tests/Processors/FileTest.php b/Tests/Processors/FileTest.php index 9a78d12..5aa5a29 100644 --- a/Tests/Processors/FileTest.php +++ b/Tests/Processors/FileTest.php @@ -7,7 +7,6 @@ class FileTest extends TestCase { - /** * @var vfsStreamDirectory */ @@ -21,15 +20,15 @@ public function test_update() $processor = new File(['dir' => $this->dir->url()]); $processor->update([ [ - 'level' => -1, + 'level' => -1, 'levelname' => 'DEBUG', - 'message' => 'Test 1', + 'message' => 'Test 1', 'timestamp' => 1234567890 ], [ - 'level' => -1, + 'level' => -1, 'levelname' => 'DEBUG', - 'message' => 'Test 2', + 'message' => 'Test 2', 'timestamp' => 1234567891 ] ]); @@ -38,12 +37,12 @@ public function test_update() $this->assertTrue($this->dir->hasChild($subdirectory)); $content = $this->dir->getChild($subdirectory . DIRECTORY_SEPARATOR . $file)->getContent(); - $this->assertContains("1234567891 [DEBUG]: Test 2\n", $content); + $this->assertStringContainsString("1234567891 [DEBUG]: Test 2\n", $content); } public function test_when_directory_does_not_exist() { - $dir = $this->dir->url() . '/nonexistent/'; + $dir = $this->dir->url() . '/nonexistent'; $this->expectException(FileProcessorException::class); $this->expectExceptionMessage('Log directory "' . $dir . '" must exist'); @@ -57,14 +56,14 @@ public function test_when_directory_is_not_writable() $dir = $this->dir->url(); $this->expectException(FileProcessorException::class); - $this->expectExceptionMessage('Log directory "' . $dir . '/" must be writable'); + $this->expectExceptionMessage('Log directory "' . $dir . '" must be writable'); vfsStreamWrapper::getRoot()->chmod(0400); $processor = new File(['dir' => $dir]); $processor->update([]); } - protected function setUp() + protected function setUp(): void { $this->dir = vfsStream::setup(); } diff --git a/Tests/Processors/MemoryTest.php b/Tests/Processors/MemoryTest.php index 16e9ee3..109a66a 100644 --- a/Tests/Processors/MemoryTest.php +++ b/Tests/Processors/MemoryTest.php @@ -2,27 +2,25 @@ namespace Koded\Logging\Processors; -use function Koded\Stdlib\dump; use PHPUnit\Framework\TestCase; class MemoryTest extends TestCase { - public function test_formatting() { $processor = new Memory([]); $processor->update([ [ - 'level' => -1, + 'level' => -1, 'levelname' => 'TEST', - 'message' => 'Hello', + 'message' => 'Hello', 'timestamp' => 1234567890 ], [ - 'level' => -1, + 'level' => -1, 'levelname' => 'TEST', - 'message' => 'World', + 'message' => 'World', 'timestamp' => 1234567891 ] ]); From fd32436c41a868bac10537ccef8818b77f3f87c8 Mon Sep 17 00:00:00 2001 From: Mihail Binev Date: Tue, 10 Dec 2019 06:24:50 +0100 Subject: [PATCH 21/28] STDOUT -> STDERR --- Processors/Cli.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Processors/Cli.php b/Processors/Cli.php index 786e9b3..0156913 100644 --- a/Processors/Cli.php +++ b/Processors/Cli.php @@ -27,13 +27,13 @@ class Cli extends Processor public function __construct(array $settings) { parent::__construct($settings); - $this->buffer = defined('STDOUT'); + $this->buffer = defined('STDERR'); } protected function parse(array $message): void { if ($this->buffer) { - fwrite(STDOUT, strtr($this->format, $message) . PHP_EOL); + fwrite(STDERR, strtr($this->format, $message) . PHP_EOL); } } } From 0f00229c44f86c8cec182ff4e52b4efa28918375 Mon Sep 17 00:00:00 2001 From: Mihail Binev Date: Tue, 10 Dec 2019 06:26:03 +0100 Subject: [PATCH 22/28] - directory path update --- Processors/File.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Processors/File.php b/Processors/File.php index 935c8e1..a354d56 100644 --- a/Processors/File.php +++ b/Processors/File.php @@ -43,7 +43,7 @@ public function __construct(array $settings) umask(umask() | 0002); $this->ext = (string)($settings['extension'] ?? '.log'); - $this->dir = rtrim((string)$settings['dir'], '/') . '/'; + $this->dir = rtrim((string)$settings['dir'], '/'); if (false === is_dir($this->dir)) { throw FileProcessorException::directoryDoesNotExist($this->dir); @@ -52,6 +52,8 @@ public function __construct(array $settings) if (false === is_writable($this->dir)) { throw FileProcessorException::directoryIsNotWritable($this->dir); } + + $this->dir .= '/'; } protected function parse(array $message): void From c8dec71e3288f2c1c065ad00bf813ac3f7a4c2be Mon Sep 17 00:00:00 2001 From: Mihail Binev Date: Tue, 10 Dec 2019 06:27:15 +0100 Subject: [PATCH 23/28] - removes the PHP 7.1 support --- .travis.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index c4d3cc3..bdb7188 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,14 @@ language: php php: - - 7.1 - 7.2 - 7.3 - - 7.4snapshot + - nightly matrix: fast_finish: true allow_failures: - - php: 7.4snapshot + - php: nightly install: - travis_retry composer update -o --no-interaction --prefer-source From de77d4b5418dd2c547c9294ca5d9f2e24d2458f0 Mon Sep 17 00:00:00 2001 From: Mihail Binev Date: Tue, 10 Dec 2019 06:28:07 +0100 Subject: [PATCH 24/28] - renamed from phpunit.xml --- phpunit.xml => phpunit.xml.dist | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename phpunit.xml => phpunit.xml.dist (100%) diff --git a/phpunit.xml b/phpunit.xml.dist similarity index 100% rename from phpunit.xml rename to phpunit.xml.dist From 8bdc25783f923ef97341e341a8a7fce3d7a0020d Mon Sep 17 00:00:00 2001 From: Mihail Binev Date: Tue, 10 Dec 2019 06:29:38 +0100 Subject: [PATCH 25/28] - adds phpbench - minimum PHP version increased to 7.2 --- composer.json | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index 12872ab..624ecfc 100644 --- a/composer.json +++ b/composer.json @@ -14,11 +14,11 @@ "authors": [ { "name": "Mihail Binev", - "homepage": "http://kodeart.com" + "homepage": "https://kodeart.com" } ], "require": { - "php": "^7.1.4", + "php": "^7.2", "psr/log": "~1", "koded/stdlib": "~4" }, @@ -31,13 +31,17 @@ ] }, "require-dev": { - "phpunit/phpunit": "~7", + "phpunit/phpunit": "^7", + "kodeart/phpunit-attribute-asserts": "*", "mikey179/vfsstream": "~1", - "scrutinizer/ocular": "~1" + "scrutinizer/ocular": "~1", + "phpbench/phpbench": "@dev" }, "autoload-dev": { "psr-4": { "Koded\\Logging\\": "Tests/" } - } + }, + "prefer-stable": true, + "minimum-stability": "dev" } \ No newline at end of file From 79833a0c9a1b6df2c225b635e1c21ba0bb333ff0 Mon Sep 17 00:00:00 2001 From: Mihail Binev Date: Tue, 10 Dec 2019 06:29:59 +0100 Subject: [PATCH 26/28] - git files update --- .gitattributes | 4 ++-- .gitignore | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.gitattributes b/.gitattributes index 93a3744..3f961de 100644 --- a/.gitattributes +++ b/.gitattributes @@ -6,5 +6,5 @@ /.gitattributes export-ignore /.gitignore export-ignore /composer.lock export-ignore -/*.xml export-ignore -/*.yml export-ignore +/.dist export-ignore +/.yml export-ignore diff --git a/.gitignore b/.gitignore index 8d74a4b..a9a7b1d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,4 @@ vendor/ .idea/ composer.lock *.phar -*.yml -!.scrutinizer.yml \ No newline at end of file +*.cache From 3107806196bc81b44a8f4a88f3542ea48aafa17e Mon Sep 17 00:00:00 2001 From: Mihail Binev Date: Tue, 10 Dec 2019 06:31:15 +0100 Subject: [PATCH 27/28] - README update --- README.md | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 44edc53..3c7f473 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,9 @@ can be set separately and process messages based on the log level. [![Latest Stable Version](https://img.shields.io/packagist/v/koded/logging.svg)](https://packagist.org/packages/koded/logging) [![Build Status](https://travis-ci.org/kodedphp/logging.svg?branch=master)](https://travis-ci.org/kodedphp/logging) -[![Codacy Badge](https://api.codacy.com/project/badge/Coverage/81ffd9cf1725485d8f6fb836617d002d)](https://www.codacy.com/app/kodeart/logging) -[![Codacy Badge](https://api.codacy.com/project/badge/Grade/81ffd9cf1725485d8f6fb836617d002d)](https://www.codacy.com/app/kodeart/logging) +[![Code Coverage](https://scrutinizer-ci.com/g/kodedphp/logging/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/kodedphp/logging/?branch=master) +[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/kodedphp/logging/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/kodedphp/logging/?branch=master) [![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%207.1-8892BF.svg)](https://php.net/) -[![Software license](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](LICENSE) Installation @@ -56,8 +55,6 @@ $log->warning("You don't see anything"); Configuration ------------- -@TODO Fix the text for `register()` and `deferred` flag - | Param | Type | Required | Default | Description | |-----------:|:------:|:--------:|:----------------|:------------| | loggers | array | yes | (empty) | An array of log processors. Every processor is defined in array with it's own configuration parameters. See [processor directives](processor-default-directives) | @@ -99,18 +96,18 @@ Tips: Processors ---------- -| Class name | Description | -|-----------:|:-------------------------------------------------------------------------------------| -| ErrorLog | uses the [error_log()][error-log] function to send the message to PHP's logger | -| SysLog | will open the system logger and send messages using the [syslog()][syslog] function | -| Cli | write the messages in the console (with STDOUT) | -| Memory | will store all messages in an array. Useful for unit tests if the logger is involved | -| File | saves the messages on a disk. **It's a slow one and should be avoided** | +| Class name | Description | +|------------------:|:-------------------------------------------------------------------------------------| +| ErrorLog | uses the [error_log()][error-log] function to send the message to PHP's logger | +| Cli | write the messages in the console (with STDERR) | +| Memory | will store all messages in an array. Useful for unit tests if the logger is involved | +| SysLog **(slow)** | will open the system logger and send messages using the [syslog()][syslog] function | +| File **(slow)** | saves the messages on a disk | License ------- - +[![Software license](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](LICENSE) The code is distributed under the terms of [The 3-Clause BSD license](LICENSE). [psr-3]: http://www.php-fig.org/psr/psr-3/ From 5de60996c3da128e42fd5b3a207d8a43c2c76231 Mon Sep 17 00:00:00 2001 From: Mihail Binev Date: Tue, 10 Dec 2019 06:35:27 +0100 Subject: [PATCH 28/28] cleanup --- composer.json | 1 - 1 file changed, 1 deletion(-) diff --git a/composer.json b/composer.json index 624ecfc..3e31b48 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,6 @@ }, "require-dev": { "phpunit/phpunit": "^7", - "kodeart/phpunit-attribute-asserts": "*", "mikey179/vfsstream": "~1", "scrutinizer/ocular": "~1", "phpbench/phpbench": "@dev"