skip to Main Content

I am using the Monolog LineFormatter as follows:

$output = "[%datetime%] %level_name% %channel%: %message%n";
$stream_handler->setFormatter(new LineFormatter($output));

This results in the following log lines:

[2023-07-03T05:30:31.327443+02:00] DEBUG test: This is a debug message.
[2023-07-03T05:30:31.327555+02:00] INFO test: This is an info level message.
[2023-07-03T05:30:31.327683+02:00] WARNING test: This is a warning level message.
[2023-07-03T05:30:31.327806+02:00] ERROR test: This is an error level message.

How would you set the %level% width (like padding in printf)?
I am trying to get this output:

[2023-07-03T05:30:31.327443+02:00] DEBUG   test: This is a debug message.
[2023-07-03T05:30:31.327555+02:00] INFO    test: This is an info level message.
[2023-07-03T05:30:31.327683+02:00] WARNING test: This is a warning level message.
[2023-07-03T05:30:31.327806+02:00] ERROR   test: This is an error level message.

I tried adding "-10s " to the $output %-10s level_name% but this just added the literal text -10s to the log lines.

Thanks in advance.

2

Answers


  1. Chosen as BEST ANSWER

    I don't understand how to do
    Set %level_name%: field width in MonologFormatterLineFormatter (PHP)
    The class LineFormatter or its parent NormalizerFormatter does not appear to have a set method.

    I am pretty new to PHP. I tried extending LineFormatter but ran into a fatal string error using the new class.

    I tried searching for how to add custom formatting to the $output fields, but nothing seemed to fit what I am trying to do.

    I did the following hack to pad the level_name. Any detailed suggestions how to this this correctly would be great.

        public function format(LogRecord $record): string
        {
            $vars = parent::format($record);
    
            $output = $this->format;
            foreach ($vars['extra'] as $var => $val) {
                if (false !== strpos($output, '%extra.'.$var.'%')) {
                    $output = str_replace('%extra.'.$var.'%', $this->stringify($val), $output);
                    unset($vars['extra'][$var]);
                }
            }
    
            foreach ($vars['context'] as $var => $val) {
                if (false !== strpos($output, '%context.'.$var.'%')) {
                    $output = str_replace('%context.'.$var.'%', $this->stringify($val), $output);
                    unset($vars['context'][$var]);
                }
            }
    
            if ($this->ignoreEmptyContextAndExtra) {
                if (count($vars['context']) === 0) {
                    unset($vars['context']);
                    $output = str_replace('%context%', '', $output);
                }
    
                if (count($vars['extra']) === 0) {
                    unset($vars['extra']);
                    $output = str_replace('%extra%', '', $output);
                }
            }
    
            /*
            // Original Code
            foreach ($vars as $var => $val) {
                $output = str_replace('%'.$var.'%', $this->stringify($val), $output);
            }
            */
            foreach ($vars as $var => $val) {
                if (false !== strpos($output, '%' . $var . '%')) {
                    if ("level_name" == $var) {
                        // https://www.elated.com/formatting-php-strings-printf-sprintf/
                        $padded_level_name = sprintf("%-9s", $this->stringify($val));
    
                        // Pad the level_name to make the log messages start in the same column.
                        $output = str_replace('%' . $var . '%', $padded_level_name, $output);
                    } else {
                        $output = str_replace('%' . $var . '%', $this->stringify($val), $output);
                    }
                }
            }
    
            // remove leftover %extra.xxx% and %context.xxx% if any
            if (false !== strpos($output, '%')) {
                $output = preg_replace('/%(?:extra|context)..+?%/', '', $output);
                if (null === $output) {
                    $pcreErrorCode = preg_last_error();
    
                    throw new RuntimeException('Failed to run preg_replace: ' . $pcreErrorCode . ' / ' . Utils::pcreLastErrorMessage($pcreErrorCode));
                }
            }
    
            return $output;
        }
    

    Now I see the expected padding:

    C:xampphtdocslogin>php MonologTest.php
    [2023-07-05T05:49:11.063827+02:00] <MonologTest>: DEBUG     This is a debug message.
    [2023-07-05T05:49:11.064226+02:00] <MonologTest>: INFO      This is an info level message.
    [2023-07-05T05:49:11.064372+02:00] <MonologTest>: WARNING   This is a warning level message.
    [2023-07-05T05:49:11.064509+02:00] <MonologTest>: ERROR     This is an error level message.
    [2023-07-05T05:49:11.064631+02:00] <MonologTest>: NOTICE    This is a notice level message.
    [2023-07-05T05:49:11.064757+02:00] <MonologTest>: CRITICAL  This is a critical level message.
    [2023-07-05T05:49:11.064879+02:00] <MonologTest>: ALERT     This is an alert level message.
    [2023-07-05T05:49:11.065005+02:00] <MonologTest>: EMERGENCY This is an emergency level message.
    

    Here is the complete test file:

    <?php
    require_once('vendor/autoload.php');
    
    use MonologLogger;
    use MonologLevel;
    use MonologHandlerStreamHandler;
    use MonologFormatterLineFormatter;
    use MonologLogRecord;
    use MonologUtils;
    
    /*
    class MyLineFormatter extends LineFormatter {
        public function format(LogRecord $record): string
        {
            $vars = parent::format($record);
    
            $output = $this->format;
            foreach ($vars['extra'] as $var => $val) {
                if (false !== strpos($output, '%extra.'.$var.'%')) {
                    $output = str_replace('%extra.'.$var.'%', $this->stringify($val), $output);
                    unset($vars['extra'][$var]);
                }
            }
    
            foreach ($vars['context'] as $var => $val) {
                if (false !== strpos($output, '%context.'.$var.'%')) {
                    $output = str_replace('%context.'.$var.'%', $this->stringify($val), $output);
                    unset($vars['context'][$var]);
                }
            }
    
            if ($this->ignoreEmptyContextAndExtra) {
                if (count($vars['context']) === 0) {
                    unset($vars['context']);
                    $output = str_replace('%context%', '', $output);
                }
    
                if (count($vars['extra']) === 0) {
                    unset($vars['extra']);
                    $output = str_replace('%extra%', '', $output);
                }
            }
    
            // Original Code
            // foreach ($vars as $var => $val) {
            //     $output = str_replace('%'.$var.'%', $this->stringify($val), $output);
            // }
    
            foreach ($vars as $var => $val) {
                if (false !== strpos($output, '%' . $var . '%')) {
                    if ("level_name" == $var) {
                        // https://www.elated.com/formatting-php-strings-printf-sprintf/
                        $padded_level_name = sprintf("%-9s", $this->stringify($val));
    
                        // Pad the level_name to make the log messages start in the same column.
                        $output = str_replace('%' . $var . '%', $padded_level_name, $output);
                    } else {
                        $output = str_replace('%' . $var . '%', $this->stringify($val), $output);
                    }
                }
            }
    
            // remove leftover %extra.xxx% and %context.xxx% if any
            if (false !== strpos($output, '%')) {
                $output = preg_replace('/%(?:extra|context)..+?%/', '', $output);
                if (null === $output) {
                    $pcreErrorCode = preg_last_error();
    
                    throw new RuntimeException('Failed to run preg_replace: ' . $pcreErrorCode . ' / ' . Utils::pcreLastErrorMessage($pcreErrorCode));
                }
            }
    
            return $output;
        }
    }
    */
    
    $logger = new Logger("MonologTest");
    
    // Log to the console.
    $stream_handler = new StreamHandler("php://stdout", Level::Debug);
    
    $output = "[%datetime%] <%channel%>: %level_name% %message%n";
    $stream_handler->setFormatter(new LineFormatter($output));
    $logger->pushHandler($stream_handler);
    
    // Log to a file.
    $logger->pushHandler(new StreamHandler(__DIR__ . '/test.log', Level::Debug));
    
    $logger->debug("This is a debug message.");
    $logger->info("This is an info level message.");
    $logger->warning("This is a warning level message.");
    $logger->error("This is an error level message.");
    $logger->notice("This is a notice level message.");
    $logger->critical("This is a critical level message.");
    $logger->alert("This is an alert level message.");
    $logger->emergency("This is an emergency level message.");
    

  2. To set the width (padding) for the %level_name% placeholder in Monolog’s LineFormatter, you can use the %-10s format specifier. However, you need to make sure you pass it as a string to the LineFormatter constructor.

    Here’s the modified code to achieve the desired output:

    $output = "[%datetime%] %-10s %channel%: %message%n";
    $stream_handler->setFormatter(new LineFormatter($output));
    

    By using %-10s, you indicate that %level_name% should be left-aligned with a width of 10 characters. The padding will be added after the log level name to reach the desired width.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search