I want to log all error's that are generated whit error_reporting(E_ALL); in a logfile.
Thus the error's you normaly see when there is an error in the PHP script.
I tried many thing but the closest that i come was whit
asdasc(); // generate error
$i=error_get_last();
if($i['message']!==''){
error_log(date('Y-m-d-h:m:s') . " | type = " .$i ['type']." message = " .$i ['message']." file = " .$i ['file']." line = " .$i ['line'] , 3, "my-errors.log");}
This way i see that there was an error at a date,but what i want to see is what error i should see if it was on mine screen. something like :
SQLSTATE[HY000] [1045] Access denied for user 'root'#'localhost' (using password: NO) OR
Fatal error: Call to undefined function blabla() in C:\xampp\htdocs...\index.php on line 125
Is there a way to do this?
thnx in advanced.
Use error_get_last():
error_log(date('Y-m-d-h:m:s') . " | error = " . print_r(error_get_last(), true), 3, "my-errors.log");
You can as well set up your own error handling class in your application:
class MyErrorHandlerClass
{
public function MyErrorHandlerMethod($errno, $errmsg, $filename, $linenum, $vars)
{
// log you error
}
}
Then you can define your own error handler:
$ErrorHandler = new MyErrorHandlerClass();
set_error_handler(array($ErrorHandler, 'MyErrorHandlerMethod'));
error_reporting() btw. does not generate errors, it only controls the level of displaying errors. When an error occurs, its stack trace is always generated.
Related
I have Sentry keeping track of uncaught exceptions in my PHP application, and I noticed a peculiar uncaught exception from PDO. The code looks like this:
/**
* #return boolean TRUE if the connection to the database worked; FALSE otherwise.
*/
public function verifyDatabase() {
try{
$this->pdo->query('SELECT 1');
return true;
}
catch (\PDOException $e) {
echo 'Lost connection to database: ' . $e->getMessage() . PHP_EOL;
return false;
}
}
This should catch errors like "MySQL server has gone away", and it indeed works on my development machine. However, Sentry recently recorded this error:
ErrorException
PDO::query(): MySQL server has gone away
According to sentry, this was thrown by the $this->pdo->query('SELECT 1'); statement above. Errors like this should have been caught by the try/catch. Why is PDO throwing an ErrorException rather than a PDOException?
I can't reproduce an ErrorException.
$pdo = new PDO('mysql:host=127.0.0.1;dbname=test', ..., ...);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
sleep(20); // during this sleep, I stop my MySQL Server instance.
$result = $pdo->query("SELECT 1");
Output:
Warning: PDO::query(): MySQL server has gone away
Warning: PDO::query(): Error reading result set's header
Fatal error: Uncaught PDOException: SQLSTATE[HY000]: General error: 2006 MySQL server has gone away
Stack trace:
#0 /Users/bkarwin/Documents/SO/pdo.php(8): PDO->query('SELECT 1')
This shows it throws a PDOException when the server has gone away, not an ErrorException.
Tested with MySQL 5.6.37 and PHP 7.1.23.
I wonder if the code you show in your question is actually the code that is deployed and throwing the exception to Sentry. Perhaps you have some code like:
catch (\PDOException $e) {
throw ErrorException($e->getMessage());
}
Either in your verifyDatabase() function, or else in the code that calls verifyDatabase(). In other words, what does your app do when verifyDatabase() returns false?
Okay, I think I've figured it out. It appears that this is related to a bug in which the PDO MySQL driver emits warnings even when they are supposed to be disabled (See also: this answer). I believe Sentry is then catching these as errors.
I was finally able to replicate, and solve this, by modifying Bill Karwin's test script:
// Initialize the Sentry reporting client
$ravenClient = new \Raven_Client(SENTRY_KEY);
$ravenClient->install();
echo 'Connecting...' . PHP_EOL;
$pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8mb4", $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
echo 'Connected. Waiting...' . PHP_EOL;
sleep(20); // during this sleep, I stop my MySQL Server instance.
echo 'Querying...' . PHP_EOL;
try {
$result = $pdo->query("SELECT 1");
}
catch(\PDOException $e) {
echo 'Caught PDOException ' . $e->getMessage() . PHP_EOL;
}
This will print the following:
Connecting...
Connected. Waiting...
Querying...
PHP Warning: PDO::query(): MySQL server has gone away in /home/xxx/src/test.php on line 37
PHP Stack trace:
PHP 1. {main}() /home/xxx/src/test.php:0
PHP 2. PDO->query() /home/xxx/src/test.php:37
Caught PDOException SQLSTATE[HY000]: General error: 2006 MySQL server has gone away
Done.
And Sentry will record an ErrorException on the query() line.
I was able to solve this issue by implementing the "solution" posted by jferrer on the PHP bug report.
// Convert NOTICE, WARNING, ... in Exceptions
$convertErrorToException = function ($level, $message, $file, $line){
throw new ErrorException($message, 0, $level, $file, $line);
};
// The $previousErrorHandler will contain Sentry's handler
$previousErrorHandler = set_error_handler($convertErrorToException);
try {
$result = $pdo->query("SELECT 1");
}
catch(\PDOException $e) {
echo 'Caught PDOException ' . $e->getMessage() . PHP_EOL;
}
catch(\ErrorException $e) {
echo 'Caught ErrorException ' . $e->getMessage() . PHP_EOL;
}
// Restore Sentry as the default handler
set_error_handler($previousErrorHandler);
This results in just the ErrorException being thrown and caught:
Connecting...
Connected. Waiting...
Querying...
Caught ErrorException PDO::query(): MySQL server has gone away
Done.
When PHP emits a PHP Notice to the log, it contains the file name and the line number where the problem occurred. In the case of a large application, this often isn't enough to reproduce the problem. What would really help is some additional information, most obviously the URL that was being called at the time this notice occurred.
Is there a way to customize the PHP Notice message in PHP >= 7?
Create your own error handler and catch the notices then log the message with whatever information you need. The code below produces the following the PHP error log file.
[27-Feb-2019 13:55:09 America/New_York] 8 Undefined variable: hello
from URI /customnotice.php
function myErrorHandler($errno,$errstr, $errfile, $errline) {
if ($errno == 8) { // this is a notice
error_log($errno . ' ' . $errstr . ' from URI ' . $_SERVER['REQUEST_URI']);
}
}
$old_error_handler = set_error_handler("myErrorHandler");
echo $hello; // will throw a notice for testing
I am trying to ultimately run and display reports from a remote Jasper Server in a PHP application. What I am trying to do this with is the jrs-rest-php-client project on github.
The error:
Fatal error: Uncaught exception 'Jaspersoft\Exception\RESTRequestException' with message 'An unexpected HTTP status code was returned by the server' in C:\xampp\htdocs\jrs\vendor\src\Jaspersoft\Tool\RESTRequest.php:409
Stack trace:
#0 C:\xampp\htdocs\jrs\vendor\src\Jaspersoft\Tool\RESTRequest.php(479): Jaspersoft\Tool\RESTRequest->handleError(0, Array, false)
#1 C:\xampp\htdocs\jrs\vendor\src\Jaspersoft\Service\ReportService.php(40): Jaspersoft\Tool\RESTRequest->prepAndSend('https://jasper....', Array, 'GET', NULL, true)
#2 C:\xampp\htdocs\jrs\report.php(30): Jaspersoft\Service\ReportService->runReport('/Reports/Distri...', 'html')
#3 {main} thrown in C:\xampp\htdocs\jrs\vendor\src\Jaspersoft\Tool\RESTRequest.php on line 409
My PHP:
require_once __DIR__ . "/vendor/autoload.php";
use Jaspersoft\Client\Client;
$d = new Client(
"http://jasper.server.com/jasperserver-pro",
"username",
"password",
"organization"
);
$info = $d->serverInfo();
Any ideas?
Looking into the code of RESTRequest.php there are two cases in which this exception is thrown:
A JSON result set with an unknown error code is returned (unknown meaning different from 200 OK)
No JSON result set is returned at all
So I suspect the connection isn't working properly. To find out more, you should catch the exception in your code and evaluate it:
It could look something like this (I'm more familiar with Java):
try {
$d = new Client(
"http://jasper.server.com/jasperserver-pro",
"username",
"password",
"organization"
);
$info = $d->serverInfo();
} catch (RESTRequestException $e) {
echo 'RESTRequestException:';
echo 'Exception message: ', $e->getMessage(), "\n";
echo 'Set parameters: ', $e->parameters, "\n";
echo 'Expected status code:', $e->expectedStatusCodes, "\n";
echo 'Error code: ', $e->errorCode, "\n";
}
If there is still an error, you can check the following:
Can you reach the jasper server from the server where this code is deployed? Sometimes e.g. firewall settings can interfere.
Is the organization called exactly like defined in the properties of the organization in the server? (Open repository / right click on organization / properties / resource-id)
The code is:
if (!geoip_db_avail(GEOIP_COUNTRY_EDITION))
{
//do this
}
and it is throwing a:
Caught fatal error: Call to undefined function geoip_db_avail()
in one of my applications.
On a test on another site on the same server using:
$country_name = apache_note("GEOIP_COUNTRY_NAME");
print "What: " . $country_name;
if (geoip_db_avail(GEOIP_COUNTRY_EDITION))
{
print "NO!";
}
else
{
print "YE";
}
It does not show the error but all the page prints is:
What: Pakistan
It does not print Ye or NO!
Manual is your friend. This function is declared in PECL geoip extension which is installed on one of your servers but not on another.
You can read here about installation and requirements.
I am using zend framework 1.12 for my project. I want to catch all types of fatal errors and send them to an email address for quick fix. I have written the below mentioned code in Bootstrap.php file for this purpose.
protected function _initFatalErrorCatcher()
{
register_shutdown_function(array($this, 'errorlogHandler'));
}
public function errorlogHandler()
{
$e = error_get_last();
if (!is_null($e)) { //fatal error
$msg = 'Fatal error: ' . $e['message'];
$msg .= ' in' . $e['file'];
$msg .= ' on line: ' . $e['line'];
$mail = new Zend_Mail('utf-8');
$mail->setBodyHtml($msg);
$mail->setFrom('zzz#z.com');
$mail->addTo('yyy#y.com');
$mail->setSubject('check this error');
$mail->send();
}
}
Using the above code, i am able to send fatal errors other than database connection related errors and query related errors to email. I followed the instructions from Catch Zend PDO Exception as well, but i believe i am missing something as its not working.
Any help on this will be appreciated.
EDIT:
I am also using Zend_Log to write the error logs in a log-file. But, using this i could not find a way to write the fatal errors. Code for this is given below.
$writer = new Zend_Log_Writer_Stream(APPLICATION_PATH . "/../data/log-file.log");
$errors = $this->_getParam('error_handler');
$exception = $errors->exception;
$log = new Zend_Log($writer);
$log->debug($exception->getMessage() . "\n" . $exception->getTraceAsString());
Scenario for database connection related issue:
If there is any error in host name, database name or in user name, it shows a Fatal error in browser like below. But its not detected by register_shutdown_function() or Zend_Log().
Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[42000] [1044] Access denied for user 'AAAA'#'%' to database 'BBBB'' in /var/www/project_name/library/Zend/Db/Adapter/Pdo/Abstract.php on line 144 PDOException: SQLSTATE[42000] [1044] Access denied for user 'AAAA'#'%' to database 'BBBB' in /var/www/project_name/library/Zend/Db/Adapter/Pdo/Abstract.php on line 129
The post here shows an example. Basically use set_error_handler to tickle php into throwing exceptions when an error is encountered. This example from link:
<?php
function exception_error_handler($errno, $errstr, $errfile, $errline ) {
throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
}
set_error_handler("exception_error_handler");
/* Trigger exception */
strpos();
?>
Hope this helps
//$array contains the values for insert
try {
$this->db->insert('Users', $array );
} catch (Exception $e){
echo $e->getMessage();
}
I have solved it by writing the below mentioned code in Bootstrap.php file.
protected function _initDbConfig()
{
$config = new Zend_Config($this->getOptions());
$params = $config->database->toArray();
try {
$db = Zend_Db::factory('Pdo_Mysql', $params);
$db->getConnection();
} catch (Zend_Db_Adapter_Exception $e) {
// perhaps the RDBMS is not running
// code to send email goes here
} catch (Zend_Exception $e) {
// perhaps factory() failed to load the specified Adapter class
// code to send email goes here
}
}
In application.ini, i have the following code.
database.host = "localhost"
database.username = "AAAA"
database.password = "*****"
database.dbname = "BBBBB"