Создайте систему внешнего мониторинга и больше не пропускайте ошибки

Node.js Redis

предисловие

Я до сих пор помню, как в моей прошлой компании некий начальник построил систему мониторинга, которая была потрясающей, и мне очень хотелось изучить, как он ее придумал. Конечно, мы не из тех людей, которые гладят себя по голове и работают, и мы не можем делать то, что делают другие. Давайте сначала представим преимущества такой платформы.

задний план

Во-первых, зачем нам делать front-end систему?Посмотрите на таблицу ниже.Очевидно, что производительность front-end сильно влияет на стоимость продукта, но если мы сможем собрать эту информацию в режиме реального времени и внедрить его Нашей целью является мониторинг и поддержание эффективной работы всего продукта в линейке продуктов.

представление доход
задержка Google400ms Количество запросов падает0.59%
Задержка Bing2s снижение доходов4.3%
задержка Yahoo400ms трафик падает5-9%
Страница Mozilla открывается меньше2.2s Количество загрузок увеличивается15.4%
Netflix включает Gzip Повышение производительности на 13,25 % при снижении пропускной способности.50%

Во-вторых, это также выгодно для продуктов, которые мы освобождаем, и мы можем найти наши ошибки вовремя. Если продукт находится в новой итерации, возникает неописуемая ошибка.

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2017/12/17/16062ce3510887ef~tplv-t2oaga2asx-image.image

правильно! Просто неописуемо. Ждать отзывов и жалоб от пользователей у нас невозможно, а лилейник к тому времени уже остынет.

Начинать

Исходя из вышеизложенного начинаем строить фронтальный мониторпростоплатформа управления. (Хотя сейчас на рынке много таких систем, например ELK, я все равно не могу с собой поделать.)

Можно только простой.

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2017/12/17/16062ce34e8a7e9f~tplv-t2oaga2asx-image.image

Братья, простите меня, я могу только помочь вам здесь.

Пожалуйста, смотрите дальше.

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2017/12/17/16062ce34f90726b~tplv-t2oaga2asx-image.image

Выше приведены некоторые из вещей, которые нам нужно сделать.

Собирать информацию

Чтобы сделать систему мониторинга, сначала у нас должен быть объект. Объекты, за которыми мы следим! объект! объект! объект.

Я написал такую ​​страницу в своей системе,

<body>
    <div>2</div>
    <div>2</div>
    <div>2</div>
    <div>2</div>
    <div>2</div>
    <div>2</div> 
</body>

Правильно, это страница, которую мы хотим отслеживать. Это... дело не в том, что я ленивый.

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2017/12/17/16062ce34a300fd3~tplv-t2oaga2asx-image.image

Затем я разработал в общей сложности 3 части данных

  • время загрузки страницы
  • Статистика по пользовательскому оборудованию
  • Статистика ошибок

время загрузки страницы

window.logInfo = {};  //统计页面加载时间
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: '白屏时间',
    readyTime: '用户可操作时间',
    allloadTime: '总下载时间',
    mobile: '使用设备',
    nowTime: '时间',
  };
  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;
};

Статистика по пользовательскому оборудованию

window.logInfo.mobile = mobileType();
function mobileType() {
  var u = navigator.userAgent, app = navigator.appVersion;
  var type =  {// 移动终端浏览器版本信息
    ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端
    iPad: u.indexOf('iPad') > -1, //是否iPad
    android: u.indexOf('Android') > -1 || u.indexOf('Linux') > -1, //android终端或者uc浏览器
    iPhone: u.indexOf('iPhone') > -1 || u.indexOf('Mac') > -1, //是否为iPhone或者QQHD浏览器
    trident: u.indexOf('Trident') > -1, //IE内核
    presto: u.indexOf('Presto') > -1, //opera内核
    webKit: u.indexOf('AppleWebKit') > -1, //苹果、谷歌内核
    gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1, //火狐内核
    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/), //是否为移动终端
    webApp: u.indexOf('Safari') == -1 //是否web应该程序,没有头部与底部
  };
  var lists = Object.keys(type);
  for(var i = 0; i < lists.length; i++) {
    if(type[lists[i]]) {
      return lists[i];
    }
  }  
}

