Skip to content

Commit 28616c1

Browse files
committed
resolving conflicts
2 parents 70db4f1 + 421ed8a commit 28616c1

File tree

6 files changed

+278
-163
lines changed

6 files changed

+278
-163
lines changed

src/Handler/RotatingFileHandler.php

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,59 +5,57 @@
55
namespace KaririCode\Logging\Handler;
66

77
use KaririCode\Contract\ImmutableValue;
8+
use KaririCode\Contract\Logging\LogRotator;
89
use KaririCode\Logging\Exception\LoggingException;
910
use KaririCode\Logging\LogLevel;
1011

1112
class RotatingFileHandler extends AbstractFileHandler
1213
{
13-
private int $maxFiles;
14-
private int $maxFileSize;
14+
private LogRotator $rotator;
1515

1616
public function __construct(
1717
string $filePath,
18-
int $maxFiles = 5,
19-
int $maxFileSize = 5 * 1024 * 1024, // 5 MB
18+
LogRotator $rotator,
2019
LogLevel $minLevel = LogLevel::DEBUG
2120
) {
2221
parent::__construct($filePath, $minLevel);
23-
$this->maxFiles = $maxFiles;
24-
$this->maxFileSize = $maxFileSize;
22+
$this->rotator = $rotator;
2523
}
2624

25+
/**
26+
* @throws LoggingException
27+
*/
2728
public function handle(ImmutableValue $record): void
2829
{
2930
if (!$this->isHandling($record)) {
3031
return;
3132
}
3233

33-
if ($this->shouldRotate()) {
34-
$this->rotate();
34+
try {
35+
$this->rotateIfNecessary();
36+
$this->writeToFile($record);
37+
} catch (\Exception $e) {
38+
throw new LoggingException("Error handling log record: " . $e->getMessage(), 0, $e);
3539
}
36-
37-
$this->writeToFile($record);
3840
}
3941

40-
private function shouldRotate(): bool
42+
/**
43+
* @throws \Exception
44+
*/
45+
private function rotateIfNecessary(): void
4146
{
42-
return file_exists($this->filePath) && filesize($this->filePath) >= $this->maxFileSize;
47+
if ($this->rotator->shouldRotate($this->filePath)) {
48+
$this->rotator->rotate($this->filePath);
49+
$this->reopenFile();
50+
}
4351
}
4452

45-
private function rotate(): void
53+
private function reopenFile(): void
4654
{
47-
for ($i = $this->maxFiles - 1; $i > 0; --$i) {
48-
$source = "{$this->filePath}.{$i}";
49-
$target = "{$this->filePath}." . ($i + 1);
50-
if (file_exists($source)) {
51-
if (!rename($source, $target)) {
52-
throw new LoggingException("Failed to rotate log file from {$source} to {$target}");
53-
}
54-
}
55-
}
56-
57-
// Close the current file handle and open a new one
5855
if (is_resource($this->fileHandle)) {
5956
fclose($this->fileHandle);
6057
}
58+
$this->fileHandle = null;
6159
$this->openFile();
6260
}
6361
}

src/Rotator/SizeBasedRotator.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace KaririCode\Logging\Rotator;
6+
7+
use KaririCode\Contract\Logging\LogRotator;
8+
9+
class SizeBasedRotator implements LogRotator
10+
{
11+
public function __construct(
12+
private int $maxFiles = 5,
13+
private int $maxFileSize = 5 * 1024 * 1024
14+
) {
15+
16+
}
17+
18+
public function shouldRotate(string $filePath): bool
19+
{
20+
return file_exists($filePath) && filesize($filePath) >= $this->maxFileSize;
21+
}
22+
23+
public function rotate(string $filePath): void
24+
{
25+
for ($i = $this->maxFiles - 1; $i > 0; --$i) {
26+
$source = "{$filePath}." . ($i - 1);
27+
$target = "{$filePath}.{$i}";
28+
if (file_exists($source)) {
29+
$this->safeRename($source, $target);
30+
}
31+
}
32+
33+
if (file_exists($filePath)) {
34+
$this->safeRename($filePath, "{$filePath}.1");
35+
}
36+
}
37+
38+
private function safeRename(string $source, string $target): void
39+
{
40+
if (!rename($source, $target)) {
41+
throw new \RuntimeException("Failed to rotate log file from {$source} to {$target}");
42+
}
43+
}
44+
}

tests/Handler/RotatingFileHandlerTest.php

Lines changed: 98 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -2,105 +2,136 @@
22

33
declare(strict_types=1);
44

5-
namespace KaririCode\Logging\Tests\Handler;
5+
namespace Tests\KaririCode\Logging\Handler;
66

7+
use KaririCode\Contract\ImmutableValue;
8+
use KaririCode\Contract\Logging\LogRotator;
79
use KaririCode\Logging\Exception\LoggingException;
810
use KaririCode\Logging\Handler\RotatingFileHandler;
911
use KaririCode\Logging\LogLevel;
10-
use KaririCode\Logging\LogRecord;
12+
use PHPUnit\Framework\MockObject\MockObject;
1113
use PHPUnit\Framework\TestCase;
1214

1315
class RotatingFileHandlerTest extends TestCase
1416
{
15-
private string $testLogDir;
16-
private string $testLogFile;
17-
private RotatingFileHandler $rotatingFileHandler;
17+
private string $tempDir;
18+
private string $logFile;
19+
private LogRotator|MockObject $mockRotator;
1820

1921
protected function setUp(): void
2022
{
21-
$this->testLogDir = sys_get_temp_dir() . '/test_rotating_logs';
22-
$this->testLogFile = $this->testLogDir . '/test_rotating.log';
23-
24-
if (!is_dir($this->testLogDir)) {
25-
mkdir($this->testLogDir, 0777, true);
26-
}
27-
28-
$this->rotatingFileHandler = new RotatingFileHandler(
29-
$this->testLogFile
30-
);
23+
$this->tempDir = sys_get_temp_dir() . '/rotating_file_handler_test_' . uniqid();
24+
mkdir($this->tempDir);
25+
$this->logFile = $this->tempDir . '/test.log';
26+
$this->mockRotator = $this->createMock(LogRotator::class);
3127
}
3228

33-
public function testHandleHappyPath(): void
29+
protected function tearDown(): void
3430
{
35-
$record = new LogRecord(LogLevel::INFO, 'Test message');
36-
$this->rotatingFileHandler->handle($record);
37-
38-
$this->assertFileExists($this->testLogFile);
39-
$this->assertStringContainsString('Test message', file_get_contents($this->testLogFile));
31+
$this->removeDirectory($this->tempDir);
4032
}
4133

42-
public function testRotateLogs(): void
34+
private function removeDirectory(string $dir): void
4335
{
44-
$this->rotatingFileHandler = new RotatingFileHandler($this->testLogFile, 2, 10); // Smaller file size
45-
46-
for ($i = 0; $i < 5; ++$i) {
47-
$record = new LogRecord(LogLevel::INFO, str_repeat('A', 10));
48-
$this->rotatingFileHandler->handle($record);
36+
if (!is_dir($dir)) {
37+
return;
4938
}
39+
$files = array_diff(scandir($dir), ['.', '..']);
40+
foreach ($files as $file) {
41+
(is_dir("$dir/$file")) ? $this->removeDirectory("$dir/$file") : unlink("$dir/$file");
42+
}
43+
rmdir($dir);
44+
}
5045

51-
$this->assertFileExists($this->testLogFile);
52-
$this->assertFileExists($this->testLogFile . '.1');
53-
$this->assertFileExists($this->testLogFile . '.2');
46+
public function testHandleWritesLogWhenLevelIsHighEnough(): void
47+
{
48+
$handler = new RotatingFileHandler($this->logFile, $this->mockRotator, LogLevel::INFO);
49+
$record = $this->createMock(ImmutableValue::class);
50+
$record->method('get')->willReturn([
51+
'level' => LogLevel::ERROR,
52+
'message' => 'Test error message',
53+
'context' => [],
54+
]);
55+
56+
$handler->handle($record);
57+
58+
$this->assertFileExists($this->logFile);
59+
$this->assertStringContainsString('Test error message', file_get_contents($this->logFile));
5460
}
5561

56-
public function testHandleThrowsExceptionOnWriteFailure(): void
62+
public function testHandleDoesNotWriteLogWhenLevelIsTooLow(): void
5763
{
58-
$this->expectException(LoggingException::class);
59-
$this->expectExceptionMessage('Unable to create log directory: /invalid');
60-
61-
// Suppress warnings for this test
62-
set_error_handler(function ($errno, $errstr, $errfile, $errline) {
63-
return true;
64-
});
65-
66-
try {
67-
$invalidHandler = new RotatingFileHandler('/invalid/path.log');
68-
$record = new LogRecord(LogLevel::INFO, 'Test message');
69-
$invalidHandler->handle($record);
70-
} finally {
71-
restore_error_handler();
72-
}
64+
$handler = new RotatingFileHandler($this->logFile, $this->mockRotator, LogLevel::ERROR);
65+
$record = $this->createMock(ImmutableValue::class);
66+
$record->method('get')->willReturn([
67+
'level' => LogLevel::INFO,
68+
'message' => 'Test info message',
69+
'context' => [],
70+
]);
71+
72+
$handler->handle($record);
73+
74+
$this->assertFileDoesNotExist($this->logFile);
7375
}
7476

75-
public function testMaxFiles(): void
77+
public function testHandleRotatesFileWhenNecessary(): void
7678
{
77-
$this->rotatingFileHandler = new RotatingFileHandler($this->testLogFile, 3, 10);
79+
$this->mockRotator->method('shouldRotate')->willReturn(true);
80+
$this->mockRotator->expects($this->once())->method('rotate');
7881

79-
for ($i = 0; $i < 5; ++$i) {
80-
$record = new LogRecord(LogLevel::INFO, str_repeat('A', 10));
81-
$this->rotatingFileHandler->handle($record);
82-
}
82+
$handler = new RotatingFileHandler($this->logFile, $this->mockRotator);
83+
$record = $this->createMock(ImmutableValue::class);
84+
$record->method('get')->willReturn([
85+
'level' => LogLevel::INFO,
86+
'message' => 'Test rotation message',
87+
'context' => [],
88+
]);
89+
90+
$handler->handle($record);
8391

84-
$this->assertFileExists($this->testLogFile);
85-
$this->assertFileExists($this->testLogFile . '.1');
86-
$this->assertFileExists($this->testLogFile . '.2');
87-
$this->assertFileExists($this->testLogFile . '.3');
88-
$this->assertFileDoesNotExist($this->testLogFile . '.4');
92+
$this->assertFileExists($this->logFile);
93+
$this->assertStringContainsString('Test rotation message', file_get_contents($this->logFile));
8994
}
9095

91-
public function testRespectLogLevel(): void
96+
public function testHandleThrowsLoggingExceptionOnError(): void
9297
{
93-
$this->rotatingFileHandler = new RotatingFileHandler($this->testLogFile, 2, 50, LogLevel::WARNING);
98+
$this->mockRotator->method('shouldRotate')->willThrowException(new \RuntimeException('Rotation error'));
9499

95-
$infoRecord = new LogRecord(LogLevel::INFO, 'Info message');
96-
$warningRecord = new LogRecord(LogLevel::WARNING, 'Warning message');
100+
$handler = new RotatingFileHandler($this->logFile, $this->mockRotator);
101+
$record = $this->createMock(ImmutableValue::class);
102+
$record->method('get')->willReturn([
103+
'level' => LogLevel::INFO,
104+
'message' => 'Test error handling',
105+
'context' => [],
106+
]);
97107

98-
$this->rotatingFileHandler->handle($infoRecord);
99-
$this->rotatingFileHandler->handle($warningRecord);
108+
$this->expectException(LoggingException::class);
109+
$this->expectExceptionMessage('Error handling log record: Rotation error');
110+
111+
$handler->handle($record);
112+
}
100113

101-
$this->assertFileExists($this->testLogFile);
102-
$content = file_get_contents($this->testLogFile);
103-
$this->assertStringNotContainsString('Info message', $content);
104-
$this->assertStringContainsString('Warning message', $content);
114+
public function testHandleReopensFileAfterRotation(): void
115+
{
116+
$this->mockRotator->method('shouldRotate')->willReturnOnConsecutiveCalls(true, false);
117+
$this->mockRotator->expects($this->once())->method('rotate');
118+
119+
$handler = new RotatingFileHandler($this->logFile, $this->mockRotator);
120+
$record = $this->createMock(ImmutableValue::class);
121+
$record->method('get')->willReturn([
122+
'level' => LogLevel::INFO,
123+
'message' => 'Test message',
124+
'context' => [],
125+
]);
126+
127+
// First call - should rotate and reopen
128+
$handler->handle($record);
129+
130+
// Second call - should write to the reopened file
131+
$handler->handle($record);
132+
133+
$this->assertFileExists($this->logFile);
134+
$logContent = file_get_contents($this->logFile);
135+
$this->assertEquals(2, substr_count($logContent, 'Test message'));
105136
}
106137
}

tests/Processor/GitProcessorTest.php

Lines changed: 0 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +0,0 @@
1-
<?php
2-
3-
declare(strict_types=1);
4-
5-
namespace KaririCode\Logging\Tests\Processor;
6-
7-
use KaririCode\Logging\LogLevel;
8-
use KaririCode\Logging\LogRecord;
9-
use KaririCode\Logging\Processor\GitProcessor;
10-
use PHPUnit\Framework\TestCase;
11-
12-
class GitProcessorTest extends TestCase
13-
{
14-
private GitProcessor $gitProcessor;
15-
16-
protected function setUp(): void
17-
{
18-
$this->gitProcessor = new GitProcessor();
19-
}
20-
21-
public function testProcessHappyPath(): void
22-
{
23-
$record = new LogRecord(LogLevel::INFO, 'Test message');
24-
25-
$processedRecord = $this->gitProcessor->process($record);
26-
27-
$this->assertArrayHasKey('git', $processedRecord->context);
28-
}
29-
30-
public function testProcessWithGitInfo(): void
31-
{
32-
// Simulate Git info
33-
file_put_contents('.git/HEAD', 'ref: refs/heads/main');
34-
file_put_contents('.git/refs/heads/main', 'commit_hash');
35-
36-
$record = new LogRecord(LogLevel::INFO, 'Test message');
37-
$processedRecord = $this->gitProcessor->process($record);
38-
39-
$this->assertEquals('main', $processedRecord->context['git']['branch']);
40-
$this->assertEquals('commit_hash', $processedRecord->context['git']['commit']);
41-
42-
// Clean up
43-
unlink('.git/HEAD');
44-
unlink('.git/refs/heads/main');
45-
}
46-
}

0 commit comments

Comments
 (0)