Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[HttpKernel] LoggerDataCollector: splitting logs on different sub-requests #23659

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 20, 2018

Conversation

vtsykun
Copy link
Contributor

@vtsykun vtsykun commented Jul 24, 2017

Q A
Branch? 4.1
Bug fix? yes
New feature? no
BC breaks? no
Deprecations? no
Tests pass? -
Fixed tickets #23620
License MIT
Doc PR -

This PR fixed performance problem in dev mode and prevent logs serialize several times for each sub-request.

STR:

  1. Install any symfony application v2.8-3.4
  2. Create a simple page with 20-50 sub-requests and 5k-10k logs
  3. Open page in dev mode

Actual

  • debug toolbar not open (404 not found)
  • sets of logs (in the logger debug panel) for different requests are same
  • log processing takes about 20-30s
  • gc also run about 50% of execution time

Expected

  • Debug toolbar should be open
  • As developer, I want to see in logger panel only those logs that are relevant to the given sub-request

Profile and performance

Tested on page with 7k logs and 70 sub-request.

Comparison:

@nicolas-grekas
Copy link
Member

nicolas-grekas commented Jul 25, 2017

Should the profile for the master request get all logs? At least yes when framework.profiler.only_master_requests is set.
Thinking about the toolbar/panel, having the total count for logs of all sub requests looks desirable to me also. On the implementation side, a simple implementation would require cloning values from collector twice. A better implem would looks for profiles of all subrequests and aggregate them for the master request.

