diff --git a/DependencyInjection/Compiler/RequestIdentifierPass.php b/DependencyInjection/Compiler/RequestIdentifierPass.php
new file mode 100644
index 0000000..0e6bd9b
--- /dev/null
+++ b/DependencyInjection/Compiler/RequestIdentifierPass.php
@@ -0,0 +1,28 @@
+
+ */
+class RequestIdentifierPass implements CompilerPassInterface
+{
+
+ /**
+ * @inheritdoc
+ */
+ public function process(ContainerBuilder $container)
+ {
+ if (!$container->hasParameter('onelog.enable_request_id') || true === $container->getParameter('onelog.enable_request_id')) {
+ return;
+ }
+
+ $container->removeDefinition(RequestIdProcessor::class);
+ }
+}
diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php
index 96525d3..1cbdbc0 100644
--- a/DependencyInjection/Configuration.php
+++ b/DependencyInjection/Configuration.php
@@ -40,6 +40,11 @@ public function getConfigTreeBuilder()
->defaultFalse()
->treatNullLike(false)
->end()
+ ->booleanNode('enable_request_id')
+ ->info('Add a request identifier to all log entries. Allows for easier tracking of logs during a request')
+ ->defaultTrue()
+ ->treatNullLike(true)
+ ->end()
->end();
return $treeBuilder;
diff --git a/DependencyInjection/OnelogExtension.php b/DependencyInjection/OnelogExtension.php
index 3cecad4..7d75332 100644
--- a/DependencyInjection/OnelogExtension.php
+++ b/DependencyInjection/OnelogExtension.php
@@ -28,5 +28,6 @@ public function load(array $configs, ContainerBuilder $container)
$container->setParameter('onelog.logger_service', $config['logger_service']);
$container->setParameter('onelog.register_global', $config['register_global']);
$container->setParameter('onelog.register_monolog_channels', $config['register_monolog_channels']);
+ $container->setParameter('onelog.enable_request_id', $config['enable_request_id']);
}
}
diff --git a/Helper/OneLogStatic.php b/Helper/OneLogStatic.php
index b593406..d9d2bf9 100644
--- a/Helper/OneLogStatic.php
+++ b/Helper/OneLogStatic.php
@@ -65,10 +65,6 @@ public static function destroy()
*/
public static function __callStatic(string $level, $params)
{
- if (!static::$resolved instanceof OneLog) {
- throw new \RuntimeException('Logger is not properly instantiated!');
- }
-
- return self::$resolved->{$level}(...$params);
+ return self::instance()->{$level}(...$params);
}
}
diff --git a/Monolog/RequestIdProcessor.php b/Monolog/RequestIdProcessor.php
new file mode 100644
index 0000000..c31d158
--- /dev/null
+++ b/Monolog/RequestIdProcessor.php
@@ -0,0 +1,50 @@
+
+ */
+class RequestIdProcessor
+{
+
+ /**
+ * @var int
+ */
+ private $logCount = 1;
+
+ /**
+ * @var RequestId
+ */
+ private $requestId;
+
+ /**
+ * RequestIdProcessor constructor.
+ *
+ * @param IdentifierInterface $requestId
+ */
+ public function __construct(IdentifierInterface $requestId = null)
+ {
+ $this->requestId = $requestId ?? RequestId::generate();
+ }
+
+ /**
+ * Add the request id to the log entry
+ *
+ * @param array $record
+ *
+ * @return array
+ */
+ public function __invoke(array $record): array
+ {
+ $record['extra']['request_id'] = $this->requestId->identifier() . '.' . $this->logCount;
+ $this->logCount++;
+
+ return $record;
+ }
+}
\ No newline at end of file
diff --git a/OnelogBundle.php b/OnelogBundle.php
index 46dd781..f43a7f2 100644
--- a/OnelogBundle.php
+++ b/OnelogBundle.php
@@ -4,6 +4,7 @@
use KoderHut\OnelogBundle\DependencyInjection\Compiler\LoggerWrapPass;
use KoderHut\OnelogBundle\DependencyInjection\Compiler\RegisterMonologChannels;
+use KoderHut\OnelogBundle\DependencyInjection\Compiler\RequestIdentifierPass;
use KoderHut\OnelogBundle\Helper\GlobalNamespaceRegister;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
@@ -29,6 +30,7 @@ public function build(ContainerBuilder $container)
{
$container->addCompilerPass(new LoggerWrapPass());
$container->addCompilerPass(new RegisterMonologChannels());
+ $container->addCompilerPass(new RequestIdentifierPass());
}
}
diff --git a/PSRLoggerTrait.php b/PSRLoggerTrait.php
index b2ee0e6..7b58ab1 100644
--- a/PSRLoggerTrait.php
+++ b/PSRLoggerTrait.php
@@ -9,11 +9,12 @@
*/
trait PSRLoggerTrait
{
+
/**
* @param mixed $message
* @param array $context
*/
- public function emergency($message, array $context = array())
+ public function emergency($message, array $context = [])
{
$this->defaultLogger->emergency($message, $context);
}
@@ -22,7 +23,7 @@ public function emergency($message, array $context = array())
* @param mixed $message
* @param array $context
*/
- public function alert($message, array $context = array())
+ public function alert($message, array $context = [])
{
$this->defaultLogger->alert($message, $context);
}
@@ -31,7 +32,7 @@ public function alert($message, array $context = array())
* @param mixed $message
* @param array $context
*/
- public function critical($message, array $context = array())
+ public function critical($message, array $context = [])
{
$this->defaultLogger->critical($message, $context);
}
@@ -40,7 +41,7 @@ public function critical($message, array $context = array())
* @param mixed $message
* @param array $context
*/
- public function error($message, array $context = array())
+ public function error($message, array $context = [])
{
$this->defaultLogger->error($message, $context);
}
@@ -49,7 +50,7 @@ public function error($message, array $context = array())
* @param mixed $message
* @param array $context
*/
- public function warning($message, array $context = array())
+ public function warning($message, array $context = [])
{
$this->defaultLogger->warning($message, $context);
}
@@ -58,7 +59,7 @@ public function warning($message, array $context = array())
* @param mixed $message
* @param array $context
*/
- public function notice($message, array $context = array())
+ public function notice($message, array $context = [])
{
$this->defaultLogger->notice($message, $context);
}
@@ -67,7 +68,7 @@ public function notice($message, array $context = array())
* @param mixed $message
* @param array $context
*/
- public function info($message, array $context = array())
+ public function info($message, array $context = [])
{
$this->defaultLogger->info($message, $context);
}
@@ -76,7 +77,7 @@ public function info($message, array $context = array())
* @param mixed $message
* @param array $context
*/
- public function debug($message, array $context = array())
+ public function debug($message, array $context = [])
{
$this->defaultLogger->debug($message, $context);
}
@@ -86,8 +87,8 @@ public function debug($message, array $context = array())
* @param mixed $message
* @param array $context
*/
- public function log($level, $message, array $context = array())
+ public function log($level, $message, array $context = [])
{
- return $this->defaultLogger->log($level, $message, $context);
+ $this->defaultLogger->log($level, $message, $context);
}
}
diff --git a/Request/Identifier.php b/Request/Identifier.php
new file mode 100644
index 0000000..fb1266a
--- /dev/null
+++ b/Request/Identifier.php
@@ -0,0 +1,55 @@
+
+ */
+class Identifier implements IdentifierInterface
+{
+
+ /**
+ * @var string
+ */
+ private $identifier;
+
+ /**
+ * Identifier constructor.
+ *
+ * @param string $identifier
+ */
+ public function __construct(string $identifier)
+ {
+ $this->identifier = $identifier;
+ }
+
+ /**
+ * It will generate an instance based on the current date
+ * using the format YmdHis.u
+ *
+ * @param string ...$salts
+ *
+ * @return Identifier
+ */
+ public static function generate(string ...$salts)
+ {
+ $hashData = (string) (new \DateTime())->format('Ymd.His.u');
+
+ if (!empty($salts)) {
+ $hashData .= '.' . implode('.', $salts);
+ }
+
+ return new self($hashData);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function identifier(): string
+ {
+ return $this->identifier;
+ }
+
+}
diff --git a/Request/IdentifierInterface.php b/Request/IdentifierInterface.php
new file mode 100644
index 0000000..610944d
--- /dev/null
+++ b/Request/IdentifierInterface.php
@@ -0,0 +1,19 @@
+
+ */
+interface IdentifierInterface
+{
+
+ /**
+ * Return the current identifier
+ *
+ * @return string
+ */
+ public function identifier(): string;
+}
diff --git a/Resources/config/onelog.xml b/Resources/config/onelog.xml
index 63a7a01..c4a3c0c 100644
--- a/Resources/config/onelog.xml
+++ b/Resources/config/onelog.xml
@@ -11,6 +11,10 @@
+
+
+
+
diff --git a/Tests/DependencyInjection/Compiler/RequestIdentifierPassTest.php b/Tests/DependencyInjection/Compiler/RequestIdentifierPassTest.php
new file mode 100644
index 0000000..7994a55
--- /dev/null
+++ b/Tests/DependencyInjection/Compiler/RequestIdentifierPassTest.php
@@ -0,0 +1,69 @@
+prophesize(ContainerBuilder::class);
+ $container->hasParameter('onelog.enable_request_id')
+ ->shouldBeCalled()
+ ->willReturn(false)
+ ;
+
+ $instance = new RequestIdentifierPass();
+
+ $instance->process($container->reveal());
+ }
+
+ /**
+ * @test
+ */
+ public function testExitEarlyIfConfigParamIsMissing()
+ {
+ $container = $this->prophesize(ContainerBuilder::class);
+ $container->hasParameter('onelog.enable_request_id')
+ ->shouldBeCalled()
+ ->willReturn(true)
+ ;
+ $container->getParameter('onelog.enable_request_id')
+ ->shouldBeCalled()
+ ->willReturn(true)
+ ;
+
+ $instance = new RequestIdentifierPass();
+
+ $instance->process($container->reveal());
+ }
+
+ /**
+ * @test
+ */
+ public function testRemoveRequestIdServiceIfConfigIsSetToFalse()
+ {
+ $container = $this->prophesize(ContainerBuilder::class);
+ $container->hasParameter('onelog.enable_request_id')
+ ->shouldBeCalled()
+ ->willReturn(true)
+ ;
+ $container->getParameter('onelog.enable_request_id')
+ ->shouldBeCalled()
+ ->willReturn(false)
+ ;
+ $container->removeDefinition(RequestIdProcessor::class)->shouldBeCalled();
+
+ $instance = new RequestIdentifierPass();
+
+ $instance->process($container->reveal());
+ }
+}
diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php
index 928cd44..91d7612 100644
--- a/Tests/DependencyInjection/ConfigurationTest.php
+++ b/Tests/DependencyInjection/ConfigurationTest.php
@@ -34,12 +34,26 @@ public function optionsProvider()
{
return [
'all_configs' => [
- ['onelog' => ['logger_service' => 'monolog', 'register_global' => true, 'register_monolog_channels' => false]],
- ['logger_service' => 'monolog', 'register_global' => true, 'register_monolog_channels' => false],
+ ['onelog' => [
+ 'logger_service' => 'monolog',
+ 'register_global' => true,
+ 'register_monolog_channels' => false,
+ ]],
+ [
+ 'logger_service' => 'monolog',
+ 'register_global' => true,
+ 'register_monolog_channels' => false,
+ 'enable_request_id' => true,
+ ],
],
'default_configs' => [
['onelog' => ['logger_service' => null]],
- ['logger_service' => 'logger', 'register_global' => false, 'register_monolog_channels' => false],
+ [
+ 'logger_service' => 'logger',
+ 'register_global' => false,
+ 'register_monolog_channels' => false,
+ 'enable_request_id' => true,
+ ],
],
];
}
diff --git a/Tests/Helper/OneLogStaticTest.php b/Tests/Helper/OneLogStaticTest.php
new file mode 100644
index 0000000..c6d51bc
--- /dev/null
+++ b/Tests/Helper/OneLogStaticTest.php
@@ -0,0 +1,65 @@
+expectException(\RuntimeException::class);
+ $this->expectExceptionMessage('OneLog is not properly instantiated!');
+
+ OneLogStatic::instance();
+ }
+
+ /**
+ * @test
+ */
+ public function testCallingPsrLoggerMethodsAreProxiedToLoggerInstance()
+ {
+ $logger = $this->prophesize(OneLog::class);
+ $logger->debug('test', ['test'])->shouldBeCalled();
+
+ OneLogStatic::setInstance($logger->reveal());
+ OneLogStatic::debug('test', ['test']);
+ }
+
+ /**
+ * @test
+ */
+ public function testDestroyClearsLoggerInstance()
+ {
+ $logger = $this->prophesize(OneLog::class);
+
+ OneLogStatic::setInstance($logger->reveal());
+
+ $this->assertSame($logger->reveal(), OneLogStatic::instance());
+
+ OneLogStatic::destroy();
+
+ $this->expectException(\RuntimeException::class);
+ $this->expectExceptionMessage('OneLog is not properly instantiated!');
+
+ OneLogStatic::instance();
+ }
+
+ /**
+ * @test
+ */
+ public function testInstanceCannotBeCreated()
+ {
+ $this->expectException(\Error::class);
+ $this->expectExceptionMessage("Call to private KoderHut\OnelogBundle\Helper\OneLogStatic::__construct() from context 'KoderHut\OneLogBundle\Tests\Helper\OneLogStaticTest'");
+
+ $instance = new OneLogStatic();
+ }
+}
diff --git a/Tests/Monolog/RequestIdProcessorTest.php b/Tests/Monolog/RequestIdProcessorTest.php
new file mode 100644
index 0000000..b5a667d
--- /dev/null
+++ b/Tests/Monolog/RequestIdProcessorTest.php
@@ -0,0 +1,65 @@
+format('Ymd');
+ $time = $dateTime->format('Hi');
+ $format = "${date}\.${time}[0-5]{1}[0-9]{1}\.[0-9]{6}\.1";
+
+ $instance = new RequestIdProcessor();
+
+ $result = $instance([]);
+
+ $this->assertArrayHasKey('extra', $result);
+ $this->assertArrayHasKey('request_id', $result['extra']);
+ $this->assertRegExp("/${format}/", $result['extra']['request_id']);
+ }
+
+ /**
+ * @test
+ */
+ public function testInstanceAcceptSpecificIdentifier()
+ {
+ $identifier = $this->prophesize(IdentifierInterface::class);
+ $identifier->identifier()->shouldBeCalled()->willReturn('test');
+ $instance = new RequestIdProcessor($identifier->reveal());
+
+ $result = $instance([]);
+ $this->assertArrayHasKey('extra', $result);
+ $this->assertArrayHasKey('request_id', $result['extra']);
+ $this->assertEquals('test.1', $result['extra']['request_id']);
+ }
+
+ /**
+ * @test
+ */
+ public function testMultipleLogEntriesWillBeMarkedIncrementally()
+ {
+ $identifier = $this->prophesize(IdentifierInterface::class);
+ $identifier->identifier()->shouldBeCalled()->willReturn('test');
+ $instance = new RequestIdProcessor($identifier->reveal());
+
+ $result = $instance([]);
+ $this->assertArrayHasKey('extra', $result);
+ $this->assertArrayHasKey('request_id', $result['extra']);
+ $this->assertEquals('test.1', $result['extra']['request_id']);
+
+ $result = $instance([]);
+ $this->assertArrayHasKey('extra', $result);
+ $this->assertArrayHasKey('request_id', $result['extra']);
+ $this->assertEquals('test.2', $result['extra']['request_id']);
+ }
+}
diff --git a/Tests/PSRLoggerTraitTest.php b/Tests/PSRLoggerTraitTest.php
new file mode 100644
index 0000000..120090b
--- /dev/null
+++ b/Tests/PSRLoggerTraitTest.php
@@ -0,0 +1,88 @@
+mockLogger = $this->prophesize(LoggerInterface::class);
+ $this->instance = new MockPSRLoggerTrait($this->mockLogger->reveal());
+ }
+
+ /**
+ * @test
+ *
+ * @dataProvider loggerMethods
+ *
+ * @param string $method
+ */
+ public function testMethodsAreDispatchedToLogger(string $method)
+ {
+ $this->mockLogger->{$method}('test', [])->shouldBeCalled();
+
+ $this->instance->{$method}('test', []);
+ }
+
+ /**
+ * @test
+ */
+ public function testCallingLogIsProxiedToLogger()
+ {
+ $this->mockLogger->log('alert', 'test', [])->shouldBeCalled();
+
+ $this->instance->log('alert', 'test', []);
+ }
+ /**
+ * @see testMethodsAreDispatchedToLogger
+ *
+ * @return array
+ */
+ public function loggerMethods()
+ {
+ return [
+ 'emergency' => ['emergency'],
+ 'alert' => ['alert'],
+ 'critical' => ['critical'],
+ 'error' => ['error'],
+ 'warning' => ['warning'],
+ 'notice' => ['notice'],
+ 'info' => ['info'],
+ 'debug' => ['debug'],
+ ];
+ }
+}
+
+/**
+ * Class MockPSRLoggerTrait
+ *
+ * @author Denis-Florin Rendler
+ */
+class MockPSRLoggerTrait
+{
+ use PSRLoggerTrait;
+
+ protected $defaultLogger;
+
+ public function __construct($logger)
+ {
+ $this->defaultLogger = $logger;
+ }
+}
diff --git a/Tests/Request/IdentifierTest.php b/Tests/Request/IdentifierTest.php
new file mode 100644
index 0000000..d980b16
--- /dev/null
+++ b/Tests/Request/IdentifierTest.php
@@ -0,0 +1,56 @@
+
+ */
+class IdentifierTest extends TestCase
+{
+
+ /**
+ * @test
+ */
+ public function testCanProvideSpecificIdentifierString()
+ {
+ $instance = new Identifier('test');
+
+ $this->assertEquals('test', $instance->identifier());
+ }
+
+ /**
+ * @test
+ */
+ public function testGeneratingInstanceFromStaticCall()
+ {
+ $dateTime = new \DateTime();
+ $date = $dateTime->format('Ymd');
+ $time = $dateTime->format('Hi');
+ $format = "${date}\.${time}[0-5]{1}[0-9]{1}\.[0-9]{6}";
+ $instance = Identifier::generate();
+
+ $this->assertInstanceOf(IdentifierInterface::class, $instance);
+ $this->assertRegExp("/${format}/", $instance->identifier());
+ }
+
+ /**
+ * @test
+ */
+ public function testAddingSaltsToIdentifier()
+ {
+ $dateTime = new \DateTime();
+ $date = $dateTime->format('Ymd');
+ $time = $dateTime->format('Hi');
+ $format = "${date}\.${time}[0-5]{1}[0-9]{1}\.[0-9]{6}\.test1\.test2";
+ $instance = Identifier::generate('test1', 'test2');
+
+ $this->assertInstanceOf(IdentifierInterface::class, $instance);
+ $this->assertRegExp("/${format}/", $instance->identifier());
+ }
+}