π¦ 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.
- Features
- Installation
- Configuration
- Quick Start
- Pattern Examples
- API Reference
- Cross-Service Tracing
- Testing
- Performance & Async Transport
- Production Best Practices
- Examples
- Contributing
- License
- π¦ 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
- π¨ Queue Context Propagation - Automatic trace context flow through queue jobs
- β 90%+ Test Coverage - Comprehensive unit and integration test suite
composer require smartness/traceflow-laravelPublish configuration:
php artisan vendor:publish --tag=traceflow-configConfigure in .env:
TRACEFLOW_TRANSPORT=http
TRACEFLOW_SOURCE=my-laravel-app
TRACEFLOW_URL=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
# Queue context propagation (enabled by default)
TRACEFLOW_QUEUE_PROPAGATE=trueuse 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]);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
}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);
}
}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);
});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]);
}
}Access the current trace from anywhere without DI:
use Smartness\TraceFlow\Context\TraceFlowContext;
class DeeplyNestedService
{
public function doWork(): void
{
// No SDK injection needed β works anywhere during the request
$traceId = TraceFlowContext::currentTraceId();
if (TraceFlowContext::hasActiveTrace()) {
// Use $traceId for logging, external API calls, etc.
Log::info('Processing', ['trace_id' => $traceId]);
}
}
}Use the TracedJob trait for automatic trace propagation through queue jobs:
use Smartness\TraceFlow\Queue\TracedJob;
class ProcessOrderJob implements ShouldQueue
{
use TracedJob;
public function __construct(public Order $order)
{
$this->initializeTracedJob(); // Captures current trace context
}
public function handle(): void
{
// Trace context is automatically restored!
$trace = TraceFlow::getCurrentTrace();
$step = $trace->startStep(
name: 'Background Processing',
stepType: 'job'
);
try {
$this->order->process();
// Dispatching another job? Context propagates automatically.
SendConfirmationEmail::dispatch($this->order);
$step->finish(['processed' => true]);
} catch (\Exception $e) {
$step->fail($e);
throw $e;
}
}
}
// Dispatch β no need to pass trace ID manually
Route::post('/orders', function (Request $request) {
$trace = TraceFlow::startTrace(title: 'Create Order');
$order = Order::create($request->all());
ProcessOrderJob::dispatch($order); // Context captured automatically
return response()->json($order);
});The trace context chains through any depth of job dispatches: Job A -> Job B -> Job C all share the same trace ID.
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)]);
}
}// 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 (checks SDK state + TraceFlowContext)
$trace = $sdk->getCurrentTrace();
// Set trace ID manually (used by queue middleware)
$sdk->setCurrentTraceId('trace-id');
// 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']);$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', ...);$step->finish(output: ['data'], metadata: ['key' => 'value']);
$step->fail(error: 'Error message');
$step->log(message: 'Message', level: 'INFO', ...);// 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: 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();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 analyseThe 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.
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');
}
}// config/traceflow.php
return [
'transport' => 'http', // or 'kafka'
'async_http' => true, // Use async HTTP (default: true)
'source' => env('APP_NAME'),
'endpoint' => 'http://localhost:3009', // env: TRACEFLOW_URL
'api_key' => 'your-api-key',
'timeout' => 5.0,
'max_retries' => 3,
'retry_delay' => 1000,
'silent_errors' => true,
'middleware' => [
'enabled' => true,
'header_name' => 'X-Trace-Id',
],
'queue' => [
'propagate_context' => true,
],
];By default, the SDK uses non-blocking async HTTP for maximum performance:
| Transport | Overhead per Event | Blocking |
|---|---|---|
| Async HTTP (default) | ~2ms | β No |
| Blocking HTTP | ~50-200ms | β Yes |
- 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
# Enabled by default (recommended)
TRACEFLOW_ASYNC_HTTP=true
# Disable for debugging or compatibility
TRACEFLOW_ASYNC_HTTP=falseAsync (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
-
Always use silent errors in production
TRACEFLOW_SILENT_ERRORS=true
-
Use middleware for automatic HTTP tracing
-
Use the
TracedJobtrait for automatic queue context propagation -
Send heartbeats for long-running processes
-
Use environment variables for configuration
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
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
MIT License - see LICENSE file for details.
Copyright Β© 2025 Smartness