Laravel. How to Avoid Permission Denied Errors for Log Files


Laravel has a logging feature which uses log files to write down if any errors occur, or any user specified log messages.

However, quite often you may see the error below:

The stream or file "/web/yoursitepath/logs/laravel-2018-08-13.log" could not be opened: failed to open stream: Permission denied 

This error is caused by Laravel being run as differenet users. For instance the artican scheduler may be invoked from a cron job running with elevated priveleges as root. At the same time the website will be running as a webuser - user with less privileges. If the log file is created while Laravel is running as an elevated user, then the user with less privileges is denied write permissions to the same file. And this is what causes the error above.

The following workaround solves this. It involves adding a custom code which will enable Monolog to create a different log file when Laravel is invoked from command line or from the web.

For Laravel up to version 5.5 add the following code snippet to the /bootstrap/app.php file :

 

/**
 * Configure Monolog log files to add invoke environment sufix.
 */
$app->configureMonologUsing(function(Monolog\Logger $monolog) {
    $filename = storage_path('logs/laravel-' . php_sapi_name() . '.log');
    $handler = new Monolog\Handler\RotatingFileHandler($filename);
    $monolog->pushHandler($handler);
});

return $app;

 

For Laravel 5.6 and above modify the single and daily logging channels in the config file at /config/logging.php :

'channels' => [
    'single' => [
        'driver' => 'single',
        'path' => storage_path('logs/laravel_' . php_sapi_name() . '.log'),
        'level' => 'debug',
    ],
    'daily' => [
        'driver' => 'daily',
        'path' => storage_path('logs/laravel_' . php_sapi_name() . '.log'),
        'level' => 'debug',
        'days' => 7,
    ],
]

The script will append the invoke enviroment slug postfix to the log file. Being separate files the permission denied exception will no lnger be thrown.

As an additional benefit it will also make very clear when you have to debug the Laravel app what is causing an error - web pages or artisan commands.