PHP VersionPackagist DownloadsPackagist StarsGitHub Actions Workflow StatusCoverage StatusKnown VulnerabilitiesGitHub IssuesGitHub ReleaseLicense

Wonolog Handler

Monolog handler that forwards Laravel logs to Inpsyde's Wonolog - the professional WordPress logging solution.

Works with any Laravel + WordPress setup: Acorn (w/wo Sage), WP Starter, Corcel, or custom integrations.

Features

  • Clean Laravel Syntax - Use Log::error(), Log::info(), etc. anywhere in your code
  • Graceful Degradation - Works with or without Wonolog active
  • wpify/scoper Support - Automatically detects scoped Wonolog namespace
  • Zero Configuration - Works out of the box with sensible defaults
  • Flexible Propagation - Control whether to stop at Wonolog or continue to other handlers

Requirements

  • PHP >= 8.2
  • WordPress >= 6.0
  • Laravel Illuminate/Support ^10.0|^11.0|^12.0|^13.0
  • Monolog ^2.0|^3.0

Note: Inpsyde's Wonolog is not required for the package to work. Without Wonolog, logs gracefully pass through to other handlers in your stack (e.g., file logging).

Installation

1. Install the handler package

In your Laravel + WordPress project (Sage theme, WP Starter, etc.):

composer require wp-spaghetti/wonolog-handler

The package auto-registers via service provider discovery.

2. Install WP Spaghetti Wonolog mu-plugin (optional)

For email notifications, sensitive data filtering, and advanced logging features, install the WP Spaghetti Wonolog mu-plugin, that provides a complete logging solution with production-ready configuration.

See the WP Spaghetti Wonolog documentation for setup and configuration options.

3. Configure logging

Update your config/logging.php:

<?phpuseWpSpaghetti\WonologHandler\Handler\WonologHandler;
return [
'default' => env('LOG_CHANNEL', 'stack'),
'channels' => [
// Recommended: Stack with Wonolog + file backup'stack' => [
'driver' => 'stack',
'channels' => ['wonolog', 'single'],
'ignore_exceptions' => false,
],
// Wonolog channel'wonolog' => [
'driver' => 'monolog',
'handler' => WonologHandler::class,
'level' => env('LOG_LEVEL', 'debug'),
],
// File backup (optional but recommended)'single' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
],
],
];

See examples/logging.php for a complete configuration example.

Usage

Basic Logging

useIlluminate\Support\Facades\Log;
// Anywhere in your Laravel + WordPress code
Log::debug('Debugging information');
Log::info('Informational message');
Log::notice('Normal but significant event');
Log::warning('Warning condition');
Log::error('Error condition');
Log::critical('Critical condition');
Log::alert('Action must be taken immediately');
Log::emergency('System is unusable');

With Context

Log::error('Payment failed', [
'user_id' => $userId,
'amount' => $amount,
'error' => $exception->getMessage(),
]);
// Wonolog channels - use UPPERCASE by convention
Log::error('Security breach', [
'channel' => 'SECURITY', // ✅ Correct'ip' => $ipAddress,
]);
// Avoid lowercase - may not be tracked// Log::error('Breach', ['channel' => 'security']); // ❌ May not work

Channel Selection

// Use only Wonolog (no file backup)
Log::channel('wonolog')->error('Critical error');
// Use only file logging
Log::channel('single')->debug('Debug info');
// Use stack (Wonolog + file) - recommended
Log::channel('stack')->warning('Warning message');

Framework-Specific Examples

Sage Themes (Acorn)

// In app/Controllers/App.php or any controlleruseIlluminate\Support\Facades\Log;
publicfunctionindex()
{
Log::info('Page viewed', ['url' => request()->url()]);
return$this->view;
}

WP Starter

// In your custom plugins or themeuseIlluminate\Support\Facades\Log;
add_action('init', function() {
Log::info('WordPress initialized');
});

Corcel

useCorcel\Model\Post;
useIlluminate\Support\Facades\Log;
$posts = Post::published()->get();
Log::info('Fetched posts', ['count' => $posts->count()]);

See examples/usage.php for more real-world examples including WordPress hooks, WooCommerce integration, API logging, and performance monitoring.

Advanced Configuration

Publish Configuration

To customize settings, publish the config:

# Sage/Acorn
wp acorn vendor:publish --tag=wonolog-handler-config
# WP Starter (using Laravel's artisan)
php vendor/bin/wp-starter vendor:publish --tag=wonolog-handler-config

This creates config/wonolog.php in your project:

<?phpreturn [
// Custom Wonolog namespace (for wpify/scoper)'namespace' => env('WONOLOG_NAMESPACE', 'Inpsyde\\Wonolog'),
// Custom action hook (for wpify/scoper or custom naming)'action' => env('WONOLOG_ACTION', 'wonolog.log'),
// Stop propagation when Wonolog is active?'stop_propagation' => env('WONOLOG_STOP_PROPAGATION', false),
];

Custom Wonolog Namespace (for wpify/scoper)

If Wonolog is scoped, override the namespace:

Via config:

// config/wonolog.php'namespace' => 'WpSpaghetti\\Deps\\Inpsyde\\Wonolog',

Via filter:

add_filter('wonolog_handler.namespace', function () {
return'WpSpaghetti\\Deps\\Inpsyde\\Wonolog';
});

Via environment:

WONOLOG_NAMESPACE="WpSpaghetti\\Deps\\Inpsyde\\Wonolog"

Custom Action Hook (for wpify/scoper)

If Wonolog uses a custom action hook name:

Via config:

// config/wonolog.php'action' => 'custom_wonolog.log',

Via filter:

add_filter('wonolog_handler.action', function () {
return'custom_wonolog.log';
});

Via environment:

WONOLOG_ACTION="custom_wonolog.log"

Control Log Propagation

By default, logs continue to other handlers in the stack after Wonolog (allowing file backup). You can change this:

Stop at Wonolog (no file backup):

// config/wonolog.php'stop_propagation' => true,

Or via environment:

WONOLOG_STOP_PROPAGATION=true

Use cases:

  • false (default): Wonolog + file backup - recommended for production
  • true: Only Wonolog - if you don't want file logs and trust Wonolog completely

How It Works

Architecture

Laravel Log::error()
↓
Monolog LogRecord
↓
WonologHandler
↓ (if Wonolog active)
do_action('wonolog.log')
↓
Wonolog Processing
├─ Email notifications
├─ WordPress database
├─ Custom handlers
└─ Filtering/redaction
↓ (if stop_propagation=false)
Continue to next handler (file, Slack, etc.)

Technical Details

Channel Handling:

  • Wonolog expects channel at the top level of the action array, not inside context
  • If user provides 'channel' => 'SECURITY' in context, it's extracted and moved to top level
  • The channel is removed from context after extraction to avoid duplication
  • If no channel is provided, it's NOT passed to Wonolog (Wonolog uses its default: DEBUG)
  • Monolog's channel (e.g., stack, development) is NEVER used - it has nothing to do with Wonolog

Example behavior:

// User specifies Wonolog channel
Log::error('Error', ['channel' => 'SECURITY', 'ip' => '1.2.3.4']);
// Result: Channel = SECURITY, Context = ['ip' => '1.2.3.4'] (no 'channel' key)// No channel specified
Log::error('Error');
// Result: Channel = DEBUG (Wonolog's default), Context = [] (empty except datetime/extra)// Monolog channel is ignored
Log::channel('stack')->error('Error');
// Result: Channel = DEBUG (Wonolog's default), NOT 'stack'

PSR-3 Placeholder Compatibility:

  • The handler uses array format when calling do_action('wonolog.log', [...])
  • This forces Wonolog's HookLogFactory::fromArray() method instead of fromString()
  • Fixes PSR-3 placeholder substitution (e.g., {url}, {handle}) which breaks in fromString()
  • Compatible with all Wonolog v2.x and v3.x versions

Extra Data:

  • Monolog's extra data is passed as $context['extra'] (following Wonolog's convention)
  • Datetime is passed as $context['datetime'] for full compatibility

Graceful Degradation

Without Wonolog mu-plugin:

  • WonologHandler detects Wonolog is not active
  • Handler does nothing and returns false
  • Logs continue to other handlers (files, etc.)
  • No errors or warnings

With Wonolog mu-plugin:

  • Handler forwards logs to Wonolog
  • Wonolog processes with email, filtering, etc.
  • Logs optionally continue to file backup (based on stop_propagation)

Namespace Detection

The handler automatically detects Wonolog's namespace:

  1. Checks default Inpsyde\Wonolog
  2. Applies filter wonolog_handler_namespace
  3. Supports scoped namespaces from wpify/scoper
  4. Verifies Configurator::ACTION_SETUP was triggered
  5. Caches result for performance

Troubleshooting

Logs not appearing in Wonolog

Check if Wonolog is active:

useWpSpaghetti\WonologHandler\Support\WonologDetector;
$detector = app(WonologDetector::class);
if (!$detector->isActive()) {
echo"Wonolog is not active!";
echo"Namespace: " . $detector->getNamespace();
echo"Action: " . $detector->getAction();
}

Wrong namespace or action detected

Override via filter or config (see Advanced Configuration).

Channel-related issues

Understanding channel behavior:

  1. Custom Wonolog channel (when explicitly provided):

    Log::error('Security breach', ['channel' => 'SECURITY', 'ip' => '1.2.3.4']);
    // Email: Channel = SECURITY, Context = ['ip' => '1.2.3.4'] (no 'channel' key)
  2. Default Wonolog channel (when not provided):

    Log::error('Error');
    // Email: Channel = DEBUG (Wonolog's default)
    Log::channel('stack')->error('Error');
    // Email: Channel = DEBUG (Wonolog's default - Monolog channel is ignored)
    Log::channel('single')->error('Error');
    // Email: Channel = DEBUG (Wonolog's default - Monolog channel is ignored)
  3. Channel extraction:

    • If 'channel' is in context, it's extracted and passed to Wonolog at top level
    • The 'channel' key is removed from context to avoid duplication
    • This ensures channel appears only once in emails (as "Channel: XXX", not in context)
  4. Monolog vs Wonolog channels:

    • Monolog channels (development, stack, single) route logs in Laravel
    • Wonolog channels (DEBUG, SECURITY, HTTP) categorize logs in Wonolog
    • They are completely separate - Monolog channels are NOT sent to Wonolog
    • To set a Wonolog channel: Log::error('msg', ['channel' => 'SECURITY'])
  5. Channel naming conventions⚠️:

    • IMPORTANT: Wonolog uses UPPERCASE channel names by convention
    • Standard Wonolog channels: DEBUG, SECURITY, HTTP, DB, PHP-ERROR, CRON, etc.
    • Using lowercase (e.g., 'security' instead of 'SECURITY') may cause logs not to be tracked
    • Using non-configured channels (e.g., 'FOO') may also not be tracked
    • This behavior depends on your Wonolog configuration and filters
    • Best practice: Always use UPPERCASE for channel names
    // ✅ Correct - uppercase
    Log::error('Breach', ['channel' => 'SECURITY']);
    // ❌ May not work - lowercase
    Log::error('Breach', ['channel' => 'security']);
    // ❌ May not work - non-configured channel
    Log::error('Error', ['channel' => 'FOO']);
  6. Custom channel names:

    • You can use custom channel names if configured in Wonolog
    • Examples: PAYMENT, API, WOOCOMMERCE, etc.
    • Make sure they're configured in your Wonolog setup
    • Always use UPPERCASE for consistency

Logs not in file backup

Ensure 'single' channel is in the stack:

'stack' => [
'driver' => 'stack',
'channels' => ['wonolog', 'single'], // ← Check this
],

And ensure stop_propagation is false (default).

Testing

composer test

More info

See LINKS file.

Changelog

Please see CHANGELOG for a detailed list of changes for each release.

We follow Semantic Versioning and use Conventional Commits to automatically generate our changelog.

Release Process

  • Major versions (1.0.0 → 2.0.0): Breaking changes
  • Minor versions (1.0.0 → 1.1.0): New features, backward compatible
  • Patch versions (1.0.0 → 1.0.1): Bug fixes, backward compatible

All releases are automatically created when changes are pushed to the main branch, based on commit message conventions.

Contributing

For your contributions please use:

See CONTRIBUTING for detailed guidelines.

Sponsor

Buy Me A Coffee

License

(ɔ) Copyleft 2026 Frugan.
GNU GPLv3, see LICENSE file.

About

Monolog handler that forwards Laravel logs to Inpsyde's Wonolog - works with any Laravel + WordPress setup: Acorn (w/wo Sage), WP Starter, Corcel, or custom integrations

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

