Console.log-оболочка, которая поддерживает номера строк и поддерживает большинство методов?

Как я могу написать оболочку журнала консоли, которая:

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

Поскольку запись в Java Script настолько противоречива, должно быть какое-то решение. Реализация его сама по себе немного утомительна, но, похоже, нет хорошей библиотеки.

В настоящее время я нашел этот регистратор, который предоставляет все функции, но он испортил номера строк. http://benalman.com/projects/javascript-debug-console-log/

Ответ 1

Есть мой собственный log4javascript, который имеет свою собственную консоль ведения журнала, но также предоставляет оболочку console.log. Он выполняет все ваши критерии, за исключением того, что номера строк не повреждены, чего невозможно достичь, если вы завершаете вызовы на console.log() и т.д. В другой функции.

var log = log4javascript.getLogger("main");
var appender = new log4javascript.BrowserConsoleAppender();
log.addAppender(appender);
log.debug("Hello world");

Ответ 2

Я бы также рекомендовал log4javascript и объяснил, как вы все еще можете хранить информацию о напечатанном имени файла и строке, по крайней мере в Chrome.

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

Концепция

В Chrome вы можете создать объект Error, который предоставляет свойство стека, которое показывает ваше текущее местоположение стека, и где-то в строке стека вы найдете файл и номер строки вашего вызова script.

  > new Error().stack
  "Error
    at eval at <anonymous> (eval at evaluate (unknown source))
    at eval at evaluate (unknown source)
    at FrameMirror.evaluate (native)
    at Object.evaluate (unknown source)
    at Object._evaluateOn (unknown source)
    at Object._evaluateAndWrap (unknown source)
    at Object.evaluateOnCallFrame (unknown source)
    at meinAjaxAufruf (http://localhost:8080/numberajax.js:21:9)
    at HTMLInputElement.onkeyup (http://localhost:8080/numberajax.html:15:188)"

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

"Error
    at Object.append (http://localhost:8080/log4javascript_uncompressed.js:1921:17)
    at Object.doAppend (http://localhost:8080/log4javascript_uncompressed.js:1047:9)
    at Object.callAppenders (http://localhost:8080/log4javascript_uncompressed.js:647:27)
    at Object.log (http://localhost:8080/log4javascript_uncompressed.js:640:10)
    at Object.debug (http://localhost:8080/log4javascript_uncompressed.js:748:9)
    at meinAjaxAufruf (http://localhost:8080/numberajax.js:36:16)
    at HTMLInputElement.onkeyup (http://localhost:8080/numberajax.html:16:188)"

И файл и строка, которые сделали вызов log4javascript, и который меня интересует, это

at meinAjaxAufruf (http://localhost:8080/numberajax.js:36:16)

Решение

Я предполагаю, что глубина стека из script заинтересована в том, где происходит фактический вызов console, всегда одна и та же. Итак, теперь вам просто нужно выяснить, где BrowserConsoleAppender делает свой window.console доступ и добавляет интересующую вас строку в форматированную строку. Я внес следующие изменения в log4javascript_uncompressed.js (версия 1.4.2, строка 1913):

} else if (window.console && window.console.log) { // Safari and Firebug
        var formattedMesage = getFormattedMessage();

        //---my additions
        var isChrome = navigator.userAgent.indexOf("Chrome") !== -1;
        if(isChrome){
            var stack = new Error().stack;
            var lineAccessingLogger = stack.split("\n")[6];
            formattedMesage += "\n" + lineAccessingLogger;
        }
        //---

        // Log to Firebug using its logging methods or revert to the console.log
        // method in Safari
        if (window.console.debug && Level.DEBUG.isGreaterOrEqual(loggingEvent.level)) {
            window.console.debug(formattedMesage);
        } else if (window.console.info && Level.INFO.equals(loggingEvent.level)) {
        ...

Теперь вместо

17:53:22,872 DEBUG - sending /NumberServlet?zahl=1&text=
                                                 log4javascript.js:154

Я получаю

17:55:53,008 DEBUG - sending /NumberServlet?zahl=1&text=

    at meinAjaxAufruf (http://localhost:8080/numberajax.js:36:16) log4javascript_uncompressed.js:1930

Это не очень хорошее решение:), но я получаю то, что мне нужно.

С небольшим количеством знаний о структуре я предполагаю, что можно изменить PatternLayout таким образом, чтобы вы могли определить, как печатать имя файла/местоположение и номер строки.

edit Вместо моего предыдущего решения я внесла некоторые изменения в функцию PatternLayout.prototype.format, поэтому теперь я могу использовать дополнительную опцию% l для определения того, где и как я хочу вывести вызов файл и его строку. Я опубликовал свои изменения и пример использования в качестве Gist.

Ответ 3

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

if(DEBUG_ENABLED && (typeof console != 'undefined')) {
    this.debug = console.log.bind(console);
}
else {
    this.debug = function(message) {};
}

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

Ответ 4

Перекрестная привязка из связанного вопроса (Собственная оболочка для console.log с правильным номером строки?), но с обновленным решением для решения нескольких методов.


Мне понравился @fredrik answer, поэтому я перевернул его с помощью другого ответа, который разбивает стек Webkit, и объединил его с @безопасной оболочкой PaulIrish console.log. "Стандартизирует" filename:line для "специального объекта", поэтому он выделяется и выглядит в основном тем же, что и в FF и Chrome.

Тестирование в скрипте: http://jsfiddle.net/drzaus/pWe6W/9/

_log = (function (methods, undefined) {

    var Log = Error; // does this do anything?  proper inheritance...?
    Log.prototype.write = function (args, method) {
        /// <summary>
        /// Paulirish-like console.log wrapper.  Includes stack trace via @fredrik SO suggestion (see remarks for sources).
        /// </summary>
        /// <param name="args" type="Array">list of details to log, as provided by `arguments`</param>
        /// <param name="method" type="string">the console method to use:  debug, log, warn, info, error</param>
        /// <remarks>Includes line numbers by calling Error object -- see
        /// * http://paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/
        /// * https://stackoverflow.com/questions/13815640/a-proper-wrapper-for-console-log-with-correct-line-number
        /// * /questions/62469/how-to-get-javascript-caller-function-line-number-how-to-get-javascript-caller-source-url/430635#430635
        /// </remarks>

        // via @fredrik SO trace suggestion; wrapping in special construct so it stands out
        var suffix = {
            "@": (this.lineNumber
                    ? this.fileName + ':' + this.lineNumber + ":1" // add arbitrary column value for chrome linking
                    : extractLineNumberFromStack(this.stack)
            )
        };

        args = args.concat([suffix]);
        // via @paulirish console wrapper
        if (console && console[method]) {
            if (console[method].apply) { console[method].apply(console, args); } else { console[method](args); } // nicer display in some browsers
        }
    };
    var extractLineNumberFromStack = function (stack) {
        /// <summary>
        /// Get the line/filename detail from a Webkit stack trace.  See /questions/62469/how-to-get-javascript-caller-function-line-number-how-to-get-javascript-caller-source-url/430635#430635
        /// </summary>
        /// <param name="stack" type="String">the stack string</param>

        // correct line number according to how Log().write implemented
        var line = stack.split('\n')[3];
        // fix for various display text
        line = (line.indexOf(' (') >= 0
            ? line.split(' (')[1].substring(0, line.length - 1)
            : line.split('at ')[1]
            );
        return line;
    };

    // method builder
    var logMethod = function(method) {
        return function (params) {
            /// <summary>
            /// Paulirish-like console.log wrapper
            /// </summary>
            /// <param name="params" type="[...]">list your logging parameters</param>

            // only if explicitly true somewhere
            if (typeof DEBUGMODE === typeof undefined || !DEBUGMODE) return;

            // call handler extension which provides stack trace
            Log().write(Array.prototype.slice.call(arguments, 0), method); // turn into proper array & declare method to use
        };//--  fn  logMethod
    };
    var result = logMethod('log'); // base for backwards compatibility, simplicity
    // add some extra juice
    for(var i in methods) result[methods[i]] = logMethod(methods[i]);

    return result; // expose
})(['error', 'debug', 'info', 'warn']);//--- _log

Ответ 5

Чтобы это было просто, я использую следующую оболочку для консольных методов:

var noop = function () {};
window.consolex = {
    debug : window.console && window.console.debug && console.debug.bind(console) || noop,
    log : window.console && window.console.log && console.log.bind(console) || noop,
    warn: window.WARN = window.console && window.console.warn && console.warn.bind(console) || noop,
    error: window.ERROR = window.console && window.console.error && console.error.bind(console) || noop
};

Кроме того, для улучшения журналов в IE и старых браузерах, пожалуйста, прочитайте: Подробное ведение журнала

Ответ 6

В Google Chrome скоро появится функция, которая будет интересна этой теме.

Вы можете включить его сейчас:

  • Включить хром://флаги/# enable-devtools-эксперименты
  • Нажмите cog в инструментах dev
  • Перейдите на вкладку "Эксперименты".
  • Отметьте "Отладка фреймворков Javascript"
  • Перейдите на вкладку Общие
  • В разделе "Источники"
  • Отметьте "Пропустить переход по источникам с конкретными именами"
  • В окне ввода шаблона введите имя файла, которое вы видите сейчас (app.log.js)

Перезапустите и наслаждайтесь:)

Ссылки:

Тесты с chrom devtools

поток проблем devtools

обзор кода devtools

Ответ 7

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

var Debugger = function(gState, klass) {
  this.debug = {}
  if (!window.console) return function(){}
  if (gState && klass.isDebug) {
    for (var m in console)
      if (typeof console[m] == 'function')
        this.debug[m] = console[m].bind(window.console, klass.toString()+": ")
  }else{
    for (var m in console)
      if (typeof console[m] == 'function')
        this.debug[m] = function(){}
  }
  return this.debug
}

И используйте его следующим образом:

isDebug = true //global debug state

debug = Debugger(isDebug, this)

debug.log('Hello Log!')

Ответ 8

Я нашел решение (требуется jquery), которое было в Интернете, но оно не работает в большинстве браузеров. Я изменил его, и он работает в Firefox (Mac, Linux, Android), Chrome (Mac, Linux, Android) и Safari и других веб-браузерах Android.

Просто напишите следующий код в файл, например, например. debug.js и включить его после включения "jquery.js" в разделе "lt; head" на вашей веб-странице, и он будет работать после загрузки страницы (document.ready). Мне все еще нужно выяснить, разрешить отладку, прежде чем все будет загружено (например, только <head> ... </head> ). Веб-страницу нужно вызывать с? D = 1 в URL-адресе и при использовании Safari? D ​​= 1s, поскольку я не могу провести различие между Safari и другим браузером Webkit в пользовательском агенте, а Safari имеет другое поведение в номере строки и имени файла чем другие браузеры Webkit.

Функция p_r (выражение) записывается в окно id #js_debug и на консоль (если она открыта) с именем файла и номером строки.

var g_d = null;


function sortObj(theObj)
{
  var sortable = [];
  for (var i in theObj) {
    sortable.push(i);
  }
  sortable.sort();

  var copy = new Object;
  for (var i in sortable) {
    var ind = sortable[i];
    copy[ind] = theObj[ind];
  }

  return copy;

}

function p_r(s, comment, level)
{
  if (!g_d) return;
  var res = s;
  var pre = new Array("","  " , "    ", "      ", "        ");
  if (comment) comment += ' : ';
  if (arguments.length<2) comment='';
  if (arguments.length<3) level = 0;
//  if (console) console.log(s);
  if (typeof(s) == 'object') {
    var copy = sortObj(s);
    comment += '\n';
    res = '[object]\n';
    if (level < 2) {
      for (var i in copy) {
        if (typeof(copy[i]) != "function")
          res += pre[level] + (i) + " : " + p_r(copy[i], '', level+1) +  " : " + typeof(copy[i]) + "\n";
      }
      res += pre[level] + "[/object]\n";
    }
  }
  else if (typeof(s) == 'function')
    res = 'function';
  else if (typeof(s) != 'string')
    res = '' + s;
  res = res.replace(/&/g, '&amp;');
  res = res.replace(/\x3C/g, '&lt;');
  res = res.replace(/>/g, '&gt;');
  if (level == 0) {
window.LOG=res;
console.log(window.LOG + comment + res);
    g_d.innerHTML += (window.LOG + comment + res + '\n');
  }
  return res;
}

if (location.href.match(/d\=[1-9]/)) {

  $(document).ready(function() {
    $("body").prepend("<div id=\"js_debugclick\" onclick=\"$('#js_debug').toggle();\">JS DEBUG</div>\
  <pre onclick=\"$('#js_debug').toggle();\" id='js_debug'></pre>\
");

    $("head").append("<style type=\"text/css\">\
pre#js_debug {\
border: solid black 1px; background-color: #1CF; color: #000; display:none; position:absolute; top: 20px;\
font-family: Lucida Console, monospace; font-size: 9pt; height: 400px; overflow:scroll; width:100%;\
z-index:100;\
} \
#js_debugclick { \
  color:red; font-weight:bold; \
} \
</style>\
");
    g_d = document.getElementById('js_debug');
  });

  var __moredebug = location.href.match(/d\=[2-9]/);

    var __issafari = /safari/.test(navigator.userAgent.toLowerCase()) && location.href.match(/d\=[1-9]s/);
    var __iswebkit = /webkit/.test(navigator.userAgent.toLowerCase());
    var __isopera  = /opera/.test(navigator.userAgent.toLowerCase());
  if (__moredebug) console.log(__issafari, __iswebkit);

/*@const*/ //for closure-compiler
//DEBUG=2 // 0=off, 1=msg:file:line:column, 2=msg:stack-trace

/*@const @constructor*/
Object.defineProperty(window,'__stack__',{get:function(){
    try{i.dont.exist()}catch(e){
if (__moredebug)  var x=e.stack.split(":"); for (i in x){console.log(i,x[i]);}
//    console.log(e.stack.split(":")[13].match(/(\d+)/)[1]);
    return e.stack.split(":")}
}})

/*@const @constructor*/
Object.defineProperty(window,'__file__',{get:function(){
    var s=__stack__,l=s.length
    var f= __issafari ? s[9] : (__isopera ? s[12] : (__iswebkit ? s[14] : s[9]));
    return f.replace(/^.+?\/([^\/]+?)\?.+?$/, "$1");
}})

/*@const @constructor*/
Object.defineProperty(window,'__line__',{get:function(){
    var s=__stack__,l=s.length
    return __issafari ? s[10].match(/(\d+)/)[1] :(__isopera ? s[13].match(/(\d+)/)[1] : (__iswebkit ? s[15] : s[10].replace(/\n/, " ").replace(/(\d+).+?$/, "$1")));
}})

/*@const @constructor*/
Object.defineProperty(window,'__col__',{get:function(){
    var s=__stack__,l=s.length
    return (isNaN(s[l-2]))?"NA":s[l-1]
}})

/*@const @constructor*/
Object.defineProperty(window,'LOG',{
    get:function(){return out},
    set:function(msg){if(0)out=msg+"\t-\t"+__stack__
        else out=__file__+" "+__line__+": ";
        }
})



}//end if(DEBUG)