Динамическое отображение логов в броузере на Node.js & WebSocket

в 8:33, , рубрики: node.js, метки:
Динамическое отображение логов в броузере на Node.js & WebSocket

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

Инструменты и модули для решения

Для серверной части был выбран Node.js. Но похожий пример можно аналогично реализовать и с использованием EM на руби. В данном случае подкупила библиотека Socket.IO, а также несколько простых модулей для работы с файлами.

Utilities for watching file trees in node.js — смысл которого отслеживать появление, удаления, а также изменения в файлах внутри указаной папки.

Пример отслеживания появления/удаления файлов внутри common.NODES_PATH и отсылка сообщения об этих событиях.

watch.createMonitor(common.NODES_PATH, function (monitor) {
  monitor.on("created", function (f, stat) {
    var file_name = common.removeRoot(f); 
    console.log(file_name + ' created');
    var idx = log_files.indexOf(file_name);
    if (idx < 0) {
      log_files.push(file_name);
      io.sockets.emit('log_files_add', [file_name]);
      watchFile(file_name);
    }
  })
  monitor.on("removed", function (f, stat) {
    var file_name = common.removeRoot(f); 
    console.log(file_name + ' removed');
    var idx = log_files.indexOf(file_name);
    if (idx >= 0) {
      log_files.splice(idx, 1);
      io.sockets.emit('log_files_remove', [file_name]);
      unwatchFile(file_name);
    }
  })
})

A nodejs directory walker — пакет который позволяет пройтись по внутренностям указаной папки и получать события на нахождения того или иного типа файла. Или если упрощенно то просто рекурсивно пройтись по всем вложеным папкам и посомтреть что там лежит. (пример чуть ниже)

Jade — Template engine — чтобы быстро набросать упрощенный UI и проверить задумку.

Пример реализации

  • Создаем веб сервер на заданном порту;
  • При первом соединении с сервером проходим с помощию walker’a по папкам и посылаем первоначальную порцию информации о существующих файлах (плюс фильтр по расширению) и последние 20 строк.
    io.sockets.on('connection', function(socket) {
      var walk = require('walk');
    
      // receive list of log files
      var walker  = walk.walk(common.NODES_PATH, { followLinks: false });
      walker.on('file', function(root, stat, next) {
        if(/log$/.test(stat.name)) {
          var file_name = common.removeRoot(root + '/' + stat.name);
          var idx = log_files.indexOf(file_name);
          if (idx < 0) {
            log_files.push(file_name);
    
            // send initial data portion
            var tail = spawn('tail', ['-n', 20].concat(common.NODES_PATH + '/' + file_name));
            tail.stdout.on('data', function(data) {
              console.log('emit starting ' + file_name + ':lines' + ' with ' + data.toString('utf-8'));
              io.sockets.emit(file_name + ':lines', data.toString('utf-8').split('n'));
            });
    
            watchFile(file_name);
          }
        }
          
        next();
      });
    
      walker.on('end', function() {
        socket.emit('log_files_add', log_files);
      });    
    })
    
  • С помощью watch создаем монитор который будет следить за появлением новых файлов или удалением. (При удалении файла блок отображающий заданный лог соответветсвенно удаляется)
    function watchFile(file) {
      console.log('start watching: ' + file);
      var tail = spawn('tail', ['-f'].concat(common.NODES_PATH + '/' + file));
      watching_processes[file] = tail;
      tail.stdout.on('data', function(data) {
        console.log('emit ' + file + ':lines' + ' with ' + data.toString('utf-8'));
        io.sockets.emit(file + ':lines', data.toString('utf-8').split('n'));
      })
    }
    
    function unwatchFile(file) {
       console.log('end watching: ' + file);
       var process = watching_processes[file];
       process.kill();
       watching_processes[file] = null;
    }
    
Запуск

В common.js настройка за какой папкой следить
var NODES_PATH = pathResolver.resolve(__dirname + '/../nodes');

При появлении *.log внутри папок и подпапок они будут отображаться в окне броузера

npm install для установки пакетов

[~/Projects/autotest_node] git:master $ mkdir nodes
[~/Projects/autotest_node] git:master $ mkdir nodes/test1
[~/Projects/autotest_node] git:master $ mkdir nodes/test2
[~/Projects/autotest_node] git:master $ echo "test_message1" >> nodes/test1/first.log
[~/Projects/autotest_node] git:master $ echo "test_message1" >> nodes/test2/first.log
[~/Projects/autotest_node] git:master $ echo "test_message2" >> nodes/test2/first.log
[~/Projects/autotest_node] git:master $ echo "test_message2" >> nodes/test1/first.log

npm start для запуска сервера

image

Исходный код

https://github.com/catz/autotest_node

Заключение

Данный пример может быть полезен, к примеру, если вы хотите расшарить результаты выполнения автоматических тесткейсов и дать возможность следить за результатом их выполнения другим лицам. Хотя для таких задач и есть более продвинутые средства, но если вы хотите сделать простой tail -f в броузере, то подобный пример вполне может подойти.

Автор: roman_truschev

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js