PHP VersionPackagist DownloadsPackagist StarsGitHub Actions Workflow StatusCoverage StatusKnown VulnerabilitiesGitHub IssuesGitHub ReleaseLicense

Wonolog Handler

Monolog handler that forwards Laravel logs to Inpsyde's Wonolog - the professional WordPress logging solution.

Works with any Laravel + WordPress setup: Acorn (w/wo Sage), WP Starter, Corcel, or custom integrations.

Features

  • Clean Laravel Syntax - Use Log::error(), Log::info(), etc. anywhere in your code
  • Graceful Degradation - Works with or without Wonolog active
  • wpify/scoper Support - Automatically detects scoped Wonolog namespace
  • Zero Configuration - Works out of the box with sensible defaults
  • Flexible Propagation - Control whether to stop at Wonolog or continue to other handlers

Requirements

  • PHP >= 8.2
  • WordPress >= 6.0
  • Laravel Illuminate/Support ^10.0|^11.0|^12.0|^13.0
  • Monolog ^2.0|^3.0

Note: Inpsyde's Wonolog is not required for the package to work. Without Wonolog, logs gracefully pass through to other handlers in your stack (e.g., file logging).

Installation

1. Install the handler package

In your Laravel + WordPress project (Sage theme, WP Starter, etc.):

composer require wp-spaghetti/wonolog-handler

The package auto-registers via service provider discovery.

2. Install WP Spaghetti Wonolog mu-plugin (optional)

For email notifications, sensitive data filtering, and advanced logging features, install the WP Spaghetti Wonolog mu-plugin, that provides a complete logging solution with production-ready configuration.

See the WP Spaghetti Wonolog documentation for setup and configuration options.

3. Configure logging

Update your config/logging.php:

<?phpuseWpSpaghetti\WonologHandler\Handler\WonologHandler;
return [
'default' => env('LOG_CHANNEL', 'stack'),
'channels' => [
// Recommended: Stack with Wonolog + file backup'stack' => [
'driver' => 'stack',
'channels' => ['wonolog', 'single'],
'ignore_exceptions' => false,
],
// Wonolog channel'wonolog' => [
'driver' => 'monolog',
'handler' => WonologHandler::class,
'level' => env('LOG_LEVEL', 'debug'),
],
// File backup (optional but recommended)'single' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
],
],
];

See examples/logging.php for a complete configuration example.

Usage

Basic Logging

useIlluminate\Support\Facades\Log;
// Anywhere in your Laravel + WordPress code
Log::debug('Debugging information');
Log::info('Informational message');
Log::notice('Normal but significant event');
Log::warning('Warning condition');
Log::error('Error condition');
Log::critical('Critical condition');
Log::alert('Action must be taken immediately');
Log::emergency('System is unusable');

With Context

Log::error('Payment failed', [
'user_id' => $userId,
'amount' => $amount,
'error' => $exception->getMessage(),
]);
// Wonolog channels - use UPPERCASE by convention
Log::error('Security breach', [
'channel' => 'SECURITY', // ✅ Correct'ip' => $ipAddress,
]);
// Avoid lowercase - may not be tracked// Log::error('Breach', ['channel' => 'security']); // ❌ May not work

Channel Selection

// Use only Wonolog (no file backup)
Log::channel('wonolog')->error('Critical error');
// Use only file logging
Log::channel('single')->debug('Debug info');
// Use stack (Wonolog + file) - recommended
Log::channel('stack')->warning('Warning message');

Framework-Specific Examples

Sage Themes (Acorn)

// In app/Controllers/App.php or any controlleruseIlluminate\Support\Facades\Log;
publicfunctionindex()
{
Log::info('Page viewed', ['url' => request()->url()]);
return$this->view;
}

WP Starter

// In your custom plugins or themeuseIlluminate\Support\Facades\Log;
add_action('init', function() {
Log::info('WordPress initialized');
});

Corcel

useCorcel\Model\Post;
useIlluminate\Support\Facades\Log;
$posts = Post::published()->get();
Log::info('Fetched posts', ['count' => $posts->count()]);

See examples/usage.php for more real-world examples including WordPress hooks, WooCommerce integration, API logging, and performance monitoring.

Advanced Configuration

Publish Configuration

To customize settings, publish the config:

# Sage/Acorn
wp acorn vendor:publish --tag=wonolog-handler-config
# WP Starter (using Laravel's artisan)
php vendor/bin/wp-starter vendor:publish --tag=wonolog-handler-config

This creates config/wonolog.php in your project:

<?phpreturn [
// Custom Wonolog namespace (for wpify/scoper)'namespace' => env('WONOLOG_NAMESPACE', 'Inpsyde\\Wonolog'),
// Custom action hook (for wpify/scoper or custom naming)'action' => env('WONOLOG_ACTION', 'wonolog.log'),
// Stop propagation when Wonolog is active?'stop_propagation' => env('WONOLOG_STOP_PROPAGATION', false),
];

Custom Wonolog Namespace (for wpify/scoper)

If Wonolog is scoped, override the namespace:

Via config:

// config/wonolog.php'namespace' => 'WpSpaghetti\\Deps\\Inpsyde\\Wonolog',

Via filter:

add_filter('wonolog_handler.namespace', function () {
return'WpSpaghetti\\Deps\\Inpsyde\\Wonolog';
});

Via environment:

WONOLOG_NAMESPACE="WpSpaghetti\\Deps\\Inpsyde\\Wonolog"

Custom Action Hook (for wpify/scoper)

If Wonolog uses a custom action hook name:

Via config:

// config/wonolog.php'action' => 'custom_wonolog.log',

Via filter:

add_filter('wonolog_handler.action', function () {
return'custom_wonolog.log';
});

Via environment:

WONOLOG_ACTION="custom_wonolog.log"

Control Log Propagation

By default, logs continue to other handlers in the stack after Wonolog (allowing file backup). You can change this:

Stop at Wonolog (no file backup):

// config/wonolog.php'stop_propagation' => true,

Or via environment:

WONOLOG_STOP_PROPAGATION=true

Use cases:

  • false (default): Wonolog + file backup - recommended for production
  • true: Only Wonolog - if you don't want file logs and trust Wonolog completely

How It Works

Architecture

Laravel Log::error()
↓
Monolog LogRecord
↓
WonologHandler
↓ (if Wonolog active)
do_action('wonolog.log')
↓
Wonolog Processing
├─ Email notifications
├─ WordPress database
├─ Custom handlers
└─ Filtering/redaction
↓ (if stop_propagation=false)
Continue to next handler (file, Slack, etc.)

Technical Details

Channel Handling:

  • Wonolog expects channel at the top level of the action array, not inside context
  • If user provides 'channel' => 'SECURITY' in context, it's extracted and moved to top level
  • The channel is removed from context after extraction to avoid duplication
  • If no channel is provided, it's NOT passed to Wonolog (Wonolog uses its default: DEBUG)
  • Monolog's channel (e.g., stack, development) is NEVER used - it has nothing to do with Wonolog

Example behavior:

// User specifies Wonolog channel
Log::error('Error', ['channel' => 'SECURITY', 'ip' => '1.2.3.4']);
// Result: Channel = SECURITY, Context = ['ip' => '1.2.3.4'] (no 'channel' key)// No channel specified
Log::error('Error');
// Result: Channel = DEBUG (Wonolog's default), Context = [] (empty except datetime/extra)// Monolog channel is ignored
Log::channel('stack')->error('Error');
// Result: Channel = DEBUG (Wonolog's default), NOT 'stack'

PSR-3 Placeholder Compatibility:

  • The handler uses array format when calling do_action('wonolog.log', [...])
  • This forces Wonolog's HookLogFactory::fromArray() method instead of fromString()
  • Fixes PSR-3 placeholder substitution (e.g., {url}, {handle}) which breaks in fromString()
  • Compatible with all Wonolog v2.x and v3.x versions

Extra Data:

  • Monolog's extra data is passed as $context['extra'] (following Wonolog's convention)
  • Datetime is passed as $context['datetime'] for full compatibility

Graceful Degradation

Without Wonolog mu-plugin:

  • WonologHandler detects Wonolog is not active
  • Handler does nothing and returns false
  • Logs continue to other handlers (files, etc.)
  • No errors or warnings

With Wonolog mu-plugin:

  • Handler forwards logs to Wonolog
  • Wonolog processes with email, filtering, etc.
  • Logs optionally continue to file backup (based on stop_propagation)

Namespace Detection

The handler automatically detects Wonolog's namespace:

  1. Checks default Inpsyde\Wonolog
  2. Applies filter wonolog_handler_namespace
  3. Supports scoped namespaces from wpify/scoper
  4. Verifies Configurator::ACTION_SETUP was triggered
  5. Caches result for performance

Troubleshooting

Logs not appearing in Wonolog

Check if Wonolog is active:

useWpSpaghetti\WonologHandler\Support\WonologDetector;
$detector = app(WonologDetector::class);
if (!$detector->isActive()) {
echo"Wonolog is not active!";
echo"Namespace: " . $detector->getNamespace();
echo"Action: " . $detector->getAction();
}

Wrong namespace or action detected

Override via filter or config (see Advanced Configuration).

Channel-related issues

Understanding channel behavior:

  1. Custom Wonolog channel (when explicitly provided):

    Log::error('Security breach', ['channel' => 'SECURITY', 'ip' => '1.2.3.4']);
    // Email: Channel = SECURITY, Context = ['ip' => '1.2.3.4'] (no 'channel' key)
  2. Default Wonolog channel (when not provided):

    Log::error('Error');
    // Email: Channel = DEBUG (Wonolog's default)
    Log::channel('stack')->error('Error');
    // Email: Channel = DEBUG (Wonolog's default - Monolog channel is ignored)
    Log::channel('single')->error('Error');
    // Email: Channel = DEBUG (Wonolog's default - Monolog channel is ignored)
  3. Channel extraction:

    • If 'channel' is in context, it's extracted and passed to Wonolog at top level
    • The 'channel' key is removed from context to avoid duplication
    • This ensures channel appears only once in emails (as "Channel: XXX", not in context)
  4. Monolog vs Wonolog channels:

    • Monolog channels (development, stack, single) route logs in Laravel
    • Wonolog channels (DEBUG, SECURITY, HTTP) categorize logs in Wonolog
    • They are completely separate - Monolog channels are NOT sent to Wonolog
    • To set a Wonolog channel: Log::error('msg', ['channel' => 'SECURITY'])
  5. Channel naming conventions⚠️:

    • IMPORTANT: Wonolog uses UPPERCASE channel names by convention
    • Standard Wonolog channels: DEBUG, SECURITY, HTTP, DB, PHP-ERROR, CRON, etc.
    • Using lowercase (e.g., 'security' instead of 'SECURITY') may cause logs not to be tracked
    • Using non-configured channels (e.g., 'FOO') may also not be tracked
    • This behavior depends on your Wonolog configuration and filters
    • Best practice: Always use UPPERCASE for channel names
    // ✅ Correct - uppercase
    Log::error('Breach', ['channel' => 'SECURITY']);
    // ❌ May not work - lowercase
    Log::error('Breach', ['channel' => 'security']);
    // ❌ May not work - non-configured channel
    Log::error('Error', ['channel' => 'FOO']);
  6. Custom channel names:

    • You can use custom channel names if configured in Wonolog
    • Examples: PAYMENT, API, WOOCOMMERCE, etc.
    • Make sure they're configured in your Wonolog setup
    • Always use UPPERCASE for consistency

Logs not in file backup

Ensure 'single' channel is in the stack:

'stack' => [
'driver' => 'stack',
'channels' => ['wonolog', 'single'], // ← Check this
],

And ensure stop_propagation is false (default).

Testing

composer test

More info

See LINKS file.

Changelog

Please see CHANGELOG for a detailed list of changes for each release.

We follow Semantic Versioning and use Conventional Commits to automatically generate our changelog.

Release Process

  • Major versions (1.0.0 → 2.0.0): Breaking changes
  • Minor versions (1.0.0 → 1.1.0): New features, backward compatible
  • Patch versions (1.0.0 → 1.0.1): Bug fixes, backward compatible

All releases are automatically created when changes are pushed to the main branch, based on commit message conventions.

Contributing

For your contributions please use:

See CONTRIBUTING for detailed guidelines.

Sponsor

Buy Me A Coffee

License

(ɔ) Copyleft 2026 Frugan.
GNU GPLv3, see LICENSE file.

About

Monolog handler that forwards Laravel logs to Inpsyde's Wonolog - works with any Laravel + WordPress setup: Acorn (w/wo Sage), WP Starter, Corcel, or custom integrations

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

