appneta/traceview-twig
Twig extension for AppNeta TraceView that instruments template rendering, adding Twig template layers and render timings to PHP APM traces so you can see which templates are slow.
Install the base instrumentation first. This package is only a Twig bridge; the actual tracing is done by the AppNeta TraceView PHP agent (a native traceview.so extension, not a Composer package). If the agent isn't loaded, appneta/traceview-twig will install and boot fine but emit nothing.
composer require appneta/traceview-twig
php -m | grep -i traceview # must return the agent module
Confirm your Twig stack. In Laravel this almost always means rcrowe/twigbridge. Check composer show | grep twig — this package predates Twig 3, so expect it to target Twig 1.x/2.x. Verify the constraint before you commit; a version conflict is the most likely reason you can't install it at all.
Register the extension with the Twig environment. With TwigBridge, add the extension class to the extensions array in config/twigbridge.php:
'extensions' => [
// ...
\AppNeta\TraceView\Twig\TraceViewExtension::class,
],
If you build Twig manually, use $twig->addExtension(new TraceViewExtension()).
Where to look first. Read the package's src/ for the extension class and any Twig_Extension/AbstractExtension subclass — that's the whole surface area. Then read the AppNeta PHP agent docs for layer naming and sampling, because that's where 90% of the actual behavior lives.
First use case: open a page that renders several partials, and confirm in the TraceView UI that template rendering appears as a distinct layer/span under the request rather than being folded into generic PHP time. That single view — "which template is slow" — is the reason to install this.
Instrument the render boundary, not every template. The useful signal is per-render (or per-partial) duration attributed to a named layer. Wrap your top-level render call so you get a span around the whole template, and let the extension handle the sub-template naming:
$html = traceview_wrap('twig.render', function () use ($twig, $name, $ctx) {
return $twig->render($name, $ctx);
});
Use whatever the agent's actual span API is (check php -i | grep traceview for enabled functions); the shape above is the pattern, not literal API.
Name layers after the template, not after the controller. A layer called twig:partials/nav aggregates across every page that includes the nav, which is what you want for spotting a globally slow partial. Controller-based names fragment the data.
Environment-gate registration. Register the extension only when the agent is loaded and you're in an environment worth tracing, so local dev and CI don't pay the overhead:
if (extension_loaded('traceview') && app()->environment(['production', 'staging'])) {
$twig->addExtension(new TraceViewExtension());
}
Note that TwigBridge's extensions config is not conditional out of the box — you'll need a service provider or a TwigBridge::extend() call to do this cleanly.
Pair it with a request-level span. Traces are far more readable when the Twig layer sits inside an entry span tagged with route name and a user/tenant identifier. Add a middleware that opens the span and sets the context, then let the Twig extension nest underneath.
Use it to adjudicate the view-vs-query argument. The recurring workflow is: a page is slow, is it N+1 queries or a pathological template loop? Having Twig time as its own layer turns that from a guess into a measurement. Make it a habit to check the layer breakdown before profiling.
For long-running workers (Octane, queue consumers), confirm the agent supports the process model before relying on template spans there — a native extension that assumes per-request lifecycle can produce misleading traces in a persistent worker.
extension_loaded('traceview') and the agent log before you touch the Twig config.Twig_Extension vs Twig\Extension\AbstractExtension, and the getName() requirement, changed in Twig 2/3. Expect to shim these if you're on a modern Twig.new TraceViewExtension() may fail if the class has constructor dependencies — resolve via app() or register a binding.{% include %}, a page with hundreds of small partials generates a lot of spans. Enable sampling in the agent and check that p99 request latency hasn't moved after rollout.render()), TwigBridge's TwigBridge::extend() hook (conditional registration), and the agent's custom-layer API if this package's naming doesn't match your conventions.How can I help you explore Laravel packages today?