Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,31 @@ Pick a method whose arguments are declared parameters, since a variadic one
carries them where the capture cannot read them. A kind lerd does not know is
ignored, so a seam can be published before the release that reads it.

A `span` seam times the call instead, for a phase of the app's own work that
the Requests lens should draw on a request's waterfall. `label` is the fixed text
the phase is shown as, and `name` optionally adds what varies per call, using the
same `this` / `arg:N` expressions a job seam takes:

```yaml
devtools:
captures:
- kind: span
class: Illuminate\Routing\Route
method: run
name: this.method:getActionName
label: Controller
```

A `session` seam names the method a session store saves with; lerd calls the
store's `all()` there and shows what the session held on the request it ran in.
A `route` seam names a method the matched route passes through, with `name`
resolving to the route's own name, which the Requests lens shows beside the URI.

`exclude_commands` lists console commands that only loop, a queue worker or a
websocket server, as typed (`queue:work`) or by class. The Debug window reports
the jobs such a command runs, each as its own process, and leaves the loop out.
A project adds its own under `debug.exclude_commands` in `.lerd.yaml`.

When a major of the package itself changes what lerd runs, give that major its
own file, `<vendor>-<name>@<major>.yaml`, and list the majors in the index entry
(`{"name": "drush/drush", "versions": ["13", "11"], "latest": "13"}`). A
Expand Down
39 changes: 37 additions & 2 deletions frameworks/bedrock/1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,45 @@ notifications:
# anything a site's own code does.
nplusone: false

# Background jobs. Action Scheduler is what WordPress plugins queue work
# through; the action reports the hook it runs rather than its own class.
# WordPress is mostly functions, so only its classes can be timed: the main
# query on the front end, and on the REST API the whole request and the
# endpoint inside it. The route is the rewrite rule the front end matched, or
# the REST path dispatched. The user is read as WordPress opens the session
# tokens of a cookie it has already validated. Action Scheduler is what
# plugins queue work through; its action reports the hook it runs.
devtools:
jobs:
- class: ActionScheduler_Action
method: execute
name: this.method:get_hook
captures:
# The query variables the request resolved to, a post's name or a category.
- kind: route_params
class: WP
method: send_headers
name: this.prop:query_vars
- kind: span
class: WP
method: query_posts
label: Query
- kind: route
class: WP
method: handle_404
name: this.prop:matched_rule
- kind: span
class: WP_REST_Server
method: serve_request
label: Handle
- kind: span
class: WP_REST_Server
method: respond_to_request
name: arg:1.method:get_route
label: Controller
- kind: route
class: WP_REST_Server
method: dispatch
name: arg:1.method:get_route
- kind: auth
class: WP_Session_Tokens
method: get_instance
name: arg:1
40 changes: 38 additions & 2 deletions frameworks/cakephp/5.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,48 @@ commands:

doctor:
migrate_command: migrate
# Queued jobs. The Queue plugin runs every job through its own interface, so
# one seam covers whatever the project queues.
# The phases of a CakePHP request: the server bootstrapping the app and its
# plugins, the middleware queue, the controller action with its auto-render,
# each view rendered and the response going out. The action is named by the
# controller method it calls. The queue worker only loops; the jobs it runs
# are reported, and the command seam names it for exclusion by class too.
devtools:
exclude_commands: [queue worker]
jobs:
- implements: Cake\Queue\Job\JobInterface
method: execute
captures:
# The routing parameters the request carries once a route matched.
- kind: route_params
class: Cake\Controller\ControllerFactory
method: invoke
name: arg:1.method:getRequest.method:getAttributes.params
- kind: span
class: Cake\Http\Server
method: bootstrap
label: Bootstrap
- kind: span
class: Cake\Console\CommandRunner
method: bootstrap
label: Bootstrap
- kind: span
class: Cake\Http\Runner
method: run
label: Middleware
- kind: span
class: Cake\Controller\Controller
method: invokeAction
name: arg:1
label: Controller
- kind: span
class: Cake\View\View
method: render
name: arg:1,this.method:getTemplate
label: View
- kind: span
class: Cake\Http\ResponseEmitter
method: emit
label: Send response

