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
Requires
- php: ^8.1
- ext-json: *
- guzzlehttp/guzzle: ^7.8
- illuminate/http: ^10.0|^11.0
- illuminate/support: ^10.0|^11.0
- ramsey/uuid: ^4.7
Requires (Dev)
- laravel/pint: ^1.26
- mockery/mockery: ^1.6
- orchestra/testbench: ^8.0|^9.0
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.5
Suggests
- enqueue/rdkafka: Required for Kafka transport support (^0.10)
README
๐ฆ Packagist Package: This package is automatically synchronized to
smartpricing/traceflow-laravelfor Packagist distribution. Install via Composer from the split repository.
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
- Installation
- Configuration
- Quick Start
- Pattern Examples
- API Reference
- Cross-Service Tracing
- Testing
- Performance & Async Transport
- Production Best Practices
- Examples
- Contributing
- License
โจ 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
- Fire-and-forget:
send()returns immediately without waiting for HTTP response - Promise-based: Uses Guzzle async promises under the hood
- Auto-flush: Promises automatically settled on Laravel shutdown
- 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
-
Always use silent errors in production
TRACEFLOW_SILENT_ERRORS=true
-
Use middleware for automatic HTTP tracing
-
Pass trace IDs to queued jobs
-
Send heartbeats for long-running processes
-
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:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - 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