如何统计WordPress页面加载时每个函数的执行时间以排查性能瓶颈
WordPress函数执行时长统计需求
请问有哪些最优的工具或方法可以逐一统计WordPress(简称WP)所有函数的单次执行时长?理想状态下我希望得到类似下图的统计列表:

上图是Debug Bar插件搭配Slow Actions扩展的界面,目前它是很好的性能排查起点,但无法满足我的需求:它仅展示hooks自身的总耗时,没有统计挂载到对应hooks上的每个独立函数(callbacks回调)的单独执行时长,参考下图:

我需要同时看到每个独立函数的执行时长数据。目前这款插件已经最接近我的需求,请问是否有可以在此基础上优化的方案?或者有没有现成的工具、自定义代码可以实现该需求?
(参考信息:我调研时找到了相关旧提问,后续也计划测试Xdebug的能力,但希望先找到WordPress专属的性能分析工具。)
感谢各位提供的帮助或建议
此致
Wiktor
解决方案
1. 现成插件方案(优先推荐)
- Query Monitor是当前WordPress生态下匹配你需求的原生性能调试工具,内置了单回调执行时长统计能力。安装启用后在顶部管理栏进入Query Monitor面板,打开「钩子」选项卡,即可看到所有钩子的总耗时,展开单个钩子就能查看对应挂载的每个回调函数的独立执行时长、优先级、所属插件/主题、调用次数等数据,还支持按耗时排序、按插件/主题筛选,定位慢函数的效率很高。
2. Debug Bar Slow Actions二次修改方案
如果你不想更换现有工具,可以直接修改插件逻辑新增回调统计能力,核心实现思路如下:
- 打开插件目录下的
debug-bar-slow-actions.php文件,找到钩子拦截逻辑的代码段 - 给每个钩子的回调队列新增计时包裹层,示例核心代码如下:
foreach ( $wp_filter[ $hook ]->callbacks as $priority => $callbacks ) { foreach ( $callbacks as $idx => $callback ) { $original_cb = $callback['function']; // 用匿名函数包裹原回调埋点计时 $wp_filter[ $hook ]->callbacks[ $priority ][ $idx ]['function'] = function() use ( $original_cb, $hook, $priority ) { $start = microtime( true ); $args = func_get_args(); $result = call_user_func_array( $original_cb, $args ); $cost = microtime( true ) - $start; // 将统计数据存入插件全局统计数组 Debug_Bar_Slow_Actions::$cb_stats[ $hook ][ $priority ][] = [ 'name' => is_array( $original_cb ) ? ( is_object( $original_cb[0] ) ? get_class( $original_cb[0] ) : $original_cb[0] ) . '::' . $original_cb[1] : $original_cb, 'cost' => $cost ]; return $result; }; } }
- 修改插件的前端输出逻辑,把统计到的回调数据渲染到对应钩子的下方即可。
3. 轻量自定义代码方案(临时排查用)
如果不需要长期调试,不想额外安装插件,可以把下面的代码临时加到主题functions.php文件中,仅对管理员输出统计结果:
add_action( 'all', function( $hook ) { static $cb_stats = []; // 跳过AJAX、定时任务请求,避免干扰正常业务 if ( wp_doing_ajax() || wp_doing_cron() || !current_user_can( 'manage_options' ) ) return; global $wp_filter; if ( isset( $wp_filter[ $hook ] ) ) { foreach ( $wp_filter[ $hook ]->callbacks as $priority => &$callbacks ) { foreach ( $callbacks as &$cb ) { $cb_id = spl_object_hash( $cb['function'] ); if ( !isset( $cb_stats[ $cb_id ] ) ) { $orig_cb = $cb['function']; $cb['function'] = function() use ( $orig_cb, $cb_id, &$cb_stats ) { $start = microtime( true ); $res = call_user_func_array( $orig_cb, func_get_args() ); $cb_stats[ $cb_id ]['cost'] += microtime( true ) - $start; $cb_stats[ $cb_id ]['calls'] += 1; $cb_stats[ $cb_id ]['name'] = is_array( $orig_cb ) ? ( is_object( $orig_cb[0] ) ? get_class( $orig_cb[0] ) : $orig_cb[0] ) . '::' . $orig_cb[1] : $orig_cb; return $res; }; } } } } // 请求结束时输出统计结果 add_action( 'shutdown', function() use ( &$cb_stats ) { echo '<div style="position:fixed;bottom:0;left:0;right:0;background:#fff;padding:20px;z-index:99999;max-height:400px;overflow-y:auto;border-top:1px solid #eee;">'; echo '<h3>回调函数执行耗时统计(按耗时降序)</h3>'; usort( $cb_stats, function( $a, $b ) { return $b['cost'] - $a['cost']; } ); foreach ( $cb_stats as $item ) { echo '<p>' . esc_html( $item['name'] ) . ':' . number_format( $item['cost'] * 1000, 2 ) . 'ms,累计调用' . intval( $item['calls'] ) . '次</p>'; } echo '</div>'; }, 999 ); }, 0 );
注意:排查完成后请及时删除该代码,避免额外占用站点性能。
内容的提问来源于stack exchange,提问作者Viktor Borýtás
相关产品推荐
相关产品推荐

