Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Sentry Monolog Adapter Laravel Package

macpaw/sentry-monolog-adapter

A Monolog handler that forwards log records to Sentry as events and breadcrumbs, making it easy to capture errors and context from Laravel and PHP applications. Configurable log levels and simple integration with existing logging stacks.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a thin adapter, not a full Sentry integration. It gives you two things: a Monolog handler that pushes records to Sentry's Hub, and a Symfony Messenger integration that keeps Sentry scope/trace context alive across async message boundaries. If you already run sentry/sentry-symfony, the Monolog side adds little; the Messenger context propagation is the actual differentiator.

Minimal path:

  1. composer require macpaw/sentry-monolog-adapter sentry/sentry-symfony
  2. Make sure a Sentry hub is available. With sentry/sentry-symfony it's autowired as Sentry\State\HubInterface. Standalone, call Sentry\init(['dsn' => ...]) early in the kernel boot and grab the hub via SentrySdk::getCurrentHub().
  3. Register the handler as a Monolog service handler rather than a built-in type:
# config/services.yaml
services:
    MacPaw\SentryMonologAdapter\SentryHandler:
        arguments:
            $hub: '@Sentry\State\HubInterface'
            $level: !php/const Monolog\Level::Error
            $bubble: false
            $tags: { app: 'checkout' }
# config/packages/monolog.yaml
monolog:
    handlers:
        sentry:
            type: service
            id: MacPaw\SentryMonologAdapter\SentryHandler
        # keep your normal file/stream handlers below it

First use case: funnel ERROR+ records to Sentry so that things logged deliberately ($logger->error('payment gateway timeout', ['gateway' => 'stripe'])) show up as Sentry messages, while uncaught exceptions still arrive via Sentry's own exception listener. Trigger a test error, confirm it lands in the project, then tune the level.

Implementation Patterns

Channel-scoped handlers. Don't send every channel to Sentry. Define the handler once, then reference it from specific channels:

monolog:
    channels: ['payments', 'messaging']
    handlers:
        sentry:
            type: service
            id: MacPaw\SentryMonologAdapter\SentryHandler
            channels: ['payments', 'messaging']

This keeps doctrine, http_client, and event noise out of your Sentry quota.

Exceptions vs. messages. The handler inspects $record->context['exception'] and routes it through captureException; otherwise it calls captureMessage with the formatted record and level mapped to a Sentry severity. The practical consequence: if you catch and re-log an exception, include the exception object in context to get a proper Sentry issue with a stack trace, not a flat message.

try {
    $this->client->charge($order);
} catch (GatewayException $e) {
    $this->logger->error('charge failed', ['exception' => $e, 'order_id' => $order->id]);
    throw $e;
}

Messenger trace continuity. Register the package's Messenger middleware/context so a log emitted inside an async handler is correlated with the HTTP request that dispatched the message. Without it, worker logs appear as orphaned events with no trace link back to the user action:

# config/packages/messenger.yaml
framework:
    messenger:
        buses:
            command_bus:
                middleware:
                    - MacPaw\SentryMonologAdapter\Messenger\SentryContextMiddleware

Check the package source for the exact middleware id in your installed version — the class name has shifted across releases.

Bubbling for dual-write. bubble: false stops records from reaching later handlers, so with the snippet above nothing hits your file log. If you want both, either set bubble: true or place the Sentry handler last in the chain and leave bubbling on for the preceding handlers.

Tags for triage. Pass static tags at construction (app, service, region) and dynamic ones via $record->extra. In Sentry these become filterable facets — worth the five minutes it takes to standardise a component key across services.

Gotchas and Tips

  • Double-reporting is the classic mistake. If sentry/sentry-symfony is installed, it registers its own Monolog handler for uncaught exceptions. Adding this handler at Error level means a thrown exception is captured twice. Either drop the level to Critical, restrict channels, or disable the bundle's handler and rely on this one.
  • The hub must exist at container compile time. In Symfony, autowiring HubInterface only works when the Sentry bundle is loaded. Without it, define the service manually against SentrySdk::getCurrentHub() inside a factory, otherwise you get "cannot autowire" at cache warmup.
  • Monolog 2 vs. 3 level types. Older releases accept Monolog\Logger::ERROR; Monolog 3 expects Monolog\Level::Error. Passing the wrong type produces a confusing TypeError during container build, not at runtime.
  • Sentry's own ignore_exceptions still applies. Records suppressed by sentry/sentry-symfony's ignore_exceptions config won't reach Sentry even if this handler fires, because filtering happens at the Hub/Scope layer. If an error is mysteriously missing, check that config first.
  • Sampling and before_send are your throttle. This handler does no rate limiting — a log loop in a worker will hammer the DSN. Set traces_sample_rate and use before_send to drop known-noisy messages before they leave the process.
  • Verify wiring before shipping. Emit $logger->error('sentry-wiring-check') from a route and from a Messenger handler, confirm both land, then check the Messenger one carries the trace id of the dispatching request.
  • Extension point: the handler is small and unsealed in spirit — subclass it and override write() if you need to reshape the payload (e.g., strip PII from $record->context before it hits captureMessage). Prefer that over forking the package.
  • Maintenance risk is real. 25 stars, zero dependents on Packagist, last release mid-2025, MIT. The surface area is small enough that you could vendor or reimplement it (a AbstractProcessingHandler subclass plus a Messenger middleware is maybe 150 lines), so have that as a fallback if the project goes quiet. Pin the version and read the diff on upgrade — pre-1.0–style API changes have happened before.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums