apm.js 3.78 KB
const queryParse = require('../lib/query-parse');
const sourceMapParse = require('../lib/sourcemap-parse');
const Sender = require('influx-batch-sender');
const _ = require('lodash');

const logger = global.yoho.logger;
const config = global.yoho.config;

const durationType = {
    dcl: 'DOMContentLoaded',
    ld: 'load',
    fs: 'firstscreen'
};

const influxSender = new Sender(config.reportApi);

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')) {
                item.pt = item.pt.substring(0, item.pt.indexOf('openby:yohobuy'));
            }
            if (item.tp === 'err') {
                let data = {
                    tags: {
                        app,
                        reqID: item.rid,
                        uid: item.u,
                        udid: item.ud,
                        route: item.pt || 'null'
                    }
                };

                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),
                        };
                    }
                    influxSender.addMessage(_.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)
                    }));
                });
            } else {
                let data = {
                    tags: {
                        app
                    },
                    fields: {
                        reqID: item.rid,
                        uid: item.u,
                        udid: item.ud,
                        route: item.pt
                    }
                };

                if (durationType[item.tp]) {
                    const duration = _.parseInt(item.t);

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

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