Пользователь
0,8
рейтинг
21 октября 2013 в 11:05

Разработка → API консоли Javascript

Разработчикам удобно пользоваться консолью для отладки, но ещё удобнее, если будет оболочка, в которой учтены особенности реализации консоли в различных браузерах, поэтому тема обёрток для консоли устойчиво существует.

Рассмотрим ранее опубликованные решения, затем сделаем обзор методов консоли с помощью перевода недавней статьи Axel Rauschmayer-а, разработчика и консультанта с более чем 15-летним стажем, затем я опубликую некоторые свои решения, которые оказались удачными в процессе эволюции и отладки на ряде проектов.
UPD 2015: обновление таблицы команд до актуального состояния, Github (ru, en; разворачивание на javascript).

Обёртки консоли на Хабре, обзоры и документация


"Используем console «на полную»" — показывает способы отличной обработки объектов, учёт особенностей различных браузеров в реализации.

Делаем консоль чуточку удобнее — Подменяет исходный объект, что решает некоторые баги. Отлично исправляет деградацию некоторых браузеров по полноте поддержки методов. Остаётся разве что длинность написания нативных методов, что есть плюс в плане неизучения новых названий, но требует набора оригинальных слов. Может быть решено ускорителями набора типа emmet в IDE.

simple wrapper for console.log — 2010 (англ.).
Console.Log: Say Goodbye to JavaScript Alerts for Debugging! (IE), 2011 (англ.)
dbug — a console wrapper (Github, 2009)
"Firebug* console API" — эта статья в чём-то полнее последующего перевода. Обе они дадут взаимно дополняющую информацию. (Воспользуемся переводом, чтобы самостоятельно не тестировать вручную, повторяя проделанную автором работу.)

Перевод обзора из блога Axel Rauschmayer


Итоговая таблица с сортировкой методов по алфавиту.
В большинстве браузеров существует глобальный объект console с методами для логирования и отладки. Он — не часть языка, но стал фактическим стандартом, появившись вначале в отладчике Firebug. Так как основная цель для него — отладка, он часто используется при разработке и редко — в работающем коде.

Как стандартизирована консоль в различных браузерах?


Firebug первым начал продвигать API консоли и его вики-документацию, успешнее других приближаясь к стандартизации. Кроме того, Brian Kardell и Paul Irish работают над спецификацией API, что в перспективе должно дать лучшую согласованность поведения браузеров. ДО сих пор их правила довольно различны, поэтому данная статья даст общее краткое описание. Дополнительную информацию вы получите в документации к различным платформам.

Chrome: developers.google.com/chrome-developer-tools/docs/console-api/
Firebug: getfirebug.com/wiki/index.php/Console_API
Firefox: developer.mozilla.org/en-US/docs/Web/API/console
IExplorer: msdn.microsoft.com/en-us/library/ie/hh772183.aspx
Node.js: nodejs.org/api/stdio.html
Safari: Safari Developer Guide

Баг в IE9: объект console существует, если хотя бы раз было открыто окно средств разработчика (Developer Tools, F12). Иначе — происходит ошибка ReferenceError. Как вариант обхода бага, нужно проверять наличие объекта и ставить заглушку в случае отсутствия.

Методы для простого логирования


console.clear() — очистить консоль;
console.debug(object1, object2?, ...) — то же, что console.log() (вопросики означают необязательность аргумента);
console.error(object1, object2?, ...) — логирование параметров под значком ошибки (без остановки кода) и, возможно, добавляется трассировка стека вызовов и ссылка на код.
  console.exception(errorObject, object1?, ...]) [только в Firebug] — объекты с интерактивным стеком трассировки;
console.info(object1?, object2?, ...) — логирование в консоль; в браузерах может помечаться значком и, возможно, имеет трассировку стека или ссылку; поддерживаются шаблоны printf, как в console.log.
console.log(object1?, object2?, ...) — логирование в консоль. Если первый аргумент — строка в формате директив printf, она форматирует значения остальных аргументов. Пример (Node.js REPL):
    > console.log('%s', { foo: 'bar' })
    [object Object]
    > console.log('%j', { foo: 'bar' })
    {"foo":"bar"}

Единственная надёжная кроссплатформенная директива форматирования — это '%s'. Node.js поддерживает '%j' для JSON-данных. Браузеры могут поддерживать другие директивы интерактивных действий для консоли (подробнее здесь (рус.)).

console.trace() — показ интерактивного стека вызовов функций в браузерах (стек вызовов, приведший к выполнению кода в данной точке, аналогично тому, что видим в сообщениях об ошибках);
console.warn(object1?, object2?, ...) — логирование под значком предупреждения; может содержать трассировку стека или ссылку. Поддерживаются шаблоны printf, как в console.log.

Chrome Firebug Firefox IE Node.js Safari
clear() -⊝- -⊝-
debug() -⊝-
error()
exception() -⊝- -⊝- -⊝- -⊝- -⊝-
info()
log()
trace()
warn()


Методы для проверок и подсчёта


console.assert(expr, obj?) — если первый аргумент ложен, то выводит объект в консоль и выбрасывает исключение; если true — ничего не делает;
console.count(label?) — подсчитывает, сколько раз встретилась функция с этой меткой.

Chrome Firebug Firefox IE Node.js Safari
assert() -⊝-
count() -⊝- -⊝-


Методы для логов с форматированием


console.dir(object) — выводит представление объекта в консоли. Может быть интерактивно — разворачиваться, просматриваться в других вкладках инструмента (в Node.js — без интерактивности).
console.dirxml(object) — выводит XML-дерево элемента HTML или XML.
console.group(object1?, object2?, ...) — начинает вывод сворачиваемого объекта в консоли, содержащего группы указанных и будущих значений в каждой новой строке. Блок объявляется завершённым командой console.groupEnd() и изначально развёрнут для просмотра, но может сворачиваться-разворачиваться вручную (мышью).
console.groupCollapsed(object1?, object2?, ...) — работает аналогично console.group(), но блок изначально свёрнут.
console.groupEnd() — закрывает группу, которая была начата console.group() или console.groupCollapsed().
console.table(data, columns?) — выводит массив как таблицу, по одному элементу на строку. Дополнительный аргумент указывает, какие свойства массива отображаются в колонках. Если пропущен — отображаются все свойства. Несуществующие свойства отображаются в колонках как неопределённые.
    var persons = [
        { firstName: 'Jane', lastName: 'Bond' },
        { firstName: 'Lars', lastName: 'Croft', age: 72 }
    ];
    // Эквивалентные записи:
    console.table(persons);
    console.table(persons, ['firstName', 'lastName', 'age']);
Будем видеть в консоли:
(index) firstName lastName age
0 «Jane» «Bond» undefined
1 «Lars» «Croft» 72

Chrome Firebug Firefox IE Node.js Safari
dir()
dirxml() -⊝- -⊝-
group() -⊝-
groupCollapsed() -⊝-
groupEnd() -⊝-
table() -⊝- -⊝- -⊝- -⊝-


Профилирование и тайминги


console.markTimeline(label) — [Safari-only] то же, что timeStamp().
console.profile(title?) — включение профилирования. Необязательный аргумент используется для комментирования в отчёте.
console.profileEnd() — останавливает профилирование и выводит отчёт.
console.time(label) — запускает таймер с указанной меткой (названием, идентификатором).
console.timeEnd(label) — останавливает таймер с меткой и показывает насчитанное время.
console.timeStamp(label?) — выводит промежуточные отсчёты времени для таймера с указанной меткой.
Chrome Firebug Firefox IE Node.js Safari
markTimeline() -⊝- -⊝- -⊝- -⊝- -⊝-
profile() (devtools) -⊝-
profileEnd() (devtools) -⊝-
time()
timeEnd()
timeStamp() -⊝- -⊝- -⊝- -⊝-

"(devtools)" означает, что метод будет работать в случае, если открыта панель инструментов разработчика.

Благодарности:
В подготовке этого обзора участвовали два человека: Matthias Reuter (@ gweax) и Philipp Kyeck (@ pkyeck).
--Автор: Axel Rauschmayer. (Конец перевода.)

Итоговая таблица с сортировкой по алфавиту. (Ссылки помогут увидеть подробности и прочитать о поддержке Оперой в дополнение к этой статье. В последнюю колонку добавлены краткие замечания об Опере оттуда.)
Chrome Firebug Firefox IE Node.js Safari Opera
assert() -⊝- ±
clear() -⊝- -⊝-
count() -⊝- -⊝- ±
debug() -⊝- ±
dir()
dirxml() -⊝- -⊝- ±
error() ±
exception() -⊝- -⊝- -⊝- -⊝- -⊝- ±
group() -⊝-
groupCollapsed() -⊝-
groupEnd() -⊝-
info()
log() ±
markTimeline() -⊝- -⊝- -⊝- -⊝- -⊝- -⊝-
profile() (devtools) -⊝- -⊝-
profileEnd() (devtools) -⊝- -⊝-
table() -⊝- -⊝- -⊝- -⊝- -⊝-
time() ±
timeEnd() ±
timeStamp() -⊝- -⊝- -⊝- -⊝- -⊝-
trace() ±
warn()

Спасибо автору блога за свежие сведения и таблицы, относящиеся к методам консоли. Зарядившись попкорном, продолжим разговор. Имеем все методы, чтобы пользоваться, но чаще всего не нужно всех. Часто достаточно одного, но универсального console.log. Есть много задач, где нужны скромные, но кроссбраузерные возможности.

Несколько сниппетов (вставок кода с функциями), для различных применений


Всё, что выше — это красиво, годится как справочник, довольно полно и хорошо. Но нужны решения, а не теория — ведь для каждой мелкой задачи понадобятся похожие решения.

Опыт показывает, что совсем не нужно иметь исчерпывающую поддержку всех функций — чаще всего достаточно одной или нескольких, и больше интересны сервисные функции: отключить консоль полностью одной переменной или выражением (когда код выложен на продакшн), выводить только ошибки по переменной или готовить ошибки для отсылки в техподдержку проекта, иметь управляемый вывод сообщений в виде нескольких вариантов.

Сложные и некроссбраузерные функции скорее не нужны, чем нужны: красивый вывод таблиц, не позволяющий отлаживаться в IE или Safari, будет скорее мешать, заставит возвращаться к уровню настроек консоли в неподходящие моменты.

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

Вначале надо проделать собственные измышления о том, что нам надо иметь. Нелюбители читать чьи-то измышления могут на этом оставить статью. На основании них будут построены примеры.

1. Дизайн кода функций.


Поговорим и выберем дизайн кода. Это — не дизайн страницы, это — то, как код будет смотреться в текстовом редакторе.

Для отладки имеем 2 стандартных способа и, соответственно, дизайна — функции типа alert/confirm/prompt и методы объекта console. Замещать стандартные имена функций и методов — всё же, не всегда хорошо. Эа исключением случаев, когда надо предотвратить ошибки реализаций, как в IE. Для своей отладки, на мой взгляд, целесообразнее выбрать другое имя для функций или их группы. Например, я экспериментировал с применением слова «Alert», с большой буквы, но и оно показалось длинным после нескольких проектов, и я перешёл на «wcl()» — символическое сокращение от «window.console.log». Если вам не понравился выбор, подставьте в примеры свой вариант.

Из «wcl()» логично и понятно следуют остальные сокращения — «wci» — window.console.info; wcw — window.console.warn; wce — window.console.error. Такие слова редко встречаются в именах перемеенных, достаточно короткие и нормально отыскиваются взглядом или поиском по проекту. Для таймингов добавим wct(), а для сброса консоли — wcc().

Эти функции предлагается класть сразу в глобальный объект. Это не будет засорять пространство глобального объекта, потому что на продакшене их можно при желании выкусить из кодов проекта. А применение экономит не менее 2 символов или больше, чем если бы они были в объекте (типа C.log, C.err,...).

На этом дизайн не закончен — достаточно удобным оказался ещё такой финт, чтобы приписать их в методы объекта String.
String.prototype.wcl = wcl;
Не спешите поднимать негодующие руки — это не нарушение чистых принципов, потому что работать это будет, опять же, на этапе отладки, разработки. На чистом проекте их тоже можно из кода выкусить. А помогает способ тем, что отладочные сообщения легко искать через Ctrl-F.
'Ajax_request'.wcl(data); //даёт вывод 'Ajax_request' Object{...данные...}
//Равносильно
wcl('Ajax_request', data);

Найти в коде этот фрагмент — по строке "Ajax_request'" (с апострофом в конце). В начале строки мы вольны поставить свои дополнительные символы для лучшего выделения сообщения в логе, например"==".

Дизайн вывода: лог не всегда доступен в браузерах. Это верно не только для IE с его знаменитым багом, но и для браузеров на телевизорах — в Smart TV — технологиях и в ТВ-приставках кабельного телевидения используются браузеры, не имеющие консольного вывода и не собирающиеся его иметь. Приходится использовать удалённый отладчик или вывод в блок на экране. Идеально, если будет переменная, которая позволит показать или создать блок, и переводящая весь лог на экран. Не помешает он и для IE, избавив нас от отслеживания ошибок отсутствия объекта.

Наконец, если надо быстро очистить продакшн от логов, в оболочке надо иметь выражение, запрещающее вывод логов. Например, выводить, если домен — localhost и не иначе. Тогда не требуется удалять их из кода парсингом проекта, хотя выключенная отладка будет немного нагружать JS и увеличивать код.

Подведём итоги кодового дизайна (он сформировался не сейчас на этих рассуждениях, а года за 2 практики; рассуждения — способ объяснить причины происхождения и получаемые удобства).

1) используем имена для логов: wcw, wci, wcl, wce, wcc, wct;
2) прочие возможности использовать не будем — не доросли до использования, но доросли до понимания, что простое лучше, чем сложное;
3) все функции могут работать как методы объекта String для удобства написания в коде: 'строка'.wcl();
4) могут отключаться одним выражением;
5) может выводить логи в другое представление — див на экране или удалённая консоль;
6) интересна возможность динамического переключения потоков для распределения информации.

2. Сниппеты


2.1. Простое логирование без IE


var noConsole =0, win = window
,wcl = function(a){ a = a!==undefined ? a :''; //консоль как метод строки или функция, с отключением по noConsole ==1
	if(win.console && !noConsole)
		win.console.log.apply(console, this instanceof String
			? ["'=="+ this +"'"].concat([].slice.call(arguments))
			: arguments);
};
String.prototype.wcl = wcl;

Здесь есть пара элементов простой уличной магии: первая строка функции позволяет не указывать аргументов и не иметь ошибок для выражений вида 'место_трассировки_116'.wcl(). А выражение [].slice.call(arguments) позволяет приобрести коллекции аргументов свойство массива, чтобы они без ошибок могли участвовать в операции concat(). (Об этом нередко спрашивают на собеседованиях: каким способом вы превращаете коллекцию в массив?)

Если решили вообще отказаться от свойства объекта, получается совсем коротко (тут вопрос — в дизайне кода, устроит ли нас написание только в формате wcl(...)):
var noConsole =0, win = window
,wcl = function(){ //консоль как метод строки или функция, с отключением по noConsole ==1
	if(win.console && !noConsole)
		win.console.log(arguments);
};
Не проверяем отсутствие аргументов, поскольку просто «wcl();» без аргументов использовать не будем. Будет работать и в IE, если открыто окно инструментов разработчика.

2.2. Простое логирование с IE


Непонимание браузером IE того, что console.log — это функция, приводит к ещё одному витку магии (и, в общем, немного замедляет работу во всех бр-рах, поэтому лучше использовать, если действительно нужен IE).
var noConsole =0, win = window
,wcl = function(a){ a = a!==undefined ? a :''; //консоль как метод строки или функция, с откл. по noConsole ==1, +IE
	if(win.console && !noConsole)
		Function.prototype.apply.call(console.log, console, this instanceof String
			? ["'=="+ this +"'"].concat([].slice.call(arguments))
			: arguments);
};
String.prototype.wcl = wcl;


2.3. Четыре уровня сообщений в логах


Если хотим немного раскрасить вывод, чтобы на цвет различать важность и характер сообщений своего проекта, воспользуемся копипейстом используем написанный в 2.1 код для работы 4 похожих и всеми поддерживаемых функций — log, warn, info, error. Поскольку копипейст в коде — не лучший, хотя и простой метод, скопируем следующий сниппет, где копипейст уже преобразован в цикл, и добавлена интересная возможность — указать уровень сообщений, которые будут выводиться в лог. Уровни:
0: warn, (warning)
1: info,
2: log,
3: error.
Некоторая переменная logLevel будет указывать, ниже какого уровня сообщения не выводить.
(function(w, logLevel, wcA){ var lvl =0
  ,$x = function(el, h){if(h) for(var i in h) el[i] = h[i]; return el};
  for(var i in wcA) //консоль[i] как метод строки или функция
    w[i] = (function(lvl, wcAI, i){
      return function(a){ a = a!==undefined|| arguments.length ? a :'';
        if(w.console && logLevel <= lvl)
          Function.prototype.apply.call(w.console[i], w.console, this instanceof String
          //w.console[i].apply(console, this instanceof String //--for without IE
            ? [wcAI + this +"'"].concat([].slice.call(arguments))
            : arguments);
        else
          w.console[i] = function(){};
    } })(lvl++, wcA[i], {wcw:'warn', wci:'info', wcl:'log', wce:'error'}[i]);
  w.wcc = w.console.clear;
  $x(String.prototype, {wcw: w.wcw, wci: w.wci, wcl: w.wcl, wce: w.wce, wcc: w.wcc });
})(window, /*logLevel*/ 0, {wcw:"'-w-", wci:'--', wcl:"'==", wce:"'=E="});


2.4. Четыре уровня сообщений в логах или в блоке на экране


На случай невозможности пользоваться консолью код придётся расширить практически вдвое, чтобы формировать вывод логов в видимый блок на экране. Как дополнительная функция, добавляется возможность перезапускать логгер в любом месте программы, меняя как подробность логирования выбором logLevel, так и вывод на экран или в консоль. Отключается логирование установкой logLevel = 4 при перезапуске.
(Чтобы это запустилось в IE8-, нужны вспомогательные определения: )
Скрытый текст
if(!Array.indexOf) //old browser support
	Array.prototype.indexOf = function(obj){
		for(var i =0, iL = this.length; i < iL; i++)
			if(this[i] == obj)
				return i;
		return -1;
	};
if(!document.getElementsByClassName)
	document.getElementsByClassName = function(className){
		if(!className) return [];
		var e = document.getElementsByTagName('*')
			,list = [];
		for(var i =0, iL = e.length; i < iL; i++){
			var clss = e[i].className.split(' ');
			if(clss.indexOf(className) >-1)
				list.push(e[i]);
		}
		return list;
	};

