Skip to main content

Debug Logging

UnBlock includes a lightweight debug logger for diagnosing expression, query, and template errors. It is disabled by default and produces zero overhead in production.

Enabling

Add this constant to wp-config.php:

define( 'UNBK_DEBUG', true );

When disabled (the default), all do_action( 'unblock/log/...' ) calls are pure no-ops — no listeners are registered, no files are created.

Log Files

Location: wp-content/uploads/unblock/logs/

Filename format: {Y-m-d}-{hash}.log

The hash suffix is generated via wp_hash(), making filenames unpredictable without access to wp-config.php. One file per day — all entries for the same day append to the same file.

Entry Format

[2026-02-16 14:32:01] [ERROR] Expression resolution failed {"expression":"post.invalid_field","error":"Unknown provider field"}
[2026-02-16 14:32:01] [WARNING] Invalid date value or modifier {"error":"Failed to parse time string"}

Log Levels

HookLevelFired When
unblock/log/errorERRORExpression resolution, query execution, or class instantiation fails
unblock/log/warningWARNINGNon-critical issue (e.g. invalid date modifier)
unblock/log/debugDEBUGAvailable for custom diagnostic logging

Third-Party Integration

Any code can hook into unblock/log/{level} regardless of whether UNBK_DEBUG is enabled:

// Forward errors to Query Monitor.
add_action( 'unblock/log/error', function ( string $message, array $context ): void {
do_action( 'qm/error', $message, $context );
}, 10, 2 );
// Send errors to Sentry.
add_action( 'unblock/log/error', function ( string $message, array $context ): void {
\Sentry\captureMessage( $message, \Sentry\Severity::error() );
}, 10, 2 );
// Listen to all levels.
foreach ( [ 'error', 'warning', 'debug' ] as $level ) {
add_action( "unblock/log/{$level}", 'my_custom_handler', 10, 2 );
}

Security

The logger is designed with zero attack surface when disabled:

  1. Constant-gated — No log file or directory is created unless UNBK_DEBUG is explicitly true.
  2. Hashed filenameswp_hash() makes log filenames unguessable (primary protection on Nginx).
  3. .htaccessdeny from all blocks direct HTTP access on Apache/LiteSpeed.
  4. index.html — Prevents directory listing as a fallback.

Next steps

  • Actions — plugin lifecycle and form hooks
  • Data filters — providers, meta, and expression extensions