Logging emails in WordPress with WP_Logging

I don’t know of any developer that’s been building sites for more than a few years that hasn’t accidentally sent a bunch of notifications from a development server to site users. I’ve done it, more than once actually, and if you haven’t it’s just going to happen.

Today we are going to look at how we can stop it happening on your WordPress development sites by capturing all outgoing emails and logging them with WP_Logging.

If you’d rather just get the screencast or see the code, jump to the bottom.

WP_Logging

We need to start be creating a basic plugin folder called wptt-email-logg and inside that we need to create a file called wptt-email-logg.php and use a basic WordPress plugin header as you see below.

/*
Plugin Name: WPTT Email Logging
Plugin URI: http://wpthemetutorial.com
Description: Stops all emails going out from WordPress and logs them.
Version: 1.0
Author: WP Theme Tutorial, Curtis McHale
Author URI: http://wpthemetutorial.com
License: GPLv2 or later
*/
 
/*
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

Now we need to get in to some actual code. Start by downloading WP_Logging by @pippinsplugins. WP_Logging is a prebuilt class that is mean to log events and errors in your WordPress project. It defines a custom post type for you, as well as a bunch of logging functions.

wp-logging-github

WP_Logging should just go at the root level of your plugin file. No need to get fancy for a simple plugin. We do need to include the file from our main plugin file.

require_once( plugin_dir_path( __FILE__ ) . 'WP_Logging.php' );

We have a few more things to do to see our logs though. By default WP_Logging doesn’t provide any type of visual view for our logs. Since it provides a filter on the register_post_type arguments, it’s easy enough to at least make the default CPT accessible from the WordPress admin area.

/**
 * Changes the default WP_Logging CPT items so that the default is to show
 * them in the WordPress admin.
 *
 * @since 1.0
 * @author SFNdesign, Curtis McHale
 */
function wptt_email_logg_cpt_mods( $args ){
 
    $args['public'] = true;
 
    return $args;
 
} // wptt_email_logg_cpt_mods
add_filter( 'wp_logging_post_type_args', 'wptt_email_logg_cpt_mods' );

So now we can see them, but that also means that our clients will be able to see any logs on the live site. In most cases that is not going to be exactly what we want. So lets set up a way to define a ‘development’ environment.

Adding to wp-config.php

The best spot to do this is in wp-config.php. We’re simply going to define a constant there and then check for it’s presence. Add the next line to wp-config.php.

define( ‘DEVELOPMENT’, true );

Now that we have a constant that tells us we are currently working under a development environment, we can change our filter call so that we only see the logs if that constant is defined, and true.

if ( defined( 'DEVELOPMENT' ) && DEVELOPMENT ){
    add_filter( 'wp_logging_post_type_args', 'wptt_email_logg_cpt_mods' );
} // if DEVELOPMENT

Now we are ready to dig in to stopping all outgoing emails, and logging them instead.

wp_mail

If you’re not familiar with wp_mail, I’ve written about it before. Basically all email that WordPress sends out goes through the wp_mail function. We want to capture all that email so that we don’t accidentally email site users a bunch of stuff they shouldn’t get.

Lucky for us, wp_mail is a pluggable function. The short version is that we can include our own function called wp_mail in our plugin and WordPress will use it instead.

One important thing to remember about pluggable functions is that you must wrap them in a function_exists conditional. When you activate your plugin for the first time wp_mail will already exist in WordPress and you will have a fatal error. Wrapping it in a function_exists check means that your plugin will actually activate.

if ( ! function_exists( 'wp_mail' ) && defined( 'DEVELOPMENT' ) && DEVELOPMENT ){
    function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() ){
 
    } // wp_mail
} // if

Notice that I not only wrapped it in a function_exists check, but I’m also checking for our DEVELOPMENT constant. We do want to send emails out on the live sites, so we don’t want to capture them here.

Actually Logging

WP_Logging provides 2 ways for us to generate a log.

WP_Logging::add()

This is the more ‘basic’ version and has 4 parameters.

  1. $title – The log entry title. Should be a string.
  2. $message – The log entry message. Should be a string.
  3. $parent – The post object ID the log should be attached to, if any. So if you want to generate a log for a payment, you’d add the payment ID here. Should be an integer.
  4. $type – Log type. Must be one of the register log types in log_types(). event and error are available by default.