# Where the schema migrations live; comparing it across checkouts picks a new worktree's database.
worktree:
Expand Down
61 changes: 59 additions & 2 deletions frameworks/drupal/11.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,66 @@ notifications:
# request, so the warning names a loop inside the framework rather than
# anything a site's own code does.
nplusone: false
# Queue items. Every queue worker is a plugin implementing this interface, so
# one seam covers cron and drush queue runs alike.
# The phases of a Drupal request. DrupalKernel does not extend Symfony's
# Kernel and Drupal routes with its own listener, so the kernel, routing and
# template seams are Drupal's; HttpKernel::handleRaw, Response::send and the
# session save still come from the Symfony packages. The controller is named
# by the callable the render-context wrapper runs, each template by its path,
# and the route by the first access check, which the router runs on the match.
# Every queue worker is a plugin implementing QueueWorkerInterface, so one job
# seam covers cron and drush queue runs alike.
devtools:
exclude_commands: [server, quick-start]
jobs:
- implements: Drupal\Core\Queue\QueueWorkerInterface
method: processItem
captures:
# The route's raw parameters, before upcasting, from the attribute Drupal's
# router keeps them in, read as the controller is resolved.
- kind: route_params
class: Drupal\Core\Controller\ControllerResolver
method: getController
name: arg:1.prop:attributes.method:all._raw_variables.method:all
- kind: span
class: Drupal\Core\DrupalKernel
method: boot
label: Bootstrap
- kind: span
class: Drupal\Core\DrupalKernel
method: preHandle
label: Prepare
- kind: span
class: Drupal\Core\Http\EventListener\RouterListener
method: onKernelRequest
label: Routing
- kind: span
class: Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber
method: wrapControllerExecutionInRenderContext
name: arg:1
label: Controller
- kind: span
implements: Drupal\Core\Render\MainContent\MainContentRendererInterface
method: renderResponse
name: arg:3.method:getRouteName
label: Render
- kind: span
class: Drupal\Core\Template\TwigThemeEngine
method: renderTemplate
name: arg:1
label: View
- kind: span
class: Drupal\Core\DrupalKernel
method: terminate
label: Terminate
- kind: span
class: Drupal\Core\Cron
method: run
label: Cron
- kind: route
class: Drupal\Core\Access\AccessManager
method: check
name: arg:1.method:getRouteName
- kind: auth
class: Drupal\Core\Session\AccountProxy
method: setAccount
name: arg:1.method:id
23 changes: 22 additions & 1 deletion frameworks/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,17 @@
],
"packages": [
{"name": "apache-solr-for-typo3/solr"},
{"name": "cakephp/authentication"},
{"name": "cakephp/cakephp"},
{"name": "cakephp/migrations"},
{"name": "cakephp/queue"},
{"name": "codeigniter4/framework"},
{"name": "codeigniter4/queue"},
{"name": "codeigniter4/shield"},
{"name": "doctrine/doctrine-fixtures-bundle"},
{"name": "doctrine/doctrine-migrations-bundle"},
{"name": "doctrine/mongodb-odm-bundle"},
{"name": "drupal/core"},
{"name": "drupal/elasticsearch_connector"},
{"name": "drupal/memcache"},
{"name": "drupal/redis"},
Expand All @@ -161,9 +166,14 @@
{"name": "laravel/cashier"},
{"name": "laravel/cloud-cli"},
{"name": "laravel/dusk"},
{"name": "laravel/framework"},
{"name": "laravel/horizon"},
{"name": "laravel/reverb"},
{"name": "league/flysystem"},
{"name": "league/flysystem-aws-s3-v3"},
{"name": "lerd/debug"},
{"name": "livewire/livewire"},
{"name": "magento/framework"},
{"name": "meilisearch/meilisearch-php"},
{"name": "meilisearch/search-bundle"},
{"name": "mongodb/laravel-mongodb"},
Expand All @@ -177,23 +187,34 @@
{"name": "php-amqplib/php-amqplib"},
{"name": "predis/predis"},
{"name": "pusher/pusher-php-server"},
{"name": "roots/wordpress"},
{"name": "sensiolabs/gotenberg-bundle"},
{"name": "sentry/sentry"},
{"name": "smi2/phpclickhouse"},
{"name": "spatie/ray"},
{"name": "stripe/stripe-php"},
{"name": "symfony/amqp-messenger"},
{"name": "symfony/beanstalkd-messenger"},
{"name": "symfony/console"},
{"name": "symfony/http-client"},
{"name": "symfony/http-foundation"},
{"name": "symfony/http-kernel"},
{"name": "symfony/mercure-bundle"},
{"name": "symfony/messenger"},
{"name": "symfony/notifier"},
{"name": "symfony/panther"},
{"name": "symfony/redis-messenger"},
{"name": "symfony/scheduler"},
{"name": "symfony/security-core"},
{"name": "tempest/command-bus"},
{"name": "tempest/database"},
{"name": "tempest/framework"},
{"name": "twig/twig"},
{"name": "typesense/typesense-php"},
{"name": "typo3/cms-core"},
{"name": "typo3/cms-scheduler"},
{"name": "vladimir-yuldashev/laravel-queue-rabbitmq"}
{"name": "vladimir-yuldashev/laravel-queue-rabbitmq"},
{"name": "webonyx/graphql-php"},
{"name": "yiisoft/yii2"}
]
}
77 changes: 77 additions & 0 deletions frameworks/magento/2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -293,3 +293,80 @@ notifications:
# request, so the warning names a loop inside the framework rather than
# anything a site's own code does.
nplusone: false

