TraceReplay Documentation
High-fidelity process tracking, deterministic replay, and AI debugging for Laravel — production & enterprise ready.
TraceReplay is not a standard error logger. It is a full-fledged execution tracer that captures every step of your complex workflows, reconstructs them with a waterfall timeline, and offers one-click AI debugging when things go wrong. It supports Laravel 10, 11, 12, and 13.
Installation
composer require iazaran/trace-replay
Publish the config:
php artisan vendor:publish --tag=trace-replay-config
Run migrations:
php artisan migrate
Migrations run automatically without publishing. They use json columns for full MySQL, MariaDB, PostgreSQL, and SQLite compatibility.
Publishing Views
TraceReplay ships with a polished, dark-themed dashboard featuring a waterfall timeline, syntax-highlighted JSON inspector, and live stats — all styled and ready to use out of the box. Publishing the views lets you customise the layout, colours, or add your own branding:
php artisan vendor:publish --tag=trace-replay-views
This copies the Blade templates to resources/views/vendor/trace-replay/ where you can edit them freely. The package will automatically use your published versions instead of its built-in views.
Recommended: Enable HTTP Middleware
To automatically trace all HTTP requests, add the TraceMiddleware to your application. This provides instant visibility into every request without manual instrumentation.
For Laravel 10 (app/Http/Kernel.php):
protected $middlewareGroups = [
'web' => [
// ...
\TraceReplay\Http\Middleware\TraceMiddleware::class,
],
];
For Laravel 11+ (bootstrap/app.php):
->withMiddleware(function (Middleware $middleware) {
$middleware->append(\TraceReplay\Http\Middleware\TraceMiddleware::class);
})
Configuration
Key options in config/trace-replay.php:
return [
'enabled' => env('TRACE_REPLAY_ENABLED', true),
'sample_rate' => env('TRACE_REPLAY_SAMPLE_RATE', 1.0), // 0.1 = 10% of requests
'project_id' => env('TRACE_REPLAY_PROJECT_ID', null),
'mask_fields' => ['password', 'password_confirmation', 'token', 'api_key',
'authorization', 'secret', 'credit_card', 'cvv', 'ssn', 'private_key'],
'track_db_queries' => env('TRACE_REPLAY_TRACK_DB', true),
'middleware' => ['web'], // add 'auth', 'can:...' for production
'api_middleware' => ['api'],
'allowed_ips' => array_filter(explode(',', env('TRACE_REPLAY_ALLOWED_IPS', ''))),
'queue' => [
'enabled' => env('TRACE_REPLAY_QUEUE_ENABLED', false),
'connection' => env('TRACE_REPLAY_QUEUE_CONNECTION', env('QUEUE_CONNECTION', 'sync')),
'queue' => env('TRACE_REPLAY_QUEUE_NAME', 'default'),
],
'replay' => [
'default_base_url' => env('TRACE_REPLAY_REPLAY_URL', env('APP_URL')),
'timeout' => env('TRACE_REPLAY_REPLAY_TIMEOUT', 30),
],
'retention_days' => env('TRACE_REPLAY_RETENTION_DAYS', 30),
'notifications' => [
'on_failure' => env('TRACE_REPLAY_NOTIFY_ON_FAILURE', false),
'channels' => ['mail'],
'mail' => ['to' => env('TRACE_REPLAY_NOTIFY_EMAIL')],
'slack' => ['webhook_url' => env('TRACE_REPLAY_SLACK_WEBHOOK')],
],
'ai' => [
'driver' => env('TRACE_REPLAY_AI_DRIVER', 'openai'), // openai | anthropic | ollama
'api_key' => env('TRACE_REPLAY_AI_KEY'),
'model' => env('TRACE_REPLAY_AI_MODEL', 'gpt-4o'),
],
'auto_trace' => [
'jobs' => env('TRACE_REPLAY_AUTO_TRACE_JOBS', true),
'commands' => env('TRACE_REPLAY_AUTO_TRACE_COMMANDS', false),
'livewire' => env('TRACE_REPLAY_AUTO_TRACE_LIVEWIRE', true),
'exclude_commands' => ['queue:work', 'queue:listen', 'horizon', 'schedule:run',
'schedule:work', 'trace-replay:prune', 'trace-replay:export'],
],
];
Manual Tracing
Wrap any complex logic in TraceReplay::step(). Each callback's return value is passed through transparently.
use TraceReplay\Facades\TraceReplay;
class BookingService
{
public function handleBooking(array $payload): void
{
TraceReplay::start('Flight Booking', ['channel' => 'web']);
try {
$inventory = TraceReplay::step('Validate Inventory', function () use ($payload) {
return Inventory::check($payload['flight_id']);
});
TraceReplay::checkpoint('Inventory OK', ['seats' => $inventory->seats]);
TraceReplay::context(['user_tier' => auth()->user()->tier]);
TraceReplay::step('Charge Credit Card', function () use ($payload) {
return PaymentGateway::charge($payload['amount']);
});
TraceReplay::end('success');
} catch (\Exception $e) {
TraceReplay::end('error');
throw $e;
}
}
}
API Reference
| Method | Description |
|---|---|
TraceReplay::start(name, tags[]) | Start a new trace; returns Trace or null |
TraceReplay::step(label, callable, extra[]) | Wrap callable — records timing, memory, DB queries, cache, HTTP, mail, logs & errors |
TraceReplay::measure(label, callable) | Alias for step() — semantic clarity for benchmarks |
TraceReplay::checkpoint(label, state[]) | Zero-overhead breadcrumb (no callable) |
TraceReplay::context(array) | Merge data into the next step's state_snapshot |
TraceReplay::end(status) | Finalise trace; status: 'success' or 'error' |
TraceReplay::getCurrentTrace() | Returns the active Trace model or null |
TraceReplay::setWorkspaceId(id) | Scope subsequent traces to a workspace |
TraceReplay::setProjectId(id) | Scope subsequent traces to a project |
Testing Helper
Use TraceReplay::fake() in tests to assert on your instrumentation without hitting the database. The fake captures all start(), step(), checkpoint(), and end() calls in memory.
use TraceReplay\Facades\TraceReplay;
public function test_booking_records_steps()
{
$fake = TraceReplay::fake();
$this->post('/book', ['flight_id' => 123]);
$fake->assertTraceStarted('Flight Booking');
$fake->assertStepRecorded('Validate Inventory');
$fake->assertCheckpointRecorded('Inventory validated');
$fake->assertTraceEnded('success');
$fake->assertTraceCount(1);
}
| Assertion | Description |
|---|---|
assertTraceStarted(name) | Assert a trace with the given name was started |
assertNoTraceStarted() | Assert no trace was started at all |
assertTraceCount(n) | Assert exactly n traces were started |
assertStepRecorded(label) | Assert a step with the given label was recorded |
assertCheckpointRecorded(label) | Assert a checkpoint with the given label was recorded |
assertStepCount(n, traceName?) | Assert exactly n steps in total (or in a named trace) |
assertTraceEnded(status) | Assert a trace with the given final status exists |
Auto Tracing — Jobs, Commands & Livewire
Queue jobs are automatically traced when auto_trace.jobs is enabled (default: true). No manual listener registration needed — the service provider wires everything up.
Artisan commands can be auto-traced by enabling auto_trace.commands:
TRACE_REPLAY_AUTO_TRACE_COMMANDS=true
Internal commands like queue:work, horizon, and trace-replay:prune are excluded by default (see auto_trace.exclude_commands).
Livewire Component Tracing
By enabling auto_trace.livewire, TraceReplay hooks into Livewire v2/v3's global events to automatically record distinct checkpoints on the timeline whenever your Livewire components go through hydration or dehydration cycles. This brings immense visibility into how heavily your components are performing over the wire.
The Dashboard
Navigate to https://your-app.com/trace-replay.
- Waterfall timeline — visual duration bars relative to total trace time
- Live stats — auto-refreshing counters (total, failed, avg duration)
- Search & filter — by name, IP, user ID; toggle failed-only
- Step inspector — syntax-highlighted JSON payloads and state snapshots
- Replay engine — re-run any HTTP step and view a structural JSON diff
- AI Fix Prompt — one-click prompt for Cursor, ChatGPT, or Claude
Security
Add authentication or authorization middleware in config/trace-replay.php:
// config/trace-replay.php
'middleware' => ['web', 'auth', 'can:view-trace-replay'],
// AuthServiceProvider
Gate::define('view-trace-replay', fn ($user) =>
in_array($user->email, ['admin@example.com'])
);
Or restrict by IP (exact match, comma-separated via env):
TRACE_REPLAY_ALLOWED_IPS=203.0.113.5,10.0.0.1
Data Masking
TraceReplay automatically redacts configured keys from all request/response payloads before they touch the database — including deeply nested values:
// config/trace-replay.php
'mask_fields' => ['password', 'token', 'secret', 'api_key', 'credit_card'],
// Input: ['username' => 'alice', 'password' => 'hunter2']
// Stored: ['username' => 'alice', 'password' => '********']
Replay Engine
When an HTTP request fails, TraceReplay lets you deterministically replay it from the dashboard. The replay engine proxies the original request, captures the new response, and generates an exact structural JSON diff.
AI Debugging
For any failed trace the dashboard shows an AI Fix Prompt button. It generates a structured markdown prompt containing:
- Full execution timeline with per-step timing and DB query stats
- Exact error message, file, line number, and first 20 stack frames
- Request/response payloads (sensitive fields masked)
- Step-by-step state snapshots
No API Key Required
The AI prompt feature works without any API key. Simply copy the generated prompt and paste it into ChatGPT, Claude, or any other AI assistant of your choice.
Optional: Direct AI Integration
For a seamless experience, configure an AI driver to get answers directly in the dashboard. Three drivers are supported:
# OpenAI (default)
TRACE_REPLAY_AI_DRIVER=openai
TRACE_REPLAY_AI_KEY=sk-your-openai-key
TRACE_REPLAY_AI_MODEL=gpt-4o
# Or Anthropic Claude
TRACE_REPLAY_AI_DRIVER=anthropic
TRACE_REPLAY_AI_KEY=sk-ant-your-key
TRACE_REPLAY_AI_MODEL=claude-3-5-sonnet-latest
# Or Ollama (local, no API key needed)
TRACE_REPLAY_AI_DRIVER=ollama
TRACE_REPLAY_AI_MODEL=llama3
TRACE_REPLAY_AI_BASE_URL=http://localhost:11434/api/generate
With a key configured, clicking "Ask AI" sends the prompt to your chosen provider and displays the response directly in the dashboard.
MCP / AI-Agent JSON-RPC API
Autonomous agents can query TraceReplay over JSON-RPC 2.0 at POST /api/trace-replay/mcp.
| Method | Params | Returns |
|---|---|---|
list_traces | limit, status | Array of trace summaries |
get_trace_context | trace_id | Full trace with steps |
generate_fix_prompt | trace_id | Markdown debugging prompt |
trigger_replay | trace_id | Replay result + JSON diff |
{
"jsonrpc": "2.0",
"method": "generate_fix_prompt",
"params": { "trace_id": "9b12f7e4-..." },
"id": 1
}
Data Retention
Prune old traces automatically via the scheduler:
// app/Console/Kernel.php
$schedule->command('trace-replay:prune --days=30')->daily();
php artisan trace-replay:prune --days=30 --dry-run # preview
php artisan trace-replay:prune --days=7 --status=error # prune only error traces
Export a trace for archiving:
php artisan trace-replay:export {id} --format=json
php artisan trace-replay:export {id} --format=csv
php artisan trace-replay:export {id} --format=json --output=/tmp/trace.json
php artisan trace-replay:export --status=error --format=json # all error traces
Testing
./vendor/bin/pest
The test suite (104 tests, 208 assertions) covers the full engine lifecycle, model scopes & accessors, payload masking (PII, case-insensitive), AI prompt & notification services, dashboard UI with date range filters, MCP JSON-RPC API, middleware (trace & auth), replay engine, log call tracking, TraceReplayFake assertions, Artisan commands (prune & export with validation), and Blade components — all using an in-memory SQLite database.
TraceReplay © 2026 — MIT License