PHP VersionPackagist DownloadsPackagist StarsGitHub Actions Workflow StatusCoverage StatusKnown VulnerabilitiesGitHub IssuesGitHub ReleaseLicense

Wonolog Handler

Monolog handler that forwards Laravel logs to Inpsyde's Wonolog - the professional WordPress logging solution.

Works with any Laravel + WordPress setup: Acorn (w/wo Sage), WP Starter, Corcel, or custom integrations.

Features

  • Clean Laravel Syntax - Use Log::error(), Log::info(), etc. anywhere in your code
  • Graceful Degradation - Works with or without Wonolog active
  • wpify/scoper Support - Automatically detects scoped Wonolog namespace
  • Zero Configuration - Works out of the box with sensible defaults
  • Flexible Propagation - Control whether to stop at Wonolog or continue to other handlers

Requirements

  • PHP >= 8.2
  • WordPress >= 6.0
  • Laravel Illuminate/Support ^10.0|^11.0|^12.0|^13.0
  • Monolog ^2.0|^3.0

Note: Inpsyde's Wonolog is not required for the package to work. Without Wonolog, logs gracefully pass through to other handlers in your stack (e.g., file logging).

Installation

1. Install the handler package

In your Laravel + WordPress project (Sage theme, WP Starter, etc.):

composer require wp-spaghetti/wonolog-handler

The package auto-registers via service provider discovery.

2. Install WP Spaghetti Wonolog mu-plugin (optional)

For email notifications, sensitive data filtering, and advanced logging features, install the WP Spaghetti Wonolog mu-plugin, that provides a complete logging solution with production-ready configuration.

See the WP Spaghetti Wonolog documentation for setup and configuration options.

3. Configure logging

Update your config/logging.php:

<?phpuseWpSpaghetti\WonologHandler\Handler\WonologHandler;
return [
'default' => env('LOG_CHANNEL', 'stack'),
'channels' => [
// Recommended: Stack with Wonolog + file backup'stack' => [
'driver' => 'stack',
'channels' => ['wonolog', 'single'],
'ignore_exceptions' => false,
],
// Wonolog channel'wonolog' => [
'driver' => 'monolog',
'handler' => WonologHandler::class,
'level' => env('LOG_LEVEL', 'debug'),
],
// File backup (optional but recommended)'single' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
],
],
];

See examples/logging.php for a complete configuration example.

Usage

Basic Logging

useIlluminate\Support\Facades\Log;
// Anywhere in your Laravel + WordPress code
Log::debug('Debugging information');
Log::info('Informational message');
Log::notice('Normal but significant event');
Log::warning('Warning condition');
Log::error('Error condition');
Log::critical('Critical condition');
Log::alert('Action must be taken immediately');
Log::emergency('System is unusable');

With Context

Log::error('Payment failed', [
'user_id' => $userId,
'amount' => $amount,
'error' => $exception->getMessage(),
]);
// Wonolog channels - use UPPERCASE by convention
Log::error('Security breach', [
'channel' => 'SECURITY', // ✅ Correct'ip' => $ipAddress,
]);
// Avoid lowercase - may not be tracked// Log::error('Breach', ['channel' => 'security']); // ❌ May not work

Channel Selection

// Use only Wonolog (no file backup)
Log::channel('wonolog')->error('Critical error');
// Use only file logging
Log::channel('single')->debug('Debug info');
// Use stack (Wonolog + file) - recommended
Log::channel('stack')->warning('Warning message');

Framework-Specific Examples

Sage Themes (Acorn)

// In app/Controllers/App.php or any controlleruseIlluminate\Support\Facades\Log;
publicfunctionindex()
{
Log::info('Page viewed', ['url' => request()->url()]);
return$this->view;
}

WP Starter

// In your custom plugins or themeuseIlluminate\Support\Facades\Log;
add_action('init', function() {
Log::info('WordPress initialized');
});

Corcel

useCorcel\Model\Post;
useIlluminate\Support\Facades\Log;
$posts = Post::published()->get();
Log::info('Fetched posts', ['count' => $posts->count()]);

See examples/usage.php for more real-world examples including WordPress hooks, WooCommerce integration, API logging, and performance monitoring.

Advanced Configuration

Publish Configuration

To customize settings, publish the config:

# Sage/Acorn
wp acorn vendor:publish --tag=wonolog-handler-config
# WP Starter (using Laravel's artisan)
php vendor/bin/wp-starter vendor:publish --tag=wonolog-handler-config

This creates config/wonolog.php in your project:

<?phpreturn [
// Custom Wonolog namespace (for wpify/scoper)'namespace' => env('WONOLOG_NAMESPACE', 'Inpsyde\\Wonolog'),
// Custom action hook (for wpify/scoper or custom naming)'action' => env('WONOLOG_ACTION', 'wonolog.log'),
// Stop propagation when Wonolog is active?'stop_propagation' => env('WONOLOG_STOP_PROPAGATION', false),
];

Custom Wonolog Namespace (for wpify/scoper)

If Wonolog is scoped, override the namespace:

Via config:

// config/wonolog.php'namespace' => 'WpSpaghetti\\Deps\\Inpsyde\\Wonolog',

Via filter:

add_filter('wonolog_handler.namespace', function () {
return'WpSpaghetti\\Deps\\Inpsyde\\Wonolog';
});

Via environment:

WONOLOG_NAMESPACE="WpSpaghetti\\Deps\\Inpsyde\\Wonolog"

Custom Action Hook (for wpify/scoper)

If Wonolog uses a custom action hook name:

Via config:

// config/wonolog.php'action' => 'custom_wonolog.log',

Via filter:

add_filter('wonolog_handler.action', function () {
return'custom_wonolog.log';
});

Via environment:

WONOLOG_ACTION="custom_wonolog.log"

Control Log Propagation

By default, logs continue to other handlers in the stack after Wonolog (allowing file backup). You can change this:

Stop at Wonolog (no file backup):

// config/wonolog.php'stop_propagation' => true,

Or via environment:

WONOLOG_STOP_PROPAGATION=true

Use cases:

  • false (default): Wonolog + file backup - recommended for production
  • true: Only Wonolog - if you don't want file logs and trust Wonolog completely

How It Works

Architecture

Laravel Log::error()
↓
Monolog LogRecord
↓
WonologHandler
↓ (if Wonolog active)
do_action('wonolog.log')
↓
Wonolog Processing
├─ Email notifications
├─ WordPress database
├─ Custom handlers
└─ Filtering/redaction
↓ (if stop_propagation=false)
Continue to next handler (file, Slack, etc.)

Technical Details

Channel Handling:

  • Wonolog expects channel at the top level of the action array, not inside context
  • If user provides 'channel' => 'SECURITY' in context, it's extracted and moved to top level
  • The channel is removed from context after extraction to avoid duplication
  • If no channel is provided, it's NOT passed to Wonolog (Wonolog uses its default: DEBUG)
  • Monolog's channel (e.g., stack, development) is NEVER used - it has nothing to do with Wonolog

Example behavior:

// User specifies Wonolog channel
Log::error('Error', ['channel' => 'SECURITY', 'ip' => '1.2.3.4']);
// Result: Channel = SECURITY, Context = ['ip' => '1.2.3.4'] (no 'channel' key)// No channel specified
Log::error('Error');
// Result: Channel = DEBUG (Wonolog's default), Context = [] (empty except datetime/extra)// Monolog channel is ignored
Log::channel('stack')->error('Error');
// Result: Channel = DEBUG (Wonolog's default), NOT 'stack'

PSR-3 Placeholder Compatibility:

  • The handler uses array format when calling do_action('wonolog.log', [...])
  • This forces Wonolog's HookLogFactory::fromArray() method instead of fromString()
  • Fixes PSR-3 placeholder substitution (e.g., {url}, {handle}) which breaks in fromString()
  • Compatible with all Wonolog v2.x and v3.x versions

Extra Data:

  • Monolog's extra data is passed as $context['extra'] (following Wonolog's convention)
  • Datetime is passed as $context['datetime'] for full compatibility

Graceful Degradation

Without Wonolog mu-plugin:

  • WonologHandler detects Wonolog is not active
  • Handler does nothing and returns false
  • Logs continue to other handlers (files, etc.)
  • No errors or warnings

With Wonolog mu-plugin:

  • Handler forwards logs to Wonolog
  • Wonolog processes with email, filtering, etc.
  • Logs optionally continue to file backup (based on stop_propagation)

Namespace Detection

The handler automatically detects Wonolog's namespace:

  1. Checks default Inpsyde\Wonolog
  2. Applies filter wonolog_handler_namespace
  3. Supports scoped namespaces from wpify/scoper
  4. Verifies Configurator::ACTION_SETUP was triggered
  5. Caches result for performance

Troubleshooting

Logs not appearing in Wonolog

Check if Wonolog is active:

useWpSpaghetti\WonologHandler\Support\WonologDetector;
$detector = app(WonologDetector::class);
if (!$detector->isActive()) {
echo"Wonolog is not active!";
echo"Namespace: " . $detector->getNamespace();
echo"Action: " . $detector->getAction();
}

Wrong namespace or action detected

Override via filter or config (see Advanced Configuration).

Channel-related issues

Understanding channel behavior:

  1. Custom Wonolog channel (when explicitly provided):

    Log::error('Security breach', ['channel' => 'SECURITY', 'ip' => '1.2.3.4']);
    // Email: Channel = SECURITY, Context = ['ip' => '1.2.3.4'] (no 'channel' key)
  2. Default Wonolog channel (when not provided):

    Log::error('Error');
    // Email: Channel = DEBUG (Wonolog's default)
    Log::channel('stack')->error('Error');
    // Email: Channel = DEBUG (Wonolog's default - Monolog channel is ignored)
    Log::channel('single')->error('Error');
    // Email: Channel = DEBUG (Wonolog's default - Monolog channel is ignored)
  3. Channel extraction:

    • If 'channel' is in context, it's extracted and passed to Wonolog at top level
    • The 'channel' key is removed from context to avoid duplication
    • This ensures channel appears only once in emails (as "Channel: XXX", not in context)
  4. Monolog vs Wonolog channels:

    • Monolog channels (development, stack, single) route logs in Laravel
    • Wonolog channels (DEBUG, SECURITY, HTTP) categorize logs in Wonolog
    • They are completely separate - Monolog channels are NOT sent to Wonolog
    • To set a Wonolog channel: Log::error('msg', ['channel' => 'SECURITY'])
  5. Channel naming conventions⚠️:

    • IMPORTANT: Wonolog uses UPPERCASE channel names by convention
    • Standard Wonolog channels: DEBUG, SECURITY, HTTP, DB, PHP-ERROR, CRON, etc.
    • Using lowercase (e.g., 'security' instead of 'SECURITY') may cause logs not to be tracked
    • Using non-configured channels (e.g., 'FOO') may also not be tracked
    • This behavior depends on your Wonolog configuration and filters
    • Best practice: Always use UPPERCASE for channel names
    // ✅ Correct - uppercase
    Log::error('Breach', ['channel' => 'SECURITY']);
    // ❌ May not work - lowercase
    Log::error('Breach', ['channel' => 'security']);
    // ❌ May not work - non-configured channel
    Log::error('Error', ['channel' => 'FOO']);
  6. Custom channel names:

    • You can use custom channel names if configured in Wonolog
    • Examples: PAYMENT, API, WOOCOMMERCE, etc.
    • Make sure they're configured in your Wonolog setup
    • Always use UPPERCASE for consistency

Logs not in file backup

Ensure 'single' channel is in the stack:

'stack' => [
'driver' => 'stack',
'channels' => ['wonolog', 'single'], // ← Check this
],

And ensure stop_propagation is false (default).

Testing

composer test

More info

See LINKS file.

Changelog

Please see CHANGELOG for a detailed list of changes for each release.

We follow Semantic Versioning and use Conventional Commits to automatically generate our changelog.

Release Process

  • Major versions (1.0.0 → 2.0.0): Breaking changes
  • Minor versions (1.0.0 → 1.1.0): New features, backward compatible
  • Patch versions (1.0.0 → 1.0.1): Bug fixes, backward compatible

All releases are automatically created when changes are pushed to the main branch, based on commit message conventions.

Contributing

For your contributions please use:

See CONTRIBUTING for detailed guidelines.

Sponsor

Buy Me A Coffee

License

(ɔ) Copyleft 2026 Frugan.
GNU GPLv3, see LICENSE file.

About

Monolog handler that forwards Laravel logs to Inpsyde's Wonolog - works with any Laravel + WordPress setup: Acorn (w/wo Sage), WP Starter, Corcel, or custom integrations

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

PHP VersionPackagist DownloadsPackagist StarsGitHub Actions Workflow StatusCoverage StatusKnown VulnerabilitiesGitHub IssuesGitHub ReleaseLicense

Wonolog Handler

Monolog handler that forwards Laravel logs to Inpsyde's Wonolog - the professional WordPress logging solution.

Works with any Laravel + WordPress setup: Acorn (w/wo Sage), WP Starter, Corcel, or custom integrations.

Features

  • Clean Laravel Syntax - Use Log::error(), Log::info(), etc. anywhere in your code
  • Graceful Degradation - Works with or without Wonolog active
  • wpify/scoper Support - Automatically detects scoped Wonolog namespace
  • Zero Configuration - Works out of the box with sensible defaults
  • Flexible Propagation - Control whether to stop at Wonolog or continue to other handlers

Requirements

  • PHP >= 8.2
  • WordPress >= 6.0
  • Laravel Illuminate/Support ^10.0|^11.0|^12.0|^13.0
  • Monolog ^2.0|^3.0

Note: Inpsyde's Wonolog is not required for the package to work. Without Wonolog, logs gracefully pass through to other handlers in your stack (e.g., file logging).

Installation

1. Install the handler package

In your Laravel + WordPress project (Sage theme, WP Starter, etc.):

composer require wp-spaghetti/wonolog-handler

The package auto-registers via service provider discovery.

2. Install WP Spaghetti Wonolog mu-plugin (optional)

For email notifications, sensitive data filtering, and advanced logging features, install the WP Spaghetti Wonolog mu-plugin, that provides a complete logging solution with production-ready configuration.

See the WP Spaghetti Wonolog documentation for setup and configuration options.

3. Configure logging

Update your config/logging.php:

<?phpuseWpSpaghetti\WonologHandler\Handler\WonologHandler;
return [
'default' => env('LOG_CHANNEL', 'stack'),
'channels' => [
// Recommended: Stack with Wonolog + file backup'stack' => [
'driver' => 'stack',
'channels' => ['wonolog', 'single'],
'ignore_exceptions' => false,
],
// Wonolog channel'wonolog' => [
'driver' => 'monolog',
'handler' => WonologHandler::class,
'level' => env('LOG_LEVEL', 'debug'),
],
// File backup (optional but recommended)'single' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
],
],
];

See examples/logging.php for a complete configuration example.

Usage

Basic Logging

useIlluminate\Support\Facades\Log;
// Anywhere in your Laravel + WordPress code
Log::debug('Debugging information');
Log::info('Informational message');
Log::notice('Normal but significant event');
Log::warning('Warning condition');
Log::error('Error condition');
Log::critical('Critical condition');
Log::alert('Action must be taken immediately');
Log::emergency('System is unusable');

With Context

Log::error('Payment failed', [
'user_id' => $userId,
'amount' => $amount,
'error' => $exception->getMessage(),
]);
// Wonolog channels - use UPPERCASE by convention
Log::error('Security breach', [
'channel' => 'SECURITY', // ✅ Correct'ip' => $ipAddress,
]);
// Avoid lowercase - may not be tracked// Log::error('Breach', ['channel' => 'security']); // ❌ May not work

Channel Selection

// Use only Wonolog (no file backup)
Log::channel('wonolog')->error('Critical error');
// Use only file logging
Log::channel('single')->debug('Debug info');
// Use stack (Wonolog + file) - recommended
Log::channel('stack')->warning('Warning message');

Framework-Specific Examples

Sage Themes (Acorn)

// In app/Controllers/App.php or any controlleruseIlluminate\Support\Facades\Log;
publicfunctionindex()
{
Log::info('Page viewed', ['url' => request()->url()]);
return$this->view;
}

WP Starter

// In your custom plugins or themeuseIlluminate\Support\Facades\Log;
add_action('init', function() {
Log::info('WordPress initialized');
});

Corcel

useCorcel\Model\Post;
useIlluminate\Support\Facades\Log;
$posts = Post::published()->get();
Log::info('Fetched posts', ['count' => $posts->count()]);

See examples/usage.php for more real-world examples including WordPress hooks, WooCommerce integration, API logging, and performance monitoring.

Advanced Configuration

Publish Configuration

To customize settings, publish the config:

# Sage/Acorn
wp acorn vendor:publish --tag=wonolog-handler-config
# WP Starter (using Laravel's artisan)
php vendor/bin/wp-starter vendor:publish --tag=wonolog-handler-config

This creates config/wonolog.php in your project:

<?phpreturn [
// Custom Wonolog namespace (for wpify/scoper)'namespace' => env('WONOLOG_NAMESPACE', 'Inpsyde\\Wonolog'),
// Custom action hook (for wpify/scoper or custom naming)'action' => env('WONOLOG_ACTION', 'wonolog.log'),
// Stop propagation when Wonolog is active?'stop_propagation' => env('WONOLOG_STOP_PROPAGATION', false),
];

Custom Wonolog Namespace (for wpify/scoper)

If Wonolog is scoped, override the namespace:

Via config:

// config/wonolog.php'namespace' => 'WpSpaghetti\\Deps\\Inpsyde\\Wonolog',

Via filter:

add_filter('wonolog_handler.namespace', function () {
return'WpSpaghetti\\Deps\\Inpsyde\\Wonolog';
});

Via environment:

WONOLOG_NAMESPACE="WpSpaghetti\\Deps\\Inpsyde\\Wonolog"

Custom Action Hook (for wpify/scoper)

If Wonolog uses a custom action hook name:

Via config:

// config/wonolog.php'action' => 'custom_wonolog.log',

Via filter:

add_filter('wonolog_handler.action', function () {
return'custom_wonolog.log';
});

Via environment:

WONOLOG_ACTION="custom_wonolog.log"

Control Log Propagation

By default, logs continue to other handlers in the stack after Wonolog (allowing file backup). You can change this:

Stop at Wonolog (no file backup):

// config/wonolog.php'stop_propagation' => true,

Or via environment:

WONOLOG_STOP_PROPAGATION=true

Use cases:

  • false (default): Wonolog + file backup - recommended for production
  • true: Only Wonolog - if you don't want file logs and trust Wonolog completely

How It Works

Architecture

Laravel Log::error()
↓
Monolog LogRecord
↓
WonologHandler
↓ (if Wonolog active)
do_action('wonolog.log')
↓
Wonolog Processing
├─ Email notifications
├─ WordPress database
├─ Custom handlers
└─ Filtering/redaction
↓ (if stop_propagation=false)
Continue to next handler (file, Slack, etc.)

Technical Details

Channel Handling:

  • Wonolog expects channel at the top level of the action array, not inside context
  • If user provides 'channel' => 'SECURITY' in context, it's extracted and moved to top level
  • The channel is removed from context after extraction to avoid duplication
  • If no channel is provided, it's NOT passed to Wonolog (Wonolog uses its default: DEBUG)
  • Monolog's channel (e.g., stack, development) is NEVER used - it has nothing to do with Wonolog

Example behavior:

// User specifies Wonolog channel
Log::error('Error', ['channel' => 'SECURITY', 'ip' => '1.2.3.4']);
// Result: Channel = SECURITY, Context = ['ip' => '1.2.3.4'] (no 'channel' key)// No channel specified
Log::error('Error');
// Result: Channel = DEBUG (Wonolog's default), Context = [] (empty except datetime/extra)// Monolog channel is ignored
Log::channel('stack')->error('Error');
// Result: Channel = DEBUG (Wonolog's default), NOT 'stack'

PSR-3 Placeholder Compatibility:

  • The handler uses array format when calling do_action('wonolog.log', [...])
  • This forces Wonolog's HookLogFactory::fromArray() method instead of fromString()
  • Fixes PSR-3 placeholder substitution (e.g., {url}, {handle}) which breaks in fromString()
  • Compatible with all Wonolog v2.x and v3.x versions

Extra Data:

  • Monolog's extra data is passed as $context['extra'] (following Wonolog's convention)
  • Datetime is passed as $context['datetime'] for full compatibility

Graceful Degradation

Without Wonolog mu-plugin:

  • WonologHandler detects Wonolog is not active
  • Handler does nothing and returns false
  • Logs continue to other handlers (files, etc.)
  • No errors or warnings

With Wonolog mu-plugin:

  • Handler forwards logs to Wonolog
  • Wonolog processes with email, filtering, etc.
  • Logs optionally continue to file backup (based on stop_propagation)

Namespace Detection

The handler automatically detects Wonolog's namespace:

  1. Checks default Inpsyde\Wonolog
  2. Applies filter wonolog_handler_namespace
  3. Supports scoped namespaces from wpify/scoper
  4. Verifies Configurator::ACTION_SETUP was triggered
  5. Caches result for performance

Troubleshooting

Logs not appearing in Wonolog

Check if Wonolog is active:

useWpSpaghetti\WonologHandler\Support\WonologDetector;
$detector = app(WonologDetector::class);
if (!$detector->isActive()) {
echo"Wonolog is not active!";
echo"Namespace: " . $detector->getNamespace();
echo"Action: " . $detector->getAction();
}

Wrong namespace or action detected

Override via filter or config (see Advanced Configuration).

Channel-related issues

Understanding channel behavior:

  1. Custom Wonolog channel (when explicitly provided):

    Log::error('Security breach', ['channel' => 'SECURITY', 'ip' => '1.2.3.4']);
    // Email: Channel = SECURITY, Context = ['ip' => '1.2.3.4'] (no 'channel' key)
  2. Default Wonolog channel (when not provided):

    Log::error('Error');
    // Email: Channel = DEBUG (Wonolog's default)
    Log::channel('stack')->error('Error');
    // Email: Channel = DEBUG (Wonolog's default - Monolog channel is ignored)
    Log::channel('single')->error('Error');
    // Email: Channel = DEBUG (Wonolog's default - Monolog channel is ignored)
  3. Channel extraction:

    • If 'channel' is in context, it's extracted and passed to Wonolog at top level
    • The 'channel' key is removed from context to avoid duplication
    • This ensures channel appears only once in emails (as "Channel: XXX", not in context)
  4. Monolog vs Wonolog channels:

    • Monolog channels (development, stack, single) route logs in Laravel
    • Wonolog channels (DEBUG, SECURITY, HTTP) categorize logs in Wonolog
    • They are completely separate - Monolog channels are NOT sent to Wonolog
    • To set a Wonolog channel: Log::error('msg', ['channel' => 'SECURITY'])
  5. Channel naming conventions⚠️:

    • IMPORTANT: Wonolog uses UPPERCASE channel names by convention
    • Standard Wonolog channels: DEBUG, SECURITY, HTTP, DB, PHP-ERROR, CRON, etc.
    • Using lowercase (e.g., 'security' instead of 'SECURITY') may cause logs not to be tracked
    • Using non-configured channels (e.g., 'FOO') may also not be tracked
    • This behavior depends on your Wonolog configuration and filters
    • Best practice: Always use UPPERCASE for channel names
    // ✅ Correct - uppercase
    Log::error('Breach', ['channel' => 'SECURITY']);
    // ❌ May not work - lowercase
    Log::error('Breach', ['channel' => 'security']);
    // ❌ May not work - non-configured channel
    Log::error('Error', ['channel' => 'FOO']);
  6. Custom channel names:

    • You can use custom channel names if configured in Wonolog
    • Examples: PAYMENT, API, WOOCOMMERCE, etc.
    • Make sure they're configured in your Wonolog setup
    • Always use UPPERCASE for consistency

Logs not in file backup

Ensure 'single' channel is in the stack:

'stack' => [
'driver' => 'stack',
'channels' => ['wonolog', 'single'], // ← Check this
],

And ensure stop_propagation is false (default).

Testing

composer test

More info

See LINKS file.

Changelog

Please see CHANGELOG for a detailed list of changes for each release.

We follow Semantic Versioning and use Conventional Commits to automatically generate our changelog.

Release Process

  • Major versions (1.0.0 → 2.0.0): Breaking changes
  • Minor versions (1.0.0 → 1.1.0): New features, backward compatible
  • Patch versions (1.0.0 → 1.0.1): Bug fixes, backward compatible

All releases are automatically created when changes are pushed to the main branch, based on commit message conventions.

Contributing

For your contributions please use:

See CONTRIBUTING for detailed guidelines.

Sponsor

Buy Me A Coffee

License

(ɔ) Copyleft 2026 Frugan.
GNU GPLv3, see LICENSE file.

About

Monolog handler that forwards Laravel logs to Inpsyde's Wonolog - works with any Laravel + WordPress setup: Acorn (w/wo Sage), WP Starter, Corcel, or custom integrations

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

PHP VersionPackagist DownloadsPackagist StarsGitHub Actions Workflow StatusCoverage StatusKnown VulnerabilitiesGitHub IssuesGitHub ReleaseLicense

Wonolog Handler

Monolog handler that forwards Laravel logs to Inpsyde's Wonolog - the professional WordPress logging solution.

Works with any Laravel + WordPress setup: Acorn (w/wo Sage), WP Starter, Corcel, or custom integrations.

Features

  • Clean Laravel Syntax - Use Log::error(), Log::info(), etc. anywhere in your code
  • Graceful Degradation - Works with or without Wonolog active
  • wpify/scoper Support - Automatically detects scoped Wonolog namespace
  • Zero Configuration - Works out of the box with sensible defaults
  • Flexible Propagation - Control whether to stop at Wonolog or continue to other handlers

Requirements

  • PHP >= 8.2
  • WordPress >= 6.0
  • Laravel Illuminate/Support ^10.0|^11.0|^12.0|^13.0
  • Monolog ^2.0|^3.0

Note: Inpsyde's Wonolog is not required for the package to work. Without Wonolog, logs gracefully pass through to other handlers in your stack (e.g., file logging).

Installation

1. Install the handler package

In your Laravel + WordPress project (Sage theme, WP Starter, etc.):

composer require wp-spaghetti/wonolog-handler

The package auto-registers via service provider discovery.

2. Install WP Spaghetti Wonolog mu-plugin (optional)

For email notifications, sensitive data filtering, and advanced logging features, install the WP Spaghetti Wonolog mu-plugin, that provides a complete logging solution with production-ready configuration.

See the WP Spaghetti Wonolog documentation for setup and configuration options.

3. Configure logging

Update your config/logging.php:

<?phpuseWpSpaghetti\WonologHandler\Handler\WonologHandler;
return [
'default' => env('LOG_CHANNEL', 'stack'),
'channels' => [
// Recommended: Stack with Wonolog + file backup'stack' => [
'driver' => 'stack',
'channels' => ['wonolog', 'single'],
'ignore_exceptions' => false,
],
// Wonolog channel'wonolog' => [
'driver' => 'monolog',
'handler' => WonologHandler::class,
'level' => env('LOG_LEVEL', 'debug'),
],
// File backup (optional but recommended)'single' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
],
],
];

See examples/logging.php for a complete configuration example.

Usage

Basic Logging

useIlluminate\Support\Facades\Log;
// Anywhere in your Laravel + WordPress code
Log::debug('Debugging information');
Log::info('Informational message');
Log::notice('Normal but significant event');
Log::warning('Warning condition');
Log::error('Error condition');
Log::critical('Critical condition');
Log::alert('Action must be taken immediately');
Log::emergency('System is unusable');

With Context

Log::error('Payment failed', [
'user_id' => $userId,
'amount' => $amount,
'error' => $exception->getMessage(),
]);
// Wonolog channels - use UPPERCASE by convention
Log::error('Security breach', [
'channel' => 'SECURITY', // ✅ Correct'ip' => $ipAddress,
]);
// Avoid lowercase - may not be tracked// Log::error('Breach', ['channel' => 'security']); // ❌ May not work

Channel Selection

// Use only Wonolog (no file backup)
Log::channel('wonolog')->error('Critical error');
// Use only file logging
Log::channel('single')->debug('Debug info');
// Use stack (Wonolog + file) - recommended
Log::channel('stack')->warning('Warning message');

Framework-Specific Examples

Sage Themes (Acorn)

// In app/Controllers/App.php or any controlleruseIlluminate\Support\Facades\Log;
publicfunctionindex()
{
Log::info('Page viewed', ['url' => request()->url()]);
return$this->view;
}

WP Starter

// In your custom plugins or themeuseIlluminate\Support\Facades\Log;
add_action('init', function() {
Log::info('WordPress initialized');
});

Corcel

useCorcel\Model\Post;
useIlluminate\Support\Facades\Log;
$posts = Post::published()->get();
Log::info('Fetched posts', ['count' => $posts->count()]);

See examples/usage.php for more real-world examples including WordPress hooks, WooCommerce integration, API logging, and performance monitoring.

Advanced Configuration

Publish Configuration

To customize settings, publish the config:

# Sage/Acorn
wp acorn vendor:publish --tag=wonolog-handler-config
# WP Starter (using Laravel's artisan)
php vendor/bin/wp-starter vendor:publish --tag=wonolog-handler-config

This creates config/wonolog.php in your project:

<?phpreturn [
// Custom Wonolog namespace (for wpify/scoper)'namespace' => env('WONOLOG_NAMESPACE', 'Inpsyde\\Wonolog'),
// Custom action hook (for wpify/scoper or custom naming)'action' => env('WONOLOG_ACTION', 'wonolog.log'),
// Stop propagation when Wonolog is active?'stop_propagation' => env('WONOLOG_STOP_PROPAGATION', false),
];

Custom Wonolog Namespace (for wpify/scoper)

If Wonolog is scoped, override the namespace:

Via config:

// config/wonolog.php'namespace' => 'WpSpaghetti\\Deps\\Inpsyde\\Wonolog',

Via filter:

add_filter('wonolog_handler.namespace', function () {
return'WpSpaghetti\\Deps\\Inpsyde\\Wonolog';
});

Via environment:

WONOLOG_NAMESPACE="WpSpaghetti\\Deps\\Inpsyde\\Wonolog"

Custom Action Hook (for wpify/scoper)

If Wonolog uses a custom action hook name:

Via config:

// config/wonolog.php'action' => 'custom_wonolog.log',

Via filter:

add_filter('wonolog_handler.action', function () {
return'custom_wonolog.log';
});

Via environment:

WONOLOG_ACTION="custom_wonolog.log"

Control Log Propagation

By default, logs continue to other handlers in the stack after Wonolog (allowing file backup). You can change this:

Stop at Wonolog (no file backup):

// config/wonolog.php'stop_propagation' => true,

Or via environment:

WONOLOG_STOP_PROPAGATION=true

Use cases:

  • false (default): Wonolog + file backup - recommended for production
  • true: Only Wonolog - if you don't want file logs and trust Wonolog completely

How It Works

Architecture

Laravel Log::error()
↓
Monolog LogRecord
↓
WonologHandler
↓ (if Wonolog active)
do_action('wonolog.log')
↓
Wonolog Processing
├─ Email notifications
├─ WordPress database
├─ Custom handlers
└─ Filtering/redaction
↓ (if stop_propagation=false)
Continue to next handler (file, Slack, etc.)

Technical Details

Channel Handling:

  • Wonolog expects channel at the top level of the action array, not inside context
  • If user provides 'channel' => 'SECURITY' in context, it's extracted and moved to top level
  • The channel is removed from context after extraction to avoid duplication
  • If no channel is provided, it's NOT passed to Wonolog (Wonolog uses its default: DEBUG)
  • Monolog's channel (e.g., stack, development) is NEVER used - it has nothing to do with Wonolog

Example behavior:

// User specifies Wonolog channel
Log::error('Error', ['channel' => 'SECURITY', 'ip' => '1.2.3.4']);
// Result: Channel = SECURITY, Context = ['ip' => '1.2.3.4'] (no 'channel' key)// No channel specified
Log::error('Error');
// Result: Channel = DEBUG (Wonolog's default), Context = [] (empty except datetime/extra)// Monolog channel is ignored
Log::channel('stack')->error('Error');
// Result: Channel = DEBUG (Wonolog's default), NOT 'stack'

PSR-3 Placeholder Compatibility:

  • The handler uses array format when calling do_action('wonolog.log', [...])
  • This forces Wonolog's HookLogFactory::fromArray() method instead of fromString()
  • Fixes PSR-3 placeholder substitution (e.g., {url}, {handle}) which breaks in fromString()
  • Compatible with all Wonolog v2.x and v3.x versions

Extra Data:

  • Monolog's extra data is passed as $context['extra'] (following Wonolog's convention)
  • Datetime is passed as $context['datetime'] for full compatibility

Graceful Degradation

Without Wonolog mu-plugin:

  • WonologHandler detects Wonolog is not active
  • Handler does nothing and returns false
  • Logs continue to other handlers (files, etc.)
  • No errors or warnings

With Wonolog mu-plugin:

  • Handler forwards logs to Wonolog
  • Wonolog processes with email, filtering, etc.
  • Logs optionally continue to file backup (based on stop_propagation)

Namespace Detection

The handler automatically detects Wonolog's namespace:

  1. Checks default Inpsyde\Wonolog
  2. Applies filter wonolog_handler_namespace
  3. Supports scoped namespaces from wpify/scoper
  4. Verifies Configurator::ACTION_SETUP was triggered
  5. Caches result for performance

Troubleshooting

Logs not appearing in Wonolog

Check if Wonolog is active:

useWpSpaghetti\WonologHandler\Support\WonologDetector;
$detector = app(WonologDetector::class);
if (!$detector->isActive()) {
echo"Wonolog is not active!";
echo"Namespace: " . $detector->getNamespace();
echo"Action: " . $detector->getAction();
}

Wrong namespace or action detected

Override via filter or config (see Advanced Configuration).

Channel-related issues

Understanding channel behavior:

  1. Custom Wonolog channel (when explicitly provided):

    Log::error('Security breach', ['channel' => 'SECURITY', 'ip' => '1.2.3.4']);
    // Email: Channel = SECURITY, Context = ['ip' => '1.2.3.4'] (no 'channel' key)
  2. Default Wonolog channel (when not provided):

    Log::error('Error');
    // Email: Channel = DEBUG (Wonolog's default)
    Log::channel('stack')->error('Error');
    // Email: Channel = DEBUG (Wonolog's default - Monolog channel is ignored)
    Log::channel('single')->error('Error');
    // Email: Channel = DEBUG (Wonolog's default - Monolog channel is ignored)
  3. Channel extraction:

    • If 'channel' is in context, it's extracted and passed to Wonolog at top level
    • The 'channel' key is removed from context to avoid duplication
    • This ensures channel appears only once in emails (as "Channel: XXX", not in context)
  4. Monolog vs Wonolog channels:

    • Monolog channels (development, stack, single) route logs in Laravel
    • Wonolog channels (DEBUG, SECURITY, HTTP) categorize logs in Wonolog
    • They are completely separate - Monolog channels are NOT sent to Wonolog
    • To set a Wonolog channel: Log::error('msg', ['channel' => 'SECURITY'])
  5. Channel naming conventions⚠️:

    • IMPORTANT: Wonolog uses UPPERCASE channel names by convention
    • Standard Wonolog channels: DEBUG, SECURITY, HTTP, DB, PHP-ERROR, CRON, etc.
    • Using lowercase (e.g., 'security' instead of 'SECURITY') may cause logs not to be tracked
    • Using non-configured channels (e.g., 'FOO') may also not be tracked
    • This behavior depends on your Wonolog configuration and filters
    • Best practice: Always use UPPERCASE for channel names
    // ✅ Correct - uppercase
    Log::error('Breach', ['channel' => 'SECURITY']);
    // ❌ May not work - lowercase
    Log::error('Breach', ['channel' => 'security']);
    // ❌ May not work - non-configured channel
    Log::error('Error', ['channel' => 'FOO']);
  6. Custom channel names:

    • You can use custom channel names if configured in Wonolog
    • Examples: PAYMENT, API, WOOCOMMERCE, etc.
    • Make sure they're configured in your Wonolog setup
    • Always use UPPERCASE for consistency

Logs not in file backup

Ensure 'single' channel is in the stack:

'stack' => [
'driver' => 'stack',
'channels' => ['wonolog', 'single'], // ← Check this
],

And ensure stop_propagation is false (default).

Testing

composer test

More info

See LINKS file.

Changelog

Please see CHANGELOG for a detailed list of changes for each release.

We follow Semantic Versioning and use Conventional Commits to automatically generate our changelog.

Release Process

  • Major versions (1.0.0 → 2.0.0): Breaking changes
  • Minor versions (1.0.0 → 1.1.0): New features, backward compatible
  • Patch versions (1.0.0 → 1.0.1): Bug fixes, backward compatible

All releases are automatically created when changes are pushed to the main branch, based on commit message conventions.

Contributing

For your contributions please use:

See CONTRIBUTING for detailed guidelines.

Sponsor

Buy Me A Coffee

License

(ɔ) Copyleft 2026 Frugan.
GNU GPLv3, see LICENSE file.

About

Monolog handler that forwards Laravel logs to Inpsyde's Wonolog - works with any Laravel + WordPress setup: Acorn (w/wo Sage), WP Starter, Corcel, or custom integrations

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

