Skip to content

Ensure all handlers are explicitly closed on kernel shutdown - #377

Merged
GromNaN merged 1 commit into
symfony:4.xfrom
andy-educake:376-add-handler-manager-to-close-handlers
Sep 9, 2026
Merged

GromNaN merged 1 commit into
symfony:4.xfrom
andy-educake:376-add-handler-manager-to-close-handlers

Conversation

@andy-educake

@andy-educake andy-educake commented Nov 24, 2020 •

Copy link
Copy Markdown
Q A
Branch? 4.x
Bug fix? yes
New feature? no
Deprecations? no
Issues -
License MIT

Close all handlers on kernel shutdown. A HandlerLifecycleManager closes every already-instantiated handler, including nested ones, through weak references, so unused handlers are not instantiated just to be closed.

@andy-educake
andy-educake force-pushed the 376-add-handler-manager-to-close-handlers branch from 6e7a558 to 5c10161 Compare November 24, 2020 10:38

@lyrixx lyrixx left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I like this PR, but I'm not sure about it. If we do that, we should do the same thing for all others bundle that does IO.

@nicolas-grekas WDYT?

Comment thread MonologBundle.php Outdated
Comment thread Services/HandlerManager.php Outdated
Comment thread Services/HandlerManager.php Outdated
Comment thread Services/HandlerManager.php Outdated
Comment thread Resources/config/monolog.xml Outdated
Comment thread Services/HandlerManager.php Outdated
@andy-educake

Copy link
Copy Markdown
Author

Thanks for the comments @lyrixx Have dealt with all of them I think. I notice that Travis has failed for PHP 5.6 and PHP 7.0. Possibly because I used the iterable type hint on the constructor param. Should I use the Iterator interface instead for bc?

Comment thread HandlerLifecycleManager.php Outdated
Comment thread Resources/config/monolog.xml Outdated
</service>

<service id="monolog.handler_manager" class="Symfony\Bundle\MonologBundle\HandlerLifecycleManager" public="true">
<argument type="tagged_iterator" tag="monolog.handler" />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Rather than using type="tagged_iterator", you should use a compiler pass to create the IteratorArgument (or drop the tag entirely and manage everything in the DI extension as I don't think we need to support handlers added elsewhere). This way, you can use weak references in the IteratorArgument, which will skip services which haven't been instantiated yet

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I'm not sure I'm familiar enough with the Container to change this right now but given #377 (comment) I should probably pause on this PR now anyway?

@stof

stof commented Nov 27, 2020

Copy link
Copy Markdown
Member

Regarding a generic solution in symfony, I think we need something similar to the ServiceResetter, but which is always called by the kernel on shutdown, for services which need to close some resources for their shutdown. This way, MonologBundle could hook the close method of handlers in there, DoctrineBundle could hook connections, etc...

Comment thread HandlerLifecycleManager.php Outdated

@GromNaN GromNaN left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is it still necessary?

Comment thread HandlerLifecycleManager.php Outdated
Comment thread MonologBundle.php Outdated
Comment thread HandlerLifecycleManager.php Outdated
@GromNaN
GromNaN force-pushed the 376-add-handler-manager-to-close-handlers branch 2 times, most recently from 954a545 to af7a879 Compare September 9, 2026 15:50
@GromNaN

GromNaN commented Sep 9, 2026

Copy link
Copy Markdown
Member

Rebased the branch onto current 4.x and applied the standing review feedback so it can move forward. The previous structure (root DependencyInjection/, Resources/config/monolog.xml, root MonologBundle.php) no longer exists on 4.x, so this is a re-apply rather than a mechanical rebase.

Changes:

  • HandlerLifecycleManager is now @internal (stof, GromNaN) and lives in src/.
  • The constructor takes iterable $handlers (derrabus).
  • MonologBundle::shutdown() uses the one-liner form (GromNaN), with a guard for when the manager is not defined.
  • All handlers (including nested ones) are tagged monolog.handler. Nested handlers are excluded from kernel.reset, but they hold real resources and must be closed on shutdown.

stof suggestion implemented: instead of a tagged_iterator (which would instantiate every handler on shutdown, opening connections/files for unused handlers just to close them), a new AddHandlersToManagerPass compiler pass injects the handlers as an IteratorArgument of weak references (ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE). This is the same mechanism Symfony uses for services_resetter: the generated iterator only yields already-instantiated services, so unused handlers are never instantiated at shutdown.

Added a CHANGELOG entry and tests (handlers are tagged; the manager receives weak references).

Local checks: full PHPUnit suite passes, php-cs-fixer is clean.

Happy to adjust anything.

@GromNaN
GromNaN force-pushed the 376-add-handler-manager-to-close-handlers branch from 59e91ac to 2adbc6e Compare September 9, 2026 16:01
@GromNaN

GromNaN commented Sep 9, 2026

Copy link
Copy Markdown
Member

Regarding stof's earlier point about a generic Symfony solution: a framework-level mechanism similar to services_resetter, but invoked on kernel shutdown (so MonologBundle could hook handler close(), DoctrineBundle could hook connections, etc.), would be the ideal long-term answer. Symfony does not provide such a mechanism today: there is no kernel.shutdown event and Kernel::shutdown() only calls Bundle::shutdown(). So this PR uses Bundle::shutdown() as the available hook.

The design is intended to be forward-compatible with that future generic closer: handlers are tagged (monolog.handler) and collected by a compiler pass into an iterator of weak references. Swapping the bundle-specific monolog.handler_lifecycle_manager service for a framework-provided closer later would not require changing how handlers are tagged or collected.

Side note on the service being public: it has to be public because Bundle::shutdown() reaches it through $this->container->get(), the same way Symfony's services_resetter is public and fetched in Kernel::boot(). Private services are not retrievable via get() at runtime, and there is no shutdown event to subscribe to instead.

@GromNaN
GromNaN force-pushed the 376-add-handler-manager-to-close-handlers branch from 2adbc6e to c184e00 Compare September 9, 2026 16:53
@GromNaN

GromNaN commented Sep 9, 2026

Copy link
Copy Markdown
Member

Thanks @andy-educake for working on this feature, this is much appreciated.

@GromNaN
GromNaN merged commit ef05c7a into symfony:4.x Sep 9, 2026
8 of 9 checks passed
@GromNaN GromNaN added this to the 4.1 milestone Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants