前端监控的服务端实现

创建
阅读 241

接上一篇 前端监控,这篇是后端的实现

统计数据的 schema

const mongoose = require('mongoose');

const statSchema = mongoose.Schema({
  /* 访问的页面信息 */
  // 域名
  domain: String,
  // 当前 URL 地址
  url: String,
  // 当前页面标题
  title: String,
  // 上一个访问页面 URL 地址
  referrer: String,

  /* 页面性能信息 */
  // DNS解析时间
  dnsTime: Number,
  // TCP建立时间
  tcpTime: Number,
  // 首屏时间
  firstPaintTime: Number,
  // DOM渲染完成时间
  domRenderTime: Number,
  // 页面onload时间
  loadTime: Number,

  /* 客户端设备信息 */
  // 屏幕高度
  sh: Number,
  // 屏幕宽度
  sw: Number,
  // 屏幕颜色深度
  scd: Number,
  // 客户端语言
  lang: String,
  // 客户端标识
  userAgent: String,
  // 客户端ip地址
  clientIp: String,
  createdAt: {
    type: Date,
    default: Date.now,
  },
});

数据处理

接收前端传递过来的统计数据

const Stat = require('../../models/stat');

// @desc      通过 GET 接收前端传递过来的统计数据
// @route     GET /v1/stat?args=
// @access    Public
exports.getStat = async (req, res, next) => {
  try {
 
    const obj = {};

    // 获取请求中查询参数
    const params = decodeURIComponent(req.query.args).split('&');

    // 将a=1&b=2 转换成 {a:1,b:2}
    params.forEach((item) => {
      const a = item.split('=');
      // obj[a[0]] = a[1];
      [, obj[a[0]]] = a;
    });

    // 获取客户端IP地址
    obj.clientIp = req.ip;
    obj.userAgent = req.headers['user-agent'];

    await Stat.create(obj);
    res.send('ok');
  } catch (error) {
    next(error);
  }
};

查询统计数据

// @desc      查询统计数据
// @route     GET /v1/statistics
// @access    Public
exports.getStatistics = async (req, res, next) => {
  // 将日期转换为时区为北京时间的日期
  function dateFormat(date) {
    if (date) {
      return new Date(date).toLocaleDateString('zh-CN', {
        timeZone: 'Asia/Shanghai',
      });
    }
    return new Date().toLocaleDateString('zh-CN', {
      timeZone: 'Asia/Shanghai',
    });
  }
  try {
    const params = {};

    // 按域名筛选
    if (req.query.domain) {
      params.domain = req.query.domain;
    }

    const limit = req.query.limit || 10;

    // 日期筛选
    // 如果只有一个日期参数,就只筛选出该日期的当天统计数据
    // 如果没有日期参数返回今天的统计数据
    let dateStart;
    let dateEnd;

    if (req.query.dateStart && req.query.dateEnd) {
      dateStart = dateFormat(req.query.dateStart);
      dateEnd = dateFormat(req.query.dateEnd);
    } else if (req.query.dateStart && !req.query.dateEnd) {
      dateStart = dateFormat(req.query.dateStart);
      dateEnd = dateFormat(req.query.dateStart);
    } else if (!req.query.dateStart && req.query.dateEnd) {
      dateStart = dateFormat(req.query.dateEnd);
      dateEnd = dateFormat(req.query.dateEnd);
    } else {
      dateStart = dateFormat();
      dateEnd = dateFormat();
    }

    params.createdAt = {
      $gte: new Date(`${dateStart} 00:00:00`),
      $lte: new Date(`${dateEnd} 23:59:59`),
    };

    // 浏览量统计
    const pv = await Stat.countDocuments(params);

    // ip 数统计
    const ip = (
      await Stat.aggregate([
        {
          $match: params,
        },
        {
          $group: { _id: '$clientIp', clientIp: { $sum: 1 } },
        },
      ])
    ).length;

    // 当天热门浏览的文章
    const hotUrl = await Stat.aggregate([
      {
        $match: params,
      },
      {
        $group: { _id: '$url', views: { $sum: 1 } },
      },
    ]).sort({ views: 'desc' }).limit(limit);

    // 获取 DNS 响应时间的平均值(排除为0的值)
    const dnsTime = await Stat.aggregate([
      {
        $match: params,
      },
      {
        $match: { dnsTime: { $ne: 0 } },
      },
      {
        $group: {
          _id: 'dnsTime',
          dnsTime: { $avg: '$dnsTime' },
        },
      },
    ]);

    // 获取 TCP 响应时间的平均值 (排除为0的值)
    const tcpTime = await Stat.aggregate([
      {
        $match: params,
      },
      {
        $match: { tcpTime: { $ne: 0 } },
      },
      {
        $group: {
          _id: 'tcpTime',
          tcpTime: { $avg: '$tcpTime' },
        },
      },
    ]);

    // 其他性能数据的平均值 firstPaintTime, domRenderTime, loadTime
    const performanceTime = await Stat.aggregate([
      {
        $match: params,
      },
      {
        $group: {
          _id: '性能信息',
          firstPaintTime: { $avg: '$firstPaintTime' },
          domRenderTime: { $avg: '$domRenderTime' },
          loadTime: { $avg: '$loadTime' },
        },
      },
      {
        $project: {
          _id: 0,
        },
      },
    ]);

    res.status(200).json({
      success: true,
      message: '获取统计数据',
      dateStart,
      dateEnd,
      ip,
      pv,
      performance: {
        dnsTime: dnsTime.length !== 0 ? dnsTime[0].dnsTime : null,
        tcpTime: tcpTime.length !== 0 ? tcpTime[0].tcpTime : null,
        ...performanceTime[0],
      },

      hotUrl,
    });
  } catch (error) {
    next(error);
  }
};

查看2021年01月31日的数据

本文链接 https://www.yidiankuaile.com/post/frontend-monitor-server

最后更新
留言
0 条留言