To generate our email log we just need to pass in the information we want.

if ( ! function_exists( 'wp_mail' ) && defined( 'DEVELOPMENT' ) && DEVELOPMENT ){
    function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() ){
 
        $title = 'Logged WordPress Email to' . $to,
 
        WP_Logging::add( $title, $message, '', 'event' )
 
    } // wp_mail
} // if

Yes we get a basic log out of this, but there is a different (better in my opinion) way to add logs and catch way more information.

WP_Logging::insert_log()

I almost always use WP_Logging::insert_log() to capture any events or errors. It provides us with 2 arrays to store information.

$log_data takes an array of 4 parameters.

  1. post_title – The title of the log you are creating. Should be a string.
  2. post_content – The ‘message’ you want as the main body of the log. Should be a string.
  3. post_parent – The post object ID that the log is related to. Should be an integer.
  4. log_type – Any of the registered log types.

So that’s all the same information we had with our first basic function. The real power comes with the second array that WP_Logging provides for us called $log_meta.

$log_meta takes as many items as you want. Really it’s any type of metadata you want to store about the current log. So lets take a look at our wp_mail function using WP_Logging::insert_log().

if ( ! function_exists( 'wp_mail' ) && defined( 'DEVELOPMENT' ) && DEVELOPMENT ){
    function wp_mail( $to, $subject, $message, $headers = '', $attachments = array() ){
 
        global $post;
 
        $log_data = array(
            'post_title'     => 'Logged WordPress email to ' . $to . '',
            'post_content'   => $message,
            'post_parent'    => $post->ID,
            'log_type'       => 'event',
        );
 
        $log_meta = array(
            'to'           => $to,
            'subject'      => $subject,
            'message'      => $message,
            'headers'      => $headers,
            'attachments'  => $attachments,
            'current_user' => wp_get_current_user(),
            'post_object'  => $post,
        );
 
        WP_Logging::insert_log( $log_data, $log_meta );
 
    } // wp_mail
} // if

Now not only am I collecting the basic information out of wp_mail I’m collecting a bunch of information about who the current user is, and the current post object. When you’re debugging there can never be enough information.

Drawbacks

Now there are a few things that WP_Logging doesn’t do for you. Remeber that we had to set the default Custom Post Type arguments to true, so that means that WP_Logging doesn’t provide a way to view the logs. Setting the post type to viewable, really just shows us the title and message and log type. We still won’t see the $log_meta we collected. The $log_meta is in the database but you have to go looking for it.

Now why is there no UI for WP_Logging? Really so many different types of data can be logged, it’s hard to come up with one generic version that’s in any way suitable for most scenarios.

Another thing to note is that WP_Logging does not do any pruning of logs. So your development site will just keep building up logs and the database will keep growing. If you end up using it to catch stuff on a live site, you probably also want to build a process to prune the logs every couple weeks.

Get the plugin on Github

Screencast

Curtis McHale

Posts Twitter Google+

Curtis is a web designer/developer specialized in WordPress eCommerce development on WooCommerce. You can get in touch with him about projects on SFNdesign.
My Blog is 4 Times Faster Than Your Blog

Trackbacks and Pingbacks:

  1. Logging wp_mail during development with WP_Logging : Post Status - May 24, 2013

    [...] Logging wp_mail during development with WP_Logging » [...]

  2. WordPress Pluggable Functions Logging emails with wp_mail Guide Part 2 - August 7, 2013

    […] Now we’re going to walk through a plugin I have on Github called WPTT Basic Email Logging. I’m not going to cover the entire plugin, just the wp_mail portion. If you’d like to see the whole plugin explained you can visit this post. […]

  3. The WPLift Guide to Pluggable Functions Part 2: Logging emails with wp_mail - WordPress KB - October 10, 2013

    […] Now we’re going to walk through a plugin I have on Github called WPTT Basic Email Logging. I’m not going to cover the entire plugin, just the wp_mail portion. If you’d like to see the whole plugin explained you can visit this post. […]