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

Newrelic Bundle Laravel Package

tiime/newrelic-bundle

Symfony bundle that integrates New Relic monitoring into your app, handling transaction naming, custom metrics, and exception tracking with minimal boilerplate. Simplifies APM setup so developers get clearer visibility into performance and errors.

View on GitHub
Deep Wiki
Context7

Getting Started

First, the framing: tiime/newrelic-bundle is a Symfony bundle, not a Laravel package. There is no service provider, no config/newrelic.php, no facade. If you're a Laravel developer, its day-to-day value to you is as a reference implementation of how to wire the New Relic PHP agent into a framework's request lifecycle — and as a drop-in if you also maintain Symfony services. In a pure Laravel app you'll use the newrelic_* functions directly (or a package like nrgy), but the patterns below transfer almost 1:1.

Minimal steps in a Symfony app:

composer require tiime/newrelic-bundle
# The bundle is a thin wrapper — it does nothing without the agent:
# install newrelic-php5 / newrelic.so and set newrelic.license in newrelic.ini
php -m | grep newrelic   # must print "newrelic"
// config/bundles.php
return [
    // ...
    Tiime\NewRelicBundle\TiimeNewRelicBundle::class => ['all' => true],
];
# config/packages/tiime_new_relic.yaml
tiime_new_relic:
    enabled: true
    app_name: "%env(NEW_RELIC_APP_NAME)%"
    interactions:
        enabled: true
        rules:
            - route      # transaction name = Symfony route name
            - controller
    exceptions:
        enabled: true
        ignored_exceptions:
            - Symfony\Component\HttpKernel\Exception\NotFoundHttpException
            - App\Exception\ValidationException

Where to look first: the bundle's Resources/config/ directory (service definitions and the service ID / interface name of the interactor), then its DependencyInjection/Configuration.php — that file is the manual; every YAML key you can use is declared there.

First use case: get a human-readable transaction name plus one business dimension. Out of the box, New Relic names transactions after the URL, so /orders/1234 and /orders/5678 fragment into different metrics. Enabling the route rule collapses them into orders_show, and one custom parameter lets you actually slice the data:

public function show(int $id, NewRelicInteractorInterface $newRelic): Response
{
    $newRelic->addCustomParameter('order_id', $id);
    $newRelic->addCustomParameter('tenant', $this->tenantContext->slug());
    // ...
}

That's the whole 20% that buys you 80% of the value.

Implementation Patterns

Inject the interface, never call the globals. The bundle's central extension point is an interactor interface (NewRelicInteractorInterface — confirm the exact FQCN in the bundle config). Inject it everywhere instead of calling newrelic_add_custom_parameter() directly. Two reasons: the bundle ships a null/no-op implementation you can bind in test, and you can decorate it to add batching, redaction, or guards for when the extension isn't loaded.

final class OrderCheckout
{
    public function __construct(private NewRelicInteractorInterface $newRelic) {}

    public function handle(Cart $cart): void
    {
        $this->newRelic->addCustomParameter('cart_items', $cart->count());
        $this->newRelic->addCustomParameter('cart_value_bucket', $this->bucket($cart->total()));
        $this->newRelic->addCustomMetric('Cart/Converted', 1);
    }
}

Transaction naming as a policy, not a line of code. Do the naming once, centrally. In Symfony the bundle does it via a kernel/request listener driven by the interactions.rules config. In Laravel you replicate it with middleware:

// app/Http/Middleware/NewRelicTransactionName.php
public function handle($request, Closure $next)
{
    if (extension_loaded('newrelic')) {
        newrelic_name_transaction(
            $request->route()?->getName() ?? $request->path()
        );
    }
    return $next($request);
}

Register it after routing middleware runs, or $request->route() is null and every transaction is named after the path — the most common silent failure in both frameworks.

Console commands and queue workers are where APM earns its keep. Web transactions are usually fine; the 2am incidents live in workers. Wrap each job:

public function handle(Job $job): void
{
    if (extension_loaded('newrelic')) {
        newrelic_background_job(true);
        newrelic_name_transaction('queue/' . class_basename($job));
        newrelic_add_custom_parameter('attempt', $job->attempts());
    }

    try {
        $job->execute();
    } catch (Throwable $e) {
        newrelic_notice_error($e);
        throw $e;
    } finally {
        newrelic_end_of_transaction();
    }
}

