All developers spend a lot of time debugging code. The thing that makes this frustrating is that it isn’t always obvious what the exact problem is, and people are left guessing. As someone who provides support for WordPress users for all experience levels, I hate saying “something is wrong, we’re going to have to do some work to find out what something means” but too often that’s what I have to do. This has put me on a mission to find better ways to discover these problems fast. In this article I want to share some cool stuff we built into Caldera Forms Pro that you can use in your own WordPress plugins and sites to better understand bugs, conflicts, and other issues.
As PHP and JavaScript developers we have two main tools for understanding where errors are happening and what the value of variables are at different states of the application — logging and step through debuggers. Step through debuggers like xDebug and Chrome Dev Tools are invaluable components of a web developer’s toolset, but logging is also important, and improving the quality of logging for WordPress, especially from plugins is something I’ve become a bit obsessed with, I want to share some tricks I’ve found to improve logging.
WordPress has a debug log that can be enabled with the constant WP_DEBUG_LOG. I wrote a very simple guide to WordPress’ built in debugging here. WordPress’ default debug logging leaves a lot to be desired. It’s limited in what it can do and does not follow the established standard for formatting log messages in a PHP application PSR-3.
The open-source library Monolog is the most common utility for interfacing with log files and other log directories. It’s highly extensive, and works great with WordPress. The good people at Inpsyde — a German WordPress development agency — recently developed an excellent open source tool for using Monolog with WordPress called Wonolog. In this post I’ll show you how to use Wonolog in your WordPress site and plugin.
With Wonolog in place, you can capture all PHP and database errors, warnings and notices on the site. By making use of some of Monolog’s error handlers, we can not just write errors to a log file, but also send those errors via email, Slack or other services. It’s a great example of how much we can improve WordPress development when we embrace standard tools fo PHP development.
A Quick Note On Composer
In this article, I’m going to assume you’re familiar with using the PHP package manager Composer with WordPress. If you’re not already using Composer, you really should be.
If you’re familiar with Composer, but have never used it with WordPress, this post will explain how to use it for plugin development. For full site development, I recommend the post I wrote on using Composer, Forge and WP Starter to manage complete WordPress sites.
Getting Started
With Wonolog in place, you can capture all PHP and database errors, warnings and notices on the site. By making use of some of Monolog’s error handlers, we can not just write errors to a log file, but also send those errors via email, Slack or other services. It’s a great example of how much we can improve WordPress development when we embrace standard tools for PHP development.
Using Wonolog is really easy. All you really need to do is install Wonolog via composer –
composer require inpsyde/Wonolog
Wonlog is loaded by using the bootstrap function:
\Inpsyde\Wonolog\bootstrap()
If you’re using Wonlogo for your complete site, the best place to do this is in a mu-plugin. For a plugin, you should bootstrap wonolog in your mail plugin file.
If all you want is better — IE PSR-3 compliant — logging to file, that’s it. You’ve got all error handling done. You can also use Wonolog to log data when a specific hook is fired using a custom hook listener.
You can also use the wonolog.log action to log any data, like this:
<?php do_action( 'wonolog.log', [ 'message' => 'Something happened.', 'channel' => 'DEBUG', 'level' => 100, 'context' => [], ] );
Sending Emails On Critical Errors
By default, Wonolog implements a Monolog Handler to log messages to file. One great thing about Monolog is that you can use more than one handler. Monolog ships with a lot of pre-designed Handlers we can use to send all or some errors to an alternative location.
Monolog formalizes log levels. These are important as for some integrations we might only want to be alerted of serious issues. For example, what if we wanted to capture everything to a file, but get site errors sent via email? Let’s look at how to do that with Wonolog.
First, we will need to setup a custom handler. Monolog has a native email handler that uses PHP’s mail function to send an email. Let’s start with that.
Wonolog’s bootstrap function returns an instance of the Wonolog\Controller class, which let’s us add additional handlers. We can use the use_handler() method of that class to add an additional handler, such as a NativeMailerHandler:
<?php use Monolog\Logger; use \Monolog\Handler\NativeMailerHandler; $email_handler = new NativeMailerHandler( '[email protected]', 'Error on ' . home_url(), '[email protected]', Logger::ERROR ); \Inpsyde\Wonolog\bootstrap( ) ->use_handler( $email_handler );
Monolog’s NativeMailerHandler uses the function mail() to send an email. In WordPress we normally use wp_mail(), which by default wraps PHP Mailer, but is often replaced. For better compatibility, we can extend NativeMailerHandler and ovveride it’s send() method to use wp_mail() instead.
I created a WPMailHandler class by cut and pasting the send() method and replacing mail() with wp_mail()
<?php namespace Example; use Monolog\Formatter\LineFormatter; use \Monolog\Handler\NativeMailerHandler; class WPMailHanlder extends NativeMailerHandler { /** * {@inheritdoc} */ protected function send(string $content, array $records) { $content = wordwrap($content, $this->maxColumnWidth); $headers = ltrim(implode("\r\n", $this->headers) . "\r\n", "\r\n"); $headers .= 'Content-type: ' . $this->getContentType() . '; charset=' . $this->getEncoding() . "\r\n"; if ($this->getContentType() == 'text/html' && false === strpos($headers, 'MIME-Version:')) { $headers .= 'MIME-Version: 1.0' . "\r\n"; } $subject = $this->subject; if ($records) { $subjectFormatter = new LineFormatter($this->subject); $subject = $subjectFormatter->format($this->getHighestRecord($records)); } foreach ($this->to as $to) { wp_mail($to, $subject, $content, $headers); } } }
With that in place, we can add this WPMailHandler like this:
<?php use Monolog\Logger; use \Monolog\Handler\NativeMailerHandler; use Example; $email_handler = new WPMailHanlder( '[email protected]', 'Error on ' . home_url(), '[email protected]', Logger::ERROR ); \Inpsyde\Wonolog\bootstrap( ) ->use_handler( $email_handler );
Sending Log Messages To Slack
One thing I’ve found really useful is to send errors of a certain level to a Slack channel making use of a Slack Webhook. Monolog has built in Slack Handler, which makes this really easy. One thing I like to do is set the lowest level of error via an environment variable so that I can easily adjust what level is sent without touching code.
The Monolog\Handler\SlackWebhookHandler class only need the URL of a Slack webhook to function. But to customize the level, like I explained, we need to use the 8th argument of the class’ constructor. I also like to use the second argument to set the channel, and the 4th to set the emoji:
<?php use Monolog\Logger; use \Monolog\Handler\SlackWebhookHandler; $slack = new SlackWebhookHandler( 'https://hooks.slack.com/services/the-rest-of-your-url', 'channel-name', null, null, 'scream_cat', //Choose your emoji:) false, false, isset( $_ENV[ 'SLACK_MIN_LEVEL' ] ) ? $_ENV[ 'SLACK_MIN_LEVEL' ] : Logger::ERROR // Use ENV variable for min level to send, else use ERROR ); \Inpsyde\Wonolog\bootstrap( ) ->use_handler( $slack );
Of course, we probably want to use multiple, handlers. The use_handler() method implements a fluid-interface — it returns the modified instance of the class. That means we can chain multiple calls to use_handler(). For example, we can combine email and slack handlers, like this:
<?php use Monolog\Logger; use \Monolog\Handler\SlackWebhookHandler; // Create our WP Mail email handler $email = new WPMailHanlder( '[email protected]', 'Error on ' . home_url(), '[email protected]', Logger::ERROR ); //Create slack handler $slack = new SlackWebhookHandler( 'https://hooks.slack.com/services/the-rest-of-your-url', 'channel-name', null, null, 'scream_cat', //Choose your emoji:) false, false, isset( $_ENV[ 'SLACK_MIN_LEVEL' ] ) ? $_ENV[ 'SLACK_MIN_LEVEL' ] : Logger::ERROR // Use ENV variable for min level to send, else use ERROR ); //Start Wonolog \Inpsyde\Wonolog\bootstrap( ) //Add slack handler ->use_handler( $slack ) //Add email handler ->use_handler( $email );
Sending Log Message To Papertrail
I’m a big fan of the service Papertrail. They provide a live updating view of log files. I’ve written about integrating Papertrail with WordPress for Torque before. Monolog doesn’t have a built-in Papertrail handler, which makes for a great example of how to repurpose an existing handler.
For Papertrail we can use Monolog’s SyslogUdpHandler class, since Papertrail is designed to work with Syslogs. We will need to provide a formatter and the URL and port for Papertrail. Here is that handler, integrated with the other three:
<?php use Monolog\Logger; use \Monolog\Handler\SlackWebhookHandler; use Monolog\Formatter\LineFormatter; use Monolog\Handler\SyslogUdpHandler; // Create our WP Mail email handler $email = new WPMailHanlder( '[email protected]', 'Error on ' . home_url(), '[email protected]', Logger::ERROR ); //Create slack handler $slack = new SlackWebhookHandler( 'https://hooks.slack.com/services/the-rest-of-your-url', 'channel-name', null, null, 'scream_cat', //Choose your emoji:) false, false, isset( $_ENV[ 'SLACK_MIN_LEVEL' ] ) ? $_ENV[ 'SLACK_MIN_LEVEL' ] : Logger::ERROR // Use ENV variable for min level to send, else use ERROR ); //Create Papertrail handler $output = "%channel%.%level_name%: %message%"; $formatter = new LineFormatter( $output ); //make sure to set your URL and port here $papertrail = new SyslogUdpHandler("logs6.papertrailapp.com", 111 ); $papertrail->setFormatter( $formatter ); //Start Wonolog \Inpsyde\Wonolog\bootstrap( ) //Add slack handler ->use_handler( $slack ) //Add email handler ->use_handler( $email ) //Add Papertrial handler ->use_handler( $papertrail );
Go Further
I encourage you to look through the docs for Wonolog and Monolog to see other ways you can use these great tools to improve your WordPress logging. Monolog works with many of the service you might already be using — for example New Relic. So see what Monolog can do and how it can work into your existing workflow. You can also look at how we set this up in Caldera Forms Pro’s WordPress client plugin. The code in that plugin is, not coincedently very similar to the example code in this article. You can use that as an example.
1 Comment