profile.js 6.64 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 endpoint = (server) => `http://${server.host}:${server.port}`;

const r = new Router;

const TABLE = {
    DURATION: '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 TYPE = {
    DOMContentLoaded: 0,
    load: 0,
    firstscreen: 0
};

const profile_sql = {
    duration() {
        return SqlBuilder.of(TABLE.DURATION);
    },
    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 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 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;
}

const profile_service = {
    async mean(server, start, end, app, lastTime) {
        const model = profile_sql.duration()
            .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 time(server, start, end, app, lastTime) {
        const model = profile_sql.duration()
            .select('*')
            .where(APP[app]);

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

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

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

        return {cde: 200, data: rows};
    },
    async error(server, start, end, app, lastTime) {
        const model = profile_sql.error()
            .select('*')
            .where(APP[app]);

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

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

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

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

const profile_controller = {
    async mean_report_index(ctx) {
        await ctx.render('action/profile_mean');
    },
    async mean_report_json(ctx) {
        const start = ctx.query.start;
        const end = ctx.query.end;
        const app = ctx.query.app;
        const server = ctx.query.server || 'aws';
        const lastTime = ctx.query.lastTime;

        const result = await profile_service.mean(server, start, end, app, lastTime);
        ctx.body = result;
    },
    async time_report_index(ctx) {
        await ctx.render('action/profile_time');
    },
    async time_report_json(ctx) {
        const start = ctx.query.start;
        const end = ctx.query.end;
        const app = ctx.query.app;
        const server = ctx.query.server || 'aws';
        const lastTime = ctx.query.lastTime;

        const result = await profile_service.time(server, start, end, app, lastTime);
        ctx.body = result;
    },
    async error_report_index(ctx) {
        await ctx.render('action/profile_error');
    },
    async error_report_json(ctx) {
        const start = ctx.query.start;
        const end = ctx.query.end;
        const app = ctx.query.app;
        const server = ctx.query.server || 'aws';
        const lastTime = ctx.query.lastTime;

        const result = await profile_service.error(server, start, end, app, lastTime);
        ctx.body = result;
    }

};

r.get('/mean', profile_controller.mean_report_index);
r.get('/mean.json', profile_controller.mean_report_json);

r.get('/time', profile_controller.time_report_index);
r.get('/time.json', profile_controller.time_report_json);

r.get('/error', profile_controller.error_report_index);
r.get('/error.json', profile_controller.error_report_json);

module.exports = r;