options = get_option( self::opt, array() ); $this->table = $GLOBALS['wpdb']->prefix . '404_log'; if( !isset( $this->options['db_version'] ) || $this->options['db_version'] < self::db_version ) { // upgrade placeholder $this->install_table(); $defaults = array( 'max_entries' => 500, 'also_record' => array( 'ip', 'ua', 'ref' ), 'ignore_bots' => false, 'only_w_ref' => false ); foreach( $defaults as $k => $v ) if( !isset( $this->options[$k] ) ) $this->options[$k] = $v; $this->options['db_version'] = self::db_version; update_option( self::opt, $this->options ); } add_action( 'template_redirect', array( $this, 'log_404s' ) ); if( is_admin() ) { add_action( 'admin_menu', array( $this, 'settings_menu' ) ); add_action( 'admin_head', array( $this, 'load_table' ) ); add_action( 'wp_dashboard_setup', array( $this, 'load_dashboard_widget' ) ); } } private function install_table() { // remember, two spaces after PRIMARY KEY otherwise WP borks $sql = "CREATE TABLE $this->table ( id BIGINT NOT NULL AUTO_INCREMENT, date DATETIME NOT NULL, url VARCHAR(512) NOT NULL, ref VARCHAR(512) NOT NULL default '', ip VARCHAR(40) NOT NULL default '', ua VARCHAR(512) NOT NULL default '', PRIMARY KEY (id) );"; require_once(ABSPATH . 'wp-admin/includes/upgrade.php'); dbDelta($sql); } public function load_dashboard_widget() { wp_add_dashboard_widget( '404_error_log_widget', 'Latest 404 Errors', array($this, 'dashboard_widget') ); } public function dashboard_widget() { global $wpdb; $recent_errors = $wpdb->get_results("SELECT COUNT(url) as count, url, max(date) as latest FROM $this->table GROUP BY url ORDER BY id DESC LIMIT 10"); if($recent_errors) { echo ''; foreach($recent_errors as $error) { $url = esc_html( $error->url ); echo ''; } echo '
URLHitsLast hit
' . $url . '' . esc_html( $error->count ) . '' . $error->latest . '
'; echo '

More details are available in the error log.

'; } else { echo '

No errors to report!

'; } } public function settings_menu() { add_submenu_page('tools.php', '404 Error Log', '404 Error Log', 'manage_options', '404_error_log', array( $this, 'settings_page') ); // Register a page for CSV add_submenu_page('tools.php', '404 Error Log CSV', '404 Error Log CSV', 'manage_options', '404_error_log_csv', array( $this, 'csv') ); // ...But hide it remove_submenu_page('tools.php', '404_error_log_csv'); } public function load_table(){ // need to load the list table early for WP to catch it if( get_current_screen()->id == 'tools_page_404_error_log' && empty( $_GET['view'] ) ) { require_once( dirname( __FILE__ ) . '/includes/class-log-404-list-table.php' ); $this->list_table = new Log_404_List_Table( $this->options['also_record'] ); } } public function csv() { global $wpdb; if( isset( $_GET['csv'] ) && isset( $_GET['noheader'] ) && check_admin_referer('404_error_log_csv') ) { $orderby = ( isset( $_GET['orderby'] ) && in_array( $_GET['orderby'], array( 'date', 'url', 'ua', 'ref', 'ip' ) ) ) ? $_GET['orderby'] : 'id'; $order = ( isset( $_GET['order'] ) && strtolower( $_GET['order'] == 'asc' ) ) ? 'ASC' : 'DESC'; $rows = $wpdb->get_results( "SELECT date, url, ref, ip, ua FROM $this->table ORDER BY $orderby $order", ARRAY_N ); $fp = fopen('php://output', 'w'); $headers = array('Date', 'URL', 'Referrer', 'IP Address', 'User Agent'); if($rows && $fp){ header('Content-Type: text/csv'); header('Content-Disposition: attachment; filename="404_error_log.csv"'); header('Cache-Control: private, max-age=0'); fputcsv($fp, $headers); foreach($rows as $row){ fputcsv($fp, $row); } } else { header('Content-Type: text/plain'); echo 'An error occurred when generating the CSV.'; } exit; } } private function supported_cache_plugin_active(){ return ( defined('W3TC') || defined('WPCACHEHOME') || class_exists('HyperCache') ); } function settings_page() { ?>

404 Error Log Search results for “' . esc_attr( $_POST['s'] ) . '”'; ?>

You do not currently have pretty permalinks enabled on your site. This means that WordPress does not handle requests for pages that are not found on your site (your web server handles them directly), and so this plugin cannot log them. You need to be using pretty permalinks in order for this plugin to work.

'; echo ''; //wrap return; } if( WP_CACHE && !$this->supported_cache_plugin_active() ) :?>

Warning: It seems that a caching/performance plugin is active on this site. This plugin has only been tested with the following caching plugins:

Other caching plugins may cache responses to requests for pages that don't exist, in which case this plugin will not be able to intercept the requests and log them.

manage_options(); } else { if( isset( $_GET['delete_all'] ) ) { check_admin_referer( '404_error_log_delete' ); $this->delete_all_entries(); wp_redirect( menu_page_url('404_error_log', false) ); exit; } $this->show_log(); } // div is closed in show_log() } private function subsubsub(){ $manage_options = isset( $_GET['view'] ) && $_GET['view'] == 'options'; echo ''; } private function show_log(){ $this->list_table->prepare_items(); $this->subsubsub(); ?>
list_table->search_box( 'Search log', 'log' ); ?> list_table->display(); ?>

Delete all log entries

Download this table as CSV

options['also_record'] = empty( $_POST['also_record'] ) ? array() : (array) $_POST['also_record']; $this->options['max_entries'] = abs( intval( $_POST['max_entries'] ) ); $this->options['ignore_bots'] = isset( $_POST['ignore_bots'] ); $this->options['only_w_ref'] = isset( $_POST['only_w_ref'] ); update_option( self::opt, $this->options ); echo '

Options updated.

'; } $this->subsubsub(); ?>
Additional data to record 'HTTP Referrer', 'ip' => 'Client IP Address', 'ua' => 'Client User Agent' ) as $k => $v ) { $checked = checked( in_array( $k, $this->options['also_record'] ), true, false ); echo "
"; } ?>
Other options

options['ignore_bots'] && !empty( $_SERVER['HTTP_USER_AGENT'] ) && preg_match( '/(bot|spider)/', $_SERVER['HTTP_USER_AGENT'] ) ) return; if ( $this->options['only_w_ref'] && empty($_SERVER['HTTP_REFERER'] ) ) return; $data = array( 'date' => current_time('mysql'), 'url' => $_SERVER['REQUEST_URI'] ); if( in_array( 'ip', $this->options['also_record'] ) ) $data['ip'] = $_SERVER['REMOTE_ADDR']; if( in_array( 'ref', $this->options['also_record'] ) && isset( $_SERVER['HTTP_REFERER'] ) ) $data['ref'] = $_SERVER['HTTP_REFERER']; if( in_array( 'ua', $this->options['also_record'] ) && isset( $_SERVER['HTTP_USER_AGENT'] ) ) $data['ua'] = $_SERVER['HTTP_USER_AGENT']; // trim stuff foreach( array( 'url', 'ref', 'ua' ) as $k ) { if( isset( $data[$k] ) ) { $data[$k] = substr( $data[$k], 0, 512 ); } } $wpdb->insert( $this->table, $data ); // pop old entries if we exceeded the limit $max = intval( $this->options['max_entries'] ); $count = intval( $wpdb->get_var( "SELECT COUNT(*) FROM $this->table" ) ); if( $count > $max ) { $extra = intval( $count - $max ); $wpdb->query("DELETE FROM $this->table ORDER BY id ASC LIMIT $extra"); } } private function delete_all_entries() { global $wpdb; $wpdb->query("TRUNCATE TABLE $this->table;"); } } new Log_404();