preface

I still remember in my last company, one of the big guys built a surveillance system and wanted to study how he did it. Of course, we are not the people who pat their heads on the head and do what others do. Here’s a look at the benefits of such a platform.

background

Front end system first of all, why should we do for you, look at this table below, can clearly see that the front end performance increase the value of the product, or pretty helpful, but if we can real-time collected these information, and to monitor implementation, make the whole product in the product line has been to maintain efficient operation, this is our purpose.

performance earnings
Google’s delay400ms Searches are down0.59%
Bing delay2s Drop in revenue4.3%
Yahoo delay400ms Flow down5-9%
Mozilla pages open less2.2 s Download increase15.4%
Netflix open Gzip Improved performance by 13.25% Reduced bandwidth50%

Secondly, it is also good for the products we release, so that we can find our mistakes in time. If a product is in a new iteration, an unspeakable error occurs.

Right! It’s just indescribable. We can’t wait for users to complain. By then, the day lilies are cold.

start

Based on the above, we began to build a front-end monitoring and simple control platform. (Although there are many such systems on the market, such as ELK, I can’t help myself.)

It has to be easy.

Guys, forgive me, this is as far as I can get you.

Here we go.

These are some of the things we need to do.

To collect information

To make a surveillance system, first we have to have an object. The object of our surveillance! Object! Object! Object.

I wrote a page in my system that looked like this,

<body>
    <div>2</div>
    <div>2</div>
    <div>2</div>
    <div>2</div>
    <div>2</div>
    <div>2</div> 
</body>
Copy the code

Yes, this is the page we’re monitoring. This… I’m not lazy.

And then I designed three pieces of data

  • Page load time
  • Collect statistics about devices used by users
  • Error statistics

Page load time

window.logInfo = {};  // Count page load time
window.logInfo.openTime = performance.timing.navigationStart;
window.logInfo.whiteScreenTime = +new Date() - window.logInfo.openTime;
document.addEventListener('DOMContentLoaded'.function (event) {
  window.logInfo.readyTime = +new Date() - window.logInfo.openTime;
});
window.onload = function () {
  window.logInfo.allloadTime = +new Date() - window.logInfo.openTime;
  window.logInfo.nowTime = new Date().getTime();
  var timname = {
    whiteScreenTime: 'White screen time'.readyTime: 'User available time'.allloadTime: 'Total Download time'.mobile: 'Use equipment'.nowTime: 'time'};var logStr = ' ';
  for (var i in timname) {
    console.warn(timname[i] + ':' + window.logInfo[i] + 'ms');
    if (i === 'mobile') {
      logStr += '&' + i + '=' + window.logInfo[i];
    } else {
      logStr += '&' + i + '=' + window.logInfo[i]; }} (new Image()).src = '/action? ' + logStr;
};
Copy the code

Collect statistics about devices used by users

window.logInfo.mobile = mobileType();
function mobileType() {
  var u = navigator.userAgent, app = navigator.appVersion;
  var type =  {// Mobile terminal browser version information
    ios:!!!!! u.match(/\(i[^;] +; ( U;) ? CPU.+Mac OS X/), / / ios terminal
    iPad: u.indexOf('iPad') > -1./ / whether the device
    android: u.indexOf('Android') > -1 || u.indexOf('Linux') > -1.// An Android terminal or uc browser
    iPhone: u.indexOf('iPhone') > -1 || u.indexOf('Mac') > -1.// Whether the browser is iPhone or QQHD
    trident: u.indexOf('Trident') > -1./ / IE kernel
    presto: u.indexOf('Presto') > -1./ / opera kernel
    webKit: u.indexOf('AppleWebKit') > -1.// Apple, Google kernel
    gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') = = -1.// The Firefox kernel
    mobile:!!!!! u.match(/AppleWebKit.*Mobile/i) | |!!!!! u.match(/MIDP|SymbianOS|NOKIA|SAMSUNG|LG|NEC|TCL|Alcatel|BIRD|DBTEL|Dopod|PHILIPS|HAIER|LENOVO|MOT-|Nokia|SonyEricsson|SIE-|Amoi |ZTE/), // Whether it is a mobile terminal
    webApp: u.indexOf('Safari') = = -1 // Whether the web should be a program, without a header and a bottom
  };
  var lists = Object.keys(type);
  for(var i = 0; i < lists.length; i++) {
    if(type[lists[i]]) {
      returnlists[i]; }}}Copy the code

Error statistics

window.onload = function () {
        window.logInfo.allloadTime = +new Date() - window.logInfo.openTime;
        window.logInfo.nowTime = new Date().getTime();
        var timname = {
            whiteScreenTime: 'White screen time'.readyTime: 'User available time'.allloadTime: 'Total Download time'.mobile: 'Use equipment'.nowTime: 'time'};var logStr = ' ';
        for (var i in timname) {
            console.warn(timname[i] + ':' + window.logInfo[i] + 'ms');
            if (i === 'mobile') {
                logStr += '&' + i + '=' + window.logInfo[i];
            } else {
                logStr += '&' + i + '=' + window.logInfo[i]; }} (new Image()).src = '/action? ' + logStr;
    };
      
    var defaults = {
        msg:' '.// Error specific information
        url:' '.// The url where the error occurred
        line:' '.// The error line
        col:' '.// The column where the error occurred
        nowTime: ' './ / time
    };
    window.onerror = function(msg,url,line,col,error) {
        col = col || (window.event && window.event.errorCharacter) || 0;

        defaults.url = url;
        defaults.line = line;
        defaults.col =  col;
        defaults.nowTime = new Date().getTime();

        if (error && error.stack){
            // If the browser has stack information, use it directly
            defaults.msg = error.stack.toString();

        }else if (arguments.callee){
            // Try to fetch stack information through callee
            var ext = [];
            var fn = arguments.callee.caller;
            var floor = 3;  
            while (fn && (--floor>0)) {
                ext.push(fn.toString());
                if (fn  === fn.caller) {
                    break;
                }
                fn = fn.caller;
            }
            ext = ext.join(",");
            defaults.msg = error.stack.toString();
        }
        var str = ' '
        for(var i in defaults) {
            // console.log(i,defaults[i]);
            if(defaults[i] === null || defaults[i] === undefined) {
                defaults[i] = 'null'; 
            }
            str += '&'+ i + '=' + defaults[i].toString();
        }
        srt = str.replace('&'.' ').replace('\n'.' ').replace(/\s/g.' ');
        (new Image()).src = '/error? ' + srt;
    }
Copy the code

That’s all you need to do to collect data, either by sending /action requests or by sending /error requests, which can be customized, but I’m just talking about how the process works.

Then I processed and recorded all the requests through one of my background express.js, and the data after recording is like this.

User_ip = 127.0.0.1 & whiteScreenTime = 185 & readyTime = 192 & allloadTime mobile = webKit&nowTime = = 208 & 1513071388941Copy the code

The data processing

Here I wrote a script to parse, parse.js, here is not specific, look at the source code. Let me show you the parsed data.

I store the data in CVS format, and I also support json format export for the following diagrams, but you need to configure the visual interface later.

Here’s the data.

charts/csvData/2017-12-16time.csv

Time, bad time, the user can operation time, total download time, 1513427051482137137153, 1513427065080470471507, 1513427080040127127143 1513428714345274275323 1513428733583267268317 1513428743167268268317 1513428754796276276328Copy the code

The data show

I’m using highcharts.js here

I will not explain the specific configuration, you can go to the official website to check.

Below is a visual chart showing the information for each time of day.

The interface may not be particularly beautiful, please forgive me.

The environment

The node > = 6.0.0

Redis > = server

Here I would like to explain that if this online environment is deployed, if every record is recorded, it will consume a lot of memory, so I set up a layer of Redis, in order to prevent the impact of heavy traffic, and then can be stored at intervals.

const express = require('express');
const performance = require('./lib/performance.js');
const app = express();
const router = express.Router();
router.get('/'.function (req, res, next) {
  req.url = './index.html';
  next();
});
app.use(router);
app.use(performance({
    time: 10.// The unit is seconds
    originalDir: './originalData'.// Directory of data
    errorDir: './errorData' // The directory where the error was reported
}))
app.use(express.static('/'));
const server = app.listen(3000)
Copy the code

You can set the default time here, but I’m going to do it in 10 seconds for demo purposes. I usually do it once a minute.

Code address: github.com/hua1995116/…

If you have good suggestions and optimization plan, please also put forward to me in Issues.

Advanced (an actual combat chestnut using monitoring platform)

I used this platform to monitor one of my projects. If you just play, please just read the original system address above, you can ignore my paragraph, after all, my system is not perfect.

Online demo: www.qiufengh.com/#/

Monitor demo:qiufengh.com :8080/

Project: github.com/hua1995116/…

Here I have set logging every 1 minute.

// Monitor import
app.use(performance({
    time: 60.// The unit is seconds
    originalDir: './originalData'.// Directory of data
    errorDir: './errorData' // The directory where the error was reported
}))
Copy the code

And parsing every 10 minutes.

function setPrase() {
    setInterval(function(){
        parseData();
      }, 1000 * 60 * 10);
}
Copy the code


2017-12-20

This article only provides an idea, if you want professional, you can use professional statistical analysis tools such as ELK.