# The phases of a Magento request. Magento generates an Interceptor subclass
# for any class with plugins whose override calls parent::, so every seam
# names the declaring class; extends: would fire on both. The route is the
# full action name, e.g. catalog_product_view. The consumer runner only loops;
# the messages it handles are reported as jobs.
devtools:
exclude_commands: [queue:consumers:start]
jobs:
- class: Magento\Framework\MessageQueue\Consumer
method: dispatchMessage
- class: Magento\AsynchronousOperations\Model\MassConsumerEnvelopeCallback
method: execute
captures:
- kind: span
class: Magento\Framework\App\Bootstrap
method: create
label: Bootstrap
- kind: span
class: Magento\Framework\App\Http
method: launch
label: Handle
- kind: span
class: Magento\Framework\App\Router\Base
method: match
name: this
label: Routing
- kind: span
class: Magento\UrlRewrite\Controller\Router
method: match
name: this
label: Routing
- kind: span
class: Magento\Cms\Controller\Router
method: match
name: this
label: Routing
- kind: span
class: Magento\Framework\App\Router\DefaultRouter
method: match
name: this
label: Routing
- kind: span
class: Magento\Framework\App\FrontController
method: processRequest
name: arg:1.method:getFullActionName
label: Controller
- kind: span
class: Magento\Framework\View\Result\Layout
method: renderResult
name: this
label: Render
- kind: span
class: Magento\Framework\View\TemplateEngine\Php
method: render
name: arg:2
label: View
- kind: span
class: Magento\Framework\HTTP\PhpEnvironment\Response
method: sendResponse
label: Send response
- kind: route
class: Magento\Backend\App\AbstractAction
method: dispatch
name: arg:1.method:getFullActionName
- kind: route
class: Magento\Framework\App\Action\Action
method: dispatch
name: arg:1.method:getFullActionName
- kind: auth
class: Magento\Customer\Model\Session
method: isLoggedIn
name: this.method:getCustomerId
- kind: auth
class: Magento\User\Model\User
method: getAclRole
name: this.method:getId
8 changes: 8 additions & 0 deletions frameworks/tempest/3.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,11 @@ commands:

doctor:
migrate_command: migrate

# Each async command runs in its own command:handle child of the monitor,
# which only loops, as do the log tails.
devtools:
exclude_commands: [command:monitor, tail:logs, tail:debug]
jobs:
- class: Tempest\CommandBus\HandleAsyncCommand
method: __invoke
Loading
Loading