diff --git a/src/Util/Logger/StreamLogger.php b/src/Util/Logger/StreamLogger.php index ad1b152d9e..10ad0a0976 100644 --- a/src/Util/Logger/StreamLogger.php +++ b/src/Util/Logger/StreamLogger.php @@ -35,6 +35,12 @@ class StreamLogger extends AbstractFriendicaLogger */ private $pid; + /** + * An error message + * @var string + */ + private $errorMessage; + /** * Translates LogLevel log levels to integer values * @var array @@ -98,7 +104,7 @@ class StreamLogger extends AbstractFriendicaLogger protected function addEntry($level, $message, $context = []) { if (!array_key_exists($level, $this->levelToInt)) { - throw new \InvalidArgumentException('The level "%s" is not valid', $level); + throw new \InvalidArgumentException(sprintf('The level "%s" is not valid.', $level)); } $logLevel = $this->levelToInt[$level]; @@ -151,12 +157,14 @@ class StreamLogger extends AbstractFriendicaLogger } $this->createDir(); - $this->stream = fopen($this->url, 'a'); + set_error_handler([$this, 'customErrorHandler']); + $this->stream = fopen($this->url, 'ab'); + restore_error_handler(); if (!is_resource($this->stream)) { $this->stream = null; - throw new \UnexpectedValueException(sprintf('The stream or file "%s" could not be opened.', $this->url)); + throw new \UnexpectedValueException(sprintf('The stream or file "%s" could not be opened: ' . $this->errorMessage, $this->url)); } } @@ -173,10 +181,18 @@ class StreamLogger extends AbstractFriendicaLogger } if (isset($dirname) && !is_dir($dirname)) { + set_error_handler([$this, 'customErrorHandler']); $status = mkdir($dirname, 0777, true); + restore_error_handler(); + if (!$status && !is_dir($dirname)) { - throw new \UnexpectedValueException(sprintf('Directory "%s" cannot get created.', $dirname)); + throw new \UnexpectedValueException(sprintf('Directory "%s" cannot get created: ' . $this->errorMessage, $dirname)); } } } + + private function customErrorHandler($code, $msg) + { + $this->errorMessage = preg_replace('{^(fopen|mkdir)\(.*?\): }', '', $msg); + } } diff --git a/tests/src/Util/Logger/StreamLoggerTest.php b/tests/src/Util/Logger/StreamLoggerTest.php new file mode 100644 index 0000000000..a2e81441b2 --- /dev/null +++ b/tests/src/Util/Logger/StreamLoggerTest.php @@ -0,0 +1,203 @@ +setUpVfsDir(); + + $this->introspection = \Mockery::mock(Introspection::class); + $this->introspection->shouldReceive('getRecord')->andReturn([ + 'file' => self::FILE, + 'line' => self::LINE, + 'function' => self::FUNC + ]); + } + + public function assertLogline($string) + { + $this->assertRegExp(self::LOGLINE, $string); + } + + public function assertLoglineNums($assertNum, $string) + { + $this->assertEquals($assertNum, preg_match_all(self::LOGLINE, $string)); + } + + public function testNormal() + { + $logfile = vfsStream::newFile('friendica.log') + ->at($this->root); + + $logger = new StreamLogger('test', $logfile->url(), $this->introspection); + $logger->emergency('working!'); + $logger->alert('working too!'); + $logger->debug('and now?'); + $logger->notice('message', ['an' => 'context']); + + $text = $logfile->getContent(); + $this->assertLogline($text); + $this->assertLoglineNums(4, $text); + } + + /** + * Test if a log entry is correctly interpolated + */ + public function testPsrInterpolate() + { + $logfile = vfsStream::newFile('friendica.log') + ->at($this->root); + + $logger = new StreamLogger('test', $logfile->url(), $this->introspection); + + $logger->emergency('A {psr} test', ['psr' => 'working']); + $logger->alert('An {array} test', ['array' => ['it', 'is', 'working']]); + $text = $logfile->getContent(); + $this->assertContains('A working test', $text); + $this->assertContains('An ["it","is","working"] test', $text); + } + + /** + * Test if a log entry contains all necessary information + */ + public function testContainsInformation() + { + $logfile = vfsStream::newFile('friendica.log') + ->at($this->root); + + $logger = new StreamLogger('test', $logfile->url(), $this->introspection); + + $logger->emergency('A test'); + + $text = $logfile->getContent(); + $this->assertContains('"process_id":' . getmypid(), $text); + $this->assertContains('"file":"' . self::FILE . '"', $text); + $this->assertContains('"line":' . self::LINE, $text); + $this->assertContains('"function":"' . self::FUNC . '"', $text); + } + + /** + * Test if the minimum level is working + */ + public function testMinimumLevel() + { + $logfile = vfsStream::newFile('friendica.log') + ->at($this->root); + + $logger = new StreamLogger('test', $logfile->url(), $this->introspection, LogLevel::NOTICE); + + $logger->emergency('working'); + $logger->alert('working'); + $logger->error('working'); + $logger->warning('working'); + $logger->notice('working'); + $logger->info('not working'); + $logger->debug('not working'); + + $text = $logfile->getContent(); + + $this->assertLoglineNums(5, $text); + } + + + /** + * Test if a file cannot get opened + * @expectedException \UnexpectedValueException + */ + public function testNoFile() + { + $logfile = vfsStream::newFile('friendica.log') + ->at($this->root) + ->chmod(0); + + $logger = new StreamLogger('test', $logfile->url(), $this->introspection); + + $logger->emergency('not working'); + } + + /** + * Test when a file isn't set + * @expectedException \LogicException + * @expectedExceptionMessage Missing stream URL. + */ + public function testNoUrl() + { + $logger = new StreamLogger('test', '', $this->introspection); + + $logger->emergency('not working'); + } + + /** + * Test when a file doesn't exist + * @expectedException \UnexpectedValueException + * @expectedExceptionMessageRegExp /The stream or file .* could not be opened: .* / + */ + public function testWrongUrl() + { + $logger = new StreamLogger('test', 'wrongfile', $this->introspection); + + $logger->emergency('not working'); + } + + /** + * Test when the directory cannot get created + * @expectedException \UnexpectedValueException + * @expectedExceptionMessageRegExp /Directory .* cannot get created: .* / + */ + public function testWrongDir() + { + $logger = new StreamLogger('test', 'a/wrong/directory/file.txt', $this->introspection); + + $logger->emergency('not working'); + } + + /** + * Test when the minimum level is not valid + * @expectedException \InvalidArgumentException + * @expectedExceptionMessageRegExp /The level ".*" is not valid./ + */ + public function testWrongMinimumLevel() + { + $logger = new StreamLogger('test', 'file.text', $this->introspection, 'NOPE'); + } + + /** + * Test when the minimum level is not valid + * @expectedException \InvalidArgumentException + * @expectedExceptionMessageRegExp /The level ".*" is not valid./ + */ + public function testWrongLogLevel() + { + $logfile = vfsStream::newFile('friendica.log') + ->at($this->root); + + $logger = new StreamLogger('test', $logfile->url(), $this->introspection); + + $logger->log('NOPE', 'a test'); + } +}