Skip to content

Commit a124770

Browse files
vtsykunnicolas-grekas
authored andcommitted
LoggerDataCollector: splitting logs on different sub-requests
1 parent 7cd5e43 commit a124770

File tree

9 files changed

+183
-19
lines changed

9 files changed

+183
-19
lines changed

src/Symfony/Bridge/Monolog/Logger.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Bridge\Monolog;
1313

1414
use Monolog\Logger as BaseLogger;
15+
use Symfony\Component\HttpFoundation\Request;
1516
use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
1617

1718
/**
@@ -24,10 +25,10 @@ class Logger extends BaseLogger implements DebugLoggerInterface
2425
/**
2526
* {@inheritdoc}
2627
*/
27-
public function getLogs()
28+
public function getLogs(/* Request $request = null */)
2829
{
2930
if ($logger = $this->getDebugLogger()) {
30-
return $logger->getLogs();
31+
return \call_user_func_array(array($logger, 'getLogs'), \func_get_args());
3132
}
3233

3334
return array();
@@ -36,10 +37,10 @@ public function getLogs()
3637
/**
3738
* {@inheritdoc}
3839
*/
39-
public function countErrors()
40+
public function countErrors(/* Request $request = null */)
4041
{
4142
if ($logger = $this->getDebugLogger()) {
42-
return $logger->countErrors();
43+
return \call_user_func_array(array($logger, 'countErrors'), \func_get_args());
4344
}
4445

4546
return 0;

src/Symfony/Bridge/Monolog/Processor/DebugProcessor.php

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,29 +12,44 @@
1212
namespace Symfony\Bridge\Monolog\Processor;
1313

1414
use Monolog\Logger;
15+
use Symfony\Component\HttpFoundation\Request;
16+
use Symfony\Component\HttpFoundation\RequestStack;
1517
use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
1618

1719
class DebugProcessor implements DebugLoggerInterface
1820
{
1921
private $records = array();
20-
private $errorCount = 0;
22+
private $errorCount = array();
23+
private $requestStack;
24+
25+
public function __construct(RequestStack $requestStack = null)
26+
{
27+
$this->requestStack = $requestStack;
28+
}
2129

2230
public function __invoke(array $record)
2331
{
24-
$this->records[] = array(
32+
$hash = $this->requestStack && ($request = $this->requestStack->getCurrentRequest()) ? spl_object_hash($request) : '';
33+
34+
$this->records[$hash][] = array(
2535
'timestamp' => $record['datetime']->getTimestamp(),
2636
'message' => $record['message'],
2737
'priority' => $record['level'],
2838
'priorityName' => $record['level_name'],
2939
'context' => $record['context'],
3040
'channel' => isset($record['channel']) ? $record['channel'] : '',
3141
);
42+
43+
if (!isset($this->errorCount[$hash])) {
44+
$this->errorCount[$hash] = 0;
45+
}
46+
3247
switch ($record['level']) {
3348
case Logger::ERROR:
3449
case Logger::CRITICAL:
3550
case Logger::ALERT:
3651
case Logger::EMERGENCY:
37-
++$this->errorCount;
52+
++$this->errorCount[$hash];
3853
}
3954

4055
return $record;
@@ -43,17 +58,25 @@ public function __invoke(array $record)
4358
/**
4459
* {@inheritdoc}
4560
*/
46-
public function getLogs()
61+
public function getLogs(/* Request $request = null */)
4762
{
48-
return $this->records;
63+
if (1 <= \func_num_args() && null !== ($request = \func_get_arg(0)) && isset($this->records[$hash = spl_object_hash($request)])) {
64+
return $this->records[$hash];
65+
}
66+
67+
return $this->records ? \call_user_func_array('array_merge', $this->records) : array();
4968
}
5069

5170
/**
5271
* {@inheritdoc}
5372
*/
54-
public function countErrors()
73+
public function countErrors(/* Request $request = null */)
5574
{
56-
return $this->errorCount;
75+
if (1 <= \func_num_args() && null !== ($request = \func_get_arg(0)) && isset($this->errorCount[$hash = spl_object_hash($request)])) {
76+
return $this->errorCount[$hash];
77+
}
78+
79+
return array_sum($this->errorCount);
5780
}
5881

5982
/**
@@ -62,6 +85,6 @@ public function countErrors()
6285
public function clear()
6386
{
6487
$this->records = array();
65-
$this->errorCount = 0;
88+
$this->errorCount = array();
6689
}
6790
}

src/Symfony/Bridge/Monolog/Tests/LoggerTest.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Symfony\Bridge\Monolog\Handler\DebugHandler;
1717
use Symfony\Bridge\Monolog\Processor\DebugProcessor;
1818
use Symfony\Bridge\Monolog\Logger;
19+
use Symfony\Component\HttpFoundation\Request;
1920

2021
class LoggerTest extends TestCase
2122
{
@@ -129,6 +130,21 @@ public function testGetLogsWithDebugProcessor2()
129130
$this->assertEquals(Logger::INFO, $record['priority']);
130131
}
131132

133+
public function testGetLogsWithDebugProcessor3()
134+
{
135+
$request = new Request();
136+
$processor = $this->getMockBuilder(DebugProcessor::class)->getMock();
137+
$processor->expects($this->once())->method('getLogs')->with($request);
138+
$processor->expects($this->once())->method('countErrors')->with($request);
139+
140+
$handler = new TestHandler();
141+
$logger = new Logger('test', array($handler));
142+
$logger->pushProcessor($processor);
143+
144+
$logger->getLogs($request);
145+
$logger->countErrors($request);
146+
}
147+
132148
public function testClear()
133149
{
134150
$handler = new TestHandler();
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bridge\Monolog\Tests\Processor;
13+
14+
use Monolog\Logger;
15+
use PHPUnit\Framework\TestCase;
16+
use Symfony\Bridge\Monolog\Processor\DebugProcessor;
17+
use Symfony\Component\HttpFoundation\Request;
18+
use Symfony\Component\HttpFoundation\RequestStack;
19+
20+
class DebugProcessorTest extends TestCase
21+
{
22+
public function testDebugProcessor()
23+
{
24+
$processor = new DebugProcessor();
25+
$processor($this->getRecord());
26+
$processor($this->getRecord(Logger::ERROR));
27+
28+
$this->assertCount(2, $processor->getLogs());
29+
$this->assertSame(1, $processor->countErrors());
30+
}
31+
32+
public function testWithRequestStack()
33+
{
34+
$stack = new RequestStack();
35+
$processor = new DebugProcessor($stack);
36+
$processor($this->getRecord());
37+
$processor($this->getRecord(Logger::ERROR));
38+
39+
$this->assertCount(2, $processor->getLogs());
40+
$this->assertSame(1, $processor->countErrors());
41+
42+
$request = new Request();
43+
$stack->push($request);
44+
45+
$processor($this->getRecord());
46+
$processor($this->getRecord(Logger::ERROR));
47+
48+
$this->assertCount(4, $processor->getLogs());
49+
$this->assertSame(2, $processor->countErrors());
50+
51+
$this->assertCount(2, $processor->getLogs($request));
52+
$this->assertSame(1, $processor->countErrors($request));
53+
}
54+
55+
private function getRecord($level = Logger::WARNING, $message = 'test')
56+
{
57+
return array(
58+
'message' => $message,
59+
'context' => array(),
60+
'level' => $level,
61+
'level_name' => Logger::getLevelName($level),
62+
'channel' => 'test',
63+
'datetime' => new \DateTime(),
64+
'extra' => array(),
65+
);
66+
}
67+
}

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,6 +756,7 @@ private function registerDebugConfiguration(array $config, ContainerBuilder $con
756756
if ($debug && class_exists(DebugProcessor::class)) {
757757
$definition = new Definition(DebugProcessor::class);
758758
$definition->setPublic(false);
759+
$definition->addArgument(new Reference('request_stack'));
759760
$container->setDefinition('debug.log_processor', $definition);
760761
}
761762
}

src/Symfony/Bundle/FrameworkBundle/Resources/config/collectors.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
<tag name="monolog.logger" channel="profiler" />
3636
<argument type="service" id="logger" on-invalid="ignore" />
3737
<argument>%kernel.cache_dir%/%kernel.container_class%</argument>
38+
<argument type="service" id="request_stack" on-invalid="ignore" />
3839
</service>
3940

4041
<service id="data_collector.time" class="Symfony\Component\HttpKernel\DataCollector\TimeDataCollector">

src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\Debug\Exception\SilencedErrorContext;
1515
use Symfony\Component\HttpFoundation\Request;
16+
use Symfony\Component\HttpFoundation\RequestStack;
1617
use Symfony\Component\HttpFoundation\Response;
1718
use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
1819

@@ -25,8 +26,10 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte
2526
{
2627
private $logger;
2728
private $containerPathPrefix;
29+
private $currentRequest;
30+
private $requestStack;
2831

29-
public function __construct($logger = null, $containerPathPrefix = null)
32+
public function __construct($logger = null, $containerPathPrefix = null, RequestStack $requestStack = null)
3033
{
3134
if (null !== $logger && $logger instanceof DebugLoggerInterface) {
3235
if (!method_exists($logger, 'clear')) {
@@ -37,14 +40,15 @@ public function __construct($logger = null, $containerPathPrefix = null)
3740
}
3841

3942
$this->containerPathPrefix = $containerPathPrefix;
43+
$this->requestStack = $requestStack;
4044
}
4145

4246
/**
4347
* {@inheritdoc}
4448
*/
4549
public function collect(Request $request, Response $response, \Exception $exception = null)
4650
{
47-
// everything is done as late as possible
51+
$this->currentRequest = $this->requestStack && $this->requestStack->getMasterRequest() !== $request ? $request : null;
4852
}
4953

5054
/**
@@ -67,9 +71,10 @@ public function lateCollect()
6771
$containerDeprecationLogs = $this->getContainerDeprecationLogs();
6872
$this->data = $this->computeErrorsCount($containerDeprecationLogs);
6973
$this->data['compiler_logs'] = $this->getContainerCompilerLogs();
70-
$this->data['logs'] = $this->sanitizeLogs(array_merge($this->logger->getLogs(), $containerDeprecationLogs));
74+
$this->data['logs'] = $this->sanitizeLogs(array_merge($this->logger->getLogs($this->currentRequest), $containerDeprecationLogs));
7175
$this->data = $this->cloneVar($this->data);
7276
}
77+
$this->currentRequest = null;
7378
}
7479

7580
/**
@@ -233,14 +238,14 @@ private function computeErrorsCount(array $containerDeprecationLogs)
233238
{
234239
$silencedLogs = array();
235240
$count = array(
236-
'error_count' => $this->logger->countErrors(),
241+
'error_count' => $this->logger->countErrors($this->currentRequest),
237242
'deprecation_count' => 0,
238243
'warning_count' => 0,
239244
'scream_count' => 0,
240245
'priorities' => array(),
241246
);
242247

243-
foreach ($this->logger->getLogs() as $log) {
248+
foreach ($this->logger->getLogs($this->currentRequest) as $log) {
244249
if (isset($count['priorities'][$log['priority']])) {
245250
++$count['priorities'][$log['priority']]['count'];
246251
} else {

src/Symfony/Component/HttpKernel/Log/DebugLoggerInterface.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Component\HttpKernel\Log;
1313

14+
use Symfony\Component\HttpFoundation\Request;
15+
1416
/**
1517
* DebugLoggerInterface.
1618
*
@@ -27,14 +29,18 @@ interface DebugLoggerInterface
2729
* timestamp, message, priority, and priorityName.
2830
* It can also have an optional context key containing an array.
2931
*
32+
* @param Request|null $request The request to get logs for
33+
*
3034
* @return array An array of logs
3135
*/
32-
public function getLogs();
36+
public function getLogs(/* Request $request = null */);
3337

3438
/**
3539
* Returns the number of errors.
3640
*
41+
* @param Request|null $request The request to count logs for
42+
*
3743
* @return int The number of errors
3844
*/
39-
public function countErrors();
45+
public function countErrors(/* Request $request = null */);
4046
}

src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@
1313

1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\Debug\Exception\SilencedErrorContext;
16+
use Symfony\Component\HttpFoundation\Request;
17+
use Symfony\Component\HttpFoundation\RequestStack;
18+
use Symfony\Component\HttpFoundation\Response;
1619
use Symfony\Component\HttpKernel\DataCollector\LoggerDataCollector;
20+
use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
1721

1822
class LoggerDataCollectorTest extends TestCase
1923
{
@@ -41,6 +45,46 @@ public function testCollectWithUnexpectedFormat()
4145
), $compilerLogs['Unknown Compiler Pass']);
4246
}
4347

48+
public function testWithMasterRequest()
49+
{
50+
$masterRequest = new Request();
51+
$stack = new RequestStack();
52+
$stack->push($masterRequest);
53+
54+
$logger = $this
55+
->getMockBuilder(DebugLoggerInterface::class)
56+
->setMethods(array('countErrors', 'getLogs', 'clear'))
57+
->getMock();
58+
$logger->expects($this->once())->method('countErrors')->with(null);
59+
$logger->expects($this->exactly(2))->method('getLogs')->with(null)->will($this->returnValue(array()));
60+
61+
$c = new LoggerDataCollector($logger, __DIR__.'/', $stack);
62+
63+
$c->collect($masterRequest, new Response());
64+
$c->lateCollect();
65+
}
66+
67+
public function testWithSubRequest()
68+
{
69+
$masterRequest = new Request();
70+
$subRequest = new Request();
71+
$stack = new RequestStack();
72+
$stack->push($masterRequest);
73+
$stack->push($subRequest);
74+
75+
$logger = $this
76+
->getMockBuilder(DebugLoggerInterface::class)
77+
->setMethods(array('countErrors', 'getLogs', 'clear'))
78+
->getMock();
79+
$logger->expects($this->once())->method('countErrors')->with($subRequest);
80+
$logger->expects($this->exactly(2))->method('getLogs')->with($subRequest)->will($this->returnValue(array()));
81+
82+
$c = new LoggerDataCollector($logger, __DIR__.'/', $stack);
83+
84+
$c->collect($subRequest, new Response());
85+
$c->lateCollect();
86+
}
87+
4488
/**
4589
* @dataProvider getCollectTestData
4690
*/

0 commit comments

Comments
 (0)