Skip to content

feat(logger): add PSR-3 logger that captures wpseo_logger output#281

Open
diedexx wants to merge 2 commits intodevelopfrom
87-logger-implementation
Open

feat(logger): add PSR-3 logger that captures wpseo_logger output#281
diedexx wants to merge 2 commits intodevelopfrom
87-logger-implementation

Conversation

@diedexx
Copy link
Copy Markdown
Member

@diedexx diedexx commented Apr 29, 2026

Summary

This PR can be summarized in the following changelog entry:

  • Adds a Logger panel to the Yoast Test Helper (Tools → Yoast Test) that replaces Yoast SEO's NullLogger via the wpseo_logger filter and persists captured entries to either a custom DB table or a JSONL file.
image image

Relevant technical choices

  • No new Composer dependency. The logger extends \YoastSEO_Vendor\Psr\Log\AbstractLogger (Yoast SEO's prefixed copy) so the resulting object satisfies the same LoggerInterface Yoast SEO's filter is typed against. Filter registration is gated by class_exists('\YoastSEO_Vendor\Psr\Log\AbstractLogger') so the test helper does not fatal when Yoast SEO is inactive.
  • Two backends, picked via dropdown: a dedicated {prefix}yoast_test_helper_log table created lazily via dbDelta on first use (capped at 1000 rows; cap is filterable through Yoast\WP\Test_Helper\logger_max_rows), or a JSONL file at a user-configurable absolute path (capped at 5MB with rotation by halving). The DB backend uses %i identifier placeholders so $wpdb->prepare() handles table-name escaping.
  • File-write concurrency: write(), clear(), and rotation all happen under a single flock(LOCK_EX) per call, so concurrent writers don't lose data during rotation.
  • Path validation rejects empty, non-absolute, ..-containing, non-writable, and paths inside WP_CONTENT_DIR / the uploads directory — those are typically web-accessible. UI also shows an inline warning under the path field.
  • Configurable level threshold (debug → emergency); entries below are dropped at capture time. PSR-3 §1.2 placeholder interpolation, with non-scalar context values JSON-encoded so {date}/{list}/{object} substitute meaningfully.
  • Viewer: compact summary with a native HTML <dialog popover> for the full table — no JavaScript required for open/close/ESC. Capped at 1000 entries; renders "1000+ logs captured" when there's more behind the cap. View / Delete are link-style buttons (Delete is button-link-delete, red), keeping Save as the only primary CTA.
  • Backend toggle JS (the only JS in this PR) hides the file-only fields when the database backend is selected.

Milestone

  • I've attached the next release's milestone to this pull request.

Test instructions

Test instructions for the acceptance test before the PR gets merged

This PR can be acceptance tested by following these steps:

  1. Activate Yoast SEO and the Yoast Test Helper.
  2. Visit Tools → Yoast Test. Confirm a new Logger block appears.
  3. Database backend: enable the logger, leave backend = Database, level = Debug, save. Confirm the table exists: wp db query "SHOW TABLES LIKE '%yoast_test_helper_log'".
  4. Trigger a log call, e.g. wp eval 'apply_filters("wpseo_logger", new \YoastSEO_Vendor\Psr\Log\NullLogger())->warning("hello {who}", ["who" => "world"]);'.
  5. Reload the panel; confirm "1 log captured." Click View captured logs — the popover shows the entry. Close it. Click Delete all captured logs — the table empties.
  6. File backend: switch backend = File, set path to /tmp/yoast-test-helper.log, save. Repeat the wp eval from step 4. tail /tmp/yoast-test-helper.log shows one JSON line containing "level":"warning" and "message":"hello world". Reload panel, confirm the viewer shows the entry. Click Delete — file truncates to 0 bytes.
  7. Threshold: set level = Error, fire a ->warning(...) and a ->critical(...). Only the critical entry persists.
  8. Invalid path: backend = File, path = /wp-content/uploads/anything.log. Confirm an error notification renders, the path doesn't save, and the logger doesn't switch on.
  9. Backend toggle: set backend = Database. Confirm the file path field and warning hide. Switch back to File. Confirm they reappear.
  10. Run composer check-cs-thresholds; should pass at the existing baseline (32 errors, 0 warnings).

Test instructions for QA when the code is in the RC

  • QA should use the same steps as above.

Fixes #87

diedexx and others added 2 commits April 29, 2026 12:26
Adds a text-input field helper alongside the existing checkbox/select
helpers, so integrations can render text inputs in the same form style
without hand-rolling HTML. Includes a small CSS rule for input[type=text]
so the new field aligns with surrounding controls.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces Yoast SEO's NullLogger via the wpseo_logger filter with a real
PSR-3 implementation that persists captured entries to a configurable
backend, plus an admin UI to enable/disable the logger, pick a level
threshold, switch between backends, and view or clear the captured logs.

Backends:
- Database: a dedicated wp_yoast_test_helper_log table created lazily
  via dbDelta on first use, capped at 1000 rows (filterable through
  Yoast\WP\Test_Helper\logger_max_rows). Uses %i identifier
  placeholders so $wpdb->prepare() handles table-name escaping.
- File: a JSONL file at a user-configurable absolute path, capped at
  5MB with rotation by halving. Writes and rotation happen under the
  same flock so concurrent writers don't lose data. Path validation
  rejects anything inside wp-content/ or the uploads directory.

Implementation extends \YoastSEO_Vendor\Psr\Log\AbstractLogger (the
prefixed copy shipped by Yoast SEO) rather than pulling in psr/log,
so the resulting object satisfies the same interface Yoast SEO's
filter is typed against. Filter registration is gated by class_exists
so the test helper doesn't fatal when Yoast SEO is inactive.

The viewer renders inline as a compact summary with View / Delete
link-buttons; clicking View opens a wide native HTML <dialog popover>
with the most recent entries (cap 1000, with "1000+" overflow text
when there's more behind the cap).

Fixes #87

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Create a logger implementation

1 participant