silpion/logger-extra-bundle
Symfony bundle that extends Monolog logging with practical extras: additional processors and configuration to enrich log records with useful context, simplify setup, and make application logs easier to inspect and route. Integrates via standard Symfony service configuration.
This is a Symfony2-era bundle that wires extra Monolog processors (and, since 0.1.0-beta1, a couple of kernel.event_listener hooks) into the container so every log record carries more context (request, user, environment, etc.) without you touching a single $logger->info() call. Treat it as a convenience layer over Monolog's processor mechanism and Symfony's event dispatcher, not as a logging framework of its own.
Minimal steps — note the release is a beta, so Composer needs to be told that:
composer require silpion/logger-extra-bundle:0.1.0-beta1
(or add "minimum-stability": "beta" / a prefer-stable combination to composer.json if you want floating constraints; pin the exact beta in a Symfony2 app, there is no Flex recipe and no extra.symfony config to fall back on).
Register it in app/AppKernel.php (Symfony 2.x style):
public function registerBundles()
{
return array(
// ...
new Silpion\LoggerExtraBundle\SilpionLoggerExtraBundle(),
);
}
Then look at these three files first, in this order — they tell you everything the bundle actually does:
Resources/config/services.xml — the service IDs and, crucially, the monolog.processor and kernel.event_listener tags. This is the real API surface, and the 0.1.0-beta1 changes live entirely here (listener priority attributes).DependencyInjection/Configuration.php — the available config keys and their defaults.DependencyInjection/SilpionLoggerExtraExtension.php — how config maps onto services and tags, and what gets skipped in which environment.First use case: get the current user and request info into every application log line so that a bug report ("it broke at 14:02 for user 4711") can be turned into a grep instead of an archaeology session. Once the bundle is registered and Monolog is configured, records logged through the standard logger service should already carry that context — verify with a quick $logger->info('smoke test'); and inspect app/logs/dev.log.
Global vs per-channel processors. Monolog's monolog.processor tag accepts a channel attribute. Use the bundle's default (global) wiring for things you want everywhere, then register additional processors yourself against a specific channel so that, e.g., a noisy backtrace processor only runs on app.error and not on doctrine or request:
services:
acme.log.processor.backtrace:
class: Monolog\Processor\IntrospectionProcessor
tags:
- { name: monolog.processor, channel: app }
Listeners vs processors — know which one you're relying on. Processors only shape the record at write time; the bundle's kernel.event_listener entries are what capture request-scoped state (request, response, security token) into a service that the processors then read. In 0.1.0-beta1 those listeners were re-prioritised to fire as early (and, where relevant, as late) as possible. Practically that means the context is populated before almost anything else in the request cycle runs — which is the behaviour you want, but see the ordering caveat in Gotchas.
Compose with FingersCrossedHandler. Context enrichment is most valuable when it is present on the buffered records that get flushed on error. Because processors are applied at write time (not flush time), the context reflects the moment of logging — which is what you want. Confirm the processor is applied to the handler that actually owns the buffer, not to a handler downstream of it.
Environment-scoped config. Keep prod lean: enable request/user processors everywhere, enable debug/backtrace processors only in dev and test. The bundle's extension already reads %kernel.debug% in places; mirror that convention in your own config_dev.yml / config_prod.yml overrides rather than hardcoding booleans in config.yml.
Structured context over string interpolation. Once context is flowing, standardise on it:
$logger->info('Order failed to capture', [
'order_id' => $order->getId(),
'gateway' => 'stripe',
'attempt' => $attempt,
]);
This lets your log aggregator index order_id instead of you writing regexes against message strings. The bundle processors just add their keys alongside yours — no conflict as long as you namespace your own keys (app.*, order.*).
Custom processors as first-class citizens. Any callable that accepts the record array and returns it can be tagged; you don't need to extend anything from the bundle. A typical in-house processor adds a correlation ID:
class CorrelationIdProcessor
{
private $id;
public function __construct($id = null) { $this->id = $id ?: uniqid('req-', true); }
public function __invoke(array $record)
{
$record['extra']['correlation_id'] = $this->id;
return $record;
}
}
Register it with the same monolog.processor tag, and it composes with the bundle's processors automatically.
Testing. Processors are pure functions over an array, so unit-test them without a kernel: instantiate, pass a sample record, assert on extra/context. For integration, boot the kernel in test env and assert on the handler's records via a TestHandler. For the listener half, dispatch a kernel.request/kernel.response event against the container-built service (or use KernelBrowser-style request tests) and assert the captured context, since listener priority is now the thing most likely to change behaviour between releases.
kernel.event_listener tags to "as early/last as possible" — no API change, no deprecation, but it is an ordering-behaviour change and it signals the priorities are still being tuned. Pin 0.1.0-beta1 explicitly, set minimum-stability accordingly, and re-read services.xml after any bump; a future beta could move them again.user key can legitimately be empty/null, and it may be populated by the time the record is written (processors read at write time). Don't assume extra.user exists in listeners and early controllers — check the actual ordering in debug:event-dispatcher.kernel.response/kernel.terminate competes with your own listeners. If you have a listener that inspects or mutates the log context at the end of the request, its relative priority against the bundle's is now effectively unspecified — register yours with an explicit priority above/below rather than relying on declaration order.setInjections, non-autowired arguments) and on Monolog 2/3 expect the processRecord() vs __invoke() signature question. Budget for either pinning the stack or vendoring the handful of processors you actually need.Acme\Logging namespace, and drop the dependency. You lose nothing but a composer require line and gain Symfony 4+/Monolog 3 compatibility.extra. If two sources both claim extra.user, you will silently get one of them. Namespace your keys.debug_backtrace() on every record. On a high-traffic prod app that is measurable CPU. Keep them dev-only, or set a sane skipClassesPartials if the processor supports it.extra keeps it alive for the record's lifetime and can retain huge graphs (and, in long-running workers, leak). Store scalars: user ID, route name, request ID.extra, and scrub tokens before they reach a shipped handler.app/console cache:clear --env=...). A "my processor doesn't fire" or "priority didn't change" report is almost always a stale container.app/console debug:container --tag=monolog.processor to see what is actually registered; app/console debug:event-dispatcher to confirm the bundle's listeners and their priorities relative to yours; check that the logger service you inject is the channel you think it is (logger vs a custom channel); verify the handler's processors config isn't overriding the tagged ones.services.yml (same ID, your class) or add a CompilerPass that replaces the definition in prepend/process — and for the listeners, set an explicit priority attribute on your replacement rather than inheriting whatever 0.1.0-beta1 shipped with. That keeps the bundle upgradeable while you own the behaviour.How can I help you explore Laravel packages today?