diff --git a/composer.json b/composer.json index e610a95b..07772b6d 100644 --- a/composer.json +++ b/composer.json @@ -67,8 +67,7 @@ "psr/http-factory": "^1.0", "psr/cache": "^2.0 || ^3.0", "psr/simple-cache": "^3.0", - "psr/log": "^1.0 || ^2.0 || ^3.0", - "sonsofphp/logger-contract": "0.3.x-dev" + "psr/log": "^1.0 || ^2.0 || ^3.0" }, "replace": { "sonsofphp/bard": "self.version", diff --git a/mkdocs.yml b/mkdocs.yml index 34ffbf2e..4cb1d437 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -106,6 +106,7 @@ nav: - HttpFactory: components/http-factory/index.md - HttpMessage: components/http-message/index.md - JSON: components/json/index.md + - Logger: components/logger/index.md - Money: - components/money/index.md - Currency Providers: components/money/currency-providers.md diff --git a/src/SonsOfPHP/Component/Logger/Handler/AbstractHandler.php b/src/SonsOfPHP/Component/Logger/Handler/AbstractHandler.php index 407c638f..d703bf81 100644 --- a/src/SonsOfPHP/Component/Logger/Handler/AbstractHandler.php +++ b/src/SonsOfPHP/Component/Logger/Handler/AbstractHandler.php @@ -20,6 +20,8 @@ public function __construct( protected ?FormatterInterface $formatter = null, ) {} + //abstract public function doHandle(RecordInterface $record, string $message): void; + public function getFilter(): ?FilterInterface { return $this->filter ?? null; @@ -32,7 +34,7 @@ public function setFilter(FilterInterface $filter): void public function getFormatter(): ?FormatterInterface { - return $this->formatter; + return $this->formatter ?? null; } public function setFormatter(FormatterInterface $formatter): void @@ -42,14 +44,15 @@ public function setFormatter(FormatterInterface $formatter): void public function handle(RecordInterface $record): void { - if (null !== $this->filter && false === $this->filter->isLoggable($record)) { + if (null !== $this->getFilter() && false === $this->filter->isLoggable($record)) { return; } - if (null !== $this->formatter) { - $record = $record->withMessage($this->formatter->formatMessage($record)); + $message = $record->getMessage(); + if (null !== $this->getFormatter()) { + $message = $record->withMessage($this->formatter->formatMessage($record)); } - // ... doHandle + $this->doHandle($record, $message); } } diff --git a/src/SonsOfPHP/Component/Logger/Handler/ConsoleHandler.php b/src/SonsOfPHP/Component/Logger/Handler/ConsoleHandler.php deleted file mode 100644 index dc3c26e2..00000000 --- a/src/SonsOfPHP/Component/Logger/Handler/ConsoleHandler.php +++ /dev/null @@ -1,21 +0,0 @@ - - */ -class ConsoleHandler extends AbstractHandler implements HandlerInterface -{ - public function handle(RecordInterface $record): void - { - throw new \RuntimeException('To Be Implemented'); - } -} diff --git a/src/SonsOfPHP/Component/Logger/Handler/FileHandler.php b/src/SonsOfPHP/Component/Logger/Handler/FileHandler.php index d487837a..0447648c 100644 --- a/src/SonsOfPHP/Component/Logger/Handler/FileHandler.php +++ b/src/SonsOfPHP/Component/Logger/Handler/FileHandler.php @@ -14,8 +14,51 @@ */ class FileHandler extends AbstractHandler implements HandlerInterface { - public function handle(RecordInterface $record): void + private bool $isOpen = false; + private $handle; + + public function __construct( + private string $filename, + ) {} + + public function doHandle(RecordInterface $record, string $message): void + { + $this->open(); + + $this->write($message); + } + + private function write(string $message): void + { + if (false === fwrite($this->handle, $message)) { + throw new \RuntimeException(sprintf('"%s" could not be written to', $this->filename)); + } + } + + private function open(): void + { + if ($this->isOpen) { + return; + } + + if (false === $this->handle = fopen($this->filename, 'a')) { + throw new \RuntimeException(sprintf('"%s" could not be opened', $this->filename)); + } + $this->isOpen = true; + } + + private function close(): void + { + if (!$this->isOpen) { + return; + } + + fclose($this->handle); + $this->isOpen = false; + } + + public function __destruct() { - throw new \RuntimeException('To Be Implemented'); + $this->close(); } } diff --git a/src/SonsOfPHP/Component/Logger/Handler/SocketHandler.php b/src/SonsOfPHP/Component/Logger/Handler/SocketHandler.php deleted file mode 100644 index b747c619..00000000 --- a/src/SonsOfPHP/Component/Logger/Handler/SocketHandler.php +++ /dev/null @@ -1,19 +0,0 @@ - - */ -class SocketHandler extends AbstractHandler implements HandlerInterface -{ - public function handle(RecordInterface $record): void - { - throw new \RuntimeException('To Be Implemented'); - } -} diff --git a/src/SonsOfPHP/Component/Logger/Handler/StreamHandler.php b/src/SonsOfPHP/Component/Logger/Handler/StreamHandler.php index 355d004a..3c11b2c2 100644 --- a/src/SonsOfPHP/Component/Logger/Handler/StreamHandler.php +++ b/src/SonsOfPHP/Component/Logger/Handler/StreamHandler.php @@ -12,8 +12,23 @@ */ class StreamHandler extends AbstractHandler implements HandlerInterface { - public function handle(RecordInterface $record): void + private bool $isOpen = false; + private $stream; + + public function __construct($stream) + { + $this->stream = $stream; + } + + public function doHandle(RecordInterface $record, string $message): void + { + $this->write($message); + } + + private function write(string $message): void { - throw new \RuntimeException('To Be Implemented'); + if (false === fwrite($this->stream, $message)) { + throw new \RuntimeException(sprintf('stream could not be written to')); + } } } diff --git a/src/SonsOfPHP/Component/Logger/Tests/Handler/FileHandlerTest.php b/src/SonsOfPHP/Component/Logger/Tests/Handler/FileHandlerTest.php new file mode 100644 index 00000000..174785a6 --- /dev/null +++ b/src/SonsOfPHP/Component/Logger/Tests/Handler/FileHandlerTest.php @@ -0,0 +1,66 @@ +assertInstanceOf(HandlerInterface::class, $handler); + } + + /** + * @covers ::doHandle + * @covers ::open + * @covers ::write + * @covers ::close + * @covers ::__destruct + */ + public function testItCanWrite(): void + { + $handler = new FileHandler('/tmp/testing.log'); + $record = new Record( + channel: 'app', + level: Level::Debug, + message: 'Example {key} Message', + context: new Context(['key' => 'value']), + datetime: new \DateTimeImmutable('2020-04-20T04:20:00+00:00'), + ); + + $this->assertFileDoesNotExist('/tmp/testing.log'); + $handler->handle($record); + $this->assertFileExists('/tmp/testing.log'); + + unset($handler); + } +} diff --git a/src/SonsOfPHP/Component/Logger/Tests/Handler/StreamHandlerTest.php b/src/SonsOfPHP/Component/Logger/Tests/Handler/StreamHandlerTest.php new file mode 100644 index 00000000..bac296c9 --- /dev/null +++ b/src/SonsOfPHP/Component/Logger/Tests/Handler/StreamHandlerTest.php @@ -0,0 +1,63 @@ +assertInstanceOf(HandlerInterface::class, $handler); + } + + /** + * @covers ::doHandle + * @covers ::write + */ + public function testItCanWrite(): void + { + $handler = new StreamHandler(fopen('/tmp/testing.log', 'a')); + $record = new Record( + channel: 'app', + level: Level::Debug, + message: 'Example {key} Message', + context: new Context(['key' => 'value']), + datetime: new \DateTimeImmutable('2020-04-20T04:20:00+00:00'), + ); + + $this->assertSame('', file_get_contents('/tmp/testing.log')); + $handler->handle($record); + $this->assertNotSame('', file_get_contents('/tmp/testing.log')); + + unset($handler); + } +}