Authored by htoooth

fix

... ... @@ -9,6 +9,9 @@ const jp = require('jsonpath');
const config = require('../../../config/config');
const ErrorModel = require('../models/errorModel');
const SlowRouteModel = require('../models/slowRouteModel');
const SortRouteModel = require('../models/sortRouteModel');
const SortApiModel = require('../models/sortApiModel');
const SortClientModel = require('../models/sortClientRouteModel');
const endpoint = (server) => `http://${server.host}:${server.port}`;
... ... @@ -195,6 +198,75 @@ const profile_service = {
const result = await errorModel.getList(app, host, type, time, code, api, route, start, length);
return result;
},
async sort_route_time(ctx, app, time) {
const sortRouteModel = new SortRouteModel(ctx);
const result = await sortRouteModel.getTimeList(app, time);
return result;
},
async sort_route_count(ctx, app, time) {
const sortRouteModel = new SortRouteModel(ctx);
const result = await sortRouteModel.getCountList(app, time);
return result;
},
async sort_api_time(ctx, app, time) {
const sortRouteModel = new SortApiModel(ctx);
const result = await sortRouteModel.getTimeList(app, time);
return result;
},
async sort_api_count(ctx, app, time) {
const sortRouteModel = new SortApiModel(ctx);
const result = await sortRouteModel.getCountList(app, time);
return result;
},
async getApp(ctx) {
let model = new ErrorModel(ctx);
let app = await model.getApp().then(r => r.map(fp.prop('app')));
return app;
},
async sort_server_route(ctx, app, time) {
let routeTime = await this.sort_route_time(ctx, app, time);
let routeCount = await this.sort_route_count(ctx, app, time);
let sortRouteTime = _.orderBy(routeTime, ['avg'], ['desc']);
let sortRouteCount = _.orderBy(routeCount, ['count'], ['desc']);
return {time: sortRouteTime, count: sortRouteCount};
},
async sort_server_api(ctx, app, time) {
let routeTime = await this.sort_api_time(ctx, app, time);
let routeCount = await this.sort_api_count(ctx, app, time);
let sortRouteTime = _.orderBy(routeTime, ['avg'], ['desc']);
let sortRouteCount = _.orderBy(routeCount, ['count'], ['desc']);
return {time: sortRouteTime, count: sortRouteCount};
},
async sort_client_time(ctx, app, time) {
const newClientTime = new SortClientModel(ctx);
const result = await newClientTime.getFirstScreenTimeList(app, time);
return result;
},
async sort_client_count(ctx, app, time) {
const newClientCount = new SortClientModel(ctx);
const result = await newClientCount.getCountList(app, time);
return result;
},
async sort_client_route(ctx, app, time) {
let routeTime = await this.sort_client_time(ctx, app, time);
let routeCount = await this.sort_client_count(ctx, app, time);
let sortRouteTime = _.orderBy(routeTime, ['avg'], ['desc']);
let sortRouteCount = _.orderBy(routeCount, ['count'], ['desc']);
return {time: sortRouteTime, count: sortRouteCount};
}
};
... ... @@ -219,7 +291,7 @@ const profile_controller = {
let type = await model.getType().then(r => r.map(fp.prop('type')));
let code = await model.getCode().then(r => r.map(fp.prop('code')));
let route = await model.getRoute().then(r => r.map(fp.prop('route')));
let api = await model.getApi().then(r => r.map(fp.prop('api')));
let api = await model.getApi().then(r => r.dmap(fp.prop('api')));
await ctx.render('action/profile_error', {hostName, app, type, code, route, api});
},
... ... @@ -238,8 +310,49 @@ const profile_controller = {
const result = await profile_service.error(ctx, app, host, type, time, code, api, route, start, length);
ctx.body = Object.assign({}, result, {draw});
}
},
async sort_route(ctx) {
const result = await profile_service.getApp(ctx);
await ctx.render('action/sort_route', {app: result})
},
async sort_route_list(ctx) {
const app = ctx.query.app;
const time = ctx.query.time;
const result = await profile_service.sort_server_route(ctx, app, time);
await ctx.render('action/sort_route_list', Object.assign({}, {
layout: false
}, result));
},
async sort_api(ctx) {
const result = await profile_service.getApp(ctx);
await ctx.render('action/sort_api', {app: result})
},
async sort_api_list(ctx) {
const app = ctx.query.app;
const time = ctx.query.time;
const result = await profile_service.sort_server_api(ctx, app, time);
await ctx.render('action/sort_api_list', Object.assign({}, {
layout: false
}, result));
},
async sort_client(ctx) {
const result = await profile_service.getApp(ctx);
await ctx.render('action/sort_client', {app: result})
},
async sort_client_list(ctx) {
const app = ctx.query.app;
const time = ctx.query.time;
const result = await profile_service.sort_client_route(ctx, app, time);
await ctx.render('action/sort_client_list', Object.assign({}, {
layout: false
}, result));
}
};
r.get('/server', profile_controller.server_mean_report_index);
... ... @@ -248,4 +361,13 @@ r.get('/server.json', profile_controller.server_mean_report_json);
r.get('/error', profile_controller.error_report_index);
r.get('/error.json', profile_controller.error_report_json);
r.get('/sort_route', profile_controller.sort_route);
r.post('/sort_route_list', profile_controller.sort_route_list);
r.get('/sort_api', profile_controller.sort_api);
r.post('/sort_api_list', profile_controller.sort_api_list);
r.get('/sort_client', profile_controller.sort_client);
r.post('/sort_client_list', profile_controller.sort_client_list);
module.exports = r;
\ No newline at end of file
... ...
... ... @@ -51,7 +51,7 @@ class errorModel extends model {
}
let recordsTotal = await this.mysql.query(`SELECT COUNT(*) as count from error_report where 1=1 ${query}`, queryVal).then(([r]) => r.count);
let data = await this.mysql.query(`SELECT id, app, type, preqid, reqid, uid, udid, code, route, api, line, script, message, stack, create_time as time from error_report where 1=1 ${query} order by create_time desc limit ?, ? `, [...queryVal, start, length]);
let data = await this.mysql.query(`SELECT id, app, type, preqid, reqid, uid, udid, code, route, api, line, script, message, stack, create_time as time, 'detail' from error_report where 1=1 ${query} order by create_time desc limit ?, ? `, [...queryVal, start, length]);
return {
recordsTotal,
... ...
const model = require('../../../lib/model');
const mysqlPromise = require('../../../lib/mysql-apm');
class SortApiModel extends model {
constructor(ctx) {
super(ctx);
this.mysql = new mysqlPromise();
}
async getTimeList(app, time) {
let query = '';
let queryVal = [];
if (app) {
query += 'and app = ?';
queryVal.push(app);
}
if (time) {
let [start,end] = time.split(':');
query += 'and create_time > ? and create_time < ?';
queryVal.push(start);
queryVal.push(end);
}
let data = await this.mysql.query(`SELECT api,avg(duration) as avg FROM slow_duration where type="api" ${query} group by api`, queryVal);
return data;
}
async getCountList(app, time) {
let query = '';
let queryVal = [];
if (app) {
query += 'and app = ?';
queryVal.push(app);
}
if (time) {
let [start,end] = time.split(':');
query += 'and create_time > ? and create_time < ?';
queryVal.push(start);
queryVal.push(end);
}
let data = await this.mysql.query(`SELECT api,count(route) as count FROM slow_duration where type="api" ${query} group by api`, queryVal);
return data;
}
async getApp() {
return await this.mysql.query('SELECT DISTINCT app FROM slow_duration');
}
}
module.exports = SortApiModel;
\ No newline at end of file
... ...
const model = require('../../../lib/model');
const mysqlPromise = require('../../../lib/mysql-apm');
const durationType = {
dcl: 'domcontentload',
ld: 'domreadyload',
fs: 'firstscreen'
};
class SortClientRouteModel extends model {
constructor(ctx) {
super(ctx);
this.mysql = new mysqlPromise();
}
async getFirstScreenTimeList(app, time) {
let query = '';
let queryVal = [];
if (app) {
query += 'and app = ?';
queryVal.push(app);
}
if (time) {
let [start,end] = time.split(':');
query += 'and create_time > ? and create_time < ?';
queryVal.push(start);
queryVal.push(end);
}
let data = await this.mysql.query(`SELECT route,avg(duration) as avg FROM slow_duration where type="${durationType.fs}" ${query} group by route`, queryVal);
return data;
}
async getDomcontentLoadList(app, time) {
let query = '';
let queryVal = [];
if (app) {
query += 'and app = ?';
queryVal.push(app);
}
if (time) {
let [start,end] = time.split(':');
query += 'and create_time > ? and create_time < ?';
queryVal.push(start);
queryVal.push(end);
}
let data = await this.mysql.query(`SELECT route,avg(duration) FROM slow_duration where type="${durationType.dcl}" ${query} group by route`, queryVal);
return data;
}
async getDomcontentReadyList(app, time) {
let query = '';
let queryVal = [];
if (app) {
query += 'and app = ?';
queryVal.push(app);
}
if (time) {
let [start,end] = time.split(':');
query += 'and create_time > ? and create_time < ?';
queryVal.push(start);
queryVal.push(end);
}
let data = await this.mysql.query(`SELECT route,avg(duration) FROM slow_duration where type="${durationType.ld}" ${query} group by route`, queryVal);
return data;
}
async getCountList(app, time) {
let query = '';
let queryVal = [];
if (app) {
query += 'and app = ?';
queryVal.push(app);
}
if (time) {
let [start,end] = time.split(':');
query += 'and create_time > ? and create_time < ?';
queryVal.push(start);
queryVal.push(end);
}
let data = await this.mysql.query(`SELECT route,count(route) as count FROM slow_duration where type="${durationType.fs}" ${query} group by route`, queryVal);
return data;
}
}
module.exports = SortClientRouteModel;
\ No newline at end of file
... ...
const model = require('../../../lib/model');
const mysqlPromise = require('../../../lib/mysql-apm');
class SortRouteModel extends model {
constructor(ctx) {
super(ctx);
this.mysql = new mysqlPromise();
}
async getTimeList(app, time) {
let query = '';
let queryVal = [];
if (app) {
query += 'and app = ?';
queryVal.push(app);
}
if (time) {
let [start,end] = time.split(':');
query += 'and create_time > ? and create_time < ?';
queryVal.push(start);
queryVal.push(end);
}
let data = await this.mysql.query(`SELECT route,avg(duration) as avg FROM slow_duration where type="route" ${query} group by route`, queryVal);
return data;
}
async getCountList(app, time) {
let query = '';
let queryVal = [];
if (app) {
query += 'and app = ?';
queryVal.push(app);
}
if (time) {
let [start,end] = time.split(':');
query += 'and create_time > ? and create_time < ?';
queryVal.push(start);
queryVal.push(end);
}
let data = await this.mysql.query(`SELECT route,count(route) as count FROM slow_duration where type="route" ${query} group by route`, queryVal);
return data;
}
async getApp() {
return await this.mysql.query('SELECT DISTINCT app FROM slow_duration');
}
}
module.exports = SortRouteModel;
\ No newline at end of file
... ...
... ... @@ -21,6 +21,10 @@
margin-right: 10px;
margin-bottom: 10px;
}
.time, .app, .type, .route, .api, .uid, .code, .preqid, .reqid, .script, .message, .stack {
word-wrap: break-word;
word-break:break-all;
}
</style>
... ... @@ -106,23 +110,16 @@
</div>
<!-- panel-heading -->
<table id="table-servers" class="table table-striped table-bordered responsive">
<table id="table-servers" class="table table-striped table-bordered">
<thead class="">
<tr>
<th>时间</th>
<th>应用</th>
<th>类型</th>
<th>请求ID</th>
<td>用户ID</td>
<td>访客ID</td>
<td>时间</td>
<td>应用</td>
<td>类型</td>
<td>路由</td>
<td>接口名</td>
<td>错误状态码</td>
<!--<td>错误信息</td>-->
<!--<td>出错文件</td>-->
<!--<td>行号</td>-->
<!--<td>列数</td>-->
<!--<td>错误堆栈</td>-->
<td>操作</td>
</tr>
</thead>
... ... @@ -131,6 +128,79 @@
</table>
</div>
<!-- panel -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog modal-lg" role="document" style="display:table;">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="myModalLabel">详细信息</h4>
</div>
<div class="modal-body">
<table id="table-servers" class="table table-striped table-bordered">
<thead class="">
<tr>
</tr>
</thead>
<tbody>
<tr>
<td width="12%">时间</td>
<td class="time"></td>
</tr>
<tr>
<td>应用</td>
<td class="app"></td>
</tr>
<tr>
<td>类型</td>
<td class="type"></td>
</tr>
<tr>
<td>路由</td>
<td class="route"></td>
</tr>
<tr>
<td>接口名</td>
<td class="api"></td>
</tr>
<tr>
<td>用户标识</td>
<td class="uid"></td>
</tr>
<tr>
<td>错识状态码</td>
<td class="code"></td>
</tr>
<tr>
<td>父请求标识</td>
<td class="preqid"></td>
</tr>
<tr>
<td>请求标识</td>
<td class="reqid"></td>
</tr>
<tr>
<td>脚本</td>
<td class="script"></td>
</tr>
<tr>
<td>接口返回消息</td>
<td class="message"></td>
</tr>
<tr>
<td>错误堆栈</td>
<td class="stack"></td>
</tr>
</tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
</div>
</div>
</div>
</div>
</div>
... ... @@ -172,7 +242,6 @@
serverSide: true,
retrieve: true,
searching: false,
responsive: true,
ajax: ajaxUrl(),
dataSrc: 'data',
pageLength: 25,
... ... @@ -180,33 +249,19 @@
{data: 'time'},
{data: 'app'},
{data: 'type'},
{data: 'reqid'},
{data: 'uid'},
{data: 'udid'},
{data: 'route'},
{data: 'api'},
{data: 'code', defaultContent: '无'},
// {data: 'message', defaultContent: '无'},
// {data: 'script', defaultContent: '无'},
// {data: 'line'},
// {data: 'column'},
// {data: 'stack', defaultContent: '无'}
{data: 'code'},
{data: 'detail'},
],
data: [{
time: "",
app: "",
type: "",
reqid: "",
uid: "",
udid: "",
route: "",
api: "",
code: "",
// message: "",
// script: "",
// line: "",
// column: "",
// stack: ""
detail: ""
}],
columnDefs: [
{
... ... @@ -217,11 +272,77 @@
return moment(data).format('YYYY/MM/DD HH:MM:ss');
},
targets: 0
targets: 0,
width: '12%'
},{
width: '10%',
targets: 1
},{
width: '5%',
targets: 2
},{
width: '20%',
targets: 3
},{
width: '20%',
targets: 4
},{
width: '8%',
targets: 5
},{
targets: -1,
data: null,
render: function ( data, type, row ) {
return '<button>查看</button>';
}
}
]
});
$('#myModal').on('hidden.bs.modal', function() {
clearModalData();
});
$('#table-servers tbody').on( 'click', 'button', function (e) {
var data = dataTable.row($(this).parents('tr')).data();
setModalData(data);
$('#myModal').modal('show');
e.preventDefault();
});
}
function clearModalData() {
$('.time').text('');
$('.app').text('');
$('.type').text('');
$('.route').text('');
$('.api').text('');
$('.uid').text('');
$('.code').text('');
$('.preqid').text('');
$('.reqid').text('');
$('.script').text('');
$('.message').text('');
$('.stack').text('');
}
function setModalData(data) {
$('.time').text(moment(data.time).format('YYYY/MM/DD HH:MM:ss'));
$('.app').text(data.app);
$('.type').text(data.type);
$('.route').text(data.route);
$('.api').text(data.api);
$('.uid').text(data.uid);
$('.code').text(data.code);
$('.preqid').text(data.preqid);
$('.reqid').text(data.reqid);
$('.script').text(data.script);
$('.message').text(data.message);
$('.stack').text(data.stack);
}
const TIME = {
... ... @@ -341,6 +462,16 @@
$('#selectedRoute').val('');
$('#selectedApi').val('');
$('#reportrange').trigger('cancel.daterangepicker');
query = {
hostname: '',
app: '',
type: '',
time: '',
code: '',
route: '',
api: ''
};
}
</script>
\ No newline at end of file
... ...
<div class="pageheader">
<div class="media">
<div class="pageicon pull-left">
<i class="fa fa-th-list"></i>
</div>
<div class="media-body">
<ul class="breadcrumb">
<li><a href=""><i class="glyphicon glyphicon-home"></i></a></li>
<li><a href="">性能监控</a></li>
<li>api排行榜</li>
</ul>
<h4>api排行榜</h4>
</div>
</div>
<!-- media -->
</div>
<!-- pageheader -->
<style>
.degrade-page {
padding: 20px;
}
.server-in-use,
.connect-err .server-name,
.connect-err .fa {
font-size: 24px;
color: #a94442;
}
.ip-port-modify-btn {
color: #999;
text-decoration: underline;
cursor: pointer;
margin-left: 10px;
}
.degrade-tab li {
cursor: pointer;
}
.pc-degrade,
.wap-degrade {
list-style: none;
padding: 20px;
}
.err-tip {
color: #a94442;
}
.config-panel .server-port {
width: 100px;
}
.config-panel .form-inline {
padding: 20px 0;
border-bottom: 1px solid #d9edf7;
}
.form-item {
margin-right: 10px;
margin-bottom: 10px;
}
</style>
<div class="degrade-page">
<div class="panel panel-info">
<div class="panel-heading">
<div class="form-inline inline-block form-item">
<label for="appName" class="control-label">应用</label>
<select id="selectedApp" class="form-control" >
<option value="">全部</option>
{{#each app}}
<option value="{{this}}">{{this}}</option>
{{/each}}
</select>
</div>
<div class="form-inline inline-block form-item">
<label for="appName" class="control-label">时间</label>
<div id="reportrange" style="display: inline-block; background: #fff; cursor: pointer; padding: 9px 10px; border: 1px solid #ccc; width: 210px; color: black;">
<i class="glyphicon glyphicon-calendar fa fa-calendar"></i>&nbsp;
<span></span> <b class="caret"></b>
</div>
</div>
<div class="form-inline inline-block">
<button id="search" class="btn btn-info" style="margin-left: 20px;">查询</button>
</div>
<div class="form-inline inline-block">
<button id="clear" class="btn btn-info" style="margin-left: 20px;">清除</button>
</div>
</div>
<div class="panel-body">
<div class="row route-data">
<div class="col-md-6 config-panel" data-type="slow">
<div class="panel panel-success">
<div class="panel-heading">
最慢降序
</div>
<div class="panel-body">
<table class="table table-striped table-bordered">
<thead>
<tr>
<td>
路由
</td>
<td>
时间 (ms)
</td>
</tr>
</thead>
<tbody>
{{#each time}}
<tr>
<td>
{{route}}
</td>
<td>
{{avg}}
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
</div>
</div>
<div class="col-md-6 config-panel" data-type="count">
<div class="panel panel-warning">
<div class="panel-heading">
调用次数
</div>
<div class="panel-body">
<table class="table table-striped table-bordered">
<thead>
<tr>
<td>
路由
</td>
<td>
调用次数
</td>
</tr>
</thead>
<tbody>
{{#each count}}
<tr>
<td>
{{route}}
</td>
<td>
{{count}}
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
var query = {
app: '',
time: ''
};
function qs(obj) {
return Object.keys(obj).map(key => {
return `${key}=${encodeURIComponent(obj[key])}`;
}).join('&')
}
$(function() {
initSelect();
initDatePicker();
});
const TIME = {
'最近1分钟': [1, 'm'],
'最近30分钟': [30, 'm'],
'最近1小时': [1, 'h'],
'最近3小时': [3, 'h'],
'最近6小时': [6, 'h'],
'最近12小时': [12, 'h']
};
function initDatePicker() {
var first = '无';
$('#reportrange span').html(first);
lastTime = TIME[first];
function cb(start, end, label) {
if (label && label !== '自定义日期') {
$('#reportrange span').html(label);
var lastTime = TIME[label];
var selectedStartTime = moment().subtract(...lastTime).unix();
var selectedEndTime = moment().unix();
query.time = `${selectedStartTime}:${selectedEndTime}`;
} else if (label && label === '自定义日期'){
$('#reportrange span').html(start.format('YYYY-MM-DD') + ' 至 ' + end.format('YYYY-MM-DD'));
var selectedStartTime = start.format('YYYY-MM-DD').unix();
var selectedEndTime = end.format('YYYY-MM-DD').unix();
query.time = `${selectedStartTime}:${selectedEndTime}`;
}
}
$('#reportrange').daterangepicker({
startDate: undefined,
endDate: undefined,
timePicker: true,
timePicker24Hour: true,
dateLimit: {
days: 7
},
ranges: {
'最近1分钟': [moment().subtract(1, 'minutes'), moment()],
'最近30分钟': [moment().subtract(30, 'minutes'), moment()],
'最近1小时': [moment().subtract(1, 'hours'), moment()],
'最近3小时': [moment().subtract(3, 'hours'), moment()],
'最近6小时': [moment().subtract(6, 'hours'), moment()],
'最近12小时': [moment().subtract(12, 'hours'), moment()]
},
locale: {
format: "YYYY-MM-DD",
applyLabel: '确定',
cancelLabel: '取消',
weekLabel: 'W',
customRangeLabel: '自定义日期',
daysOfWeek: '一_二_三_四_五_六_日'.split('_'),
monthNames: '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'),
firstDay: 1
},
}, cb);
$('#reportrange').on('cancel.daterangepicker', function() {
$('#reportrange').data('daterangepicker').setStartDate(undefined);
$('#reportrange').data('daterangepicker').setEndDate(undefined);
$('#reportrange span').html(first);
});
}
function initSelect() {
$('#selectedApp').change(function() {
query.app = $('#selectedApp').val();
});
$('#search').on('click', () => {
_handleChanged();
});
$('#clear').on('click', () => {
clearSelect();
})
}
function clearSelect() {
$('#selectedApp').val('');
$('#reportrange').trigger('cancel.daterangepicker');
query = {
app: '',
time: '',
};
}
function _handleChanged() {
$.post(`/profile/sort_api_list?${qs(query)}`).then(result => {
$('.route-data').replaceWith(result);
});
}
</script>
\ No newline at end of file
... ...
<div class="row route-data">
<div class="col-md-6 config-panel" data-type="slow">
<div class="panel panel-success">
<div class="panel-heading">
最慢降序
</div>
<div class="panel-body">
<table class="table table-striped table-bordered">
<thead>
<tr>
<td>
路由
</td>
<td>
时间 (ms)
</td>
</tr>
</thead>
<tbody>
{{#each time}}
<tr>
<td>
{{api}}
</td>
<td>
{{avg}}
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
</div>
</div>
<div class="col-md-6 config-panel" data-type="count">
<div class="panel panel-warning">
<div class="panel-heading">
调用次数
</div>
<div class="panel-body">
<table class="table table-striped table-bordered">
<thead>
<tr>
<td>
路由
</td>
<td>
调用次数
</td>
</tr>
</thead>
<tbody>
{{#each count}}
<tr>
<td>
{{api}}
</td>
<td>
{{count}}
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
</div>
</div>
</div>
\ No newline at end of file
... ...
<div class="pageheader">
<div class="media">
<div class="pageicon pull-left">
<i class="fa fa-th-list"></i>
</div>
<div class="media-body">
<ul class="breadcrumb">
<li><a href=""><i class="glyphicon glyphicon-home"></i></a></li>
<li><a href="">性能监控</a></li>
<li>api排行榜</li>
</ul>
<h4>api排行榜</h4>
</div>
</div>
<!-- media -->
</div>
<!-- pageheader -->
<style>
.degrade-page {
padding: 20px;
}
.server-in-use,
.connect-err .server-name,
.connect-err .fa {
font-size: 24px;
color: #a94442;
}
.ip-port-modify-btn {
color: #999;
text-decoration: underline;
cursor: pointer;
margin-left: 10px;
}
.degrade-tab li {
cursor: pointer;
}
.pc-degrade,
.wap-degrade {
list-style: none;
padding: 20px;
}
.err-tip {
color: #a94442;
}
.config-panel .server-port {
width: 100px;
}
.config-panel .form-inline {
padding: 20px 0;
border-bottom: 1px solid #d9edf7;
}
.form-item {
margin-right: 10px;
margin-bottom: 10px;
}
</style>
<div class="degrade-page">
<div class="panel panel-info">
<div class="panel-heading">
<div class="form-inline inline-block form-item">
<label for="appName" class="control-label">应用</label>
<select id="selectedApp" class="form-control" >
<option value="">全部</option>
{{#each app}}
<option value="{{this}}">{{this}}</option>
{{/each}}
</select>
</div>
<div class="form-inline inline-block form-item">
<label for="appName" class="control-label">时间</label>
<div id="reportrange" style="display: inline-block; background: #fff; cursor: pointer; padding: 9px 10px; border: 1px solid #ccc; width: 210px; color: black;">
<i class="glyphicon glyphicon-calendar fa fa-calendar"></i>&nbsp;
<span></span> <b class="caret"></b>
</div>
</div>
<div class="form-inline inline-block">
<button id="search" class="btn btn-info" style="margin-left: 20px;">查询</button>
</div>
<div class="form-inline inline-block">
<button id="clear" class="btn btn-info" style="margin-left: 20px;">清除</button>
</div>
</div>
<div class="panel-body">
<div class="row route-data">
<div class="col-md-6 config-panel" data-type="slow">
<div class="panel panel-success">
<div class="panel-heading">
最慢降序
</div>
<div class="panel-body">
<table class="table table-striped table-bordered">
<thead>
<tr>
<td>
路由
</td>
<td>
时间 (ms)
</td>
</tr>
</thead>
<tbody>
{{#each time}}
<tr>
<td>
{{route}}
</td>
<td>
{{avg}}
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
</div>
</div>
<div class="col-md-6 config-panel" data-type="count">
<div class="panel panel-warning">
<div class="panel-heading">
调用次数
</div>
<div class="panel-body">
<table class="table table-striped table-bordered">
<thead>
<tr>
<td>
路由
</td>
<td>
调用次数
</td>
</tr>
</thead>
<tbody>
{{#each count}}
<tr>
<td>
{{route}}
</td>
<td>
{{count}}
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
var query = {
app: '',
time: ''
};
function qs(obj) {
return Object.keys(obj).map(key => {
return `${key}=${encodeURIComponent(obj[key])}`;
}).join('&')
}
$(function() {
initSelect();
initDatePicker();
});
const TIME = {
'最近1分钟': [1, 'm'],
'最近30分钟': [30, 'm'],
'最近1小时': [1, 'h'],
'最近3小时': [3, 'h'],
'最近6小时': [6, 'h'],
'最近12小时': [12, 'h']
};
function initDatePicker() {
var first = '无';
$('#reportrange span').html(first);
lastTime = TIME[first];
function cb(start, end, label) {
if (label && label !== '自定义日期') {
$('#reportrange span').html(label);
var lastTime = TIME[label];
var selectedStartTime = moment().subtract(...lastTime).unix();
var selectedEndTime = moment().unix();
query.time = `${selectedStartTime}:${selectedEndTime}`;
} else if (label && label === '自定义日期'){
$('#reportrange span').html(start.format('YYYY-MM-DD') + ' 至 ' + end.format('YYYY-MM-DD'));
var selectedStartTime = start.format('YYYY-MM-DD').unix();
var selectedEndTime = end.format('YYYY-MM-DD').unix();
query.time = `${selectedStartTime}:${selectedEndTime}`;
}
}
$('#reportrange').daterangepicker({
startDate: undefined,
endDate: undefined,
timePicker: true,
timePicker24Hour: true,
dateLimit: {
days: 7
},
ranges: {
'最近1分钟': [moment().subtract(1, 'minutes'), moment()],
'最近30分钟': [moment().subtract(30, 'minutes'), moment()],
'最近1小时': [moment().subtract(1, 'hours'), moment()],
'最近3小时': [moment().subtract(3, 'hours'), moment()],
'最近6小时': [moment().subtract(6, 'hours'), moment()],
'最近12小时': [moment().subtract(12, 'hours'), moment()]
},
locale: {
format: "YYYY-MM-DD",
applyLabel: '确定',
cancelLabel: '取消',
weekLabel: 'W',
customRangeLabel: '自定义日期',
daysOfWeek: '一_二_三_四_五_六_日'.split('_'),
monthNames: '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'),
firstDay: 1
},
}, cb);
$('#reportrange').on('cancel.daterangepicker', function() {
$('#reportrange').data('daterangepicker').setStartDate(undefined);
$('#reportrange').data('daterangepicker').setEndDate(undefined);
$('#reportrange span').html(first);
});
}
function initSelect() {
$('#selectedApp').change(function() {
query.app = $('#selectedApp').val();
});
$('#search').on('click', () => {
_handleChanged();
});
$('#clear').on('click', () => {
clearSelect();
})
}
function clearSelect() {
$('#selectedApp').val('');
$('#reportrange').trigger('cancel.daterangepicker');
query = {
app: '',
time: '',
};
}
function _handleChanged() {
$.post(`/profile/sort_client_list?${qs(query)}`).then(result => {
$('.route-data').replaceWith(result);
});
}
</script>
\ No newline at end of file
... ...
<div class="row route-data">
<div class="col-md-6 config-panel" data-type="slow">
<div class="panel panel-success">
<div class="panel-heading">
最慢降序
</div>
<div class="panel-body">
<table class="table table-striped table-bordered">
<thead>
<tr>
<td>
路由
</td>
<td>
时间 (ms)
</td>
</tr>
</thead>
<tbody>
{{#each time}}
<tr>
<td>
{{route}}
</td>
<td>
{{avg}}
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
</div>
</div>
<div class="col-md-6 config-panel" data-type="count">
<div class="panel panel-warning">
<div class="panel-heading">
调用次数
</div>
<div class="panel-body">
<table class="table table-striped table-bordered">
<thead>
<tr>
<td>
路由
</td>
<td>
调用次数
</td>
</tr>
</thead>
<tbody>
{{#each count}}
<tr>
<td>
{{route}}
</td>
<td>
{{count}}
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
</div>
</div>
</div>
\ No newline at end of file
... ...
<div class="pageheader">
<div class="media">
<div class="pageicon pull-left">
<i class="fa fa-th-list"></i>
</div>
<div class="media-body">
<ul class="breadcrumb">
<li><a href=""><i class="glyphicon glyphicon-home"></i></a></li>
<li><a href="">性能监控</a></li>
<li>路由排行榜</li>
</ul>
<h4>路由排行榜</h4>
</div>
</div>
<!-- media -->
</div>
<!-- pageheader -->
<style>
.degrade-page {
padding: 20px;
}
.server-in-use,
.connect-err .server-name,
.connect-err .fa {
font-size: 24px;
color: #a94442;
}
.ip-port-modify-btn {
color: #999;
text-decoration: underline;
cursor: pointer;
margin-left: 10px;
}
.degrade-tab li {
cursor: pointer;
}
.pc-degrade,
.wap-degrade {
list-style: none;
padding: 20px;
}
.err-tip {
color: #a94442;
}
.config-panel .server-port {
width: 100px;
}
.config-panel .form-inline {
padding: 20px 0;
border-bottom: 1px solid #d9edf7;
}
.form-item {
margin-right: 10px;
margin-bottom: 10px;
}
</style>
<div class="degrade-page">
<div class="panel panel-info">
<div class="panel-heading">
<div class="form-inline inline-block form-item">
<label for="appName" class="control-label">应用</label>
<select id="selectedApp" class="form-control" >
<option value="">全部</option>
{{#each app}}
<option value="{{this}}">{{this}}</option>
{{/each}}
</select>
</div>
<div class="form-inline inline-block form-item">
<label for="appName" class="control-label">时间</label>
<div id="reportrange" style="display: inline-block; background: #fff; cursor: pointer; padding: 9px 10px; border: 1px solid #ccc; width: 210px; color: black;">
<i class="glyphicon glyphicon-calendar fa fa-calendar"></i>&nbsp;
<span></span> <b class="caret"></b>
</div>
</div>
<div class="form-inline inline-block">
<button id="search" class="btn btn-info" style="margin-left: 20px;">查询</button>
</div>
<div class="form-inline inline-block">
<button id="clear" class="btn btn-info" style="margin-left: 20px;">清除</button>
</div>
</div>
<div class="panel-body">
<div class="row route-data">
<div class="col-md-6 config-panel" data-type="slow">
<div class="panel panel-success">
<div class="panel-heading">
最慢降序
</div>
<div class="panel-body">
<table class="table table-striped table-bordered">
<thead>
<tr>
<td>
路由
</td>
<td>
时间 (ms)
</td>
</tr>
</thead>
<tbody>
{{#each time}}
<tr>
<td>
{{route}}
</td>
<td>
{{avg}}
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
</div>
</div>
<div class="col-md-6 config-panel" data-type="count">
<div class="panel panel-warning">
<div class="panel-heading">
调用次数
</div>
<div class="panel-body">
<table class="table table-striped table-bordered">
<thead>
<tr>
<td>
路由
</td>
<td>
调用次数
</td>
</tr>
</thead>
<tbody>
{{#each count}}
<tr>
<td>
{{route}}
</td>
<td>
{{count}}
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
var query = {
app: '',
time: ''
};
function qs(obj) {
return Object.keys(obj).map(key => {
return `${key}=${encodeURIComponent(obj[key])}`;
}).join('&')
}
$(function() {
initSelect();
initDatePicker();
});
const TIME = {
'最近1分钟': [1, 'm'],
'最近30分钟': [30, 'm'],
'最近1小时': [1, 'h'],
'最近3小时': [3, 'h'],
'最近6小时': [6, 'h'],
'最近12小时': [12, 'h']
};
function initDatePicker() {
var first = '无';
$('#reportrange span').html(first);
lastTime = TIME[first];
function cb(start, end, label) {
if (label && label !== '自定义日期') {
$('#reportrange span').html(label);
var lastTime = TIME[label];
var selectedStartTime = moment().subtract(...lastTime).unix();
var selectedEndTime = moment().unix();
query.time = `${selectedStartTime}:${selectedEndTime}`;
} else if (label && label === '自定义日期'){
$('#reportrange span').html(start.format('YYYY-MM-DD') + ' 至 ' + end.format('YYYY-MM-DD'));
var selectedStartTime = start.format('YYYY-MM-DD').unix();
var selectedEndTime = end.format('YYYY-MM-DD').unix();
query.time = `${selectedStartTime}:${selectedEndTime}`;
}
}
$('#reportrange').daterangepicker({
startDate: undefined,
endDate: undefined,
timePicker: true,
timePicker24Hour: true,
dateLimit: {
days: 7
},
ranges: {
'最近1分钟': [moment().subtract(1, 'minutes'), moment()],
'最近30分钟': [moment().subtract(30, 'minutes'), moment()],
'最近1小时': [moment().subtract(1, 'hours'), moment()],
'最近3小时': [moment().subtract(3, 'hours'), moment()],
'最近6小时': [moment().subtract(6, 'hours'), moment()],
'最近12小时': [moment().subtract(12, 'hours'), moment()]
},
locale: {
format: "YYYY-MM-DD",
applyLabel: '确定',
cancelLabel: '取消',
weekLabel: 'W',
customRangeLabel: '自定义日期',
daysOfWeek: '一_二_三_四_五_六_日'.split('_'),
monthNames: '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'),
firstDay: 1
},
}, cb);
$('#reportrange').on('cancel.daterangepicker', function() {
$('#reportrange').data('daterangepicker').setStartDate(undefined);
$('#reportrange').data('daterangepicker').setEndDate(undefined);
$('#reportrange span').html(first);
});
}
function initSelect() {
$('#selectedApp').change(function() {
query.app = $('#selectedApp').val();
});
$('#search').on('click', () => {
_handleChanged();
});
$('#clear').on('click', () => {
clearSelect();
})
}
function clearSelect() {
$('#selectedApp').val('');
$('#reportrange').trigger('cancel.daterangepicker');
query = {
app: '',
time: '',
};
}
function _handleChanged() {
$.post(`/profile/sort_route_list?${qs(query)}`).then(result => {
$('.route-data').replaceWith(result);
});
}
</script>
\ No newline at end of file
... ...
<div class="row route-data">
<div class="col-md-6 config-panel" data-type="slow">
<div class="panel panel-success">
<div class="panel-heading">
最慢降序
</div>
<div class="panel-body">
<table class="table table-striped table-bordered">
<thead>
<tr>
<td>
路由
</td>
<td>
时间 (ms)
</td>
</tr>
</thead>
<tbody>
{{#each time}}
<tr>
<td>
{{route}}
</td>
<td>
{{avg}}
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
</div>
</div>
<div class="col-md-6 config-panel" data-type="count">
<div class="panel panel-warning">
<div class="panel-heading">
调用次数
</div>
<div class="panel-body">
<table class="table table-striped table-bordered">
<thead>
<tr>
<td>
路由
</td>
<td>
调用次数
</td>
</tr>
</thead>
<tbody>
{{#each count}}
<tr>
<td>
{{route}}
</td>
<td>
{{count}}
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
</div>
</div>
</div>
\ No newline at end of file
... ...
... ... @@ -82,8 +82,11 @@
<li class="parent"><a><i class="fa fa-list"></i> <span>性能统计</span></a>
<ul class="children">
<li><a href="/profile/server"> <span>路由时间统计</span></a></li>
<li><a href="/profile/error"> <span>错误信息统计</span></a></li>
<li><a href="/profile/sort_route"> <span>路由排行榜</span></a></li>
<li><a href="/profile/sort_api"> <span>api排行榜</span></a></li>
<li><a href="/profile/sort_client"> <span>client排行榜</span></a></li>
</ul>
</li>
{{/if}}
... ...