Skip to content

Instantly share code, notes, and snippets.

@pingpoli
Created September 25, 2024 10:16
Show Gist options
  • Save pingpoli/fa7644397d507e4f38432d77320259ef to your computer and use it in GitHub Desktop.
Save pingpoli/fa7644397d507e4f38432d77320259ef to your computer and use it in GitHub Desktop.
<?php
$logfiles = [
"/home/user/logs/php.log"
];
$apacheLogFiles = [
"/var/log/apache2/error.log"
];
// after this many lines in the log file a warning will be included in the e-mail that the log file is getting long
$lengthWarning = 100;
const SMTP_HOST = "YOUR_SMTP_HOST";
const SMTP_USERNAME = "YOUR_SMTP_USERNAME";
const SMTP_PASSWORD = "YOUR_SMTP_PASSWORD";
// 25 hours because time changes might mess this up otherwise, reporting errors twice might be less bad than missing some on a single day of the year?
$last25Hours = time()-25*60*60;
$str = "PHP errors have occured in the last 25 hours:<br><br>".PHP_EOL;
$errorCounter = 0;
// normal log files
foreach ( $logfiles as $logfile )
{
$file = fopen( $logfile , "r" );
if ( $file )
{
$errors = [];
$linesCounter = 0;
while ( ( $line = fgets( $file ) ) !== false )
{
$time = substr( $line , 0 , strpos( $line , " >" ) );
$timestamp = strtotime( $time );
if ( $timestamp > $last25Hours )
{
$backtraceStr = "";
if ( ( $pos = strpos( $line , "backtrace: " ) ) != false )
{
// add the normal error
$error = substr( $line , 0 , $pos-3 );
$backtraceStr = $error."<br>".PHP_EOL;
// format backtrace
$backtrace = substr( $line , $pos+11 );
$json = json_decode( $backtrace );
for ( $i = 0 ; $i < count($json) ; ++$i )
{
$backtraceStr .= "&emsp;".$json[$i]->file.":".$json[$i]->line;
if ( $i != count($json)-1 ) $backtraceStr .= "<br>".PHP_EOL;
}
}
else
{
$backtraceStr = $line;
}
$errors[] = $backtraceStr;
$errorCounter++;
}
$linesCounter++;
}
if ( count( $errors ) > 0 )
{
$str .= "<b>".$logfile."</b>:<br>".PHP_EOL;
foreach ( $errors as $error )
{
$str .= $error."<br>".PHP_EOL;
}
if ( $linesCounter > $lengthWarning )
{
$str .= "<b>WARNING</b> > This logfile is getting long.<br>".PHP_EOL;
}
$str .= "<br>".PHP_EOL;
}
fclose( $file );
}
}
$str .= "Apache Error Logs:<br><br>".PHP_EOL;
// apache log files
foreach ( $apacheLogFiles as $apacheLogFile )
{
$file = fopen( $apacheLogFile , "r" );
if ( $file )
{
$errors = [];
$attemptedFilenames = [];
$linesCounter = 0;
while ( ( $line = fgets( $file ) ) !== false )
{
$time = substr( $line , 1 , strpos( $line , "]" )-1 );
$parts = explode( " " , $time );
// convert the time to something strtotime can understand
$time = $parts[1]." ".$parts[2]." ".$parts[4]." ".substr( $parts[3] , 0 , strpos( $parts[3] , "." ) );
$timestamp = strtotime( $time );
// not needed, the server rotates the log file daily anyway
// if ( $timestamp > $last25Hours )
{
// all these errors look something like this
// [Sat Sep 21 20:03:14.424470 2024] [php7:error] [pid 0:tid 0] [client IP] script '/var/www/wp.php' not found or unable to stat
// apparently there can be some slightly different "not found" lines without the single quotes, so exclude them here
if ( strpos( $line , "not found or unable to stat" ) !== false && strpos( $line , "'" ) !== false )
{
// find the string between the single quotes
$attemptedFilename = substr( $line , strpos( $line , "'" )+1 , strrpos( $line , "'" )-strpos( $line , "'" )-1 );
$attemptedFilename = str_replace( "/var/www/" , "" , $attemptedFilename );
$attemptedFilenames[] = $attemptedFilename;
$errorCounter++;
}
else
{
$errors[] = $line;
$errorCounter++;
}
}
$linesCounter++;
}
if ( count( $errors ) > 0 || count( $attemptedFilenames ) > 0 )
{
$str .= "<b>".$apacheLogFile."</b>:<br>".PHP_EOL;
foreach ( $errors as $error )
{
$str .= $error."<br>".PHP_EOL;
}
if ( count( $attemptedFilenames ) > 0 )
{
$str .= "<b>People tried to access the following files</b>:<br>".PHP_EOL;
foreach ( $attemptedFilenames as $attemptedFilename )
{
$str .= $attemptedFilename."<br>".PHP_EOL;
}
}
// if ( $linesCounter > $lengthWarning )
// {
// $str .= "<b>WARNING</b> > This logfile is getting long.<br>".PHP_EOL;
// }
$str .= "<br>".PHP_EOL;
}
fclose( $file );
}
}
if ( $errorCounter > 0 )
{
require( "phplibs/Exception.php" );
require( "phplibs/PHPMailer.php" );
require( "phplibs/SMTP.php" );
sendEmail( $str );
}
function sendEmail( $str )
{
$mail = new PHPMailer\PHPMailer\PHPMailer( true );
try
{
// disable debug output
$mail->SMTPDebug = 0;
$mail->isSMTP();
$mail->Host = SMTP_HOST;
$mail->SMTPAuth = true;
$mail->Username = SMTP_USERNAME;
$mail->Password = SMTP_PASSWORD;
$mail->SMTPSecure = "tls";
$mail->Port = 587;
// recipients
$mail->setFrom( "[email protected]" , "PHP Logger" );
$mail->addAddress( "[email protected]" , "User" );
// content
$mail->isHTML( true );
$mail->Subject = "PHP Logger";
$mail->Body = $str;
$mail->AltBody = $str;
// send
$mail->send();
return true;
}
catch ( PHPMailer\PHPMailer\Exception $e )
{
error_log( "PHPMailer > failed to send email, error: ".$mail->ErrorInfo );
return false;
}
}
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment