50% off on Spike Billing!

Creating custom log types

Log Viewer allows you to create additional log processors. This does require some knowledge with Regular Expressions. RegExr is a great place to practice, learn, and validate your regular expressions.

First, create a new Log class that extends the Opcodes\LogViewer\Logs\Log class, and give it a name.

use Opcodes\LogViewer\Logs\Log;class CronLog extends Log{    public static string $name = 'Cron';    //}

Next, define the RegEx pattern that would match a single log entry. It should include named capture groups for any data points you want to extract. By default, it expectsdatetime, level and message named capture groups.

For example, assume that a single Cron log entry looks like this:

Jul 31 06:25:01 mycomputer CRON[12345]: (root) CMD (command -option argument > /dev/null 2>&1)

We can extract the datetime, host (mycomputer), the type CRON, process ID (12345), the user running the cron task (root) and the actual command, which we'll treat as the message of the log (CMD (command -option argument > /dev/null 2>&1)).

To do this, let's add the follow $regex static property:

use Opcodes\LogViewer\Logs\Log;class CronLog extends Log{    public static string $name = 'Cron';    public static string $regex = '/^(?<datetime>\S+[\s\d:]+) (?<hostname>\S+) (?<type>.*)\[(?<pid>\d+)\]: ?(\((?<user>\S+)\))? (?<message>.*)/';}

Here is a link to the Regular Expression above and a few example log entries that I have tested on RegExr.

Next, define a fillMatches(array $matches = []) method, which would receive a list of matches from the regex, and you would use these to fill in the properties of the Log class.

use Opcodes\LogViewer\Logs\Log;class CronLog extends Log{    public static string $regex = '/^(?<datetime>\S+[\s\d:]+) (?<hostname>\S+) (?<type>[^\[\]]+)\[(?<pid>\d+)\]: \((?<user>\S+)\) (?<message>.*)/';    public function fillMatches(array $matches = []): void    {        // The parent class already handles the "datetime", "level" and "message" matches. But you're free to assign them yourself.        // If you added custom regex named groups, you'll have to assign them to the `$this->context` array below.        parent::fillMatches($matches);        $this->context = [            'hostname' => $matches['hostname'],            'type' => $matches['type'],            'user' => $matches['user'],            'pid' => $matches['pid'],        ];    }}

And finally, register the new log type with any identifier of your choice in the AppServiceProvider:

use Opcodes\LogViewer\Facades\LogViewer;/** * Bootstrap any application services. * * @return void */public function boot(){    LogViewer::extend('cron', CronLog::class);}

And that's it! You should now see cron logs in your Log Viewer UI, provided that you have also added the file paths to includes_paths configuration option in config/log-viewer.php.

Cron logs in Log Viewer

Next, let's learn how configure the different severity levels found within these logs.

Custom log severity levels

Some types of logs have different levels of severity. Some, like Laravel logs, might have info, debug, warning, error and other similar severity levels. Meanwhile, Horizon logs don't have severities, but instead statuses such as Running, Done or Fail. For Apache/Nginx access logs, for example, we have chosen the status code of the response to represent the severity.

In order for Log Viewer to understand the different severity levels (or statuses, or whatever you want to represent how important that log entry is), you can define your own Log Level class.

use Opcodes\LogViewer\LogLevels\LevelInterface;use Opcodes\LogViewer\LogLevels\LevelClass;class CustomLevel implements LevelInterface{    // ...    public function getName(): string    {        return ucfirst($this->value);    }    public function getClass(): LevelClass    {        return match ($this->value) {            self::Debug   => LevelClass::info(),            self::Warning => LevelClass::warning(),            self::Error   => LevelClass::danger(),            default       => LevelClass::none(),        };    }}

The getClass() method should return a LevelClass instance, which determines the colour of the log in the front-end (success, info, warning, danger or none).

Once the level class is defined, you can relate it to a Log class by setting the $logClass static property:

class CronLog extends Log{    public static string $levelClass = CustomLevel::class;        // ...}

Customising the columns displayed in the UI

Log Viewer now allows to choose what data is visible in the Log Viewer table of logs. By default, it displays the datetime, severity, and the message. Various built-in log types will sometimes have a different setup.

To customise the columns displayed, add a static $columns property on the log class. Notice how the data_path uses the dot-notation for any nested properties.

class CronLog extends Log{    // ...        /** @var array|\string[][] The columns displayed on the frontend, and which data they should display */    public static array $columns = [        ['label' => 'Datetime', 'data_path' => 'datetime'],        ['label' => 'Type', 'data_path' => 'context.type'],        ['label' => 'User', 'data_path' => 'context.user'],        ['label' => 'Message', 'data_path' => 'message'],    ];        // ...}

In the example above, we don't display the level property which usually is displayed, because we simply don't have it in our Cron logs. Instead, we will display the type and user who ran the Cron command.


If you have any questions, feedback, or need any help setting up Log Viewer within your project, feel free to reach out via GitHub Discussions or GitHub Issues .