I just switched to monolog and wanted to log my message to the PHP console instead of a file. This might seem obvious for some people, but it took me a little while to figure out how to do that and I couldn't find a similar question/answer on SO.
The example on Monolog's Github readme only shows how to use a file:
<?php
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
// create a log channel
$log = new Logger('name');
$log->pushHandler(new StreamHandler('path/to/your.log', Logger::WARNING)); // <<< uses a file
// add records to the log
$log->addWarning('Foo');
$log->addError('Bar');
But it doesn't state anywhere how messages can be logged to the console. After searching on Google, I landed either on a help page for Symfony or questions of people looking for a way to log to the browser console.
The solution is rather simple. Since the example shows a StreamHandler it's possible to pass in a stream (instead of the path to a file). By default, everything that is echo'ed in PHP is written to php://stdout / php://output so we can simple use one of those as stream for the StreamHandler:
<?php
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
// create a log channel
$log = new Logger('name');
$log->pushHandler(new StreamHandler('php://stdout', Logger::WARNING)); // <<< uses a stream
// add records to the log
$log->warning('Foo');
$log->error('Bar');
Hope this saves somebody some time :)
Some extra details if you want to tweak the default message formatting at the same time:
use Monolog\Logger;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\StreamHandler;
$output = "[%datetime%] %channel%.%level_name%: %message%\n";
$formatter = new LineFormatter($output);
$streamHandler = new StreamHandler('php://stdout', Logger::DEBUG);
$streamHandler->setFormatter($formatter);
$logger = new Logger('LoggerName');
$logger->pushHandler($streamHandler);
Related
I'm using the following lines to create a logger via zend-log:
use Zend\Log\Logger;
use Zend\Log\Writer\Stream;
$logger = new Logger();
$writer = new Stream(__DIR__ . '/application.log');
$logger->addWriter($writer);
This works fine so far and I can write debug messages with:
$logger->debug('Hello World');
Is there a possibiility to log the file name of the script and the line of code which calls the debug function? I mean something like this:
$logger->debug(__FILE__, __LINE__, 'Hello World');
I know I can pass a second parameter (extra) to the debug function but is there a formatter or writer that logs these information automatically?
I was looking for a PHP Intrusion Detection System and it seems like
Exposed was a good candidate. The latest
documentation is here.
I am having some problems understanding the installing of Exposed, so here is my question.
Say I want to add this layer of security on a file called users.php, I have read the documentation and my file would then look like this:
<?php
// <EXSPOSED_SNIPPET_LOGIC>
// Enable loader, mocklogger
require 'vendor/autoload.php';
// Initiate Monolog
use Monolog\Logger;
use Monolog\Handler\FirePHPHandler;
ini_set('display_errors', 0);
// The data to filter
$data = array(
'GET' => $_GET,
'POST' => $_POST,
'COOKIE' => $_COOKIE
);
// Load filters
$filters = new \Expose\FilterCollection();
$filters->load();
// Initiate logger
$logger = new Logger('my_logger');
$logger->pushHandler(new StreamHandler(__DIR__.'/__exposed.log', Logger::DEBUG));
$logger->pushHandler(new FirePHPHandler());
// Run tests
$manager = new \Expose\Manager($filters, $logger);
$manager->run($data);
// Define a threatlevel to act upon
$threatlevel = 12;
if( $manager->getImpact() >= $threatlevel ){
// **001**
// We are most likely under attack or atleast we should pay attention
// What do we log here, how do we log it and do we send email to alert?
$reports = $manager->getReports();
print_r($reports);
} else {
// **002**
// Assuming there is normal activity
// Should we log anything here, and if so what?
}
// </EXSPOSED_SNIPPET_LOGIC>
// Here comes the original code belonging to users.php
/* ... */
?>
The code will now write all request to a logfile from the line:
$logger->pushHandler(new StreamHandler(__DIR__.'/__exposed.log', Logger::DEBUG));
Is this needed, this will be a huge amount of data in a short while and can I even use the log for any purposa at a later time?
I also wonder on the Monolog Handler, this line:
use Monolog\Handler\FirePHPHandler;
...
$logger->pushHandler(new FirePHPHandler());
What does this code even do? I cannot see any difference in output to screen with or without it, the logfile also seems identical with or without this.
My main questions are on the lines ** 001 ** AND ** 002 ** in comments, that is, if I have understood the concept correctly hopefully my questions are not missing the target here.
It seems to me that checking the input and when a certain level is triggered, a certrain logging part and tracking should be enabled. Maby this is not part of Exposed? If you have some input on finalizing this integration code I would be very greatful.
Thank you in advance
There are two noisy console commands in my Laravel 5.3 app that I want to keep logs for but would prefer to have them write to a different log file from the rest of the system.
Currently my app writes logs to a file configured in bootstrap/app.php using $app->configureMonologUsing(function($monolog) { ...
Second prize is writing all console commands to another log file, but ideally just these two.
I tried following these instructions (https://blog.muya.co.ke/configure-custom-logging-in-laravel-5/ and https://laracasts.com/discuss/channels/general-discussion/advance-logging-with-laravel-and-monolog) to reroute all console logs to another file but it did not work and just caused weird issues in the rest of the code.
If this is still the preferred method in 5.3 then I will keep trying, but was wondering if there was newer method or a method to only change the file for those two console commands.
They are two approaches you could take
First, you could use Log::useFiles or Log::useDailyFiles like suggests here.
Log::useDailyFiles(storage_path().'/logs/name-of-log.log');
Log::info([info to log]);
The downside of this approach is that everything will still be log in your default log file because the default Monolog is executed before your code.
Second, to avoid to have everything in your default log, you could overwrite the default logging class. An exemple of this is given here. You could have a specific log file for let's say Log::info() and all the others logs could be written in your default file. The obvious downside of this approach is that it requires more work and code maintenance.
This is possible but first you need to remove existing handlers.
Monolog already has had some logging handlers set, so you need to get rid of those with $monolog->popHandler();. Then using Wistar's suggestion a simple way of adding a new log is with $log->useFiles('/var/log/nginx/ds.console.log', $level='info');.
public function fire (Writer $log)
{
$monolog = $log->getMonolog();
$monolog->popHandler();
$log->useFiles('/var/log/nginx/ds.console.log', $level='info');
$log->useFiles('/var/log/nginx/ds.console.log', $level='error');
...
For multiple handlers
If you have more than one log handler set (if for example you are using Sentry) you may need to pop more than one before the handlers are clear. If you want to keep a handler, you need to loop through all of them and then readd the ones you wanted to keep.
$monolog->popHandler() will throw an exception if you try to pop a non-existant handler so you have to jump through hoops to get it working.
public function fire (Writer $log)
{
$monolog = $log->getMonolog();
$handlers = $monolog->getHandlers();
$numberOfHandlers = count($handlers);
$saveHandlers = [];
for ($idx=0; $idx<$numberOfHandlers; $idx++)
{
$handler = $monolog->popHandler();
if (get_class($handler) !== 'Monolog\Handler\StreamHandler')
{
$saveHandlers[] = $handler;
}
}
foreach ($saveHandlers as $handler)
{
$monolog->pushHandler($handler);
}
$log->useFiles('/var/log/nginx/ds.console.log', $level='info');
$log->useFiles('/var/log/nginx/ds.console.log', $level='error');
...
For more control over the log file, instead of $log->useFiles() you can use something like this:
$logStreamHandler = new \Monolog\Handler\StreamHandler('/var/log/nginx/ds.console.log');
$pid = getmypid();
$logFormat = "%datetime% $pid [%level_name%]: %message%\n";
$formatter = new \Monolog\Formatter\LineFormatter($logFormat, null, true);
$logStreamHandler->setFormatter($formatter);
$monolog->pushHandler($logStreamHandler);
I've heard a lot about monolog(https://github.com/Seldaek/monolog) & trying to use that in one of our app. But, can't able to fig. out how to use that. Don't know it's I'm only can't able to get any documentation of it or really it has no documentation at all.
We want to log all our errors in DB & as well as send an email notification about error when it'll get generate. For sending email we are using Swiftmailer(swiftmailer.org).
I can be able to run this sample code from Github link,
<?php
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
// create a log channel
$log = new Logger('name');
$log->pushHandler(new StreamHandler('path/to/your.log', Logger::WARNING));
// add records to the log
$log->addWarning('Foo');
$log->addError('Bar');
but can't able to understand how to use this with DB & any other email library.
You posted an example yourself. Instead of StreamHandler use one or more of the other handlers that monolog is offering.
You have to look into the code of the handlers to see which dependencies they need. Look inside the Monolog directory and you'll find the Handler classes. Code is the most reliable documentation.
<?php
use Monolog\Logger;
use Monolog\Handler\SwiftMailerHandler;
use Swift_Mailer;
// ... more dependencies you need
// create your Swift_Mailer and Swift_Message instances
$handlers = [
new SwiftMailerHandler($swiftMailer, $swiftMessage),
// add more handler you need
];
$log = new Logger('name', $handlers);
$log->warning('Foo');
$log->error('Bar');
You have to create a Swift_Mailer and Swift_Message instance for the SwiftMailerHandler. Instead of pushHandler, you can add an array of handlers into the Logger constructor.
The Swift_Message instance is used for every log message where the message replaces every time the mail body.
I can only suggest to you to read the monolog code for information where a further documentation is missing.
I use error_log for my logging, but I'm realizing that there must be a more idiomatic way to log application progress. is there an info_log ? or equivalent ?
You can use error_log to append to a specified file.
error_log($myMessage, 3, 'my/file/path/log.txt');
Note that you need to have the 3 (message type) in order to append to the given file.
You can create a function early on in your script to wrap this functionality:
function log_message($message) {
error_log($message, 3, 'my/file/path/log.txt');
}
The equivalent is syslog() with the LOG_INFO constant:
syslog(LOG_INFO, 'Message');
If you want to use a file (not a good idea because there is no log rotation and it can fail because of concurrency), you can do:
file_put_contents($filename, 'INFO Message', FILE_APPEND);
I would recommend you to use Monolog:
https://github.com/Seldaek/monolog
You can add the dependency by composer:
composer require monolog/monolog
Then you can initialize a file streaming log, for an app called your-app-name, into the file path/to/your.log, as follow:
<?php
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
// create a log channel
$log = new Logger('your-app-name');
$log->pushHandler(new StreamHandler('path/to/your.log', Logger::WARNING));
Monolog is very powerful, you can implement many types of handlers, formatters, and processors. It worth having a look:
https://github.com/Seldaek/monolog/blob/master/doc/02-handlers-formatters-processors.md
And finally call it like:
// add records to the log
$log->warning('Foo');
$log->error('Bar');
In order to make it global, I recommend you to add it to your dependency injector, if you have one, or use a singleton with static calls.
Let me know if you want more details about this.