@nicolas-grekas nicolas-grekas added this to the 3.3 milestone Jul 25, 2017
if (func_num_args() === 1) {
$requestHash = func_get_arg(0);
if (isset($this->recordsRequest[$requestHash]['records'])
&& $requestHash !== $this->masterRequestHash
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually don't split these long if() conditions, so this should be one line.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -45,6 +66,15 @@ public function __invoke(array $record)
*/
public function getLogs()
{
if (func_num_args() === 1) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Symfony code we use Yoda conditions, so this should be 1 === func_num_args () instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

if (func_num_args() === 1) {
$requestHash = func_get_arg(0);
if (isset($this->recordsRequest[$requestHash]['errorCount'])
&& $requestHash !== $this->masterRequestHash
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same with this if() condition

@vtsykun
Copy link
Contributor Author

vtsykun commented Jul 26, 2017

I guess that profile should for the master request get all logs

vtsykun referenced this pull request in oroinc/platform Jul 26, 2017
…1598)

- added local cache for a logger collector
- overwrite symfony`s data_collector.logger class
- added tests
@vtsykun vtsykun force-pushed the patch-splitting-logs branch from c0a300d to c02cedd Compare July 29, 2017 00:09
@vtsykun vtsykun changed the title [WIP] [HttpKernel] LoggerDataCollector: splitting logs on different sub-requests [HttpKernel] LoggerDataCollector: splitting logs on different sub-requests Jul 29, 2017
@nicolas-grekas
Copy link
Member

I guess that profile should for the master request get all logs

@vtsykun same to me. OK to finish this PR then?

@nicolas-grekas
Copy link
Member

ping @vtsykun

@vtsykun
Copy link
Contributor Author

vtsykun commented Aug 31, 2017

Hi @nicolas-grekas, this done, please review
Yes, I updated PR and squashed commit on Jul 29. So in this implementation I show all logs for master request.

Copy link
Contributor

@ro0NL ro0NL left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice 👍

@@ -25,6 +25,7 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte
{
private $logger;
private $containerPathPrefix;
private $splRequestHash;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$currentRequestHash? (compared to $masterRequestHash above)

}

/**
* @param GetResponseEvent $event
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be removed i guess

return;
}

$this->masterRequestHash = spl_object_hash($event->getRequest());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might store the current request hash as well (to use if not passed on the calling side)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is passing it on the calling side really needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thinking the better move it on LoggerDataCollector side.

use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;

class DebugProcessor implements DebugLoggerInterface
{
private $records = array();
private $errorCount = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about array for both? in favor of array_column

Copy link
Contributor

@ro0NL ro0NL left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

almost

if (null !== $this->requestStack && $this->requestStack->getMasterRequest() !== $request) {
$this->currentRequestHash = spl_object_hash($request);
} else {
//gets all logs for the master request
Copy link
Contributor

@ro0NL ro0NL Sep 15, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

space missing + gets > get

}

if (1 === func_num_args()) {
$requestHash = func_get_arg(0);
Copy link
Contributor

@ro0NL ro0NL Sep 15, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

collector might pass null here, should we be explicit? the key would be ""

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

@@ -27,7 +27,7 @@ class Logger extends BaseLogger implements DebugLoggerInterface
public function getLogs()
{
if ($logger = $this->getDebugLogger()) {
return $logger->getLogs();
return call_user_func_array(array($logger, 'getLogs'), func_get_args());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be replace with: $logger->getLogs(...func_get_args()); which is easier to read and faster.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not php 5.5 compat

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -39,7 +39,7 @@ public function getLogs()
public function countErrors()
{
if ($logger = $this->getDebugLogger()) {
return $logger->countErrors();
return call_user_func_array(array($logger, 'countErrors'), func_get_args());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

@ro0NL
Copy link
Contributor

ro0NL commented Sep 21, 2017

From #24275; do events have this same issue?

@nicolas-grekas nicolas-grekas modified the milestones: 3.3, 3.4 Oct 9, 2017
@vtsykun vtsykun changed the base branch from 3.3 to 3.4 October 13, 2017 15:48
@nicolas-grekas
Copy link
Member

nicolas-grekas commented Apr 9, 2018

@vtsykun thank you for this PR, and sorry I didn't manage to review it earlier. I just rebased it on top of the latest version of branch 3.4, with a few minor changes. The code looks to good to me. It sill misses a few unit tests. Would you be up for adding them? If yes, don't forget to fetch+rebase --hard on your remote patch-splitting-logs branch. If not, I'm sure someone else can take over. Please advise.

Copy link
Member

@nicolas-grekas nicolas-grekas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I just pushed some unit tests)

Copy link
Member

@fabpot fabpot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like something for 4.1/master intead

@nicolas-grekas
Copy link
Member

Looks like something for 4.1/master instead

It could go on 4.1 for sure. The only thing that made me keep it for 3.4 is that the reported behavior affects 3.4 (sub-requests heavy pages suffer from a significant penalty because of log duplication.)

@nicolas-grekas nicolas-grekas changed the base branch from 3.4 to master April 20, 2018 09:44
@nicolas-grekas nicolas-grekas modified the milestones: 3.4, 4.1 Apr 20, 2018
@nicolas-grekas
Copy link
Member

Rebased on master, ready for 4.1.

@nicolas-grekas
Copy link
Member

Thank you @vtsykun.

@nicolas-grekas nicolas-grekas merged commit d0cb1de into symfony:master Apr 20, 2018
nicolas-grekas added a commit that referenced this pull request Apr 20, 2018
…fferent sub-requests (vtsykun)

This PR was merged into the 4.1-dev branch.

Discussion
----------

[HttpKernel] LoggerDataCollector: splitting logs on different sub-requests

| Q             | A
| ------------- | ---
| Branch?       | 4.1
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | -
| Fixed tickets | #23620
| License       | MIT
| Doc PR        | -

This PR fixed performance problem in dev mode and prevent logs serialize several times for each sub-request.

STR:
1) Install any symfony application v2.8-3.4
2) Create a simple page with 20-50 sub-requests and 5k-10k logs
3) Open page in dev mode

**Actual**
- debug toolbar not open (404 not found)
- sets of logs (in the logger debug panel) for different requests are same
- log processing takes about 20-30s
- gc also run about 50% of execution time

**Expected**
- Debug toolbar should be open
- As developer, I want to see in logger panel only those logs that are relevant to the given sub-request

### Profile and performance

Tested on page with 7k logs and 70 sub-request.

Comparison:
- v3.3.5 - this patch: https://blackfire.io/profiles/compare/b1d410b3-c4a7-4778-9b6d-514f72193e28/graph
- v3.3.5 - patch #23644 https://blackfire.io/profiles/compare/d0715b04-7834-4981-8da2-9f1dcb2ef90c/graph

Commits
-------

d0cb1de [HttpKernel] LoggerDataCollector: splitting logs on different sub-requests
@fabpot fabpot mentioned this pull request May 7, 2018
fabpot added a commit that referenced this pull request Nov 26, 2018
…0NL)

This PR was merged into the 4.1 branch.

Discussion
----------

[MonologBridge] Return empty list for unknown requests

| Q             | A
| ------------- | ---
| Branch?       | 4.1
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no     <!-- see https://symfony.com/bc -->
| Deprecations? | no
| Tests pass?   | yes    <!-- please add some, will be required by reviewers -->
| Fixed tickets | #...   <!-- #-prefixed issue number(s), if any -->
| License       | MIT
| Doc PR        | symfony/symfony-docs#... <!-- required for new features -->

Small continuation of #23659. Currently if the specified request is unknown, you'll get all available requests merged. This fixes it.

Commits
-------

69be8e6 [MonologBridge] Return empty list for unknonwn requests
fabpot added a commit that referenced this pull request Nov 26, 2018
This PR was merged into the 3.4 branch.

Discussion
----------

[EventDispatcher] Unwrap wrapped listeners internally

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no     <!-- see https://symfony.com/bc -->
| Deprecations? | no
| Tests pass?   | yes    <!-- please add some, will be required by reviewers -->
| Fixed tickets | partially #24275
| License       | MIT
| Doc PR        | symfony/symfony-docs#... <!-- required for new features -->

I believe what happens is we re-register the listeners on the wrapped dispatcher, so in case of sub request we get those and wrap it again. The profiler seems to confirm this in case of an exception (default 404 here) and thus creates a sub-request.

## Before: (main request)

No exception: (OK)

![image](https://user-images.githubusercontent.com/1047696/48968597-295ec180-eff2-11e8-982c-756e9019e107.png)

Exception: (KO)

![image](https://user-images.githubusercontent.com/1047696/48968612-4abfad80-eff2-11e8-8556-37bfbd51bc3b.png)

## After (main request):

No exception: same

Exception: (OK)

![image](https://user-images.githubusercontent.com/1047696/48968636-9f632880-eff2-11e8-983e-b4099133600b.png)

I haven't furhter investigated if we should split events per request as done in #23659 for logs. It seems somewhere we deduplicate events.. so im not sure the profiler actually shows correct data.

Commits
-------

448e2e2 [EventDispatcher] Unwrap wrapped listeners internally
fabpot added a commit that referenced this pull request Apr 2, 2019
This PR was squashed before being merged into the 4.3-dev branch (closes #29312).

Discussion
----------

[EventDispatcher] Split events across requests

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no     <!-- see https://symfony.com/bc -->
| Deprecations? | no
| Tests pass?   | yes    <!-- please add some, will be required by reviewers -->
| Fixed tickets | #24275
| License       | MIT
| Doc PR        | symfony/symfony-docs#... <!-- required for new features -->

Split events per request, as currently a profiled sub-request includes all events. Follows same approach how logs are split in #23659.

Commits
-------

c3477ba [EventDispatcher] Split events across requests
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants