I'm working with Laravel and I trying to do a log of some function output with this Log function:
Log::warning("some message")
But if I want to write in console and log file I need to write two times the same message with different function like this:
Log::warning("some message") //This work good for log file
dump("some message") //This work good for artisan console output
There is some function that allow me only use one of both?
You can use Laravel events to integrate this functionality without requiring any change to the way you currently log information.
Add a listener for the Illuminate\Log\Events\MessageLogged event, and within your listener output the log entry to the console if the request has come from the console -- using runningInConsole().
Register a new listener called MessageLoggedListener, e.g:
protected $listen = [
'Illuminate\Log\Events\MessageLogged' => [
'App\Listeners\MessageLoggedListener',
],
];
Generate your listener with php artisan event:generate
Add the event handler to your listener:
/**
* Handle the event.
*
* #param MessageLogged $event
* #return void
*/
public function handle(MessageLogged $event)
{
if (app()->runningInConsole()) {
$output = new ConsoleOutput();
$output->writeln("<error>{$event->message}</error>");
}
}
That's it! You're now ready to test the functionality. From a console command log a message, e.g:
public function handle()
{
Log::error('Hello world! This is an error.');
}
This is the output you'll see:
$ php artisan command
Hello world! This is an error.
And within your log file you'll see:
[2018-01-15 16:55:46] local.WARNING: Hello world! This is an error.
You can improve the functionality by adding different output styles, for example you may wish to use error for errors and info for info. You can read about styling your output here in the Symfony documentation.
Related
I have set up a listener by adding it in EventServiceProvider
protected $subscribe = [
MyListener::class
];
The listener (MyListener) has a subscribe function that subscribes to the events the listener wants to listen for - and it works fine.
Now, I'm trying to add a check to restrict which events should be listened to. Something like
public function subscribe($events)
{
$config = ConfigService::getUserConfig();
if ($config->shouldSubscribe) {
$events->listen(.....);
}
}
I'm having some issues after adding this logic however.
It seems that when running composer install it executes the subscribe method.
This causes an issue, because there is no active session when running composer install - so I'm met with a SQL error - it can't find which database to search for configuration in - followed by this error
Script #php artisan package:discover handling the post-autoload-dump event returned with error code 1
How can I conditionally subscribe to certain events in the listener?
It is not the exact response for your answer, but it should work in your case. You can detect if your code is running from console by using Application::runningInConsole() function.
Example:
public function subscribe($events)
{
// Running from cli script, abort ship!
if(app()->runningInConsole())
{
return;
}
$config = ConfigService::getUserConfig();
if ($config->shouldSubscribe) {
$events->listen(.....);
}
}
I have an event listener that creates and saves a pdf file from a record in the db.
This process completes correctly, then I log the results and wish to fire a Job or another Event and neither will run.
If I run either from a controller they execute flawlessly.
handle method looks like so:
/**
* Handle the event.
*
* #param SaveRecordPdf $event
* #return void
*/
public function handle(SaveRecordPdf $event)
{
$record = $event->record;
if ($this->service->publish($record)) {
Log::info($record->published_filename . ' was stored successfully.');
// fire publish job
PublishFile::dispatch($record);
}
}
Is there any known reason why this would be happening?
Try running the following in your repository:
composer dumpautoload && php artisan clear-compiled
I'm having problems with logging some basic info line from Console. Locally, this works fine, everything gets logged (errors, info messages) from web and from console. But on server, error logging works fine, but when i call logger myself and try to log some info message it does not work.
I'm using Laravel 5.4 version.
This is some App\Console\Commands\DummyCommand class for test purposes.
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Log\Writer as Log;
class DummyCommand extends Command
{
/**
* Create a new console command instance.
*
* #param \Illuminate\Log\Writer $log
* #return void
*/
public function __construct(Log $log)
{
parent::__construct();
$this->log = $log;
}
/**
* Execute the console command.
*
* #return mixed
*/
public function handle()
{
$message = "Some dummy message";
$this->info($message); // gets printed on console both locally and on server
$this->log->info($message);
$ret = $this->log->getMonolog()->addInfo($message);
dump($ret); // locally = true, server = false
if ($ret === false) {
throw new \Exception('Error while logging!'); // this log works fine both locally and on server
}
}
}
I run the following on the server:
php artisan dummy // prints "Some dummy message" and loggs error message "Error while logging!"
php artisan schedule:run // same thing as above
Any idea why is this not working ?
Permissions on storage folder is fine for all folders and files. Error messages (exceptions) gets logged but info messages does not.
Edited:
This is working, I need to add following line before calling $this->log->info(); but there is no way i would use on all Commands and every time need to set path to log file. (source: https://laracasts.com/discuss/channels/general-discussion/logging-in-l5/replies/11771)
$this->log->useDailyFiles(storage_path().'/logs/laravel.log');
Figured it out, I added multiple message type:
$this->log->getMonolog()->addAlert($message);
$this->log->getMonolog()->addCritical($message);
$this->log->getMonolog()->addDebug($message);
$this->log->getMonolog()->addError($message);
$this->log->getMonolog()->addWarning($message);
And it logs all messages with error prefix (everything except info). So I looked in .env file, and there was next line about log level:
APP_LOG_LEVEL=notice
Changed that to "debug" and now it's working fine.
I'm pretty new to Codeception and I have come across a problem I cannot figure out. I have about 40 tests in my test suite, and if a test fails, I need to send an email with the reason it failed. For example, if Codeception cannot find an element on the page resulting in a failed test, I need to send an email with just the error, like this:
Failed to verify emailing wish list behaves as expected in ThisClass::thisTest (/home/qauser/codeception_tests///acceptance-mobile/Wishlist/EmailWishlistCest.php)
Couldn't see "Success!","//*[#id="wish-list-confirm-popup"]/div/div/div[1]/h4":
I don't want to send the full stack trace, just the actual error. Does anyone know if this is possible?
Codeception exposes a useful collection of events that will come in handy for this use case. Take a look at the Customization: Events section of Codeception's documentation for more information.
I'd recommend intercepting two of the events described on that page:
The test.fail event, to aggregate information about each failed test.
The test.fail.print event, to process the aggregated data (eg. by sending a summary email) when Codeception has completed the test suite and prints its own summary of the failures to the screen.
To accomplish this, you simply need to build a custom event handler class and register it as an extension in the config file:
# codeception.yml
extensions:
enabled: [MyCustomEventHandler]
# MyCustomEventHandler.php
<?php
// Note: this was drafted using Codeception 2.0. Some of the namespaces
// maybe different if you're using a more-recent version of Codeception.
class MyCustomEventHandler extends \Codeception\Platform\Extension
{
/**
* #var \Exception[]
*/
protected $testFailures = [];
/**
* Maps Codeception events to method names in this class.
*
* Defining an event/method pair in this array essentially subscribes
* the method as a listener for its corresponding event.
*
* #var array
*/
public static $events = [
\Codeception\Events::TEST_FAIL => 'singleTestJustFailed',
\Codeception\Events::TEST_FAIL_PRINT => 'allTestFailuresAreBeingDisplayed',
];
/**
* This method will automatically be invoked by Codeception when a test fails.
*
* #param \Codeception\Event\FailEvent $event
*/
public function singleTestJustFailed(\Codeception\Event\FailEvent $event)
{
// Here we build a list of all the failures. They'll be consumed further downstream.
$this->testFailures[] = $event->getFail();
}
/**
* This method will automatically be invoked by Codeception when it displays
* a summary of all the test failures at the end of the test suite.
*/
public function allTestFailuresAreBeingDisplayed()
{
// Build the email.
$emailBody = '';
foreach ($this->testFailures as $failure) {
// Methods in scope include: $failure->getMessage(), $failure->getFile(), etc.
$emailBody .= $failure->getMessage() . "\r\n";
}
// Now send the email!
}
}
Hope this helps!
How can Laravel 5's logging be changed to Monolog\Handler\BrowserConsoleHandler?
What doesn't work in Laravel 5 but does work in a standalone PHP file:
use Illuminate\Support\Facades\Log;
use Monolog\Handler\BrowserConsoleHandler;
use Monolog\Logger;
// create a log channel
$log = Log::getMonolog();
// $log = new Logger('Testlogger'); //doesn't make any difference
$log->pushHandler(new BrowserConsoleHandler(\Psr\Log\LogLevel::INFO));
// add records to the log
$log->addWarning('Foo');
$log->addError('Bar');
All that happens is that my logs appear in the logfile but don't find their way to the browser. If I try the code in a single PHP file without framework it works, so I assume it's a Laravel problem.
I get it working with Firebug and FirePHP installed and $log->pushHandler(new FirePHPHandler()); instead of BrowserConsoleHandler but this is not a solution since it sends the logs with headers but I already sent some debug-echos when the logger wants to send the headers.
BrowserConsoleHandler on the other hand adds a JavaScript snippet to the end of the site that perfectly fits my needs.
So, did anyone succeed in adding BrowserConsoleHandler to Laravel's logging? How?
After reading plenty of source code and getting xdebug to work I finally figuered it out:
BrowserConsoleHandler sends the script snipped after finishing the php script by register_shutdown_function(). At this time, Laravel already sent the full response to the browser. So the script snipped from BrowseConsoleHandler gets generated but never sent to the browser.
As a workaround you can build your own Middleware (http://laravel.com/docs/5.0/middleware) that invokes the code generation manually and adds it to the response before it gets sent.
Create app/Http/Middleware/LogBrowserConsole.php:
<?php
namespace App\Http\Middleware;
use Illuminate\Contracts\Routing\Middleware;
use Illuminate\Support\Facades\Log;
use Monolog\Handler\BrowserConsoleHandler;
class LogBrowserConsole implements Middleware {
public function handle($request, \Closure $next)
{
// add BrowserConsoleHandler to Laravel's Logger
$log = Log::getMonolog();
$log->pushHandler(new BrowserConsoleHandler(\Psr\Log\LogLevel::INFO));
// invokes all your stuff like it would do without the middleware but with the new logger
$response = $next($request);
// after the request is done we care about the log entries
$handlers = $log->getHandlers();
$scriptSnippet = "";
foreach($handlers as $handler){ // only handle BrowserConsoleHandler
if($handler instanceof BrowserConsoleHandler){
ob_start(); //start output buffer so we can save echo to variable
$handler->send(); // create the scriptSnipped
$scriptSnippet .= ob_get_clean();
}
}
// write scriptSnippet to end of response content
$content = $response->getContent();
$response->setContent($content.$scriptSnippet);
return $response;
}
}
Register the Middleware in app/Http/Kernel.php:
protected $routeMiddleware = [
'log.browserconsole' => 'App\Http\Middleware\LogBrowserConsole'
];
and invoke your Controller with the Middleware in app/Http/routes.php:
Route::get('test', ['middleware' => 'log.browserconsole', 'uses'=>'TestController#test']);
Alternatively, if you want to use the Middleware for every request you can add it to
protected $middleware = [
'App\Http\Middleware\LogBrowserConsole'
];
in app/Http/Kernel.php.
Your Route would look like Route::get('test', 'TestController#test');
Now, your Log::debug() etc. messages get sent to the logfile (the default LogHandler is still available, you just added another) and the script snipped from BrowserConsoleHandler gets built and sent to the browser with all your log items.
Keep in mind to eventually change the log level \Psr\LogLevel::INFO in app/Http/Middleware/LogBrowserConsole to fit your needs.