$this->checkStream();
- $this->stream = fopen($this->url, 'a');
$formattedLog = $this->formatLog($level, $message, $context);
fwrite($this->stream, $formattedLog);
}
use Friendica\Network\HTTPException\InternalServerErrorException;
use Friendica\Util\Introspection;
-use Psr\Log\InvalidArgumentException;
use Psr\Log\LogLevel;
/**
public function mapLevelToPriority($level)
{
if (!array_key_exists($level, $this->logLevels)) {
- throw new InvalidArgumentException('LogLevel \'' . $level . '\' isn\'t valid.');
+ throw new \InvalidArgumentException(sprintf('The level "%s" is not valid.', $level));
}
return $this->logLevels[$level];
throw new InternalServerErrorException('Can\'t open syslog for ident "' . $this->channel . '" and facility "' . $this->logFacility . '""');
}
- syslog($priority, $message);
+ $this->syslogWrapper($priority, $message);
}
/**
return $logMessage;
}
+
+ /**
+ * A syslog wrapper to make syslog functionality testable
+ *
+ * @param int $level The syslog priority
+ * @param string $entry The message to send to the syslog function
+ */
+ protected function syslogWrapper($level, $entry)
+ {
+ syslog($level, $entry);
+ }
}
--- /dev/null
+<?php
+
+namespace Friendica\Test\src\Util\Logger;
+
+use Friendica\Test\MockedTest;
+use Friendica\Util\Introspection;
+use Mockery\MockInterface;
+use Psr\Log\LoggerInterface;
+use Psr\Log\LogLevel;
+
+abstract class AbstractLoggerTest extends MockedTest
+{
+ use LoggerDataTrait;
+
+ const LOGLINE = '/.* \[.*\]: .* \{.*\"file\":\".*\".*,.*\"line\":\d*,.*\"function\":\".*\".*,.*\"uid\":\".*\".*}/';
+
+ const FILE = 'test';
+ const LINE = 666;
+ const FUNC = 'myfunction';
+
+ /**
+ * @var Introspection|MockInterface
+ */
+ protected $introspection;
+
+ /**
+ * Returns the content of the current logger instance
+ *
+ * @return string
+ */
+ abstract protected function getContent();
+
+ /**
+ * Returns the current logger instance
+ *
+ * @param string $level the default loglevel
+ *
+ * @return LoggerInterface
+ */
+ abstract protected function getInstance($level = LogLevel::DEBUG);
+
+ protected function setUp()
+ {
+ parent::setUp();
+
+ $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));
+ }
+
+ /**
+ * Test if the logger works correctly
+ */
+ public function testNormal()
+ {
+ $logger = $this->getInstance();
+ $logger->emergency('working!');
+ $logger->alert('working too!');
+ $logger->debug('and now?');
+ $logger->notice('message', ['an' => 'context']);
+
+ $text = $this->getContent();
+ $this->assertLogline($text);
+ $this->assertLoglineNums(4, $text);
+ }
+
+ /**
+ * Test if a log entry is correctly interpolated
+ */
+ public function testPsrInterpolate()
+ {
+ $logger = $this->getInstance();
+
+ $logger->emergency('A {psr} test', ['psr' => 'working']);
+ $logger->alert('An {array} test', ['array' => ['it', 'is', 'working']]);
+ $text = $this->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()
+ {
+ $logger = $this->getInstance();
+ $logger->emergency('A test');
+
+ $text = $this->getContent();
+ $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()
+ {
+ $logger = $this->getInstance(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 = $this->getContent();
+
+ $this->assertLoglineNums(5, $text);
+ }
+
+ /**
+ * Test with different logging data
+ * @dataProvider dataTests
+ */
+ public function testDifferentTypes($function, $message, array $context)
+ {
+ $logger = $this->getInstance();
+ $logger->$function($message, $context);
+
+ $text = $this->getContent();
+
+ $this->assertLogline($text);
+
+ $this->assertContains(@json_encode($context), $text);
+ }
+}
--- /dev/null
+<?php
+
+namespace Friendica\Test\src\Util\Logger;
+
+trait LoggerDataTrait
+{
+ public function dataTests()
+ {
+ return [
+ 'emergency' => [
+ 'function' => 'emergency',
+ 'message' => 'test',
+ 'context' => ['a' => 'context'],
+ ],
+ 'alert' => [
+ 'function' => 'alert',
+ 'message' => 'test {test}',
+ 'context' => ['a' => 'context', 2 => 'so', 'test' => 'works'],
+ ],
+ 'critical' => [
+ 'function' => 'critical',
+ 'message' => 'test crit 2345',
+ 'context' => ['a' => 'context', 'wit' => ['more', 'array']],
+ ],
+ 'error' => [
+ 'function' => 'error',
+ 'message' => 2.554,
+ 'context' => [],
+ ],
+ 'warning' => [
+ 'function' => 'warning',
+ 'message' => 'test warn',
+ 'context' => ['a' => 'context'],
+ ],
+ 'notice' => [
+ 'function' => 'notice',
+ 'message' => 2346,
+ 'context' => ['a' => 'context'],
+ ],
+ 'info' => [
+ 'function' => 'info',
+ 'message' => null,
+ 'context' => ['a' => 'context'],
+ ],
+ 'debug' => [
+ 'function' => 'debug',
+ 'message' => true,
+ 'context' => ['a' => false],
+ ],
+ ];
+ }
+}
--- /dev/null
+<?php
+
+namespace Friendica\Test\src\Util\Logger;
+
+use Friendica\Test\MockedTest;
+use Friendica\Util\Logger\ProfilerLogger;
+use Friendica\Util\Profiler;
+use Mockery\MockInterface;
+use Psr\Log\LoggerInterface;
+use Psr\Log\LogLevel;
+
+class ProfilerLoggerTest extends MockedTest
+{
+ use LoggerDataTrait;
+
+ /**
+ * @var LoggerInterface|MockInterface
+ */
+ private $logger;
+ /**
+ * @var Profiler|MockInterface
+ */
+ private $profiler;
+
+ protected function setUp()
+ {
+ parent::setUp();
+
+ $this->logger = \Mockery::mock(LoggerInterface::class);
+ $this->profiler = \Mockery::mock(Profiler::class);
+ }
+
+ /**
+ * Test if the profiler is profiling data
+ * @dataProvider dataTests
+ */
+ public function testProfiling($function, $message, array $context)
+ {
+ $logger = new ProfilerLogger($this->logger, $this->profiler);
+
+ $this->logger->shouldReceive($function)->with($message, $context)->once();
+ $this->profiler->shouldReceive('saveTimestamp')->with(\Mockery::any(), 'file', \Mockery::any())->once();
+ $logger->$function($message, $context);
+ }
+
+ /**
+ * Test the log() function
+ */
+ public function testProfilingLog()
+ {
+ $logger = new ProfilerLogger($this->logger, $this->profiler);
+
+ $this->logger->shouldReceive('log')->with(LogLevel::WARNING, 'test', ['a' => 'context'])->once();
+ $this->profiler->shouldReceive('saveTimestamp')->with(\Mockery::any(), 'file', \Mockery::any())->once();
+
+ $logger->log(LogLevel::WARNING, 'test', ['a' => 'context']);
+ }
+}
namespace Friendica\Test\src\Util\Logger;
-use Friendica\Test\MockedTest;
use Friendica\Test\Util\VFSTrait;
-use Friendica\Util\Introspection;
use Friendica\Util\Logger\StreamLogger;
-use Mockery\MockInterface;
use org\bovigo\vfs\vfsStream;
+use org\bovigo\vfs\vfsStreamFile;
use Psr\Log\LogLevel;
-class StreamLoggerTest extends MockedTest
+class StreamLoggerTest extends AbstractLoggerTest
{
- const LOGLINE = '/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} .* \[.*\]: .* \{.*\"file\":\".*\".*,.*\"line\":\d*,.*\"function\":\".*\".*,.*\"uid\":\".*\".*,.*\"process_id\":\d*.*\}/';
-
- const FILE = 'test';
- const LINE = 666;
- const FUNC = 'myfunction';
-
use VFSTrait;
/**
- * @var Introspection|MockInterface
+ * @var StreamLogger
+ */
+ private $logger;
+
+ /**
+ * @var vfsStreamFile
*/
- private $introspection;
+ private $logfile;
protected function setUp()
{
parent::setUp();
$this->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()
+ /**
+ * {@@inheritdoc}
+ */
+ protected function getInstance($level = LogLevel::DEBUG)
{
- $logfile = vfsStream::newFile('friendica.log')
+ $this->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']);
+ $this->logger = new StreamLogger('test', $this->logfile->url(), $this->introspection, $level);
- $text = $logfile->getContent();
- $this->assertLogline($text);
- $this->assertLoglineNums(4, $text);
+ return $this->logger;
}
/**
- * Test if a log entry is correctly interpolated
+ * {@inheritdoc}
*/
- public function testPsrInterpolate()
+ protected function getContent()
{
- $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);
+ return $this->logfile->getContent();
}
/**
- * Test if a log entry contains all necessary information
+ * Test if a stream is working
*/
- public function testContainsInformation()
+ public function testStream()
{
$logfile = vfsStream::newFile('friendica.log')
->at($this->root);
- $logger = new StreamLogger('test', $logfile->url(), $this->introspection);
+ $filehandler = fopen($logfile->url(), 'ab');
- $logger->emergency('A test');
+ $logger = new StreamLogger('test', $filehandler, $this->introspection);
+ $logger->emergency('working');
$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);
+
+ $this->assertLogline($text);
}
/**
- * Test if the minimum level is working
+ * Test if the close statement is working
*/
- public function testMinimumLevel()
+ public function testClose()
{
$logfile = vfsStream::newFile('friendica.log')
->at($this->root);
- $logger = new StreamLogger('test', $logfile->url(), $this->introspection, LogLevel::NOTICE);
-
+ $logger = new StreamLogger('test', $logfile->url(), $this->introspection);
$logger->emergency('working');
- $logger->alert('working');
- $logger->error('working');
- $logger->warning('working');
- $logger->notice('working');
- $logger->info('not working');
- $logger->debug('not working');
+ $logger->close();
+ // close doesn't affect
+ $logger->emergency('working too');
$text = $logfile->getContent();
- $this->assertLoglineNums(5, $text);
+ $this->assertLoglineNums(2, $text);
}
/**
$logger->log('NOPE', 'a test');
}
+
+ /**
+ * Test when the file is null
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage A stream must either be a resource or a string.
+ */
+ public function testWrongFile()
+ {
+ $logger = new StreamLogger('test', null, $this->introspection);
+ }
}
--- /dev/null
+<?php
+
+namespace Friendica\Test\src\Util\Logger;
+
+use Friendica\Util\Logger\SyslogLogger;
+use Psr\Log\LogLevel;
+
+class SyslogLoggerTest extends AbstractLoggerTest
+{
+ /**
+ * @var SyslogLoggerWrapper
+ */
+ private $logger;
+
+ protected function setUp()
+ {
+ parent::setUp();
+
+ $this->introspection->shouldReceive('addClasses')->with([SyslogLogger::class]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getContent()
+ {
+ return $this->logger->getContent();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function getInstance($level = LogLevel::DEBUG)
+ {
+ $this->logger = new SyslogLoggerWrapper('test', $this->introspection, $level);
+
+ return $this->logger;
+ }
+
+
+ /**
+ * Test when the minimum level is not valid
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessageRegExp /The level ".*" is not valid./
+ */
+ public function testWrongMinimumLevel()
+ {
+ $logger = new SyslogLoggerWrapper('test', $this->introspection, 'NOPE');
+ }
+
+ /**
+ * Test when the minimum level is not valid
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessageRegExp /The level ".*" is not valid./
+ */
+ public function testWrongLogLevel()
+ {
+ $logger = new SyslogLoggerWrapper('test', $this->introspection);
+
+ $logger->log('NOPE', 'a test');
+ }
+}
--- /dev/null
+<?php
+
+namespace Friendica\Test\src\Util\Logger;
+
+use Friendica\Util\Introspection;
+use Friendica\Util\Logger\SyslogLogger;
+use Psr\Log\LogLevel;
+
+class SyslogLoggerWrapper extends SyslogLogger
+{
+ private $content;
+
+ public function __construct($channel, Introspection $introspection, $level = LogLevel::NOTICE, $logOpts = LOG_PID, $logFacility = LOG_USER)
+ {
+ parent::__construct($channel, $introspection, $level, $logOpts, $logFacility);
+
+ $this->content = '';
+ }
+
+ /**
+ * Gets the content from the wrapped Syslog
+ *
+ * @return string
+ */
+ public function getContent()
+ {
+ return $this->content;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function syslogWrapper($level, $entry)
+ {
+ $this->content .= $entry . PHP_EOL;
+ }
+}
--- /dev/null
+<?php
+
+namespace Friendica\Test\src\Util\Logger;
+
+use Friendica\Test\MockedTest;
+use Friendica\Util\Logger\VoidLogger;
+use Psr\Log\LogLevel;
+
+class VoidLoggerTest extends MockedTest
+{
+ use LoggerDataTrait;
+
+ /**
+ * Test if the profiler is profiling data
+ * @dataProvider dataTests
+ */
+ public function testNormal($function, $message, array $context)
+ {
+ $logger = new VoidLogger();
+ $logger->$function($message, $context);
+ }
+
+ /**
+ * Test the log() function
+ */
+ public function testProfilingLog()
+ {
+ $logger = new VoidLogger();
+ $logger->log(LogLevel::WARNING, 'test', ['a' => 'context']);
+ }
+}