From a2995618ddbdb1d840ed08f8a21f1b2ea5933d30 Mon Sep 17 00:00:00 2001 From: Cesar Date: Fri, 24 Dec 2021 14:11:04 +0100 Subject: [PATCH] feat: add truncate option --- README.md | 2 + src/Rotation.php | 65 +++++++++++++++++++--- tests/Compress/GzTest.php | 6 +- tests/OptionTest.php | 6 +- tests/Processors/RotativeProcessorTest.php | 8 +-- tests/RotationTest.php | 30 +++++++++- 6 files changed, 95 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 68d2e99..7443b31 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ $rotation ->compress() // Optional, compress the file after rotated. Default false ->files(30) // Optional, files are rotated 30 times before being removed. Default 366 ->minSize(1024) // Optional, are rotated when they grow bigger than 1024 bytes. Default 0 + ->truncate() // Optional, truncate the original log file in place after creating a copy, instead of moving the old log file. ->then(function ($filenameTarget, $filenameRotated) {}) // Optional, to get filename target and original filename ->catch(function (RotationFailed $exception) {}) // Optional, to catch a exception in rotating ->rotate('file.log'); @@ -46,6 +47,7 @@ $rotation = new Rotation([ 'files' => 1, 'compress' => true, 'min-size' => 10, + 'truncate' => false, 'then' => function ($filename) {}, 'catch' => function (RotationFailed $exception) {}, ]); diff --git a/src/Rotation.php b/src/Rotation.php index bd286c6..5f1e2b5 100644 --- a/src/Rotation.php +++ b/src/Rotation.php @@ -17,6 +17,8 @@ class Rotation private int $_minSize = 0; + private bool $_truncate = false; + private $thenCallback = null; public function __construct(array $options = []) @@ -25,6 +27,7 @@ public function __construct(array $options = []) $this->methodsOptionables([ 'compress', + 'truncate', 'minSize', 'files', 'then', @@ -60,6 +63,20 @@ public function compress(bool $compress = true): self return $this; } + /** + * Truncate the original log file in place after creating a copy, instead of + * moving the old log file. + * + * It can be used when some program cannot be told to close its logfile and + * thus might continue writing (appending) to the previous log file forever. + */ + public function truncate(bool $truncate = true): self + { + $this->_truncate = $truncate; + + return $this; + } + /** * Log files are rotated when they grow bigger than size bytes. */ @@ -95,18 +112,28 @@ public function rotate(string $filename): bool return false; } - $filenameRotated = $this->runProcessor( + $fileTemporary = $this->_truncate + ? $this->copyAndTruncate($filename) + : $this->move($filename); + + if (is_null($fileTemporary)) { + return false; + } + + $fileTarget = $this->runProcessor( $filename, - $this->moveContentToTempFile($filename) + $fileTemporary ); - $filenameRotated = is_null($filenameRotated) - ? $filenameRotated - : $this->runCompress($filenameRotated); + if (is_null($fileTarget)) { + return false; + } - $this->sucessfull($filename, $filenameRotated); + $fileTarget = $this->runCompress($fileTarget); - return !empty($filenameRotated); + $this->sucessfull($filename, $fileTarget); + + return true; } /** @@ -186,9 +213,9 @@ private function fileIsValid(string $filename): bool } /** - * move data to temp file and truncate. + * copy data to temp file and truncate. */ - private function moveContentToTempFile(string $filename): ?string + private function copyAndTruncate(string $filename): ?string { clearstatcache(); @@ -247,4 +274,24 @@ private function moveContentToTempFile(string $filename): ?string return $filenameTarget; } + + private function move(string $filename): ?string + { + clearstatcache(); + + $filenameTarget = tempnam(dirname($filename), 'LOG'); + + if (!rename($filename, $filenameTarget)) { + $this->exception( + new Exception( + sprintf('the file %s not can move to temp file %s.', $filename, $filenameTarget), + 22 + ) + ); + + return null; + } + + return $filenameTarget; + } } diff --git a/tests/Compress/GzTest.php b/tests/Compress/GzTest.php index e2fa08e..0cf7fdb 100644 --- a/tests/Compress/GzTest.php +++ b/tests/Compress/GzTest.php @@ -7,7 +7,7 @@ class GzTest extends TestCase { - public function test_rotation_processor_with_gz_processor() + public function testRotationProcessorWithGzProcessor() { $rotation = new Rotation(); @@ -27,10 +27,10 @@ public function test_rotation_processor_with_gz_processor() $this->assertEquals(self::DIR_WORK.'file.log.1.gz', $fileRotated); })->rotate(self::DIR_WORK.'file.log'); - $this->assertStringEqualsFile(self::DIR_WORK.'file.log', ''); + // $this->assertStringEqualsFile(self::DIR_WORK.'file.log', ''); $this->assertFileExists(self::DIR_WORK.'file.log.1.gz'); - $this->assertEquals($content, implode("", gzfile(self::DIR_WORK.'file.log.1.gz'))); + $this->assertEquals($content, implode('', gzfile(self::DIR_WORK.'file.log.1.gz'))); } } diff --git a/tests/OptionTest.php b/tests/OptionTest.php index 17e5cb6..b38435f 100644 --- a/tests/OptionTest.php +++ b/tests/OptionTest.php @@ -3,16 +3,16 @@ namespace Cesargb\Log\Test; use Cesargb\Log\Rotation; -use Cesargb\Log\Test\TestCase; class OptionTest extends TestCase { - public function test_pass_options() + public function testPassOptions() { $rotation = new Rotation([ 'files' => 1, 'compress' => true, 'min-size' => 10, + 'truncate' => false, 'then' => function ($filename) {}, 'catch' => function ($error) {}, ]); @@ -20,7 +20,7 @@ public function test_pass_options() $this->assertNotNull($rotation); } - public function test_catch_exceptio_if_method_is_not_permited() + public function testCatchExceptioIfMethodIsNotPermited() { $this->expectException(\LogicException::class); diff --git a/tests/Processors/RotativeProcessorTest.php b/tests/Processors/RotativeProcessorTest.php index 58dddee..2af46a6 100644 --- a/tests/Processors/RotativeProcessorTest.php +++ b/tests/Processors/RotativeProcessorTest.php @@ -7,7 +7,7 @@ class RotativeProcessorTest extends TestCase { - public function test_rotation_processor() + public function testRotationProcessor() { $maxFiles = 5; @@ -20,7 +20,7 @@ public function test_rotation_processor() $rotation->rotate(self::DIR_WORK.'file.log'); } - $this->assertStringEqualsFile(self::DIR_WORK.'file.log', ''); + // $this->assertStringEqualsFile(self::DIR_WORK.'file.log', ''); foreach (range(1, $maxFiles) as $n) { $this->assertFileExists(self::DIR_WORK.'file.log.'.$n); @@ -29,7 +29,7 @@ public function test_rotation_processor() $this->assertFalse(is_file(self::DIR_WORK.'file.log.'.($maxFiles + 1))); } - public function test_rotation_processor_with_gz_processor() + public function testRotationProcessorWithGzProcessor() { $maxFiles = 5; @@ -43,7 +43,7 @@ public function test_rotation_processor_with_gz_processor() $rotation->rotate(self::DIR_WORK.'file.log'); } - $this->assertStringEqualsFile(self::DIR_WORK.'file.log', ''); + // $this->assertStringEqualsFile(self::DIR_WORK.'file.log', ''); foreach (range(1, $maxFiles) as $n) { $this->assertFileExists(self::DIR_WORK."file.log.{$n}.gz"); diff --git a/tests/RotationTest.php b/tests/RotationTest.php index 5aaf5e7..0f77390 100644 --- a/tests/RotationTest.php +++ b/tests/RotationTest.php @@ -28,7 +28,7 @@ public function testNotRotateIfFileIsEmpty() $this->assertFileDoesNotExist(self::DIR_WORK.'file.log.1'); } - public function testOptionNocompress() + public function testRotationDefault() { file_put_contents(self::DIR_WORK.'file.log', microtime(true)); @@ -36,8 +36,6 @@ public function testOptionNocompress() $rotation->rotate(self::DIR_WORK.'file.log'); - $this->assertStringEqualsFile(self::DIR_WORK.'file.log', ''); - $this->assertFileExists(self::DIR_WORK.'file.log.1'); } @@ -104,4 +102,30 @@ public function testOptionMinsize() $this->assertFileExists(self::DIR_WORK.'file.log.1'); } + + public function testRotationTruncate() + { + file_put_contents(self::DIR_WORK.'file.log', microtime(true)); + + $rotation = new Rotation(); + + $rotation->truncate()->rotate(self::DIR_WORK.'file.log'); + + $this->assertStringEqualsFile(self::DIR_WORK.'file.log', ''); + + $this->assertFileExists(self::DIR_WORK.'file.log.1'); + } + + public function testOptionTruncateAndCompress() + { + file_put_contents(self::DIR_WORK.'file.log', microtime(true)); + + $rotation = new Rotation(); + + $rotation->compress()->truncate()->rotate(self::DIR_WORK.'file.log'); + + $this->assertStringEqualsFile(self::DIR_WORK.'file.log', ''); + + $this->assertFileExists(self::DIR_WORK.'file.log.1.gz'); + } }