log4php + PHP 7 - catching and logging Throwable - php

I am having trouble logging a "TypeError" with log4php. I suspect this is because I recently upgraded from php 5.5 to 7.1.
Usually, my syntax looks like this:
<?
use Logger;
class MyClass
{
/** #var Logger */
private $logger;
function __construct(array $configParams)
{
Logger::configure('logger.xml');
$this->logger = Logger::getLogger(__CLASS__);
}
public function dostuff()
{
try
{
// ...
}
catch (Exception $ex)
{
$this->logger->error("ERROR CAUGHT", $ex);
}
}
}
?>
The above syntax will print lots of info to the log file, including a stack trace. However after reading the latest php7 docs, I believe I'm meant to handle the \Throwable interface, in order to catch both errors and exceptions (which is great). So I replace the above catch with the following:
catch (\Throwable $ex)
This still prints stack-trace information for my exceptions, but when a "TypeError" is caught, nothing gets printed to the log file.
I assume that this is due to log4php not knowing how to log errors. How can I log errors using log4php in a universal way?
Thanks,

I think you should use $ex->getMessage() for error() method your code should be
catch (Exception $ex)
{
$this->logger->error("ERROR CAUGHT", $ex->getMessage());
}
For logging trace you should use trace
catch (Exception $ex)
{
$this->logger->trace("ERROR CAUGHT", $ex);
}

Try to fix this
$this->logger->error("ERROR CAUGHT", $ex);
with
$this->logger->error("ERROR CAUGHT : " . $ex->getMessage());
log4php's documentation says that error method has just one parameter.

It took me a while to figure out why the original file name and line number was not logged by log4php when an exception is thrown. It turned out that my custom exception_handler class was only logging the exception message (by doing $exception->getMessage()), which does not contain the file name nor the line number. All I to do is concatenate that info: $exception->getFile() and $exception->getLine():
public function exception_handler ($exception) {
$logger = Logger9::create();
$logger->info($exception->getMessage()." ".$exception->getFile()." ".$exception->getLine());
}
Don't forget to register the custom handler:
#set_exception_handler(array($this, 'exception_handler'));

Related

Laravel exception not catching

I'm trying to do a very basic exception try catch, but it doesn't catch.
$id =0;
try {
$question = $this->model->find($id); // will not find anything since $id = 0
$question->delete(); // throw an exception
return true;
} catch (\Exception $e) {
dd ('hello'); // should end up here, but no?!?!?
} catch (FatalThrowableError $f) {
echo ("fatal"); // or here... but no.
}
but the catch doesn't "catch". I get an Fatal error in the browser saying that delete was called on a null object. But that's exactly what I was trying to do: do a delete on a null object (id = 0 is not in the DB), to test the exception.
I have tried
use Symfony\Component\Debug\Exception;
use Symfony\Component\Debug\Exception\FatalThrowableError;
or simply
Exception;
FatalThrowableError;
Also, having the \Exception $e or Exception $e (with or without ) doesn't change anything.
Note that if I add a line like $foo = 4/0 I get into the Exception section (dd (hello)).
in .env APP_DEBUG=true, APP_LOG_LEVEL=debug
I'm on Laravel 5.5 using PHP 7.0.10 on windows 7.
http://php.net/manual/en/language.errors.php7.php
As the Error hierarchy does not inherit from Exception, code that uses
catch (Exception $e) { ... } blocks to handle uncaught exceptions in
PHP 5 will find that these Errors are not caught by these blocks.
Either a catch (Error $e) { ... } block or a set_exception_handler()
handler is required.
You can, additionally, catch (\Throwable $e) {} to account for both Error and Exception types.

Can't catch exceptions in laravel

I have the following situation:
try {
DB::beginTransaction();
$task = new Task();
$task->setTracker("");
//thrown \Symfony\Component\Debug\Exception\FatalThrowableError
DB::commit();
}catch (\Exception $e){
DB::rollBack();
Log::error($e);
//throw $e;
}
I am not entering to the catch area.
Any idea why?
update
This is the error thrown:
[Symfony\Component\Debug\Exception\FatalThrowableError]
Type error: Argument 1 passed to App\Models\Task::setTracker() must be an instance of Carbon\Carbon, integer given, called in /var/www/app/Services/ShareLogic.php on line 60
and will not be catched
Thanks
Catching Throwable did the trick.
Have no idea why?
Anyone does?
It does not catch the exception because you are trying to catch \Exception which Symfony\Component\Debug\Exception\FatalThrowableError does not extend.
Instead try to catch the actual exception by importing it..
use Symfony\Component\Debug\Exception\FatalThrowableError;
And then you can do..
try {
//
} catch(FatalThrowableError e) {
//
}
Edit
Ok, so in addition to the above solution it seems PHP 7+ handles error a bit differently than PHP 5. So try this..
try {
//
} catch(Error $e) {
// This should work
} catch(Throwable $e) {
// This should work as well
}
Symfony's Debug component is much more sophisticated in order to log and report all kinds of errors but take look at this simple example (php 7.1.x):
<?php
class MyUncatchableError extends Exception {}
function myExceptionHandler($e) {
throw new MyUncatchableError('BANG: '.$e->getMessage());
}
set_exception_handler('myExceptionHandler');
$foo = true;
try {
$foo->modify();
} catch (Exception $e) {
echo 'nope';
} catch (MyUncatchableError $e) {
echo 'nope2';
}
What will be the outcome? Well:
Fatal error: Uncaught MyUncatchableError: BANG: Call to a member function modify() on boolean in /in/WJErU:6
Stack trace:
0 [internal function]: myExceptionHandler(Object(Error))
1 {main}
thrown in /in/WJErU on line 6
and you can't catch that exception because you should catch the original.. throwable here, which is Error for this kind of "error". You can catch it by catching "Error" class. And with PHP7 hierarchy it implements Throwable interface, that's why you can't catch it using Exception (because while Exception implements Throwable, Error is no an Exception - see: http://php.net/manual/en/language.errors.php7.php).
And this is true for PHP7+ because with 5.* there was no Throwable nor Error, and doing $foo->modify(); would just stop the script and return a Fatal Error. You can make your own error handler (set_error_handler) and throw an exception there (and Debug component does that for php 5.*) but this method does not work for Fatal Errors. Instead Debug component hooks into script shutdown and reads last error and throws FatalErrorException.
This description may not be completely accurate as I have't dug deeply into Symfony but you can get the idea here.