PHP VersionPackagist DownloadsPackagist StarsGitHub Actions Workflow StatusCoverage StatusKnown VulnerabilitiesGitHub IssuesGitHub ReleaseLicense

Wonolog Handler

Monolog handler that forwards Laravel logs to Inpsyde's Wonolog - the professional WordPress logging solution.

Works with any Laravel + WordPress setup: Acorn (w/wo Sage), WP Starter, Corcel, or custom integrations.

Features

  • Clean Laravel Syntax - Use Log::error(), Log::info(), etc. anywhere in your code
  • Graceful Degradation - Works with or without Wonolog active
  • wpify/scoper Support - Automatically detects scoped Wonolog namespace
  • Zero Configuration - Works out of the box with sensible defaults
  • Flexible Propagation - Control whether to stop at Wonolog or continue to other handlers

Requirements

  • PHP >= 8.2
  • WordPress >= 6.0
  • Laravel Illuminate/Support ^10.0|^11.0|^12.0|^13.0
  • Monolog ^2.0|^3.0

Note: Inpsyde's Wonolog is not required for the package to work. Without Wonolog, logs gracefully pass through to other handlers in your stack (e.g., file logging).

Installation

1. Install the handler package

In your Laravel + WordPress project (Sage theme, WP Starter, etc.):

composer require wp-spaghetti/wonolog-handler

The package auto-registers via service provider discovery.

2. Install WP Spaghetti Wonolog mu-plugin (optional)

For email notifications, sensitive data filtering, and advanced logging features, install the WP Spaghetti Wonolog mu-plugin, that provides a complete logging solution with production-ready configuration.

See the WP Spaghetti Wonolog documentation for setup and configuration options.

3. Configure logging

Update your config/logging.php:

<?phpuseWpSpaghetti\WonologHandler\Handler\WonologHandler;
return [
'default' => env('LOG_CHANNEL', 'stack'),
'channels' => [
// Recommended: Stack with Wonolog + file backup'stack' => [
'driver' => 'stack',
'channels' => ['wonolog', 'single'],
'ignore_exceptions' => false,
],
// Wonolog channel'wonolog' => [
'driver' => 'monolog',
'handler' => WonologHandler::class,
'level' => env('LOG_LEVEL', 'debug'),
],
// File backup (optional but recommended)'single' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
],
],
];

See examples/logging.php for a complete configuration example.

Usage

Basic Logging

useIlluminate\Support\Facades\Log;
// Anywhere in your Laravel + WordPress code
Log::debug('Debugging information');
Log::info('Informational message');
Log::notice('Normal but significant event');
Log::warning('Warning condition');
Log::error('Error condition');
Log::critical('Critical condition');
Log::alert('Action must be taken immediately');
Log::emergency('System is unusable');

With Context

Log::error('Payment failed', [
'user_id' => $userId,
'amount' => $amount,
'error' => $exception->getMessage(),
]);
// Wonolog channels - use UPPERCASE by convention
Log::error('Security breach', [
'channel' => 'SECURITY', // ✅ Correct'ip' => $ipAddress,
]);
// Avoid lowercase - may not be tracked// Log::error('Breach', ['channel' => 'security']); // ❌ May not work

Channel Selection

// Use only Wonolog (no file backup)
Log::channel('wonolog')->error('Critical error');
// Use only file logging
Log::channel('single')->debug('Debug info');
// Use stack (Wonolog + file) - recommended
Log::channel('stack')->warning('Warning message');

Framework-Specific Examples

Sage Themes (Acorn)

// In app/Controllers/App.php or any controlleruseIlluminate\Support\Facades\Log;
publicfunctionindex()
{
Log::info('Page viewed', ['url' => request()->url()]);
return$this->view;
}

WP Starter

// In your custom plugins or themeuseIlluminate\Support\Facades\Log;
add_action('init', function() {
Log::info('WordPress initialized');
});

Corcel

useCorcel\Model\Post;
useIlluminate\Support\Facades\Log;
$posts = Post::published()->get();
Log::info('Fetched posts', ['count' => $posts->count()]);

See examples/usage.php for more real-world examples including WordPress hooks, WooCommerce integration, API logging, and performance monitoring.

Advanced Configuration

Publish Configuration

To customize settings, publish the config:

# Sage/Acorn
wp acorn vendor:publish --tag=wonolog-handler-config
# WP Starter (using Laravel's artisan)
php vendor/bin/wp-starter vendor:publish --tag=wonolog-handler-config

This creates config/wonolog.php in your project:

<?phpreturn [
// Custom Wonolog namespace (for wpify/scoper)'namespace' => env('WONOLOG_NAMESPACE', 'Inpsyde\\Wonolog'),
// Custom action hook (for wpify/scoper or custom naming)'action' => env('WONOLOG_ACTION', 'wonolog.log'),
// Stop propagation when Wonolog is active?'stop_propagation' => env('WONOLOG_STOP_PROPAGATION', false),
];

Custom Wonolog Namespace (for wpify/scoper)

If Wonolog is scoped, override the namespace:

Via config:

// config/wonolog.php'namespace' => 'WpSpaghetti\\Deps\\Inpsyde\\Wonolog',

Via filter:

add_filter('wonolog_handler.namespace', function () {
return'WpSpaghetti\\Deps\\Inpsyde\\Wonolog';
});

Via environment:

WONOLOG_NAMESPACE="WpSpaghetti\\Deps\\Inpsyde\\Wonolog"

Custom Action Hook (for wpify/scoper)

If Wonolog uses a custom action hook name:

Via config:

// config/wonolog.php'action' => 'custom_wonolog.log',

Via filter:

add_filter('wonolog_handler.action', function () {
return'custom_wonolog.log';
});

Via environment:

WONOLOG_ACTION="custom_wonolog.log"

Control Log Propagation

By default, logs continue to other handlers in the stack after Wonolog (allowing file backup). You can change this:

Stop at Wonolog (no file backup):

// config/wonolog.php'stop_propagation' => true,

Or via environment:

WONOLOG_STOP_PROPAGATION=true

Use cases:

  • false (default): Wonolog + file backup - recommended for production
  • true: Only Wonolog - if you don't want file logs and trust Wonolog completely

How It Works

Architecture

Laravel Log::error()
↓
Monolog LogRecord
↓
WonologHandler
↓ (if Wonolog active)
do_action('wonolog.log')
↓
Wonolog Processing
├─ Email notifications
├─ WordPress database
├─ Custom handlers
└─ Filtering/redaction
↓ (if stop_propagation=false)
Continue to next handler (file, Slack, etc.)

Technical Details

Channel Handling:

  • Wonolog expects channel at the top level of the action array, not inside context
  • If user provides 'channel' => 'SECURITY' in context, it's extracted and moved to top level
  • The channel is removed from context after extraction to avoid duplication
  • If no channel is provided, it's NOT passed to Wonolog (Wonolog uses its default: DEBUG)
  • Monolog's channel (e.g., stack, development) is NEVER used - it has nothing to do with Wonolog

Example behavior:

// User specifies Wonolog channel
Log::error('Error', ['channel' => 'SECURITY', 'ip' => '1.2.3.4']);
// Result: Channel = SECURITY, Context = ['ip' => '1.2.3.4'] (no 'channel' key)// No channel specified
Log::error('Error');
// Result: Channel = DEBUG (Wonolog's default), Context = [] (empty except datetime/extra)// Monolog channel is ignored
Log::channel('stack')->error('Error');
// Result: Channel = DEBUG (Wonolog's default), NOT 'stack'

PSR-3 Placeholder Compatibility:

  • The handler uses array format when calling do_action('wonolog.log', [...])
  • This forces Wonolog's HookLogFactory::fromArray() method instead of fromString()
  • Fixes PSR-3 placeholder substitution (e.g., {url}, {handle}) which breaks in fromString()
  • Compatible with all Wonolog v2.x and v3.x versions

Extra Data:

  • Monolog's extra data is passed as $context['extra'] (following Wonolog's convention)
  • Datetime is passed as $context['datetime'] for full compatibility

Graceful Degradation

Without Wonolog mu-plugin:

  • WonologHandler detects Wonolog is not active
  • Handler does nothing and returns false
  • Logs continue to other handlers (files, etc.)
  • No errors or warnings

With Wonolog mu-plugin:

  • Handler forwards logs to Wonolog
  • Wonolog processes with email, filtering, etc.
  • Logs optionally continue to file backup (based on stop_propagation)

Namespace Detection

The handler automatically detects Wonolog's namespace:

  1. Checks default Inpsyde\Wonolog
  2. Applies filter wonolog_handler_namespace
  3. Supports scoped namespaces from wpify/scoper
  4. Verifies Configurator::ACTION_SETUP was triggered
  5. Caches result for performance

Troubleshooting

Logs not appearing in Wonolog

Check if Wonolog is active:

useWpSpaghetti\WonologHandler\Support\WonologDetector;
$detector = app(WonologDetector::class);
if (!$detector->isActive()) {
echo"Wonolog is not active!";
echo"Namespace: " . $detector->getNamespace();
echo"Action: " . $detector->getAction();
}

Wrong namespace or action detected

Override via filter or config (see Advanced Configuration).

Channel-related issues

Understanding channel behavior:

  1. Custom Wonolog channel (when explicitly provided):

    Log::error('Security breach', ['channel' => 'SECURITY', 'ip' => '1.2.3.4']);
    // Email: Channel = SECURITY, Context = ['ip' => '1.2.3.4'] (no 'channel' key)
  2. Default Wonolog channel (when not provided):

    Log::error('Error');
    // Email: Channel = DEBUG (Wonolog's default)
    Log::channel('stack')->error('Error');
    // Email: Channel = DEBUG (Wonolog's default - Monolog channel is ignored)
    Log::channel('single')->error('Error');
    // Email: Channel = DEBUG (Wonolog's default - Monolog channel is ignored)
  3. Channel extraction:

    • If 'channel' is in context, it's extracted and passed to Wonolog at top level
    • The 'channel' key is removed from context to avoid duplication
    • This ensures channel appears only once in emails (as "Channel: XXX", not in context)
  4. Monolog vs Wonolog channels:

    • Monolog channels (development, stack, single) route logs in Laravel
    • Wonolog channels (DEBUG, SECURITY, HTTP) categorize logs in Wonolog
    • They are completely separate - Monolog channels are NOT sent to Wonolog
    • To set a Wonolog channel: Log::error('msg', ['channel' => 'SECURITY'])
  5. Channel naming conventions⚠️:

    • IMPORTANT: Wonolog uses UPPERCASE channel names by convention
    • Standard Wonolog channels: DEBUG, SECURITY, HTTP, DB, PHP-ERROR, CRON, etc.
    • Using lowercase (e.g., 'security' instead of 'SECURITY') may cause logs not to be tracked
    • Using non-configured channels (e.g., 'FOO') may also not be tracked
    • This behavior depends on your Wonolog configuration and filters
    • Best practice: Always use UPPERCASE for channel names
    // ✅ Correct - uppercase
    Log::error('Breach', ['channel' => 'SECURITY']);
    // ❌ May not work - lowercase
    Log::error('Breach', ['channel' => 'security']);
    // ❌ May not work - non-configured channel
    Log::error('Error', ['channel' => 'FOO']);
  6. Custom channel names:

    • You can use custom channel names if configured in Wonolog
    • Examples: PAYMENT, API, WOOCOMMERCE, etc.
    • Make sure they're configured in your Wonolog setup
    • Always use UPPERCASE for consistency

Logs not in file backup

Ensure 'single' channel is in the stack:

'stack' => [
'driver' => 'stack',
'channels' => ['wonolog', 'single'], // ← Check this
],

And ensure stop_propagation is false (default).

Testing

composer test

More info

See LINKS file.

Changelog

Please see CHANGELOG for a detailed list of changes for each release.

We follow Semantic Versioning and use Conventional Commits to automatically generate our changelog.

Release Process

  • Major versions (1.0.0 → 2.0.0): Breaking changes
  • Minor versions (1.0.0 → 1.1.0): New features, backward compatible
  • Patch versions (1.0.0 → 1.0.1): Bug fixes, backward compatible

All releases are automatically created when changes are pushed to the main branch, based on commit message conventions.

Contributing

For your contributions please use:

See CONTRIBUTING for detailed guidelines.

Sponsor

Buy Me A Coffee

License

(ɔ) Copyleft 2026 Frugan.
GNU GPLv3, see LICENSE file.

About

Monolog handler that forwards Laravel logs to Inpsyde's Wonolog - works with any Laravel + WordPress setup: Acorn (w/wo Sage), WP Starter, Corcel, or custom integrations

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

