Log Management for Laravel SaaS Applications
Tutorials January 15, 2026 ยท 4 min read

Log Management for Laravel SaaS Applications

Learn best practices for logging in multi-tenant Laravel SaaS applications. Handle tenant isolation, debug customer issues, and scale your logging infrastructure.

Building a SaaS application with Laravel brings unique logging challenges. You need to track issues across multiple tenants, debug customer-specific problems, and maintain log isolation for security and compliance. Here's how to build a robust logging strategy for your Laravel SaaS.

The Multi-Tenant Logging Challenge

Unlike single-tenant applications, SaaS logging must answer questions like:

  • Which tenant experienced this error?
  • How do I view all logs for a specific customer?
  • Are errors affecting one tenant or all tenants?
  • How do I share relevant logs with a customer without exposing others?

Tenant-Aware Logging Context

The foundation of SaaS logging is adding tenant context to every log entry:

<?php
// app/Providers/AppServiceProvider.php

use Illuminate\Support\Facades\Log;

public function boot(): void
{
    // Add tenant context to all logs
    Log::shareContext([
        'tenant_id' => fn () => tenant()?->id,
        'tenant_name' => fn () => tenant()?->name,
    ]);
}

For packages like Spatie's multitenancy:

<?php
// Using Spatie Laravel Multitenancy
use Spatie\Multitenancy\Events\MakingTenantCurrentEvent;

Event::listen(MakingTenantCurrentEvent::class, function ($event) {
    Log::shareContext([
        'tenant_id' => $event->tenant->id,
        'tenant_domain' => $event->tenant->domain,
    ]);
});

Structured Logging for SaaS

Structure your logs for easy filtering and analysis:

<?php
// Good: Structured with context
Log::info('Subscription upgraded', [
    'tenant_id' => $tenant->id,
    'plan_from' => $oldPlan->name,
    'plan_to' => $newPlan->name,
    'mrr_change' => $newPlan->price - $oldPlan->price,
]);

// Bad: Unstructured message
Log::info("Tenant {$tenant->id} upgraded from {$oldPlan} to {$newPlan}");

Customer-Specific Log Channels

For enterprise customers, you might need dedicated log streams:

<?php
// config/logging.php

'channels' => [
    'tenant' => [
        'driver' => 'custom',
        'via' => App\Logging\TenantLoggerFactory::class,
    ],
],

// app/Logging/TenantLoggerFactory.php
class TenantLoggerFactory
{
    public function __invoke(array $config): LoggerInterface
    {
        $tenant = tenant();

        return new Logger('tenant', [
            new StreamHandler(
                storage_path("logs/tenants/{$tenant->id}.log"),
                Level::Debug
            ),
        ]);
    }
}

Handling High-Volume SaaS Logging

Queue Log Processing

<?php
// Don't block requests with logging
Log::channel('queue')->info('User action', $context);

// config/logging.php
'queue' => [
    'driver' => 'custom',
    'via' => App\Logging\QueuedLoggerFactory::class,
],

Sampling for High-Traffic Tenants

<?php
// Log sampling for high-volume events
if (tenant()->plan === 'enterprise' || random_int(1, 100) <= 10) {
    Log::debug('Request processed', $context);
}

Error Tracking with Tenant Context

<?php
// bootstrap/app.php

->withExceptions(function (Exceptions $exceptions) {
    $exceptions->report(function (Throwable $e) {
        // Add tenant context to all exception reports
        if ($tenant = tenant()) {
            Log::error($e->getMessage(), [
                'tenant_id' => $tenant->id,
                'tenant_plan' => $tenant->plan,
                'exception' => $e,
            ]);
        }
    });
})

Building a Customer Debug View

Let customers see their own logs (carefully filtered):

<?php
// app/Http/Controllers/Tenant/LogsController.php

class LogsController extends Controller
{
    public function index(Request $request)
    {
        $logs = LogEntry::query()
            ->where('tenant_id', tenant()->id)
            ->where('level', '>=', $request->get('level', 'warning'))
            ->latest()
            ->paginate(50);

        return view('tenant.logs.index', compact('logs'));
    }
}

Security Considerations

Log Isolation

  • Never log sensitive data (passwords, API keys, PII)
  • Ensure log queries are always tenant-scoped
  • Use separate log storage for enterprise compliance requirements

Audit Logging

<?php
// Critical actions need audit trails
Log::channel('audit')->info('User permission changed', [
    'tenant_id' => tenant()->id,
    'actor_id' => auth()->id(),
    'target_user_id' => $user->id,
    'permission_added' => $permission,
    'ip' => request()->ip(),
]);

Centralized Log Management

Forward all tenant logs to a central service for analysis:

<?php
// config/logging.php

'stack' => [
    'driver' => 'stack',
    'channels' => ['single', '401clicks'],
],

'401clicks' => [
    'driver' => 'custom',
    'via' => App\Logging\FourOhOneClicksLogger::class,
    'api_key' => env('401CLICKS_API_KEY'),
],

Useful Log Queries for SaaS

With proper context, you can answer important questions:

# Errors by tenant (last 24h)
tenant_id:* level:error | stats count() by tenant_id

# Slow requests for specific tenant
tenant_id:"abc123" response_time:>1000

# Feature usage across tenants
message:"feature_used" feature:"export" | stats count() by tenant_id

Metrics from Logs

Extract business metrics from your logs:

<?php
// Log events that matter for business metrics
Log::info('subscription_event', [
    'tenant_id' => $tenant->id,
    'event' => 'trial_converted',
    'plan' => $plan->name,
    'value' => $plan->price,
]);

// Then aggregate in your log management tool

Integration with 401 Clicks

401 Clicks makes SaaS logging simple:

  • Filter logs by tenant ID instantly
  • Set up alerts for tenant-specific error thresholds
  • Share read-only log views with enterprise customers
  • Track error patterns across your entire customer base

Conclusion

Effective SaaS logging is about context. Add tenant information to every log entry, structure your data for querying, and use a centralized log management solution that can handle multi-tenant filtering. Your future self (and your support team) will thank you when debugging that urgent customer issue at 2 AM.

A

Admin

Published on January 15, 2026