The finally block matters: a daemon-style worker (queue:work with no restart) runs many jobs in one PHP process, and the agent's transaction is per-process unless you explicitly end it. Without it, all jobs after the first get attributed to whatever transaction was opened first.

Error reporting: pick one path. The bundle's exceptions listener reports a request-killing exception automatically. If you also call newrelic_notice_error() in a global exception handler, you get double-counted error rates and confusing stack traces. Choose the listener and drop the manual call, or disable the listener and report manually — never both.

Deployment markers from CI. Most of these bundles expose the New Relic REST API for deployment recording. In a Laravel app it's a one-liner in your deploy script:

curl -X POST "https://api.newrelic.com/v2/applications/${APP_ID}/deployments.json" \
  -H "X-Api-Key: ${NEW_RELIC_API_KEY}" \
  -H "Content-Type: application/json" \
  -d "{\"deployment\":{\"revision\":\"${GIT_SHA}\",\"changelog\":\"${CI_COMMIT_TITLE}\"}}"

This is what lets you answer "did the p95 regress at 14:02, and what shipped at 14:02?" — the single highest-leverage thing after transaction naming.

Testing. Bind a fake interactor in the test container and assert on recorded calls. This is the reason to inject the interface rather than calling globals: your checkout tests don't need newrelic.so present, and you get a regression test that says "we still tag tenant on every order." With globals, you'd be calling extension_loaded() guards forever.

Gotchas and Tips

It's a no-op when the extension is absent — silently. Every newrelic_* call returns null and logs nothing if newrelic.so isn't loaded or the daemon socket is unreachable. Your code passes, CI passes, production has no data, and nothing anywhere tells you. Add a startup assertion (php -m | grep newrelic in your deploy healthcheck) or log a warning once when !extension_loaded('newrelic') && $env === 'prod'.

Transaction naming is first-write-wins. newrelic_name_transaction() only takes effect on the first call per transaction. If your interactions.rules order is route then controller, and some earlier listener already named the transaction after the URI, your rules are dead weight. When names look wrong, suspect a listener priority conflict — dump the effective listener order before touching config.

Config is cached; newrelic.ini and bundle config fight each other. newrelic.appname in the ini and app_name in the bundle YAML can disagree, and Symfony's config cache means your edit did nothing until cache:clear. Also on a multi-tenant setup, changing the app name at runtime after the first transaction has started silently doesn't apply — you must set it before the transaction begins.

Attribute budget is small and hard-capped. Roughly 64 custom parameters per transaction, ~255 chars each, and a few KB total. Passing a full serialized request body or a JSON blob of headers will get silently truncated or dropped, and the drop is not logged. Never pass PII (emails, names, tokens) — this data leaves your network. Bucket your values (cart_value_bucket: "100-250") instead of sending raw numbers you'll never use in a facet.

Long-running processes leak transaction state. Covered above, but worth restating because it produces confidently wrong dashboards: a Messenger worker or Octane process will attribute hours of work to one transaction unless you end_of_transaction/start_transaction per unit of work. Related: Octane's persistent workers need the same treatment as Symfony's — this is the #1 thing people get wrong moving from php-fpm to a resident worker model.

Don't let the agent break the request. The agent runs in-process; a daemon hiccup can add latency. If you're latency-sensitive, set newrelic.special = api or gate on a sampling flag so you can turn it off without a deploy.

Maintenance risk is the real gotcha. Two stars, zero dependents, and this is a fork lineage (it descends from ekino/newrelic-bundle, which itself descends from Exeu/NewRelicBundle). That's a small team maintaining code that touches your framework's kernel. Concrete mitigations: pin to an exact version ("2.x.y" not "^2.0"), read the diff before every bump, and keep the surface area you depend on to three things — transaction naming, custom parameters, exception reporting. Everything else you can do with raw newrelic_* calls. If the bundle stalls, those three features are a weekend of middleware and a listener to replace, and you'll have lost nothing.

Extension points worth knowing. Decorate the interactor interface to intercept all agent calls in one place — that's your hook for redaction, sampling, or a debug mode that logs instead of sends. For transaction naming beyond what config covers (say, naming by tenant + route), a small listener with an explicit priority beats fighting the rules array.

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.
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle