Search by

zerethonapp / flow-laravel

leeseawuyhs

Laravel instrumentation adapter for Flow real trace capture.

Package info

github.com/zerethonapp/flow-laravel

pkg:composer/zerethonapp/flow-laravel

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.2.0 2026-07-28 04:10 UTC

This package is auto-updated.

Last update: 2026-08-28 04:31:37 UTC


README

Flow logo

flow-laravel

Tests License: MIT PHP Packagist Version Packagist Downloads

Laravel instrumentation adapter for Flow Phase 3 & 4 (real runtime traces).

Want to see it running before installing anything? flow-laravel-demo is a runnable Laravel app with 7 instrumented demo routes — clone it, composer install, and hit a route to see a real captured trace.

Contributing? See CONTRIBUTING.md. Found a security issue? See SECURITY.md, not a public issue. Release history: CHANGELOG.md. This project follows the Contributor Covenant.

Why Flow?

Most developers:

  • ❌ Optimize database first (without proof)
  • ❌ Guess bottlenecks based on intuition
  • ❌ Rely on generic monitoring tools

Flow shows the REAL bottleneck instantly:

  • ✅ Identifies actual slow operations
  • ✅ Prevents wasted optimization effort
  • ✅ Provides clear, explainable insights

Real Example

Your request takes 100ms. Where's the bottleneck?

Without Flow:

  • Maybe add database index? 🤔
  • Cache everything? 🤔
  • Optimize queries? 🤔

With Flow:

╔═══════════════════════════════════════════════════════════════════════════╗
║                           FLOW EXECUTION ANALYSIS                         ║
╚═══════════════════════════════════════════════════════════════════════════╝

  Top Bottleneck:    UserService.fetch
  Duration:          90ms
  Impact:            90%
  Classification:    clear bottleneck

  Database:          2ms (2%)

Conclusion: Database is NOT the problem. Focus on service logic instead.

What this package does

  • Zero-config: Automatically captures HTTP requests after install
  • Captures one real Laravel HTTP request as a Flow trace
  • Produces real nodes and edges from runtime execution
  • Stores trace records in .zerethon/flow-history.json (CLI-compatible format)
  • Supports manual service/external instrumentation for v1 practicality
  • Captures database query timings through Laravel's DB listener

Scope (v1)

Included:

  • request lifecycle node
  • controller scoped node
  • database query nodes
  • manual service traces (Flow::trace(...))
  • manual external traces (Flow::external(...))
  • JSON storage compatible with flow-cli scan/analyze

Not included yet:

  • distributed tracing
  • queue/job tracing
  • automatic service discovery
  • UI/dashboard

Install

composer require zerethonapp/flow-laravel

That's it! Flow will now automatically trace HTTP requests in every environment except testing.

Hit any route:

curl http://your-app.test/any-route

Check the trace:

cat .zerethon/flow-history.json

Or browse individual traces:

ls storage/flow-traces/

Zero-Config Experience

By default, Flow:

  • ✅ Automatically registers middleware globally
  • ✅ Captures all HTTP requests (web + api)
  • ✅ Captures controller execution
  • ✅ Captures database queries
  • ✅ Writes traces to .zerethon/flow-history.json
  • ✅ Runs everywhere except testing (production included by default — see Connected Mode below)

No code changes required.

Connected Mode (Push Traces to Flow)

By default, traces only ever land in the local .zerethon/flow-history.json file above (Offline mode) — nothing leaves your machine/server unless you configure Connected mode. To have every captured trace pushed automatically to Flow, get a project's credentials from the Flow dashboard (shown once, at project creation) and add them to .env:

FLOW_SERVER=https://flow-api.zerethon.com
FLOW_PROJECT_ID=<project uuid>
FLOW_SECRET_KEY=flw_sk_...

Then verify it's wired up correctly:

php artisan flow:install

That's it — no other code changes. Traces still write to the local file too (Connected mode is additive, not a replacement); if any of the three env vars are missing, Flow just skips the push silently and behaves exactly as it did before.

Production is enabled by default (except_environments only excludes testing, see Configuration below) — the three FLOW_* env vars above are all that's needed on a real deployed environment. If you want Flow off in a specific environment (e.g. a staging server you don't want traced), publish the config (php artisan vendor:publish --tag=flow-config) and add that environment to except_environments — this check runs before anything else in FlowServiceProvider::boot(), so an excluded environment gets zero overhead, not just a silenced push.

Building an adapter for a different language/framework? flow-docs/ADAPTER_PROTOCOL.md documents the exact wire contract this package's push implements — it's plain HTTP+JSON, no PHP/Laravel-specific mechanism involved.

