Pull to refresh

Как бороться с console.log?

Привет читатель.
В процессе разработки и дебаггинга, по крайней мере у меня раньше, код зачастую обрастает всякого рода условными console.log вида:

if(config.debug)
  console.log(method, params);
...
if(config.profiler)
  console.log(method, (new Date).getTime()-start_time);
и тп.

Их постоянно нужно включать/отключать, перегрупировывать опции, в итоге появляются всякие dbDebug, validateDebug, authDebug, и далее, насколько хватит фантазии. Рано или поздно появляется желание выводить все это не в консоль, а скажем в различные файлы, или к примеру какой-нибудь admin tool на socket.io. Собирать и менять все это во всем проекте задача нетривиальная.
Под катом я расскажу какое решение применил я на текущем рабочем проекте.

Итак, что нам нужно? Некий централизованный обработчик отладочных данных. Решение, предлагаемое мной, пришло ко мне, когда я писал soket.io admin tool для упрощения административных операций (чтение логов, очистка базы(нетривиальная задача для dynamoDB) в планах запилить еще многое).
Так вот, было 2 возможности:
— клиент обращается к серверу, а тот выдает ему порцию «свежих» данных
— сервер сам рассылает всю информацию всем своим подключенным клиентам

Первый вариант я сразу отбросил, так как понятие «свежести» данных меня отпугнуло: сервер должен знать для какого клиента, какие данные являются свежими, да и вообще, это означает необходимость хранения данных, а в моей задаче это не требуется по умолчанию.

А вот второй вариант привлек меня своей простотой и удобством.
Здесь мне просто-напросто нужно было вспомнить о том, что есть в природе такой паттерн проектирования, как Observer.

Итак приступим:

Создаем простенький объект, например profiler…
  var profiler = {}


Нам нужно где-то хранить обработчики данных, создаем массив:
  profiler.clients = [];


Также нам нужна возможность подписываться и отписываться от рассылаемых данных:
  profiler.subscribe = function(id, next){
    this.clients[id] = next;
  }
  profiler.unsubscribe = function(id){
    delete this.clients[id]
  }


Все, мы имеем возможность добавлять/удалять обработчики отладочных данных, дело за малым: их нужно вызывать:
  profiler.announce = function(data){
    for(var id in this.clients)
       this.clients[id](data);
  }

Просто пробегаемся по всем своим функциям-подписчикам и вызываем их с параметром переданным в announce.

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

  Все вызовы:
  if(condition)
    console.log(data);

   я заменил на что-то вроде:

  profiler.announce({
    type: condition,
    data, data
  });
  или 
  profiler.announce({
    type: 'dynamoDB',
    query: query,
    responsse: response,
    time: (new Date).getTime()-start
  });

В общем, тут на что фантазии хватит, я решил классифицировать типы данных всегда передавая type, а в обработчиках у меня есть возможность фильтровать их как мне заблагорассудится. Например, напишем логирование в файлы:
profiler.subscribe('file logging', function(data){
  var filename = '';
  switch(data.type){
    case 'dynamoDB': filename = 'dynamoDB.log'; break;
    case 'debug': filename = 'debug.log'; break;
    case 'bug': filename = 'error.log'; break;
    default: filename = 'other.log'; break; 
  }
  appendToFile(filename, JSON.stringify(data));
})

а баги отправим на почту:
profiler.subscribe('bugs mail',function(data){
  if(data.type == 'bug')
    sendEmail(config.admin, data.method, data.error + '\n' + data.stack + '\n');
})

ну и выведем все в консоль: profiler.subscribe('console', console.log);

а также подпишем на эти данные socket.io
        profiler.subscribe(socket.id, function(data){
            socket.json.send({'event': 'logging data',  'data': data});    
        });
эту подписку я делаю по команде с клиента, по другой же или по disconnect отписываюсь:
    socket.on('disconnect', function() {
        profiler.unsubscribe(socket.id);
    });    


Как видите, очень гибко и удобно, надеюсь кому-нибудь это поможет в нелегком деле рефакторинга.

Спасибо за внимание!

PS: Для тех, кому не нравится неприватность свойства clients в нашем профайлере, всегда можно( и нужно ) сделать так:
var profiler = function(){
  var clients = [];
  this.add = function(id, next){...}
  this.del = function(id){...}
  this.log = function(data){...}
}
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.