Create separate log for database events

Breaking: The option `[logger.file]` has been renamed to `[logger.filename]` for consistency.
This commit is contained in:
Florine W. Dekker 2023-03-20 13:55:31 +01:00
parent f43c59c681
commit 80835d6a08
Signed by: FWDekker
GPG Key ID: D3DCFAA8A4560BE0
11 changed files with 71 additions and 38 deletions

View File

@ -17,7 +17,7 @@ This tool regularly checks if people are still alive according to Wikipedia, and
* [npm](https://www.npmjs.com/) * [npm](https://www.npmjs.com/)
### Setting up ### Setting up
Install the latest dependencies. Install the dependencies.
Run this after cloning the repo, and each time after pulling new commits. Run this after cloning the repo, and each time after pulling new commits.
```shell ```shell
composer.phar install composer.phar install
@ -87,10 +87,12 @@ For example, you can add the following lines to your crontab (e.g. using `sudo -
Replace `secret_password` with the password you configured in `config.ini.php`. Replace `secret_password` with the password you configured in `config.ini.php`.
### Logs ### Logs
It is recommended to also use a tool such as `newsyslog` to manage log rotation. It is recommended to also use a tool such as
[`newsyslog`](https://man.freebsd.org/cgi/man.cgi?query=newsyslog.conf&sektion=5) to manage log rotation.
For example, create the file `/etc/newsyslog.conf.d/death-notifier.conf` with the following contents: For example, create the file `/etc/newsyslog.conf.d/death-notifier.conf` with the following contents:
``` ```
/var/www/death-notifier/.death-notifier.log www:www 644 7 * @T00 JpE /var/www/death-notifier/.death-notifier.log www:www 600 7 * @T00 JE
/var/www/death-notifier/.death-notifier.db.log www:www 600 7 * $W0D23 JpE
``` ```
### Initialize ### Initialize

View File

@ -1,7 +1,7 @@
{ {
"name": "fwdekker/death-notifier", "name": "fwdekker/death-notifier",
"description": "Get notified when a famous person dies.", "description": "Get notified when a famous person dies.",
"version": "1.0.1", "_comment_version": "Also update version in `package.json`!", "version": "1.1.0", "_comment_version": "Also update version in `package.json`!",
"type": "project", "type": "project",
"license": "MIT", "license": "MIT",
"homepage": "https://git.fwdekker.com/tools/death-notifier", "homepage": "https://git.fwdekker.com/tools/death-notifier",

BIN
composer.lock generated

Binary file not shown.

BIN
package-lock.json generated

Binary file not shown.

View File

@ -1,6 +1,6 @@
{ {
"name": "death-notifier", "name": "death-notifier",
"version": "1.0.1", "_comment_version": "Also update version in `composer.json`!", "version": "1.1.0", "_comment_version": "Also update version in `composer.json`!",
"description": "Get notified when a famous person dies.", "description": "Get notified when a famous person dies.",
"author": "Florine W. Dekker", "author": "Florine W. Dekker",
"browser": "dist/bundle.js", "browser": "dist/bundle.js",

View File

@ -36,7 +36,7 @@ use com\fwdekker\deathnotifier\Util;
require_once __DIR__ . "/.vendor/autoload.php"; require_once __DIR__ . "/.vendor/autoload.php";
$logger = LoggerUtil::with_name(); $logger = LoggerUtil::with_name(); // This also registers error handler
// Wrap everything in try-catch to always return *something* to user // Wrap everything in try-catch to always return *something* to user

View File

@ -5,13 +5,19 @@
cli_password = REPLACE THIS WITH A SECRET VALUE cli_password = REPLACE THIS WITH A SECRET VALUE
[database] [database]
# Relative path to SQLite database. # File to store SQLite database in.
filename = .death-notifier.db filename = .death-notifier.db
[logger] [logger]
# File to store logs in. # File to store general logs in.
file = .death-notifier.log filename = .death-notifier.log
# Log level. See https://seldaek.github.io/monolog/doc/01-usage.html#log-levels # Log level for general log events. See https://seldaek.github.io/monolog/doc/01-usage.html#log-levels
level = 250
[logger_db]
# File to store database logs in.
filename = .death-notifier.db.log
# Log level for database log events. See https://seldaek.github.io/monolog/doc/01-usage.html#log-levels
level = 250 level = 250
[mail] [mail]

View File

@ -24,7 +24,7 @@ class Database
/** /**
* @var Logger the logger to use for logging * @var Logger the logger to use for logging
*/ */
private readonly Logger $logger; private readonly Logger $db_logger;
/** /**
* @var PDO the PDO object that connects to the database * @var PDO the PDO object that connects to the database
@ -39,7 +39,7 @@ class Database
*/ */
public function __construct(string $filename) public function __construct(string $filename)
{ {
$this->logger = LoggerUtil::with_name($this::class); $this->db_logger = LoggerUtil::db_with_name($this::class);
$this->conn = new PDO("sqlite:$filename", options: array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION)); $this->conn = new PDO("sqlite:$filename", options: array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
$this->conn->exec("PRAGMA foreign_keys = ON;"); $this->conn->exec("PRAGMA foreign_keys = ON;");
@ -64,7 +64,7 @@ class Database
if ($stmt->fetch()[0] !== 0) if ($stmt->fetch()[0] !== 0)
return; return;
$this->logger->notice("Database does not exist. Installing new database."); $this->db_logger->notice("Database does not exist. Installing new database.");
// Create `meta` table // Create `meta` table
$this->conn->exec("CREATE TABLE meta(k TEXT NOT NULL UNIQUE PRIMARY KEY, v TEXT);"); $this->conn->exec("CREATE TABLE meta(k TEXT NOT NULL UNIQUE PRIMARY KEY, v TEXT);");
@ -77,7 +77,7 @@ class Database
$user_list->install(); $user_list->install();
$tracking_list->install(); $tracking_list->install();
$this->logger->notice("Installation complete."); $this->db_logger->notice("Installation complete.");
}); });
} }
@ -96,7 +96,7 @@ class Database
if (Comparator::greaterThanOrEqualTo($db_version, Database::LATEST_VERSION)) if (Comparator::greaterThanOrEqualTo($db_version, Database::LATEST_VERSION))
return; return;
$this->logger->notice("Current db is v$db_version. Will migrate to v" . Database::LATEST_VERSION . "."); $this->db_logger->notice("Current db is v$db_version. Will migrate to v" . Database::LATEST_VERSION . ".");
// Get current version // Get current version
$stmt = $this->conn->prepare("SELECT v FROM meta WHERE k='version';"); $stmt = $this->conn->prepare("SELECT v FROM meta WHERE k='version';");
@ -115,7 +115,7 @@ class Database
$stmt->bindValue(":version", Database::LATEST_VERSION); $stmt->bindValue(":version", Database::LATEST_VERSION);
$stmt->execute(); $stmt->execute();
$this->logger->notice("Completed migration to v" . Database::LATEST_VERSION . "."); $this->db_logger->notice("Completed migration to v" . Database::LATEST_VERSION . ".");
}); });
} }
@ -127,7 +127,7 @@ class Database
*/ */
private function migrate_0_5_0(): void private function migrate_0_5_0(): void
{ {
$this->logger->notice("Migrating to v0.5.0."); $this->db_logger->notice("Migrating to v0.5.0.");
$this->conn->exec("ALTER TABLE users $this->conn->exec("ALTER TABLE users
ADD COLUMN email_notifications_enabled INT NOT NULL DEFAULT(1);"); ADD COLUMN email_notifications_enabled INT NOT NULL DEFAULT(1);");
@ -141,7 +141,7 @@ class Database
*/ */
private function migrate_0_8_0(): void private function migrate_0_8_0(): void
{ {
$this->logger->notice("Migrating to v0.8.0."); $this->db_logger->notice("Migrating to v0.8.0.");
$this->conn->exec("CREATE TABLE new_email_tasks(type TEXT NOT NULL, $this->conn->exec("CREATE TABLE new_email_tasks(type TEXT NOT NULL,
recipient TEXT NOT NULL, recipient TEXT NOT NULL,
@ -163,7 +163,7 @@ class Database
*/ */
private function migrate_0_10_0(): void private function migrate_0_10_0(): void
{ {
$this->logger->notice("Migrating to v0.10.0."); $this->db_logger->notice("Migrating to v0.10.0.");
$this->conn->exec("CREATE TABLE new_email_tasks(type TEXT NOT NULL, $this->conn->exec("CREATE TABLE new_email_tasks(type TEXT NOT NULL,
recipient TEXT NOT NULL, recipient TEXT NOT NULL,
@ -188,7 +188,7 @@ class Database
*/ */
private function migrate_0_16_0(): void private function migrate_0_16_0(): void
{ {
$this->logger->notice("Migrating to v0.16.0."); $this->db_logger->notice("Migrating to v0.16.0.");
$this->conn->exec("DROP TABLE email_tasks;"); $this->conn->exec("DROP TABLE email_tasks;");
$this->conn->exec("CREATE TABLE email_tasks(type_key TEXT NOT NULL, $this->conn->exec("CREATE TABLE email_tasks(type_key TEXT NOT NULL,
@ -206,7 +206,7 @@ class Database
*/ */
private function migrate_0_18_0(): void private function migrate_0_18_0(): void
{ {
$this->logger->notice("Migrating to v0.18.0."); $this->db_logger->notice("Migrating to v0.18.0.");
$this->conn->exec("CREATE TABLE new_trackings(user_uuid TEXT NOT NULL, $this->conn->exec("CREATE TABLE new_trackings(user_uuid TEXT NOT NULL,
person_name TEXT NOT NULL, person_name TEXT NOT NULL,

View File

@ -15,28 +15,49 @@ use Monolog\Logger;
class LoggerUtil class LoggerUtil
{ {
/** /**
* @var Logger|null the main logger instance that other loggers are derived from, or `null` if the main logger has * @var Logger|null the logger instance for general log events not covered by other loggers
* not been created yet
*/ */
private static ?Logger $main_logger = null; private static ?Logger $general_logger = null;
/**
* @var Logger|null the logger instance for database log events
*/
private static ?Logger $db_logger = null;
/** /**
* Returns a logger with the given name. * Returns a general logger with the given name.
* *
* @param string $name the name of the logger to return * @param string $name the name of the general logger to return
* @return Logger a logger with the given name * @return Logger a general logger with the given name
*/ */
public static function with_name(string $name = "main"): Logger public static function with_name(string $name = "general"): Logger
{ {
if (self::$main_logger === null) { if (self::$general_logger === null) {
$config = Config::get("logger"); $config = Config::get("logger");
self::$main_logger = new Logger("main"); self::$general_logger = new Logger("general");
self::$main_logger->pushHandler(new StreamHandler($config["file"], $config["level"])); self::$general_logger->pushHandler(new StreamHandler($config["filename"], $config["level"]));
ErrorHandler::register(self::$main_logger); ErrorHandler::register(self::$general_logger);
} }
return self::$main_logger->withName($name); return self::$general_logger->withName($name);
}
/**
* Returns a database logger with the given name.
*
* @param string $name the name of the database logger to return
* @return Logger a database logger with the given name
*/
public static function db_with_name(string $name = "database"): Logger
{
if (self::$db_logger === null) {
$config = Config::get("logger_db");
self::$db_logger = new Logger("general");
self::$db_logger->pushHandler(new StreamHandler($config["filename"], $config["level"]));
}
return self::$db_logger->withName($name);
} }
} }

View File

@ -31,6 +31,10 @@ class UpdateTrackingsAction extends Action
* @var Logger the logger to log with * @var Logger the logger to log with
*/ */
private readonly Logger $logger; private readonly Logger $logger;
/**
* @var Logger the database logger to log with
*/
private readonly Logger $db_logger;
/** /**
* @var TrackingList the list of trackings to update * @var TrackingList the list of trackings to update
*/ */
@ -55,6 +59,7 @@ class UpdateTrackingsAction extends Action
public function __construct(TrackingList $tracking_list, Wikipedia $wikipedia, EmailQueue $mailer) public function __construct(TrackingList $tracking_list, Wikipedia $wikipedia, EmailQueue $mailer)
{ {
$this->logger = LoggerUtil::with_name($this::class); $this->logger = LoggerUtil::with_name($this::class);
$this->db_logger = LoggerUtil::db_with_name($this::class);
$this->tracking_list = $tracking_list; $this->tracking_list = $tracking_list;
$this->wikipedia = $wikipedia; $this->wikipedia = $wikipedia;
$this->mailer = $mailer; $this->mailer = $mailer;
@ -105,21 +110,20 @@ class UpdateTrackingsAction extends Action
// Send mails, log events // Send mails, log events
// TODO: Restrict number of notifications to 1 per hour (excluding "oops we're not sure" message) // TODO: Restrict number of notifications to 1 per hour (excluding "oops we're not sure" message)
// TODO: Wait for some time after person has been marked as dead before sending the email // TODO: Wait for some time after person has been marked as dead before sending the email
$logger = LoggerUtil::with_name($this::class);
$emails = []; $emails = [];
$person_names = $new_deletions + $new_undeletions + array_keys($new_status_changes); $person_names = $new_deletions + $new_undeletions + array_keys($new_status_changes);
$trackers = $this->tracking_list->list_trackers($person_names); $trackers = $this->tracking_list->list_trackers($person_names);
foreach ($new_deletions as $new_deletion) { foreach ($new_deletions as $new_deletion) {
$logger->notice("Deleted article $new_deletion."); $this->db_logger->notice("Deleted article $new_deletion.");
foreach ($trackers[$new_deletion] as $user_email) foreach ($trackers[$new_deletion] as $user_email)
$emails[] = new NotifyArticleDeletedEmail($user_email, $new_deletion); $emails[] = new NotifyArticleDeletedEmail($user_email, $new_deletion);
} }
foreach ($new_undeletions as $new_undeletion) { foreach ($new_undeletions as $new_undeletion) {
$logger->notice("Undeleted article $new_undeletion."); $this->db_logger->notice("Undeleted article $new_undeletion.");
foreach ($trackers[$new_undeletion] as $user_email) foreach ($trackers[$new_undeletion] as $user_email)
$emails[] = new NotifyArticleUndeletedEmail($user_email, $new_undeletion); $emails[] = new NotifyArticleUndeletedEmail($user_email, $new_undeletion);
@ -127,7 +131,7 @@ class UpdateTrackingsAction extends Action
foreach ($new_status_changes as $person_name => $person_status) { foreach ($new_status_changes as $person_name => $person_status) {
if ($person_status === PersonStatus::Alive) if ($person_status === PersonStatus::Alive)
$logger->notice("Person $person_name is now alive again."); $this->db_logger->notice("Person $person_name is now alive again.");
foreach ($trackers[$person_name] as $user_email) foreach ($trackers[$person_name] as $user_email)
$emails[] = new NotifyStatusChangedEmail($user_email, $person_name, $person_status->value); $emails[] = new NotifyStatusChangedEmail($user_email, $person_name, $person_status->value);

View File

@ -1,6 +1,6 @@
<?php <?php
namespace com\fwdekker\deathnotifier\validation; namespace com\fwdekker\deathnotifier;
use com\fwdekker\deathnotifier\Config; use com\fwdekker\deathnotifier\Config;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;