{"id":35148471,"url":"https://github.com/wp-spaghetti/wonolog","last_synced_at":"2026-04-07T05:31:36.962Z","repository":{"id":330838063,"uuid":"1124139743","full_name":"wp-spaghetti/wonolog","owner":"wp-spaghetti","description":"An opinionated WordPress logging plugin built on top of Inpsyde's Wonolog","archived":false,"fork":false,"pushed_at":"2025-12-29T11:11:21.000Z","size":68,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-30T22:28:13.757Z","etag":null,"topics":["logger","monolog","psr-3","wonolog","wordpress","wordpress-plugin"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wp-spaghetti.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":".github/SECURITY.md","support":".github/SUPPORT.md","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["frugan-dev"],"buy_me_a_coffee":"frugan"}},"created_at":"2025-12-28T12:24:27.000Z","updated_at":"2025-12-29T11:11:24.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/wp-spaghetti/wonolog","commit_stats":null,"previous_names":["wp-spaghetti/wonolog"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/wp-spaghetti/wonolog","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wp-spaghetti%2Fwonolog","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wp-spaghetti%2Fwonolog/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wp-spaghetti%2Fwonolog/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wp-spaghetti%2Fwonolog/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wp-spaghetti","download_url":"https://codeload.github.com/wp-spaghetti/wonolog/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wp-spaghetti%2Fwonolog/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31501903,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-07T03:10:19.677Z","status":"ssl_error","status_checked_at":"2026-04-07T03:10:13.982Z","response_time":105,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["logger","monolog","psr-3","wonolog","wordpress","wordpress-plugin"],"created_at":"2025-12-28T14:43:25.938Z","updated_at":"2026-04-07T05:31:36.939Z","avatar_url":"https://github.com/wp-spaghetti.png","language":"PHP","funding_links":["https://github.com/sponsors/frugan-dev","https://buymeacoffee.com/frugan"],"categories":[],"sub_categories":[],"readme":"![GitHub Downloads (all assets, all releases)](https://img.shields.io/github/downloads/wp-spaghetti/wonolog/total)\n![GitHub Actions Workflow Status](https://github.com/wp-spaghetti/wonolog/actions/workflows/release.yml/badge.svg)\n![GitHub Issues](https://img.shields.io/github/issues/wp-spaghetti/wonolog)\n![GitHub Release](https://img.shields.io/github/v/release/wp-spaghetti/wonolog)\n![License](https://img.shields.io/github/license/wp-spaghetti/wonolog)\n\u003c!--\n![Coverage Status](https://img.shields.io/codecov/c/github/wp-spaghetti/wonolog)\n![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=wp-spaghetti_wonolog\u0026metric=alert_status)\n![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=wp-spaghetti_wonolog\u0026metric=security_rating)\n![Known Vulnerabilities](https://snyk.io/test/github/wp-spaghetti/wonolog/badge.svg)\n![PHP Version](https://img.shields.io/badge/php-\u003e=8.1-blue)\n![Code Climate](https://img.shields.io/codeclimate/maintainability/wp-spaghetti/wonolog)\n--\u003e\n\n# Wonolog (WordPress Plugin)\n\nAn **opinionated** WordPress logging plugin built on top of [Inpsyde's Wonolog](https://github.com/inpsyde/Wonolog). This plugin provides a pre-configured, production-ready logging solution with sensible defaults, environment-aware configuration, and comprehensive error tracking.\n\n## What Makes This \"Opinionated\"?\n\nThis plugin takes Wonolog's powerful PSR-3 logging capabilities and wraps them in a batteries-included configuration that works out of the box. Instead of requiring manual setup, it provides:\n\n- **Environment-aware logging**: Automatically adjusts behavior based on `WP_DEBUG` and `WP_ENVIRONMENT_TYPE`\n- **Email notifications**: Sends HTML-formatted error reports to administrators (with deduplication in production)\n- **Rotating log files**: Keeps your logs manageable with automatic rotation (default: 10 files)\n- **Request tracking**: Captures HTTP request context with sensitive data protection\n- **Database error filtering**: Pre-configured patterns to reduce noise from common database warnings\n- **Zero configuration required**: Works immediately after installation with sensible defaults\n\nIf you need full control over logging configuration, use [Inpsyde's Wonolog](https://github.com/inpsyde/Wonolog) directly. If you want logging to \"just work\" with best practices, this plugin is for you.\n\n## Features\n\n- **PSR-3 Compliant**: Based on Monolog via Wonolog, supporting standard PSR-3 logging interfaces\n- **Multi-Handler Setup**: File logging + email notifications for errors\n- **Environment Detection**: Uses [WP Env](https://github.com/wp-spaghetti/wp-env) for reliable environment detection\n- **Sensitive Data Protection**: Automatically filters passwords, keys, and tokens from logs\n- **Request Context**: Captures `$_REQUEST`, `$_POST`, `$_FILES`, `$_SERVER` (with sensitive data removed)\n- **Deduplication**: Prevents email spam by deduplicating repeated errors (production only)\n- **Customizable Patterns**: Filter out specific error messages via environment variables or WordPress filters\n- **Developer-Friendly**: Detailed error logging in development, cleaner logs in production\n- **Dependency Isolation**: All dependencies are scoped via [wpify/scoper](https://github.com/wpify/scoper) to prevent conflicts with other plugins\n\n## Requirements\n\n- PHP ^8.1\n- WordPress ^6.0\n\n## Installation\n\nYou can install the plugin in three ways: manually, via Composer from [WPackagist](https://wpackagist.org), or via Composer from [GitHub Releases](../../releases).\n\n\u003cdetails\u003e\n\u003csummary\u003eManual Installation\u003c/summary\u003e\n\n1. Go to the [Releases](../../releases) section of this repository.\n2. Download the latest release zip file.\n3. Log in to your WordPress admin dashboard.\n4. Navigate to `Plugins` \u003e `Add New`.\n5. Click `Upload Plugin`.\n6. Choose the downloaded zip file and click `Install Now`.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eInstallation via Composer from WPackagist\u003c/summary\u003e\n\nIf you use Composer to manage WordPress plugins, you can install it from [WordPress Packagist](https://wpackagist.org):\n\n1. Open your terminal.\n2. Navigate to the root directory of your WordPress installation.\n3. Ensure your `composer.json` file has the following configuration: *\n\n```json\n{\n    \"require\": {\n        \"composer/installers\": \"^1.0 || ^2.0\",\n        \"wpackagist-plugin/wonolog\": \"^0.2\"\n    },\n    \"extra\": {\n        \"installer-paths\": {\n            \"wp-content/plugins/{$name}/\": [\n               \"type:wordpress-plugin\"\n            ]\n        }\n    }\n}\n```\n4. Run the following command:\n\n```sh\ncomposer update\n```\n\n\u003csub\u003e\u003ci\u003e\n_Note:_  \n_* `composer/installers` might already be required by another dependency._\n\u003c/i\u003e\u003c/sub\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eInstallation via Composer from GitHub Releases\u003c/summary\u003e\n\nIf you use Composer to manage WordPress plugins, you can install it from this repository directly.\n\n**Standard Version** (uses WordPress update system):\n\n1. Open your terminal.\n2. Navigate to the root directory of your WordPress installation.\n3. Ensure your `composer.json` file has the following configuration: *\n\n```json\n{\n    \"require\": {\n        \"composer/installers\": \"^1.0 || ^2.0\",\n        \"wp-spaghetti/wonolog\": \"^0.2\"\n    },\n    \"repositories\": [\n        {\n            \"type\": \"package\",\n            \"package\": {\n                \"name\": \"wp-spaghetti/wonolog\",\n                \"version\": \"0.2.0\",\n                \"type\": \"wordpress-plugin\",\n                \"dist\": {\n                    \"url\": \"https://github.com/wp-spaghetti/wonolog/releases/download/v0.2.0/wonolog.zip\",\n                    \"type\": \"zip\"\n                }\n            }\n        }\n    ],\n    \"extra\": {\n        \"installer-paths\": {\n            \"wp-content/plugins/{$name}/\": [\n               \"type:wordpress-plugin\"\n            ]\n        }\n    }\n}\n```\n\n**Version with Git Updater** (uses Git Updater Lite for updates):\n\nFor installations that need updates managed via Git instead of WordPress.org, use the `--with-git-updater` version:\n\n```json\n{\n    \"require\": {\n        \"composer/installers\": \"^1.0 || ^2.0\",\n        \"wp-spaghetti/wonolog\": \"^0.2\"\n    },\n    \"repositories\": [\n        {\n            \"type\": \"package\",\n            \"package\": {\n                \"name\": \"wp-spaghetti/wonolog\",\n                \"version\": \"0.2.0\",\n                \"type\": \"wordpress-plugin\",\n                \"dist\": {\n                    \"url\": \"https://github.com/wp-spaghetti/wonolog/releases/download/v0.2.0/wonolog--with-git-updater.zip\",\n                    \"type\": \"zip\"\n                }\n            }\n        }\n    ],\n    \"extra\": {\n        \"installer-paths\": {\n            \"wp-content/plugins/{$name}/\": [\n               \"type:wordpress-plugin\"\n            ]\n        }\n    }\n}\n```\n\n4. Run the following command:\n\n```sh\ncomposer update\n```\n\n\u003csub\u003e\u003ci\u003e\n_Note:_  \n_* `composer/installers` might already be required by another dependency._  \n_* The `--with-git-updater` version includes [Git Updater Lite](https://github.com/afragen/git-updater-lite) for automatic updates detection, while the standard version relies on WordPress.org update system._\n\u003c/i\u003e\u003c/sub\u003e\n\u003c/details\u003e\n\n### Must-Use Plugin (Recommended)\n\nFor optimal performance and reliability, it's **highly recommended** to install Wonolog as a [must-use plugin](https://wordpress.org/documentation/article/must-use-plugins/). This ensures that logging is initialized as early as possible in the WordPress bootstrap process, allowing you to capture errors that occur during plugin loading.\n\n**Why must-use?** Wonolog's underlying library uses `earlyAddAction('muplugins_loaded', ...)` to hook into WordPress as early as possible. Installing it as a regular plugin works, but you'll miss errors that occur before regular plugins are loaded.\n\n**Automatic mu-plugin loading:** To avoid creating manual loader files, you can use [roots/bedrock-autoloader](https://github.com/roots/bedrock-autoloader) which automatically loads plugins from the `mu-plugins` directory.\n\n## Quick Start\n\n### Zero Configuration Mode\n\nThe plugin works immediately after activation with no configuration required:\n\n1. Install and activate the plugin\n2. Errors are automatically logged to rotating files in production or PHP error log in development (log location is [customizable via Wonolog configuration](https://inpsyde.github.io/Wonolog/06-log-handlers/#logs-folder))\n3. Critical errors are emailed to recipients defined in `WONOLOG_MAIL_TO` environment variable, or to the WordPress admin email as fallback\n\n### Development vs Production Behavior\n\nThe plugin automatically detects your environment using [WP Env](https://github.com/wp-spaghetti/wp-env) and adjusts behavior:\n\n**Development Mode** (`WP_DEBUG=true`):\n- Logs to PHP error log (`error_log`)\n- Captures silenced PHP errors (`@error`)\n- More verbose error reporting\n- No email deduplication\n\n**Production Mode** (`WP_DEBUG=false`):\n- Logs to rotating files (default: 10 files max, customizable via `WONOLOG_MAX_FILES`)\n- Email notifications with deduplication (default: 24 hours, customizable via `WONOLOG_DEDUP_TIME`)\n- Cleaner error reporting (filters notices and deprecation warnings)\n- Performance optimized\n\n**Note**: Log file location defaults to `wp-content/wonolog/` but can be customized via `WP_DEBUG_LOG` constant. See [Wonolog's documentation](https://inpsyde.github.io/Wonolog/06-log-handlers/#logs-folder) for details.\n\n## Configuration\n\n### Environment Variables\n\nConfigure the plugin via environment variables (`.env` file or `wp-config.php`):\n\n```php\n// Email configuration\ndefine('WONOLOG_MAIL_TO', 'admin@example.com,dev@example.com'); // Comma-separated recipients\ndefine('WONOLOG_MAIL_FROM', 'wordpress@example.com');\ndefine('WONOLOG_EMAIL_LEVEL', 'ERROR'); // Minimum level for email notifications (default: ERROR)\n\n// File logging configuration\ndefine('WONOLOG_MAX_FILES', 10); // Maximum number of rotating log files (default: 10)\ndefine('WONOLOG_FILE_PERMISSION', 0777); // File permissions for log files (default: 0777)\ndefine('WONOLOG_DEDUP_TIME', 86400); // Email deduplication time in seconds (default: 86400 = 24 hours)\n\n// Environment type (auto-detected if not set)\ndefine('WP_ENVIRONMENT_TYPE', 'production'); // or 'development', 'staging'\ndefine('WP_ENV', 'production'); // Alternative format\n\n// Log file location (optional)\ndefine('WP_DEBUG_LOG', '/custom/path/to/debug.log'); // Custom log file path\n```\n\n### Ignore Patterns\n\nFilter out specific error messages using JSON-encoded patterns:\n\n```php\n// Replace default patterns entirely\ndefine('WONOLOG_IGNORE_PATTERNS', json_encode([\n    [\n        'pattern' =\u003e '^Custom error pattern$',\n        'level' =\u003e 'ERROR',\n        'channel' =\u003e 'DB'\n    ]\n]));\n\n// Add patterns to defaults\ndefine('WONOLOG_IGNORE_PATTERNS_ADDITIONAL', json_encode([\n    [\n        'pattern' =\u003e '^Another pattern to ignore$',\n        'level' =\u003e null,      // Apply to all levels\n        'channel' =\u003e null     // Apply to all channels\n    ]\n]));\n```\n\n**Default Patterns** (database errors):\n- `Can't DROP '.+'; check that column/key exists`\n- `Deadlock found when trying to get lock`\n- `Duplicate entry '.+' for key`\n- `Table '.+' doesn't exist`\n\n### WordPress Filters\n\nCustomize behavior using WordPress filters:\n\n```php\n// Add custom sensitive patterns to filter from logs\nadd_filter('wonolog_sensitive_patterns', function(array $patterns): array {\n    $patterns[] = 'STRIPE_SECRET';\n    $patterns[] = 'OAUTH_TOKEN';\n    return $patterns;\n});\n\n// Modify ignore patterns\nadd_filter('wonolog_ignore_patterns', function(array $patterns): array {\n    $patterns[] = [\n        'pattern' =\u003e '^Your custom pattern$',\n        'level' =\u003e null,\n        'channel' =\u003e null\n    ];\n    return $patterns;\n});\n```\n\n#### IP Detection Configuration\n\nWonolog uses [Vectorface/whip](https://github.com/Vectorface/whip) for robust IP detection. Configure it using these filters:\n\n**1. Configure detection methods** (default: `Whip::ALL_METHODS`):\n\n```php\nuse WpSpaghetti\\Deps\\Vectorface\\Whip\\Whip;\n\n// Example 1: Only trust Cloudflare and direct connection (recommended for Cloudflare sites)\nadd_filter('wonolog_ip_detection_methods', function(): int {\n    return Whip::CLOUDFLARE_HEADERS | Whip::REMOTE_ADDR;\n});\n\n// Example 2: For sites behind standard reverse proxy (Nginx, Traefik)\nadd_filter('wonolog_ip_detection_methods', function(): int {\n    return Whip::PROXY_HEADERS | Whip::REMOTE_ADDR;\n});\n\n// Example 3: Direct connection only (no proxy trust)\nadd_filter('wonolog_ip_detection_methods', function(): int {\n    return Whip::REMOTE_ADDR;\n});\n\n// Example 4: Use default (try all methods - suitable for most cases)\n// No filter needed - Whip::ALL_METHODS is the default\n```\n\n**2. Add custom proxy headers** (for non-standard proxies):\n\n```php\nadd_filter('wonolog_ip_custom_headers', function(): array {\n    return [\n        'X-Real-IP',              // Nginx, Traefik\n        'X-My-Custom-IP-Header',  // Your custom proxy\n    ];\n});\n\n// Don't forget to enable CUSTOM_HEADERS method:\nadd_filter('wonolog_ip_detection_methods', function(): int {\n    return Whip::CUSTOM_HEADERS | Whip::REMOTE_ADDR;\n});\n```\n\n**3. Whitelist trusted proxy IPs** (advanced - for security):\n\n```php\nadd_filter('wonolog_ip_whitelists', function(): array {\n    return [\n        // Only trust Cloudflare IPs for CF-Connecting-IP header\n        Whip::CLOUDFLARE_HEADERS =\u003e [\n            Whip::IPV4 =\u003e [\n                '199.27.128.0/21',\n                '173.245.48.0/20',\n                '103.21.244.0/22',\n                // ... full Cloudflare IP list\n            ],\n            Whip::IPV6 =\u003e [\n                '2400:cb00::/32',\n                '2606:4700::/32',\n                // ... full Cloudflare IPv6 list\n            ]\n        ],\n        \n        // Only trust your load balancer for X-Forwarded-For\n        Whip::PROXY_HEADERS =\u003e [\n            Whip::IPV4 =\u003e [\n                '10.0.0.1',      // Your load balancer IP\n                '10.0.0.2',\n            ]\n        ]\n    ];\n});\n```\n\n**Available Whip Methods** (combine with `|` operator):\n\n| Constant | Description | Headers Used |\n|----------|-------------|--------------|\n| `Whip::REMOTE_ADDR` | Direct connection IP | `$_SERVER['REMOTE_ADDR']` |\n| `Whip::CLOUDFLARE_HEADERS` | Cloudflare's header | `CF-Connecting-IP` |\n| `Whip::INCAPSULA_HEADERS` | Incapsula CDN | `Incap-Client-IP` |\n| `Whip::PROXY_HEADERS` | Standard proxy headers | `X-Forwarded-For`, `X-Real-IP`, etc. |\n| `Whip::CUSTOM_HEADERS` | Your custom headers | Headers from `wonolog_ip_custom_headers` |\n| `Whip::ALL_METHODS` | Try all methods (default) | All of the above |\n\n**Security Notes**:\n- **Default (`ALL_METHODS`)**: Suitable for most sites, tries methods in priority order\n- **`PROXY_HEADERS` risk**: Headers like `X-Forwarded-For` can be spoofed by clients if you're not behind a trusted proxy\n- **Whitelisting**: Use `wonolog_ip_whitelists` to only trust specific proxy IPs\n- **No proxy**: Use only `REMOTE_ADDR` if not behind any proxy\n\n**Log Output**: The detected IP and its source are automatically logged:\n- `client_ip` - The detected IP address\n- `client_ip_source` - Which header/method was used (e.g., \"cf-connecting-ip\", \"remote-addr\")\n- `hostbyaddr` - Reverse DNS lookup (when available)\n\n## Use Cases\n\n### 1. E-commerce Site Error Monitoring\n\n```php\n// wp-config.php\ndefine('WP_ENVIRONMENT_TYPE', 'production');\ndefine('WONOLOG_MAIL_TO', 'ops@shop.com,dev@shop.com');\n\n// Ignore common WooCommerce transient errors\nadd_filter('wonolog_ignore_patterns', function($patterns) {\n    $patterns[] = [\n        'pattern' =\u003e 'Transient .+ not found',\n        'level' =\u003e 'WARNING',\n        'channel' =\u003e null\n    ];\n    return $patterns;\n});\n```\n\n### 2. Multi-Environment Development\n\n```php\n// .env.development\nWP_ENVIRONMENT_TYPE=development\nWP_DEBUG=true\n\n// .env.staging\nWP_ENVIRONMENT_TYPE=staging\nWP_DEBUG=true\nWONOLOG_MAIL_TO=staging-alerts@example.com\n\n// .env.production\nWP_ENVIRONMENT_TYPE=production\nWP_DEBUG=false\nWONOLOG_MAIL_TO=admin@example.com,ops@example.com\n```\n\n### 3. Custom Application Logging\n\nUse PSR-3 logger throughout your codebase:\n\n```php\nuse WpSpaghetti\\Deps\\Inpsyde\\Wonolog\\Wonolog;\n\nclass MyPlugin {\n    public function processOrder($orderId) {\n        $logger = Wonolog::logger();\n        \n        $logger-\u003einfo('Processing order', ['order_id' =\u003e $orderId]);\n        \n        try {\n            // ... processing logic\n            $logger-\u003enotice('Order processed successfully', [\n                'order_id' =\u003e $orderId,\n                'amount' =\u003e $amount\n            ]);\n        } catch (\\Exception $e) {\n            $logger-\u003eerror('Order processing failed', [\n                'order_id' =\u003e $orderId,\n                'error' =\u003e $e-\u003egetMessage(),\n                'trace' =\u003e $e-\u003egetTraceAsString()\n            ]);\n            throw $e;\n        }\n    }\n}\n```\n\n### 4. API Integration Monitoring\n\n```php\nclass APIClient {\n    private $logger;\n    \n    public function __construct() {\n        $this-\u003elogger = \\WpSpaghetti\\Deps\\Inpsyde\\Wonolog\\Wonolog::logger();\n    }\n    \n    public function makeRequest($endpoint) {\n        $this-\u003elogger-\u003edebug('API request started', [\n            'endpoint' =\u003e $endpoint,\n            'method' =\u003e 'GET'\n        ]);\n        \n        $response = wp_remote_get($endpoint);\n        \n        if (is_wp_error($response)) {\n            $this-\u003elogger-\u003eerror('API request failed', [\n                'endpoint' =\u003e $endpoint,\n                'error' =\u003e $response-\u003eget_error_message()\n            ]);\n            return false;\n        }\n        \n        $this-\u003elogger-\u003einfo('API request completed', [\n            'endpoint' =\u003e $endpoint,\n            'status' =\u003e wp_remote_retrieve_response_code($response)\n        ]);\n        \n        return $response;\n    }\n}\n```\n\n### 5. Security Monitoring with Trusted Proxy Configuration\n\n```php\n// wp-config.php or mu-plugin\n\nuse WpSpaghetti\\Deps\\Vectorface\\Whip\\Whip;\n\n// Example 1: Site behind Cloudflare only\nadd_filter('wonolog_ip_detection_methods', function(): int {\n    return Whip::CLOUDFLARE_HEADERS | Whip::REMOTE_ADDR;\n});\n\n// Optional: Whitelist only Cloudflare IPs for extra security\nadd_filter('wonolog_ip_whitelists', function(): array {\n    return [\n        Whip::CLOUDFLARE_HEADERS =\u003e [\n            Whip::IPV4 =\u003e [\n                '199.27.128.0/21',\n                '173.245.48.0/20',\n                '103.21.244.0/22',\n                '103.22.200.0/22',\n                '103.31.4.0/22',\n                '141.101.64.0/18',\n                '108.162.192.0/18',\n                '190.93.240.0/20',\n                '188.114.96.0/20',\n                '197.234.240.0/22',\n                '198.41.128.0/17',\n                '162.158.0.0/15',\n                '104.16.0.0/12'\n            ],\n            Whip::IPV6 =\u003e [\n                '2400:cb00::/32',\n                '2606:4700::/32',\n                '2803:f800::/32',\n                '2405:b500::/32',\n                '2405:8100::/32'\n            ]\n        ]\n    ];\n});\n\n// Example 2: Site behind AWS ALB (no Cloudflare)\nadd_filter('wonolog_ip_detection_methods', function(): int {\n    return Whip::PROXY_HEADERS | Whip::REMOTE_ADDR;\n});\n\n// Whitelist your ALB IP addresses\nadd_filter('wonolog_ip_whitelists', function(): array {\n    return [\n        Whip::PROXY_HEADERS =\u003e [\n            Whip::IPV4 =\u003e [\n                '10.0.1.0/24',  // Your ALB subnet\n            ]\n        ]\n    ];\n});\n\n// Example 3: Custom proxy setup (Nginx with X-Real-IP)\nadd_filter('wonolog_ip_detection_methods', function(): int {\n    return Whip::CUSTOM_HEADERS | Whip::REMOTE_ADDR;\n});\n\nadd_filter('wonolog_ip_custom_headers', function(): array {\n    return ['X-Real-IP'];\n});\n\nadd_filter('wonolog_ip_whitelists', function(): array {\n    return [\n        Whip::CUSTOM_HEADERS =\u003e [\n            Whip::IPV4 =\u003e ['192.168.1.1']  // Your Nginx server\n        ]\n    ];\n});\n\n// Example 4: Monitor failed login attempts with real client IP\nadd_action('wp_login_failed', function(string $username) {\n    do_action('wonolog.log.warning', [\n        'message' =\u003e 'Failed login attempt',\n        'channel' =\u003e \\WpSpaghetti\\Deps\\Inpsyde\\Wonolog\\Channels::SECURITY,\n        'context' =\u003e [\n            'username' =\u003e $username,\n            'user_agent' =\u003e $_SERVER['HTTP_USER_AGENT'] ?? 'unknown',\n            // client_ip and client_ip_source are automatically added by Wonolog\n        ],\n    ]);\n});\n\n// Example 5: Rate limiting by IP using Whip directly\nadd_action('rest_api_init', function() {\n    // Create Whip instance matching your configuration\n    $whip = new Whip(Whip::CLOUDFLARE_HEADERS | Whip::REMOTE_ADDR);\n    $ip = $whip-\u003egetValidIpAddress();\n    \n    if ($ip) {\n        $transient_key = 'api_rate_limit_' . md5($ip);\n        $count = (int) get_transient($transient_key);\n        \n        if ($count \u003e 100) {\n            do_action('wonolog.log.error', [\n                'message' =\u003e 'API rate limit exceeded',\n                'context' =\u003e [\n                    'ip' =\u003e $ip,\n                    'requests' =\u003e $count,\n                ],\n            ]);\n            \n            wp_send_json_error(['message' =\u003e 'Rate limit exceeded'], 429);\n        }\n        \n        set_transient($transient_key, $count + 1, MINUTE_IN_SECONDS);\n    }\n});\n\n// Example 6: Block requests from specific IP ranges\nadd_action('init', function() {\n    $whip = new Whip();\n    $ip = $whip-\u003egetValidIpAddress();\n    \n    if (!$ip) {\n        return;\n    }\n    \n    // Check if IP is in blocked range\n    $blockedRange = new \\WpSpaghetti\\Deps\\Vectorface\\Whip\\IpRange\\Ipv4Range('192.0.2.0/24');\n    \n    if ($blockedRange-\u003econtainsIp($ip)) {\n        do_action('wonolog.log.critical', [\n            'message' =\u003e 'Blocked IP range access attempt',\n            'context' =\u003e [\n                'ip' =\u003e $ip,\n                'requested_url' =\u003e $_SERVER['REQUEST_URI'] ?? 'unknown',\n            ],\n        ]);\n        \n        wp_die('Access denied', 'Forbidden', ['response' =\u003e 403]);\n    }\n});\n\n// Example 7: Monitor suspicious activity from flagged IPs\nadd_filter('wonolog.log-data-context', function(array $context, $log): array {\n    $suspiciousIps = ['192.0.2.1', '198.51.100.1'];\n    \n    if (isset($context['client_ip']) \u0026\u0026 in_array($context['client_ip'], $suspiciousIps, true)) {\n        // Add flag to context\n        $context['flagged_ip'] = true;\n        \n        // Escalate to critical\n        do_action('wonolog.log.critical', [\n            'message' =\u003e 'Activity from flagged IP detected',\n            'context' =\u003e [\n                'original_message' =\u003e $log-\u003emessage(),\n                'client_ip' =\u003e $context['client_ip'],\n                'client_ip_source' =\u003e $context['client_ip_source'] ?? 'unknown',\n            ],\n        ]);\n    }\n    \n    return $context;\n}, 10, 2);\n```\n\n**Why Whip with Whitelisting?**\n- **Security**: Only trusts IPs from your actual infrastructure\n- **Spoofing Prevention**: Headers from non-whitelisted sources are ignored\n- **Flexibility**: Easy to update when adding/removing proxies\n- **Reliability**: Handles edge cases and malformed headers gracefully\n\n**Common Infrastructure Patterns**:\n\n| Setup | Detection Methods | Whitelist |\n|-------|------------------|-----------|\n| Direct (no proxy) | `REMOTE_ADDR` | Not needed |\n| Behind Cloudflare | `CLOUDFLARE_HEADERS \\| REMOTE_ADDR` | Cloudflare IP ranges |\n| Behind AWS ALB | `PROXY_HEADERS \\| REMOTE_ADDR` | ALB subnet IPs |\n| Behind Nginx | `CUSTOM_HEADERS \\| REMOTE_ADDR` | Nginx server IP |\n| CF + ALB | `CLOUDFLARE_HEADERS \\| PROXY_HEADERS \\| REMOTE_ADDR` | Both CF and ALB IPs |\n| Behind Incapsula | `INCAPSULA_HEADERS \\| REMOTE_ADDR` | Incapsula IP ranges |\n\n**Getting Proxy IP Lists**:\n- **Cloudflare**: [IPv4](https://www.cloudflare.com/ips-v4) / [IPv6](https://www.cloudflare.com/ips-v6)\n- **AWS**: Check your VPC subnet configuration\n- **Your Infrastructure**: Use `$_SERVER['REMOTE_ADDR']` to see proxy IPs\n\n**Log Output**:\n- `client_ip`: Validated client IP address\n- `client_ip_source`: Which header was used (e.g., \"cf-connecting-ip\", \"remote-addr\")\n- `hostbyaddr`: Reverse DNS lookup (when available)\n\n## Log Levels\n\nThe plugin uses standard PSR-3 log levels:\n\n- **DEBUG**: Detailed debugging information (development only)\n- **INFO**: Interesting events (user login, SQL queries)\n- **NOTICE**: Normal but significant events\n- **WARNING**: Exceptional occurrences that are not errors\n- **ERROR**: Runtime errors (emailed in production)\n- **CRITICAL**: Critical conditions (emailed in production)\n- **ALERT**: Action must be taken immediately (emailed in production)\n- **EMERGENCY**: System unusable (emailed in production)\n\n## Log File Location\n\n- **Production**: Rotating files with automatic cleanup (default location can be customized via `WP_DEBUG_LOG`, default max files: 10, customizable via `WONOLOG_MAX_FILES`)\n- **Development**: PHP error log (location depends on PHP configuration)\n- **Deduplication log**: Used in production to track duplicate errors (default: 24-hour window, customizable via `WONOLOG_DEDUP_TIME`)\n\nSee [Wonolog's documentation on log handlers](https://inpsyde.github.io/Wonolog/06-log-handlers/#logs-folder) for advanced log file location configuration.\n\n## Dependencies\n\nThis plugin uses [WP Env](https://github.com/wp-spaghetti/wp-env) for reliable environment detection and configuration management. All environment-related features (development/staging/production detection, Docker detection, configuration loading) are powered by WP Env.\n\n**IP Detection**: Uses [Vectorface/whip](https://github.com/Vectorface/whip) for robust and secure client IP detection with proxy header validation. Whip provides battle-tested protection against IP spoofing and supports various proxy configurations (Cloudflare, AWS ALB, Nginx, custom proxies, etc.).\n\n**All dependencies are scoped** via [wpify/scoper](https://github.com/wpify/scoper) to prevent conflicts with other plugins, ensuring the plugin works reliably in any WordPress environment.\n\n## Recommended Packages\n\n**[WP Logger](https://github.com/wp-spaghetti/wp-logger)** is a wrapper service specifically designed for plugin developers who want to integrate logging capabilities into their WordPress plugins while maintaining flexibility and WordPress.org compliance.\n\nKey features:\n- Automatically detects and uses Wonolog when available\n- Provides secure file logging fallback when Wonolog is not installed\n- Isolates logs per plugin for easier debugging\n- Ensures WordPress.org compliance for distributed plugins\n\nUse **WP Logger** if you:\n- Are developing plugins for WordPress.org distribution\n- Need plugin-specific log isolation\n- Want automatic fallback without Wonolog dependency\n- Prefer simplified WordPress-native configuration\n\nUse **Wonolog** directly if you:\n- Need full control over Monolog configuration\n- Have Wonolog as a must-use plugin (always available)\n- Require advanced Monolog features (custom handlers, processors)\n- Want enterprise-level logging customization\n\n## Frequently Asked Questions\n\n### Does this require any configuration?\n\nNo! The plugin works out of the box with sensible defaults. However, you can customize it via environment variables or WordPress filters if needed.\n\n### Where are logs stored?\n\n* **Development mode** (`WP_DEBUG=true`): PHP error log\n* **Production mode** (`WP_DEBUG=false`): Rotating log files (default location can be customized via `WP_DEBUG_LOG` constant, see [Wonolog documentation](https://inpsyde.github.io/Wonolog/06-log-handlers/#logs-folder))\n\n### Will I get email notifications for every error?\n\nIn production mode, email notifications are deduplicated (default: 24-hour window, customizable via `WONOLOG_DEDUP_TIME`) to prevent spam. In development mode, emails are sent immediately. Email recipients are defined via `WONOLOG_MAIL_TO` environment variable, with WordPress admin email as fallback.\n\n### Can I filter out specific errors?\n\nYes! Use the `WONOLOG_IGNORE_PATTERNS` or `WONOLOG_IGNORE_PATTERNS_ADDITIONAL` environment variables, or the `wonolog_ignore_patterns` WordPress filter.\n\n### What's the difference between this and Inpsyde's Wonolog?\n\nInpsyde's Wonolog is a powerful, flexible logging framework that requires configuration. This plugin provides an opinionated, pre-configured setup that works immediately. Use Wonolog if you need full control; use this plugin if you want it to \"just work\".\n\n## Troubleshooting\n\n### Logs Not Appearing\n\n1. Check file permissions on `wp-content/wonolog/` directory\n2. Verify `WP_DEBUG` setting matches your expectation\n3. Check PHP error log location (development mode)\n4. Ensure the plugin is activated\n\n### Email Notifications Not Sent\n\n1. Verify `WONOLOG_MAIL_TO` is set or WordPress admin email is configured\n2. Check spam folder\n3. Test WordPress email functionality with a test plugin\n4. Review deduplication log to see if errors are being suppressed\n\n### Too Many Email Notifications\n\n1. In production, deduplication is automatic (24-hour window)\n2. Use `WONOLOG_IGNORE_PATTERNS_ADDITIONAL` to filter specific errors\n3. Consider raising the minimum log level for emails\n\n### Debug Environment Information\n\n```php\n// Temporary debugging snippet\nadd_action('init', function() {\n    $env = \\WpSpaghetti\\WpEnv\\Environment::getDebugInfo();\n    error_log(print_r($env, true));\n});\n```\n\n## More info\n\nSee [LINKS](docs/LINKS.md) file.\n\n## Changelog\n\nPlease see [CHANGELOG](CHANGELOG.md) for a detailed list of changes for each release.\n\nWe follow [Semantic Versioning](https://semver.org/) and use [Conventional Commits](https://www.conventionalcommits.org/) to automatically generate our changelog.\n\n### Release Process\n\n- **Major versions** (1.0.0 → 2.0.0): Breaking changes\n- **Minor versions** (1.0.0 → 1.1.0): New features, backward compatible\n- **Patch versions** (1.0.0 → 1.0.1): Bug fixes, backward compatible\n\nAll releases are automatically created when changes are pushed to the `main` branch, based on commit message conventions.\n\n## Contributing\n\nFor your contributions please use:\n\n- [Conventional Commits](https://www.conventionalcommits.org)\n- [Pull request workflow](https://docs.github.com/en/get-started/exploring-projects-on-github/contributing-to-a-project)\n\nSee [CONTRIBUTING](.github/CONTRIBUTING.md) for detailed guidelines.\n\n## Sponsor\n\n[\u003cimg src=\"https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png\" width=\"200\" alt=\"Buy Me A Coffee\"\u003e](https://buymeacoff.ee/frugan)\n\n## License\n\n(ɔ) Copyleft 2026 [Frugan](https://frugan.it).  \n[GNU GPLv3](https://choosealicense.com/licenses/gpl-3.0/), see [LICENSE](LICENSE) file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwp-spaghetti%2Fwonolog","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwp-spaghetti%2Fwonolog","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwp-spaghetti%2Fwonolog/lists"}