Статистика ошибок

window.onload = function () {
        window.logInfo.allloadTime = +new Date() - window.logInfo.openTime;
        window.logInfo.nowTime = new Date().getTime();
        var timname = {
            whiteScreenTime: '白屏时间',
            readyTime: '用户可操作时间',
            allloadTime: '总下载时间',
            mobile: '使用设备',
            nowTime: '时间',
        };
        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:'',  // 错误的具体信息
        url:'',  // 错误所在的url
        line:'', // 错误所在的行
        col:'',  // 错误所在的列
        nowTime: '',// 时间
    };
    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){
            // 如果浏览器有堆栈信息,直接使用
            defaults.msg = error.stack.toString();

        }else if (arguments.callee){
            // 尝试通过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;
    }

Все вышесказанное касается сбора данных, отправив запрос /action или запрос /error, их можно настроить, я говорю только о том, как реализован весь процесс.

Затем все запросы обрабатываются и записываются через один из моих бэкендов express.js.Записываемые данные выглядят так.

user_ip=127.0.0.1&whiteScreenTime=185&readyTime=192&allloadTime=208&mobile=webKit&nowTime=1513071388941

обработка данных

Здесь я анализирую написанный мной скрипт parse.js, который здесь не объясняется, просто взгляните на исходный код. Показываю обработанные данные.

Я храню его в формате данных cvs.Из-за необходимости диаграммы позже я также поддерживаю экспорт в формате json, но вам нужно будет настроить визуальный интерфейс позже самостоятельно.

Данные такие.

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

时间,白屏时间,用户可操作时间,总下载时间
1513427051482,137,137,153
1513427065080,470,471,507
1513427080040,127,127,143
1513428714345,274,275,323
1513428733583,267,268,317
1513428743167,268,268,317
1513428754796,276,276,328

Отображение данных

Здесь я используюhighcharts.js

Я не буду объяснять конкретную конфигурацию, вы можете сами проверить ее на официальном сайте.

Ниже представлена ​​наглядная диаграмма, показывающая информацию для каждого временного периода дня.

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2017/12/17/16062ce35649b7e7~tplv-t2oaga2asx-image.image

Интерфейс может быть не особо красивым, прошу меня простить.

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2017/12/17/16062ce3539eb419~tplv-t2oaga2asx-image.image

окружающая обстановка

node >= 6.0.0

redis >= 2.6.0

Я объясню здесь, потому что, если это развернуто в онлайн-среде, если каждая запись будет записана, это будет потреблять много памяти, поэтому я настроил слой Redis, чтобы предотвратить влияние большого трафика, а затем каждое хранилище за определенный период времени.

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, // 秒为单位
    originalDir: './originalData', // 数据的目录
    errorDir: './errorData' // 报错的目录
}))
app.use(express.static('./'));
const server = app.listen(3000)

Здесь можно установить время по умолчанию, я использую 10 секунд в качестве единицы измерения ради демонстрационного эффекта. Обычно я использую одно хранилище в минуту.

Кодовый адрес:GitHub.com/flower1995116/…

Если у вас есть хорошие предложения и оптимизированные решения, пожалуйста, дайте им мне по вопросам.

Расширенный (практичный каштан с использованием платформы мониторинга)

Я использовал эту платформу для мониторинга одного из моих проектов. Если вы просто играете, пожалуйста, прочитайте только исходный системный адрес выше, вы можете игнорировать мой абзац, в конце концов, моя система не идеальна.

Онлайн-демонстрация:www.qiufengh.com/#/

Демонстрация монитора:qiufengh.com:8080/

проект:GitHub.com/flower1995116/…

Здесь я устанавливаю запись журнала каждую 1 минуту.

// 监控引入
app.use(performance({
    time: 60, // 秒为单位
    originalDir: './originalData', // 数据的目录
    errorDir: './errorData' // 报错的目录
}))

и разбор каждые 10 минут.

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

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2017/12/17/16062ce389311f6b~tplv-t2oaga2asx-image.image


2017-12-20

Эта статья просто дает идею, если вы хотите быть профессионалом, вы можете использоватьELKпрофессиональные инструменты статистического анализа.