Can we create a custom log file for different purposes in laravel 5.2 like for order related log entries that should be in order.log and for payment related stuff the entry shou
There is a simple way:
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
$log = ['orderId' => 10,
'description' => 'Some description'];
//first parameter passed to Monolog\Logger sets the logging channel name
$orderLog = new Logger('order');
$orderLog->pushHandler(new StreamHandler(storage_path('logs/order.log')), Logger::INFO);
$orderLog->info('OrderLog', $log);
Output in logs/order.log:
[2017-04-30 00:00:00] order.INFO: OrderLog {"orderId":10, "description":"Some description"} []
Quickest Way to output log to different files
Log::useFiles('path/to/file.log');
Log::info('Info');
To expand on ShQ's answer:
One issue I noticed is that the log will be appended with [] []
, which are the empty array values for $context
and $extra
within LineFormatter.format();
ie, vendor/monolog/monolog/src/Monolog/Formatter/LineFormatter.php
There are two ways around this, either provide a format which does not include extra or context to the constructor of LineFormatter
, or provide the 4th argument $ignoreEmptyContextAndExtra
= true
.
All files within ShQ's answer remain the same but ChannelStreamHandler
must change.
ChannelStreamHandler:
<?php
namespace App\Helpers;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
/**
* Use channels to log into separate files
*
*/
class ChannelStreamHandler extends StreamHandler
{
/**
* Channel name
*
* @var String
*/
protected $channel;
/**
* @param String $channel Channel name to write
* @param bool|int $stream
* @param bool|int $level
* @param bool $bubble
* @param null $filePermission
* @param bool $useLocking
* @see parent __construct for params
*/
public function __construct(
$channel,
$stream,
$level = Logger::DEBUG,
$bubble = true,
$filePermission = null,
$useLocking = false
) {
$this->channel = $channel;
$formatter = new LineFormatter(null, null, false, true);
$this->setFormatter($formatter);
parent::__construct($stream, $level, $bubble);
}
/**
* When to handle the log record.
*
* @param array $record
* @return bool
*/
public function isHandling(array $record)
{
//Handle if Level high enough to be handled (default mechanism)
//AND CHANNELS MATCHING!
if (isset($record['channel'])) {
return ($record['level'] >= $this->level && $record['channel'] == $this->channel);
} else {
return ($record['level'] >= $this->level);
}
}
}
The important change is to provide 4th param of true, which is $ignoreEmptyContextAndExtra
. This param, tells LineFormatter
to ignore either context
of extra
arrays if empty:
$formatter = new LineFormatter(null, null, false, true);
$this->setFormatter($formatter);
You must be sure to also ensure your running monolog 1.22 because it includes a bug fix regarding ignoreEmptyContextAndExtra
.
I also added an override for info() to the ChannelWritter
class:
public function info($channel, $message, array $context = [])
{
$level = array_flip($this->levels)[$this->channels[$channel]['level']];
$this->writeLog($channel, $level, $message, $context);
}
Additionally, I wasn't happy with the "lazy load logger" in ShQ's solution so modified to use the service provider/IoC
Replace ChannelWriter.writeLog()
:
public function writeLog(string $channel, string $level, string $message, array $context = [])
{
if (!in_array($channel, array_keys($this->channels))) {
throw new InvalidArgumentException('Invalid channel used.');
}
$logger = \App::make("{$channel}log");
$channelHandler = new ChannelStreamHandler(
$channel,
storage_path() . '/' . $this->channels[$channel]['path'],
$this->channels[$channel]['level']
);
$logger->pushHandler($channelHandler);
$logger->{$level}($message);
}
and in your AppServiceProvider
:
$this->app->bind('eventlog', function () {
return new Logger('event');
});
$this->app->bind('auditlog', function () {
return new Logger('audit');
});
I'll try bundle this together into a package.
For me in Laravel 5.3, I am not sure if it was my install previously but I found the bootstrap/app.php did not work for me.
I needed to put this in app/Providers/AppServiceProvider.php.
n.b. This is where I had the setting of the log level from config before, so I end up with 3 log handlers.
public function register()
{
$monolog = Log::getMonolog();
foreach ($monolog->getHandlers() as $handler) {
$handler->setLevel(Config::get('app.log_level'));
}
$bubble = false;
$infoStreamHandler = new \Monolog\Handler\StreamHandler( storage_path("logs/info.log"), \Monolog\Logger::INFO, $bubble);
$monolog->pushHandler($infoStreamHandler);
$warningStreamHandler = new \Monolog\Handler\StreamHandler( storage_path("logs/warning.log"), \Monolog\Logger::WARNING, $bubble);
$monolog->pushHandler($warningStreamHandler);
}
Solution:
step1: create a channel inside config/logging.php file
example :
'channels' => [
'single' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'level' => 'debug',
],
'web' => [
'driver' => 'single',
'path' => storage_path('logs/web/web.log'),
],
]
Step2: Now set dynamic path from the controller like this
config(['logging.channels.web.path' => storage_path('logs/web/'.time().'.log')]);
Step3 : now generate your log
Log::channel('web')->info("your message goes here");
Enjoy :)
Based on the ShQ answer, a shorter and simpler logger helper that allows you to log to a custom file on the fly. You can also add your custom handler and set the file path.
App\Helper
<?php
/**
* Logger helper to log into different files
*
* @package App\Helpers
* @author Romain Laneuville <romain.laneuville@hotmail.fr>
*/
namespace App\Helpers;
use Monolog\Logger;
use Monolog\Handler\HandlerInterface;
use Monolog\Handler\StreamHandler;
/**
* Class LogToChannels
*
* @package App\Helpers
*/
class LogToChannels
{
/**
* The LogToChannels channels.
*
* @var Logger[]
*/
protected $channels = [];
/**
* LogToChannels constructor.
*/
public function __construct()
{
}
/**
* @param string $channel The channel to log the record in
* @param int $level The error level
* @param string $message The error message
* @param array $context Optional context arguments
*
* @return bool Whether the record has been processed
*/
public function log(string $channel, int $level, string $message, array $context = []): bool
{
// Add the logger if it doesn't exist
if (!isset($this->channels[$channel])) {
$handler = new StreamHandler(
storage_path() . DIRECTORY_SEPARATOR . 'logs' . DIRECTORY_SEPARATOR . $channel . '.log'
);
$this->addChannel($channel, $handler);
}
// LogToChannels the record
return $this->channels[$channel]->{Logger::getLevelName($level)}($message, $context);
}
/**
* Add a channel to log in
*
* @param string $channelName The channel name
* @param HandlerInterface $handler The channel handler
* @param string|null $path The path of the channel file, DEFAULT storage_path()/logs
*
* @throws \Exception When the channel already exists
*/
public function addChannel(string $channelName, HandlerInterface $handler, string $path = null)
{
if (isset($this->channels[$channelName])) {
throw new \Exception('This channel already exists');
}
$this->channels[$channelName] = new Logger($channelName);
$this->channels[$channelName]->pushHandler(
new $handler(
$path === null ?
storage_path() . DIRECTORY_SEPARATOR . 'logs' . DIRECTORY_SEPARATOR . $channelName . '.log' :
$path . DIRECTORY_SEPARATOR . $channelName . '.log'
)
);
}
/**
* Adds a log record at the DEBUG level.
*
* @param string $channel The channel name
* @param string $message The log message
* @param array $context The log context
*
* @return bool Whether the record has been processed
*/
public function debug(string $channel, string $message, array $context = []): bool
{
return $this->log($channel, Logger::DEBUG, $message, $context);
}
/**
* Adds a log record at the INFO level.
*
* @param string $channel The channel name
* @param string $message The log message
* @param array $context The log context
*
* @return bool Whether the record has been processed
*/
public function info(string $channel, string $message, array $context = []): bool
{
return $this->log($channel, Logger::INFO, $message, $context);
}
/**
* Adds a log record at the NOTICE level.
*
* @param string $channel The channel name
* @param string $message The log message
* @param array $context The log context
*
* @return bool Whether the record has been processed
*/
public function notice(string $channel, string $message, array $context = []): bool
{
return $this->log($channel, Logger::NOTICE, $message, $context);
}
/**
* Adds a log record at the WARNING level.
*
* @param string $channel The channel name
* @param string $message The log message
* @param array $context The log context
*
* @return bool Whether the record has been processed
*/
public function warn(string $channel, string $message, array $context = []): bool
{
return $this->log($channel, Logger::WARNING, $message, $context);
}
/**
* Adds a log record at the WARNING level.
*
* @param string $channel The channel name
* @param string $message The log message
* @param array $context The log context
*
* @return bool Whether the record has been processed
*/
public function warning(string $channel, string $message, array $context = []): bool
{
return $this->log($channel, Logger::WARNING, $message, $context);
}
/**
* Adds a log record at the ERROR level.
*
* @param string $channel The channel name
* @param string $message The log message
* @param array $context The log context
*
* @return bool Whether the record has been processed
*/
public function err(string $channel, string $message, array $context = []): bool
{
return $this->log($channel, Logger::ERROR, $message, $context);
}
/**
* Adds a log record at the ERROR level.
*
* @param string $channel The channel name
* @param string $message The log message
* @param array $context The log context
*
* @return bool Whether the record has been processed
*/
public function error(string $channel, string $message, array $context = []): bool
{
return $this->log($channel, Logger::ERROR, $message, $context);
}
/**
* Adds a log record at the CRITICAL level.
*
* @param string $channel The channel name
* @param string $message The log message
* @param array $context The log context
*
* @return bool Whether the record has been processed
*/
public function crit(string $channel, string $message, array $context = []): bool
{
return $this->log($channel, Logger::CRITICAL, $message, $context);
}
/**
* Adds a log record at the CRITICAL level.
*
* @param string $channel The channel name
* @param string $message The log message
* @param array $context The log context
*
* @return Boolean Whether the record has been processed
*/
public function critical(string $channel, string $message, array $context = []): bool
{
return $this->log($channel, Logger::CRITICAL, $message, $context);
}
/**
* Adds a log record at the ALERT level.
*
* @param string $channel The channel name
* @param string $message The log message
* @param array $context The log context
*
* @return bool Whether the record has been processed
*/
public function alert(string $channel, string $message, array $context = []): bool
{
return $this->log($channel, Logger::ALERT, $message, $context);
}
/**
* Adds a log record at the EMERGENCY level.
*
* @param string $channel The channel name
* @param string $message The log message
* @param array $context The log context
*
* @return bool Whether the record has been processed
*/
public function emerg(string $channel, string $message, array $context = []): bool
{
return $this->log($channel, Logger::EMERGENCY, $message, $context);
}
/**
* Adds a log record at the EMERGENCY level.
*
* @param string $channel The channel name
* @param string $message The log message
* @param array $context The log context
*
* @return bool Whether the record has been processed
*/
public function emergency(string $channel, string $message, array $context = []): bool
{
return $this->log($channel, Logger::EMERGENCY, $message, $context);
}
}
App\Providers\AppServiceProvider.php (add to register function)
//Facade to Object binding
$this->app->bind('LogToChannels', 'App\Helpers\LogToChannels');
config\app.php (add to aliases)
// Custom Alias Class
'Log' => App\Contracts\Facades\LogToChannels::class
Then anywhere in your app you can call
Log::info('logger_name', 'Log message');
Log::error('other_logger_name', 'Log message', $someContext);
You can even customize your logger output by calling
Log::addChannel('channel_name', $customHandler);
And it will be accessible when you will call its name anywhere in your app.