Monolog does not work in Laravel Console on server - php

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.

Related

Download a file from command line in laravel artisan

I am using laravel Artisan Commands to download a file from public folder .
Actually i want to download a zip file which i have successfully made in public folder.
I am able to download the file when I call the methods on button click.
But when I execute the command in console it does not work and also not throw any error.
public function downloadExportRawData() {
$a=public_path().'/temp/rawexport/rawexportproduct.zip';
$path='/temp/rawexport/rawexportproduct.zip';
if(file_exists('temp/rawexport/rawexportproduct.zip')){
return Response::download($a);
}else{
return "hello";
}
}
Tried this also...
public static function downloadZip($rand_folder, $zipname, $fileName) {
header('Content-Type: application/force-download');
header('Content-Disposition: attachment; filename="' . $fileName . '.zip"');
header('Content-Length: ' . filesize($zipname));
$handle = fopen($zipname, 'rb');
//exec("rm -r ".$temp);
while (!feof($handle)) {
echo fread($handle, 1048576);
ob_flush();
flush();
}
fclose($handle);
// exec("rm -r " . $rand_folder);
}
and the command is
php artisan product:export --from=http://localhost/valuable-app/dev-master/rest_app/public/ --productid=5b273b0e4bb5995b108b4576 --tokenexport=354c3ddb2bd45aa6e5c4f749749b0b58cbacef48
The main problem is that it is going through the function in controller but not downloading it.
And I tried to echo the response it prints code in zip form means a encoded form which have something related to my zip file means its printing the zip not downloading it.
I am using Artisan first time. Any idea how to download that file using using user-defined Artisan command.
I am trying this from last two weeks but not able to do anything.
How about this:
First identify the file which you want to download and then call
string shell_exec ( string $cmd )
you can use wget to get the file.
for example, in your command
$fileName = $this->argument('filename');
$file = url('storage/public/' . $fileName);
shell_exec("wget $file");
$this->info('downloading');
if you'r using the console, the http download will not work (you are not using a browser).
Instead you can move the file to your wished destination or something like that, but not download it via console
console will not handle http response as download (will not save it)
I think you should consider to add second argument in your console:
--destination=/home/user/downloads/filetosaveas.extension and in your code use curl to download
or more simple use core curl:
curl http://example.com --output my.file --silent
It would be nice if you actually went through the nicely done Writing Commands guide at the Laravel Docs site, here.
In a nutshell, there are mainly two ways to register artisan command. Be it that one is an alternative to the other.
(Like routes) specify your instructions within a closure.
(Like controllers) create a command class and specify your instructions.
The closure way
Inside my routes/console.php, I specify something like below:
Artisan::command('download:file', function () {
// your code goes here.
})->describe('Download a file.');
Now execute a help routine for your newly created command.
-> php artisan help download:file
Description:
Donwload a file.
Usage:
donwload:file
//...
The command way
Generate the command class using php artisan make:command EmailFileCommand. This command will create app/Console/Commands/EmailFileCommand.php. Update the file to specify what we want it to do.
// ...
class EmailFileCommand extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'email:file';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Email a file.';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return mixed
*/
public function handle()
{
// your code goes here.
}
}
Trigger commands via HTTP
Say you want to call the commands via an http request. In my routes/web.php file,
//...
use Artisan;
//...
Route::get('/download', function () {
Artisan::call('download:file');
});
Route::get('/email', function () {
Artisan::call('email:file');
});
//...
Now execute your http requests /download and /email.
To download a file, you can use Laravel's Filesystem.
//...
use Storage;
//...
Storage::download('file.jpg')
This post has been extracted from my blog post here.
One can use the shell_exec() command to run a terminal command through PHP function or script. It will execute the command without opening the terminal window. the behaviour of command will be little bit different from the original command.

Log Laravel with artisan output

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.

Set up Monolog in Laravel global middleware

