Skip to content

feat: declare request timing, route, auth, session, storage and command seams for every supported framework - #97

Closed
ExeQue wants to merge 14 commits into
lerd-env:mainfrom
ExeQue:feat/request-timing-seams
Closed

ExeQue wants to merge 14 commits into
lerd-env:mainfrom
ExeQue:feat/request-timing-seams

Conversation

@ExeQue

@ExeQue ExeQue commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

The Requests lens in lerd draws each request as a timeline and shows what happened in it, tab by tab. This adds the store side of that, so the phases of a request, its Redis commands and storage operations, its session, its route name, its Livewire components and the console commands to leave out come from declarations here rather than from anything lerd knows about a framework.

laravel/framework times Bootstrap, Routing, the Middleware stack, the Controller and each View and Terminate, names the controller by the action it runs, a closure by the file and line it was written on and a Livewire page by its component, reads the session as the store saves it, takes the route name as the matched route binds, and times every Redis command at the connection's command method, which both drivers go through. symfony/http-kernel times Bootstrap, Routing, Handle and Terminate, symfony/http-foundation times sending the response, which Laravel shares, and reads a Symfony session as it saves. symfony/security-core reports who a request runs as from the token the firewall stores. twig/twig times each template. league/flysystem times each storage operation with the path it touched, which covers Laravel's Storage facade and its S3 disks. livewire/livewire reports mount, render, updateProperty and callMethods for every component.

lerd/debug declares a lerd seam on Lerd::track, which every timeline row and tab block an app writes through that package passes, so an app's own entries and tabs show up in the Requests lens without any of it knowing lerd.

Console commands that only loop are listed under devtools.exclude_commands, as typed or by class: laravel/framework leaves out queue:work, queue:listen and schedule:work, laravel/horizon its own processes and laravel/reverb the websocket server, so the Debug window reports the jobs they run, each as its own process, and not the loop around them. symfony/console declares a command seam on Command::run, which Laravel's commands go through as well, so one listed by class is recognised.

A span seam takes an optional label key, the text the phase is shown as, and a name expression may walk a dotted property into arrays and offer alternatives separated by commas. The new kinds and keys are ignored by a lerd that does not know them.

Every other supported framework gets the same treatment. yiisoft/yii2 times the application constructor, request resolution, the controller's filters and action, each view file and the response, takes the route as the application's beforeAction runs and the user as yii\web\User takes the identity; its queue jobs report through JobInterface, with queue/listen left out. CakePHP 5 times the server bootstrap, the middleware runner, the controller action, each view and the response emitter, and cakephp/authentication reports the identity it builds. codeigniter4/framework times boot, routing, the required and route filters, the controller, each view and the response, with the matched route, and codeigniter4/shield reports who logged in. Drupal 11 times the kernel boot and preHandle, its own router listener, the controller wrapper, the main content renderer, each Twig template through TwigThemeEngine, terminate and cron, takes the route from the first access check and the user from the account proxy, and drush/drush leaves out watchdog:tail and runserver. TYPO3 13 times bootstrap, site and page matching, the frontend and backend middleware stacks, the page handler, each Extbase controller and Fluid view and the response, with the page uid or backend route as the route and the frontend or backend user. Magento 2 times bootstrap, launch, each router, the controller, layout rendering, each .phtml and the response, all on the declaring class so generated interceptors do not time a call twice, with the full action name as the route, the customer or admin as the user, and the message queue consumers as jobs. tempest/framework times kernel boot, routing, the route middleware, each view and the response, with the route's URI and the session and user. WordPress and Bedrock time the main query and the REST request and endpoint, with the matched rewrite rule or REST path and the user whose cookie WordPress validated.

An auth seam whose first argument is not a security token now reads the user's id from its name expression, which is what the frameworks without Symfony's token use.

Each supported framework's core package also lists lerd/debug under a new optional suggest_packages key, so a site that does not have it installed is offered it on its Debug tab with a button that installs it as a dev dependency.

symfony/http-client declares an http_response seam on each transport's response destructor, so an outgoing request's status, headers, sizes and timing are read once its response has completed, without changing when the client checks the status.

Nothing here does anything until the debugging work in lerd-env/lerd#2113 ships, so there is no rush to merge this before that lands.

Refs lerd-env/lerd#2088

…eams

The Requests lens in lerd draws each request as a waterfall and shows what happened in it. This adds the store side of that: span seams for the phases of a request, so the app's own work shows up as timed rows, plus a session seam, a route seam and the Livewire component lifecycle.

