Usage
Once the bundle is installed and its key is set, exception reporting works with no further code; url is only set for a self-hosted instance. Log forwarding and manual reporting take one extra step each.
Automatic exception capture
The bundle registers monitor.exception_subscriber, an event subscriber listening on Symfony's kernel.exception event at priority -64 (low, so framework listeners run first, the bundle only observes). For every unhandled throwable it:
- checks the configured
environmentsagainstkernel.environmentand skips when the current environment is not allowed; - builds a payload from the throwable (class, message, file, line and the stack trace, full by default);
- attaches request context, the HTTP method and URL, plus the environment and release;
- scrubs sensitive keys and POSTs the result to
/api/v1/ingest.
HTTP exceptions with a status code below 500 (NotFoundHttpException and other expected client errors, such as bot probes hitting 404s) are skipped: they are never reported and never count against your event quota. HTTP exceptions of 500 and above are reported like any other throwable.
It is additive: it never alters the response, never stops propagation, and never throws. A send that fails never reaches your users.
It is no longer silent either. The bundle passes your application's logger to the Reporter when the service exists, so an unreachable server, and a refusal the server explains (a 401 on a mistyped key, a 402 while a payment is outstanding, a 403 on a plan gate), are written to your own log as a warning naming the status and the address. No logger was passed before, and this client has no console of its own, so a client that had never worked said so nowhere.
The subscriber reports throwables that reach the kernel. Exceptions you catch and handle yourself are, by definition, not unhandled, report those manually (below).
Forwarding logs
Set logs.enabled: true to turn log forwarding on. The Monolog handler service monitor.log_handler is always registered while the bundle is enabled; with logs.enabled: false it simply drops every record, so a monolog.yaml referencing it keeps compiling. The handler:
- buffers records at or above
logs.level; - honours the
environmentsallowlist exactly like the exception subscriber: outside the allowed environments, no record leaves your application; - skips records that carry an
exceptionin their context: those are already covered by the exception pipeline; - flushes to
/api/v1/logsin batches oflogs.max_batch, and again when the handler closes at the end of the request.
Registering the service is not enough on its own, you must attach it to Monolog as a service handler:
Now records flowing through Monolog (level warning and above by default) are batched and shipped to your dashboard. This requires monolog/monolog ^3.0 in your application.
Reporting manually
The shared Reporter is registered as the service id monitor.reporter. Because it is a private service that is not aliased to its class name, plain autowiring by type (Reporter $reporter) will not resolve it, wire it explicitly.
Using the #[Autowire] attribute on a constructor argument:
Or bind it in services.yaml:
The Reporter exposes:
reportException(Throwable $e, array $context = []): bool: POST/api/v1/ingest;sendLogs(array $logs): bool: POST/api/v1/logs;sendDependencies(array $packages): bool: POST/api/v1/dependencies.
Each returns false (and never throws) when the client is not configured or the send fails.
Scrubbing sensitive data
Before anything leaves your application, the configured scrub keys are masked recursively in the context array with [scrubbed]. Matching is a case-insensitive substring match on the key name: a configured password also masks user_password. The defaults cover password, password_confirmation, passphrase, token, signature, secret, authorization, cookie, referer, referrer and api_key. Add your own keys through the scrub option, see Configuration.
From version 0.3.1, a hyphen and an underscore count as the same character (api_key also masks an x-api-key key), and the scrubber reaches into the strings that carry named values. The request URL (request.url) loses the values of its parameters named in the list (?token=abc comes out as ?token=%5Bscrubbed%5D) and every path segment made of forty letters or digits in a row; a JSON value written as a string is opened and masked by key; and the message of every forwarded log goes through the same rule: when it starts with a URL or is JSON, the values it carries are masked as above, and the rest of its text is kept. Also from version 0.3.1, four URL parameters are masked whatever the scrub list holds, because an address carrying one opens a door until it expires: hash (login links, login_link), _hash (the UriSigner), sig and signature; the name must be exact, so a content_hash parameter or a hash key stay. See the core's scrubbing page.
The core adds a second pass: values shaped like an email address, an IBAN, a card number, a French social security number or a French phone number are masked wherever they sit, message text and URL segments included, and are named in the mask ([redacted:email]). It is on by default and is configured by the redact and redact_custom keys of the monitor tree, an empty redact list turning it off. See Configuration and the core's scrubbing page.
Dependency scanning
The bundle does not ship a console command to snapshot your composer.lock (unlike the Laravel SDK). The underlying Reporter::sendDependencies() method exists, and the server's /api/v1/dependencies endpoint is platform-neutral, so you can wire your own console command or CI step that reads composer.lock and posts the package list with your project key. A first-class command may arrive in a future bundle release.
You are reading the Symfony Bundle v1.0 documentation.