diff --git a/lib/DbConnection.php b/lib/DbConnection.php index 06a20f3d..cb9a310f 100644 --- a/lib/DbConnection.php +++ b/lib/DbConnection.php @@ -58,7 +58,7 @@ class DbConnection{ var_dump($ex); } else{ - Logger::WriteErrorLogEntry('Error connecting to ' . $connectionString . '. Exception: ' . vds($ex)); + Log::WriteErrorLogEntry('Error connecting to ' . $connectionString . '. Exception: ' . vds($ex)); } if($require){ @@ -139,9 +139,9 @@ class DbConnection{ throw($ex); } else{ - Logger::WriteErrorLogEntry($ex->getMessage()); - Logger::WriteErrorLogEntry($preparedSql); - Logger::WriteErrorLogEntry(vds($params)); + Log::WriteErrorLogEntry($ex->getMessage()); + Log::WriteErrorLogEntry($preparedSql); + Log::WriteErrorLogEntry(vds($params)); throw($ex); } } diff --git a/lib/Email.php b/lib/Email.php index fee8af40..9ee5224d 100644 --- a/lib/Email.php +++ b/lib/Email.php @@ -63,10 +63,10 @@ class Email{ } if(SITE_STATUS == SITE_STATUS_DEV){ - Logger::WriteErrorLogEntry('Sending mail to ' . $this->To . ' from ' . $this->From); - Logger::WriteErrorLogEntry('Subject: ' . $this->Subject); - Logger::WriteErrorLogEntry($this->Body); - Logger::WriteErrorLogEntry($this->TextBody); + Log::WriteErrorLogEntry('Sending mail to ' . $this->To . ' from ' . $this->From); + Log::WriteErrorLogEntry('Subject: ' . $this->Subject); + Log::WriteErrorLogEntry($this->Body); + Log::WriteErrorLogEntry($this->TextBody); } else{ $phpMailer->Send(); @@ -74,7 +74,7 @@ class Email{ } catch(Exception $ex){ if(SITE_STATUS != SITE_STATUS_DEV){ - Logger::WriteErrorLogEntry('Failed sending email to ' . $this->To . ' Exception: ' . $ex->errorMessage() . "\n" . ' Subject: ' . $this->Subject . "\nBody:\n" . $this->Body); + Log::WriteErrorLogEntry('Failed sending email to ' . $this->To . ' Exception: ' . $ex->errorMessage() . "\n" . ' Subject: ' . $this->Subject . "\nBody:\n" . $this->Body); } return false; diff --git a/lib/Log.php b/lib/Log.php new file mode 100644 index 00000000..37a4ea03 --- /dev/null +++ b/lib/Log.php @@ -0,0 +1,39 @@ +RequestId = substr(sha1(time() . rand()), 0, 8); + $this->LogFilePath = $logFilePath; + } + + public function Write(string $text): void{ + if($this->LogFilePath === null){ + self::WriteErrorLogEntry($text); + } + else{ + try{ + $fp = fopen($this->LogFilePath, 'a+'); + } + catch(\Exception $ex){ + self::WriteErrorLogEntry('Couldn\'t open log file: ' . $this->LogFilePath . '. Exception: ' . vds($ex)); + return; + } + + fwrite($fp, gmdate('Y-m-d H:i:s') . "\t" . $this->RequestId . "\t" . $text . "\n"); + fclose($fp); + } + } + + public static function WriteErrorLogEntry(string $text): void{ + error_log($text); + } +} diff --git a/lib/Logger.php b/lib/Logger.php deleted file mode 100644 index 26790539..00000000 --- a/lib/Logger.php +++ /dev/null @@ -1,33 +0,0 @@ -Write('Received GitHub webhook.'); if($_SERVER['REQUEST_METHOD'] != 'POST'){ throw new Exceptions\WebhookException('Expected HTTP POST.'); @@ -44,10 +43,10 @@ try{ switch($_SERVER['HTTP_X_GITHUB_EVENT']){ case 'ping': // Silence on success. - Logger::WriteGithubWebhookLogEntry($requestId, 'Event type: ping.'); + $log->Write('Event type: ping.'); throw new Exceptions\NoopException(); case 'push': - Logger::WriteGithubWebhookLogEntry($requestId, 'Event type: push.'); + $log->Write('Event type: push.'); // Get the ebook ID. PHP doesn't throw exceptions on invalid array indexes, so check that first. if(!array_key_exists('repository', $data) || !array_key_exists('name', $data['repository'])){ @@ -57,7 +56,7 @@ try{ $repoName = trim($data['repository']['name'], '/'); if(in_array($repoName, GITHUB_IGNORED_REPOS)){ - Logger::WriteGithubWebhookLogEntry($requestId, 'Repo is in ignore list, no action taken.'); + $log->Write('Repo is in ignore list, no action taken.'); throw new Exceptions\NoopException(); } @@ -77,19 +76,19 @@ try{ } } - Logger::WriteGithubWebhookLogEntry($requestId, 'Processing ebook `' . $repoName . '` located at `' . $dir . '`.'); + $log->Write('Processing ebook `' . $repoName . '` located at `' . $dir . '`.'); // Check the local repo's last commit. If it matches this push, then don't do anything; we're already up to date. $lastCommitSha1 = trim(shell_exec('git -C ' . escapeshellarg($dir) . ' rev-parse HEAD 2>&1') ?? ''); if($lastCommitSha1 == ''){ - Logger::WriteGithubWebhookLogEntry($requestId, 'Error getting last local commit. Output: ' . $lastCommitSha1); + $log->Write('Error getting last local commit. Output: ' . $lastCommitSha1); throw new Exceptions\WebhookException('Couldn\'t process ebook.', $post); } else{ if($data['after'] == $lastCommitSha1){ // This commit is already in our local repo, so silent success - Logger::WriteGithubWebhookLogEntry($requestId, 'Local repo already in sync, no action taken.'); + $log->Write('Local repo already in sync, no action taken.'); throw new Exceptions\NoopException(); } } @@ -98,7 +97,7 @@ try{ $output = []; exec('sudo --set-home --user se-vcs-bot git -C ' . escapeshellarg($dir) . ' rev-parse HEAD', $output, $returnCode); if($returnCode != 0){ - Logger::WriteGithubWebhookLogEntry($requestId, 'Couldn\'t get last commit of local repo. Output: ' . implode("\n", $output)); + $log->Write('Couldn\'t get last commit of local repo. Output: ' . implode("\n", $output)); } else{ $lastPushHashFlag = ' --last-push-hash ' . escapeshellarg($output[0]); @@ -108,22 +107,22 @@ try{ $output = []; exec('sudo --set-home --user se-vcs-bot ' . SITE_ROOT . '/scripts/pull-from-github ' . escapeshellarg($dir) . ' 2>&1', $output, $returnCode); if($returnCode != 0){ - Logger::WriteGithubWebhookLogEntry($requestId, 'Error pulling from GitHub. Output: ' . implode("\n", $output)); + $log->Write('Error pulling from GitHub. Output: ' . implode("\n", $output)); throw new Exceptions\WebhookException('Couldn\'t process ebook.', $post); } else{ - Logger::WriteGithubWebhookLogEntry($requestId, '`git pull` from GitHub complete.'); + $log->Write('`git pull` from GitHub complete.'); } // Our local repo is now updated. Build the ebook! $output = []; exec('sudo --set-home --user se-vcs-bot tsp ' . SITE_ROOT . '/web/scripts/deploy-ebook-to-www' . $lastPushHashFlag . ' ' . escapeshellarg($dir) . ' 2>&1', $output, $returnCode); if($returnCode != 0){ - Logger::WriteGithubWebhookLogEntry($requestId, 'Error queueing ebook for deployment to web. Output: ' . implode("\n", $output)); + $log->Write('Error queueing ebook for deployment to web. Output: ' . implode("\n", $output)); throw new Exceptions\WebhookException('Couldn\'t process ebook.', $post); } else{ - Logger::WriteGithubWebhookLogEntry($requestId, 'Queue for deployment to web complete.'); + $log->Write('Queue for deployment to web complete.'); } break; @@ -141,8 +140,8 @@ catch(Exceptions\InvalidCredentialsException $ex){ catch(Exceptions\WebhookException $ex){ // Uh oh, something went wrong! // Log detailed error and debugging information locally. - Logger::WriteGithubWebhookLogEntry($requestId, 'Webhook failed! Error: ' . $ex->getMessage()); - Logger::WriteGithubWebhookLogEntry($requestId, 'Webhook POST data: ' . $ex->PostData); + $log->Write('Webhook failed! Error: ' . $ex->getMessage()); + $log->Write('Webhook POST data: ' . $ex->PostData); // Print less details to the client. print($ex->getMessage()); diff --git a/www/webhooks/postmark.php b/www/webhooks/postmark.php index a67123aa..6b1e67db 100644 --- a/www/webhooks/postmark.php +++ b/www/webhooks/postmark.php @@ -8,13 +8,12 @@ use function Safe\file_get_contents; use function Safe\json_decode; use function Safe\substr; -// Get a semi-random ID to identify this request within the log. -$requestId = substr(sha1(time() . rand()), 0, 8); +$log = new Log(POSTMARK_WEBHOOK_LOG_FILE_PATH); try{ $smtpUsername = trim(file_get_contents(POSTMARK_SECRET_FILE_PATH)) ?: ''; - Logger::WritePostmarkWebhookLogEntry($requestId, 'Received Postmark webhook.'); + $log->Write('Received Postmark webhook.'); if($_SERVER['REQUEST_METHOD'] != 'POST'){ throw new Exceptions\WebhookException('Expected HTTP POST.'); @@ -35,13 +34,13 @@ try{ if($post->RecordType == 'SpamComplaint'){ // Received when a user marks an email as spam - Logger::WritePostmarkWebhookLogEntry($requestId, 'Event type: spam complaint.'); + $log->Write('Event type: spam complaint.'); Db::Query('delete from NewsletterSubscribers where Email = ?', [$post->Email]); } elseif($post->RecordType == 'SubscriptionChange' && $post->SuppressSending){ // Received when a user clicks Postmark's "Unsubscribe" link in a newsletter email - Logger::WritePostmarkWebhookLogEntry($requestId, 'Event type: unsubscribe.'); + $log->Write('Event type: unsubscribe.'); $email = $post->Recipient; @@ -59,27 +58,27 @@ try{ curl_close($handle); } elseif($post->RecordType == 'SubscriptionChange' && $post->SuppressionReason === null){ - Logger::WritePostmarkWebhookLogEntry($requestId, 'Event type: suppression deletion.'); + $log->Write('Event type: suppression deletion.'); } else{ - Logger::WritePostmarkWebhookLogEntry($requestId, 'Unrecognized event: ' . $post->RecordType); + $log->Write('Unrecognized event: ' . $post->RecordType); } - Logger::WritePostmarkWebhookLogEntry($requestId, 'Event processed.'); + $log->Write('Event processed.'); // "Success, no content" http_response_code(204); } catch(Exceptions\InvalidCredentialsException $ex){ // "Forbidden" - Logger::WritePostmarkWebhookLogEntry($requestId, 'Invalid key: ' . ($_SERVER['HTTP_X_SE_KEY'] ?? '')); + $log->Write('Invalid key: ' . ($_SERVER['HTTP_X_SE_KEY'] ?? '')); http_response_code(403); } catch(Exceptions\WebhookException $ex){ // Uh oh, something went wrong! // Log detailed error and debugging information locally. - Logger::WritePostmarkWebhookLogEntry($requestId, 'Webhook failed! Error: ' . $ex->getMessage()); - Logger::WritePostmarkWebhookLogEntry($requestId, 'Webhook POST data: ' . $ex->PostData); + $log->Write('Webhook failed! Error: ' . $ex->getMessage()); + $log->Write('Webhook POST data: ' . $ex->PostData); // Print less details to the client. print($ex->getMessage());