Authored by htoooth

fix

... ... @@ -4,6 +4,7 @@ const Router = require('koa-router');
const SqlBuilder = require('../utils/sql-builder');
const request = require('superagent');
const _ = require('lodash');
const fp = require('lodash/fp');
const jp = require('jsonpath');
const config = require('../../../config/config');
const ErrorModel = require('../models/errorModel');
... ... @@ -189,9 +190,9 @@ const profile_service = {
return result;
},
async error(ctx, app, start, length) {
async error(ctx, app, host, type, time, code, api, route, start, length) {
const errorModel = new ErrorModel(ctx);
const result = await errorModel.getList(app, start, length);
const result = await errorModel.getList(app, host, type, time, code, api, route, start, length);
return result;
}
... ... @@ -211,15 +212,31 @@ const profile_controller = {
ctx.body = Object.assign({}, result, {draw});
},
async error_report_index(ctx) {
await ctx.render('action/profile_error');
let model = new ErrorModel(ctx);
let hostName = await model.getHost().then(r => r.map(fp.prop('hostname')));
let app = await model.getApp().then(r => r.map(fp.prop('app')));
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')));
await ctx.render('action/profile_error', {hostName, app, type, code, route, api});
},
async error_report_json(ctx) {
const app = APP_NAME[(ctx.query.app)] || APP_NAME.default;
const start = parseInt(ctx.query.start) || 0;
const length = parseInt(ctx.query.length) || 10;
const draw = ctx.query.draw;
const result = await profile_service.error(ctx, app, start, length);
const app = ctx.query.app;
const host = ctx.query.hostname;
const type = ctx.query.type;
const time = ctx.query.time;
const code = ctx.query.code;
const route = ctx.query.route;
const api = ctx.query.api;
const result = await profile_service.error(ctx, app, host, type, time, code, api, route, start, length);
ctx.body = Object.assign({}, result, {draw});
}
... ...
... ... @@ -7,11 +7,51 @@ class errorModel extends model {
this.mysql = new mysqlPromise();
}
async getList(app, page, pageSize) {
pageSize = pageSize || 10;
async getList(app, host, type, time, code, api, route ,start, length) {
length = length || 10;
let recordsTotal = await this.mysql.query('SELECT COUNT(*) as count from error_report where app = ?', [app]).then(([r]) => r.count);
let data = await this.mysql.query('SELECT id, app, type, preqid, reqid, uid, udid, code, line, `column`, script, message, stack, create_time as time from error_report where app = ? order by create_time desc limit ?, ? ', [app, page, pageSize]);
let query = '';
const queryVal = [];
if (app) {
query += 'and app = ?';
queryVal.push(app);
}
if (host) {
query += 'and hostname = ?';
queryVal.push(host);
}
if (type) {
query += 'and type = ?';
queryVal.push(type);
}
if (time) {
let [start,end] = time.split(':');
query += 'and create_time > ? and create_time < ?';
queryVal.push(start);
queryVal.push(end);
}
if (code) {
query += 'and code = ?';
queryVal.push(code);
}
if (api) {
query += 'and api = ?';
queryVal.push(api);
}
if (route) {
query += 'and route = ?';
queryVal.push(route);
}
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]);
return {
recordsTotal,
... ... @@ -19,6 +59,30 @@ class errorModel extends model {
data
}
}
async getApp() {
return await this.mysql.query('SELECT DISTINCT app FROM error_report');
}
async getHost() {
return await this.mysql.query('SELECT DISTINCT hostname FROM error_report');
}
async getType() {
return await this.mysql.query('SELECT DISTINCT type FROM error_report');
}
async getCode() {
return await this.mysql.query('SELECT DISTINCT code FROM error_report');
}
async getRoute() {
return await this.mysql.query('SELECT DISTINCT route FROM error_report');
}
async getApi() {
return await this.mysql.query('SELECT DISTINCT api FROM error_report');
}
}
module.exports = errorModel;
\ No newline at end of file
... ...
... ... @@ -16,29 +16,93 @@
</div>
<!-- pageheader -->
<style>
.form-item {
margin-right: 10px;
margin-bottom: 10px;
}
</style>
<div class="contentpanel page-servers">
<div class="panel panel-primary-head">
<div class="panel panel-default">
<div class="panel-heading" style="overflow: hidden">
<div class="form-inline inline-block form-item">
<label for="appName" class="control-label">主机</label>
<select id="selectedHost" class="form-control" >
<option value="">全部</option>
{{#each hostName}}
<option value="{{this}}">{{this}}</option>
{{/each}}
</select>
</div>
<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>
<select id="selectedType" class="form-control" >
<option value="">全部</option>
{{#each type}}
<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 form-item">
<label for="appName" class="control-label">错误编码</label>
<select id="selectedCode" class="form-control" >
<option value="">全部</option>
{{#each code}}
<option value="{{this}}">{{this}}</option>
{{/each}}
</select>
</div>
<div class="form-inline inline-block form-item">
<label for="appName" class="control-label">路由</label>
<select id="selectedRoute" class="form-control" >
<option value="">全部</option>
{{#each route}}
<option value="{{this}}">{{this}}</option>
{{/each}}
</select>
</div>
<div class="form-inline inline-block form-item">
<label for="appName" class="control-label">api</label>
<select id="selectedApi" class="form-control" >
<option value="">全部</option>
{{#each api}}
<option value="{{this}}">{{this}}</option>
{{/each}}
</select>
</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 class="pull-left">
<select id="selectedServer" class="selectpicker show-menu-arrow form-control"
style="margin-left: 10px;">
<option value="aws" selected>aws</option>
<option value="qcloud">qcloud</option>
</select>
</div>
<div class="pull-left">
<select id="selectedApp" class="selectpicker show-menu-arrow form-control" style="margin-left: 10px;">
<option value="default">全部</option>
<option value="pc">PC</option>
<option value="h5">H5</option>
</select>
</div>
<div class="pull-left">
<button id="search" class="btn btn-info" style="margin-left: 20px;">查询</button>
</div>
</div>
<!-- panel-heading -->
... ... @@ -51,12 +115,14 @@
<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>-->
<!--<td>错误堆栈</td>-->
</tr>
</thead>
... ... @@ -69,107 +135,44 @@
<script>
// var selectedServer, selectedApp;
// var data_end_point = '/profile/error.json';
// var dataTable = null;
//
// function ajaxUrl() {
// let url = `${data_end_point}?app=${selectedApp}&server=${selectedServer}`;
// return url
// }
//
// $(function() {
// init();
// initTable();
// initSelect();
// });
//
// function init() {
// selectedServer = 'aws';
// selectedApp = 'default';
// }
//
// function initTable() {
// dataTable = $("#table-servers").DataTable({
// pageLength: 20,
// serverSide: true,
// retrieve: true,
// searching: false,
// ajax: ajaxUrl(),
// dataSrc: 'data',
// pageLength: 25,
// columns: [
// {data: 'time'},
// {data: 'app'},
// {data: 'type'},
// {data: 'reqID'},
// {data: 'uid'},
// {data: 'udid'},
// {data: 'code', defaultContent: '无'},
// {data: 'message', defaultContent: '无'},
// {data: 'script', defaultContent: '无'},
// {data: 'line'},
// {data: 'column'},
// {data: 'stack', defaultContent: '无'}
// ],
// data: [{
// time: "",
// app: "",
// type: "",
// reqID: "",
// uid: "",
// udid: "",
// code: "",
// message: "",
// script: "",
// line: "",
// column: "",
// stack: ""
// }],
// });
// }
//
// function initSelect() {
// $('#selectedApp').change(function() {
// selectedApp = $('#selectedApp').val();
// });
//
// $('#search').on('click', () => {
// _handleChanged();
// })
// }
//
// function _handleChanged() {
// dataTable && dataTable.ajax.url(ajaxUrl()).load();
// }
var selectedServer, selectedApp;
var data_end_point = '/profile/error.json';
var dataTable = null;
var query = {
hostname: '',
app: '',
type: '',
time: '',
code: '',
route: '',
api: ''
};
function qs(obj) {
return Object.keys(obj).map(key => {
return `${key}=${encodeURIComponent(obj[key])}`;
}).join('&')
}
function ajaxUrl() {
let url = `${data_end_point}?app=${selectedApp}&server=${selectedServer}`;
return url
let result = `${data_end_point}?${qs(query)}`;
return result;
}
$(function() {
init();
initTable();
initSelect();
initDatePicker();
});
function init() {
selectedServer = 'aws';
selectedApp = 'default';
}
function initTable() {
dataTable = $("#table-servers").DataTable({
pageLength: 20,
serverSide: true,
retrieve: true,
searching: false,
responsive: true,
ajax: ajaxUrl(),
dataSrc: 'data',
pageLength: 25,
... ... @@ -180,12 +183,14 @@
{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: 'message', defaultContent: '无'},
// {data: 'script', defaultContent: '无'},
// {data: 'line'},
// {data: 'column'},
// {data: 'stack', defaultContent: '无'}
],
data: [{
time: "",
... ... @@ -194,12 +199,14 @@
reqid: "",
uid: "",
udid: "",
route: "",
api: "",
code: "",
message: "",
script: "",
line: "",
column: "",
stack: ""
// message: "",
// script: "",
// line: "",
// column: "",
// stack: ""
}],
columnDefs: [
{
... ... @@ -217,18 +224,117 @@
});
}
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() {
$('#selectedHost').change(function() {
query.hostname = $('#selectedHost').val();
});
$('#selectedApp').change(function() {
selectedApp = $('#selectedApp').val();
query.app = $('#selectedApp').val();
});
$('#selectedType').change(function() {
query.type = $('#selectedType').val();
});
$('#selectedCode').change(function() {
query.code = $('#selectedCode').val();
});
$('#selectedRoute').change(function() {
query.route = $('#selectedRoute').val();
});
$('#selectedApi').change(function() {
query.api = $('#selectedApi').val();
});
$('#search').on('click', () => {
_handleChanged();
});
$('#clear').on('click', () => {
clearSelect();
})
}
function _handleChanged() {
dataTable && dataTable.ajax.url(ajaxUrl()).load();
}
function clearSelect() {
}
</script>
\ No newline at end of file
... ...
... ... @@ -102,14 +102,14 @@ ansi-align@^2.0.0:
dependencies:
string-width "^2.0.0"
ansi-regex@*, ansi-regex@^3.0.0:
version "3.0.0"
resolved "http://npm.yoho.cn/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
ansi-regex@^2.0.0:
version "2.1.1"
resolved "http://npm.yoho.cn/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
ansi-regex@^3.0.0:
version "3.0.0"
resolved "http://npm.yoho.cn/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
ansi-styles@^2.2.1:
version "2.2.1"
resolved "http://npm.yoho.cn/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
... ... @@ -764,7 +764,7 @@ debug@2.3.3:
dependencies:
ms "0.7.2"
debuglog@*, debuglog@^1.0.1:
debuglog@^1.0.1:
version "1.0.1"
resolved "http://npm.yoho.cn/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
... ... @@ -1680,7 +1680,7 @@ import-lazy@^2.1.0:
version "2.1.0"
resolved "http://npm.yoho.cn/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43"
imurmurhash@*, imurmurhash@^0.1.4:
imurmurhash@^0.1.4:
version "0.1.4"
resolved "http://npm.yoho.cn/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
... ... @@ -2180,10 +2180,6 @@ lodash._basecopy@^3.0.0:
version "3.0.1"
resolved "http://npm.yoho.cn/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36"
lodash._baseindexof@*:
version "3.1.0"
resolved "http://npm.yoho.cn/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c"
lodash._baseuniq@~4.6.0:
version "4.6.0"
resolved "http://npm.yoho.cn/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8"
... ... @@ -2191,14 +2187,10 @@ lodash._baseuniq@~4.6.0:
lodash._createset "~4.0.0"
lodash._root "~3.0.0"
lodash._bindcallback@*, lodash._bindcallback@^3.0.0:
lodash._bindcallback@^3.0.0:
version "3.0.1"
resolved "http://npm.yoho.cn/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e"
lodash._cacheindexof@*:
version "3.0.2"
resolved "http://npm.yoho.cn/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92"
lodash._createassigner@^3.0.0:
version "3.1.1"
resolved "http://npm.yoho.cn/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz#838a5bae2fdaca63ac22dee8e19fa4e6d6970b11"
... ... @@ -2207,17 +2199,11 @@ lodash._createassigner@^3.0.0:
lodash._isiterateecall "^3.0.0"
lodash.restparam "^3.0.0"
lodash._createcache@*:
version "3.1.2"
resolved "http://npm.yoho.cn/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093"
dependencies:
lodash._getnative "^3.0.0"
lodash._createset@~4.0.0:
version "4.0.3"
resolved "http://npm.yoho.cn/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26"
lodash._getnative@*, lodash._getnative@^3.0.0:
lodash._getnative@^3.0.0:
version "3.9.1"
resolved "http://npm.yoho.cn/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
... ... @@ -2264,7 +2250,7 @@ lodash.keys@^3.0.0:
lodash.isarguments "^3.0.0"
lodash.isarray "^3.0.0"
lodash.restparam@*, lodash.restparam@^3.0.0:
lodash.restparam@^3.0.0:
version "3.6.1"
resolved "http://npm.yoho.cn/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805"
... ... @@ -3249,7 +3235,7 @@ readable-stream@~2.1.5:
string_decoder "~0.10.x"
util-deprecate "~1.0.1"
readdir-scoped-modules@*, readdir-scoped-modules@^1.0.0:
readdir-scoped-modules@^1.0.0:
version "1.0.2"
resolved "http://npm.yoho.cn/readdir-scoped-modules/-/readdir-scoped-modules-1.0.2.tgz#9fafa37d286be5d92cbaebdee030dc9b5f406747"
dependencies:
... ... @@ -4106,7 +4092,7 @@ uuid@^3.0.0, uuid@^3.1.0:
version "3.1.0"
resolved "http://npm.yoho.cn/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04"
validate-npm-package-license@*, validate-npm-package-license@^3.0.1:
validate-npm-package-license@^3.0.1:
version "3.0.1"
resolved "http://npm.yoho.cn/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz#2804babe712ad3379459acfbe24746ab2c303fbc"
dependencies:
... ...