Authored by htoooth

fix

... ... @@ -9,3 +9,5 @@ db/**
packages/
.idea/
*.log
\ No newline at end of file
... ...
... ... @@ -50,4 +50,11 @@ app.use(mount('/', webApp));
server.listen(port, () => {
console.log(`app started in ${port}`);
// process.on('uncaughtException', function(err) {
// console.error('Caught exception:', err.stack)
// });
// process.on('unhandledRejection', function(reason, p) {
// console.error('Unhandled Rejection at: Promise ', p, ' reason: ', reason.stack)
// });
});
... ...
'use strict';
const Router = require('koa-router');
const SqlBuilder = require('../utils/sql-builder');
const request = require('superagent');
const _ = require('lodash');
const r = new Router;
const TABLE = {
DURATION: 'api-duration',
REPORT: 'error-report'
};
const profile_sql = {
duration() {
return SqlBuilder.of(TABLE.DURATION);
},
error() {
return SqlBuilder.of(TABLE.REPORT);
}
};
async function exec(sql) {
console.log('influx sql =>', sql);
return request.get('http://influxd.yoho.cn/query')
.query({
q: sql,
db: 'web-apm'
}).then(({body: result}) => {
const series = _.get(result, 'results[0].series[0]', {});
const col = _.get(series, 'columns', []);
const values = _.get(series, "values", []);
return values.map(v => {
return _.zipObject(col, v)
})
});
}
const profile_service = {
async time() {
const model = profile_sql.duration().select('*');
const rows = await exec(model.toSql());
return {times: rows}
},
async error() {
const model = profile_sql.error().select('*');
const rows = await exec(model.toSql());
return {errors: rows};
}
};
const profile_controller = {
async time_report(ctx) {
const result = await profile_service.time();
await ctx.render('action/profile_time', result);
},
async error_report(ctx) {
const result = await profile_service.error();
await ctx.render('action/profile_error', result);
}
};
r.get('/time', profile_controller.time_report);
r.get('/error', profile_controller.error_report);
module.exports = r;
\ No newline at end of file
... ...
... ... @@ -21,6 +21,7 @@ const api = require('./actions/api');
const abuseProtection = require('./actions/abuse_protection');
const crawler = require('./actions/crawler');
const seo = require('./actions/seo');
const profile = require('./actions/profile');
const checkcode = require('./actions/checkcode').router;
const noAuth = new Router();
const base = new Router();
... ... @@ -55,6 +56,7 @@ module.exports = function(app) {
base.use('/degrade', degrade.routes(), degrade.allowedMethods());
base.use('/deploys', deploy.routes(), deploy.allowedMethods());
base.use('/keywords', keywords.routes(), keywords.allowedMethods());
base.use('/profile', profile.routes(), profile.allowedMethods());
const white = crawler('/crawler/ip_whitelists', '/crawler/ua_whitelists', '白名单', false);
const black = crawler('/crawler/ip_blacklists', '/crawler/ua_blacklists', '黑名单', true);
... ...
class SqlBuilder {
constructor(table) {
this._select = [];
this._from = table;
this._where = [];
this._orWhere = [];
this._limit = 10;
this._offset = 0;
this._orderBy = null;
}
static of(table) {
return new SqlBuilder(table)
}
// placeholer is {}
// select * from where time={}
static raw(sql, ...args) {
return args.reduce((t, a) => {
return t.replace('{}', a)
}, sql.slice(0))
}
select(...args) {
this._select.push(...args);
return this;
}
where(field, op, value) {
this._where.push({field, op, value});
return this;
}
orWhere(field, op, value) {
this._orWhere.push({field, op, value});
return this;
}
from(table) {
this._from = table;
return this;
}
limit(limit) {
this._limit = limit;
return this;
}
offset(offset) {
this._offset = offset;
return this;
}
// order = desc or asc
orderBy(order = 'DESC') {
this._orderBy = order;
return this;
}
toSql() {
return this.toString();
}
toString() {
let sql = `SELECT ${this._select.join(',')} FROM "${this._from}" `;
let andWhere = this._where.map(({field, op, value}) => `${field}${op}${value}`).join(' AND ');
let orWhere = this._orWhere.map(({field, op, value}) => `${field}${op}${value}`).join(' OR ');
if (andWhere) {
sql += `WHERE ${andWhere} `;
}
if (andWhere && orWhere) {
sql += `AND (${orWhere}) `;
} else {
if (orWhere) {
sql += `WHERE ${orWhere}`
}
}
if (this._orderBy) {
sql += `ORDER BY time ${this._orderBy.order} `
}
sql += `LIMIT ${this._limit} `;
sql += `OFFSET ${this._offset} `;
return sql;
}
}
module.exports = SqlBuilder;
\ 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 -->
<div class="contentpanel page-servers">
<div class="panel panel-primary-head">
<!--<div class="panel-heading">-->
<!--<div class="pull-right">-->
<!--<a id="new-page" href="/servers/new" class="btn btn-success btn-rounded"><i class="glyphicon glyphicon-plus"></i> 新增服务器</a>-->
<!--</div>-->
<!--<h4 class="panel-title">服务器设置</h4>-->
<!--<p>配置服务器连接方式、可以通过标签区分</p>-->
<!--</div>-->
<!-- panel-heading -->
<table id="table-servers" class="table table-striped table-bordered responsive">
<thead class="">
<tr>
<th>时间</th>
<th>应用</th>
<th>类型</th>
<th>父请求ID</th>
<th>请求ID</th>
<td>用户ID</td>
<td>访客ID</td>
<td>错误状态码</td>
<td>错误信息</td>
<td>出错文件</td>
<td>行号</td>
<td>列数</td>
<td>错误堆栈</td>
</tr>
</thead>
<tbody>
{{#each errors}}
<tr>
<td>{{time}}</td>
<td>{{app}}</td>
<td>{{type}}</td>
<td>{{preqID}}</td>
<td>{{reqID}}</td>
<td>{{uid}}</td>
<td>{{udid}}</td>
<td>{{code}}</td>
<td>{{message}}</td>
<td>{{script}}</td>
<td>{{line}}</td>
<td>{{column}}</td>
<td>{{stack}}</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
<!-- panel -->
</div>
<script>
$(document).off().on('ready pjax:end', function() {
console.log('page: servers');
// $("#table-servers").on('draw.dt', function() {
// eventBind();
// });
function eventBind() {
$('.server-del').off().on('click', function(){
var id = $(this).parent().data('id');
$.post('/servers/del', {id: id}, function(ret){
if(ret.code == 200){
var i = layer.alert('操作成功', function(){
layer.close(i);
location.href = location.href;
});
}
});
});
$('.server-edit').off().on('click', function(){
var id = $(this).parent().data('id');
location.href = '/servers/edit?id=' + id;
});
}
eventBind();
$("#table-servers").DataTable({
pageLength: 20,
retrieve: true,
responsive: true,
searching: false
});
});
</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>耗时分析</li>
</ul>
<h4>耗时分析</h4>
</div>
</div>
<!-- media -->
</div>
<!-- pageheader -->
<div class="contentpanel page-servers">
<div class="panel panel-primary-head">
<!--<div class="panel-heading">-->
<!--<div class="pull-right">-->
<!--<a id="new-page" href="/servers/new" class="btn btn-success btn-rounded"><i class="glyphicon glyphicon-plus"></i> 新增服务器</a>-->
<!--</div>-->
<!--<h4 class="panel-title">服务器设置</h4>-->
<!--<p>配置服务器连接方式、可以通过标签区分</p>-->
<!--</div>-->
<!-- panel-heading -->
<table id="table-servers" class="table table-striped table-bordered responsive">
<thead class="">
<tr>
<th>时间</th>
<th>应用</th>
<th>类型</th>
<th>父请求ID</th>
<th>请求ID</th>
<td>用户ID</td>
<td>访客ID</td>
<td>接口名称</td>
<td>请求路由</td>
<td>耗时</td>
</tr>
</thead>
<tbody>
{{#each times}}
<tr>
<td>{{time}}</td>
<td>{{app}}</td>
<td>{{type}}</td>
<td>{{preqID}}</td>
<td>{{reqID}}</td>
<td>{{uid}}</td>
<td>{{udid}}</td>
<td>{{api}}</td>
<td>{{route}}</td>
<td>{{duration}}</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
<!-- panel -->
</div>
<script>
$(document).off().on('ready pjax:end', function() {
console.log('page: servers');
// $("#table-servers").on('draw.dt', function() {
// eventBind();
// });
function eventBind() {
$('.server-del').off().on('click', function(){
var id = $(this).parent().data('id');
$.post('/servers/del', {id: id}, function(ret){
if(ret.code == 200){
var i = layer.alert('操作成功', function(){
layer.close(i);
location.href = location.href;
});
}
});
});
$('.server-edit').off().on('click', function(){
var id = $(this).parent().data('id');
location.href = '/servers/edit?id=' + id;
});
}
eventBind();
$("#table-servers").DataTable({
pageLength: 20,
retrieve: true,
responsive: true,
searching: false
});
});
</script>
\ No newline at end of file
... ...
... ... @@ -74,6 +74,13 @@
<li><a href="/seo/tdk"><span>TDK管理</span></a></li>
</ul>
</li>
<li class="parent"><a><i class="fa fa-list"></i> <span>性能分析</span></a>
<ul class="children">
<li><a href="/profile/time"> <span>耗时</span></a></li>
<li><a href="/profile/error"> <span>错误</span></a></li>
</ul>
</li>
{{/if}}
</ul>
... ...
... ... @@ -64,6 +64,7 @@
"shelljs": "^0.7.0",
"socket.io": "^1.4.6",
"ssh2": "^0.5.4",
"superagent": "^3.6.0",
"tar": "^2.2.1",
"utility": "^1.8.0"
},
... ...
This diff could not be displayed because it is too large.