log4php logger->setLevel() doesn't work - php

Not sure why this very simple thing doesn't work -
$logger = Logger::getLogger("test");
$logger->setLevel(LoggerLevel::getLevelWarn());
$logger->debug("debug");
$logger->info("info");
$logger->warn("warn");
This will print:
DEBUG - debug
INFO - info
WARN - warn
I must be doing something obviously stupid! I would assume that neither the debug nor info logging would come through with the setLevel() to warn was present.
Thoughts?
I would like to have multiple loggers that each log at different levels. Not sure why this is so hard...

If you check the created Logger object (after you initialize it with Logger::getLogger("test");), you'll see that it already has a parent Logger, object(LoggerRoot). This object has its level set to DEBUG, and that's actually the logger that prints the debug and info messages.
One possible approach is to use a RootLogger instead:
$logger = Logger::getRootLogger();
$logger->setLevel(LoggerLevel::getLevelWarn());
$logger->debug("debug");
$logger->info("info");
$logger->warn("warn");
Another approach is to configure rootLogger so that it will log only messages of some very high level (FATAL), and let your loggers to set levels appropriate to them. But in that case you need to add your own appenders to those (it's appenders that do the logging job; loggers just manage them). Again, one possible approach:
$rootLogger = Logger::getRootLogger();
$rootLogger->setLevel(LoggerLevel::getLevelFatal());
$logger = Logger::getLogger('some');
$logger->addAppender($rootLogger->getAppender('default'));
$logger->setLevel(LoggerLevel::getLevelInfo());
$logger->debug('debug'); // won't print
$logger->info('info'); // will be printed
$logger->warn('warn'); // will be printed too
$logger->fatal('fatal dup'); // will be printed TWICE:
// with $logger, then with $rootLogger
$logger->setAdditivity(false); // switching off log event propagation
$logger->fatal('fatal once'); // will be printed ONCE
But it you actually need a complex hierarchy of loggers, I'd highly suggest configuring them all at once, following the approach described in this section of log4php documentation.

Related

How to Turn off Debugging Logs with Monolog

I'm using Monolog in a project, it's not Symfony, just my own application that uses the stand-alone Monolog composer package.
What I'd like to do is programmatically turn off debugging logs. I'm writing to a log file and I'm using the Monolog::StreamHandler. I'm controlling whether the application is in debug mode or not with a Configuration class that gets the debug value from a configuration file. So when someone changes that value to debugging is false, debug logging should turn off.
I felt like the easiest way to do this would be to extend StreamHandler and override StreamHandler's write method like this.
class DurpLogger extends StreamHandler {
protected function write(array $record) {
if ($this->getLevel() == Durp::Debug && !Configuration::debug()) {
return;
}
parent::write($record);
}
}
So if a log request comes in and the log level for the handler is set to DEBUG and the application's Configuration::debug() is FALSE then just return without writing the log message. Otherwise, StreamHandler will do its thing.
I'm wondering if this is the best way to use Monolog or if there's perhaps a cleaner way to do this.
I envision there being a handler in my application for DEBUG, INFO, ERROR and whatever levels I might need for my application. Perhaps it makes sense to not rely on a Configuration::debug() that can only be TRUE or FALSE, but rather a Configuration::logLevel() that will allow me to more granularly control logging output.
But even still, does extending StreamHandler make the most sense when controlling Monolog at the application level?
UPDATE
Now, I'm thinking something like this, that uses level rather than just boolean debug.
class DurpLogger extends StreamHandler {
public function __construct() {
parent::__construct(Configuration::logFile(), Configuration::logLevel());
}
protected function write(array $record) {
if (!($this->getLevel() >= Configuration::logLevel())) {
return;
}
parent::write($record);
}
}
Then I'd use it in the application like this.
class Durp {
private $logger;
public function __construct() {
$this->logger = new Logger('durp-service');
$this->logger->pushHandler(new DurpLogger());
$this->logger->addDebug('Debugging enabled');
$this->logger->addInfo('Starting Durp');
}
}
I figured the StreamHandler handles the file writing stuff, so that's why I'm extending it. And if I turn up the log level in Configuration to Logger::INFO, the "Debugging enabled" message doesn't get logged.
Open to suggestions to make this better.
A common alternative would be to use the NullHandler instead of the StreamHandler.
Maybe switch between them depending on your condition like follows:
if (!Configuration::debug()) {
$logger->pushHandler(new \Monolog\Handler\NullHandler());
}
I would like to give you an example that is more adapted to your usage,
but I need to see some code in order to know how you use it.
Update
For the question about default format, the empty [] at end represent the extra data that can be added with log entries.
From #Seldaek (Monolog's owner) :
The default format of the LineFormatter is:
"[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n". the username/age is the context, and extra that is typically empty results in this empty array [].
If you use processors to attach data to log records they typically write it to the extra key to avoid conflicts with context info. If it really is an issue for you you can change the default format and omit %extra%.
Edit: As of Monolog 1.11 the LineFormatter has a $ignoreEmptyContextAndExtra parameter in the constructor that lets you remove these, so you can use this:
// the last "true" here tells it to remove empty []'s
$formatter = new LineFormatter(null, null, false, true);
$handler->setFormatter($formatter);
See How not to show last bracket in a monolog log line? and Symfony2 : use Processors while logging in different files about the processors which #Seldaek is talking about.

Laravel 5 different log levels for development and production

I'm using Laravel 5.1 and trying to set different logging logic for a development and production environment.
Throughout my application I am using the Log facade with most of the following different methods:
Log::emergency($error);
Log::alert($error);
Log::critical($error);
Log::error($error);
Log::warning($error);
Log::notice($error);
Log::info($error);
Log::debug($error);
However, in my production environment, I would like to only log anything that is an Error, Critical, Alert or Emergency priority and ignore log requests with lower priority.
I couldn't find anything in the documentation or by exploring the code (both Log facade and the Monolog class).
My current thought is to create a custom wrapper around the Log facade that simply checks the environment and ignores anything below 400 (Monolog level for Error). Basically I would create a threshold variable in the environment file and anything below it will simply not be logged to the files.
Before I do so, I wanted to ask the community if there is an existing method/configuration for that which I could use, so that I don't re-invent the wheel.
If not - what would be the best approach?
This gist shows a more comfortable answer, as is not dependent on the
chosen handler.
I'm just providing the essential part in an answer here in case the above link gets deleted in some time.
In the AppServiceProviders' register method:
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
$monolog = Log::getMonolog();
foreach($monolog->getHandlers() as $handler) {
$handler->setLevel(Config::get('app.log-level'));
}
}
Then just add an additional key to your config/app.php:
'log-level' => 'info', // or whatever minimum log level you would like.
Add the following code to your AppServiceProvider::register():
$this->app->configureMonologUsing(function ($monolog) {
$monolog->pushHandler(
$handler = new RotatingFileHandler(
$this->app->storagePath() . '/logs/laravel.log',
$this->app->make('config')->get('app.log_max_files', 5),
$this->app->make('config')->get('app.level', 'debug')
)
);
$handler->setFormatter(new LineFormatter(null, null, true, true));
});
This recreates the logic that Laravel does when setting up the daily handler, but adds passing level to the handler.
You can set your minimum logging level by setting level value in your config/app.php:
'level' => 'debug', //debug, info, notice, warning, error, critical, alert, emergency
This is a bit of a workaround and each type of handler would need to be set up separately. I'm currently working on a pull-request to Laravel that would add setting minimum debug level from the config file without writing a line of code in your AppServiceProvider.
The code above hasn't been tested, so let me know if you see any typos or something doesn't work properly and I'll be more than happy to make that work for you.

