Package Data | |
---|---|
Maintainer Username: | dmitry-ivanov |
Maintainer Contact: | dmitry.g.ivanov@gmail.com (Dmitry Ivanov) |
Package Create Date: | 2016-06-14 |
Package Last Update: | 2024-03-06 |
Home Page: | |
Language: | PHP |
License: | MIT |
Last Refreshed: | 2024-12-14 15:14:21 |
Package Statistics | |
---|---|
Total Downloads: | 67,684 |
Monthly Downloads: | 525 |
Daily Downloads: | 7 |
Total Stars: | 86 |
Total Watchers: | 6 |
Total Forks: | 11 |
Total Open Issues: | 0 |
Logging and notifications for Laravel console commands.
| Laravel | Console Logger | | ------- | :-----------------------------------------------------------------------: | | 5.1.* | 5.1.* | | 5.2.* | 5.2.* | | 5.3.* | 5.3.* | | 5.4.* | 5.4.* | | 5.5.* | 5.5.* | | 5.6.* | 5.6.* | | 5.7.* | 5.7.* | | 5.8.* | 5.8.* |
Install the package via Composer:
composer require illuminated/console-logger
Use Illuminated\Console\Loggable
trait:
use Illuminated\Console\Loggable;
class ExampleCommand extends Command
{
use Loggable;
public function handle()
{
$this->logInfo('Hello World!');
}
// ...
}
Now your command is loggable!
[2016-05-11 17:19:21]: [INFO]: Command `App\Console\Commands\ExampleCommand` initialized.
[2016-05-11 17:19:21]: [INFO]: Host: `MyHost.local` (`10.0.1.1`).
[2016-05-11 17:19:21]: [INFO]: Database host: `MyHost.local`, port: `3306`, ip: ``.
[2016-05-11 17:19:21]: [INFO]: Database date: `2016-05-11 17:19:21`.
[2016-05-11 17:19:21]: [INFO]: Hello World!
[2016-05-11 17:19:21]: [INFO]: Execution time: 0.009 sec.
[2016-05-11 17:19:21]: [INFO]: Memory peak usage: 8 MB.
As soon as you're using Loggable
trait, these PSR-3 methods are available for you:
logDebug
logInfo
logNotice
logWarning
logError
logCritical
logAlert
logEmergency
Each of them expects the message and optional context for additional data.
Channels are very simple. It's just the different ways to handle log messages.
File channel is nothing more than writing log messages into the file. It is the main channel, and it is always enabled.
Log file can be found at storage/logs/[command-name]/[date].log
. Namespaced commands exploded into subfolders.
| Command | Logs |
| ------------------------- | ------------------------------------- |
| php artisan send-report
| storage/logs/send-report/[date].log
|
| php artisan send:report
| storage/logs/send/report/[date].log
|
As you can see, each command has a separate folder for its logs. Also, you get configured log files rotation out of the box. By default, only latest thirty log files are stored. However, you can override this behavior as you wish:
class ExampleCommand extends Command
{
use Loggable;
protected function getLogPath()
{
return storage_path('logs/custom-folder/date.log');
}
protected function getLogMaxFiles()
{
return 45;
}
// ...
}
Want to be notified if some error occurred? Meet notifications!
Notification channels are optional and disabled by default. Each of them can be enabled and configured as needed. By default, you'll get notifications of each level which is higher than NOTICE (see PSR-3 log levels). It means that you'll get notifications about each NOTICE, WARNING, ERROR, CRITICAL, ALERT and EMERGENCY, occurred while execution.
Of course, you can change this and other channel-specific aspects as you wish.
Email channel provides notifications via email.
The only thing you have to do is specify recipients. Set recipients and email notifications are ready to go!
class ExampleCommand extends Command
{
use Loggable;
protected function getEmailNotificationsRecipients()
{
return [
['address' => 'john.doe@example.com', 'name' => 'John Doe'],
['address' => 'jane.smith@example.com', 'name' => 'Jane Smith'],
];
}
// ...
}
There is a bunch of methods specific to email channel. If you want to change email notifications level, or change the subject, or change the from address, or something else - override proper method as you wish. And you're done!
Another cool feature of email notifications is deduplication. Sometimes the same error can be produced many times. For example, you're using some external web service which is down. Or imagine that database server goes down. You'll get a lot of similar emails in those cases. Email notifications deduplication is the solution for these scenarios.
Disabled by default, it can be enabled and also adjusted the time in seconds, for which deduplication works.
class ExampleCommand extends Command
{
use Loggable;
protected function useEmailNotificationsDeduplication()
{
return true;
}
protected function getEmailNotificationsDeduplicationTime()
{
return 90;
}
// ...
}
Database channel provides saving of notifications into the database.
Disabled by default, it can be easily enabled by the proper method.
class ExampleCommand extends Command
{
use Loggable;
protected function useDatabaseNotifications()
{
return true;
}
// ...
}
By default, you will get iclogger_notifications
table, which would be created automatically, if it doesn't exist yet.
Of course, you can change the table name or even the logic of notification saving by overriding proper methods. It can be
useful if you want to add some custom fields to the notifications table. Here is the basic example of what it may look like:
class ExampleCommand extends Command
{
use Loggable;
protected function useDatabaseNotifications()
{
return true;
}
protected function getDatabaseNotificationsTable()
{
return 'custom_notifications';
}
protected function getDatabaseNotificationsCallback()
{
return function (array $record) {
CustomNotification::create([
'level' => $record['level'],
'level_name' => $record['level_name'],
'message' => $record['message'],
'context' => get_dump($record['context']),
'some_custom_field' => 'Lorem!',
]);
};
}
// ...
}
One of the coolest features is error handling. Each exception, each error and even PHP notices and warnings are handled. It would be automatically logged as an error, and you will get proper notifications according to your command's setup. You'll know immediately if something went wrong while execution. Very useful, especially for scheduled commands.
You can throw an exception of any type from your code, and it would be properly handled by the exception handler.
However, if you want to pass an additional context, use Illuminated\Console\Exceptions\RuntimeException
class:
use Illuminated\Console\Exceptions\RuntimeException;
class ExampleCommand extends Command
{
use Loggable;
public function handle()
{
throw new RuntimeException('Oooups! Houston, we have a problem!', [
'some' => 123,
'extra' => true,
'context' => null,
]);
}
// ...
}
[2016-05-11 17:19:21]: [ERROR]: Oooups! Houston, we have a problem!
array:5 [
"code" => 0
"message" => "Oooups! Houston, we have a problem!"
"file" => "/Applications/MAMP/htdocs/icl-test/app/Console/Commands/ExampleCommand.php"
"line" => 22
"context" => array:3 [
"some" => 123
"extra" => true
"context" => null
]
]
If you're using Guzzle, well, maybe you'll want to have logs of your HTTP interactions.
There is a helper function iclogger_guzzle_middleware
, which makes it very easy:
use GuzzleHttp\Client;
use GuzzleHttp\HandlerStack;
$handler = HandlerStack::create();
$middleware = iclogger_guzzle_middleware($log);
$handler->push($middleware);
$client = new Client([
'handler' => $handler,
'base_uri' => 'http://example.com',
]);
Now, your guzzle interactions are fully loggable. Each request, response and even errors would be logged for you.
You can also set type, as a second argument. Set it to json
to get auto JSON decoding for request params and response body.
And even more advanced options are the third and the fourth optional arguments, which are callbacks, by which you can customize your logging logic if needed.
Both of them should return a bool. shouldLogRequest
determines if request bodies should be logged or not, and shouldLogResponse
determines the same for the response bodies.
You can set any of your custom logic here. For example, maybe you want to skip logging for just specific URLs, or maybe you want to check the content length of the response, etc.
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
$middleware = iclogger_guzzle_middleware($log, 'json',
function (RequestInterface $request) {
if (ends_with($request->getUri(), '/foo')) {
return false; // skips logging for /foo request bodies
}
return true;
},
function (RequestInterface $request, ResponseInterface $response) {
$contentLength = $response->getHeaderLine('Content-Length');
if ($contentLength > (100 * 1024)) {
return false; // skips logging for responses greater than 100 KB
}
return true;
}
);
This package is using the Monolog logging library with all of its power and benefits.
If needed, you may access the underlying Monolog instance in a two ways:
Using icLogger
command's method:
class ExampleCommand extends Command
{
use Loggable;
public function handle()
{
$log = $this->icLogger();
}
// ...
}
Through Laravel service container:
$log = $app('log.iclogger');
Note, that Loggable
trait is overriding initialize
method:
trait Loggable
{
protected function initialize(InputInterface $input, OutputInterface $output)
{
$this->initializeLogging();
}
// ...
}
If your command is overriding initialize
method too, then you should call initializeLogging
method by yourself:
class ExampleCommand extends Command
{
use Loggable;
protected function initialize(InputInterface $input, OutputInterface $output)
{
$this->initializeLogging();
$this->foo = $this->argument('foo');
$this->bar = $this->argument('bar');
$this->baz = $this->argument('baz');
}
// ...
}
If you're using another illuminated/console-%
package, then you can find yourself getting into the "traits conflict".
For example, if you're trying to build loggable command, which is protected against overlapping:
class ExampleCommand extends Command
{
use Loggable;
use WithoutOverlapping;
// ...
}
You'll get the fatal error - the traits conflict, because of both of these traits are overriding initialize
method:
If two traits insert a method with the same name, a fatal error is produced, if the conflict is not explicitly resolved.
Override initialize
method by yourself, and initialize traits in required order:
class ExampleCommand extends Command
{
use Loggable;
use WithoutOverlapping;
protected function initialize(InputInterface $input, OutputInterface $output)
{
$this->initializeMutex();
$this->initializeLogging();
}
// ...
}
The MIT License. Please see License File for more information.