Write JSON logs in Laravel

Configure a Laravel daily log channel with Monolog JSON formatting, preserve exception traces explicitly, and validate one event per line before collection.

Best practices
Illustration of monitoring and troubleshooting application behavior

Laravel uses Monolog to format and write application logs. Add a dedicated channel, then apply Monolog's JsonFormatter through a tap class. Existing calls can use the new format once they are routed to that channel; historical files are not rewritten.

The examples use Laravel 12.x with Monolog 3. Check your installed versions before copying configuration into an older application.

Add a daily channel

Inside the channels array in config/logging.php:

'json' => [
    'driver' => 'daily',
    'path' => storage_path('logs/laravel.json'),
    'level' => 'debug',
    'days' => 14,
    'tap' => [App\Logging\JsonFormatter::class],
],

This uses daily file rotation with a retention count. It is not a strict size cap: a busy day can still produce a large file, and cleanup occurs as the handler rotates.

Apply the JSON formatter

Create app/Logging/JsonFormatter.php:

<?php

namespace App\Logging;

use Illuminate\Log\Logger;
use Monolog\Formatter\JsonFormatter as MonologJsonFormatter;

class JsonFormatter
{
    public function __invoke(Logger $logger): void
    {
        $formatter = new MonologJsonFormatter();
        $formatter->includeStacktraces(true);

        foreach ($logger->getHandlers() as $handler) {
            $handler->setFormatter($formatter);
        }
    }
}

The daily channel's rotating-file handler supports formatting. If you adapt this tap for a mixed custom handler stack, verify each handler's formatter support.

Monolog's JSON formatter normalizes exceptions, but stack traces are not included by default in this version. includeStacktraces(true) requests them explicitly. Treat trace contents and context values as potentially sensitive.

Send records to the channel

use Illuminate\Support\Facades\Log;

Log::channel('json')->info('User signed in', [
    'user_id' => 42,
    'ip' => '192.0.2.10',
]);

To route ordinary Log::info() calls through it, set the intended default channel or include json in your configured stack. A stack containing both single and json writes both copies, which may be intentional but increases volume. Update or rebuild Laravel's configuration cache as part of applying the change, and restart long-lived workers when needed.

Validate the event shape

Generate one normal record and one exception record. Verify the timestamp, severity, context, and requested trace fields, and confirm that each event occupies one physical line. A valid JSON document alone does not guarantee that a downstream system indexes the expected fields.

For DataKit log collection, match the rotating filenames, configure parsing, and compare the collected event with the original sample.

Can I convert old text logs? Changing the channel affects future records only. Historical text needs a separate parser or transformation; a Pipeline can extract fields when its rules match the old format.

How do single and daily differ? single writes a fixed filename. daily rotates date-suffixed files, such as laravel-2026-10-19.json, and applies configured retention. Neither replaces disk-capacity monitoring.

See Laravel logging customization and Monolog's JSON formatter.

Get a tailored plan

Contact us

Join the community

Scan with WeChat
to join the community

Try Guance

Start online and pay only for what you use.

Get started

Choose a Guance plan

Code hosting