phpunit check logs file

I have several integration tests with phpunit,
and in the proccess of the tests there are some logs written to files in the system.
I would like to check if a line was written during a test, is that possible?
example:
/** #test */
function action_that_writes_to_log() {
$this->call('GET', 'path/to/action', [], [], $requestXml);
//I want this:
$this->assertFileHas('the log line written', '/log/file/path.log');
}
The obvious way:
Implementing a custom assertion method, like the one you propose: assertFileHas. It's quite easy, just check if the string appears in the file. The problem you can get is that the line can already exist from another test or the same test already run. A possible solution for this is deleting the logs content before each test or test class, depending on your needs. You would need a method that deletes the logs and call it from setUp or setUpBeforeClass.
I would go with another approach: mocking the logging component, and checking that the right call is being done:
$logger_mock->expects($this->once())
->method('log')
->with($this->equalTo('the log line written'));
This makes easy to test that the components are logging the right messages, but you also need to implement a test that verifies that the logger is capable of actually writting to the file. But it's easier to implement that test once, and then just check that each component calls the logging method.

log4php will not work properly

So I recently discovered that the log4* package was available for PHP and upon seeing this eagerly downloaded the latest from the log4php website and followed the installation instructions. The instructions indicate that one is to download the tar package, untar, and place the following directory in a place of one's choosing: log4php/src/main/php
Therefore I copied the contents of log4php/src/main/php into my lib dir under lib/log4php.
In my script, as required, I required the 'Logger.php' class and indicated a properties file to manage my appenders. The properties file, log4php.properties, is located on my filesystem at "/home1/ioforgec/www/devlab/pnotes/config/log4php.properties".
Here is the logfile content:
#
# Example Logger
#
log4php.appender.EA1 = LoggerAppenderConsole
log4php.appender.EA1.target = STDOUT
log4php.appender.EA1.layout = LoggerLayoutPattern
log4php.appender.EA1.ConversionPattern = "%m"
log4php.appender.EA1.threshold = FATAL
log4php.exampleLogger = FATAL, EA1
So here is a copy of my script that implements (more or less 'wraps' the log4php functionality):
<?php
require_once('/home1/ioforgec/www/devlab/pnotes/lib/log4php/Logger.php');
class LogUtil
{
public $logger;
public $properties_file = "/home1/ioforgec/www/devlab/pnotes/config/log4php.properties";
public static function logExample($msg)
{
Logger::configure($properties_file);
$logger = Logger::getLogger("example");
$logger->debug("Shouldnt see this print because of config max level FATAL");
$logger->fatal($msg);
}
}
?>
In order to test that this is working properly, the following script calls the LogUtil.php shown above:
#!/usr/bin/php
<?php
require_once("lib/utils/LogUtil.php");
LogUtil::logExample("example message");
?>
So, despite configuring the example logger to format the messages as the plain message, despite setting the level of the logger to a max of FATAL, not only in the logger declaration but also in the threshold command, the print to the console looks to me like the default root logger:
Mon Oct 18 20:30:05 2010,705 [827] DEBUG example - Shouldnt see this print because of config max level FATAL
Mon Oct 18 20:30:05 2010,711 [827] FATAL example - example message
I see the print statement without the plain formatting as specified, and I see two print statements, the debug print (which should never have printed due to the setting of FATAL on the logger config - FATAL is MUCH higher than DEBUG)
What on earth am I doing wrong? Ive tried every combination of setup I can possibly think of. Does anyone have any suggestions?
Edit - Update - This is still an issue. No progress.
Edit2 - Really? Has no one on this forum tried to use this very popular logging package and its incarnation as a PHP module?
I have tried every conceivable avenue to try and get my question answered, including the log4php user's mailing list.
After many many more hours of manipulating the configuration file I have discovered a small, yet critically important factoid. In order to specify a 'logger' you must name it under the 'logger' hierarchy and its appenders under the 'appender' hierarchy, etc etc. For example:
# for the 'console' logger named 'example' we first define the appender:
log4php.appender.consoleAppender = LoggerAppenderConsole
log4php.appender.consoleAppender.target = STDOUT
log4php.appender.consoleAppender.layout = LoggerLayoutSimple
# we then assign the appender and the minimum log level to the logger:
log4php.logger.example = WARN, consoleAppender
My mistake was in seeing the root logger hierarchy name 'log4php.rootLogger' and assuming that in order to assign a logger one need only name a value for 'log4php.lognameLogger'. Sadly this is no excuse as there was an example, buried, in the documentation. I suppose finding it at 9+ hours is better than not at all :)
Thanks to those who reminded me that there was more debugging work to be done, I appreciated the reminder to keep trying

