TraceReplay

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

MethodDescription
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);
}
AssertionDescription
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.

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:

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.

MethodParamsReturns
list_traceslimit, statusArray of trace summaries
get_trace_contexttrace_idFull trace with steps
generate_fix_prompttrace_idMarkdown debugging prompt
trigger_replaytrace_idReplay 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