shark/formlogger-bundle
A Symfony bundle for logging form submissions. The Shark FormLogger Bundle helps developers capture and store submitted form data for auditing, debugging, and analysis, with straightforward integration into existing Symfony applications and configurable logging behavior.
The first thing to get straight: shark/formlogger-bundle is not a Laravel package. It is a Symfony 2 bundle (kernel bundle class, DI extension, form-event listener) targeting a framework that went end-of-life in 2019. With 10 stars, zero dependents, and a composite score of 0.11, treat it as a reference implementation to read, not a dependency to install. Day-to-day, your "getting started" is one of two paths:
Path A — you actually maintain a legacy Symfony 2 app (the only place it installs cleanly):
composer require shark/formlogger-bundle (or add a vcs repository entry and clone if the Packagist package has disappeared).app/AppKernel.php:
public function registerBundles()
{
$bundles = array(
new Shark\FormLoggerBundle\SharkFormLoggerBundle(), // verify namespace from *Bundle.php
);
}
Resources/config/services.xml — real service IDs, tags, and whether it auto-attaches to form events.DependencyInjection/Configuration.php — the actual config root and keys.FormEvents::POST_SUBMIT (or SUBMIT) is hooked.app/logs/dev.log by default).Path B — you're on Laravel (which the brief is): don't install it. Read the listener, note that it serializes $form->getData() and $form->getErrors() into a PSR-3 logger, and reimplement the behaviour in ~40 lines. Your starting points in Laravel are Illuminate\Validation\ValidationException, the FormRequest::failedValidation() hook, and a dedicated channel in config/logging.php. Estimated effort is 1–2 days including redaction and tests — less than the cost of forking and owning a Symfony 2 dependency.
Either way, the "first use case" is identical in spirit: capture the failing payload plus the validation messages for a single, high-value form so you can reproduce user reports without asking for screenshots.
Pattern 1 — automatic event-subscriber logging (Symfony 2 style). If services.xml tags the listener on kernel.event_subscriber, every submitted form is logged with no controller code:
// roughly what the bundle does
public function postSubmit(FormEvent $event)
{
$form = $event->getForm();
$this->logger->info('form.submitted', [
'name' => $form->getName(),
'data' => $form->getData(),
'errors' => $this->flatten($form->getErrors(true)),
]);
}
Convenient, noisy, and dangerous with anything sensitive — you get every field whether you wanted it or not.
Pattern 2 — explicit service call (preferred even in Symfony). Inject the bundle's service (or your own) and log deliberately, controlling level, context, and whether the form validates.
Pattern 3 — the Laravel-native equivalent (what you'll actually run). Mirror the same behaviour with framework primitives:
// app/Http/Middleware/LogFormFailures.php (or a listener on ValidationException)
public function handle($request, Closure $next)
{
try {
return $next($request);
} catch (ValidationException $e) {
try {
Log::channel('form_errors')->info('form.validation_failed', [
'route' => $request->route()?->getName(),
'fields' => array_keys($e->errors()),
'errors' => $e->errors(),
'payload' => $this->redact($request->except(['_token', 'password', 'password_confirmation'])),
]);
} catch (\Throwable $logFailure) {
// logging must never change the response
report($logFailure);
}
throw $e;
}
}
Pair it with a JSON-formatted channel so downstream ingestion can parse it:
// config/logging.php
'form_errors' => [
'driver' => 'daily',
'path' => storage_path('logs/form_errors.log'),
'level' => 'info',
'days' => 14,
'formatter' => Monolog\Formatter\JsonFormatter::class,
],
Workflow tips that carry over from the bundle's design:
debug and log the raw value for that field only, temporarily.Integration tip: if a teammate really has already prototyped with this bundle inside a Symfony 2 service, keep it behind a queue or HTTP boundary rather than importing its classes into Laravel. There is no shared contract to bridge against — no service provider, no facade, no publishable config.
symfony/form, symfony/http-kernel, symfony/dependency-injection, and symfony/event-dispatcher alongside Illuminate. Expect duplicate dispatcher bindings and autoload/version conflicts. There's no package discovery and no service provider to bridge them._token, card numbers, health/financial fields, and free-text messages all land in the log by default. Use an allowlist of fields to capture, never a denylist, and define retention before enabling it on any real form.getData(), an unwritable log target), the user sees a server error instead of a validation message. Wrap all logging in try/catch and never let it influence the response.composer.json files typically declare php >=5.3 with no upper bound or an incompatible one, and predate PSR-4 hygiene. Running it on PHP 8.2+ ranges from untested to broken. Pin components in an isolated namespace if you must.DependencyInjection/Configuration.php; verify before assuming a key name from the README. When in doubt, the bundle source is the specification.services.xml, (3) is the log level high enough for the channel, (4) does the form reach POST_SUBMIT at all — a form that fails early never fires it, (5) is the handler writing somewhere you aren't tailing.Bottom line for a Laravel developer: you cannot meaningfully leverage this package day-to-day on Laravel. Read it once as a behavioral spec, then implement a native ValidationException listener with a JSON log channel, allowlist redaction, sampling, and a try/catch guard. That gives you the same capability without the EOL framework, the PII exposure, or the maintenance tax.
How can I help you explore Laravel packages today?