In my local environment I want all logs (all flags) to go the browser's console (BrowserConsoleHandler) and then to the default StreamHandler.
In production, I want the errors and other critical messages to go to an e-mail then stored in the database or (if fails) to a log file (default StreamHandler)
I want to set up this in a global middle-ware that I have created, which looks like this now:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Log;
use Monolog\Logger;
use Monolog\Handler\BrowserConsoleHandler;
class GlobalConfig
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
// Get Monolog instance
$monolog = Log::getMonolog();
// In local environment the logs will be shown on browser
if (App::environment('local')) {
// Show logs in browser console
$monolog -> pushHandler(new BrowserConsoleHandler());
} else {
// Here we set up for production
}
Log::debug("Browser handler working");
return $next($request);
}
}
This doesn't work (the message is stored in the log file only, not shown on console). What I can't figure out is how to let the Log facade know about this new handler, because here, as is obvious, things are only changed within the function's scope. I know I can do it in bootstrap/app.php but isn't it to early to get the environment? Also, if I need to save logs to the database, it must already be connected, I guess
You can try to do next in bootstrap\app.php
if(env('APP_ENV') == 'local') {
$app->configureMonologUsing(function($monolog) use ($app) {
$monolog->pushHandler(
$handler = new \Monolog\Handler\RotatingFileHandler(
$app->storagePath().'/logs/laravel.log',
$app->make('config')->get('app.log_max_files', 30),
\Monolog\Logger::DEBUG
)
);
$handler->setFormatter(new \Monolog\Formatter\LineFormatter(null, null, true, true));
$monolog->pushHandler(new \Monolog\Handler\NativeMailerHandler(
'to#mail.com',
'Log::error!',
'from#mail.com'
));
});
}
Idea here is to fully control your logging on live/local. Bad news - configureMonologUsing replace all default laravel loggers, so you need to configure all your logs manually here.
Middleware isn't really the place for this, unless you wanted to actually make a particular log entry at some point in the request cycle.
Your call to pushHandler belongs in bootstrap/app.php (in Laravel 5.2), according to the docs.
I would think it would be possible to extract this logic out into a provider in case it becomes complicated and you need to move it out of bootstrap/app.php
solution here
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.

PHP webhook script (bitbucket)

I'm trying to attempt using a webhook script so that I can just commit locally and have the script triggered server-side and pull in any change.
Now if I login to the server via SSH and run php webhook.php the cript is triggered successfully and the files are updated. So I know the file does work. But if I make edits to the files, commit and push to master, I'm not seeing the files updated.
The log file produced by the script suggests everything is fine, but clearly it's not.
My file structure is like this:
var
www
my-project
- webhook.php
repo-folder
So the file should be pulling the files into repo-folder and the webhook.php is set as the webhook via the bitbucket control panel. If I view the log within bitbucket, it's showing a successful request every time I push a commit.
The script:
<?php
date_default_timezone_set('Europe/London');
class Deploy {
/**
* A callback function to call after the deploy has finished.
*
* #var callback
*/
public $post_deploy;
/**
* The name of the file that will be used for logging deployments. Set to
* FALSE to disable logging.
*
* #var string
*/
private $_log = 'deployments.log';
/**
* The timestamp format used for logging.
*
* #link http://www.php.net/manual/en/function.date.php
* #var string
*/
private $_date_format = 'Y-m-d H:i:sP';
/**
* The name of the branch to pull from.
*
* #var string
*/
private $_branch = 'master';
/**
* The name of the remote to pull from.
*
* #var string
*/
private $_remote = 'origin';
/**
* The directory where your website and git repository are located, can be
* a relative or absolute path
*
* #var string
*/
private $_directory;
/**
* Sets up defaults.
*
* #param string $directory Directory where your website is located
* #param array $data Information about the deployment
*/
public function __construct($directory, $options = array())
{
// Determine the directory path
$this->_directory = realpath($directory).DIRECTORY_SEPARATOR;
$available_options = array('log', 'date_format', 'branch', 'remote');
foreach ($options as $option => $value)
{
if (in_array($option, $available_options))
{
$this->{'_'.$option} = $value;
}
}
$this->log('Attempting deployment...');
}
/**
* Writes a message to the log file.
*
* #param string $message The message to write
* #param string $type The type of log message (e.g. INFO, DEBUG, ERROR, etc.)
*/
public function log($message, $type = 'INFO')
{
if ($this->_log)
{
// Set the name of the log file
$filename = $this->_log;
if ( ! file_exists($filename))
{
// Create the log file
file_put_contents($filename, '');
// Allow anyone to write to log files
chmod($filename, 0666);
}
// Write the message into the log file
// Format: time --- type: message
file_put_contents($filename, date($this->_date_format).' --- '.$type.': '.$message.PHP_EOL, FILE_APPEND);
}
}
/**
* Executes the necessary commands to deploy the website.
*/
public function execute()
{
try
{
// Make sure we're in the right directory
chdir($this->_directory);
$this->log('Changing working directory... ');
// Discard any changes to tracked files since our last deploy
exec('git reset --hard HEAD', $output);
$this->log('Reseting repository... '.implode(' ', $output));
// Update the local repository
exec('git pull '.$this->_remote.' '.$this->_branch, $output);
$this->log('Pulling in changes... '.implode(' ', $output));
// Secure the .git directory
exec('chmod -R og-rx .git');
$this->log('Securing .git directory... ');
if (is_callable($this->post_deploy))
{
call_user_func($this->post_deploy, $this->_data);
}
$this->log('Deployment successful.');
}
catch (Exception $e)
{
$this->log($e, 'ERROR');
}
}
}
// This is just an example
$deploy = new Deploy('/var/www/site-name/repo-name');
$deploy->execute();
?>
i was researching this more and the best way you can do is like i mention find out what is the user being used by apache server by building a php script and run the shell_exec('whoami'); and run on your browser to see that user it is.
Then on your document root for your website mine per example is /var/www you would need to create shh keys for this directory when you do that also add a config file with the host bitbucket and reference to the key you created.
Add the key to bitbucket
then you would need to add permission for apache to run the git command run the command visudo and add:
yourapacheuser ALL=(yourapacheuser) NOPASSWD: /usr/bin/ #the /usr/bin in my case is where i have installed git so you need to see what is the directory path where you install git.
After that you should be able to run your script without problems, in case it complains about a requiretty message then on visudo again add: Defaults:yourapacheuser !requiretty
hope it helps
did you configure SSH keys for the server you are using in bitbucket and add the webhook with the url of your script?
As user1361389 wrote you need to know what users are running the different processes. This is how I did on an Amazon Ubuntu instance.
I have a php file that calls a bash script:
shell_exec("sudo -u ubuntu /home/ubuntu/gitpull.sh");
Create SSH keys for user ubuntu and uploaded the public key to bitbucket.
Also, make sure that the php files on your server are owned by the correct user. In my case ubuntu.
You then need to impersonate ubuntu when calling the php file to deploy code. Add this line in the sudoer file (>sudo visudo )
www-data ALL=(ubuntu) NOPASSWD: /path/to/gitpull.sh
Then in bitbucket add the URL to your hook.

