├── assets ├── css │ └── main.css └── js │ └── main.js ├── classes ├── CommonPanel.php ├── EP_Debug_Bar_ElasticPress.php ├── QueryFormatter.php ├── QueryLog.php ├── QueryMonitorCollector.php ├── QueryMonitorOutput.php └── QueryOutput.php ├── debug-bar-elasticpress.php ├── lang └── debug-bar-elasticpress.pot └── readme.txt /assets/css/main.css: -------------------------------------------------------------------------------- 1 | .ep-debug-bar-warning { 2 | background-color: #ffffe0; 3 | border: 1px solid #e6db55; 4 | clear: both; 5 | padding: 0.5em 10px; 6 | } 7 | 8 | .ep-queries-debug pre { 9 | padding: 10px; 10 | } 11 | 12 | .ep-queries-debug .dashicons { 13 | cursor: pointer; 14 | } 15 | 16 | .ep-queries-debug .dashicons::before { 17 | content: "\f140"; 18 | } 19 | 20 | .ep-queries-debug .hide-query-results .query-results, 21 | .ep-queries-debug .hide-query-args .query-args, 22 | .ep-queries-debug .hide-query-headers .query-headers, 23 | .ep-queries-debug .hide-query-errors .query-errors, 24 | .ep-queries-debug .hide-query-body .query-body { 25 | display: none; 26 | } 27 | 28 | .ep-queries-debug .query-body, 29 | .ep-queries-debug .query-args, 30 | .ep-queries-debug .query-headers, 31 | .ep-queries-debug .query-errors, 32 | .ep-queries-debug .query-results { 33 | background-color: white; 34 | } 35 | 36 | .ep-queries-debug .hide-query-results .query-result-toggle::before, 37 | .ep-queries-debug .hide-query-args .query-args-toggle::before, 38 | .ep-queries-debug .hide-query-headers .query-headers-toggle::before, 39 | .ep-queries-debug .hide-query-errors .query-errors-toggle::before, 40 | .ep-queries-debug .hide-query-body .query-body-toggle::before { 41 | content: "\f142"; 42 | } 43 | 44 | .ep-queries-debug .ep-query-failed .ep-query-response-code { 45 | color: red; 46 | font-weight: bold; 47 | } 48 | 49 | .ep-queries-buttons-wrapper { 50 | align-items: center; 51 | clear: both; 52 | display: flex; 53 | gap: 10px; 54 | padding: 1em 0; 55 | } 56 | 57 | .ep-queries-debug-container .copy-curl { 58 | border-color: #000; 59 | border-radius: 3px; 60 | border-style: solid; 61 | border-width: 1px; 62 | color: #000; 63 | cursor: pointer; 64 | display: inline-block; 65 | font-size: 13px; 66 | line-height: 2.1538; 67 | min-height: 30px; 68 | padding: 0 10px; 69 | text-decoration: none; 70 | } 71 | 72 | .ep-queries-debug-container { 73 | clear: left; 74 | } 75 | 76 | /* stylelint-disable selector-id-pattern */ 77 | 78 | /* The giant list of selectors here is needed to 79 | * override QM's important rules 80 | */ 81 | #debug-menu-target-EP_Debug_Bar_ElasticPress .button, 82 | #debug-menu-target-EP_Debug_Bar_ElasticPress a.button, 83 | #qm-elasticpress .button, 84 | #qm-elasticpress a.button { 85 | background: #f6f7f7 !important; 86 | border: 1px solid #2271b1 !important; 87 | border-radius: 3px !important; 88 | 89 | /* Overriding some Debug Bar "important" rules */ 90 | color: #2271b1 !important; 91 | cursor: pointer; 92 | display: inline-block; 93 | font-family: inherit; 94 | font-size: 13px; 95 | line-height: 2.1538 !important; 96 | padding: 0 10px !important; 97 | text-decoration: none !important; 98 | text-shadow: none; 99 | } 100 | 101 | #debug-menu-target-EP_Debug_Bar_ElasticPress .button:hover, 102 | #query-monitor-main #qm-elasticpress .button:hover { 103 | background: #f0f0f1; 104 | border-color: #0a4b78; 105 | color: #0a4b78 !important; 106 | text-decoration: none !important; 107 | } 108 | 109 | #debug-menu-target-EP_Debug_Bar_ElasticPress .button:active, 110 | #query-monitor-main #qm-elasticpress .button:hover { 111 | background: #f6f7f7 !important; 112 | border-color: #8c8f94 !important; 113 | box-shadow: none; 114 | } 115 | 116 | #debug-menu-target-EP_Debug_Bar_ElasticPress .button:focus, 117 | #query-monitor-main #qm-elasticpress .button:focus { 118 | outline: none; 119 | } 120 | 121 | /* Overriding some Debug Bar "important" rules */ 122 | #debug-menu-target-EP_Debug_Bar_ElasticPress .button-primary, 123 | #debug-menu-target-EP_Debug_Bar_ElasticPress a.button-primary, 124 | #debug-menu-target-EP_Debug_Bar_ElasticPress button.button-primary, 125 | #query-monitor-main #qm-elasticpress .button-primary { 126 | background: #007cba !important; 127 | border: 1px solid #007cba !important; 128 | color: #fff !important; 129 | } 130 | 131 | #debug-menu-target-EP_Debug_Bar_ElasticPress a.button-primary:hover, 132 | #debug-menu-target-EP_Debug_Bar_ElasticPress a.button-primary:focus, 133 | #query-monitor-main #qm-elasticpress a.button-primary:hover, 134 | #query-monitor-main #qm-elasticpress a.button-primary:focus { 135 | background: #007cba !important; 136 | color: #fff !important; 137 | text-decoration: none !important; 138 | } 139 | 140 | #query-monitor-main #qm-elasticpress h3, 141 | #query-monitor-main #qm-elasticpress p { 142 | margin: 0 !important; 143 | padding: 0 !important; 144 | text-align: center !important; 145 | } 146 | 147 | #query-monitor-main #qm-elasticpress li { 148 | border-top: 1px solid var(--qm-panel-separator) !important; 149 | line-height: 20px !important; 150 | padding: 20px 0 !important; 151 | } 152 | 153 | /* stylelint-enable selector-id-pattern */ 154 | -------------------------------------------------------------------------------- /assets/js/main.js: -------------------------------------------------------------------------------- 1 | /* global ClipboardJS */ 2 | 3 | wp.domReady(() => { 4 | const copyBtn = document.querySelectorAll('.ep-copy-button'); 5 | const clipboard = new ClipboardJS(copyBtn); 6 | 7 | /** 8 | * Handle successful copy. 9 | * 10 | * @param {Event} event Copy event. 11 | * @returns {void} 12 | */ 13 | const onSuccess = (event) => { 14 | event.trigger.nextElementSibling.style.display = 'initial'; 15 | setTimeout(() => { 16 | event.trigger.nextElementSibling.style.display = 'none'; 17 | }, 3000); 18 | event.clearSelection(); 19 | }; 20 | 21 | /** 22 | * Bind copy button events. 23 | */ 24 | clipboard.on('success', onSuccess); 25 | 26 | let queries = document.querySelectorAll('.ep-queries-debug'); 27 | 28 | if (queries.length > 0) { 29 | queries = queries[0]; 30 | 31 | queries.addEventListener('click', function (event) { 32 | let queryWrapper = event.target; 33 | 34 | while (event.currentTarget.contains(queryWrapper)) { 35 | if (queryWrapper.nodeName === 'LI') { 36 | if (event.target.className.match(/query-body-toggle/i)) { 37 | if (queryWrapper.className.match(/hide-query-body/i)) { 38 | queryWrapper.className = queryWrapper.className.replace( 39 | /hide-query-body/i, 40 | '', 41 | ); 42 | } else { 43 | queryWrapper.className += ' hide-query-body'; 44 | } 45 | } 46 | 47 | if (event.target.className.match(/query-result-toggle/i)) { 48 | if (queryWrapper.className.match(/hide-query-results/i)) { 49 | queryWrapper.className = queryWrapper.className.replace( 50 | /hide-query-results/i, 51 | '', 52 | ); 53 | } else { 54 | queryWrapper.className += ' hide-query-results'; 55 | } 56 | } 57 | 58 | if (event.target.className.match(/query-args-toggle/i)) { 59 | if (queryWrapper.className.match(/hide-query-args/i)) { 60 | queryWrapper.className = queryWrapper.className.replace( 61 | /hide-query-args/i, 62 | '', 63 | ); 64 | } else { 65 | queryWrapper.className += ' hide-query-args'; 66 | } 67 | } 68 | 69 | if (event.target.className.match(/query-headers-toggle/i)) { 70 | if (queryWrapper.className.match(/hide-query-headers/i)) { 71 | queryWrapper.className = queryWrapper.className.replace( 72 | /hide-query-headers/i, 73 | '', 74 | ); 75 | } else { 76 | queryWrapper.className += ' hide-query-headers'; 77 | } 78 | } 79 | 80 | if (event.target.className.match(/query-errors-toggle/i)) { 81 | if (queryWrapper.className.match(/hide-query-errors/i)) { 82 | queryWrapper.className = queryWrapper.className.replace( 83 | /hide-query-errors/i, 84 | '', 85 | ); 86 | } else { 87 | queryWrapper.className += ' hide-query-errors'; 88 | } 89 | } 90 | 91 | break; 92 | } else { 93 | queryWrapper = queryWrapper.parentNode; 94 | } 95 | } 96 | }); 97 | } 98 | }); 99 | -------------------------------------------------------------------------------- /classes/CommonPanel.php: -------------------------------------------------------------------------------- 1 | get_query_log() ); 24 | 25 | if ( $queries_count ) { 26 | return sprintf( 27 | /* translators: %d: number of queries */ 28 | esc_html__( 'ElasticPress (%d)', 'debug-bar-elasticpress' ), 29 | $queries_count 30 | ); 31 | } 32 | 33 | return esc_html__( 'ElasticPress', 'debug-bar-elasticpress' ); 34 | } 35 | 36 | /** 37 | * Enqueue scripts for front end and admin 38 | */ 39 | public function enqueue_scripts_styles() { 40 | if ( ! is_user_logged_in() ) { 41 | return; 42 | } 43 | 44 | wp_enqueue_script( 'debug-bar-elasticpress', EP_DEBUG_URL . 'assets/js/main.js', array( 'wp-dom-ready', 'clipboard' ), EP_DEBUG_VERSION, true ); 45 | wp_enqueue_style( 'debug-bar-elasticpress', EP_DEBUG_URL . 'assets/css/main.css', array(), EP_DEBUG_VERSION ); 46 | } 47 | 48 | /** 49 | * Show the contents of the panel 50 | */ 51 | public function render() { 52 | $queries = \ElasticPress\Elasticsearch::factory()->get_query_log(); 53 | 54 | if ( function_exists( '\ElasticPress\Utils\is_indexing' ) && \ElasticPress\Utils\is_indexing() ) { 55 | ?> 56 |
57 | 58 |
59 | render_buttons(); 64 | $debug_bar_output->render_additional_buttons(); 65 | $debug_bar_output->render_queries(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /classes/EP_Debug_Bar_ElasticPress.php: -------------------------------------------------------------------------------- 1 | title( esc_html__( 'ElasticPress', 'debug-bar-elasticpress' ) ); 39 | 40 | $this->common_panel = new \DebugBarElasticPress\CommonPanel(); 41 | $this->common_panel->enqueue_scripts_styles(); 42 | } 43 | 44 | /** 45 | * Enqueue scripts for front end and admin 46 | */ 47 | public function enqueue_scripts_styles() { 48 | _deprecated_function( __METHOD__, '3.1.0', 'DebugBarElasticPress\EP_Panel::enqueue_scripts_styles()' ); 49 | } 50 | 51 | /** 52 | * Show the menu item in Debug Bar. 53 | */ 54 | public function prerender() { 55 | $this->set_visible( true ); 56 | } 57 | 58 | /** 59 | * Show the contents of the panel 60 | */ 61 | public function render() { 62 | $queries = \ElasticPress\Elasticsearch::factory()->get_query_log(); 63 | $total_query_time = 0; 64 | 65 | foreach ( $queries as $query ) { 66 | if ( ! empty( $query['time_start'] ) && ! empty( $query['time_finish'] ) ) { 67 | $total_query_time += ( $query['time_finish'] - $query['time_start'] ); 68 | } 69 | } 70 | 71 | ?> 72 | 73 |

74 | Total ElasticPress Queries: %d', 'debug-bar-elasticpress' ), count( $queries ) ) 78 | ); 79 | ?> 80 |

81 |

82 | Total Blocking ElasticPress Query Time: %d ms', 'debug-bar-elasticpress' ), (int) ( $total_query_time * 1000 ) ) 86 | ); 87 | ?> 88 |

89 | 90 | common_panel->render(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /classes/QueryFormatter.php: -------------------------------------------------------------------------------- 1 | esc_html__( 'Page URL', 'debug-bar-elasticpress' ), 32 | 'es_req' => esc_html__( 'Elasticsearch Request', 'debug-bar-elasticpress' ), 33 | 'request_id' => esc_html__( 'Request ID', 'debug-bar-elasticpress' ), 34 | 'timestamp' => esc_html__( 'Time', 'debug-bar-elasticpress' ), 35 | 'query_time' => esc_html__( 'Time Spent (ms)', 'debug-bar-elasticpress' ), 36 | 'wp_args' => esc_html__( 'WP Query Args', 'debug-bar-elasticpress' ), 37 | 'status_code' => esc_html__( 'HTTP Status Code', 'debug-bar-elasticpress' ), 38 | 'body' => esc_html__( 'Query Body', 'debug-bar-elasticpress' ), 39 | 'result' => esc_html__( 'Query Result', 'debug-bar-elasticpress' ), 40 | ]; 41 | 42 | $failed_queries_obj = new \ElasticPress\StatusReport\FailedQueries( $this ); 43 | 44 | foreach ( $queries as $query ) { 45 | $query = $this->format_log_entry( $query, 'query' ); 46 | list( $error, $solution ) = $failed_queries_obj->analyze_log( $query ); 47 | 48 | $fields = []; 49 | if ( ! empty( $error ) ) { 50 | $fields['error'] = [ 51 | 'label' => __( 'Error', 'debug-bar-elasticpress' ), 52 | 'value' => $error, 53 | ]; 54 | $fields['recommended_solution'] = [ 55 | 'label' => __( 'Recommended Solution', 'debug-bar-elasticpress' ), 56 | 'value' => $solution, 57 | ]; 58 | } 59 | 60 | foreach ( $query as $field => $value ) { 61 | // Already outputted in the title 62 | if ( in_array( $field, [ 'wp_url', 'timestamp' ], true ) ) { 63 | continue; 64 | } 65 | 66 | $fields[ $field ] = [ 67 | 'label' => $labels[ $field ] ?? $field, 68 | 'value' => $value, 69 | ]; 70 | } 71 | 72 | $formatted_queries[] = [ 73 | 'title' => sprintf( '%s (%s)', $query['wp_url'], date_i18n( 'Y-m-d H:i:s', $query['timestamp'] ) ), 74 | 'fields' => $fields, 75 | ]; 76 | } 77 | 78 | return $formatted_queries; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /classes/QueryLog.php: -------------------------------------------------------------------------------- 1 | sanitize_enable_logging( $_POST['ep_enable_logging'] ) ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput 65 | if ( isset( $_POST['ep_query_log_by_status'] ) ) { 66 | update_site_option( 'ep_query_log_by_status', sanitize_text_field( wp_unslash( $_POST['ep_query_log_by_status'] ) ) ); 67 | } 68 | if ( ! empty( $_POST['ep_query_log_by_context'] ) ) { 69 | $ep_query_log_by_context = array_map( 'sanitize_text_field', wp_unslash( $_POST['ep_query_log_by_context'] ) ); 70 | update_site_option( 'ep_query_log_by_context', $ep_query_log_by_context ); 71 | } else { 72 | update_site_option( 'ep_query_log_by_context', [] ); 73 | } 74 | } else { 75 | register_setting( 76 | 'ep-debug', 77 | 'ep_enable_logging', 78 | [ 'sanitize_callback' => [ $this, 'sanitize_enable_logging' ] ] 79 | ); 80 | register_setting( 81 | 'ep-debug', 82 | 'ep_query_log_by_status', 83 | [ 'sanitize_callback' => 'sanitize_text_field' ] 84 | ); 85 | register_setting( 86 | 'ep-debug', 87 | 'ep_query_log_by_context', 88 | [ 89 | 'sanitize_callback' => function ( $value ) { 90 | return ! empty( $value ) ? array_map( 'sanitize_text_field', $value ) : []; 91 | }, 92 | ] 93 | ); 94 | } 95 | } 96 | 97 | /** 98 | * Clear query log 99 | * 100 | * @since 1.3 101 | */ 102 | public function maybe_clear_log() { 103 | if ( 104 | empty( $_GET['ep_clear_query_log'] ) 105 | || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['ep_clear_query_log'] ) ), 'ep_clear_query_log' ) 106 | ) { 107 | return; 108 | } 109 | 110 | Utils\delete_option( 'ep_query_log' ); 111 | 112 | wp_safe_redirect( remove_query_arg( 'ep_clear_query_log' ) ); 113 | exit(); 114 | } 115 | 116 | /** 117 | * Add options page 118 | * 119 | * @since 1.3 120 | * @return void 121 | */ 122 | public function action_admin_menu() { 123 | add_submenu_page( 124 | 'elasticpress', 125 | esc_html__( 'Query Log', 'debug-bar-elasticpress' ), 126 | esc_html__( 'Query Log', 'debug-bar-elasticpress' ), 127 | 'manage_options', 128 | 'ep-query-log', 129 | array( $this, 'screen_options' ) 130 | ); 131 | } 132 | 133 | /** 134 | * Only log delete index error if not 2xx AND not 404 135 | * 136 | * @param array $query Remote request arguments 137 | * @since 1.3 138 | * @return bool 139 | */ 140 | public function maybe_log_delete_index( $query ) { 141 | $response_code = wp_remote_retrieve_response_code( $query['request'] ); 142 | 143 | return ( ( $response_code < 200 || $response_code > 299 ) && 404 !== $response_code ); 144 | } 145 | 146 | /** 147 | * Log all non-200 requests 148 | * 149 | * @param array $query Remote request arguments 150 | * @since 1.3 151 | * @return bool 152 | */ 153 | public function is_query_error( $query ) { 154 | if ( is_wp_error( $query['request'] ) ) { 155 | return true; 156 | } 157 | 158 | $response_code = wp_remote_retrieve_response_code( $query['request'] ); 159 | 160 | return ( $response_code < 200 || $response_code > 299 ); 161 | } 162 | 163 | /** 164 | * Check the request body, as usually bulk indexing does not return a status error. 165 | * 166 | * @since 2.1.0 167 | * @param array $query Remote request arguments 168 | * @return boolean 169 | */ 170 | public function is_bulk_index_error( $query ) { 171 | if ( $this->is_query_error( $query ) ) { 172 | return true; 173 | } 174 | 175 | $request_body = json_decode( wp_remote_retrieve_body( $query['request'] ), true ); 176 | return ! empty( $request_body['errors'] ); 177 | } 178 | 179 | /** 180 | * Conditionally save a query to the log which is stored in options. This is a big performance hit so be careful. 181 | * 182 | * @param array $query Remote request arguments 183 | * @param string $type Request type 184 | * @since 1.3 185 | */ 186 | public function log_query( $query, $type ) { 187 | if ( ! $this->is_enabled() ) { 188 | return; 189 | } 190 | 191 | if ( ! $this->should_log_by_context() ) { 192 | return; 193 | } 194 | 195 | if ( ! $this->should_log_by_status( $query, $type ) ) { 196 | return; 197 | } 198 | 199 | $log = Utils\get_option( 'ep_query_log', [] ); 200 | 201 | $log[] = array( 202 | 'query' => $query, 203 | 'type' => $type, 204 | ); 205 | 206 | // Storing this log would exceed the limit 207 | if ( mb_strlen( maybe_serialize( $log ) ) > $this->get_logging_storage_limit() ) { 208 | return; 209 | } 210 | 211 | Utils\update_option( 'ep_query_log', $log ); 212 | } 213 | 214 | /** 215 | * Output query log page 216 | * 217 | * @since 1.3 218 | */ 219 | public function screen_options() { 220 | $log = Utils\get_option( 'ep_query_log', array() ); 221 | $enabled = Utils\get_option( 'ep_enable_logging' ); 222 | $by_status = Utils\get_option( 'ep_query_log_by_status', 'failed' ); 223 | $by_context = Utils\get_option( 'ep_query_log_by_context', [] ); 224 | 225 | if ( is_array( $log ) ) { 226 | $log = array_reverse( $log ); 227 | } 228 | 229 | $action = 'options.php'; 230 | 231 | if ( defined( 'EP_IS_NETWORK' ) && EP_IS_NETWORK ) { 232 | $action = ''; 233 | } 234 | 235 | $is_time_limit = ! empty( $enabled ) && ! in_array( $enabled, [ '0', 0, '-1', -1 ], true ); 236 | ?> 237 | 238 |
239 |

240 | 241 |
242 | 243 | 244 | 245 | 246 | 247 | 248 | 253 | 275 | 276 | 277 | 278 | 284 | 285 | 286 | 287 | 305 | 306 | 307 |
249 | 252 | 254 | 259 |
260 | 261 | severe performance implications on your website.', 'debug-bar-elasticpress' ) ); 263 | if ( $is_time_limit ) { 264 | echo ' ' . wp_kses_post( 265 | sprintf( 266 | /* translators: date */ 267 | __( 'Logging queries until %s.', 'debug-bar-elasticpress' ), 268 | wp_date( 'Y-m-d H:i:s', $enabled ) 269 | ) 270 | ); 271 | } 272 | ?> 273 | 274 |
279 | 283 |
288 |
292 |
296 |
300 | 304 |
308 | 309 |

310 | %s', 'debug-bar-elasticpress' ), 315 | size_format( $this->get_logging_storage_limit() ) 316 | ) 317 | ); 318 | ?> 319 |

320 | 321 |

322 | 323 | 324 | 325 | 326 | 327 |

328 |
329 | 330 | render_buttons(); 340 | $debug_bar_output->render_queries( [ 'display_context' => true ] ); 341 | ?> 342 |
343 | setup(); 358 | } 359 | 360 | return $instance; 361 | } 362 | 363 | /** 364 | * Store the queries as JSON objects. 365 | * 366 | * This is necessary because otherwise, WP will run it thought `maybe_unserialize()` and break it. 367 | * 368 | * @param mixed $value The ep_query_log option value. 369 | * @return string 370 | */ 371 | public function json_encode_query_log( $value ) { 372 | return wp_json_encode( $value ); 373 | } 374 | 375 | /** 376 | * Decode the queries back to an associative array. 377 | * 378 | * @param string $value A JSON string. 379 | * @return array 380 | */ 381 | public function json_decode_query_log( $value ) { 382 | return ( is_string( $value ) ) ? json_decode( $value, true ) : $value; 383 | } 384 | 385 | /** 386 | * Conditionally add the request type to the request args 387 | * 388 | * @since 3.1.0 389 | * @param array $args Request args 390 | * @param string $path Site URL to retrieve 391 | * @param array $query_args The query args originally passed to WP_Query. 392 | * @param string|null $type Type of request, used for debugging. 393 | * @return array New request args 394 | */ 395 | public function maybe_add_request_type( array $args, string $path, array $query_args, $type ): array { 396 | if ( ! empty( $args['ep_query_type'] ) ) { 397 | return $args; 398 | } 399 | 400 | if ( ! empty( $type ) ) { 401 | $args['ep_query_type'] = $type; 402 | 403 | if ( 'get' === $type ) { 404 | $args['ep_query_type'] = esc_html__( 'Raw ES document', 'debug-bar-elasticpress' ); 405 | } 406 | } 407 | 408 | if ( '_nodes/plugins' === $path ) { 409 | $args['ep_query_type'] = esc_html__( 'Elasticsearch check', 'debug-bar-elasticpress' ); 410 | } 411 | 412 | return $args; 413 | } 414 | 415 | /** 416 | * Conditionally add the context of the query 417 | * 418 | * @param array $args Request args 419 | * @return array 420 | */ 421 | public function maybe_add_request_context( array $args ): array { 422 | $args['ep_context'] = $this->get_current_context(); 423 | 424 | return $args; 425 | } 426 | 427 | /** 428 | * Get the current context 429 | * 430 | * @since 3.1.0 431 | * @return string 432 | */ 433 | protected function get_current_context(): string { 434 | $context = 'public'; 435 | 436 | if ( is_admin() ) { 437 | $context = 'admin'; 438 | } 439 | 440 | if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) { 441 | $context = 'ajax'; 442 | } 443 | 444 | if ( defined( 'REST_REQUEST' ) && REST_REQUEST ) { 445 | $context = 'rest'; 446 | } 447 | 448 | return $context; 449 | } 450 | 451 | /** 452 | * Conditionally add the request type to the request args (for query requests) 453 | * 454 | * @since 3.1.0 455 | * @param array $request_args Request arguments 456 | * @param string $path Request path 457 | * @param string $index Index name 458 | * @param string $type Index type 459 | * @param array $query Prepared Elasticsearch query 460 | * @param array $query_args Query arguments 461 | * @param mixed $query_object Could be WP_Query, WP_User_Query, etc. 462 | * @return array New request arguments 463 | */ 464 | public function maybe_add_request_query_type( array $request_args, string $path, string $index, string $type, array $query, array $query_args, $query_object ): array { 465 | $request_args['ep_query_type'] = $this->determine_request_query_type( $request_args, $path, $index, $type, $query, $query_args, $query_object ); 466 | return $request_args; 467 | } 468 | 469 | /** 470 | * Enqueue assets if we are in the correct admin screen 471 | * 472 | * @since 3.1.0 473 | */ 474 | public function admin_enqueue_scripts() { 475 | $current_screen = get_current_screen(); 476 | 477 | if ( ! isset( $current_screen->id ) || 'elasticpress_page_ep-query-log' !== $current_screen->id ) { 478 | return; 479 | } 480 | 481 | ( new CommonPanel() )->enqueue_scripts_styles(); 482 | } 483 | 484 | /** 485 | * Conditionally add the request type to the request args (for query requests) 486 | * 487 | * @since 3.1.0 488 | * @param array $request_args Request arguments 489 | * @param string $path Request path 490 | * @param string $index Index name 491 | * @param string $type Index type 492 | * @param array $query Prepared Elasticsearch query 493 | * @param array $query_args Query arguments 494 | * @param mixed $query_object Could be WP_Query, WP_User_Query, etc. 495 | * @return string Request type 496 | */ 497 | protected function determine_request_query_type( array $request_args, string $path, string $index, string $type, array $query, array $query_args, $query_object ): string { 498 | if ( $query_object instanceof \WP_Query && $query_object->is_main_query() ) { 499 | return esc_html__( 'Main query', 'debug-bar-elasticpress' ); 500 | } 501 | 502 | if ( empty( $query['query'] ) && ! empty( $query['aggs'] ) ) { 503 | return esc_html__( 'Possible values for EP filter', 'debug-bar-elasticpress' ); 504 | } 505 | 506 | $search_term = $query_args['s'] ?? ''; 507 | if ( '' !== $search_term ) { 508 | $type = 'Search'; 509 | if ( apply_filters( 'ep_autosuggest_query_placeholder', 'ep_autosuggest_placeholder' ) === $search_term ) { 510 | return esc_html__( 'Autosuggest template', 'debug-bar-elasticpress' ); 511 | } 512 | 513 | return esc_html__( 'Search', 'debug-bar-elasticpress' ); 514 | } 515 | 516 | return $type; 517 | } 518 | 519 | /** 520 | * Whether logging is enabled or not 521 | * 522 | * @since 3.1.0 523 | * @return boolean 524 | */ 525 | protected function is_enabled(): bool { 526 | $enabled = Utils\get_option( 'ep_enable_logging' ); 527 | 528 | return ! empty( $enabled ); 529 | } 530 | 531 | /** 532 | * Whether the current context should or not be logged 533 | * 534 | * @since 3.1.0 535 | * @return boolean 536 | */ 537 | protected function should_log_by_context() { 538 | $by_context = Utils\get_option( 'ep_query_log_by_context', [] ); 539 | 540 | return empty( $by_context ) || in_array( $this->get_current_context(), $by_context, true ); 541 | } 542 | 543 | /** 544 | * Whether a query (and a type) should be logged or not 545 | * 546 | * @since 3.1.0 547 | * @param array $query Remote request arguments 548 | * @param string $type Query type 549 | * @return boolean 550 | */ 551 | protected function should_log_by_status( array $query, $type ): bool { 552 | $by_status = Utils\get_option( 'ep_query_log_by_status', 'failed' ); 553 | 554 | if ( 'all' === $by_status ) { 555 | return true; 556 | } 557 | 558 | /** 559 | * This filter allows you to map query types to callables. If the callable returns true, 560 | * that query will be logged. 561 | * 562 | * @var array 563 | * @since 1.3 564 | * @since 2.1.0 Added `bulk_index` 565 | */ 566 | $allowed_log_types = apply_filters( 567 | 'ep_debug_bar_allowed_log_types', 568 | array( 569 | 'put_mapping' => array( $this, 'is_query_error' ), 570 | 'delete_network_alias' => array( $this, 'is_query_error' ), 571 | 'create_network_alias' => array( $this, 'is_query_error' ), 572 | 'bulk_index' => array( $this, 'is_bulk_index_error' ), 573 | 'bulk_index_posts' => array( $this, 'is_query_error' ), 574 | 'delete_index' => array( $this, 'maybe_log_delete_index' ), 575 | 'create_pipeline' => array( $this, 'is_query_error' ), 576 | 'get_pipeline' => array( $this, 'is_query_error' ), 577 | 'query' => array( $this, 'is_query_error' ), 578 | ), 579 | $query, 580 | $type 581 | ); 582 | 583 | if ( ! isset( $allowed_log_types[ $type ] ) ) { 584 | return false; 585 | } 586 | 587 | return call_user_func( $allowed_log_types[ $type ], $query ); 588 | } 589 | 590 | /** 591 | * Return the size limit for stored logs 592 | * 593 | * @since 3.1.0 594 | * @return integer 595 | */ 596 | protected function get_logging_storage_limit(): int { 597 | /** 598 | * Filter the log size limit 599 | * 600 | * @since 3.1.0 601 | * @hook ep_debug_bar_log_size_limit 602 | * @param {int} $number Log size limit 603 | * @return {int} New limit 604 | */ 605 | return apply_filters( 'ep_debug_bar_log_size_limit', MB_IN_BYTES ); 606 | } 607 | 608 | /** 609 | * Conditionally disable logging based on period 610 | * 611 | * @since 3.1.0 612 | */ 613 | public function maybe_disable() { 614 | $enabled = Utils\get_option( 'ep_enable_logging' ); 615 | 616 | $is_time_limit = ! empty( $enabled ) && ! in_array( $enabled, [ '0', 0, '-1' ], true ); 617 | if ( ! $is_time_limit || $enabled > wp_date( 'U' ) ) { 618 | return; 619 | } 620 | 621 | Utils\update_option( 'ep_enable_logging', 0 ); 622 | } 623 | 624 | /** 625 | * Sanitize the ep_enable_logging option, conditionally setting it as a time limit 626 | * 627 | * @since 3.1.0 628 | * @param mixed $value Value sent 629 | * @return mixed 630 | */ 631 | public function sanitize_enable_logging( $value ) { 632 | if ( 'time_limit' === $value ) { 633 | $value = wp_date( 'U', strtotime( '+5 minutes' ) ); 634 | } else { 635 | $value = ! empty( $value ) ? -1 : 0; 636 | } 637 | 638 | return $value; 639 | } 640 | } 641 | -------------------------------------------------------------------------------- /classes/QueryMonitorCollector.php: -------------------------------------------------------------------------------- 1 | common_panel = new CommonPanel(); 33 | 34 | add_filter( 'qm/output/menus', [ $this, 'admin_menu' ] ); 35 | } 36 | 37 | /** 38 | * Panel title 39 | * 40 | * @return string 41 | */ 42 | public function name(): string { 43 | return $this->common_panel->get_title(); 44 | } 45 | 46 | /** 47 | * Echoes the output 48 | * 49 | * @return void 50 | */ 51 | public function output() { 52 | ?> 53 |
54 | render_summary(); ?> 55 | common_panel->render(); ?> 56 |
57 | get_query_log(); 67 | $total_query_time = 0; 68 | 69 | foreach ( $queries as $query ) { 70 | if ( ! empty( $query['time_start'] ) && ! empty( $query['time_finish'] ) ) { 71 | $total_query_time += ( $query['time_finish'] - $query['time_start'] ); 72 | } 73 | } 74 | ?> 75 |
76 |
77 |

78 | 79 |

80 |

81 |
82 |
83 |

84 | 85 |

86 |

87 | 96 |

97 |
98 |
99 | queries = $queries; 36 | } 37 | 38 | /** 39 | * Render the download and copy&paste buttons 40 | * 41 | * @since 3.0.0 42 | * @return void 43 | */ 44 | public function render_buttons() { 45 | if ( empty( $this->queries ) ) { 46 | return; 47 | } 48 | 49 | $copy_paste_output = $this->get_copy_paste_report(); 50 | ?> 51 |
52 | 53 | 54 | 55 | 58 | 61 |
62 | 74 |
75 |
    76 | queries ) ) { 78 | ?> 79 |
  1. 80 | queries as $query ) { 83 | $type = $query['args']['ep_query_type'] ?? ''; 84 | $context = ( ! empty( $args['display_context'] ) && ! empty( $query['args']['ep_context'] ) ) ? 85 | $query['args']['ep_context'] : 86 | ''; 87 | $this->render_query( $query, $type, $context ); 88 | } 89 | } 90 | ?> 91 |
92 |
93 | = 300 ? 'ep-query-failed' : ''; 110 | $log['result'] = json_decode( $result, true ); 111 | 112 | if ( class_exists( '\ElasticPress\StatusReport\FailedQueries' ) && class_exists( 'ElasticPress\QueryLogger' ) ) { 113 | $query_logger = apply_filters( 'ep_query_logger', new \ElasticPress\QueryLogger() ); 114 | if ( $query_logger ) { 115 | $failed_queries = new \ElasticPress\StatusReport\FailedQueries( $query_logger ); 116 | $error = $failed_queries->analyze_log( $log ); 117 | $error = array_filter( $error ); 118 | } 119 | } 120 | 121 | $curl_request = 'curl -X' . strtoupper( $query['args']['method'] ); 122 | 123 | if ( ! empty( $query['args']['headers'] ) ) { 124 | foreach ( $query['args']['headers'] as $key => $value ) { 125 | $curl_request .= " -H '$key: $value'"; 126 | } 127 | } 128 | 129 | if ( ! empty( $query['args']['body'] ) ) { 130 | $curl_request .= " -d '" . wp_json_encode( json_decode( $query['args']['body'], true ) ) . "'"; 131 | } 132 | 133 | $curl_request .= " '" . $query['url'] . "'"; 134 | 135 | ?> 136 |
  • 137 | 138 | 139 | 140 |
    141 | Error: %s', 'debug-bar-elasticpress' ), $error[0] ) 145 | ); 146 | ?> 147 |
    148 |
    149 | Recommended Solution: %s', 'debug-bar-elasticpress' ), $error[1] ) 153 | ); 154 | ?> 155 |
    156 | 157 | 158 |
    159 |
    160 |
    errors, JSON_PRETTY_PRINT ) ); ?>
    161 |
    162 | 163 | 164 | 165 |
    166 | 167 | 168 |
    169 | 170 | 171 | 172 |
    173 | 174 | 175 |
    176 | 177 | 178 |
    179 | 180 | 181 |
    182 | 183 |
    184 | Time Taken: %d ms', 'debug-bar-elasticpress' ), ( $query_time * 1000 ) ) 189 | ); 190 | else : 191 | echo wp_kses_post( 192 | __( 'Time Taken: -', 'debug-bar-elasticpress' ) 193 | ); 194 | endif; 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 | 234 |
    235 |
    236 | 237 | 238 | 239 |
    240 | Query Response Code: HTTP %d', 'debug-bar-elasticpress' ), (int) $response ) 244 | ); 245 | ?> 246 |
    247 |
    248 |
    249 |
    250 |
    251 | 252 |
    253 | 254 |
    255 | 256 | "> 257 | 258 | 259 | 262 |
  • 263 | format_queries_for_display( $this->queries ); 280 | 281 | foreach ( $formatted_queries as $query ) { 282 | $output .= "### {$query['title']} ###\n"; 283 | foreach ( $query['fields'] as $slug => $field ) { 284 | $value = $field['value'] ?? ''; 285 | 286 | $output .= "{$slug}: "; 287 | $output .= $this->render_value( $value ); 288 | $output .= "\n"; 289 | } 290 | $output .= "\n"; 291 | } 292 | 293 | return $output; 294 | } 295 | 296 | /** 297 | * Render a value based on its type 298 | * 299 | * @since 3.0.0 300 | * @param mixed $value The value 301 | * @return string 302 | */ 303 | protected function render_value( $value ) { 304 | if ( is_array( $value ) || is_object( $value ) ) { 305 | return var_export( $value, true ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions 306 | } 307 | 308 | if ( is_bool( $value ) ) { 309 | return $value ? 'true' : 'false'; 310 | } 311 | 312 | return (string) $value; 313 | } 314 | 315 | /** 316 | * Render the additional buttons. 317 | * 318 | * @since 3.1.0 319 | * @return void 320 | */ 321 | public function render_additional_buttons() { 322 | $buttons = [ 323 | $this->get_explain_query_button(), 324 | $this->get_retrieve_raw_document_button(), 325 | ]; 326 | 327 | /** 328 | * Filter the additional buttons. 329 | * 330 | * @since 3.1.0 331 | * @hook ep_debug_bar_additional_buttons 332 | * @param array $buttons Buttons. 333 | * @return array 334 | */ 335 | apply_filters( 'ep_debug_bar_additional_buttons', $buttons ); 336 | 337 | $buttons = array_filter( $buttons ); 338 | 339 | if ( empty( $buttons ) ) { 340 | return; 341 | } 342 | 343 | ?> 344 |
    345 | 350 |
    351 | queries ) ) { 362 | return ''; 363 | } 364 | 365 | $button_link = add_query_arg( 366 | [ 367 | 'explain' => '1', 368 | ], 369 | isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '' 370 | ); 371 | 372 | return sprintf( 373 | ' 374 | %s 375 | ', 376 | esc_url( $button_link ), 377 | esc_html__( 'Explain queries', 'debug-bar-elasticpress' ) 378 | ); 379 | } 380 | 381 | /** 382 | * Get the retrieve raw document button. 383 | * 384 | * @since 3.1.0 385 | * @return string 386 | */ 387 | public function get_retrieve_raw_document_button(): string { 388 | if ( ! is_indexable_singular() ) { 389 | return ''; 390 | } 391 | 392 | $button_link = add_query_arg( 393 | [ 394 | 'ep-retrieve-es-document' => '1', 395 | '_wpnonce' => wp_create_nonce( 'ep-retrieve-es-document' ), 396 | ], 397 | isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : '' 398 | ); 399 | 400 | return sprintf( 401 | ' 402 | %s 403 | ', 404 | esc_url( $button_link ), 405 | esc_html__( 'Reload and retrieve raw ES document', 'debug-bar-elasticpress' ) 406 | ); 407 | } 408 | } 409 | -------------------------------------------------------------------------------- /debug-bar-elasticpress.php: -------------------------------------------------------------------------------- 1 | get_elasticsearch_version(); 127 | } 128 | if ( function_exists( '\ElasticPress\Utils\is_epio' ) && \ElasticPress\Utils\is_epio() ) { 129 | $elasticsearch_version = esc_html__( 'ElasticPress.io Managed Platform', 'debug-bar-elasticpress' ); 130 | } 131 | $stati[] = array( 132 | 'es_version', 133 | esc_html__( 'Elasticsearch Version', 'debug-bar-elasticpress' ), 134 | $elasticsearch_version, 135 | ); 136 | return $stati; 137 | } 138 | 139 | /** 140 | * Add explain=true to elastic post query 141 | * 142 | * @param array $formatted_args Formatted Elasticsearch query 143 | * @return array 144 | */ 145 | function add_explain_args( $formatted_args ) { 146 | if ( isset( $_GET['explain'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification 147 | $formatted_args['explain'] = true; 148 | } 149 | return $formatted_args; 150 | } 151 | 152 | 153 | /** 154 | * Render an admin notice about the absence of the minimum ElasticPress plugin version. 155 | * 156 | * @since 3.0.0 157 | */ 158 | function admin_notice_min_ep_version() { 159 | ?> 160 |
    161 |

    162 | 169 |

    170 |
    171 | get( 'post' ); 189 | $indexable_post_types = $post_indexable->get_indexable_post_types(); 190 | 191 | return in_array( $post_type, $indexable_post_types, true ); 192 | } 193 | 194 | /** 195 | * Get document from Elasticsearch. 196 | * 197 | * @since 3.1.0 198 | * @return void 199 | */ 200 | function retrieve_raw_document_from_es() { 201 | if ( empty( $_GET['ep-retrieve-es-document'] ) || empty( $_GET['_wpnonce'] ) || ! wp_verify_nonce( sanitize_key( $_GET['_wpnonce'] ), 'ep-retrieve-es-document' ) ) { 202 | return; 203 | } 204 | 205 | if ( ! is_indexable_singular() ) { 206 | return; 207 | } 208 | 209 | \ElasticPress\Indexables::factory()->get( 'post' )->get( get_the_ID() ); 210 | } 211 | 212 | /** 213 | * Include our QM Output 214 | * 215 | * @since 3.1.0 216 | * @param array $output Array of registered output 217 | * @return array 218 | */ 219 | function register_qm_output( $output ) { 220 | $collector = \QM_Collectors::get( 'elasticpress' ); 221 | 222 | if ( $collector ) { 223 | $output['elasticpress'] = new QueryMonitorOutput( $collector ); 224 | } 225 | 226 | return $output; 227 | } 228 | -------------------------------------------------------------------------------- /lang/debug-bar-elasticpress.pot: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2024 10up 2 | # This file is distributed under the GPLv2. 3 | msgid "" 4 | msgstr "" 5 | "Project-Id-Version: ElasticPress Debugging Add-On 3.1.1\n" 6 | "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/debug-bar-elasticpress\n" 7 | "Last-Translator: FULL NAME \n" 8 | "Language-Team: LANGUAGE \n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "POT-Creation-Date: 2024-12-11T17:24:37+00:00\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "X-Generator: WP-CLI 2.8.1\n" 15 | "X-Domain: debug-bar-elasticpress\n" 16 | 17 | #. Plugin Name of the plugin 18 | msgid "ElasticPress Debugging Add-On" 19 | msgstr "" 20 | 21 | #. Plugin URI of the plugin 22 | msgid "https://wordpress.org/plugins/debug-bar-elasticpress" 23 | msgstr "" 24 | 25 | #. Description of the plugin 26 | msgid "Extends the Query Monitor and Debug Bar plugins for ElasticPress queries." 27 | msgstr "" 28 | 29 | #. Author of the plugin 30 | msgid "10up" 31 | msgstr "" 32 | 33 | #. Author URI of the plugin 34 | msgid "https://10up.com" 35 | msgstr "" 36 | 37 | #. translators: %d: number of queries 38 | #: classes/CommonPanel.php:28 39 | msgid "ElasticPress (%d)" 40 | msgstr "" 41 | 42 | #: classes/CommonPanel.php:33 43 | #: classes/EP_Debug_Bar_ElasticPress.php:38 44 | msgid "ElasticPress" 45 | msgstr "" 46 | 47 | #: classes/CommonPanel.php:57 48 | msgid "ElasticPress is currently indexing." 49 | msgstr "" 50 | 51 | #. translators: queries count. 52 | #: classes/EP_Debug_Bar_ElasticPress.php:77 53 | msgid "Total ElasticPress Queries: %d" 54 | msgstr "" 55 | 56 | #. translators: blocking query time. 57 | #: classes/EP_Debug_Bar_ElasticPress.php:85 58 | msgid "Total Blocking ElasticPress Query Time: %d ms" 59 | msgstr "" 60 | 61 | #: classes/QueryFormatter.php:31 62 | msgid "Page URL" 63 | msgstr "" 64 | 65 | #: classes/QueryFormatter.php:32 66 | msgid "Elasticsearch Request" 67 | msgstr "" 68 | 69 | #: classes/QueryFormatter.php:33 70 | msgid "Request ID" 71 | msgstr "" 72 | 73 | #: classes/QueryFormatter.php:34 74 | msgid "Time" 75 | msgstr "" 76 | 77 | #: classes/QueryFormatter.php:35 78 | msgid "Time Spent (ms)" 79 | msgstr "" 80 | 81 | #: classes/QueryFormatter.php:36 82 | msgid "WP Query Args" 83 | msgstr "" 84 | 85 | #: classes/QueryFormatter.php:37 86 | msgid "HTTP Status Code" 87 | msgstr "" 88 | 89 | #: classes/QueryFormatter.php:38 90 | msgid "Query Body" 91 | msgstr "" 92 | 93 | #: classes/QueryFormatter.php:39 94 | msgid "Query Result" 95 | msgstr "" 96 | 97 | #: classes/QueryFormatter.php:51 98 | msgid "Error" 99 | msgstr "" 100 | 101 | #: classes/QueryFormatter.php:55 102 | msgid "Recommended Solution" 103 | msgstr "" 104 | 105 | #: classes/QueryLog.php:125 106 | #: classes/QueryLog.php:126 107 | msgid "Query Log" 108 | msgstr "" 109 | 110 | #: classes/QueryLog.php:239 111 | msgid "ElasticPress Query Log" 112 | msgstr "" 113 | 114 | #: classes/QueryLog.php:250 115 | msgid "Enable or disable query logging:" 116 | msgstr "" 117 | 118 | #: classes/QueryLog.php:255 119 | msgid "Disable" 120 | msgstr "" 121 | 122 | #: classes/QueryLog.php:256 123 | msgid "Enable for 5 minutes" 124 | msgstr "" 125 | 126 | #: classes/QueryLog.php:257 127 | msgid "Keep enabled" 128 | msgstr "" 129 | 130 | #: classes/QueryLog.php:262 131 | msgid "Note that query logging can have severe performance implications on your website." 132 | msgstr "" 133 | 134 | #. translators: date 135 | #: classes/QueryLog.php:267 136 | msgid "Logging queries until %s." 137 | msgstr "" 138 | 139 | #: classes/QueryLog.php:277 140 | msgid "Log by status:" 141 | msgstr "" 142 | 143 | #: classes/QueryLog.php:280 144 | msgid "Only failed queries" 145 | msgstr "" 146 | 147 | #: classes/QueryLog.php:281 148 | msgid "All queries" 149 | msgstr "" 150 | 151 | #: classes/QueryLog.php:286 152 | msgid "Log by context:" 153 | msgstr "" 154 | 155 | #: classes/QueryLog.php:290 156 | msgid "Public" 157 | msgstr "" 158 | 159 | #: classes/QueryLog.php:294 160 | msgid "Admin" 161 | msgstr "" 162 | 163 | #: classes/QueryLog.php:298 164 | msgid "AJAX" 165 | msgstr "" 166 | 167 | #: classes/QueryLog.php:302 168 | msgid "REST API" 169 | msgstr "" 170 | 171 | #. translators: Current limit 172 | #: classes/QueryLog.php:314 173 | msgid "Please note that logs are stored until the storage limit is reached. The current limit is: %s" 174 | msgstr "" 175 | 176 | #: classes/QueryLog.php:322 177 | msgid "Save Changes" 178 | msgstr "" 179 | 180 | #: classes/QueryLog.php:325 181 | msgid "Empty Log" 182 | msgstr "" 183 | 184 | #: classes/QueryLog.php:404 185 | msgid "Raw ES document" 186 | msgstr "" 187 | 188 | #: classes/QueryLog.php:409 189 | msgid "Elasticsearch check" 190 | msgstr "" 191 | 192 | #: classes/QueryLog.php:499 193 | msgid "Main query" 194 | msgstr "" 195 | 196 | #: classes/QueryLog.php:503 197 | msgid "Possible values for EP filter" 198 | msgstr "" 199 | 200 | #: classes/QueryLog.php:510 201 | msgid "Autosuggest template" 202 | msgstr "" 203 | 204 | #: classes/QueryLog.php:513 205 | msgid "Search" 206 | msgstr "" 207 | 208 | #: classes/QueryMonitorOutput.php:78 209 | msgid "Total ElasticPress Queries:" 210 | msgstr "" 211 | 212 | #: classes/QueryMonitorOutput.php:84 213 | msgid "Total Blocking ElasticPress Query Time:" 214 | msgstr "" 215 | 216 | #. translators: time spent 217 | #: classes/QueryMonitorOutput.php:91 218 | msgid "%d ms" 219 | msgstr "" 220 | 221 | #: classes/QueryOutput.php:53 222 | msgid "Download Requests Info" 223 | msgstr "" 224 | 225 | #: classes/QueryOutput.php:56 226 | msgid "Copy Requests Info to Clipboard" 227 | msgstr "" 228 | 229 | #: classes/QueryOutput.php:59 230 | #: classes/QueryOutput.php:260 231 | msgid "Copied!" 232 | msgstr "" 233 | 234 | #: classes/QueryOutput.php:79 235 | msgid "No queries to show" 236 | msgstr "" 237 | 238 | #. translators: Debug bar elasticpress error message 239 | #: classes/QueryOutput.php:144 240 | msgid "Error: %s" 241 | msgstr "" 242 | 243 | #. translators: Debug bar elasticpress recommended solution for the error 244 | #: classes/QueryOutput.php:152 245 | msgid "Recommended Solution: %s" 246 | msgstr "" 247 | 248 | #: classes/QueryOutput.php:159 249 | msgid "Errors:" 250 | msgstr "" 251 | 252 | #: classes/QueryOutput.php:166 253 | msgid "Type:" 254 | msgstr "" 255 | 256 | #: classes/QueryOutput.php:173 257 | msgid "Context:" 258 | msgstr "" 259 | 260 | #: classes/QueryOutput.php:179 261 | msgid "Host:" 262 | msgstr "" 263 | 264 | #. translators: time spent running the query. 265 | #: classes/QueryOutput.php:188 266 | msgid "Time Taken: %d ms" 267 | msgstr "" 268 | 269 | #: classes/QueryOutput.php:192 270 | msgid "Time Taken: -" 271 | msgstr "" 272 | 273 | #: classes/QueryOutput.php:199 274 | msgid "URL:" 275 | msgstr "" 276 | 277 | #: classes/QueryOutput.php:204 278 | msgid "Method:" 279 | msgstr "" 280 | 281 | #: classes/QueryOutput.php:210 282 | msgid "Headers:" 283 | msgstr "" 284 | 285 | #: classes/QueryOutput.php:217 286 | msgid "Query Args:" 287 | msgstr "" 288 | 289 | #: classes/QueryOutput.php:224 290 | msgid "Query Body:" 291 | msgstr "" 292 | 293 | #. translators: Query HTTP Code response 294 | #: classes/QueryOutput.php:243 295 | msgid "Query Response Code: HTTP %d" 296 | msgstr "" 297 | 298 | #: classes/QueryOutput.php:248 299 | msgid "Query Result:" 300 | msgstr "" 301 | 302 | #: classes/QueryOutput.php:253 303 | msgid "Query Response Code:" 304 | msgstr "" 305 | 306 | #: classes/QueryOutput.php:253 307 | msgid "Request Error" 308 | msgstr "" 309 | 310 | #: classes/QueryOutput.php:257 311 | msgid "Copy cURL Request" 312 | msgstr "" 313 | 314 | #: classes/QueryOutput.php:275 315 | msgid "Queries info" 316 | msgstr "" 317 | 318 | #: classes/QueryOutput.php:377 319 | msgid "Explain queries" 320 | msgstr "" 321 | 322 | #: classes/QueryOutput.php:405 323 | msgid "Reload and retrieve raw ES document" 324 | msgstr "" 325 | 326 | #: debug-bar-elasticpress.php:117 327 | msgid "ElasticPress Version" 328 | msgstr "" 329 | 330 | #: debug-bar-elasticpress.php:129 331 | msgid "ElasticPress.io Managed Platform" 332 | msgstr "" 333 | 334 | #: debug-bar-elasticpress.php:133 335 | msgid "Elasticsearch Version" 336 | msgstr "" 337 | 338 | #. translators: Min. EP version 339 | #: debug-bar-elasticpress.php:165 340 | msgid "ElasticPress Debugging Add-On needs at least ElasticPress %s to work properly." 341 | msgstr "" 342 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === ElasticPress Debugging Add-On === 2 | Contributors: tlovett1, 10up 3 | Tags: debug, debug bar, elasticpress, elasticsearch 4 | Requires at least: 4.6 5 | Tested up to: 6.7 6 | Requires PHP: 7.0 7 | Stable tag: 3.1.1 8 | License: GPLv2 9 | License URI: http://www.gnu.org/licenses/gpl-2.0.html 10 | 11 | Extends the Query Monitor and Debug Bar plugins for ElasticPress queries. 12 | 13 | == Description == 14 | 15 | Allows you to examine every ElasticPress query running on any given request by adding an [ElasticPress](https://wordpress.org/plugins/elasticpress) panel to [Debug Bar](https://wordpress.org/plugins/debug-bar/) and/or [Query Monitor](https://wordpress.org/plugins/query-monitor/) plugins. 16 | 17 | Alternatively, go to ElasticPress > Query Log and set it to record ElasticPress queries. 18 | 19 | = Requirements: = 20 | 21 | * [ElasticPress 4.4.0+](https://wordpress.org/plugins/elasticpress) 22 | * [Debug Bar 1.0+](https://wordpress.org/plugins/debug-bar/) 23 | * PHP 7.0+ 24 | 25 | == Installation == 26 | 1. Install [ElasticPress](https://wordpress.org/plugins/elasticpress). 27 | 2. Optionally install [Debug Bar](https://wordpress.org/plugins/debug-bar/) or [Query Monitor](https://wordpress.org/plugins/query-monitor/). 28 | 3. Install the plugin in WordPress. 29 | 30 | == Changelog == 31 | 32 | = 3.1.1 - 2024-12-11 = 33 | 34 | __Added:__ 35 | 36 | * Display the number of queries in Query Monitor's tab title. Props [@felipeelia](https://github.com/felipeelia) and [@burhandodhy](https://github.com/burhandodhy). 37 | * ElasticPress as a plugin dependency. Props [@jeffpaul](https://github.com/jeffpaul). 38 | 39 | __Changed:__ 40 | 41 | * More modern versions of GitHub Actions, node, and node packages. Props [@felipeelia](https://github.com/felipeelia). 42 | 43 | __Fixed:__ 44 | 45 | * Notices related to i18n calls. Props [@felipeelia](https://github.com/felipeelia) and [@archon810](https://github.com/archon810). 46 | * Use new plugin name in the admin notice. Props [@burhandodhy](https://github.com/burhandodhy). 47 | 48 | = 3.1.0 - 2023-09-20 = 49 | 50 | __Added:__ 51 | 52 | * New button to explain ES queries. Props [@burhandodhy](https://github.com/burhandodhy), [@felipeelia](https://github.com/felipeelia), [@MARQAS](https://github.com/MARQAS), and [@brandwaffle](https://github.com/brandwaffle). 53 | * New button to Reload and retrieve raw ES document. Props [@burhandodhy](https://github.com/burhandodhy), [@felipeelia](https://github.com/felipeelia), and [@brandwaffle](https://github.com/brandwaffle). 54 | * Query types (and context when listing queries in the Query Log admin screen.) Props [@felipeelia](https://github.com/felipeelia) and [@burhandodhy](https://github.com/burhandodhy). 55 | * Log query by context, status, and fixed time. Props [@felipeelia](https://github.com/felipeelia). 56 | * Official support to Query Monitor. Props [@felipeelia](https://github.com/felipeelia). 57 | 58 | __Security:__ 59 | 60 | * Bumped `tough-cookie` from 4.1.2 to 4.1.3. Props [@dependabot](https://github.com/dependabot). 61 | * Bumped `word-wrap` from 1.2.3 to 1.2.4. Props [@dependabot](https://github.com/dependabot). 62 | 63 | = 3.0.0 - 2023-03-23 = 64 | 65 | This release drops the support for older versions of ElasticPress and PHP. 66 | 67 | __Added:__ 68 | 69 | * Instructions with error code for failed queries. Props [@MARQAS](https://github.com/MARQAS) and [@felipeelia](https://github.com/felipeelia). 70 | * Buttons to copy or download all requests info. Props [@MARQAS](https://github.com/MARQAS), [@felipeelia](https://github.com/felipeelia), and [@burhandodhy](https://github.com/burhandodhy). 71 | * Compatibility with the WordPress localization system. Props [@burhandodhy](https://github.com/burhandodhy) and [@felipeelia](https://github.com/felipeelia). 72 | * SECURITY.md file. Props [@felipeelia](https://github.com/felipeelia). 73 | 74 | __Changed:__ 75 | 76 | * Set minimum requirement for PHP to 7.0 and ElasticPress to 4.4.0. Props [@burhandodhy](https://github.com/burhandodhy) and [@felipeelia](https://github.com/felipeelia). 77 | * CSS and JS code lint by 10up toolkit. Props [@burhandodhy](https://github.com/burhandodhy). 78 | 79 | __Fixed:__ 80 | 81 | * Unnecessary `stripslashes()` call when outputting JSON objects. Props [@felipeelia](https://github.com/felipeelia), [@goldenapples](https://github.com/goldenapples), and [@mattonomics](https://github.com/mattonomics). 82 | * JS error on copy action. Props [@burhandodhy](https://github.com/burhandodhy). 83 | 84 | __Security:__ 85 | 86 | * Bumped `minimatch` from 3.0.4 to 3.1.2. Props [@dependabot](https://github.com/dependabot). 87 | * Bumped `json5` from 2.2.0 to 2.2.3. Props [@dependabot](https://github.com/dependabot). 88 | * Bumped `webpack` from 5.75.0 to 5.76.2. Props [@dependabot](https://github.com/dependabot). 89 | 90 | 91 | = 2.1.1 - 2022-08-04 = 92 | 93 | __Security:__ 94 | 95 | * Fix XSS vulnerability. Props [@piotr-bajer](https://github.com/piotr-bajer) and [@felipeelia](https://github.com/felipeelia). 96 | * Bumped `path-parse` from 1.0.6 to 1.0.7. Props [@dependabot](https://github.com/dependabot). 97 | * Bumps `minimist` from 1.2.5 to 1.2.6. Props [@dependabot](https://github.com/dependabot). 98 | * Bumps `ansi-regex` from 5.0.0 to 5.0.1. Props [@dependabot](https://github.com/dependabot). 99 | 100 | 101 | = 2.1.0 = 102 | 103 | __Added:__ 104 | 105 | * ElasticPress and Elasticsearch versions. Props to [@oscarssanchez](https://github.com/oscarssanchez) and [@felipeelia](https://github.com/felipeelia). 106 | * Log of bulk_index requests. Props [@felipeelia](https://github.com/felipeelia). 107 | * Warning when ElasticPress is indexing. Props [@nathanielks](https://github.com/nathanielks) and [@felipeelia](https://github.com/felipeelia). 108 | 109 | __Changed:__ 110 | 111 | * Only load CSS and JS files for logged-in users. Props [@cbratschi](https://github.com/cbratschi) and [@felipeelia](https://github.com/felipeelia). 112 | 113 | = 2.0.0 = 114 | This release drops the support for older versions of WordPress Core, ElasticPress and Debug Bar. 115 | 116 | * Code refactoring. Props [@felipeelia](https://github.com/felipeelia) 117 | * Fixed Query Logs in EP Dashboard [@felipeelia](https://github.com/felipeelia) 118 | * Fixed typo from "clsas" to "class" in the query output. Props [@Rahmon](https://github.com/Rahmon) 119 | 120 | = 1.4 = 121 | * Support ElasticPress 3.0+ 122 | 123 | = 1.3 = 124 | * Add query log 125 | 126 | = 1.2 = 127 | * Show query errors (i.e. cURL timeout) 128 | * Add ?explain to query if GET param is set 129 | 130 | = 1.1.1 = 131 | * Only show query body if it exits 132 | 133 | = 1.1 = 134 | * Improve formatting 135 | * Show original query args (EP 2.1+) 136 | 137 | = 1.0 = 138 | * Initial release 139 | --------------------------------------------------------------------------------