Sessions · Guides
Follow the journey
Your server sends a step for each page shown or write taken by a signed-in person, recorded or not. A step holds the route's name, the status and the time it took, never a value.
Turn it on
The journey is optional, and off until you switch it on. It needs the URL and the server secret in your .env, then this line beside them:
MYBEAST_SESSIONS_JOURNEY=trueThe SDK's middleware, RecordJourneyStep, then joins the end of your web group by itself. It works after the answer has gone out: one small file written, every failure swallowed and logged once, so it never slows or breaks a request. To place it yourself, publish the config, set journey.middleware to false, and add it once:
// bootstrap/app.php, with journey.middleware set to false in config/mybeast-sessions.php
use MyBeast\Sessions\Laravel\Journey\RecordJourneyStep;
->withMiddleware(function (Middleware $middleware) {
$middleware->appendToGroup('web', RecordJourneyStep::class);
})What a step is
Each meaningful request of a signed-in person is one step, of one of these kinds:
| Kind | Meaning |
|---|---|
visit | A page shown. |
save | A write taken. |
refused | A write sent back, with the names of the refused fields. |
error | The server failed: a 5xx. |
blocked | A door shut: 401, 403, 404, 405, 419 or 429. |
A write that flashes your error toast (the journey.error_toast key, error by default) is refused; one whose toast begins with one of journey.server_error_toasts is an error. A 404 is a step only when a route of the group answers it, such as Route::fallback(): an address no route matches never reaches the middleware.
These are never steps:
- guests, and
HEADandOPTIONSrequests, - prefetches, and Inertia's partial reloads and version checks,
- JSON reads such as polling and badges, except the routes you list in
journey.data_reads, - broadcasting's sign-in, routes named
mybeast.*, and the routes injourney.skip_routes.
A route is named by its route name, never its URL. A route without a name, or with a name Sessions would refuse, goes as no name, logged once.
Who the person is
The defaults need no code: the person is the signed-in user's id, read off the guard that already has them; there's no account; their account's age comes from the user's created_at; and everyone allows it. Change any of them from a service provider:
use Illuminate\Http\Request;
use MyBeast\Sessions\Laravel\Facades\Sessions;
// In a service provider's boot(). Each hook is optional.
Sessions::journeyUsing(
account: fn (Request $request) => $request->user()?->current_team_id,
allows: fn (string $subject, Request $request) => ! $request->user()?->objects_to_journey,
// also: subject:, subjectSince:, serverError:, page:
);subjectandaccountare ids of 1 to 64 letters, digits and. _ : -, never an e-mail address. A person whose id isn't one is not sent.allowsis asked last, only for a request that would be kept: returnfalsefor a person who objected.pagenames the page answered; by default, an Inertia answer's component.
While the browser records, a step and the recording meet in one visit, by person and time, and the recording's presses and dialogs join the person's journey too. If you use Logs, each step also names its request (the X-Request-Id header), so the replay opens exactly that request's lines.
Pages shown from the browser's cache
A page Inertia shows from its prefetch cache, or any other client-side visit, never reaches your server. Keep a small route the browser tells instead:
use Illuminate\Http\Request;
use MyBeast\Sessions\Laravel\Facades\Sessions;
Route::post('journey/page-shown', fn (Request $request) => response()->json([
'kept' => Sessions::pageShown(
Sessions::routeForAddress((string) $request->input('url')),
$request->input('component'),
),
]))->middleware(['web', 'auth']);routeForAddress() reads only the address's path and answers the name of your GET route for it, or null. pageShown() keeps one visit step for the request's person and never throws. The report itself is no step.
How steps are sent
Steps wait in a spool of files on each server (journey.spool_path, never the database) and go to Sessions in compressed batches of up to 500:
- after a response, once the oldest step has waited
journey.flush_afterseconds, - after each queued job,
- and every minute while Laravel's scheduler runs (
php artisan schedule:work, or cron runningschedule:run): the SDK putsmybeast:sessions-flushon it.
php artisan mybeast:sessions-flush sends what waits at any time. A step Sessions refuses is dropped and logged, since it would never be taken. With no answer or a server error, steps wait for the next try, up to 20,000; past that, new ones are dropped, logged once.
When someone objects
The journey runs for every signed-in person, so each may object. Keep their no in your own records, answer false from allows so their steps aren't even sent, and tell Sessions:
// From a queued job with retries: each call throws until Sessions confirms it.
Sessions::objected($user->id); // their journey goes, and none is kept from now on
Sessions::objectionLifted($user->id); // kept again from now onOnce Sessions has the objection, it erases the person's journey, drops any step that still comes, and adds no presses or dialogs from their recordings. To erase a journey without an objection, for example when an account closes, see Erasing a person's data.
The journey needs no yes, recordings do
What you see
- A person's journey, by day and by visit, from their id on any recording or stuck point. A visit is one person's steps with no pause longer than 30 minutes.
- Stuck points, read from the journey every five minutes.
- Key flows: how many start the tasks you name, how many finish, and where the rest give up.