symfony / monolog-bundle

Symfony Monolog Bundle

Home Page:symfony.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

"child process 5920 exited with status 3221225725 -- Restarting" & "The connection was reset (net::ERR_CONNECTION_RESET)" when using monolog on XAMPP

JPustkuchen opened this issue · comments

Context:

  • XAMPP on Windows
  • PHP 8.0.3

Problem:

Monolog seems to eat up ressources (ThreadStackSize? ThreadsPerChild? MaxRequestsPerChild? - reasons unknown yet) which leads to a mysterious problem on Windows / WAMP. In my case it's XAMPP, but I guess it might also happen on other machines as they might use the same defaults.

Using Symfony with monolog (e.g. simply reproducable with https://github.com/symfony/demo) leads to these horrible errors:
Browser:
"The connection was reset" net::ERR_CONNECTION_RESET
grafik

errorlog:
[Fri Jul 02 06:42:45.297942 2021] [mpm_winnt:notice] [pid 5920:tid 684] AH00354: Child: Starting 150 worker threads.
[Fri Jul 02 06:42:57.561559 2021] [mpm_winnt:notice] [pid 4312:tid 652] AH00428: Parent: child process 5920 exited with status 3221225725 -- Restarting.

After two days and nights ( ;) ) I found out that it was monolog causing the problem. As I removed
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
and the monolog.yaml everything simply works on WAMP! Side loads, no errors!

My first thought was, that this might be caused by an infinite loop caused by an Error / exception but there's definitely no Error / Exception to be handled by Monolog and even it that had been the case, monolog should not fail and end up in such an inexpressive nightmare error like this.
We all know these kinds of errors where you have no clue where it may come from. This is one.

