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

Monolog Enricher Laravel Package

newrelic/monolog-enricher

New Relic Monolog Enricher adds New Relic linking metadata to Monolog log records, helping correlate PHP application logs with traces and errors for faster debugging and observability.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is two Monolog components: a Processor that stamps each record with New Relic linking metadata (trace.id, span.id, entity.guid, entity.name, hostname) and a Handler that ships the record straight to the New Relic Logs HTTP API. There is no service provider and no Laravel auto-discovery — you wire it into config/logging.php yourself.

  1. composer require newrelic/monolog-enricher (and make sure newrelic/newrelic-php-agent / the PHP extension is installed if you want trace correlation).
  2. Set NEW_RELIC_LICENSE_KEY and, for EU accounts, the EU ingestion endpoint.
  3. Define a channel:
// config/logging.php
'newrelic' => [
    'driver'      => 'monolog',
    'level'       => env('LOG_LEVEL', 'info'),
    'handler'     => \NewRelic\Monolog\Enricher\Handler::class,
    'handler_with'=> ['licenseKey' => env('NEW_RELIC_LICENSE_KEY')],
    'processors'  => [\NewRelic\Monolog\Enricher\Processor::class],
],
  1. First use case: fan out to it from your stack, e.g. 'stack' => ['channels' => ['daily', 'newrelic']]. Then Log::info('order placed', ['order_id' => $id]) and check New Relic Logs for message: 'order placed' with trace.id present; click through to the trace. That trace→log pivot is the whole reason to adopt this package.

Where to look first: the README's two snippets and src/Processor.php — the processor is a dozen lines that call the agent's linking-metadata function, and reading it tells you exactly which fields you'll get (and what's silently absent when the agent is off).

Implementation Patterns

Don't replace local logging — stack it. Sending everything only to the API kills tail -f/storage/logs workflows. Keep daily/stderr in the stack and add newrelic as a peer channel. In dev/test, drop the channel entirely ('channels' => app()->isProduction() ? [...] : ['daily']) so you don't burn Logs ingest on local noise.

Use the Processor without the Handler. If you already ship logs to New Relic via Fluent Bit, the infrastructure agent, or CloudWatch Firehose, you only need trace correlation. Push the processor onto existing channels ('processors' => [Processor::class]) and keep your current transport. This is the lower-risk adoption path given the package's age.

Add application attributes with your own processor. Processor only adds agent metadata. For user.id, tenant, request_id, push a small processor that writes into $record->extra (Monolog 2 array style) — the bundled JSON formatter flattens extra into top-level attributes, which is what New Relic queries want. Register it after the enricher so ordering is obvious in config, and keep context flat: New Relic flattens nested arrays into dotted keys and chokes on non-scalars, so never put raw models or exceptions in context.

Wrap the handler for throughput. The handler performs a synchronous HTTP write per record; a chatty endpoint can add tens of ms per log line. Put a BufferHandler/FingersCrossedHandler in front, or better, log to a channel and let an async shipper do the network work. For high-volume apps, buffer with flushOnClose and a reasonable limit.

Queue correlation. trace.id/span.id are only populated when there's an active agent transaction. HTTP requests get one automatically; queue workers don't unless you start a transaction (job middleware calling newrelic_start_transaction() or the New Relic Laravel instrumentation). Otherwise job logs land with entity.* but no trace link — verify before you promise "click log → trace" for queued work.

Standardize field names. New Relic treats message, timestamp, level, service.name, hostname specially. Align service.name with your APM app name (via NEW_RELIC_APP_NAME or a processor) or logs and traces sit in separate entities and the correlation stops working.

Gotchas and Tips

  • Monolog 3 will likely break it. Last release is March 2021, i.e. Monolog 2-era signatures (arrays, Logger::DEBUG). Laravel 9+/10+ ship Monolog 3, where handler signatures and Level constants changed. If you hit type errors, either pin monolog/monolog:^2.0 in a legacy app or copy Processor.php (it's tiny) into app/Logging and maintain it — the enrichment logic is not worth a dependency conflict.
  • Silent no-op without the extension. If newrelic isn't loaded, or the agent is disabled/newrelic.linking_metadata isn't available, the processor adds nothing and you get plain JSON logs with no warning. Assert extension_loaded('newrelic') in a health check or boot-time check.
  • EU endpoint. The default host is the US Logs API. EU accounts must point the handler at https://log-api.eu.newrelic.com/logs/v1 (constructor argument / handler_with) or you'll get 403s and see nothing, with no clear error surfaced through Monolog by default.
  • License key, not Insights insert key. Ingestion rejects the wrong key type. Keep the key in env, never in config/logging.php defaults committed to git.
  • Unmaintained, 25 stars, zero dependents. Treat it as three small classes rather than infrastructure. Budget for vendoring; there will be no upstream fix for Monolog 3, PSR-3 changes, or API endpoint drift.
  • Debugging locally: push a Monolog\Handler\TestHandler alongside and assert on $record['extra']['trace.id']; also call the agent's linking-metadata function directly to see the raw array. If fields are missing, the problem is almost always the agent/transaction, not the package.
  • Handler config is positional. handler_with keys map to constructor argument names; extra options (level, bubble, endpoint) must match the signature exactly — mismatches throw at container resolution, not at config load.
  • Extension points: a custom Formatter (subclass the bundled JSON one) if you need to rename keys or enforce scalar-only output, and a custom handler if you want batching/gzip. Both are ~50 lines; don't fight the package for features it never had.
  • Watch ingest cost. One record per log line, DEBUG in production, and a stack that duplicates to file + API can triple volume. Set the channel's level to info or above and audit what Log::debug calls remain.
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.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource