smartness/traceflow-laravel

Production-ready distributed tracing SDK for Laravel with event-sourced architecture, async HTTP transport, and cross-service context propagation

Installs: 0

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

pkg:composer/smartness/traceflow-laravel

v1.0.0 2026-02-03 20:56 UTC

This package is auto-updated.

Last update: 2026-02-03 21:06:41 UTC


README

๐Ÿ“ฆ Packagist Package: This package is automatically synchronized to smartpricing/traceflow-laravel for Packagist distribution. Install via Composer from the split repository.

Packagist Version PHP Version Laravel Version License: MIT Tests

Production-ready, stateless distributed tracing SDK for Laravel applications with event-sourced architecture.

TraceFlow SDK for Laravel provides enterprise-grade distributed tracing capabilities with zero local state dependencies. Built on an event-sourced architecture, it delivers comprehensive observability across microservices using HTTP or Kafka transport, without compromising application reliability or performance.

Table of Contents

โœจ Features

  • ๐Ÿ“ฆ Stateless Architecture - No Redis, no databases, pure event streaming
  • โšก Non-Blocking Performance - Async HTTP transport with <2ms overhead per event
  • ๐Ÿ”€ Transport Agnostic - Use HTTP REST API or Kafka with identical API
  • ๐Ÿงต Context-Aware - Automatic context propagation across service boundaries
  • ๐Ÿ”„ Retry Logic - Built-in exponential backoff and circuit breaker patterns
  • ๐Ÿ›ก๏ธ Production-Ready - Silent error mode ensures tracing never fails your application
  • ๐ŸŽฏ Type-Safe - Full PHP 8.1+ support with typed properties and enums
  • ๐Ÿ“ Event-Based - Append-only event model for audit trails and replay capabilities
  • ๐Ÿš€ Laravel Integration - Seamless integration via Middleware, Facade, and Service Provider
  • ๐ŸŒ Cross-Service Tracing - Propagate trace context across distributed systems
  • โœ… 90%+ Test Coverage - Comprehensive unit and integration test suite

๐Ÿ“ฆ Installation

composer require smartness/traceflow-laravel

โš™๏ธ Configuration

Publish configuration:

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

Configure in .env:

TRACEFLOW_TRANSPORT=http
TRACEFLOW_SOURCE=my-laravel-app
TRACEFLOW_ENDPOINT=http://localhost:3009
TRACEFLOW_API_KEY=your-api-key

# Optional
TRACEFLOW_TIMEOUT=5.0
TRACEFLOW_MAX_RETRIES=3
TRACEFLOW_SILENT_ERRORS=true

# Performance (Async HTTP enabled by default)
TRACEFLOW_ASYNC_HTTP=true

๐Ÿš€ Quick Start

Using Facade

use Smartness\TraceFlow\Facades\TraceFlow;

// Start a trace
$trace = TraceFlow::startTrace(
    traceType: 'api_request',
    title: 'Process User Request'
);

// Start a step
$step = $trace->startStep(name: 'Validate Input');
$step->log('Validation successful');
$step->finish(['valid' => true]);

// Finish trace
$trace->finish(['success' => true]);

Using Middleware (Recommended)

Add middleware to app/Http/Kernel.php:

protected $middleware = [
    // ...
    \Smartness\TraceFlow\Middleware\TraceFlowMiddleware::class,
];

Now all HTTP requests are automatically traced!

// In your controller
public function show(Request $request, string $id)
{
    // Get trace from request
    $trace = $request->attributes->get('trace');
    
    // Add steps
    $step = $trace->startStep(name: 'Fetch User from DB');
    $user = User::find($id);
    $step->finish(['user_id' => $user->id]);
    
    return response()->json($user);
    // Trace auto-completes after response
}

Using Dependency Injection

use Smartness\TraceFlow\TraceFlowSDK;

class UserController extends Controller
{
    public function __construct(private TraceFlowSDK $sdk)
    {
    }
    
    public function index()
    {
        $trace = $this->sdk->startTrace(
            traceType: 'list_users',
            title: 'List Users'
        );
        
        $step = $trace->startStep(name: 'Query Database');
        $users = User::all();
        $step->finish(['count' => $users->count()]);
        
        $trace->finish(['users' => $users]);
        
        return response()->json($users);
    }
}

๐Ÿ’ก Pattern Examples

Pattern 1: HTTP Request with Custom ID

use Smartness\TraceFlow\Facades\TraceFlow;

Route::post('/orders', function (Request $request) {
    // Start trace with custom ID
    $traceId = $request->header('X-Request-ID') ?? Str::uuid();
    
    $trace = TraceFlow::startTrace(
        traceId: $traceId,
        traceType: 'create_order',
        title: 'Create Order'
    );
    
    // Process order...
    $step1 = $trace->startStep(name: 'Validate Order');
    $step1->finish();
    
    $step2 = $trace->startStep(name: 'Save to Database');
    $order = Order::create($request->all());
    $step2->finish(['order_id' => $order->id]);
    
    $trace->finish(['order' => $order]);
    
    return response()->json($order);
});

Pattern 2: Service Layer Integration

class OrderService
{
    public function __construct(private TraceFlowSDK $sdk)
    {
    }
    
    public function createOrder(array $data, string $traceId): Order
    {
        // Retrieve trace in service layer
        $trace = $this->sdk->getTrace($traceId);
        
        $trace->log('Starting order creation');
        
        $step = $trace->startStep(
            name: 'Create Order',
            stepType: 'database',
            input: $data
        );
        
        try {
            $order = Order::create($data);
            
            // Send notification (nested operation)
            $this->sendOrderNotification($order, $traceId);
            
            $step->finish(['order_id' => $order->id]);
            
            return $order;
        } catch (\Exception $e) {
            $step->fail($e);
            throw $e;
        }
    }
    
    private function sendOrderNotification(Order $order, string $traceId): void
    {
        $trace = $this->sdk->getTrace($traceId);
        
        $step = $trace->startStep(name: 'Send Email Notification');
        
        // Send email...
        Mail::to($order->user)->send(new OrderCreated($order));
        
        $step->finish(['sent' => true]);
    }
}

Pattern 3: Background Jobs

use Smartness\TraceFlow\Facades\TraceFlow;

class ProcessOrderJob implements ShouldQueue
{
    public function __construct(
        public Order $order,
        public string $traceId
    ) {
    }
    
    public function handle(): void
    {
        // Retrieve trace in job
        $trace = TraceFlow::getTrace($this->traceId);
        
        $step = $trace->startStep(
            name: 'Background Processing',
            stepType: 'job'
        );
        
        try {
            // Process order...
            $this->order->process();
            
            $step->finish(['processed' => true]);
        } catch (\Exception $e) {
            $step->fail($e);
            throw $e;
        }
    }
}

// Dispatch job with trace ID
Route::post('/orders', function (Request $request) {
    $trace = TraceFlow::startTrace(title: 'Create Order');
    
    $order = Order::create($request->all());
    
    // Pass trace ID to job
    ProcessOrderJob::dispatch($order, $trace->traceId);
    
    return response()->json($order);
});

Pattern 4: Long-Running Processes

use Smartness\TraceFlow\Facades\TraceFlow;

class ImportUsersCommand extends Command
{
    public function handle(): void
    {
        $trace = TraceFlow::startTrace(
            traceType: 'batch_import',
            title: 'Import Users from CSV'
        );
        
        $users = $this->loadUsersFromCSV();
        
        foreach ($users as $index => $userData) {
            $step = $trace->startStep(
                name: "Import User #{$index}",
                input: $userData
            );
            
            User::create($userData);
            
            $step->finish();
            
            // Send heartbeat every 100 users
            if ($index % 100 === 0) {
                TraceFlow::heartbeat($trace->traceId);
            }
        }
        
        $trace->finish(['imported' => count($users)]);
    }
}

๐Ÿ“š API Reference

TraceFlowSDK Methods

// Start trace
$trace = TraceFlow::startTrace(
    traceType: 'process_type',     // Optional
    title: 'Human readable title', // Optional
    description: 'Description',    // Optional
    owner: 'team-name',           // Optional
    tags: ['tag1', 'tag2'],       // Optional
    metadata: ['key' => 'value'], // Optional
    params: $inputData,           // Optional
    traceTimeoutMs: 5000,         // Optional - custom timeout
    stepTimeoutMs: 2000           // Optional - custom step timeout
);

// Get existing trace
$trace = $sdk->getTrace('trace-id');

// Get current trace from context
$trace = $sdk->getCurrentTrace();

// Send heartbeat
$sdk->heartbeat('trace-id');

// Start step (requires active trace)
$step = $sdk->startStep(
    name: 'Step Name',
    stepType: 'database',
    input: ['data'],
    metadata: ['key' => 'value']
);

// Log message
$sdk->log('Message', 'INFO', 'event_type', ['details']);

TraceHandle Methods

$trace->finish(result: ['data'], metadata: ['key' => 'value']);
$trace->fail(error: 'Error message');
$trace->cancel();
$trace->startStep(name: 'Step Name', ...);
$trace->log(message: 'Message', level: 'INFO', ...);

