Making WordPress.org

Changeset 8287


Ignore:
Timestamp:
02/20/2019 01:06:00 AM (7 years ago)
Author:
coreymckrill
Message:

WordCamp: More improvements to error handling/logging

  • Rename 0-slack.php to 0-error-handling.php, since that file's only purpose is to load the Slack library for error handling purposes anyway.
  • Move all of the error handling stuff from wcorg-misc.php to 0-error-handling.php. There's enough of it to warrant a separate file, and this way it all gets loaded before the rest of the mu-plugins so it can potentially catch errors there as well.
  • Run the custom error handler and shutdown function in other environments besides production, as long as the SANDBOX_SLACK_USERNAME constant is defined. This way the errors will be sent to the sandbox owner's Slack account, rather than the log channel.
  • In the shutdown function, only send the error to Slack if it is one of the types of errors that can't be handled in the custom error handler.
  • Modify the layout and contents of error messages sent to Slack to make them easier to parse when skimming through backscroll.
Location:
sites/trunk/wordcamp.org/public_html/wp-content/mu-plugins
Files:
1 added
1 deleted
1 edited

Legend:

Unmodified
Added
Removed
  • sites/trunk/wordcamp.org/public_html/wp-content/mu-plugins/wcorg-misc.php

    r8267 r8287  
    443443add_filter( 'map_meta_cap', 'wcorg_central_modify_export_caps', 10, 2 );
    444444
    445 define( 'ERROR_RATE_LIMITING_DIR', '/tmp/error_limiting' );
    446 
    447 /**
    448  * Check and create filesystem dirs to manage rate limiting in error handling.
    449  * For legacy bugs we are doing rate limiting via filesystem. We would be investigating to see if we can instead use memcache to rate limit sometime in the future.
    450  *
    451  * @return bool Return true if file permissions etc are present
    452  */
    453 function init_error_handling() {
    454     if ( ! file_exists( ERROR_RATE_LIMITING_DIR ) ) {
    455         mkdir( ERROR_RATE_LIMITING_DIR );
    456     }
    457 
    458     return is_dir( ERROR_RATE_LIMITING_DIR ) && is_writeable( ERROR_RATE_LIMITING_DIR );
    459 }
    460 
    461 /**
    462  * Error handler to send errors to slack. Always return false.
    463  */
    464 function send_error_to_slack( $err_no, $err_msg, $file, $line ) {
    465     if ( ! init_error_handling() ) {
    466         return false;
    467     }
    468 
    469     // Checks to see if the error-throwing expression is prepended with the @ control operator.
    470     // See https://secure.php.net/manual/en/function.set-error-handler.php
    471     if ( 0 === error_reporting() ) {
    472         error_clear_last();
    473 
    474         return false;
    475     }
    476 
    477     $error_safelist = [
    478         E_ERROR,
    479         E_USER_ERROR,
    480         E_CORE_ERROR,
    481         E_COMPILE_ERROR,
    482         E_PARSE,
    483         E_NOTICE,
    484         E_DEPRECATED,
    485         E_WARNING,
    486     ];
    487 
    488     if ( ! in_array( $err_no, $error_safelist ) ) {
    489         return false;
    490     }
    491 
    492     // Always use constants in the keys here to avoid path disclosure.
    493     $error_ignorelist = [
    494         // See https://core.trac.wordpress.org/ticket/29204
    495         ABSPATH . 'wp-includes/SimplePie/Registry.php:215' => 'Non-static method WP_Feed_Cache::create() should not be called statically',
    496     ];
    497 
    498     if ( isset( $error_ignorelist[ "$file:$line" ] ) && false !== strpos( $err_msg, $error_ignorelist[ "$file:$line" ] ) ) {
    499         return false;
    500     }
    501 
    502     // Max file length for ubuntu system is 255.
    503     $err_key = substr( base64_encode("$file-$line-$err_no" ), -254 );
    504 
    505     $error_file = ERROR_RATE_LIMITING_DIR . "/$err_key";
    506 
    507     $text = '';
    508 
    509     $data = array(
    510         'last_reported_at' => time(),
    511         'error_count'      => 0, // since last reported.
    512     );
    513 
    514     if ( ! file_exists( $error_file ) ) {
    515         $text = 'Error occured. ';
    516         file_put_contents( $error_file, wp_json_encode( $data ) );
    517     } else {
    518         $data                 = json_decode( file_get_contents( $error_file ), true );
    519         $data['error_count'] += 1;
    520         $time_elasped         = time() - $data['last_reported_at'];
    521 
    522         if ( $time_elasped > 600 ) {
    523             $text                     = "Still happening. Happened ${data['error_count']} time(s) since last reported. ";
    524             $data['last_reported_at'] = time();
    525             $data['error_count']      = 0;
    526 
    527             file_put_contents( $error_file, wp_json_encode( $data ) );
    528         } else {
    529             file_put_contents( $error_file, wp_json_encode( $data ) );
    530             return false;
    531         }
    532     }
    533 
    534     $domain    = get_site_url();
    535     $page_slug = esc_html( trim( $_SERVER['REQUEST_URI'], '/' ) );
    536     $text      = $text . "Message : \"$err_msg\" occurred on \"$file:$line\" \n Domain: $domain \n Page: $page_slug \n Error type: $err_no";
    537 
    538     $message = array(
    539         'fallback'    => $text,
    540         'color'       => '#ff0000',
    541         'pretext'     => "Error on \"$file:$line\" ",
    542         'author_name' => $domain,
    543         'text'        => $text,
    544     );
    545 
    546     $send = new \Dotorg\Slack\Send( SLACK_ERROR_REPORT_URL );
    547     $send->add_attachment( $message );
    548 
    549     $send->send( WORDCAMP_LOGS_SLACK_CHANNEL );
    550 
    551     return false;
    552 }
    553 
    554 /**
    555  * Shutdown handler which forwards errors to slack.
    556  */
    557 function send_fatal_to_slack() {
    558     $error = error_get_last();
    559     if ( ! $error ) {
    560         return;
    561     }
    562 
    563     return send_error_to_slack( $error['type'], $error['message'], $error['file'], $error['line'] );
    564 }
    565 
    566 if ( 'production' === WORDCAMP_ENVIRONMENT ) {
    567     register_shutdown_function( 'send_fatal_to_slack' );
    568     set_error_handler( 'send_error_to_slack', E_ERROR );
    569 }
    570 
    571 /**
    572  * Function `send_error_to_slack` above also creates a bunch of files in /tmp/error_limiting folder in order to rate limit the notification.
    573  * This function will be used as a cron to clear these error_limiting files periodically.
    574  */
    575 function handle_clear_error_rate_limiting_files() {
    576     if ( ! init_error_handling() ) {
    577         return;
    578     }
    579 
    580     foreach ( new DirectoryIterator( ERROR_RATE_LIMITING_DIR ) as $file_info ) {
    581         if ( ! $file_info->isDot() ) {
    582             unlink( $file_info->getPathname() );
    583         }
    584     }
    585 
    586 }
    587 
    588 add_action( 'clear_error_rate_limiting_files', 'handle_clear_error_rate_limiting_files' );
    589 
    590 if ( ! wp_next_scheduled( 'clear_error_rate_limiting_files' ) ) {
    591     wp_schedule_event( time(), 'daily', 'clear_error_rate_limiting_files' );
    592 }
    593 
    594445/**
    595446 * Allow individual site administrators to activate and deactivate optional plugins.
Note: See TracChangeset for help on using the changeset viewer.