Seldaek / monolog

Sends your logs to files, sockets, inboxes, databases and various web services
https://seldaek.github.io/monolog/
MIT License
20.95k stars 1.9k forks source link

Truncated JsonFormatter logs at 8191 characters #1890

Closed Mad4T closed 1 month ago

Mad4T commented 3 months ago

version Monolog version: 3.0 Laravel: 10.0

Issue Description: We're encountering log truncation at 8191 characters within our Laravel application deployed on AWS ECS. CloudWatch logs from this specific app are affected, while logs from other apps function normally. We suspect a potential limitation within Monolog (version 3.0) might be causing this behavior.

Expected Behavior: We expect CloudWatch logs from our Laravel app to be captured in their entirety without truncation.

Code Snippet (relevant part of logger.php): 'stderr' => [ 'driver' => 'monolog', 'handler' => StreamHandler::class, 'formatter' => env('LOG_STDERR_FORMATTER'), 'with' => [ 'stream' => 'php://stderr', ], ]

Seldaek commented 2 months ago

AFAIK there is no such limit in Monolog itself, but I am not very familiar with ECS and there might be something else there.

Mad4T commented 2 months ago

Issue happens in ECS and EKS. Also worth noting that logs from none php apps are not truncated.

Seldaek commented 2 months ago

Yeah sorry but that's something you'll have to debug yourself I am afraid.. There is too much environment specific stuff at play here for me to help much.

Seldaek commented 1 month ago

If you figure it out please share here, it may help someone else one day

Mad4T commented 1 month ago

if we ever find the reason will post it here, but we ended up doing a workaround to truncate the logs using Processor :

<?php

namespace App\Logging;

use Monolog\LogRecord;
use Monolog\Processor\ProcessorInterface;
use Throwable;

class TruncateStackTraceProcessor implements ProcessorInterface
{
    private int $traceLimit;

    public function __construct(int $traceLimit = 30)
    {
        $this->traceLimit = $traceLimit;
    }

    public function __invoke(LogRecord $record): LogRecord
    {
        $context = $record->context;
        if (isset($context['exception']) && $context['exception'] instanceof Throwable) {
            $exception = $record['context']['exception'];
            $trace = $exception->getTrace();

            if (count($trace) > $this->traceLimit) {
                $trace = array_slice($trace, 0, $this->traceLimit);
            }

            $context['exception'] = [
                'class' => get_class($record['context']['exception']),
                'message' => $record['context']['exception']->getMessage(),
                'code' => $record['context']['exception']->getCode(),
                'file' => $record['context']['exception']->getFile() . ':' . $record['context']['exception']->getLine(),
                'trace' => $this->formatStacktrace($trace),
            ];

            $record = $record->with(context: $context);
        }

        return $record;
    }

    private function formatStacktrace(array $trace): string {
        return collect($trace)
            ->map(function ($el) {
                $file = $el['file'] ?? '[unknown]';
                $line = $el['line'] ?? '[unknown]';
                $class = $el['class'] ?? '[unknown]';
                $function = $el['function'] ?? '[unknown]';
                $type = $el['type'] ?? '->';
                return "at {$class}{$type}{$function} ({$file}:{$line})";
            })
            ->join("\n");
    }
}