fintem/workflow-logger-bundle
Fintem Workflow Logger Bundle adds structured logging around workflow transitions and events, making it easier to trace state changes, debug process flows, and audit activity in PHP applications. Capture context-rich records for every workflow step and troubleshoot issues faster.
First, the mismatch. This is a Symfony bundle, not a Laravel package. It has no service provider, no facade, no config/workflow-logger.php. It ships a Symfony Bundle class, a DependencyInjection extension, and Resources/config/services.xml — none of which Laravel's container knows how to boot. As a Laravel developer you have three realistic entry points:
composer.json.Look here first in vendor/fintem/workflow-logger-bundle/:
src/DependencyInjection/*Extension.php — the config tree (channel name, enable/disable flags).Resources/config/services.xml (or services.yaml) — this tells you exactly which classes are registered and what they're injected with. This is the whole package in one file.Symfony\Component\Workflow\Event\Event via WorkflowEvents::TRANSITION, ::ENTERED, ::COMPLETED, etc., and writes to a PSR-3 logger.Minimal first use case: log order: pending → paid → shipped transitions with the actor and a timestamp, into a dedicated channel you can grep. Do not start by trying to register the bundle.
composer require symfony/workflow # the component works fine without Symfony
composer require fintem/workflow-logger-bundle # read it, don't boot it
The symfony/workflow component is framework-agnostic — DefinitionBuilder, MethodMarkingStore, Registry, and a standalone Symfony\Component\EventDispatcher\EventDispatcher all instantiate cleanly inside a Laravel service provider. That is the useful half. The bundle is just 100-ish lines of wiring around it.
Pattern A — port the subscriber, keep the semantics. Read the listener, then rewrite it as a Laravel-flavoured class that takes Psr\Log\LoggerInterface (Laravel's Log manager resolves to a PSR-3 Logger, so injection is free):
class WorkflowLogSubscriber implements EventSubscriberInterface
{
public function __construct(private LoggerInterface $logger) {}
public static function getSubscribedEvents(): array
{
return [
WorkflowEvents::TRANSITION => 'onTransition',
WorkflowEvents::ENTERED => 'onEntered',
];
}
public function onTransition(Event $event): void
{
$this->logger->info('workflow.transition', [
'workflow' => $event->getWorkflowName(),
'subject' => $event->getSubject()::class,
'subject_id' => $event->getSubject()->getId(),
'transition' => $event->getTransition()->getName(),
'from' => array_keys($event->getMarking()->getPlaces()),
'to' => $event->getTransition()->getTos(),
]);
}
}
Register it in a WorkflowServiceProvider and attach it to the shared Symfony EventDispatcher you pass into each workflow — not to Laravel's dispatcher directly. The two are different objects; confusing them is the single most common integration bug.
Pattern B — structured audit table instead of log files. Log files are fine for debugging, useless for "who moved this order and when." Mirror the subscriber into an workflow_transitions Eloquent model (subject_type, subject_id, transition, from, to, actor_id, context JSON, created_at). Keep the PSR-3 call as well, at debug level, so you get both.
Pattern C — dedicated channel. Add config/logging.php → 'workflow' => ['driver' => 'daily', 'path' => storage_path('logs/workflow.log'), 'days' => 30] and point the subscriber at it. The bundle's extension typically exposes a channel/logger config key; replicate that as a constructor arg rather than a hardcoded Log::channel().
Pattern D — Symfony sidecar. If the state machine genuinely lives in a Symfony app, let the bundle do its job there (register in bundles.php, configure the channel under fintem_workflow_logger:), and have Laravel consume the resulting audit table or a queue message. Don't proxy workflow calls over HTTP just to get logging.
Testing. With Log::spy() (or Log::shouldReceive('info')), assert exact payload shape — workflow, transition, from, to — for one happy path and one guard-rejected path. Guard rejections fire WorkflowEvents::GUARD and never reach TRANSITION, so your audit trail silently misses failed attempts unless you subscribe to that too.
composer.json pins php: ^7.0 and symfony/symfony: ^3.4. On PHP 8.2+ you'll get deprecation noise at best and a hard constraint conflict at worst. Force-install only if you're reading the source, never in production.LaravelServiceProvider in extra.laravel.providers, composer require gives you nothing but files on disk. Everything is manual.Extension::load(), services.xml, and any CompilerPass never execute. Don't spend time trying to make config/packages/fintem_workflow_logger.yaml equivalent work — just instantiate the listener yourself.announce, entered, completed, and transition all fire per hop. A four-place workflow generates roughly four events per transition. Filter by event type at the subscriber level, not by log level after the fact.$event->getContext() is whatever you put there — often the full subject or request payload. Strip it or whitelist keys before logging.getMarking() timing. On TRANSITION the marking is pre-transition; on ENTERED it's post. Getting from/to from the wrong event produces logs that contradict each other.bin/console debug:event-dispatcher. In Laravel: confirm the subscriber is attached to the dispatcher instance actually passed to new Workflow(...) — a Registry built with a fresh dispatcher per workflow is a classic silent failure.app/Workflows/ (it's MIT, ~100 lines) and drop the dependency. You get the same audit trail, PHP 8 compatibility, and no abandoned package in your composer.lock — which matters more than 1 star suggests when a security scanner flags it in 2026.How can I help you explore Laravel packages today?