$e() — это способ генерации блоков:
Скрытый текст
var $e = function(g){//g={el,cl,ht,cs,at,on,apT}
  if(!g.el && g.el !==undefined && g.el !='') return g.el;
  g.el = g.el ||'DIV';
  var o = g.el = typeof g.el =='string'? document.createElement(g.el) : g.el;
  if(o){
    if(g.cl)
      o.className = g.cl;
    if(g.cs){
      if(!IE) $x(o.style, g.cs);
      else{
        var s ='';
        for(i in g.cs)
          s += toCsKey(i) +':'+ g.cs[i] +';';
        o.style.cssText = s;
    }}
    if(g.ht || g.at){
      var at = g.at ||{}; if(g.ht) at.innerHTML = g.ht;}
    if(at)
      for(var i in at){
        if(i=='innerHTML') o[i] = at[i];
        else o.setAttribute(i, at[i]);}
    g.apT && g.apT.appendChild(o); //ставится по ориентации, если новый
  return o;
}

var logOnScreen =1
  ,logLevel =0 //0..4
  ,consoleOrig
,loadLogger = function(onScreen, logLevel){ logLevel = logLevel !==undefined ? logLevel : 3;
  var w = window, wcA ={wcw:"'-w-", wci:'--', wcl:"'==", wce:"'=E="}
	,cons = w.document.getElementsByClassName('console')[0];
  if(onScreen){ //вывод сообщений консоли в блок на экране
    if(!cons)
      cons = $e({cl:'console',cs:{position:'fixed',width:'600px',minHeight:'150px',maxHeight:'800px',overflow:'auto',overflowX:'hidden',overflowY:'auto',top:'-2px',left:'300px',zIndex:99999,fontSize:'13px',fontFamily:'Arial',backgroundColor:'#a5c6ee',opacity:0.65, filter:'progid:DXImageTransform.Microsoft.Alpha(opacity=65)'}, apT: w.document.body });
    cons && (cons.style.display ='block');
    var consA = {warn:'w', info:'i', log:'', error:'E'};
    if(!w.console) w.console ={};
    consoleOrig = w.console;
    w.console ={};
	lvl =0;
    for(var i in consA){
      w.console[i] = (function(lvl, consAI, conCA){return function(aa){
        if(cons && logLevel <= lvl){
          cons.innerHTML +=['<i class=cons'+ conCA +'>'+ (this instanceof String ?"'=="+ this +"'": consAI) +'</i>'].concat([].slice.call(arguments))
            .join('<i class=consDelim>/ </i>') +'<br>';
          cons.scrollTop = Math.max(0, cons.scrollHeight);
        }
      } })(lvl++, consA[i], i.charAt(0).toUpperCase() + i.substr(1) );
    }
    w.console.clear = function(a){if(cons) cons.innerHTML ='';}
  }else
    cons && (cons.style.display ='none');
  lvl =0;
  for(var i in wcA) //консоль[i] как метод строки или функция
    w[i] = (function(lvl, wcAI, i){
      return function(a){ a = a!==undefined|| arguments.length ? a :'';
        if(w.console && logLevel <= lvl)
        Function.prototype.apply.call(w.console[i], w.console, this instanceof String
        //w.console[i].apply(console, this instanceof String //--for without IE
          ? [wcAI + this +"'"].concat([].slice.call(arguments))
          : arguments);
        else
          w.console[i] = function(){};
    } })(lvl++, wcA[i], {wcw:'warn', wci:'info', wcl:'log', wce:'error'}[i]);
  w.wcc = w.console.clear;
  $x(String.prototype, {wcw: w.wcw, wci: w.wci, wcl: w.wcl, wce: w.wce, wcc: w.wcc });
};


Запускается этот логгер после загрузки DOM, потому что может использоваться див для вывода логов.
if(window.addEventListener)
  this.addEventListener('DOMContentLoaded',tst,!1);
else
  this.attachEvent('onload',tst);
var tst = function(){
  loadLogger(logOnScreen, logLevel);
  wcl('tst1');
  wcl();
  'tst2'.wcl();
  'tst3'.wcl({t: 23, o:{s: true}});
  'tst-wcw'.wcw(120)
  'tst-wci'.wci(121)
  'tst-wcl'.wcl(122)
  'tst-wce'.wce(123)
};
Если высота текста превысит максимальную высоту блока, будет действовать подкрутка скролла строкой «cons.scrollTop = Math.max(0, cons.scrollHeight);».

3. Дизайн таймингов


Есть основания не придерживаться формата логов группы «time*», потому что в исходном формате они малоинформативны. Используется только один аргумент, а строка вывода содержит только сосчитанное время. Чтобы строка не пустовала, лучше заполнить её одним из форматов логов, например, wci(), поставив в него посчитанный интервал и другие значения из остальных аргументов. Правда, для этого надо продублировать механизм подсчёта, сделав заодно его и для IE. Это же даст возможность выводить значения в див.

Сделаем для начала проще — пусть таймеры работают только для консоли. Но у нас есть избыточная форма: контекстный метод и обычная функция. Пусть 'x'.wct() будет стартом таймера, а wct('x') — его окончанием. Если же аргументов больше двух — выводится 2 строки: обычный лог, а затем тайминг.

В этой системе нет места timeStamp(), хотя можно было бы придумать для них функцию wcts(), тоже с любым количеством аргументов. Но лучше придерживаться минимализма.
(свернём вброс кода, похожего на прежний)
var logOnScreen =0
  ,logLevel =1 //0..4
  ,logTime =1
  ,consoleOrig
,loadLogger = function(onScreen, logLevel){ logLevel = logLevel !==undefined ? logLevel : 3;
  var w = window, wcA ={wcw:"'-w-", wci:'--', wcl:"'==", wce:"'=E="}
	,cons = w.document.getElementsByClassName('console')[0];
  if(onScreen){ //вывод сообщений консоли в блок на экране
    if(!cons)
      cons = $e({cl:'console',cs:{position:'fixed',width:'600px',minHeight:'150px',maxHeight:'800px',overflow:'auto',overflowX:'hidden',overflowY:'auto',top:'-2px',left:'300px',zIndex:99999,fontSize:'13px',fontFamily:'Arial',backgroundColor:'#a5c6ee',opacity:0.65, filter:'progid:DXImageTransform.Microsoft.Alpha(opacity=65)'}, apT: w.document.body });
    cons && (cons.style.display ='block');
    var consA = {warn:'w', info:'i', log:'', error:'E'};
    if(!w.console) w.console ={};
    consoleOrig = w.console;
    w.console ={};
	lvl =0;
    for(var i in consA){
      w.console[i] = (function(lvl, consAI, conCA){return function(aa){
        if(cons && logLevel <= lvl){
          cons.innerHTML +=['<i class=cons'+ conCA +'>'+ (this instanceof String ?"'=="+ this +"'": consAI) +'</i>'].concat([].slice.call(arguments))
            .join('<i class=consDelim>/ </i>') +'<br>';
          cons.scrollTop = Math.max(0, cons.scrollHeight);
        }
      } })(lvl++, consA[i], i.charAt(0).toUpperCase() + i.substr(1) );
    }
	w.console.clear = function(){if(cons) cons.innerHTML ='';};
  }else
    cons && (cons.style.display ='none');
  lvl =0;
  for(var i in wcA) //консоль[i] как метод строки или функция
    w[i] = (function(lvl, wcAI, i){
      return function(a){ a = a!==undefined|| arguments.length ? a :'';
        if(w.console && logLevel <= lvl)
        Function.prototype.apply.call(w.console[i], w.console, this instanceof String
        //w.console[i].apply(console, this instanceof String //--for without IE
          ? [wcAI + this +"'"].concat([].slice.call(arguments))
          : arguments);
        else
          w.console[i] = function(){};
    } })(lvl++, wcA[i], {wcw:'warn', wci:'info', wcl:'log', wce:'error'}[i]);
  w.wcc = w.console.clear;
  w.wct = !document.all && logTime ? (function(lvl, wcAI, i){
    return function(a){
      arguments.length ? (arguments.length !=1 || this != w ? this.wcl.apply(this,arguments) :0
      ,console.timeEnd.call(console, this != w ? this : a) ): console.time.call(console,this);
    } })() : function(){};
  $x(String.prototype, {wcw: w.wcw, wci: w.wci, wcl: w.wcl, wce: w.wce, wcc: w.wcc, wct: w.wct });
};

Проверить его можно так:
var tst = function(){
  loadLogger(logOnScreen, logLevel);
  'x'.wct()
  wcl('tst1');
  wcl();
  'tst2'.wcl();
  'y'.wct()
  'tst3'.wcl({t: 23, o:{s: true}});
  wct('x')
  wct('y','другие данные')
};

Выглядит:

Фиддл: jsfiddle.net/spmbt/Wgah8
Пользовались ли вы оболочками для функций логирования? Делали ли её под свои нужды?

Проголосовало 303 человека. Воздержалось 67 человек.

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

spmbt @spmbt
карма
154,5
рейтинг 0,8
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (30)

  • –1
    Это пять:)
    • –3
      С плюсом
  • 0
    Немного оффтопно, но всё же, у меня почему-то console.info не ставит свой знаёк в Хроме, это баг или фича?

    image
    • 0
      Why so serious?

      Понял свою ошибку, в Хроме console.log и console.info идентичный.
      • 0
        счастлив вам сообщить, что уже нет!
        • 0
          Да-да, при чём уже не сколько версий)
  • 0
    Результаты голосования радуют :) Сам пользуюсь только console.log с куском кода который проверяет доступен ли объект console.

    При учёте, что уже более 600 просмотров… Есть мнение, что форма голосования не заметна в потоке инфы.
  • +2
    Я использую свою console-cap
    Когда пишешь обёртку важно несколько вещей:
    1. Когда в логе появляется сообщение — строка должна указывать не на обёртку, а на место, где вызвана функция
    2. Обёртка должна спасать браузеры, в которых нету console.*
    3. Должна быть лёгкая и быстрая возможность передать функцию как коллбек
    element.onclick = console.log
    

    4. Может ещё что, не помню точно)
    • 0
      А logLevel так и не добавил )
      • 0
        Извини, я как-то не могу понять, насколько оно вписывается в либу.
        С одной стороны функциональность интересна и может пригодиться
        С другой — она нужна слишком редко и не хотелось бы переутяжелять библиотеку, а оставить её максимально простой.
        Пока решил оставить пул-реквест открытым, чтобы если кому-то понадобится на твою версию могли выйти и посмотреть другие комментарии, если появятся)
        • 0
          Вот пара примеров использования:
          1. В dev окружении в консоль выводятся все сообщения. В production — только ошибки. Один параметр при сборке.
          2. Нужно посмотреть только debug (или только info) сообщения, логи не нужны.
          • 0
            Вообще с идеей я согласен, не спорю =)
            • 0
              У меня этот уровень появился, потому что в одном проекте уже был вывод по разным методам. Сразу возникла мысль, а почему бы не просто отключать или оставлять error для продакшна, а ввести число — уровень подробности логов? Одно число решает сразу 3 задачи: 1) полное отключение, 2) только ошибки, 3) разные уровни подробности логов.

              Ещё изощрённее и при этом понятнее для преемника — это описывать подробность логов списком функций, пропускаемых на вывод. Тогда это — то, же, что число, но +самодокументируемо и +возможность выбирать произвольное множество выводов. (Но всё равно, впрок лучше не писать, только под реальную задачу.)
    • 0
      > строка должна указывать не на обёртку, а на место
      интересно, как это сделать в Хроме, особенно не для console.error? Я бы пользовался. Эту информацию (строку первого уровня стека вызовов) даёт Firefox в свойстве .line. Хром, сколько ни искал, ничего такого не даёт.

      > 3. element.onclick = console.log
      Это я читал у Вас в статье, но как-то так и не понял, а как же аргументы? Нужно уметь передавать не функцию, а функцию с аргументами.
      • +1
        > интересно, как это сделать в Хроме
        Посмотрите console-cap, увидите, как это сделать в хроме.

        > а как же аргументы?
        А какие аргументы вы ждете в onclick? Event придет, console.log его выведет.
        • 0
          1) посмотрел. Написано всё понятно, мне, действительно, нужно поучиться так писать. Но «решение» основано на том, что подмены функции для Хрома не происходит (выполняется bind(console, console.log) ). Это не решение, которое мне нужно.
          2) я жду произвольные аргументы, заданные мной. Выводить на онклик объект event — это даже не полдела, которое обычно нужно. Нужно вывести иили часть ивента, которую надо видеть в логе, или сопутствующий контент типа объекта не из event или результат выражения (скажем, геометрические вычисления, рассояние до точки, и т.д.).
          • 0
            1. Не совсем тогда понимаю, что вам нужно. Определите свои методы, в них используйте стандартную console. Всё будет работать.
            2. А кто мешает написать в вашем случае не element.onclick = console.log, а
            element.onclick = funciton(e) { console.log(e.someData, myObject, function() { ... }()) }
            
          • +1
            Это не решение, которое мне нужно.

            Интересно, а в чём видите недостаток данного решения?

            2) я жду произвольные аргументы, заданные мной. Выводить на онклик объект event — это даже не полдела, которое обычно нужно. Нужно вывести иили часть ивента, которую надо видеть в логе, или сопутствующий контент типа объекта не из event или результат выражения (скажем, геометрические вычисления, рассояние до точки, и т.д.).

            Ну это да, тут можно расширять. Самый просто вариаант — быстро повесить лог, проверить работает ли вообще.

            element.onclick = console.log


            Нужно добавить какую-то метку или результат вычислений? Запросто:

            element1.onclick = console.log.bind(null, 'el-first')
            element2.onclick = console.log.bind(null, 'el-second')
            


            На крайняк — всегда можно записать полный вариант:

            element1.onclick = function (e) {
              console.log(e.target);
            }
            

            • 0
              Интересно, а в чём видите недостаток данного решения?

              Нет, если имя можно сменить, а перехода на новый уровень стека не произойдёт и будет отражаться нужная строка — то то, что нужно. Не знаю, как ведёт себя bind в этом плане, позже познакомлюсь, и тогда уже поправлю свои коды — это довольно сильный недостаток.

              2) в jsfiddle в тест дописал
              document.body.onclick = wcl;
              
              — работает, выводит ссылку на event. Байнды — да, это первое, что приходит на ум. Но в Вашей статье писалось про возможные проблемы именно с присваиванием «голой» функции (проверил — в fiddle.net, действительно, присваивание онклику console.log не срабатывает и ошибки не выводит. Никогда не пытался так делать, поэтому не стал тогда разбираться в сути проблем. Ну да ладно).
              • 0
                Нет, если имя можно сменить, а перехода на новый уровень стека не произойдёт и будет отражаться нужная строка — то то, что нужно

                Да. Можно написать
                wcl = console.log.bind( console )
                


                Только в браузерах, где нету bind будет работать некорректно — IE8- и древние оперы.
      • +1
        интересно, как это сделать в Хроме

        Что в Хроме что в Фоксе — одинаково, при помощи бинд)
        var myLog = console.log.bind(console);
        
        myLog(1, 2, 3); // correct line in chrome and firefox
        
  • +9
    Эти функции предлагается класть сразу в глобальный объект. Это не будет засорять пространство глобального объекта, потому что на продакшене их можно при желании выкусить из кодов проекта. А применение экономит не менее 2 символов или больше, чем если бы они были в объекте (типа C.log, C.err,...).

    Это глупость, извините. Во-первых, мы не засоряем глобальный объект не для продакшена, а для нас, для кода. Названия должны быть не короткими, а понятными и читабельными. Когда кто-то заходит и читает в коде:
    console.log( variable )
    

    То он сразу понимает, что это. Если же я бы прочитал
    wcl( variable )
    

    Я бы прифигел.

    Ваш код (в месте «свернём вброс кода, похожего на прежний») весь из-за таких сокращений похож на обфусцированный. «Вы программист, а не обфускатор».

    apT? cl? consAI? conCA? Да вы, блин, издеваетесь? Если первые два я ещё догадался, что это appendTo и Class в силу контекста, но что такие consAI? Console Artificial Intelligence? А т.к. con уже без s — это видимо другое слово? Предполагаю, что conCA — это «Consortium of Cocaine Anonymous».

    Если не хотите, чтобы ваши коллеги проклинали вас — пишите код красивый и понятный, а не короткий, честное слово.
    • –5
      С этим, конечно, можно спорить, особенно, видя Ваше пристрастие к формулировке «console.log» в цитируемой статье. Тут я приведу контрпример: человек видит «console.log()» в коде, но офигевает оттого, что он работает там, где не дложен. Или console.time() в IE, для примера. В том и в другом случае он должен заглядывать в определение, но в первом случае (wcl()) он точно знает, что это — не консоль-лог, во втором — бежит знакомиться после смутных догадок.

      А насчёт коротких формулировок — они потому и короткие, потому что часто используются — раз. Имена вида consAI — это следствие моей системы именования временных переменных — два. Она мне сразу подсказывает, что аргумент есть consA[i], где consA — массив. Это — лучше, чем писать аргумент своим выдуманным словом типа elements или items или даже itemsOfConsoleArray. Всё равно придётся разбираться, что за items, что за Array. Но я не настаиваю. Каждый выбирает то, что ему удобнее, а я не случайно за пару лет пришёл к такой системе.
      • 0
        Не работает под IE? Почему бы в этом случае не определить свой console обьект?
        If ( typeof ( console ) == 'undefined' ) { var console = { ... } }

        Насчёт наименований, вынужден с вами не согласиться. Сокращения, тем более короткие и не принятые глобально — это не понятно никому, кроме вас. Если вы всегда разрабатываете один, редактируете свой же код тоже сами — то это конечно ваше личное дело. Но как только другой разработчик начинает читать ваш код — он будет в ступоре.

        Представьте, вас попросили, как знакомого программиста, убрать с сайта ошибку в консоли «ReferenceError: ec is not defined». Вы открываете код, нужную строчку и видите:

        gc: function( str ) {
        
        	var v = this.dgc();
        	var st = v.indexOf( " " + str + "=" );
        	if ( st == -1 ) {
        		st = v.indexOf( str + "=" );
        	}
        	if ( st == -1 ) {
        		v = null;
        	} else {
        		st = v.indexOf( "=", st ) + 1;
        		var e = v.indexOf( ";", st );
        		if ( ec == -1 ) {
        			ec = v.length;
        		}
        		v = unescape( v.substring( st, e ) );
        	}
        	return v;
        },
        


        О чём код? Ошибку нашли? Что он делает? Где его проверить?
        • +1
          Вижу, что вместо ec надо написать e, но вот что делает код осталось для меня загадкой.
        • 0
          Критика совершенно понятна, но это просто тема отдельной статьи. Здесь, в комментариях, совершенно невыгодно что-то доказывать по простейшей причине — для каждого читателя wcl — слово новое, для меня десятки раз на проект встречающееся. Не считаю, что вправе писать myConsoleLog для всех, если я использую wcl — тут «хук» TheShock -a совершенно профессиональный и безошибочный. Те, кто будет применять myConsoleLog, будет изнывать от многословия, если, конечно, имеют дело с большими проектами и не всегда пользуются автопродлением строк.

          Пример, который Вы привели — не из той оперы. В куске кода — все переменные — короткие. С такими приходится бороться, переименовывать, давая понятные имена. Но, как ни странно, wcl к ним не относится, потому что их — много. Когда у Вас в проекте их будет много, всё встанет на свои места.
  • 0
    я активно использую debugger; там, где надо вручную поставить точку остановки в коде. просто в коде пишите debugger; и хром остановится на указанном месте
  • 0
    Пишу кастомную консоль для ноды. С форматированием, стилями, префиксами и разными дополнительными штуками. github: console-ultimate.
    Основная цель: реализовать совместимую со стандартной консоль, которая будет иметь ряд дополнительных фич, какие есть в API консоли браузера.

    Из дополнительных фич:
    * Тайминги с возможностью возврата в переменную.
    * Тайминги с использованием hi-res таймера process.hrtime.
    * Счётчики count.
    * Базовые фичи, копирующие по функциям стандартную консоль: log, info, warn, error. Дополнительно цвета, префиксы, выбор потока, куда выводить.
    * Очистка консоли.

    Планирую реализовать:
    * Некое подобие групп (group, groupEnd): вывод будет обводиться рамками.
    * Таблицы.
    * Улучшенные стек-трейсы: асинхронные хопы в стеке и улучшенное форматирование.
    • 0
      Ещё больше возможностей даст удалённая консоль в браузере. Тогда и группы можно сделать полноценно, и таблицы не псевдографикой. Есть ли аналоги такому подходу?
      • 0
        Такой возможности нет, но я, пожалуй, это запишу. Надо проверить как можно интегрироваться с node-inspector. Ещё вместо удалённой консоли можно использовать repl, который доступен извне, по сокету.

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