我在 Symfony 4.4 应用程序中遇到一个问题,其中有十几个侦听器正在侦听扩展
Symfony\Contracts\EventDispatcher\Event
的单个事件。问题是,如果其中一个侦听器抛出异常,那么它后面的侦听器都不会被执行。
我正在寻找一种方法来捕获侦听器的异常,这样就不会发生这种情况。我尝试扩展
Symfony\Component\EventDispatcher\EventDispatcher
并重写 callListeners()
方法以包含 try
/catch
,以便我可以记录异常但继续执行。但我不知道如何告诉 Symfony 使用我的 EventDispatcher
而不是它自己的。
我不知道这是否是解决这个问题的推荐方法。知道如何让它发挥作用,或者是否有其他替代方案?
Event Dispatcher 组件并不适合与完全解耦的侦听器异步使用。它按照设计同步执行,没有传输支持,并且所有侦听器都在同一请求/执行线程上运行。
如果抛出异常并且未处理,则执行应该停止并且永远不会到达下一个侦听器。
即使您使用完全异步的解决方案,例如带有传输的 Symfony Messenger,抛出异常而不处理它也会阻止任何其他侦听器执行同一事件。
我认为您使用了错误的工具来完成这项工作。
如果您不想停止执行,您的侦听器不应该抛出异常。处理监听器中的异常,利用机会进行日志记录等无需直接尝试/捕获而安全地调度事件的一种方法是装饰事件调度程序并在那里应用尝试/捕获。这样,就可以在需要时注入
这是事件调度程序装饰器:
class SafelyLogErrorsEventDispatcher implements EventDispatcherInterface
{
public function __construct(
private EventDispatcherInterface $eventDispatcher,
) {
}
public function dispatch(object $event, ?string $eventName = null): object
{
try {
return $this->eventDispatcher->dispatch($event, $eventName);
} (catch Throwable $exception) {
$this->logger->error('Error found in dispatcher', ['exception' => $exception]);
}
}
}
它可以通过
services.yaml
或 php 属性(或任何其他 DI 配置)中的配置注入。
还有装饰器的配置
,但我对此并不熟悉,也不确定这里是否需要它。通过属性:
class ExampleController extends AbstractController
{
public __construct(
#[Autowire(service: SafelyLogErrorsEventDispatcher::class)
private EventDispatcherInterface $eventDispatcher
) {}
}
或yaml:
services:
App\ExampleController:
arguments:
$eventDispatcher: App\SafelyLogErrorsEventDispatcher