Laravel observability is essential for monitoring production applications effectively. While Laravel makes building web applications easy, debugging production issues—like slow requests, database bottlenecks, and intermittent errors—requires proper application performance monitoring (APM) and distributed tracing.
This comprehensive guide covers Laravel observability best practices for 2025, including how to implement distributed tracing, detect N+1 queries, monitor Laravel performance, and debug production issues faster.
Why Laravel Needs Observability
Laravel applications often evolve into complex systems:
- Multiple background jobs processing queues
- External API integrations (Stripe, AWS, third-party services)
- Database queries that grow more complex over time
- Caching layers (Redis, Memcached)
- Event broadcasting and real-time features
When something breaks, Laravel’s logs tell you that an error occurred, but not why or how the user got there.
The Three Pillars of Observability
1. Distributed Tracing
Track requests as they flow through your Laravel app, from the controller to database queries to external API calls. See the complete timeline with sub-millisecond precision.
2. Structured Logging
Move beyond Laravel’s default logs. Add context like user IDs, request IDs, and trace IDs so you can correlate events across your application.
3. Metrics & Alerts
Track key metrics (request duration, error rates, queue depth) and get alerted before users report issues.
Setting Up Observability in Laravel
Step 1: Install TraceKit for Laravel
composer require tracekit/laravel-apm
Step 2: Add Your API Key
# .env
TRACEKIT_API_KEY=your_api_key_here
TRACEKIT_SERVICE_NAME=my-laravel-app
Step 3: Publish Config (Optional)
php artisan vendor:publish --provider="TraceKit\Laravel\TracekitServiceProvider"
That’s it! Your Laravel app is now automatically tracing:
- All HTTP requests
- Database queries (with query text and timing)
- Redis operations
- External HTTP calls
- Queue jobs
- Events and listeners
For complete installation details and configuration options, see the TraceKit Laravel documentation.
Best Practices for Laravel Observability
1. Add Custom Spans for Business Logic
While automatic instrumentation covers framework code, add custom spans for your business logic:
use TraceKit\Laravel\TracekitClient;
public function processOrder(Order $order, TracekitClient $tracekit)
{
// Create a custom span for order processing
$span = $tracekit->startSpan('process-order', null, [
'order.id' => $order->id,
'user.id' => $order->user_id,
]);
try {
// Calculate pricing
$total = $this->calculateTotal($order);
// Charge customer
$payment = $this->chargeStripe($order, $total);
// Send confirmation email
Mail::to($order->user)->send(new OrderConfirmation($order));
// End span with success
$tracekit->endSpan($span, [
'order.total' => $total,
'payment.status' => $payment->status,
]);
return $payment;
} catch (\Exception $e) {
$tracekit->recordException($span, $e);
$tracekit->endSpan($span, [], 'ERROR');
throw $e;
}
}
Now you can see exactly which step is slow when processing orders. For more examples, check the manual instrumentation guide.
2. Tag Spans with Business Context
Add user IDs, order IDs, and other business data to traces when creating spans:
use TraceKit\Laravel\TracekitClient;
public function checkout(Request $request, TracekitClient $tracekit)
{
$span = $tracekit->startSpan('checkout-process', null, [
'user.id' => auth()->id(),
'user.plan' => auth()->user()->subscription->plan,
'order.id' => $request->input('order_id'),
'order.total' => $request->input('total'),
]);
try {
// Your checkout logic here
$result = $this->processCheckout($request);
$tracekit->endSpan($span);
return $result;
} catch (\Exception $e) {
$tracekit->recordException($span, $e);
$tracekit->endSpan($span, [], 'ERROR');
throw $e;
}
}
When debugging, you can filter traces by user, plan, or order value.
3. Trace Background Jobs
Queue jobs are automatically traced by TraceKit with job class name, queue name, status, and payload captured out of the box. No additional code needed!
use Illuminate\Contracts\Queue\ShouldQueue;
class ProcessPayment implements ShouldQueue
{
public $order;
public function __construct(Order $order)
{
$this->order = $order;
}
public function handle()
{
// Job is automatically traced!
// TraceKit captures: job class, queue name, execution time, status
$this->processOrder();
}
}
4. Monitor Eloquent N+1 Queries
TraceKit automatically detects N+1 query problems and highlights them in traces:
// Bad: N+1 query (100 users = 101 queries)
$users = User::all();
foreach ($users as $user) {
echo $user->posts->count(); // Query for each user!
}
// Good: Eager loading (2 queries total)
$users = User::with('posts')->get();
foreach ($users as $user) {
echo $user->posts->count();
}
Flame graphs make N+1 queries obvious – you’ll see hundreds of tiny database calls instead of one large query.
5. Trace External API Calls
When calling external APIs, wrap them in spans to track timing and failures:
use Illuminate\Support\Facades\Http;
use TraceKit\Laravel\TracekitClient;
public function fetchUserData($userId, TracekitClient $tracekit)
{
$span = $tracekit->startSpan('fetch-external-user', null, [
'external.api' => 'user-service',
'user.id' => $userId,
]);
try {
$response = Http::timeout(5)->get("https://api.example.com/users/{$userId}");
$tracekit->endSpan($span, [
'http.status_code' => $response->status(),
'http.success' => $response->successful(),
]);
return $response->json();
} catch (\Exception $e) {
$tracekit->recordException($span, $e);
$tracekit->endSpan($span, [], 'ERROR');
throw $e;
}
}
6. Monitor Queue Performance
Queue jobs are automatically traced by TraceKit, giving you insights into job duration, failures, and queue depth. You can monitor queue metrics in your scheduled commands:
use Illuminate\Support\Facades\Queue;
use Illuminate\Console\Command;
class MonitorQueues extends Command
{
public function handle()
{
$queueSize = Queue::size('default');
// TraceKit automatically traces command execution
$this->info("Queue size: {$queueSize}");
if ($queueSize > 1000) {
// Alert your team!
$this->error("Queue backlog detected!");
}
}
}
Common Laravel Performance Issues (and How to Find Them)
Issue 1: Slow Database Queries
Symptom: Traces show wide blue boxes for database calls
Solution: Add indexes, optimize queries, or add caching
Issue 2: N+1 Queries
Symptom: Flame graph shows hundreds of tiny database calls
Solution: Use eager loading (with()) or lazy eager loading
Issue 3: Slow External APIs
Symptom: Traces show wide purple boxes for HTTP calls
Solution: Add timeouts, use async HTTP, or cache responses
Issue 4: Heavy Middleware
Symptom: Traces show time spent before reaching your controller
Solution: Optimize or remove unnecessary middleware
Issue 5: Slow Jobs Blocking Queue
Symptom: Job traces show long durations, queue size growing
Solution: Split large jobs into smaller chunks or use batch jobs
Real-World Example: E-commerce Checkout
An e-commerce site using Laravel had slow checkouts. Users complained about waiting 5+ seconds after clicking “Place Order.”
Distributed tracing revealed:
- Request took 5200ms total
- Stripe API call took only 800ms (not the bottleneck!)
- Email sending took 3500ms (blocking the response!)
- Tax calculation took 700ms (making 50 API calls to a tax service)
Solutions implemented:
- Moved email sending to a queue job (saved 3500ms)
- Cached tax calculations for 1 hour (saved 600ms)
- Final response time: 900ms (82% improvement)
Monitoring Production Laravel Apps
Set up alerts for common issues:
- High error rates: Alert if error rate > 1%
- Slow requests: Alert if P95 latency > 1000ms
- Queue backlog: Alert if queue size > 5000 jobs
- Failed jobs: Alert on any failed job
Frequently Asked Questions (FAQ)
What is Laravel observability?
Laravel observability is the practice of monitoring and understanding your Laravel application’s internal state through logs, metrics, and distributed tracing. It helps developers detect performance issues, debug production errors, and optimize database queries in real-time.
How do I monitor a Laravel application?
To monitor a Laravel application, install an APM tool like TraceKit using composer require tracekit/laravel-apm, configure your API key, and enable automatic tracing for HTTP requests, database queries, queue jobs, and external API calls.
What’s the best Laravel monitoring tool?
The best Laravel monitoring tool depends on your needs. TraceKit offers zero-config distributed tracing with automatic instrumentation for Laravel 10, 11, and 12. Other options include Laravel Telescope (for local development), New Relic, and Datadog.
How do I detect N+1 queries in Laravel?
N+1 queries are automatically detected by observability tools that trace database calls. In TraceKit’s flame graphs, N+1 queries appear as hundreds of tiny database spans. Fix them using eager loading with Model::with('relationship').
Is Laravel observability different from logging?
Yes. While logging records discrete events, observability provides a complete picture through distributed tracing, metrics, and structured logs. Observability shows the entire request flow, timing, and relationships between operations—not just isolated error messages.
Next Steps
Ready to add observability to your Laravel app?
- Start a free trial and instrument your app in 5 minutes
- Check the Laravel docs for advanced configuration and manual instrumentation
- Try the demo to see Laravel traces in action
Modern Laravel applications need modern observability. Stop guessing and start knowing.
Questions about Laravel observability? Start your free trial or check our Laravel documentation.