diff --git a/.gitignore b/.gitignore index 1ac84db1..69df9537 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,4 @@ # Logs -logs/**/* -!logs/an_empty_log.txt *.log npm-debug.log* yarn-debug.log* diff --git a/packages/join-block/logs/an_empty_log.txt b/packages/join-block/logs/an_empty_log.txt deleted file mode 100644 index 427dd28b..00000000 --- a/packages/join-block/logs/an_empty_log.txt +++ /dev/null @@ -1 +0,0 @@ -No logs yet! \ No newline at end of file diff --git a/packages/join-block/src/Logging.php b/packages/join-block/src/Logging.php index 60df8094..6a94ec2d 100644 --- a/packages/join-block/src/Logging.php +++ b/packages/join-block/src/Logging.php @@ -13,25 +13,90 @@ class Logging { + public static function getLogDirectory() + { + // Prefer the uploads dir (survives plugin updates), but fall back to + // WP_CONTENT_DIR if uploads is misconfigured or unwritable. + $uploads = wp_upload_dir(null, false); + $basedir = (is_array($uploads) && empty($uploads['error']) && !empty($uploads['basedir'])) + ? $uploads['basedir'] + : null; + + $candidates = []; + if ($basedir) { + $candidates[] = $basedir . '/join-block-logs'; + } + if (defined('WP_CONTENT_DIR')) { + $candidates[] = WP_CONTENT_DIR . '/join-block-logs'; + } + + $logLocation = null; + $created = false; + foreach ($candidates as $candidate) { + $existed = is_dir($candidate); + if (!$existed && !wp_mkdir_p($candidate)) { + continue; + } + if (!is_writable($candidate)) { + continue; + } + $logLocation = $candidate; + $created = !$existed; + break; + } + + if ($logLocation === null) { + error_log( + 'join-block: unable to create a writable log directory (tried: ' + . implode(', ', $candidates ?: ['']) . '); ' + . 'file-based logging disabled for this request' + ); + return null; + } + + // On first creation, migrate any pre-existing logs from the old + // in-plugin location, which WordPress wipes on plugin update. + if ($created) { + $legacyLocation = __DIR__ . '/../logs'; + if (is_dir($legacyLocation)) { + $legacyFiles = scandir($legacyLocation) ?: []; + foreach ($legacyFiles as $file) { + if ($file === '.' || $file === '..') { + continue; + } + $src = $legacyLocation . '/' . $file; + $dst = $logLocation . '/' . $file; + if (is_file($src) && !file_exists($dst)) { + @copy($src, $dst); + } + } + } + } + + return $logLocation; + } + public static function init() { global $joinBlockLog; $joinBlockLog = new Logger('join-block'); - $logFilenameHash = null; - $logLocation = __DIR__ . "/../logs"; - $logFiles = scandir($logLocation); - foreach ($logFiles as $logFile) { - if (str_starts_with($logFile, "debug-")) { - $parts = explode("-", $logFile); - $logFilenameHash = $parts[1]; - break; + $logLocation = self::getLogDirectory(); + if ($logLocation !== null) { + $logFilenameHash = null; + $logFiles = scandir($logLocation) ?: []; + foreach ($logFiles as $logFile) { + if (str_starts_with($logFile, "debug-")) { + $parts = explode("-", $logFile); + $logFilenameHash = $parts[1]; + break; + } } + if (!$logFilenameHash) { + $logFilenameHash = bin2hex(random_bytes(18)); + } + $logFilename = "debug-$logFilenameHash.log"; + $joinBlockLog->pushHandler(new RotatingFileHandler("$logLocation/$logFilename", 10, Level::Info)); } - if (!$logFilenameHash) { - $logFilenameHash = bin2hex(random_bytes(18)); - } - $logFilename = "debug-$logFilenameHash.log"; - $joinBlockLog->pushHandler(new RotatingFileHandler("$logLocation/$logFilename", 10, Level::Info)); $joinBlockLog->pushProcessor(new WebProcessor()); } diff --git a/packages/join-block/src/Settings.php b/packages/join-block/src/Settings.php index 7bbf61a6..c9ca6488 100644 --- a/packages/join-block/src/Settings.php +++ b/packages/join-block/src/Settings.php @@ -239,8 +239,8 @@ public static function init() /** @var Html_Field $logField */ $logField = Field::make('html', 'ck_join_flow_log_contents'); $logField->set_html(function () { - $joinBlockLogLocation = __DIR__ . "/../logs"; - $logfiles = scandir($joinBlockLogLocation, SCANDIR_SORT_DESCENDING) ?: []; + $joinBlockLogLocation = Logging::getLogDirectory(); + $logfiles = $joinBlockLogLocation ? scandir($joinBlockLogLocation, SCANDIR_SORT_DESCENDING) : []; $logfiles = array_values(array_filter($logfiles, function ($file) use ($joinBlockLogLocation) { return str_starts_with($file, 'debug-') && is_file($joinBlockLogLocation . '/' . $file); })); diff --git a/packages/join-block/tests/SessionLockTest.php b/packages/join-block/tests/SessionLockTest.php index 8cf21a81..0651a239 100644 --- a/packages/join-block/tests/SessionLockTest.php +++ b/packages/join-block/tests/SessionLockTest.php @@ -18,7 +18,7 @@ public function testLockSerializesByKey(): void $scriptPath = __DIR__ . "/SessionLockTestProcess.php"; // Use an email-shaped key to cover the typical webhook/join lock-key form. $lockKey = "test+" . microtime(true) . "@example.com"; - $logFile = __DIR__ . "/../logs/tests.log"; + $logFile = sys_get_temp_dir() . "/join-block-tests.log"; // Ensure clean log file output // phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink @unlink($logFile); diff --git a/packages/join-block/tests/SessionLockTestProcess.php b/packages/join-block/tests/SessionLockTestProcess.php index 04c89862..bc9a14e6 100644 --- a/packages/join-block/tests/SessionLockTestProcess.php +++ b/packages/join-block/tests/SessionLockTestProcess.php @@ -23,7 +23,7 @@ // Set up log file so SessionLockTest can monitor progress of this script global $joinBlockLog; $joinBlockLog = new Logger('join-block-test'); -$joinBlockLogLocation = __DIR__ . '/../logs/tests.log'; +$joinBlockLogLocation = sys_get_temp_dir() . '/join-block-tests.log'; // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen $joinBlockLogFile = fopen($joinBlockLogLocation, 'a'); $joinBlockLog->pushHandler(new StreamHandler($joinBlockLogFile, Level::Info));