StepHandle Methods

$step->finish(output: ['data'], metadata: ['key' => 'value']);
$step->fail(error: 'Error message');
$step->log(message: 'Message', level: 'INFO', ...);

๐ŸŒ Cross-Service Tracing

Service A (API Gateway)

// Service A: Start trace
$trace = TraceFlow::startTrace(title: 'User Registration');

// Call Service B with trace ID
Http::withHeaders([
    'X-Trace-Id' => $trace->traceId,
])->post('http://service-b/api/endpoint', $data);

$trace->finish();

Service B (Email Service)

// Service B: Retrieve existing trace
$traceId = request()->header('X-Trace-Id');

// Get the trace started by Service A
$trace = TraceFlow::getTrace($traceId);

// Add steps to the same trace
$trace->startStep(name: 'Send Welcome Email');

// Process...
$trace->finish();

๐Ÿงช Testing

The SDK includes comprehensive test coverage for async transport:

# Run all tests
composer test

# Run unit tests only (async transport, SDK)
composer test:unit

# Run integration tests (end-to-end scenarios)
composer test:feature

# Generate coverage report
composer test:coverage

# Run static analysis
composer analyse

Test Coverage

The SDK maintains comprehensive test coverage:

  • AsyncHttpTransport: Non-blocking behavior, retries, promise handling
  • TraceFlowSDK: Configuration, context propagation, lifecycle
  • Integration: Complete workflows, performance benchmarks
  • 90%+ code coverage with unit and feature tests

See tests/README.md for detailed testing documentation.

Example Test

use Smartness\TraceFlow\Facades\TraceFlow;

class UserControllerTest extends TestCase
{
    public function test_creates_user()
    {
        // TraceFlow::fake(); // Coming soon

        $response = $this->post('/users', ['name' => 'John']);

        $response->assertStatus(201);
        $response->assertHeader('X-Trace-Id');
    }
}

๐Ÿ“‹ Configuration Reference

// config/traceflow.php
return [
    'transport' => 'http',                    // or 'kafka'
    'async_http' => true,                     // Use async HTTP (default: true)
    'source' => env('APP_NAME'),
    'endpoint' => 'http://localhost:3009',
    'api_key' => 'your-api-key',
    'username' => 'user',
    'password' => 'pass',
    'timeout' => 5.0,
    'max_retries' => 3,
    'retry_delay' => 1000,
    'silent_errors' => true,

    'middleware' => [
        'enabled' => true,
        'header_name' => 'X-Trace-Id',
    ],
];

โšก Performance & Async Transport

By default, the SDK uses non-blocking async HTTP for maximum performance:

Performance Comparison

Transport Overhead per Event Blocking
Async HTTP (default) ~2ms โŒ No
Blocking HTTP ~50-200ms โœ… Yes

How Async Works

  1. Fire-and-forget: send() returns immediately without waiting for HTTP response
  2. Promise-based: Uses Guzzle async promises under the hood
  3. Auto-flush: Promises automatically settled on Laravel shutdown
  4. Retry logic: Exponential backoff handled asynchronously

Configuration

# Enabled by default (recommended)
TRACEFLOW_ASYNC_HTTP=true

# Disable for debugging or compatibility
TRACEFLOW_ASYNC_HTTP=false

Trade-offs

Async (default):

  • Pros: Minimal latency impact (~2ms), no additional infrastructure needed
  • Cons: Events lost if PHP crashes before shutdown, slightly higher memory usage

Blocking:

  • Pros: Guaranteed delivery before request completes
  • Cons: High latency impact (50-200ms per event), slows down user requests

๐Ÿ”’ Production Best Practices

  1. Always use silent errors in production

    TRACEFLOW_SILENT_ERRORS=true
  2. Use middleware for automatic HTTP tracing

  3. Pass trace IDs to queued jobs

  4. Send heartbeats for long-running processes

  5. Use environment variables for configuration

๐Ÿ“– Examples

See examples/ directory for:

  • BasicExample.php - Fundamental SDK usage patterns
  • CustomTimeouts.php - Configuring trace and step timeouts
  • AsyncPerformance.php - Performance comparison and async transport demo
  • Laravel API integration
  • Background jobs
  • Distributed tracing
  • Long-running processes

๐Ÿค Contributing

Contributions are welcome! Please follow these steps:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Please ensure:

  • All tests pass (composer test)
  • Code follows PSR-12 standards (composer format)
  • Static analysis passes (composer analyse)
  • You've added tests for new features

๐Ÿ“„ License

MIT License - see LICENSE file for details.

Copyright ยฉ 2025 Smartness