PHP VersionPackagist DownloadsPackagist StarsGitHub Actions Workflow StatusCoverage StatusKnown VulnerabilitiesGitHub IssuesGitHub ReleaseLicense

Wonolog Handler

Monolog handler that forwards Laravel logs to Inpsyde's Wonolog - the professional WordPress logging solution.

Works with any Laravel + WordPress setup: Acorn (w/wo Sage), WP Starter, Corcel, or custom integrations.

Features

  • Clean Laravel Syntax - Use Log::error(), Log::info(), etc. anywhere in your code
  • Graceful Degradation - Works with or without Wonolog active
  • wpify/scoper Support - Automatically detects scoped Wonolog namespace
  • Zero Configuration - Works out of the box with sensible defaults
  • Flexible Propagation - Control whether to stop at Wonolog or continue to other handlers

Requirements

  • PHP >= 8.2
  • WordPress >= 6.0
  • Laravel Illuminate/Support ^10.0|^11.0|^12.0|^13.0
  • Monolog ^2.0|^3.0

Note: Inpsyde's Wonolog is not required for the package to work. Without Wonolog, logs gracefully pass through to other handlers in your stack (e.g., file logging).

Installation

1. Install the handler package

In your Laravel + WordPress project (Sage theme, WP Starter, etc.):

composer require wp-spaghetti/wonolog-handler

The package auto-registers via service provider discovery.

2. Install WP Spaghetti Wonolog mu-plugin (optional)

For email notifications, sensitive data filtering, and advanced logging features, install the WP Spaghetti Wonolog mu-plugin, that provides a complete logging solution with production-ready configuration.

See the WP Spaghetti Wonolog documentation for setup and configuration options.

3. Configure logging

Update your config/logging.php:

<?phpuseWpSpaghetti\WonologHandler\Handler\WonologHandler;
return [
'default' => env('LOG_CHANNEL', 'stack'),
'channels' => [
// Recommended: Stack with Wonolog + file backup'stack' => [
'driver' => 'stack',
'channels' => ['wonolog', 'single'],
'ignore_exceptions' => false,
],
// Wonolog channel'wonolog' => [
'driver' => 'monolog',
'handler' => WonologHandler::class,
'level' => env('LOG_LEVEL', 'debug'),
],
// File backup (optional but recommended)'single' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
],
],
];

See examples/logging.php for a complete configuration example.

Usage

Basic Logging

useIlluminate\Support\Facades\Log;
// Anywhere in your Laravel + WordPress code
Log::debug('Debugging information');
Log::info('Informational message');
Log::notice('Normal but significant event');
Log::warning('Warning condition');
Log::error('Error condition');
Log::critical('Critical condition');
Log::alert('Action must be taken immediately');
Log::emergency('System is unusable');

With Context

Log::error('Payment failed', [
'user_id' => $userId,
'amount' => $amount,
'error' => $exception->getMessage(),
]);
// Wonolog channels - use UPPERCASE by convention
Log::error('Security breach', [
'channel' => 'SECURITY', // ✅ Correct'ip' => $ipAddress,
]);
// Avoid lowercase - may not be tracked// Log::error('Breach', ['channel' => 'security']); // ❌ May not work

Channel Selection

// Use only Wonolog (no file backup)
Log::channel('wonolog')->error('Critical error');
// Use only file logging
Log::channel('single')->debug('Debug info');
// Use stack (Wonolog + file) - recommended
Log::channel('stack')->warning('Warning message');

Framework-Specific Examples

Sage Themes (Acorn)

// In app/Controllers/App.php or any controlleruseIlluminate\Support\Facades\Log;
publicfunctionindex()
{
Log::info('Page viewed', ['url' => request()->url()]);
return$this->view;
}

WP Starter

// In your custom plugins or themeuseIlluminate\Support\Facades\Log;
add_action('init', function() {
Log::info('WordPress initialized');
});

Corcel

useCorcel\Model\Post;
useIlluminate\Support\Facades\Log;
$posts = Post::published()->get();
Log::info('Fetched posts', ['count' => $posts->count()]);

See examples/usage.php for more real-world examples including WordPress hooks, WooCommerce integration, API logging, and performance monitoring.

Advanced Configuration

Publish Configuration

To customize settings, publish the config:

# Sage/Acorn
wp acorn vendor:publish --tag=wonolog-handler-config
# WP Starter (using Laravel's artisan)
php vendor/bin/wp-starter vendor:publish --tag=wonolog-handler-config

This creates config/wonolog.php in your project:

<?phpreturn [
// Custom Wonolog namespace (for wpify/scoper)'namespace' => env('WONOLOG_NAMESPACE', 'Inpsyde\\Wonolog'),
// Custom action hook (for wpify/scoper or custom naming)'action' => env('WONOLOG_ACTION', 'wonolog.log'),
// Stop propagation when Wonolog is active?'stop_propagation' => env('WONOLOG_STOP_PROPAGATION', false),
];

Custom Wonolog Namespace (for wpify/scoper)

If Wonolog is scoped, override the namespace:

Via config:

// config/wonolog.php'namespace' => 'WpSpaghetti\\Deps\\Inpsyde\\Wonolog',

Via filter:

add_filter('wonolog_handler.namespace', function () {
return'WpSpaghetti\\Deps\\Inpsyde\\Wonolog';
});

Via environment:

WONOLOG_NAMESPACE="WpSpaghetti\\Deps\\Inpsyde\\Wonolog"

Custom Action Hook (for wpify/scoper)

If Wonolog uses a custom action hook name:

Via config:

// config/wonolog.php'action' => 'custom_wonolog.log',

Via filter:

add_filter('wonolog_handler.action', function () {
return'custom_wonolog.log';
});

Via environment:

WONOLOG_ACTION="custom_wonolog.log"

Control Log Propagation

By default, logs continue to other handlers in the stack after Wonolog (allowing file backup). You can change this:

Stop at Wonolog (no file backup):

// config/wonolog.php'stop_propagation' => true,

Or via environment:

WONOLOG_STOP_PROPAGATION=true

Use cases:

  • false (default): Wonolog + file backup - recommended for production
  • true: Only Wonolog - if you don't want file logs and trust Wonolog completely

How It Works

Architecture

Laravel Log::error()
↓
Monolog LogRecord
↓
WonologHandler
↓ (if Wonolog active)
do_action('wonolog.log')
↓
Wonolog Processing
├─ Email notifications
├─ WordPress database
├─ Custom handlers
└─ Filtering/redaction
↓ (if stop_propagation=false)
Continue to next handler (file, Slack, etc.)

Technical Details

Channel Handling:

  • Wonolog expects channel at the top level of the action array, not inside context
  • If user provides 'channel' => 'SECURITY' in context, it's extracted and moved to top level
  • The channel is removed from context after extraction to avoid duplication
  • If no channel is provided, it's NOT passed to Wonolog (Wonolog uses its default: DEBUG)
  • Monolog's channel (e.g., stack, development) is NEVER used - it has nothing to do with Wonolog

Example behavior:

// User specifies Wonolog channel
Log::error('Error', ['channel' => 'SECURITY', 'ip' => '1.2.3.4']);
// Result: Channel = SECURITY, Context = ['ip' => '1.2.3.4'] (no 'channel' key)// No channel specified
Log::error('Error');
// Result: Channel = DEBUG (Wonolog's default), Context = [] (empty except datetime/extra)// Monolog channel is ignored
Log::channel('stack')->error('Error');
// Result: Channel = DEBUG (Wonolog's default), NOT 'stack'

PSR-3 Placeholder Compatibility:

  • The handler uses array format when calling do_action('wonolog.log', [...])
  • This forces Wonolog's HookLogFactory::fromArray() method instead of fromString()
  • Fixes PSR-3 placeholder substitution (e.g., {url}, {handle}) which breaks in fromString()
  • Compatible with all Wonolog v2.x and v3.x versions

Extra Data:

  • Monolog's extra data is passed as $context['extra'] (following Wonolog's convention)
  • Datetime is passed as $context['datetime'] for full compatibility

Graceful Degradation

Without Wonolog mu-plugin:

  • WonologHandler detects Wonolog is not active
  • Handler does nothing and returns false
  • Logs continue to other handlers (files, etc.)
  • No errors or warnings

With Wonolog mu-plugin:

  • Handler forwards logs to Wonolog
  • Wonolog processes with email, filtering, etc.
  • Logs optionally continue to file backup (based on stop_propagation)

Namespace Detection

The handler automatically detects Wonolog's namespace:

  1. Checks default Inpsyde\Wonolog
  2. Applies filter wonolog_handler_namespace
  3. Supports scoped namespaces from wpify/scoper
  4. Verifies Configurator::ACTION_SETUP was triggered
  5. Caches result for performance

Troubleshooting

Logs not appearing in Wonolog

Check if Wonolog is active:

useWpSpaghetti\WonologHandler\Support\WonologDetector;
$detector = app(WonologDetector::class);
if (!$detector->isActive()) {
echo"Wonolog is not active!";
echo"Namespace: " . $detector->getNamespace();
echo"Action: " . $detector->getAction();
}

Wrong namespace or action detected

Override via filter or config (see Advanced Configuration).

Channel-related issues

Understanding channel behavior:

  1. Custom Wonolog channel (when explicitly provided):

    Log::error('Security breach', ['channel' => 'SECURITY', 'ip' => '1.2.3.4']);
    // Email: Channel = SECURITY, Context = ['ip' => '1.2.3.4'] (no 'channel' key)
  2. Default Wonolog channel (when not provided):

    Log::error('Error');
    // Email: Channel = DEBUG (Wonolog's default)
    Log::channel('stack')->error('Error');
    // Email: Channel = DEBUG (Wonolog's default - Monolog channel is ignored)
    Log::channel('single')->error('Error');
    // Email: Channel = DEBUG (Wonolog's default - Monolog channel is ignored)
  3. Channel extraction:

    • If 'channel' is in context, it's extracted and passed to Wonolog at top level
    • The 'channel' key is removed from context to avoid duplication
    • This ensures channel appears only once in emails (as "Channel: XXX", not in context)
  4. Monolog vs Wonolog channels:

    • Monolog channels (development, stack, single) route logs in Laravel
    • Wonolog channels (DEBUG, SECURITY, HTTP) categorize logs in Wonolog
    • They are completely separate - Monolog channels are NOT sent to Wonolog
    • To set a Wonolog channel: Log::error('msg', ['channel' => 'SECURITY'])
  5. Channel naming conventions⚠️:

    • IMPORTANT: Wonolog uses UPPERCASE channel names by convention
    • Standard Wonolog channels: DEBUG, SECURITY, HTTP, DB, PHP-ERROR, CRON, etc.
    • Using lowercase (e.g., 'security' instead of 'SECURITY') may cause logs not to be tracked
    • Using non-configured channels (e.g., 'FOO') may also not be tracked
    • This behavior depends on your Wonolog configuration and filters
    • Best practice: Always use UPPERCASE for channel names
    // ✅ Correct - uppercase
    Log::error('Breach', ['channel' => 'SECURITY']);
    // ❌ May not work - lowercase
    Log::error('Breach', ['channel' => 'security']);
    // ❌ May not work - non-configured channel
    Log::error('Error', ['channel' => 'FOO']);
  6. Custom channel names:

    • You can use custom channel names if configured in Wonolog
    • Examples: PAYMENT, API, WOOCOMMERCE, etc.
    • Make sure they're configured in your Wonolog setup
    • Always use UPPERCASE for consistency

Logs not in file backup

Ensure 'single' channel is in the stack:

'stack' => [
'driver' => 'stack',
'channels' => ['wonolog', 'single'], // ← Check this
],

And ensure stop_propagation is false (default).

Testing

composer test

More info

See LINKS file.

Changelog

Please see CHANGELOG for a detailed list of changes for each release.

We follow Semantic Versioning and use Conventional Commits to automatically generate our changelog.

Release Process

  • Major versions (1.0.0 → 2.0.0): Breaking changes
  • Minor versions (1.0.0 → 1.1.0): New features, backward compatible
  • Patch versions (1.0.0 → 1.0.1): Bug fixes, backward compatible

All releases are automatically created when changes are pushed to the main branch, based on commit message conventions.

Contributing

For your contributions please use:

See CONTRIBUTING for detailed guidelines.

Sponsor

Buy Me A Coffee

License

(ɔ) Copyleft 2026 Frugan.
GNU GPLv3, see LICENSE file.

About

Monolog handler that forwards Laravel logs to Inpsyde's Wonolog - works with any Laravel + WordPress setup: Acorn (w/wo Sage), WP Starter, Corcel, or custom integrations

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

