profile.js 6.25 KB
// http://www.daterangepicker.com/#usage

const Router = require('koa-router');
const SqlBuilder = require('../utils/sql-builder');
const request = require('superagent');
const _ = require('lodash');
const jp = require('jsonpath');
const config = require('../../../config/config');
const ErrorModel = require('../models/errorModel');
const SlowRouteModel = require('../models/slowRouteModel');

const endpoint = (server) => `http://${server.host}:${server.port}`;

const r = new Router;

const TABLE = {
    CLIENT: 'web-client-duration',
    REPORT: 'error-report',
    SERVER: 'web-server-duration'
};

const DB_NAME = 'web-apm';

const APP_NAME = {
    pc: 'yohobuy-node',
    h5: 'yohobuywap-node'
};

const APP_NAME2 = {
    pc: 'www.yohobuy.com',
    h5: 'm.yohobuy.com'
};

const TYPE = {
    DOMContentLoaded: 0,
    load: 0,
    firstscreen: 0
};

const profile_sql = {
    client() {
        return SqlBuilder.of(TABLE.CLIENT);
    },
    error() {
        return SqlBuilder.of(TABLE.REPORT);
    },
    server() {
        return SqlBuilder.of(TABLE.SERVER);
    }
};

async function exec(server, sql) {
    console.log('influx query from ', `[${server}] [${DB_NAME}]`, 'sql =>', sql);
    return request.get(`${server}/query`)
        .query({
            q: sql,
            db: DB_NAME
        }).then(({body: result}) => {
            return result;
        }).catch((err) => {
            console.log(err);
            return {};
        });
}

const APP = {
    default: {field: '', op: '', value: ''},
    pc: {field: 'app', op: '=', value: APP_NAME.pc},
    h5: {field: 'app', op: '=', value: APP_NAME.h5}
};

const APP2 = {
    default: {field: '', op: '', value: ''},
    pc: {field: 'app', op: '=', value: APP_NAME2.pc},
    h5: {field: 'app', op: '=', value: APP_NAME2.h5}
};

const SERVER = {
    aws: endpoint(config.apm.aws),
    qcloud: endpoint(config.apm.qcloud)
};


function handleZip(items) {
    const col = _.get(items, 'columns', []);
    const values = _.get(items, "values", []);

    return values.map((v) => {
        return _.zipObject(col, v)
    });
}

function handleZip2(item) {
    const col = _.get(item, 'columns', []);
    const values = _.get(item, "values[0]", []);
    const tags = _.get(item, "tags", []);

    return Object.assign({}, _.zipObject(col, values), tags)
}

function handleRows(rows) {
    let stats = rows.reduce((acc, cur) => {
        _.updateWith(acc, [cur.route, cur.type, 'duration'], function(n) {
            return _.isNil(n) ? cur.duration : n + cur.duration;
        });

        _.updateWith(acc, [cur.route, cur.type, 'count'], function(n) {
            return _.isNil(n) ? 1 : n + 1
        });

        _.updateWith(acc, [cur.route, 'detail'], function(n) {
            if (_.isNil(n)) {
                return [cur];
            } else {
                n.push(cur);
            }

            return n;
        });

        return acc;
    }, {});

    let result = _.map(stats, (v,k) => {
        let result = {
            route: '',
            count: 0,
            mean: 0,
            DOMContentLoaded: 0,
            load: 0,
            firstscreen: 0,
            detail: []

        };

        result.route = k;
        result.count = _.sum(jp.query(v, '$..count'));
        result.DOMContentLoaded = +(_.get(v, 'DOMContentLoaded.duration', 0) / _.get(v, 'DOMContentLoaded.count', 1)).toFixed(0);
        result.load = +(_.get(v, 'load.duration', 0) / _.get(v, 'load.count', 1)).toFixed(0);
        result.firstscreen = +(_.get(v, 'firstscreen.duration', 0) / _.get(v, 'firstscreen.count', 1)).toFixed(0);
        result.mean = result.load + result.DOMContentLoaded + result.firstscreen;
        result.detail = v.detail;

        return result;
    });

    return result;
}

function handleApi(totalRows, apiRows) {
    let totalObj = totalRows.map(handleZip2);
    let apiObj = apiRows.map(handleZip2);

    totalObj.forEach(i => {
        i.detail = jp.query(apiObj, `$..[?(@.route=="${i.route}")]`);
        i.length = i.detail.length;
    });

    return totalObj;
}

const profile_service = {
    async client_time(server, start, end, app, lastTime) {
        const model = profile_sql.client()
            .select('*')
            .where(APP[app]);

        if (lastTime) {
            model.where('time', '>=', SqlBuilder.raw(`now() - ${lastTime}`))
        }

        if (start && end) {
            model.where('time', '>=', start)
                .where('time', '<', end);
        }

        let rows = await exec(SERVER[server], model.toSql())
            .then(result => _.get(result, 'results[0].series[0]', []))
            .then(handleZip);

        rows = handleRows(rows);

        return {code: 200, data: rows}
    },
    async server_time(ctx, app, start, length) {
        const slowRouterModel = new SlowRouteModel(ctx);
        const result = await slowRouterModel.getList(app, start, length);

        return result;
    },
    async error(ctx, app, start, length) {
        const errorModel = new ErrorModel(ctx);
        const result = await errorModel.getList(app, start, length);

        return {cde: 200, data: result};
    }
};

const profile_controller = {
    async server_mean_report_index(ctx) {
        await ctx.render('action/profile_server');
    },
    async server_mean_report_json(ctx) {
        const app = parseInt(ctx.query.app) || 0;
        const start = parseInt(ctx.query.start) || 0;
        const length = parseInt(ctx.query.length) || 10;
        const draw = ctx.query.draw;

        const result = await profile_service.server_time(ctx, app, start, length);
        ctx.body = Object.assign({}, result, {draw});
    },
    async error_report_index(ctx) {
        await ctx.render('action/profile_error');
    },
    async error_report_json(ctx) {
        const app = parseInt(ctx.query.app) || 0;
        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);
        ctx.body = Object.assign({}, result, {draw});
    }

};

r.get('/server', profile_controller.server_mean_report_index);
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);

module.exports = r;