How to design error reporting in PHP

How should I write error reporting modules in PHP?
Say, I want to write a function in PHP: 'bool isDuplicateEmail($email)'.
In that function, I want to check if the $email is already present in the database.
It will return 'true', if exists. Else 'false'.
Now, the query execution can also fail, In that time I want to report 'Internal Error' to the user.
The function should not die with typical mysql error: die(mysql_error(). My web app has two interfaces: browser and email(You can perform certain actions by sending an email).
In both cases it should report error in good aesthetic.
Do I really have to use exception handling for this?
Can anyone point me to some good PHP project where I can learn how to design robust PHP web-app?
In my PHP projects, I have tried several different tacts. I've come to the following solution which seems to work well for me:
First, any major PHP application I write has some sort of central singleton that manages application-level data and behaviors. The "Application" object. I mention that here because I use this object to collect generated feedback from every other module. The rendering module can query the application object for the feedback it deems should be displayed to the user.
On a lower-level, every class is derived from some base class that contains error management methods. For example an "AddError(code,string,global)" and "GetErrors()" and "ClearErrors". The "AddError" method does two things: stores a local copy of that error in an instance-specific array for that object and (optionally) notifies the application object of this error ("global" is a boolean) which then stores that error for future use in rendering.
So now here's how it works in practice:
Note that 'Object' defines the following methods: AddError ClearErrors GetErrorCodes GetErrorsAsStrings GetErrorCount and maybe HasError for convenience
// $GLOBALS['app'] = new Application();
class MyObject extends Object
{
/**
* #return bool Returns false if failed
*/
public function DoThing()
{
$this->ClearErrors();
if ([something succeeded])
{
return true;
}
else
{
$this->AddError(ERR_OP_FAILED,"Thing could not be done");
return false;
}
}
}
$ob = new MyObject();
if ($ob->DoThing())
{
echo 'Success.';
}
else
{
// Right now, i may not really care *why* it didn't work (the user
// may want to know about the problem, though (see below).
$ob->TrySomethingElse();
}
// ...LATER ON IN THE RENDERING MODULE
echo implode('<br/>',$GLOBALS['app']->GetErrorsAsStrings());
The reason I like this is because:
I hate exceptions because I personally believe they make code more convoluted that it needs to be
Sometimes you just need to know that a function succeeded or failed and not exactly what went wrong
A lot of times you don't need a specific error code but you need a specific error string and you don't want to create an error code for every single possible error condition. Sometimes you really just want to use an "opfailed" code but go into some detail for the user's sake in the string itself. This allows for that flexibility
Having two error collection locations (the local level for use by the calling algorithm and global level for use by rendering modules for telling the user about them) has really worked for me to give each functional area exactly what it needs to get things done.
Using MVC, i always use some sort of default error/exception handler, where actions with exceptions (and no own error-/exceptionhandling) will be caught.
There you could decide to answer via email or browser-response, and it will always have the same look :)
I'd use a framework like Zend Framework that has a thorough exception handling mechanism built all through it.
Look into exception handling and error handling in the php manual. Also read the comments at the bottom, very useful.
There's aslo a method explained in those page how to convert PHP errors into exceptions, so you only deal with exceptions (for the most part).

Categories