Configuration (Optional)

Publish config if you want to customize behavior:

php artisan vendor:publish --tag=flow-config

The published config file at config/flow.php provides fine-grained control:

Enable/Disable

// Explicitly enable or disable (null = auto-detect based on environment)
'enabled' => env('FLOW_ENABLED'),

// Environments where Flow should NOT run
'except_environments' => ['testing'],

// URI patterns to exclude from tracing
'except' => [
    'telescope*',
    'horizon*',
],

// Sample rate (0.0 - 1.0): trace only a percentage of requests
'sample_rate' => env('FLOW_SAMPLE_RATE', 1.0),

Sources

Control which data sources are active:

'sources' => [
    'request' => true,      // Request lifecycle
    'controller' => true,   // Controller execution
    'database' => true,     // Database queries
    'external' => true,     // External HTTP calls
],

Source Options

Configure behavior per source:

'options' => [
    'database' => [
        'capture_sql' => false,      // Include SQL text in trace
        'capture_bindings' => false, // Include query bindings
    ],
],

Storage

'storage_path' => base_path('.zerethon/flow-history.json'),
'trace_directory' => storage_path('flow-traces'),
'max_records' => 1000,

Enable request capture middleware

Add middleware alias flow.trace to routes you want to capture.

Example:

Route::middleware(['flow.trace'])->group(function () {
    Route::get('/orders/{id}', [OrderController::class, 'show']);
});

Manual service and external tracing

Use the Facade:

use Zerethon\Flow\Laravel\Facades\Flow;

$order = Flow::traceService('OrderService.findOrder', function () use ($id) {
    return $this->orderService->findOrder($id);
});

$payload = Flow::traceExternal('BillingApi.charge', function () use ($order) {
    return Http::post('https://billing.example.com/charge', ['order_id' => $order->id])->json();
});

Or use the global helper:

flow()->traceService('UserService.findUser', fn () => $service->findUser($id));

Generic trace method:

Flow::trace('service', 'UserService.findUser', fn () => $service->findUser($id));
Flow::trace('external', 'PaymentApi.charge', fn () => $api->charge($amount));

Assisted Tracing

Flow provides multiple ways to make tracing easier and less verbose:

Using the Traceable Trait

Add the trait to your service classes:

use Zerethon\Flow\Laravel\Support\Traceable;

class UserService
{
    use Traceable;

    public function findUser(int $id): User
    {
        return $this->traceService('UserService.findUser', function () use ($id) {
            // Business logic here
            return User::find($id);
        });
    }

    public function syncWithExternalApi(): void
    {
        $this->traceExternal('ExternalUserApi.sync', function () {
            // External API call
            Http::post('https://api.example.com/sync');
        });
    }
}

Using PHP Attributes (Future)

use Zerethon\Flow\Laravel\Support\Trace;

class OrderService
{
    #[Trace('service')]
    public function processOrder(int $orderId): void
    {
        // Automatically traced as "OrderService.processOrder"
    }

    #[Trace('external', 'PaymentAPI.charge')]
    public function chargePayment(float $amount): void
    {
        // Automatically traced with custom label
    }
}

Note: Attribute-based tracing requires additional AOP/interceptor setup and is not yet fully implemented.

Output format

Traces are appended to:

  • .zerethon/flow-history.json (default)

Record shape matches Flow core storage:

  • traceId
  • timestamp
  • flow (schema_version, trace_id, nodes, edges, meta)
  • result (status, totalTime, nodeCount, executedNodes, optional errors)

Analyze with existing CLI

From the Laravel app root (where .zerethon/flow-history.json exists):

flow scan
flow analyze
flow status

Raw JSON mode:

flow scan --json

Demo scenario (real request trace)

Use middleware + manual service/external blocks:

// routes/web.php
Route::middleware(['flow.trace'])->get('/flow-demo', function () {
    $user = \Zerethon\Flow\Laravel\Helpers\Flow::trace('service', 'UserService.findUser', function () {
        return DB::table('users')->where('id', 1)->first();
    });

    $remote = \Zerethon\Flow\Laravel\Helpers\Flow::external('BillingApi.status', function () {
        return Http::get('https://httpbin.org/status/200')->status();
    });

    return response()->json(['user' => $user?->id, 'remote_status' => $remote]);
});

Then call the route once and run:

flow scan

Config

config/flow.php

  • enabled
  • storage_path
  • max_records
  • capture_controller
  • capture_query_sql