So from my perspective you can discuss if the related limits (see https://stackoverflow.com/a/43120319/10031165) are too low by default. But there's no reason to me, why monolog should eat up resources like this, especially if there isn't even an error.

Also see

monolog.yaml configuration was NOT changed, all default! I tried dev, test and prod - all failed!

Reproduction steps:

  1. Install XAMPP on Windows with PHP 8
  2. Install Symfony demo as documented on https://github.com/symfony/demo
  3. See the problem
  4. Lose days and nights to find out the reason :/

Possible reason:

Mad combination of:

  1. monolog eats up too many resources even if there is no error due to unknown reasons
  2. Too low (really?) defaults in httpd.conf for mpm_winnt_module on Windows (but changing that should be last resource I guess)?
  3. ???

Proposed solution:

  1. Workaround: Disable Monolog like I did described above until this is fixed
  2. Find out why monolog eats up resources (and which - ThreadStackSize? Child Processes?) and fix / limit it
  3. Increase limits like described in the links above

Wow, I've been having this trouble since the Symfony 6 release. I work in Windows so I use xampp for almost everything, except the new Symfony 6 projects that indeed, always return net::ERR_CONNECTION_RESET in the browser, so they're basically unusable (the project works great with the Symfony built-in server though [symfony serve] and in the production server [Ubuntu]).

In the beginning, I thought that maybe my VirtualHost was misconfigured or something like that, but randomly I found your issue and decided to comment out Monolog from the project as you mentioned (including the monolog.yaml file), and voila! The project finally worked through XAMPP as I used to do with my old projects.

As in my case, removing Monolog from the project wasn't the solution, modifying the mpm_winnt_module settings in the apache\conf\extra\httpd-mpm.conf file like this finally made the project work in XAMPP without having to remove monolog:

# C:\xampp8012\apache\conf\extra\httpd-mpm.conf
# WinNT MPM
# ThreadsPerChild: constant number of worker threads in the server process
# MaxConnectionsPerChild: maximum number of connections a server process serves
<IfModule mpm_winnt_module>
    ThreadStackSize 8388608
    ThreadsPerChild        250
    MaxConnectionsPerChild   0
</IfModule>

@JPustkuchen thank you so much for the hints!

Just wanted to chime in and report that I had the exact same problem. After upgrading to Symfony 6, I soon noticed that I was sometimes getting blank browser pages with "ERR_CONNECTION_RESET" messages on XAMPP.

Once the problem starts to appear, you're seemingly stuck with it. The only remedy I found was refreshing the cache via cache:clear. Afterwards, it would work again for maybe two, three page loads until it broke yet again.

As @JPustkuchen and @sdkcarlos have found out, the culprit seems to the the Monolog-bundle as disabling the package removes the issue. As for the Apache config, increasing ThreadsPerChild to 1000 alone didn't fix it for me, but setting ThreadStackSize to 8388608 seemingly did. It doesn't feel like an ideal solution, but at least it is one.

Also, I'm not sure if this is pertinent to the issue, but while executing cache:clear while the problem was happening (triggering on each page load), I got some warnings that I wouldn't get when the problem was currently not happening. I got one for each loaded bundle from bundles.php, so 15 in total for me. They all had the format:

WARNING [app] Failed to generate ConfigBuilder for extension Symfony\Bundle\FrameworkBundle\DependencyInjection\FrameworkExtension. ["exception" => Symfony\Component\DependencyInjection\Exception\EnvNotFoundException^ { …},"extensionClass" => "Symfony\Bundle\FrameworkBundle\DependencyInjection\FrameworkExtension"]

So it seems he doesn't find all required env vars. Does this perhaps cause the logger to spin out of control? Or is it the other way around? Either way, as I said, I don't get these warnings again after executing cache:clear a second time right afterwards, and the site works again (for a time).

Hello, I also encountered the ERR_CONNECTION_RESET error when upgrading Xampp from PHP 8.0.3 / Apache 2.4.4.46 (where I had no issue), to any new release from PHP 8.0.5 / Apache 2.4.4.47. I always encounter the issue while generating or regenerating the cache while browsing my local website (through Apache).

I could identify the issue comes from the call to class_exists($this->resource) in Symfony\Component\Config\Resource\ClassExistenceResource::isFresh() when this->resource is Symfony\Bundle\MonologBundle\DependencyInjection\Configuration. This class_exists trigger the autoloader and while reading / validating the monolog configuration the ThreadStackSize goes over the limit (At this stage I really don't know if it's normal or not).

So I also had to increase the ThreadStackSize to 8388608.

For more information see the related ticket.

All Symfony devs that works on Windows will be impacted by this issue. It would be great to investigate why we don't have the same issue with Xampp PHP 8.0.3 / Apache 2.4.4.46 and why it happens also only with monolog-bundle.

I think it's server or even PHP independent problem. I have the same issue on Apache Lounge 2.4.46 (Win64) PHP/8.1.4.
Increasing ThreadStackSize did not solve the problem.

@derrabus @jderusse ping hopefully either of you has time to look into this. php/php-src#8315 explains a bit more what is going on. Seems to be failing on PHP 8.1 and possibly some 8.0 releases too so I think it needs to be fixed as it'll only get worse as more users migrate.

Splitting up the Configuration class into smaller steps/methods should drop the stack size fairly easily.

So the issue is chaining method calls? The DSL of the config components is pretty much designed around doing just that.

It appears so.. And yes I can see that but this bundle makes a pretty extreme use of it in one single call

We can certainly split this up a bit, but this still feels like something that should be fixed in PHP instead.

I'd also like to get an opinion from @nicolas-grekas if he has time.

I confirm that splitting the config like this (by handlers) for example fixes the issue:

$handlers = $rootNode
    ->children()
        ->arrayNode('handlers')
            ->prototype('array')
                ->children();

$mongoNode = $handlers
    ->arrayNode('mongo')
        ->canBeUnset()
        ->beforeNormalization()
        ->ifString()
        ->then(function ($v) { return ['id' => $v]; })
        ->end()
        ->children()
            ->scalarNode('id')->end()
            ->scalarNode('host')->end()
            ->scalarNode('port')->defaultValue(27017)->end()
            ->scalarNode('user')->end()
            ->scalarNode('pass')->end()
            ->scalarNode('database')->defaultValue('monolog')->end()
            ->scalarNode('collection')->defaultValue('logs')->end()
        ->end()
        ->validate()
        ->ifTrue(function ($v) {
            return !isset($v['id']) && !isset($v['host']);
        })
        ->thenInvalid('What must be set is either the host or the id.')
        ->end()
        ->validate()
        ->ifTrue(function ($v) {
            return isset($v['user']) && !isset($v['pass']);
        })
        ->thenInvalid('If you set user, you must provide a password.')
        ->end()
    ->end()
; // mongo

$elasticsearchNode = $handlers
    ->arrayNode('elasticsearch')
        ->canBeUnset()
        ->beforeNormalization()
        ->ifString()
        ->then(function ($v) { return ['id' => $v]; })
        ->end()
        ->children()
            ->scalarNode('id')->end()
            ->scalarNode('host')->end()
            ->scalarNode('port')->defaultValue(9200)->end()
            ->scalarNode('transport')->defaultValue('Http')->end()
            ->scalarNode('user')->defaultNull()->end()
            ->scalarNode('password')->defaultNull()->end()
        ->end()
        ->validate()
        ->ifTrue(function ($v) {
            return !isset($v['id']) && !isset($v['host']);
        })
        ->thenInvalid('What must be set is either the host or the id.')
        ->end()
    ->end()
; // elasticsearch

$redisNode = $handlers
    ->arrayNode('redis')
        ->canBeUnset()
        ->beforeNormalization()
        ->ifString()
        ->then(function ($v) { return ['id' => $v]; })
        ->end()
        ->children()
            ->scalarNode('id')->end()
            ->scalarNode('host')->end()
            ->scalarNode('password')->defaultNull()->end()
            ->scalarNode('port')->defaultValue(6379)->end()
            ->scalarNode('database')->defaultValue(0)->end()
            ->scalarNode('key_name')->defaultValue('monolog_redis')->end()
        ->end()
        ->validate()
        ->ifTrue(function ($v) {
            return !isset($v['id']) && !isset($v['host']);
        })
        ->thenInvalid('What must be set is either the host or the service id of the Redis client.')
        ->end()
    ->end()
; // redis

$predisNode = $handlers
    ->arrayNode('predis')
        ->canBeUnset()
        ->beforeNormalization()
        ->ifString()
        ->then(function ($v) { return ['id' => $v]; })
        ->end()
        ->children()
            ->scalarNode('id')->end()
            ->scalarNode('host')->end()
        ->end()
        ->validate()
        ->ifTrue(function ($v) {
            return !isset($v['id']) && !isset($v['host']);
        })
        ->thenInvalid('What must be set is either the host or the service id of the Predis client.')
        ->end()
    ->end()
; // predis

Is it possible to have PR #432 merged and new version released in a short time? Is there anything that blocks delivery of @raziel057's change?