Testing PHP parse errors with phpunit

I need to test how our error logger works in various scenarios. One such scenario are parse errors. Here's an example:
public function testParseErrorLogsAnError()
{
$this->assertCount(0, $this->log_handler->getRecords());
try {
eval('<?php not good');
$this->fail('Code above should throw a parse error');
} catch (\Exception $e) {
$this->assertInstanceOf(\ParseError::class, $e);
}
$this->assertCount(1, $this->log_handler->getRecords());
}
Problem is that phpunit always exists with an exception, and never enters catch block. How to disable or orverride phpunit's exception handler, so we can test our own?
For this answer, I'm assuming you're using PHP 7. In PHP 5, Parse Errors cannot be caught and will always terminate your PHP process.
In PHP 7, you can catch Parse Errors using a try/catch statement (contrary to what the other answer says). However, PHP 7's ParseError class extends the Error class, not Exception (see also the documentation). So catch (\Exception $e) will not work, but any of these should:
catch (\ParseError $e) { ...
catch (\Error $e) { ...
catch (\Throwable $e) { ...
Alternatively, use the #expectedException annotation als already suggested by #DevDonkey:
/**
* #expectedException ParseError
*/
public function testParseErrorLogsAnError()
{
eval('<?php not good');
}

PHP Custom Error Handler: How to determine if exception was generated within try{}catch{} block?

I have a custom error handlers:
set_error_handler('API_Error_Handler');
register_shutdown_function('Fatal_Error_Handler'); // This one calls API_Error_Handler eventually
In the following example, both catch{} section AND API_Error_Handler are executed.
try{
// Exception raised here
} catch(Exception $e){
// No error reporting needed, do something else
}
I want ONLY catch{} to execute. How do I do that? Maybe determine within API_Error_Handler whether exception is already caught by try-catch? Or are there other approaches available?
Example code:
set_error_handler(function() {
echo "Error is handled by custom error handler. <br>";
});
try{
new SoapClient('http://bad.address/wsdl');
} catch(Exception $e){
echo "Error is caught. <br>";
}
I think the best way will be to create an Exception class by your self that extends Exception:
class MyCustomException extends \Exception {}
and throw this where you need it. Then change
} catch (Exception $e)
to
} catch (MyCustomException $e)
and you should only get your custom exception catched

Php selective exception handling

I have a problem where I want to catch all exception except descendants of my custom exception.
Maybe bad design, but here it is (Simplified and names changed, but the code is quite accurate):
function doStuff()
{
try {
// code
if (something) {
// manually throw an exception
throw StuffError("Something is bad.");
}
// a third-party code, can throw exceptions
LibraryClass::arcaneMagic();
} catch (Exception $e) {
throw new StuffError("Error occured while doing stuff: "
. $e->getMessage());
}
}
/** My custom exception */
class StuffError extends Exception
{
function __construct($msg) {
parent::__construct('StuffError: ' . $msg);
}
}
However, the issue here is that I don't want the try-catch to intercept the manually throws StuffError. Or, seamlessly rethrow it or something.
As it is now, I'd get:
StuffError: Error occured while doing stuff: StuffError: Something is bad.
I want just:
StuffError: Something is bad.
How would I do it?
You can have multiple catch clauses, and the first one that matches will be the one that runs. So you could have something like this:
try {
do_some_stuff();
}
catch (StuffError $e) {
throw $e;
}
catch (Exception $e) {
throw new StuffError(Error occurred while doing stuff: " . $e->getMessage());
}
But you might want to rethink wrapping stuff like this. It obscures the real cause of the error. For one thing, you lose the stack trace. But it also complicates error handling, since now someone can't differentiate exception types the way you're trying to do, short of trying to parse the exception message (which is rather an anti-pattern in itself).
I might be misinterpreting you, but I think this is what you're looking for:
...
} catch (Exception $e) {
if (get_class($e) == 'StuffError' || is_subclass_of($e, 'StuffError')) {
throw $e;
} else {
throw new StuffError("Error occured while doing stuff: "
. $e->getMessage());
}
}
...
Replace your catch statement with the code above. It checks to see if the exception is a StuffError or a child class of StuffError. I'm still very confused at why you would need to throw a StuffError exception after you catch, but maybe that's just some weirdness coming from translating/cleaning your code.

Categories