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.
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.
composer require newrelic/monolog-enricher (and make sure newrelic/newrelic-php-agent / the PHP extension is installed if you want trace correlation).NEW_RELIC_LICENSE_KEY and, for EU accounts, the EU ingestion endpoint.// 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],
],
'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).
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.
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.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.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.config/logging.php defaults committed to git.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_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.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.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.How can I help you explore Laravel packages today?