Introduction
Laravel allows you to configure the default Monolog logger instance, but this largely allows you to add additional handlers or to put debug
into another file for example.
The default logging implementation sets the log level to debug
and doesn't appear to provide a way to change this. This means that even if you specify a separate handler for debug
logs, all log events with higher priority (info
, notice
, error
, etc) will still be logged there.
You could use the setHandlers
method to create an entirely new set of stream handlers and log each level - but this is still the minimum logging level. If you create a handler for the error
level, you will still get all levels above this - critical
, alert
, and emergency
.
I didn't want to have to remove the debug
level log messages - Log::debug()
- from my code, but didn't want to have them logged in production, as they are expected to get quite noisy.
Following the documentation
Your first thought might be to handle this within the bootstrap/app.php
file per the documentatino:
1$level = $app->environment('local') ? 'debug' : 'warning';2 3$app->configureMonologUsing(function ($monolog) {4 $monolog->pushHandler(new StreamHandler(storage_path('logs/laravel.log'), $level);5});
Done, right? Wrong.
At this stage of the boot process, the environment has not yet been configured, so environment()
triggers a fatal error:
1Fatal error: Uncaught ReflectionException: Class env does not exist in /home/vagrant/Code/laravel.test/vendor/laravel/framework/src/Illuminate/Container/Container.php:741
There also doesn't appear to be any way to set the $level
parameter of Laravel's underlying useFiles
, useDailyFiles
, useSyslog
, or useErrorLog
methods.
In order to get around this limitation, you need to override the functionality entirely.
A custom ConfigureLogging class
1<?php 2 3namespace App\Bootstrap; 4 5use Illuminate\Foundation\Bootstrap\ConfigureLogging as BaseConfigureLogging; 6use Illuminate\Contracts\Foundation\Application; 7use Illuminate\Log\Writer; 8 9class ConfigureLogging extends BaseConfigureLogging10{11 protected function configureSingleHandler(Application $app, Writer $log)12 {13 $log->useFiles($app->storagePath().'/logs/laravel.log', $this->resolveLogLevel($app));14 }15 16 protected function configureDailyHandler(Application $app, Writer $log)17 {18 $log->useDailyFiles(19 $app->storagePath().'/logs/laravel.log',20 $app->make('config')->get('app.log_max_files', 5),21 $this->resolveLogLevel($app)22 );23 }24 25 protected function configureSyslogHandler(Application $app, Writer $log)26 {27 $log->useSyslog('laravel', $this->resolveLogLevel($app));28 }29 30 protected function configureErrorlogHandler(Application $app, Writer $log)31 {32 $log->useErrorLog($this->resolveLogLevel($app));33 }34 35 /**36 * Based on the current environment, return the desired log level.37 *38 * @param \Illuminate\Contracts\Foundation\Application $app39 *40 * @return string41 */42 private function resolveLogLevel(Application $app)43 {44 return $app->isLocal() ? 'debug' : 'warning';45 }46}
In doing so, you overwrite the relevant methods in Laravel's ConfigureLogging
class, giving you the ability to set the log level based on the environment via the new resolveLogLevel
method.
How you derive the appropriate log level is up to you; you might target a specific environment using $app->environment('staging')
, for example.
Implementing the custom logging
At this point, you've overwritten the necessary methods, but there's a little more work needed to wire it all up.
This part of the process isn't managed via Service Providers like much of Laravel's configuration is; it's handled via an array of bootstrappers
in the Illuminate\Foundation\Http\Kernel
and Illuminate\Foundation\Console\Kernel
classes. This can be relatively easily achieved by overwriting the constructor of your application's console or HTTP (or both) Kernel
classes as needed.
1<?php 2 3namespace App\Http; 4 5use Illuminate\Foundation\Http\Kernel as HttpKernel; 6use Illuminate\Contracts\Foudnation\Application; 7use Illuminate\Routing\Router; 8 9class Kernel extends HttpKernel10{11 // ...12 public function __construct(Application $app, Router $router)13 {14 $this->bootstrappers = array_map(function ($bootstrapper) {15 return $bootstrapper === 'Illuminate\Foundation\Bootstrap\ConfigureLogging'16 ? 'App\Bootstrap\ConfigureLogging'17 : $bootstrapper;18 }, $this->bootstrappers);19 }20}
Conclusion
This is, up until Laravel 5.2.35, the only way to swap out the logging configuration within Laravel.
In the process of writing this post, and subsequent tweet from Taylor, I discovered that this was added to the framework in early May. If you're still using the LTS (5.1) version of Laravel, you'll need to continue to follow the approach outlined in this post.
If you're using Laravel >= 5.2.35, you can simply add log_level
into config/app.php
at your desired level, and the log level will be applied. To make it switchable per-environment, set this value via an environment variable and you're all set.
1// config/app.php2return [3 // ...4 'log_level' => env('APP_LOG_LEVEL', 'error'),5];
In doing this, calls to Log::debug()
can be left in your code, without worrying about them filling your production log files.
Another option you could take would be to add an environment variable to manage the log level. In doing so, you could easily turn debug on and off in production, allowing you to diagnose an issue.