laravel/framework times Bootstrap, Routing, the Middleware stack, the Controller named by its action, each View and Terminate, reads the session as the store saves it and takes the route name as the matched route binds. symfony/http-kernel times Bootstrap, Routing, Handle and Terminate, symfony/http-foundation times sending the response, which Laravel shares, and reads a Symfony session as it saves. twig/twig times each template. livewire/livewire reports mount, render, updateProperty and callMethods for every component with its name and what it was handed.

A span seam carries a new optional label key with the text the phase is shown as. The span, session and route kinds are ignored by a lerd that does not know them, so these files are safe to publish ahead of the release that reads them.
@ExeQue
ExeQue requested a review from a team as a code owner October 4, 2026 16:08
ExeQue added 4 commits October 4, 2026 19:18
… of the Debug window

The Controller span now names a controller by the action it runs, a closure by the file and line it was written on, and a Livewire page by its component rather than the page controller Livewire routes it through. The name expression takes a dotted property that walks into arrays and alternatives separated by commas, the first that yields something winning.

Packages can list console commands that only loop under devtools.exclude_commands, as typed or by class. laravel/framework leaves out queue:work, queue:listen and schedule:work, laravel/horizon its own processes and laravel/reverb the websocket server, so the Debug window reports the jobs they run, each as its own process, and not the loop around them.
symfony/console declares a command seam on Command::run, which Laravel's commands run through as well, so a command listed under exclude_commands by its class is recognised and left out like one listed as typed.
laravel/framework declares a redis seam on the connection's command method, which both the phpredis and the Predis driver go through, named by the connection it ran on. league/flysystem declares a filesystem seam on each Filesystem operation, the path as its first argument, which covers Laravel's Storage facade and its S3 disks as well as Symfony's Flysystem bundle.
@ExeQue ExeQue changed the title feat: declare request timing, session, route and Livewire component seams feat: declare request timing, storage, session, route and command seams for the Debug window Oct 4, 2026
lerd/debug passes every entry an app writes, a row on the request's timeline or a block of a tab of its own, through Lerd::track, so one lerd seam there brings them all into the Requests lens.
@ExeQue ExeQue changed the title feat: declare request timing, storage, session, route and command seams for the Debug window feat: declare request timing, storage, session, route, command and app entry seams for the Debug window Oct 4, 2026
ExeQue added 2 commits October 4, 2026 20:53
symfony/security-core declares an auth seam on the token storage's setToken, so the user the firewall authenticated is shown in the request's header.
…nto, Tempest and WordPress

Every supported framework now declares where its request phases happen, so the Requests lens draws a timeline for it the way it does for Laravel and Symfony: bootstrap, routing, middleware or filters, the controller, each view rendered and sending the response, with the matched route and, where the framework has one, who the request runs as.

Seams a composer package owns live in package files (yiisoft/yii2, codeigniter4/framework and shield, cakephp/authentication, tempest/framework), while those tied to one major sit in that major's framework file. Queue workers and log tails that only loop are excluded, and their jobs still report on their own.
@ExeQue ExeQue changed the title feat: declare request timing, storage, session, route, command and app entry seams for the Debug window feat: declare request timing, route, auth, session, storage and command seams for every supported framework Oct 4, 2026
ExeQue added 5 commits October 4, 2026 22:37
Each supported framework's core package now declares lerd/debug under suggest_packages, so a site that does not have it installed is offered it on its Debug tab with a button that installs it as a dev dependency. The new key is optional and ignored by a lerd that does not know it.
symfony/http-client declares an http_response seam on each transport's response destructor, so lerd reads an outgoing request's status, headers, body size and phase times once the response has completed and the client has checked its status, without holding it and changing when that check runs.
Symfony, Drupal, Yii, CakePHP, WordPress and Bedrock declare a route_params seam where the matched route's raw parameters can be read, so a parameter a project masks is masked in the request's path. Each sits on a method no other seam claims: the controller resolver on Symfony and Drupal, the module running the action on Yii, the controller factory on CakePHP and the WP class sending headers on WordPress.
Livewire 4 compiles a single-file component into a cached class, which CacheManager::getClassName loads from the source path it is handed. A source seam there lets lerd show and open the component at its source file instead of the compiled copy.
webonyx/graphql-php is the engine Lighthouse, Rebing's GraphQL for Laravel and Overblog's bundle run on. A span on its executor puts GraphQL execution on the timeline, and a graphql_schema seam on GraphQL::promiseToExecute hands lerd the schema, so each field a request called is named by the type it returns and links to where the app defines it.
DrupalKernel::preHandle loads the legacy includes and modules for the request after the kernel has booted, and was shown as a second Bootstrap row on the timeline. It now has a label of its own.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants