apm.js 4.72 KB
const queryParse = require('../lib/query-parse');
const sourceMapParse = require('../lib/sourcemap-parse');
const Sender = require('influx-batch-sender');
const MysqlSender = require('../lib/mysql-sender');
const _ = require('lodash');
const logger = global.yoho.logger;
const config = global.yoho.config;
const msg2row = require('./msg2row');
const routeDecode = require('./route-decode');

const durationType = {
    dcl: 'domcontentload',
    ld: 'domreadyload',
    fs: 'firstscreen'
};

const routeInfluxSender = new Sender(config.reportRoute);

const slowRouterSender = new MysqlSender(config.table.slow);
const errorRouterSender = new MysqlSender(config.table.error);

module.exports = (req, res, next) => {
    if (!req.query.l) {
        logger.error('query l is undefined');
        return res.send();
    }
    const app = req.query.s || 'unknown';

    try {
        const report = queryParse.parse(req.query.l);

        if (!report) {
            logger.error('report can not be parse:', req.query.l);
            return res.send();
        }
        report.forEach(item => {
            if (item.pt && item.pt.indexOf('openby:yohobuy') >= 0) {
                item.pt = item.pt.substring(0, item.pt.indexOf('openby:yohobuy'));
            }

            if (item.r) {
                item.r = routeDecode(item.r) || 'null';
            }

            if (item.tp === 'err') {
                let data = {
                    tags: {
                        app,
                        reqID: item.rid,
                        uid: item.u,
                        udid: item.ud,
                        route: item.r
                    },
                    time: new Date().getTime() * 1000000
                };

                sourceMapParse.parse({
                    fileUrl: item.sc,
                    line: parseInt(item.ln || 0, 10),
                    column: parseInt(item.cn || 0, 10)
                }).then(originError => {
                    let fields = {};

                    if (!originError) {
                        // logger.error('sourcemap can not be parse', item);
                        fields = {
                            script: item.sc,
                            line: _.parseInt(item.ln || 0),
                            column: _.parseInt(item.cn || 0),
                        };
                    } else {
                        fields = {
                            script: originError.source,
                            line: _.parseInt(originError.line || 0),
                            column: _.parseInt(originError.column || 0),
                        };
                    }
                    _.merge(data, {
                        measurement: 'error-report',
                        tags: {
                            type: 'client'
                        },
                        fields: Object.assign({
                            message: item.msg,
                            useragent: req.get('user-agent'),
                            stack: item.st ? item.st.replace(/"/g, '') : ''
                        }, fields)
                    });

                    errorRouterSender.addMessage(msg2row.errorRouter(data));
                });
            } else {
                let data = {
                    tags: {
                        app,
                        route: item.r
                    },
                    fields: {
                        reqID: item.rid,
                        uid: item.u,
                        udid: item.ud
                    },
                    time: new Date().getTime() * 1000000
                };

                const duration = _.parseInt(item.t);

                if (duration < 1000 * 60 * 10) {
                    _.merge(data, {
                        measurement: 'route-info',
                        tags: {
                            type: durationType[item.tp],
                        },
                        fields: {
                            duration: duration,
                            useragent: req.get('user-agent')
                        }
                    });

                    routeInfluxSender.addMessage({
                        tags: {
                            app: app,
                            host: item.hostname,
                            route: item.r
                        },
                        fields: {
                            duration: duration
                        }
                    });

                    if (duration > config.slowRoute.min && duration < config.slowRoute.max) {
                        slowRouterSender.addMessage(msg2row.slowRouter(data));
                    }
                }
            }
        });

        res.json();
    } catch (e) {
        return next(e);
    }
};