Authored by htoooth

fix

@@ -8,4 +8,6 @@ db/** @@ -8,4 +8,6 @@ db/**
8 8
9 packages/ 9 packages/
10 10
11 -.idea/  
  11 +.idea/
  12 +
  13 +*.log
@@ -50,4 +50,11 @@ app.use(mount('/', webApp)); @@ -50,4 +50,11 @@ app.use(mount('/', webApp));
50 50
51 server.listen(port, () => { 51 server.listen(port, () => {
52 console.log(`app started in ${port}`); 52 console.log(`app started in ${port}`);
53 -});  
  53 +
  54 + // process.on('uncaughtException', function(err) {
  55 + // console.error('Caught exception:', err.stack)
  56 + // });
  57 + // process.on('unhandledRejection', function(reason, p) {
  58 + // console.error('Unhandled Rejection at: Promise ', p, ' reason: ', reason.stack)
  59 + // });
  60 +});
  1 +'use strict';
  2 +const Router = require('koa-router');
  3 +const SqlBuilder = require('../utils/sql-builder');
  4 +const request = require('superagent');
  5 +const _ = require('lodash');
  6 +
  7 +const r = new Router;
  8 +
  9 +const TABLE = {
  10 + DURATION: 'api-duration',
  11 + REPORT: 'error-report'
  12 +};
  13 +
  14 +const profile_sql = {
  15 + duration() {
  16 + return SqlBuilder.of(TABLE.DURATION);
  17 + },
  18 + error() {
  19 + return SqlBuilder.of(TABLE.REPORT);
  20 + }
  21 +};
  22 +
  23 +async function exec(sql) {
  24 + console.log('influx sql =>', sql);
  25 + return request.get('http://influxd.yoho.cn/query')
  26 + .query({
  27 + q: sql,
  28 + db: 'web-apm'
  29 + }).then(({body: result}) => {
  30 + const series = _.get(result, 'results[0].series[0]', {});
  31 + const col = _.get(series, 'columns', []);
  32 + const values = _.get(series, "values", []);
  33 +
  34 + return values.map(v => {
  35 + return _.zipObject(col, v)
  36 + })
  37 + });
  38 +}
  39 +
  40 +const profile_service = {
  41 + async time() {
  42 + const model = profile_sql.duration().select('*');
  43 + const rows = await exec(model.toSql());
  44 + return {times: rows}
  45 + },
  46 +
  47 + async error() {
  48 + const model = profile_sql.error().select('*');
  49 + const rows = await exec(model.toSql());
  50 + return {errors: rows};
  51 + }
  52 +};
  53 +
  54 +const profile_controller = {
  55 + async time_report(ctx) {
  56 + const result = await profile_service.time();
  57 + await ctx.render('action/profile_time', result);
  58 + },
  59 + async error_report(ctx) {
  60 + const result = await profile_service.error();
  61 + await ctx.render('action/profile_error', result);
  62 + }
  63 +};
  64 +
  65 +r.get('/time', profile_controller.time_report);
  66 +r.get('/error', profile_controller.error_report);
  67 +
  68 +module.exports = r;
@@ -21,6 +21,7 @@ const api = require('./actions/api'); @@ -21,6 +21,7 @@ const api = require('./actions/api');
21 const abuseProtection = require('./actions/abuse_protection'); 21 const abuseProtection = require('./actions/abuse_protection');
22 const crawler = require('./actions/crawler'); 22 const crawler = require('./actions/crawler');
23 const seo = require('./actions/seo'); 23 const seo = require('./actions/seo');
  24 +const profile = require('./actions/profile');
24 const checkcode = require('./actions/checkcode').router; 25 const checkcode = require('./actions/checkcode').router;
25 const noAuth = new Router(); 26 const noAuth = new Router();
26 const base = new Router(); 27 const base = new Router();
@@ -55,6 +56,7 @@ module.exports = function(app) { @@ -55,6 +56,7 @@ module.exports = function(app) {
55 base.use('/degrade', degrade.routes(), degrade.allowedMethods()); 56 base.use('/degrade', degrade.routes(), degrade.allowedMethods());
56 base.use('/deploys', deploy.routes(), deploy.allowedMethods()); 57 base.use('/deploys', deploy.routes(), deploy.allowedMethods());
57 base.use('/keywords', keywords.routes(), keywords.allowedMethods()); 58 base.use('/keywords', keywords.routes(), keywords.allowedMethods());
  59 + base.use('/profile', profile.routes(), profile.allowedMethods());
58 60
59 const white = crawler('/crawler/ip_whitelists', '/crawler/ua_whitelists', '白名单', false); 61 const white = crawler('/crawler/ip_whitelists', '/crawler/ua_whitelists', '白名单', false);
60 const black = crawler('/crawler/ip_blacklists', '/crawler/ua_blacklists', '黑名单', true); 62 const black = crawler('/crawler/ip_blacklists', '/crawler/ua_blacklists', '黑名单', true);
  1 +class SqlBuilder {
  2 + constructor(table) {
  3 + this._select = [];
  4 + this._from = table;
  5 + this._where = [];
  6 + this._orWhere = [];
  7 + this._limit = 10;
  8 + this._offset = 0;
  9 + this._orderBy = null;
  10 + }
  11 +
  12 + static of(table) {
  13 + return new SqlBuilder(table)
  14 + }
  15 +
  16 + // placeholer is {}
  17 + // select * from where time={}
  18 + static raw(sql, ...args) {
  19 + return args.reduce((t, a) => {
  20 + return t.replace('{}', a)
  21 + }, sql.slice(0))
  22 + }
  23 +
  24 + select(...args) {
  25 + this._select.push(...args);
  26 + return this;
  27 + }
  28 +
  29 + where(field, op, value) {
  30 + this._where.push({field, op, value});
  31 + return this;
  32 + }
  33 +
  34 + orWhere(field, op, value) {
  35 + this._orWhere.push({field, op, value});
  36 + return this;
  37 + }
  38 +
  39 + from(table) {
  40 + this._from = table;
  41 + return this;
  42 + }
  43 +
  44 + limit(limit) {
  45 + this._limit = limit;
  46 + return this;
  47 + }
  48 +
  49 + offset(offset) {
  50 + this._offset = offset;
  51 + return this;
  52 + }
  53 +
  54 + // order = desc or asc
  55 + orderBy(order = 'DESC') {
  56 + this._orderBy = order;
  57 + return this;
  58 + }
  59 +
  60 + toSql() {
  61 + return this.toString();
  62 + }
  63 +
  64 + toString() {
  65 + let sql = `SELECT ${this._select.join(',')} FROM "${this._from}" `;
  66 +
  67 + let andWhere = this._where.map(({field, op, value}) => `${field}${op}${value}`).join(' AND ');
  68 + let orWhere = this._orWhere.map(({field, op, value}) => `${field}${op}${value}`).join(' OR ');
  69 +
  70 + if (andWhere) {
  71 + sql += `WHERE ${andWhere} `;
  72 + }
  73 +
  74 + if (andWhere && orWhere) {
  75 + sql += `AND (${orWhere}) `;
  76 + } else {
  77 + if (orWhere) {
  78 + sql += `WHERE ${orWhere}`
  79 + }
  80 + }
  81 +
  82 + if (this._orderBy) {
  83 + sql += `ORDER BY time ${this._orderBy.order} `
  84 + }
  85 +
  86 + sql += `LIMIT ${this._limit} `;
  87 + sql += `OFFSET ${this._offset} `;
  88 +
  89 + return sql;
  90 + }
  91 +}
  92 +
  93 +module.exports = SqlBuilder;
  1 +<div class="pageheader">
  2 + <div class="media">
  3 + <div class="pageicon pull-left">
  4 + <i class="fa fa-th-list"></i>
  5 + </div>
  6 + <div class="media-body">
  7 + <ul class="breadcrumb">
  8 + <li><a href=""><i class="glyphicon glyphicon-home"></i></a></li>
  9 + <li><a href="">性能监控</a></li>
  10 + <li>错误分析</li>
  11 + </ul>
  12 + <h4>错误分析</h4>
  13 + </div>
  14 + </div>
  15 + <!-- media -->
  16 +</div>
  17 +<!-- pageheader -->
  18 +
  19 +<div class="contentpanel page-servers">
  20 + <div class="panel panel-primary-head">
  21 + <!--<div class="panel-heading">-->
  22 + <!--<div class="pull-right">-->
  23 + <!--<a id="new-page" href="/servers/new" class="btn btn-success btn-rounded"><i class="glyphicon glyphicon-plus"></i> 新增服务器</a>-->
  24 + <!--</div>-->
  25 + <!--<h4 class="panel-title">服务器设置</h4>-->
  26 + <!--<p>配置服务器连接方式、可以通过标签区分</p>-->
  27 +
  28 + <!--</div>-->
  29 + <!-- panel-heading -->
  30 +
  31 + <table id="table-servers" class="table table-striped table-bordered responsive">
  32 + <thead class="">
  33 + <tr>
  34 + <th>时间</th>
  35 + <th>应用</th>
  36 + <th>类型</th>
  37 + <th>父请求ID</th>
  38 + <th>请求ID</th>
  39 + <td>用户ID</td>
  40 + <td>访客ID</td>
  41 + <td>错误状态码</td>
  42 + <td>错误信息</td>
  43 + <td>出错文件</td>
  44 + <td>行号</td>
  45 + <td>列数</td>
  46 + <td>错误堆栈</td>
  47 + </tr>
  48 + </thead>
  49 +
  50 + <tbody>
  51 + {{#each errors}}
  52 + <tr>
  53 + <td>{{time}}</td>
  54 + <td>{{app}}</td>
  55 + <td>{{type}}</td>
  56 + <td>{{preqID}}</td>
  57 + <td>{{reqID}}</td>
  58 + <td>{{uid}}</td>
  59 + <td>{{udid}}</td>
  60 + <td>{{code}}</td>
  61 + <td>{{message}}</td>
  62 + <td>{{script}}</td>
  63 + <td>{{line}}</td>
  64 + <td>{{column}}</td>
  65 + <td>{{stack}}</td>
  66 + </tr>
  67 + {{/each}}
  68 + </tbody>
  69 + </table>
  70 + </div>
  71 + <!-- panel -->
  72 +</div>
  73 +
  74 +
  75 +<script>
  76 + $(document).off().on('ready pjax:end', function() {
  77 +
  78 + console.log('page: servers');
  79 +
  80 + // $("#table-servers").on('draw.dt', function() {
  81 + // eventBind();
  82 + // });
  83 +
  84 + function eventBind() {
  85 + $('.server-del').off().on('click', function(){
  86 + var id = $(this).parent().data('id');
  87 + $.post('/servers/del', {id: id}, function(ret){
  88 + if(ret.code == 200){
  89 + var i = layer.alert('操作成功', function(){
  90 + layer.close(i);
  91 + location.href = location.href;
  92 + });
  93 + }
  94 + });
  95 + });
  96 +
  97 + $('.server-edit').off().on('click', function(){
  98 + var id = $(this).parent().data('id');
  99 + location.href = '/servers/edit?id=' + id;
  100 + });
  101 + }
  102 +
  103 + eventBind();
  104 +
  105 + $("#table-servers").DataTable({
  106 + pageLength: 20,
  107 + retrieve: true,
  108 + responsive: true,
  109 + searching: false
  110 + });
  111 +
  112 + });
  113 +
  114 +</script>
  1 +<div class="pageheader">
  2 + <div class="media">
  3 + <div class="pageicon pull-left">
  4 + <i class="fa fa-th-list"></i>
  5 + </div>
  6 + <div class="media-body">
  7 + <ul class="breadcrumb">
  8 + <li><a href=""><i class="glyphicon glyphicon-home"></i></a></li>
  9 + <li><a href="">性能监控</a></li>
  10 + <li>耗时分析</li>
  11 + </ul>
  12 + <h4>耗时分析</h4>
  13 + </div>
  14 + </div>
  15 + <!-- media -->
  16 +</div>
  17 +<!-- pageheader -->
  18 +
  19 +<div class="contentpanel page-servers">
  20 + <div class="panel panel-primary-head">
  21 + <!--<div class="panel-heading">-->
  22 + <!--<div class="pull-right">-->
  23 + <!--<a id="new-page" href="/servers/new" class="btn btn-success btn-rounded"><i class="glyphicon glyphicon-plus"></i> 新增服务器</a>-->
  24 + <!--</div>-->
  25 + <!--<h4 class="panel-title">服务器设置</h4>-->
  26 + <!--<p>配置服务器连接方式、可以通过标签区分</p>-->
  27 +
  28 + <!--</div>-->
  29 + <!-- panel-heading -->
  30 +
  31 + <table id="table-servers" class="table table-striped table-bordered responsive">
  32 + <thead class="">
  33 + <tr>
  34 + <th>时间</th>
  35 + <th>应用</th>
  36 + <th>类型</th>
  37 + <th>父请求ID</th>
  38 + <th>请求ID</th>
  39 + <td>用户ID</td>
  40 + <td>访客ID</td>
  41 + <td>接口名称</td>
  42 + <td>请求路由</td>
  43 + <td>耗时</td>
  44 + </tr>
  45 + </thead>
  46 +
  47 + <tbody>
  48 + {{#each times}}
  49 + <tr>
  50 + <td>{{time}}</td>
  51 + <td>{{app}}</td>
  52 + <td>{{type}}</td>
  53 + <td>{{preqID}}</td>
  54 + <td>{{reqID}}</td>
  55 + <td>{{uid}}</td>
  56 + <td>{{udid}}</td>
  57 + <td>{{api}}</td>
  58 + <td>{{route}}</td>
  59 + <td>{{duration}}</td>
  60 + </tr>
  61 + {{/each}}
  62 + </tbody>
  63 + </table>
  64 + </div>
  65 + <!-- panel -->
  66 +</div>
  67 +
  68 +
  69 +<script>
  70 + $(document).off().on('ready pjax:end', function() {
  71 +
  72 + console.log('page: servers');
  73 +
  74 + // $("#table-servers").on('draw.dt', function() {
  75 + // eventBind();
  76 + // });
  77 +
  78 + function eventBind() {
  79 + $('.server-del').off().on('click', function(){
  80 + var id = $(this).parent().data('id');
  81 + $.post('/servers/del', {id: id}, function(ret){
  82 + if(ret.code == 200){
  83 + var i = layer.alert('操作成功', function(){
  84 + layer.close(i);
  85 + location.href = location.href;
  86 + });
  87 + }
  88 + });
  89 + });
  90 +
  91 + $('.server-edit').off().on('click', function(){
  92 + var id = $(this).parent().data('id');
  93 + location.href = '/servers/edit?id=' + id;
  94 + });
  95 + }
  96 +
  97 + eventBind();
  98 +
  99 + $("#table-servers").DataTable({
  100 + pageLength: 20,
  101 + retrieve: true,
  102 + responsive: true,
  103 + searching: false
  104 + });
  105 +
  106 + });
  107 +
  108 +</script>
@@ -74,6 +74,13 @@ @@ -74,6 +74,13 @@
74 <li><a href="/seo/tdk"><span>TDK管理</span></a></li> 74 <li><a href="/seo/tdk"><span>TDK管理</span></a></li>
75 </ul> 75 </ul>
76 </li> 76 </li>
  77 +
  78 + <li class="parent"><a><i class="fa fa-list"></i> <span>性能分析</span></a>
  79 + <ul class="children">
  80 + <li><a href="/profile/time"> <span>耗时</span></a></li>
  81 + <li><a href="/profile/error"> <span>错误</span></a></li>
  82 + </ul>
  83 + </li>
77 {{/if}} 84 {{/if}}
78 </ul> 85 </ul>
79 86
@@ -64,6 +64,7 @@ @@ -64,6 +64,7 @@
64 "shelljs": "^0.7.0", 64 "shelljs": "^0.7.0",
65 "socket.io": "^1.4.6", 65 "socket.io": "^1.4.6",
66 "ssh2": "^0.5.4", 66 "ssh2": "^0.5.4",
  67 + "superagent": "^3.6.0",
67 "tar": "^2.2.1", 68 "tar": "^2.2.1",
68 "utility": "^1.8.0" 69 "utility": "^1.8.0"
69 }, 70 },
This diff could not be displayed because it is too large.