tracekit / laravel-apm
TraceKit APM for Laravel - Zero-config distributed tracing and code monitoring
Installs: 3
Dependents: 0
Suggesters: 0
Security: 0
Stars: 1
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/tracekit/laravel-apm
Requires
- php: ^8.1
- guzzlehttp/guzzle: ^7.0
- illuminate/cache: ^10.0|^11.0|^12.0
- illuminate/database: ^10.0|^11.0|^12.0
- illuminate/support: ^10.0|^11.0|^12.0
- open-telemetry/api: ^1.0
- open-telemetry/exporter-otlp: ^1.0
- open-telemetry/sdk: ^1.0
Requires (Dev)
- orchestra/testbench: ^8.0|^9.0
- phpunit/phpunit: ^10.0
README
Zero-config distributed tracing and performance monitoring for Laravel applications.
Features
- Zero Configuration - Works out of the box with sensible defaults
- Automatic Instrumentation - No code changes needed
- HTTP Request Tracing - Track every request, route, and middleware
- Database Query Monitoring - See every query with actual SQL and bindings
- Queue Job Tracking - Monitor Laravel jobs and queues
- Slow Query Detection - Automatically highlight slow database queries
- Error Tracking - Capture exceptions with full context
- Code Monitoring - Live debugging with breakpoints and variable inspection
- Low Overhead - < 5% performance impact
Installation
composer require tracekit/laravel-apm
Quick Start
1. Install the package
php artisan tracekit:install
2. Add your API key to .env
TRACEKIT_API_KEY=your-api-key-here TRACEKIT_SERVICE_NAME=my-laravel-app # Optional: Custom endpoint (defaults to https://app.tracekit.dev/v1/traces) # TRACEKIT_ENDPOINT=https://your-custom-endpoint.com/v1/traces
Get your API key at https://app.tracekit.dev
3. Done!
Your Laravel app is now automatically traced. Visit your TraceKit dashboard to see your traces.
Configuration
Publish the configuration file:
php artisan vendor:publish --tag=tracekit-config
This creates config/tracekit.php where you can customize:
Laravel 12 Setup
Laravel 12 changed how middleware is registered. TraceKit attempts to register middleware automatically for all Laravel versions (10, 11, and 12).
If automatic registration doesn't work, you can manually add the TraceKit middleware to your bootstrap/app.php:
use Illuminate\Foundation\Application; use Illuminate\Foundation\Configuration\Middleware; use TraceKit\Laravel\Middleware\TracekitMiddleware; return Application::configure(basePath: dirname(__DIR__)) ->withMiddleware(function (Middleware $middleware) { $middleware->web(append: [ TracekitMiddleware::class, ]); $middleware->api(append: [ TracekitMiddleware::class, ]); }) // ... rest of your configuration ->create();
For most cases, the automatic registration via the service provider should work without any manual configuration.
Configuration Options
return [ // Enable/disable tracing 'enabled' => env('TRACEKIT_ENABLED', env('APP_ENV') !== 'local'), // Your TraceKit API key 'api_key' => env('TRACEKIT_API_KEY', ''), // OTLP endpoint for sending traces 'endpoint' => env('TRACEKIT_ENDPOINT', 'https://app.tracekit.dev/v1/traces'), // Service name as it appears in TraceKit 'service_name' => env('TRACEKIT_SERVICE_NAME', env('APP_NAME', 'laravel-app')), // Sample rate (0.0 to 1.0) 'sample_rate' => env('TRACEKIT_SAMPLE_RATE', 1.0), // Enable/disable specific features 'features' => [ 'http' => env('TRACEKIT_HTTP_ENABLED', true), 'database' => env('TRACEKIT_DATABASE_ENABLED', true), 'cache' => env('TRACEKIT_CACHE_ENABLED', true), // Coming soon 'queue' => env('TRACEKIT_QUEUE_ENABLED', true), 'redis' => env('TRACEKIT_REDIS_ENABLED', true), // Coming soon ], // Routes to ignore 'ignored_routes' => [ '/health', '/up', '/_healthz', ], // Slow query threshold (ms) 'slow_query_threshold' => env('TRACEKIT_SLOW_QUERY_MS', 100), // Include query bindings in traces 'include_query_bindings' => env('TRACEKIT_INCLUDE_BINDINGS', true), // Map hostnames to service names for service graph 'service_name_mappings' => [ 'localhost:8082' => 'payment-service', 'localhost:8083' => 'user-service', ], ];
Code Monitoring (Live Debugging)
TraceKit includes production-safe code monitoring for live debugging without redeployment.
Enable Code Monitoring
Add to your .env file:
TRACEKIT_CODE_MONITORING_ENABLED=true TRACEKIT_CODE_MONITORING_POLL_INTERVAL=30 # How often to check for breakpoints (seconds) # Supported: 1, 5, 10, 15, 30, 60, 300, 600 # Lower = faster updates, higher load TRACEKIT_CODE_MONITORING_MAX_DEPTH=3 # Nested array/object inspection depth TRACEKIT_CODE_MONITORING_MAX_STRING=1000 # Max length for captured strings
Configuration Options:
enabled: Master switch for code monitoring (default:false)poll_interval: Background polling frequency in seconds (default:30)1= Every second (highest load, instant updates)5= Every 5 seconds (high load, very fast updates)10= Every 10 seconds (moderate load, fast updates)30= Every 30 seconds (recommended for production)60= Every minute (low load, slower updates)300+= Every 5+ minutes (very low load, periodic checks)
max_variable_depth: How deep to inspect nested structures (default:3)max_string_length: Maximum string length to capture (default:1000)
Add Debug Points
Add checkpoints anywhere in your code to capture variable state and stack traces:
<?php namespace App\Http\Controllers; use Illuminate\Http\Request; class CheckoutController extends Controller { public function processPayment(Request $request) { $cart = $request->input('cart'); $userId = $request->input('user_id'); // Automatic snapshot capture with label tracekit_snapshot('checkout-validation', [ 'user_id' => $userId, 'cart_items' => count($cart['items'] ?? []), 'total_amount' => $cart['total'] ?? 0, ]); try { // Process payment $result = $this->paymentService->charge($cart['total'], $userId); // Another checkpoint tracekit_snapshot('payment-success', [ 'user_id' => $userId, 'payment_id' => $result['payment_id'], 'amount' => $result['amount'], ]); return response()->json(['success' => true]); } catch (\Exception $e) { // Automatic error capture (configured in service provider) tracekit_error_snapshot($e, [ 'user_id' => $userId, 'cart_total' => $cart['total'] ?? 0, ]); return response()->json(['error' => 'Payment failed'], 500); } } }
Helper Functions
TraceKit provides convenient helper functions:
// Basic snapshot capture tracekit_snapshot('my-label', ['key' => 'value']); // Debug helper (logs + captures) tracekit_debug('debug-point', ['data' => $data]); // Error snapshot with exception details tracekit_error_snapshot($exception, ['context' => 'additional data']);
Automatic Breakpoint Management
- Auto-Registration: First call to
tracekit_snapshot()automatically creates breakpoints in TraceKit - Smart Matching: Breakpoints match by function name + label (stable across code changes)
- Background Sync: SDK polls for active breakpoints every 30 seconds
- Production Safe: No performance impact when breakpoints are inactive
What Gets Captured
Snapshots include:
- Variables: Local variables at capture point
- Stack Trace: Full call stack with file/line numbers
- Request Context: HTTP method, URL, headers, query params
- Execution Time: When the snapshot was captured
- Eloquent Models: Automatically serialized with relationships
Exception Handling
Exceptions are automatically captured when TRACEKIT_CODE_MONITORING_ENABLED=true - no additional code required!
The TraceKit service provider automatically registers an exception reporter that:
- Captures full stack traces with file and line numbers
- Records exception type, message, and context
- Includes HTTP request details (route, headers, params)
- Enables automatic code discovery for debugging
// Exceptions are automatically captured - just throw them as normal! Route::post('/payment', function (Request $request) { if (!$request->has('amount')) { // This exception will be automatically captured with full context throw new \Exception('Amount is required'); } $payment = Payment::process($request->amount); return response()->json(['payment_id' => $payment->id]); }); // You can also manually add snapshots before throwing Route::post('/checkout', function (Request $request) { try { $order = Order::create($request->all()); } catch (\Exception $e) { // Optional: Add additional context before exception is auto-captured tracekit_snapshot('checkout-failed', [ 'cart_total' => $request->input('total'), 'user_id' => auth()->id(), 'error' => $e->getMessage(), ]); throw $e; // Still automatically captured! } });
How It Works:
- Any uncaught exception is automatically reported to TraceKit
- Stack trace is formatted and attached to the current OpenTelemetry span
- Exception event includes
exception.stacktracefor code discovery - TraceKit backend parses stack traces and indexes your code locations
- Visit the "Browse Code" tab in
/snapshotsto see discovered code
Automatic Service Discovery
TraceKit automatically instruments outgoing HTTP calls made with Laravel's HTTP client to create service dependency graphs.
How It Works
When your service makes an HTTP request using Laravel's Http facade:
- ✅ TraceKit creates a CLIENT span for the outgoing request
- ✅ Trace context is automatically injected into request headers (
traceparent) - ✅ The receiving service creates a SERVER span linked to your CLIENT span
- ✅ TraceKit maps the dependency: YourService → TargetService
Supported HTTP Clients
- ✅ Laravel HTTP Client (
Illuminate\Support\Facades\Http) - Automatic! - ✅ Guzzle (Laravel's HTTP client uses Guzzle under the hood)
Zero configuration required! Just use Laravel's HTTP client as normal:
use Illuminate\Support\Facades\Http; // All of these automatically create CLIENT spans: Http::get('http://payment-service/charge'); Http::post('http://inventory-service/reserve', ['item_id' => 123]); Http::withToken($token)->get('http://user-service/profile/123');
Service Name Detection
TraceKit intelligently extracts service names from URLs:
| URL | Extracted Service Name |
|---|---|
http://payment-service:3000 |
payment-service |
http://payment.internal |
payment |
http://payment.svc.cluster.local |
payment |
https://api.example.com |
api.example.com |
This works seamlessly with:
- Kubernetes service names
- Internal DNS names
- Docker Compose service names
- External APIs
Custom Service Name Mappings
For local development or when service names can't be inferred from hostnames, add service_name_mappings to your config/tracekit.php:
return [ // ... other config // Map localhost URLs to actual service names 'service_name_mappings' => [ 'localhost:8082' => 'payment-service', 'localhost:8083' => 'user-service', 'localhost:8084' => 'inventory-service', 'localhost:5001' => 'analytics-service', ], ];
Then make HTTP calls as normal:
use Illuminate\Support\Facades\Http; // Now requests to localhost:8082 will show as "payment-service" in the service graph $response = Http::get('http://localhost:8082/charge'); // -> Creates CLIENT span with peer.service = "payment-service"
This is especially useful when:
- Running microservices locally on different ports
- Using Docker Compose with localhost networking
- Testing distributed tracing in development
Example: Multi-Service Application
use Illuminate\Support\Facades\Http; Route::post('/checkout', function (Request $request) { // This HTTP call automatically creates a CLIENT span $paymentResponse = Http::post('http://payment-service/charge', [ 'amount' => $request->input('amount'), 'user_id' => auth()->id(), ]); // This one too! $inventoryResponse = Http::post('http://inventory-service/reserve', [ 'item_id' => $request->input('item_id'), ]); return response()->json([ 'status' => 'success', 'payment_id' => $paymentResponse['payment_id'], ]); });
Viewing Service Dependencies
Visit your TraceKit dashboard to see:
- Service Map: Visual graph showing which services call which
- Service List: Table of all services with health metrics
- Service Detail: Deep dive on individual services with upstream/downstream dependencies
Disabling Auto-Instrumentation
Add to your .env file:
TRACEKIT_AUTO_INSTRUMENT_HTTP_CLIENT=false
Or in config/tracekit.php:
return [ 'auto_instrument_http_client' => env('TRACEKIT_AUTO_INSTRUMENT_HTTP_CLIENT', true), ];
What Gets Traced?
Incoming HTTP Requests (SERVER spans)
Every HTTP request to your Laravel app is automatically traced with:
- Route name and URI
- HTTP method and status code
- Request duration
- User agent and client IP
- Query parameters
- Response size
Outgoing HTTP Requests (CLIENT spans)
Every HTTP request from your Laravel app is automatically traced with:
- Target URL and HTTP method
- HTTP status code
- Request duration
peer.serviceattribute for service dependency mapping
Database Queries
All database queries are traced with:
- Actual SQL with bound parameters
- Query duration
- Slow query highlighting (configurable threshold)
- Connection name and database
Queue Jobs
Laravel queue jobs are traced with:
- Job class name
- Queue name and connection
- Job status (completed/failed)
- Execution time
- Failure reasons and exceptions
Errors and Exceptions
All exceptions are automatically captured with:
- Exception type and message
- Full stack trace
- Request context
- User information
Coming Soon
The following features are planned for future releases:
- Cache Operations - Redis, Memcached, and file cache tracing
- Redis Commands - Direct Redis command tracing
- External HTTP Calls - Outgoing HTTP request tracking
Advanced Usage
Manual Tracing
You can create custom traces in your code:
use TraceKit\Laravel\TracekitClient; class MyController extends Controller { public function myMethod(TracekitClient $tracekit) { $span = $tracekit->startSpan('my-custom-operation', null, [ 'user.id' => auth()->id(), 'custom.attribute' => 'value', ]); try { // Your code here $result = $this->doSomething(); $tracekit->endSpan($span, [ 'result.count' => count($result), ]); } catch (\Exception $e) { $tracekit->recordException($span, $e); $tracekit->endSpan($span, [], 'ERROR'); throw $e; } } }
Environment-Based Configuration
Disable tracing in local development:
# .env.local TRACEKIT_ENABLED=false
Enable only specific features:
TRACEKIT_HTTP_ENABLED=true TRACEKIT_DATABASE_ENABLED=true TRACEKIT_CACHE_ENABLED=true TRACEKIT_QUEUE_ENABLED=false TRACEKIT_REDIS_ENABLED=true
Sampling
Trace only a percentage of requests (e.g., 10%):
TRACEKIT_SAMPLE_RATE=0.1
Performance
TraceKit APM is designed to have minimal performance impact:
- < 5% overhead on average request time
- Asynchronous trace sending (doesn't block responses)
- Automatic batching and compression
- Configurable sampling for high-traffic apps
Security
- Sensitive data handling: Query bindings can be disabled
- Secure transmission: HTTPS only
- API key authentication
- No PII collected by default
Disable query bindings:
TRACEKIT_INCLUDE_BINDINGS=false
Requirements
- PHP 8.1 or higher
- Laravel 10.x, 11.x, or 12.x
Support
- Documentation: https://app.tracekit.dev/docs
- Issues: https://github.com/Tracekit-Dev/laravel-apm/issues
- Email: support@tracekit.dev
License
MIT License. See LICENSE for details.
Credits
Built with ❤️ by the TraceKit team.