WP_Logging is a general use logging system that I have written for WordPress. It was first written for my Easy Digital Downloads but I have adapted it for general use. The main idea behind the class it to provide a simple solution for logging events, actions, errors, etc, inside of your WordPress plugins or themes.

The class provides a set of methods that can be used for recording log entries, relating entries to posts/pages/cpts, retrieving log entries, including those related to any post ID, and also retrieving total log counts.

Download the Class

In Easy Digital Downloads, the class (actually a slightly modified version) is used for tracking purchase histories, file downloads, and purchase errors from payment gateways. By storing this information, we’re much more easily able to present the store owner with an idea of what is happening behind the scenes of the site. For example, a meta box that displays the complete log history for all file downloads related to a particular product is shown to site admins:

Creating log systems like this is the entire point of the WP_Logging class.

Let’s see how you can use it.

Log Types

Log entries are designed to be separated into “types”. By default the class has “error” and event” log types. You can change these to anything you want by modifying the array in the log_types() method:

1
2
3
4
5
6
7
private function log_types() {
	$terms = array(
		'error', 'event'
	);
 
	return apply_filters( 'wp_log_types', $terms );
}

You could also register additional log types using a filter:

1
2
3
4
5
6
7
function pw_add_log_types( $types ) {
 
	$types[] = 'registration';
	return $types;
 
}
add_filter( 'wp_log_types', 'pw_add_log_types' );

Recording Log Entries

There are two ways to record a log entry, one is quick and simple and the one is more involved, but also gives more control.

Using add():

1
$log_entry = WP_Logging::add( $title = '', $message = '', $parent = 0, $type = null );

$title (string) – The log entry title
$message – The log entry message (string)
$parent (int) – The post object ID that you want this log entry connected to, if any
$type (string) – The type classification to give this log entry. Must be one of the types registered in log_types(). This is optional.

A sample log entry creation might look like this:

1
2
3
4
5
6
$title 		= 'Payment Error';
$message 	= 'There was an error processing the payment. Here are details of the transaction: (details shown here)';
$parent 	= 46; // This might be the ID of a payment post type item we want this log item connected to
$type 		= 'error';
 
WP_Logging::add( $title, $message, $parent, $type );

Or, without a type:

1
2
3
4
5
$title 		= 'Payment Error';
$message 	= 'There was an error processing the payment. Here are details of the transaction: (details shown here)';
$parent 	= 46; // This might be the ID of a payment post type item we want this log item connected to
 
WP_Logging::add( $title, $message, $parent );

Using insert_log():

1
$log_entry = WP_logging::insert_log( $log_data = array(), $log_meta = array() );

This method requires that all log data be passed via arrays. One array is used for the main post object data and one for additional log meta to be recorded with the log entry.

The $log_data array accepts all arguments that can be passed to [wp_insert_post()](http://codex.wordpress.org/Function_Reference/wp_insert_post), with one additional parameter for log_type.

The $log_data array expects key/value pairs for any meta data that should be recorded with the log entry. The meta data is stored is normal post meta, though all meta data is prefixed with _wp_log_.

Creating a log entry with insert_log() might look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
$log_data = array(
	'post_title' 	=> 'Payment Error',
	'post_content' 	=>  'There was an error processing the payment. Here are details of the transaction: (details shown here)',
	'post_parent'	=> 46, // This might be the ID of a payment post type item we want this log item connected to
	'log_type'		=> 'error'
);
 
$log_meta = array(
	'customer_ip' 	=> 'xxx.xx.xx.xx' // the customer's IP address
	'user_id' 		=> get_current_user_id() // the ID number of the currently logged-in user
);
 
$log_entry = WP_Logging::insert_log( $log_data, $log_meta );

Retrieving Log Entries

There are two methods for retrieving entries that have been stored via this logging class:

WP_Logging::get_logs( $object_id = 0, $type = null, $paged = null )
WP_Logging::get_connected_logs( $args = array() )

get_logs() is the simple method that lets you quickly retrieve logs that are connected to a specific post object. For example, to retrieve error logs connected to post ID 57, you’d do:

1
$logs = WP_Logging::get_logs( 57, 'error' );

This will retrieve the first 10 entries related to ID 57. Note that the third parameter is for $paged. This allows you to pass a page number (in the case you’re building an admin UI for showing logs) and WP_Logging will adjust the logs retrieved to match the page number.

If you need more granular control, you will want to use get_connected_logs(). This method takes a single array of key/value pairs as the only parameter. The $args array accepts all arguments that can be passed to [wp_insert_post()](http://codex.wordpress.org/Function_Reference/wp_insert_post), with one additional parameter for log_type.

Here’s an example of using get_connected_logs():

1
2
3
4
5
6
7
$args = array(
	'post_parent' 	=> 57,
	'posts_per_page'=> 10,
	'paged'			=> get_query_var( 'paged' ),
	'log_type'		=> 'error'
);
$logs = WP_Logging::get_connected_logs( $args );

If you want to retrieve all log entries and ignore pagination, you can do this:

1
2
3
4
5
6
$args = array(
	'post_parent' 	=> 57,
	'posts_per_page'=> -1,
	'log_type'		=> 'error'
);
$logs = WP_Logging::get_connected_logs( $args );

Both get_logs() and get_connected_logs() will return a typical array of post objects, just like [get_posts()](http://codex.wordpress.org/Template_Tags/get_posts)

Get Log Entry Counts

The get_log_count() method allows you to retrieve a count for the total number of log entries stored in the database. It allows you to retrieve logs attached to a specific post object ID, of a particular type, and also allows you to pass optional meta options for only counting log entries that have meta values stored.

The method looks like this:

1
WP_Logging:: get_log_count( $object_id = 0, $type = null, $meta_query = null )

To retrieve the total number of error logs attached to post 57, you can do this:

1
$count = WP_Logging::get_log_count( 57, 'error' );

To retrieve the total number of logs (regardless of type) attached to post object ID 57, you can do this:

1
$count = WP_Logging::get_log_count( 57 );

The third parameter is for passing a meta query array. This array should be in the same form as meta queries passed to [WP_Query](http://codex.wordpress.org/Class_Reference/WP_Query). For example, to retrieve a count of error log entries that have a user IP that match a specific IP address, you can do this:

1
2
3
4
5
6
7
$meta_query = array(
	array(
		'key' 	=> '_wp_log_customer_ip',	// the meta key
		'value' => 'xxx.xx.xx.xx'			// the IP address to retrieve logs for
	)
);
$count = WP_Logging::get_log_count( 57, 'error', $meta_query );

The Complete Class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
<?php
 
/**
 * Class for logging events and errors
 *
 * @package     WP Logging Class
 * @copyright   Copyright (c) 2012, Pippin Williamson
 * @license     http://opensource.org/licenses/gpl-2.0.php GNU Public License
*/
 
class WP_Logging {
 
 
	/**
	 * Class constructor.
	 *
	 * @since 1.0
	 *
	 * @access public
	 * @return void
	 */
	function __construct() {
 
		// create the log post type
		add_action( 'init', array( $this, 'register_post_type' ), -1 );
 
		// create types taxonomy and default types
		add_action( 'init', array( $this, 'register_taxonomy' ), -1 );
 
	}
 
 
	/**
	 * Log types
	 *
	 * Sets up the default log types and allows for new ones to be created
	 *
	 * @access      private
	 * @since       1.0
	 *
	 * @return     array
	*/
 
	private function log_types() {
		$terms = array(
			'error', 'event'
		);
 
		return apply_filters( 'wp_log_types', $terms );
	}
 
 
	/**
	 * Registers the wp_log Post Type
	 *
	 * @access      public
	 * @since       1.0
	 *
	 * @uses 		register_post_type()
	 *
	 * @return     void
	*/
 
	public function register_post_type() {
 
		/* logs post type */	
 
		$log_args = array(
			'labels'			=> array( 'name' => __( 'Logs', 'wp-logging' ) ),
			'public'			=> false,
			'query_var'			=> false,
			'rewrite'			=> false,
			'capability_type'	=> 'post',
			'supports'			=> array( 'title', 'editor' ),
			'can_export'		=> false
		); 
		register_post_type( 'wp_log', $log_args );
 
	}
 
 
	/**
	 * Registers the Type Taxonomy
	 *
	 * The Type taxonomy is used to determine the type of log entry
	 *
	 * @access      public
	 * @since       1.0
	 *
	 * @uses 		register_taxonomy()
	 * @uses 		term_exists()
	 * @uses 		wp_insert_term()
	 *
	 * @return     void
	*/
 
	public function register_taxonomy() {
 
		register_taxonomy( 'wp_log_type', 'wp_log' );
 
		$types = self::log_types();
 
		foreach ( $types as $type ) {
			if( ! term_exists( $type, 'wp_log_type' ) ) {
				wp_insert_term( $type, 'wp_log_type' );
			}
		}
	}
 
 
	/**
	 * Check if a log type is valid
	 *
	 * Checks to see if the specified type is in the registered list of types
	 *
	 * @access      private
	 * @since       1.0
	 *
	 *
	 * @return     array
	*/
 
	private function valid_type( $type ) {
		return in_array( $type, self::log_types() );
	}
 
 
	/**
	 * Create new log entry
	 *
	 * This is just a simple and fast way to log something. Use self::insert_log()
	 * if you need to store custom meta data
	 *
	 * @access      private
	 * @since       1.0
	 *
	 * @uses 		self::insert_log()
	 *
	 * @return      int The ID of the new log entry
	*/
 
	public static function add( $title = '', $message = '', $parent = 0, $type = null ) {
 
		$log_data = array(
			'post_title'   => $title,
			'post_content' => $message,
			'post_parent'  => $parent,
			'log_type'     => $type
		);
 
		return self::insert_log( $log_data );
 
	}
 
 
	/**
	 * Stores a log entry
	 *
	 * @access      private
	 * @since       1.0
	 *
	 * @uses 		wp_parse_args()
	 * @uses 		wp_insert_post()
	 * @uses 		update_post_meta()
	 * @uses 		wp_set_object_terms()
	 * @uses 		sanitize_key()
	 *
	 * @return      int The ID of the newly created log item
	*/
 
	public static function insert_log( $log_data = array(), $log_meta = array() ) {
 
		$defaults = array(
			'post_type'   => 'wp_log',
			'post_status' => 'publish',
			'post_parent' => 0,
			'post_content'=> '',
			'log_type'    => false
		);
 
		$args = wp_parse_args( $log_data, $defaults );
 
		do_action( 'wp_pre_insert_log' );
 
		// store the log entry
		$log_id = wp_insert_post( $args );
 
		// set the log type, if any
		if( $log_data['log_type'] && self::valid_type( $log_data['log_type'] ) ) {
			wp_set_object_terms( $log_id, $log_data['log_type'], 'wp_log_type', false );
		}
 
 
		// set log meta, if any
		if( $log_id && ! empty( $log_meta ) ) {
			foreach( (array) $log_meta as $key => $meta ) {
				update_post_meta( $log_id, '_wp_log_' . sanitize_key( $key ), $meta );
			}
		}
 
		do_action( 'wp_post_insert_log', $log_id );
 
		return $log_id;
 
	}
 
 
	/**
	 * Update and existing log item
	 *
	 * @access      private
	 * @since       1.0
	 *
	 * @uses 		wp_parse_args()
	 * @uses 		wp_update_post()
	 * @uses 		update_post_meta()
	 *
	 * @return      bool True if successful, false otherwise
	*/
	public static function update_log( $log_data = array(), $log_meta = array() ) {
 
		do_action( 'wp_pre_update_log', $log_id );
 
		$defaults = array(
			'post_type'   => 'wp_log',
			'post_status' => 'publish',
			'post_parent' => 0
		);
 
		$args = wp_parse_args( $log_data, $defaults );
 
		// store the log entry
		$log_id = wp_update_post( $args );
 
		if( $log_id && ! empty( $log_meta ) ) {
			foreach( (array) $log_meta as $key => $meta ) {
				if( ! empty( $meta ) )
					update_post_meta( $log_id, '_wp_log_' . sanitize_key( $key ), $meta );
			}
		}
 
		do_action( 'wp_post_update_log', $log_id );
 
	}
 
 
	/**
	 * Easily retrieves log items for a particular object ID
	 *
	 * @access      private
	 * @since       1.0
	 *
	 * @uses 		self::get_connected_logs()
	 *
	 * @return      array
	*/
 
	public static function get_logs( $object_id = 0, $type = null, $paged = null ) {
		return self::get_connected_logs( array( 'post_parent' => $object_id, 'paged' => $paged, 'log_type' => $type ) );
 
	}
 
 
	/**
	 * Retrieve all connected logs
	 *
	 * Used for retrieving logs related to particular items, such as a specific purchase.
	 *
	 * @access  private
	 * @since 	1.0
	 *
	 * @uses 	wp_parse_args()
	 * @uses 	get_posts()
	 * @uses 	get_query_var()
	 * @uses 	self::valid_type()
	 *
	 * @return  array / false
	*/
 
	public static function get_connected_logs( $args = array() ) {
 
		$defaults = array(
			'post_parent'    => 0,
			'post_type'      => 'wp_log',
			'posts_per_page' => 10,
			'post_status'    => 'publish',
			'paged'          => get_query_var( 'paged' ),
			'log_type'       => false
		);
 
		$query_args = wp_parse_args( $args, $defaults );
 
		if( $query_args['log_type'] && self::valid_type( $query_args['log_type'] ) ) {
 
			$query_args['tax_query'] = array(
				array(
					'taxonomy' => 'wp_log_type',
					'field'    => 'slug',
					'terms'    => $query_args['log_type']
				)
			);
 
		}
 
		$logs = get_posts( $query_args );
 
		if( $logs )
			return $logs;
 
		// no logs found
		return false;
 
	}
 
 
	/**
	 * Retrieves number of log entries connected to particular object ID
	 *
	 * @access  private
	 * @since 	1.0
	 *
	 * @uses 	WP_Query()
	 * @uses 	self::valid_type()
	 *
	 * @return  int
	*/
 
	public static function get_log_count( $object_id = 0, $type = null, $meta_query = null ) {
 
		$query_args = array(
			'post_parent'   => $object_id,
			'post_type'     => 'wp_log',
			'posts_per_page'=> -1,
			'post_status'   => 'publish'
		);
 
		if( ! empty( $type ) && self::valid_type( $type ) ) {
 
			$query_args['tax_query'] = array(
				array(
					'taxonomy' => 'wp_log_type',
					'field'    => 'slug',
					'terms'    => $type
				)
			);
 
		}
 
		if( ! empty( $meta_query ) ) {
			$query_args['meta_query'] = $meta_query;
		}
 
		$logs = new WP_Query( $query_args );
 
		return (int) $logs->post_count;
 
	}
 
}
$GLOBALS['wp_logs'] = new WP_Logging();

You can download or fork the class on Github.

  1. srcnix

    Nice class fella. I was going to create a logger for YAPS framework – mind if I fork and extend your class?

    • Pippin

      Fork all you want! If you make changes that would benefit everyone, I’d love to see some pull requests ;)

    • srcnix

      Mainly because then I do not need to maintain as much. Either that or how do you fancy, when released, extending the framework with your class?

    • Pippin

      Are you familiar with how to extend classes in PHP?

  2. srcnix

    Well, extending classes yes, but sadly PHP doesn’t allow injecting of methods like that of languages such as Ruby.

    However, I’m looking to add some clever setters and getters that will allow for extending core classes of such. For example, $yaps->setLogger(new LoggerClass()) would allow a global usage of $yaps->getLogger()->loggerClassMethod().

    Note: this process has yet to be thought out and was off the top of my head as an example.

    • srcnix

      Just to clarify,you can do method overridingand injection into classes but PHP needs to be compiled with said functionality and there’s a good chance user’s hosts won’t have.

    • Pippin

      Sounds pretty cool to me and I’d love to see what you come up with :D

  3. saas786

    Hi Pippin,
    Thanks for the class, I already have similar logging class, but only missing thing was relating it to objects (posts, pages, etc). And thanks for providing this and I also liked showing errors on Edit screens.

    Thanks,
    Syed

    • Pippin

      You’re welcome!

  4. drbigfresh

    This is great, and just what I was looking for. Anyone know if there is a hook or filter for when a plugin is installed or upgraded? I would love to be able to add a log entry so I can track what might be a system event that could cause errors. Thanks!

    • Pippin

      I’m not aware of one, though you could check for the presence of query vars. You’ll notice that there are several vars added to the URL when a plugin is activated.

Your email address will not be published. Required fields are marked *