Skip to content
Tech

Structured Logging in Laravel: Making Production Issues Easy to Trace

Move beyond scattered log lines: add context, correlation IDs and JSON formatting so you can actually find what went wrong in production.

Editorial illustration of neatly stacked translucent log cards with a magnifying glass highlighting one entry
Editorial illustration of neatly stacked translucent log cards with a magnifying glass highlighting one entry
In this article

    When something breaks in production, logs are often the only witness. Unfortunately, many applications produce logs that are noisy, inconsistent and missing the one detail you need. Structured logging fixes this by treating every log entry as data with consistent fields rather than a free-form sentence.

    Why plain text logs fall short

    A line such as "Payment failed for user" tells you something went wrong but not for whom, on which order, or during which request. Searching thousands of such lines is slow and frustrating. Structured logs attach key-value context, so you can filter by user, order or request in seconds.

    Always pass context

    Laravel's logger accepts a context array as its second argument. Use it every time.

    Log::warning('Payment declined', [
        'order_id' => $order->id,
        'user_id' => $user->id,
        'gateway' => 'card',
        'reason' => $response->code(),
    ]);

    Keep the message short and stable, and put the variable details in the context. Stable messages make it easy to count how often a particular event happens.

    Add a request ID to every entry

    A correlation or request ID ties together everything that happened during a single request, including queued jobs it dispatched. A small middleware can generate one and share it with the logger.

    public function handle($request, Closure $next)
    {
        $id = $request->header('X-Request-Id') ?? (string) Str::uuid();
        Log::withContext(['request_id' => $id]);
        $response = $next($request);
        $response->headers->set('X-Request-Id', $id);
        return $response;
    }

    Return the ID in a response header too. When a user reports an error, support can ask for it and jump straight to the relevant logs.

    Use JSON in production

    Log aggregation tools parse JSON far more reliably than custom text formats. In config/logging.php, configure your production channel to use Monolog's JsonFormatter. Locally you can keep the readable line format; the point is that machines read production logs first.

    Choose log levels deliberately

    • debug: detailed information useful while developing; usually disabled in production.
    • info: notable business events such as a user signing up or an export finishing.
    • warning: something unexpected that the system handled, such as a retry.
    • error: an operation failed and needs attention.
    • critical and above: the application or a key dependency is unusable.

    If everything is logged as an error, alerts become noise and people stop reading them. Agree on these definitions as a team.

    Separate channels for separate concerns

    Laravel's stack channel lets you send logs to several destinations. You might write everything to a daily file, send errors to an alerting service and route audit events to their own channel. Dedicated channels for payments or integrations make investigations far quicker.

    Never log secrets or personal data

    It is easy to log an entire request payload while debugging and forget to remove it. That can expose passwords, tokens, card details or personal information, which creates security and privacy risks. Log identifiers rather than full records, mask sensitive fields and review logging in code review just as you would review database queries.

    Log exceptions with useful context

    Laravel's exception handler already records unhandled exceptions. You can enrich them by defining a context() method on your custom exceptions, returning relevant IDs. Avoid catching exceptions only to log a vague message and swallow the stack trace.

    Make logs searchable by the whole team

    Logs help most when everyone can reach them. Send production logs to a central tool your developers and support staff can search, set a sensible retention period, and save common queries such as "all errors for this request ID" or "failed payments today". Build a few dashboards and alerts on top of the most important events, so problems surface before customers report them.

    Wrapping up

    Good logging is a habit rather than a library. Pass context consistently, tag entries with a request ID, emit JSON in production, use levels with intent and keep sensitive data out. The next time something fails at an awkward hour, you will be able to trace it in minutes instead of guessing.

    Written by Raj

    Founder & Editor

    I write HorizonDesk: practical, no-hype articles on technology, money, business and the vehicles we drive. Everything here is something I’ve researched, tested or wanted explained clearly myself.