PHP VersionPackagist DownloadsPackagist StarsGitHub Actions Workflow StatusCoverage StatusKnown VulnerabilitiesGitHub IssuesGitHub ReleaseLicense

Wonolog Handler

Monolog handler that forwards Laravel logs to Inpsyde's Wonolog - the professional WordPress logging solution.

Works with any Laravel + WordPress setup: Acorn (w/wo Sage), WP Starter, Corcel, or custom integrations.

Features

  • Clean Laravel Syntax - Use Log::error(), Log::info(), etc. anywhere in your code
  • Graceful Degradation - Works with or without Wonolog active
  • wpify/scoper Support - Automatically detects scoped Wonolog namespace
  • Zero Configuration - Works out of the box with sensible defaults
  • Flexible Propagation - Control whether to stop at Wonolog or continue to other handlers

Requirements

  • PHP >= 8.2
  • WordPress >= 6.0
  • Laravel Illuminate/Support ^10.0|^11.0|^12.0|^13.0
  • Monolog ^2.0|^3.0

Note: Inpsyde's Wonolog is not required for the package to work. Without Wonolog, logs gracefully pass through to other handlers in your stack (e.g., file logging).

Installation

1. Install the handler package

In your Laravel + WordPress project (Sage theme, WP Starter, etc.):

composer require wp-spaghetti/wonolog-handler

The package auto-registers via service provider discovery.

2. Install WP Spaghetti Wonolog mu-plugin (optional)

For email notifications, sensitive data filtering, and advanced logging features, install the WP Spaghetti Wonolog mu-plugin, that provides a complete logging solution with production-ready configuration.

See the WP Spaghetti Wonolog documentation for setup and configuration options.

3. Configure logging

Update your config/logging.php:

<?phpuseWpSpaghetti\WonologHandler\Handler\WonologHandler;
return [
'default' => env('LOG_CHANNEL', 'stack'),
'channels' => [
// Recommended: Stack with Wonolog + file backup'stack' => [
'driver' => 'stack',
'channels' => ['wonolog', 'single'],
'ignore_exceptions' => false,
],
// Wonolog channel'wonolog' => [
'driver' => 'monolog',
'handler' => WonologHandler::class,
'level' => env('LOG_LEVEL', 'debug'),
],
// File backup (optional but recommended)'single' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
],
],
];

See examples/logging.php for a complete configuration example.

Usage

Basic Logging

useIlluminate\Support\Facades\Log;
// Anywhere in your Laravel + WordPress code
Log::debug('Debugging information');
Log::info('Informational message');
Log::notice('Normal but significant event');
Log::warning('Warning condition');
Log::error('Error condition');
Log::critical('Critical condition');
Log::alert('Action must be taken immediately');
Log::emergency('System is unusable');

With Context

Log::error('Payment failed', [
'user_id' => $userId,
'amount' => $amount,
'error' => $exception->getMessage(),
]);
// Wonolog channels - use UPPERCASE by convention
Log::error('Security breach', [
'channel' => 'SECURITY', // ✅ Correct'ip' => $ipAddress,
]);
// Avoid lowercase - may not be tracked// Log::error('Breach', ['channel' => 'security']); // ❌ May not work

Channel Selection

// Use only Wonolog (no file backup)
Log::channel('wonolog')->error('Critical error');
// Use only file logging
Log::channel('single')->debug('Debug info');
// Use stack (Wonolog + file) - recommended
Log::channel('stack')->warning('Warning message');

Framework-Specific Examples

Sage Themes (Acorn)

// In app/Controllers/App.php or any controlleruseIlluminate\Support\Facades\Log;
publicfunctionindex()
{
Log::info('Page viewed', ['url' => request()->url()]);
return$this->view;
}

WP Starter

// In your custom plugins or themeuseIlluminate\Support\Facades\Log;
add_action('init', function() {
Log::info('WordPress initialized');
});

Corcel

useCorcel\Model\Post;
useIlluminate\Support\Facades\Log;
$posts = Post::published()->get();
Log::info('Fetched posts', ['count' => $posts->count()]);

See examples/usage.php for more real-world examples including WordPress hooks, WooCommerce integration, API logging, and performance monitoring.

Advanced Configuration

Publish Configuration

To customize settings, publish the config:

# Sage/Acorn
wp acorn vendor:publish --tag=wonolog-handler-config
# WP Starter (using Laravel's artisan)
php vendor/bin/wp-starter vendor:publish --tag=wonolog-handler-config

This creates config/wonolog.php in your project:

<?phpreturn [
// Custom Wonolog namespace (for wpify/scoper)'namespace' => env('WONOLOG_NAMESPACE', 'Inpsyde\\Wonolog'),
// Custom action hook (for wpify/scoper or custom naming)'action' => env('WONOLOG_ACTION', 'wonolog.log'),
// Stop propagation when Wonolog is active?'stop_propagation' => env('WONOLOG_STOP_PROPAGATION', false),
];

Custom Wonolog Namespace (for wpify/scoper)

If Wonolog is scoped, override the namespace:

Via config:

// config/wonolog.php'namespace' => 'WpSpaghetti\\Deps\\Inpsyde\\Wonolog',

Via filter:

add_filter('wonolog_handler.namespace', function () {
return'WpSpaghetti\\Deps\\Inpsyde\\Wonolog';
});

Via environment:

WONOLOG_NAMESPACE="WpSpaghetti\\Deps\\Inpsyde\\Wonolog"

Custom Action Hook (for wpify/scoper)

If Wonolog uses a custom action hook name:

Via config:

// config/wonolog.php'action' => 'custom_wonolog.log',

Via filter:

add_filter('wonolog_handler.action', function () {
return'custom_wonolog.log';
});

Via environment:

WONOLOG_ACTION="custom_wonolog.log"

Control Log Propagation

By default, logs continue to other handlers in the stack after Wonolog (allowing file backup). You can change this:

Stop at Wonolog (no file backup):

// config/wonolog.php'stop_propagation' => true,

Or via environment:

WONOLOG_STOP_PROPAGATION=true

Use cases:

  • false (default): Wonolog + file backup - recommended for production
  • true: Only Wonolog - if you don't want file logs and trust Wonolog completely

How It Works

Architecture

Laravel Log::error()
↓
Monolog LogRecord
↓
WonologHandler
↓ (if Wonolog active)
do_action('wonolog.log')
↓
Wonolog Processing
├─ Email notifications
├─ WordPress database
├─ Custom handlers
└─ Filtering/redaction
↓ (if stop_propagation=false)
Continue to next handler (file, Slack, etc.)

Technical Details

Channel Handling:

  • Wonolog expects channel at the top level of the action array, not inside context
  • If user provides 'channel' => 'SECURITY' in context, it's extracted and moved to top level
  • The channel is removed from context after extraction to avoid duplication
  • If no channel is provided, it's NOT passed to Wonolog (Wonolog uses its default: DEBUG)
  • Monolog's channel (e.g., stack, development) is NEVER used - it has nothing to do with Wonolog

Example behavior:

// User specifies Wonolog channel
Log::error('Error', ['channel' => 'SECURITY', 'ip' => '1.2.3.4']);
// Result: Channel = SECURITY, Context = ['ip' => '1.2.3.4'] (no 'channel' key)// No channel specified
Log::error('Error');
// Result: Channel = DEBUG (Wonolog's default), Context = [] (empty except datetime/extra)// Monolog channel is ignored
Log::channel('stack')->error('Error');
// Result: Channel = DEBUG (Wonolog's default), NOT 'stack'

PSR-3 Placeholder Compatibility:

  • The handler uses array format when calling do_action('wonolog.log', [...])
  • This forces Wonolog's HookLogFactory::fromArray() method instead of fromString()
  • Fixes PSR-3 placeholder substitution (e.g., {url}, {handle}) which breaks in fromString()
  • Compatible with all Wonolog v2.x and v3.x versions

Extra Data:

  • Monolog's extra data is passed as $context['extra'] (following Wonolog's convention)
  • Datetime is passed as $context['datetime'] for full compatibility

Graceful Degradation

Without Wonolog mu-plugin:

  • WonologHandler detects Wonolog is not active
  • Handler does nothing and returns false
  • Logs continue to other handlers (files, etc.)
  • No errors or warnings

With Wonolog mu-plugin:

  • Handler forwards logs to Wonolog
  • Wonolog processes with email, filtering, etc.
  • Logs optionally continue to file backup (based on stop_propagation)

Namespace Detection

The handler automatically detects Wonolog's namespace:

  1. Checks default Inpsyde\Wonolog
  2. Applies filter wonolog_handler_namespace
  3. Supports scoped namespaces from wpify/scoper
  4. Verifies Configurator::ACTION_SETUP was triggered
  5. Caches result for performance

Troubleshooting

Logs not appearing in Wonolog

Check if Wonolog is active:

useWpSpaghetti\WonologHandler\Support\WonologDetector;
$detector = app(WonologDetector::class);
if (!$detector->isActive()) {
echo"Wonolog is not active!";
echo"Namespace: " . $detector->getNamespace();
echo"Action: " . $detector->getAction();
}

Wrong namespace or action detected

Override via filter or config (see Advanced Configuration).

Channel-related issues

Understanding channel behavior:

  1. Custom Wonolog channel (when explicitly provided):

    Log::error('Security breach', ['channel' => 'SECURITY', 'ip' => '1.2.3.4']);
    // Email: Channel = SECURITY, Context = ['ip' => '1.2.3.4'] (no 'channel' key)
  2. Default Wonolog channel (when not provided):

    Log::error('Error');
    // Email: Channel = DEBUG (Wonolog's default)
    Log::channel('stack')->error('Error');
    // Email: Channel = DEBUG (Wonolog's default - Monolog channel is ignored)
    Log::channel('single')->error('Error');
    // Email: Channel = DEBUG (Wonolog's default - Monolog channel is ignored)
  3. Channel extraction:

    • If 'channel' is in context, it's extracted and passed to Wonolog at top level
    • The 'channel' key is removed from context to avoid duplication
    • This ensures channel appears only once in emails (as "Channel: XXX", not in context)
  4. Monolog vs Wonolog channels:

    • Monolog channels (development, stack, single) route logs in Laravel
    • Wonolog channels (DEBUG, SECURITY, HTTP) categorize logs in Wonolog
    • They are completely separate - Monolog channels are NOT sent to Wonolog
    • To set a Wonolog channel: Log::error('msg', ['channel' => 'SECURITY'])
  5. Channel naming conventions⚠️:

    • IMPORTANT: Wonolog uses UPPERCASE channel names by convention
    • Standard Wonolog channels: DEBUG, SECURITY, HTTP, DB, PHP-ERROR, CRON, etc.
    • Using lowercase (e.g., 'security' instead of 'SECURITY') may cause logs not to be tracked
    • Using non-configured channels (e.g., 'FOO') may also not be tracked
    • This behavior depends on your Wonolog configuration and filters
    • Best practice: Always use UPPERCASE for channel names
    // ✅ Correct - uppercase
    Log::error('Breach', ['channel' => 'SECURITY']);
    // ❌ May not work - lowercase
    Log::error('Breach', ['channel' => 'security']);
    // ❌ May not work - non-configured channel
    Log::error('Error', ['channel' => 'FOO']);
  6. Custom channel names:

    • You can use custom channel names if configured in Wonolog
    • Examples: PAYMENT, API, WOOCOMMERCE, etc.
    • Make sure they're configured in your Wonolog setup
    • Always use UPPERCASE for consistency

Logs not in file backup

Ensure 'single' channel is in the stack:

'stack' => [
'driver' => 'stack',
'channels' => ['wonolog', 'single'], // ← Check this
],

And ensure stop_propagation is false (default).

Testing

composer test

More info

See LINKS file.

Changelog

Please see CHANGELOG for a detailed list of changes for each release.

We follow Semantic Versioning and use Conventional Commits to automatically generate our changelog.

Release Process

  • Major versions (1.0.0 → 2.0.0): Breaking changes
  • Minor versions (1.0.0 → 1.1.0): New features, backward compatible
  • Patch versions (1.0.0 → 1.0.1): Bug fixes, backward compatible

All releases are automatically created when changes are pushed to the main branch, based on commit message conventions.

Contributing

For your contributions please use:

See CONTRIBUTING for detailed guidelines.

Sponsor

Buy Me A Coffee

License

(ɔ) Copyleft 2026 Frugan.
GNU GPLv3, see LICENSE file.

About

Monolog handler that forwards Laravel logs to Inpsyde's Wonolog - works with any Laravel + WordPress setup: Acorn (w/wo Sage), WP Starter, Corcel, or custom integrations

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages