-
Notifications
You must be signed in to change notification settings - Fork 29
Add wp profile requests command to monitor HTTP requests
#206
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
c27d533
a7f209a
9c3cf1d
69f6d56
8418ad2
49258a1
9ae6961
721c27a
a31e12b
dbbb4f1
64c6426
d20638b
2f5632b
b601c03
9b66b2c
5f2f176
fe1eeed
6275bc2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| Feature: Profile HTTP requests | ||
|
|
||
| Scenario: Profile HTTP requests during WordPress load | ||
| Given a WP install | ||
| And that HTTP requests to https://www.apple.com/ will respond with: | ||
| """ | ||
| HTTP/1.1 200 | ||
| Content-Type: text/plain | ||
|
|
||
| Hello world | ||
| """ | ||
| And that HTTP requests to https://www.example.com/ will respond with: | ||
| """ | ||
| HTTP/1.1 201 | ||
| Content-Type: application/json | ||
|
|
||
| {"status":"created"} | ||
| """ | ||
| And a wp-content/mu-plugins/http-requests.php file: | ||
| """ | ||
| <?php | ||
| add_action( 'muplugins_loaded', function() { | ||
| wp_remote_get( 'https://www.apple.com/' ); | ||
| wp_remote_post( 'https://www.example.com/', array( 'body' => 'test' ) ); | ||
| }); | ||
| """ | ||
|
|
||
| When I run `wp profile requests --fields=method,url` | ||
| Then STDOUT should be a table containing rows: | ||
| | method | url | | ||
| | GET | https://www.apple.com/ | | ||
| | POST | https://www.example.com/ | | ||
| And STDOUT should contain: | ||
| """ | ||
| total (2) | ||
| """ | ||
|
|
||
| Scenario: Profile shows no requests when none are made | ||
| Given a WP install | ||
| And a wp-content/mu-plugins/no-requests.php file: | ||
| """ | ||
| <?php | ||
| // Don't make any HTTP requests | ||
| add_filter( 'pre_http_request', '__return_false', 1 ); | ||
| """ | ||
|
|
||
| When I run `wp profile requests --fields=method,url` | ||
| Then STDOUT should contain: | ||
| """ | ||
| total (0) | ||
| """ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -48,6 +48,9 @@ class Profiler { | |
| private $tick_cache_hit_offset = null; | ||
| private $tick_cache_miss_offset = null; | ||
|
|
||
| private $request_start_time = null; | ||
| private $request_args = null; | ||
|
|
||
| public function __construct( $type, $focus ) { | ||
| $this->type = $type; | ||
| $this->focus = $focus; | ||
|
|
@@ -124,10 +127,10 @@ function () { | |
| ) { | ||
| $start_hook = substr( $this->focus, 0, -6 ); | ||
| WP_CLI::add_wp_hook( $start_hook, array( $this, 'wp_tick_profile_begin' ), 9999 ); | ||
| } else { | ||
| } elseif ( 'request' !== $this->type ) { | ||
| WP_CLI::add_wp_hook( 'all', array( $this, 'wp_hook_begin' ) ); | ||
| } | ||
| WP_CLI::add_wp_hook( 'pre_http_request', array( $this, 'wp_request_begin' ) ); | ||
| WP_CLI::add_wp_hook( 'pre_http_request', array( $this, 'wp_request_begin' ), 10, 3 ); | ||
| WP_CLI::add_wp_hook( 'http_api_debug', array( $this, 'wp_request_end' ) ); | ||
| $this->load_wordpress_with_template(); | ||
| } | ||
|
|
@@ -381,21 +384,93 @@ public function handle_function_tick() { | |
| /** | ||
| * Profiling request time for any active Loggers | ||
| */ | ||
| public function wp_request_begin( $filter_value = null ) { | ||
| public function wp_request_begin( $preempt = null, $parsed_args = null, $url = null ) { | ||
| foreach ( Logger::$active_loggers as $logger ) { | ||
| $logger->start_request_timer(); | ||
| } | ||
| return $filter_value; | ||
|
|
||
| // For request profiling, capture details of each HTTP request | ||
| if ( 'request' === $this->type ) { | ||
| // Reset properties first to handle cases where previous request was preempted | ||
| $this->request_start_time = null; | ||
| $this->request_args = null; | ||
|
|
||
| // Now capture the new request details | ||
| $this->request_start_time = microtime( true ); | ||
| $this->request_args = array( | ||
| 'url' => $url, | ||
| 'method' => ( is_array( $parsed_args ) && isset( $parsed_args['method'] ) ) ? $parsed_args['method'] : 'GET', | ||
| ); | ||
|
|
||
| // If request is preempted (mocked), log it now since http_api_debug won't fire | ||
| if ( false !== $preempt && ! is_null( $preempt ) ) { | ||
| $request_time = 0; // Preempted requests happen instantly | ||
| $status = ''; | ||
|
|
||
| // Extract status code from preempted response | ||
| if ( is_wp_error( $preempt ) ) { | ||
| $status = 'Error'; | ||
| } elseif ( is_array( $preempt ) && isset( $preempt['response']['code'] ) ) { | ||
| $status = $preempt['response']['code']; | ||
| } | ||
|
|
||
| $logger = new Logger( | ||
| array( | ||
| 'method' => $this->request_args['method'], | ||
| 'url' => $this->request_args['url'], | ||
| 'status' => $status, | ||
| ) | ||
| ); | ||
| $logger->time = $request_time; | ||
|
|
||
| $this->loggers[] = $logger; | ||
|
|
||
| // Reset for next request | ||
| $this->request_start_time = null; | ||
| $this->request_args = null; | ||
| } | ||
| } | ||
swissspidy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| return $preempt; | ||
| } | ||
|
|
||
| /** | ||
| * Profiling request time for any active Loggers | ||
| */ | ||
| public function wp_request_end( $filter_value = null ) { | ||
| public function wp_request_end( $response = null ) { | ||
| foreach ( Logger::$active_loggers as $logger ) { | ||
| $logger->stop_request_timer(); | ||
| } | ||
| return $filter_value; | ||
|
|
||
| // For request profiling, log individual request | ||
| if ( 'request' === $this->type && ! is_null( $this->request_start_time ) ) { | ||
| $request_time = microtime( true ) - $this->request_start_time; | ||
| $status = ''; | ||
|
|
||
| // Extract status code from response | ||
| if ( is_wp_error( $response ) ) { | ||
| $status = 'Error'; | ||
| } elseif ( is_array( $response ) && isset( $response['response']['code'] ) ) { | ||
| $status = $response['response']['code']; | ||
| } | ||
|
|
||
| $logger = new Logger( | ||
| array( | ||
| 'method' => $this->request_args['method'], | ||
| 'url' => $this->request_args['url'], | ||
| 'status' => $status, | ||
| ) | ||
| ); | ||
| $logger->time = $request_time; | ||
|
|
||
| $this->loggers[] = $logger; | ||
|
|
||
| // Reset for next request | ||
| $this->request_start_time = null; | ||
| $this->request_args = null; | ||
| } | ||
|
Comment on lines
+446
to
+471
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The logic inside this |
||
|
|
||
| return $response; | ||
| } | ||
|
|
||
| /** | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.