Trouble executing the Yii web service demo

I'm fairly new to Yii and am currently trying to use this framework to create some PHP web services. While trying to execute the brief tutorial on web services provided on the Yii web site http://www.yiiframework.com/doc/guide/1.1/en/topics.webservice#declaring-web-service-action I ran into some trouble. Namely, I get a "Maximum execution time of 60 seconds exceeded" fatal error when executing the script. My guess is that the getPrice() method actually never gets called.
I'd appreciate any suggestions related to why this may be happening. The contents of my index.php file are listed below. (Note that the Yii gramework is properly installed and I'm running PHP 5.3.0 with the php_soap extension).
<?php
$yii=dirname(__FILE__).'/../yii/framework/yii.php';
defined('YII_DEBUG') or define('YII_DEBUG',true);
defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL',3);
require_once($yii);
class StockController extends CController{
function __construct(){
parent::__construct($this->id, $this->module);
}
public function actions(){
return array(
'quote'=>array(
'class'=>'CWebServiceAction',
),
);
}
/**
* #param string the symbol of the stock
* #return float the stock price
* #soap
*/
public function getPrice($symbol){
$prices=array('IBM'=>100, 'GOOGLE'=>350);
return isset($prices[$symbol])?$prices[$symbol]:0;
//...return stock price for $symbol
}
}
$client=new SoapClient('http://localhost/SampleWebService/?r=stock/quote');
echo $client->getPrice('GOOGLE');
?>
It seems strange that you are declaring the call in the index.php entry script... I'm also not sure why you are overriding the constructor?
And I think, if this really in your entry script, you are missing call the create the application, either Yii::createWebApplication($config)->run(); or Yii::createConsoleApplication($config)->run();, depending on if you are running this as a web or console application.
Have you made sure that the application is running as expected without the SOAP/services stuff? I would set up a basic Hello World app (web or console), then try the web services.

Categories