feat: move log directory to uploads folder for persistence on plugin updates#103
Conversation
There was a problem hiding this comment.
Pull request overview
This PR moves Join Block’s Monolog file logging from the plugin directory into wp-content/uploads so logs persist across plugin updates, and updates the admin “Logging” settings tab to read from the new location.
Changes:
- Introduces
Logging::getLogDirectory()to resolve (and create) an uploads-based log directory and attempt legacy log migration. - Switches both
Logging::init()and the Settings log viewer to use the new log directory. - Removes the in-repo placeholder log file and updates
.gitignorelog rules.
Reviewed changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| packages/join-block/src/Settings.php | Reads displayed log contents from the new uploads-based log directory. |
| packages/join-block/src/Logging.php | Adds uploads-based log directory resolution/creation + uses it for Monolog initialization. |
| packages/join-block/logs/an_empty_log.txt | Removes the placeholder file that previously kept packages/join-block/logs/ present. |
| .gitignore | Removes repo-root logs/ ignore exceptions, leaving only *.log patterns. |
Comments suppressed due to low confidence (2)
packages/join-block/src/Settings.php:249
- The log viewer assumes the first entry from scandir() is a readable log file. When the directory is empty, scandir() returns only '.' and '..', so file_get_contents() will target '..' and fall back to the generic error message. Consider filtering out '.'/'..' (and any non-regular files) and showing a clearer 'No logs yet' message when no log files exist.
$joinBlockLogLocation = Logging::getLogDirectory();
$logfiles = scandir($joinBlockLogLocation, SCANDIR_SORT_DESCENDING);
// Ignore file_get_contents error because this will always be a local file
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
$log = $logfiles ? @file_get_contents($joinBlockLogLocation . '/' . $logfiles[0]) : "";
if (!$log) {
$log = 'Could not load error log. Please contact Common Knowledge support.';
}
packages/join-block/logs/an_empty_log.txt:1
- Removing the only tracked file in packages/join-block/logs/ deletes the directory from the repo, but PHPUnit’s SessionLockTestProcess writes to tests/../logs/tests.log without creating the directory. This will make fopen() fail and can break the CI PHP test workflow. Either keep the directory (e.g., a .gitkeep) or update the tests to write into a temp dir and mkdir as needed.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…n into feat/persistant-logs
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 6 changed files in this pull request and generated 3 comments.
Comments suppressed due to low confidence (1)
packages/join-block/src/Settings.php:250
- The log contents are interpolated directly into HTML (
<pre>...</pre>) without escaping. Because logs include user-supplied values (emails, payloads), this can enable stored XSS in the WP admin when viewing the log tab. Escape the log output (e.g.,esc_html($log)) before rendering.
$log = $logfiles ? @file_get_contents($joinBlockLogLocation . '/' . $logfiles[0]) : "";
if (!$log) {
$log = 'Could not load error log. Please contact Common Knowledge support.';
}
return "<pre style=\"max-width:150ch\">$log</pre>";
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 6 changed files in this pull request and generated 5 comments.
Comments suppressed due to low confidence (1)
packages/join-block/tests/SessionLockTest.php:34
- The test now writes to a fixed filename in the shared system temp dir (
/tmp/join-block-tests.log). This can cause flaky tests if multiple test runs/processes execute on the same machine concurrently. Consider generating a unique temp filename (e.g. viatempnam()/ PID) and passing it to the child process (argv/env) so each run is isolated.
$logFile = sys_get_temp_dir() . "/join-block-tests.log";
// Ensure clean log file output
// phpcs:ignore WordPress.WP.AlternativeFunctions.unlink_unlink
@unlink($logFile);
$shellKey = escapeshellarg($lockKey);
// Start two processes in parallel
// Each script takes 1 second to complete. Therefore, if
// they run in parallel, they should both complete in a little
// over 1 second. However, if they run in series, they will not
// both be complete until after 2 seconds.
exec("php $scriptPath $shellKey > /dev/null 2>&1 &");
exec("php $scriptPath $shellKey > /dev/null 2>&1 &");
97295bc to
1505efe
Compare
| // 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'; | ||
| } |
| error_log( | ||
| 'join-block: unable to create a writable log directory (tried: ' | ||
| . implode(', ', $candidates ?: ['<none>']) . '); ' | ||
| . 'file-based logging disabled for this request' | ||
| ); |
| 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); | ||
| } | ||
| } |
| $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); | ||
| })); |
No description provided.