Laravel Observability: Best Practices for 2025

Laravel observability dashboard showing distributed tracing and performance monitoring

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:

  1. Request took 5200ms total
  2. Stripe API call took only 800ms (not the bottleneck!)
  3. Email sending took 3500ms (blocking the response!)
  4. 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.

About Terry Osayawe

Founder of TraceKit. On a mission to make production debugging effortless.

Ready to Debug 10x Faster?

Join teams who stopped guessing and started knowing

Start Free Trial
Start Free Trial

7-day free trial • $0 until trial ends