首先介绍我实现的是xhprof插件的日志转为无限树状图,先看效果图:
废话不多说,直接看代码:(辛辛苦苦敲了好久才搞定,逻辑比较多,新手多揣摩)
控制器:
<?php
namespace Admin\Controller;
class XhprofController extends AdminbaseController
{
function _initialize()
{
parent::_initialize();
}
//获取当前文件目录
public function index()
{
$file = scandir("./public/xhprof");
$str = ".xhprof";
foreach ($file as $value) {
if (preg_replace("/($str)/", "", $value) != $value) {
$dir[] = $value;
}
}
$this->assign("dir", $dir);
$this->display();
}
//获取适当的结构
public function tree($file_name)
{
$file_path = "./public/xhprof/$file_name";
$file_name = str_replace('.', '', $file_name);
if (F($file_name)) {
$mainTree = F($file_name);
} else {
if (file_exists($file_path)) {
$fp = fopen($file_path, "r");
$str = fread($fp, filesize($file_path));//指定读取大小,这里把整个文件内容读取出来
$array = json_decode($str, true);
$array_keys = array_keys($array);
//按照调用者整理数据
$calls = [];
foreach ($array_keys as $array_key) {
$caller = explode("==>", $array_key)[0];
$beCall = explode("==>", $array_key)[1];
if (!$calls[$caller]) {
$calls[$caller] = [
"caller" => $caller,
"beCalls" => []
];
}
$status = 0;//是否有子目录
foreach ($array_keys as $keyv) {
if (strpos($keyv, $beCall . '==>') !== false && strpos($keyv, "@") === false && $beCall !== null && $beCall !== "count") {
$status = 1;
}
}
if ($beCall !== null) {
$calls[$caller]['beCalls'][] = array('name' => $beCall, "status" => $status, 'data' => $array[$array_key]);
}
}
foreach ($calls as &$v) {
$tmpAll = 0;
foreach ($v['beCalls'] as $vv) {
$tmpAll += $vv['data']['wt'];
}
foreach ($v['beCalls'] as &$vv1) {
$vv1['data']['wtp'] = round($vv1['data']['wt'] * 100 / $tmpAll, 2);
$vv1['wtp'] = $vv1['data']['wt'];
}
}
$mainTree = $calls;
//缓存$mainTree
F($file_name, $mainTree);
} else {
$mainTree = "";
}
}
array_multisort(array_column($mainTree["main()"]["beCalls"], "wtp"), SORT_DESC, array_column($mainTree["main()"]["beCalls"], "name"), SORT_DESC, $mainTree["main()"]["beCalls"]);
$this->assign("main", $array["main()"]);
$this->assign("mainTree", $mainTree["main()"]);
$this->assign("level", 1);
$this->assign("file_name", $file_name);
$this->assign("file_path", $file_path);
$this->display();
}
public function ajax()
{
$note = htmlspecialchars_decode(I("post.note"));
$file_name = htmlspecialchars_decode(I("post.file_name"));
$file_path = htmlspecialchars_decode(I("post.file_path"));
//处理$mainTree
if (F($file_name)) {
$mainTree = F($file_name);
} else {
if (file_exists($file_path)) {
$fp = fopen($file_path, "r");
$str = fread($fp, filesize($file_path));//指定读取大小,这里把整个文件内容读取出来
$array = json_decode($str, true);
$array_keys = array_keys($array);
//按照调用者整理数据
$calls = [];
foreach ($array_keys as $array_key) {
$caller = explode("==>", $array_key)[0];
$beCall = explode("==>", $array_key)[1];
if (!$calls[$caller]) {
$calls[$caller] = [
"caller" => $caller,
"beCalls" => []
];
}
$status = 0;//是否有子目录
foreach ($array_keys as $keyv) {
if (strpos($keyv, $beCall . '==>') !== false && strpos($keyv, "@") === false && $beCall !== null && $beCall !== "count") {
$status = 1;
}
}
if ($beCall !== null) {
$calls[$caller]['beCalls'][] = array('name' => $beCall, "status" => $status, 'data' => $array[$array_key]);
}
}
foreach ($calls as &$v) {
$tmpAll = 0;
foreach ($v['beCalls'] as $vv) {
$tmpAll += $vv['data']['wt'];
}
foreach ($v['beCalls'] as &$vv1) {
$vv1['data']['wtp'] = round($vv1['data']['wt'] * 100 / $tmpAll, 2);
$vv1['wtp'] = $vv1['data']['wt'];
}
}
$mainTree = $calls;
//缓存$mainTree
F($file_name, $mainTree);
} else {
$mainTree = "";
}
}
array_multisort(array_column($mainTree[$note]["beCalls"], "wtp"), SORT_DESC, array_column($mainTree[$note]["beCalls"], "name"), SORT_DESC, $mainTree[$note]["beCalls"]);
$this->ajaxReturn($mainTree[$note]);
}
}
veiw界面:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Xhprof数据分析</title>
<link rel="stylesheet" type="text/css" href="__TMPL__Public/assets/css/bootstrap.min.css"/>
<link rel="stylesheet" type="text/css" href="__TMPL__Public/assets/css/xhprof.css"/>
<script type="text/javascript" src="__PUBLIC__/js/jquery.js"></script>
</head>
<body>
<div class="tree well">
<ul>
<li>
<span class="main">
<i class='icon-minus'> </i> <b class="name">main()</b>
第<b> 1 </b>级   
ct:<b class="bw">{$main.ct}</b>
wt:<b class="red" >{$main.wt} (100%)</b>
cpu:<b class="bw">{$main.cpu}</b>
mu:<b class="bw">{$main.mu}</b>
pmu:<b class="bw">{$main.pmu}</b>
</span>
<foreach name="mainTree['beCalls']" item="vo">
<ul>
<li class='parent_li'>
<span title='Collapse this branch' note="{$vo.name}" level="2" file_name={$file_name} file_path={$file_path}>
<if condition="$vo['status']==1">
<i class="icon-plus"></i>
<else/>
<b style='width:14px;display: inline-block;'></b>
</if>
<div style="display: inline-block;">
<b class="name" title={$vo.name}>{$vo.name}</b>
第<b> 2 </b>级   
ct:<b class="ct">{$vo.data.ct}</b>
<if condition="$vo['data']['wtp'] gt 10">
wt:<b class="red">{$vo.data.wt} ({$vo.data.wtp|round=2}%)</b>
<else/>
wt:<b class="wt" style="display: inline-block;min-width: 150px;">{$vo.data.wt} ({$vo.data.wtp|round=2}%)</b>
</if>
cpu:<b class="bw">{$vo.data.cpu}</b>
mu:<b class="bw">{$vo.data.mu}</b>
pmu:<b class="bw">{$vo.data.pmu}</b>
</div>
</span>
</li>
</ul>
</foreach>
</li>
</ul>
</div>
</body>
<script>
$(function () {
$('.tree li:has(ul)').addClass('parent_li').find(' > span').attr('title', 'Collapse this branch');
$('body').on('click', "span", function (e) {
e.stopPropagation();
if ($(this).attr('is_click') === 1) {
return false;
}
$(this).attr('is_click', 1);
var children = $(this).parent('li.parent_li').find(' > ul > li');
if (children.is(":visible")) {
children.hide('fast');
$(this).attr('title', 'Expand this branch').find(' > i').addClass('icon-plus').removeClass('icon-minus');
$(this).attr('is_click', 0);
} else {
children.show('fast');
$(this).attr('title', 'Collapse this branch').find(' > i').addClass('icon-minus').removeClass('icon-plus');
if ($(this).hasClass('main')) {
$(this).attr('is_click', 0);
return;
}
if (!$(this).siblings('.child').length) {
do_ajax($(this));
} else {
$(this).attr('is_click', 0);
}
}
});
function do_ajax(e) {
var note_js = e.attr('note');
var level_js = e.attr('level');
var file_name_js = e.attr('file_name');
var file_path_js = e.attr('file_path');
$.ajax({
type: "post",
cache: false,
dataType: "json",
url: "{:U('Admin/Xhprof/ajax')}",
data: {
note: note_js,
level: level_js,
file_name: file_name_js,
file_path: file_path_js
},
success: function (data) {
if (data.beCalls !== null) {
var str = "";
for (var i = 0; i < data.beCalls.length; i++) {
str += "<ul class='child'>";
str += "<li class='parent_li'>";
str += "<span title='Collapse this branch' class='load' note=" + data.beCalls[i].name + " level= " + eval(Number(level_js) + 1) + " file_name= " + file_name_js + " file_path= " + file_path_js + ">";
if (data.beCalls[i].status == 1) {
str += "<i class='icon-plus' ></i>";
} else {
str += "<b style='width:14px;display: inline-block;'> </b>";
}
str += "<b class='name' title="+ data.beCalls[i].name + ">" + data.beCalls[i].name + '</b>第<b> ' + eval(Number(level_js) + 1) + "</b> 级   ct:<b class='ct'>" + data.beCalls[i].data.ct + "</b> wt:<b class= " + (data.beCalls[i].data.wtp < 10 ? 'wt' : 'red') + " >" + data.beCalls[i].data.wt + "(" + data.beCalls[i].data.wtp + "%)</b> cpu:<b class='bw' >" + data.beCalls[i].data.cpu + "</b> mu:<b class='bw'>" + data.beCalls[i].data.mu + "</b> pmu:<b class='bw'>" + data.beCalls[i].data.pmu + "</b></span>";
str += '</li>';
str += '</ul>';
}
e.siblings('.child').remove();
e.after(str);
e.attr('is_click', 0);
}
},
error: function () {
}
})
}
});
</script>
</html>
|
请发表评论