Правильная оболочка для console.log с правильным номером строки?

132

Сейчас я разрабатываю приложение и устанавливаю глобальный isDebugпереключатель. Хотелось бы обернуть console.logдля более удобного использования.

//isDebug controls the entire site.
var isDebug = true;

//debug.js
function debug(msg, level){
    var Global = this;
    if(!(Global.isDebug && Global.console && Global.console.log)){
        return;
    }
    level = level||'info';
    Global.console.log(level + ': '+ msg);
}

//main.js
debug('Here is a msg.');

Затем я получаю этот результат в консоли Firefox.

info: Here is a msg.                       debug.js (line 8)

Что, если я хочу войти в систему с номером строки, откуда debug()вызывается, например info: Here is a msg. main.js (line 2)?

Руфус
источник
Вы можете использовать console.logдля информации, console.warnпредупреждения и console.errorошибки, вместо того, чтобы добавлять что-то console.logчерез функцию-оболочку.
Элвин Вонг
2
@AlvinWong Да, я знаю это, но проблема в том, что мне нужен глобальный переключатель отладки, который определяет, consoleнужно ли его использовать. Кажется, что обертка - единственный способ достичь такой цели?
Руфус
Для Google Chrome см. Stackoverflow.com/a/25729203/1429301 В вашем случае шаблон будет debug.js
Александр

Ответы:

117

Это старый вопрос, и все предоставленные ответы чрезмерно хакерские, имеют ОСНОВНЫЕ проблемы с кроссбраузерностью и не содержат ничего сверхполезного. Это решение работает во всех браузерах и сообщает все данные консоли в точности так, как должно. Никаких взломов не требуется и одна строка кода. Ознакомьтесь с кодом .

var debug = console.log.bind(window.console)

Создайте переключатель следующим образом:

isDebug = true // toggle this to turn on / off for global controll

if (isDebug) var debug = console.log.bind(window.console)
else var debug = function(){}

Затем просто позвоните следующим образом:

debug('This is happening.')

Вы даже можете управлять console.log с помощью такого переключателя:

if (!isDebug) console.log = function(){}

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

var Debugger = function(gState, klass) {

  this.debug = {}

  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!')
debug.trace('Hello trace!')

Теперь вы можете добавить его в свои классы:

var MyClass = function() {
  this.isDebug = true //local state
  this.debug = Debugger(isDebug, this)
  this.debug.warn('It works in classses')
}
arctelix
источник
16
Поправьте меня, если я ошибаюсь, но это не позволяет вам добавлять какие-либо дополнительные функции, верно? Вы по сути только псевдоним консольного объекта? Грубый пример - нет возможности console.log () событие дважды для каждого debug.log ()?
AB Carroll
3
@ABCarroll Вы можете console.logдважды связать пользовательскую log()функцию, содержащую два вызова console.log, однако номера строк будут отражать строку, которая console.logдействительно находится, а не то, где debug.logвызывается. Однако вы можете делать такие вещи, как добавление динамических префиксов / суффиксов и т. Д. Есть также способы компенсировать проблему с номером строки, но я думаю, это другой вопрос. Посмотрите этот проект для примера: github.com/arctelix/iDebugConsole/blob/master/README.md
arctelix
2
Этот метод не работает в Firefox с 47 по 49 версии включительно. И было исправлено только в версии 50.0a2. Что ж, FF50 будет выпущен через 2 недели, но я потратил несколько часов, чтобы понять, почему он не работает. Думаю, эта информация может кому-то пригодиться. ссылка
Владимир Любимов 01
Я считаю, что @ABCarroll имел в виду, что все внутри экземпляра не может использоваться во время выполнения. в другом случае глобальное состояние может быть определено только при создании экземпляра, поэтому, если вы позже измените его this.isDebugна false, это не имеет значения. Я просто не знаю, есть ли какой-то способ обойти это, может быть, это сделано намеренно. В этом смысле, isDebugэто вводит в заблуждение varи constвместо этого должно быть .
cregox
3
Это не отвечает на вопрос «Что, если я хочу войти в журнал с номером строки, в которой вызывается debug ()?»
техномаг
24

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

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

_log = (function (undefined) {
    var Log = Error; // does this do anything?  proper inheritance...?
    Log.prototype.write = function (args) {
        /// <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>
        /// <remarks>Includes line numbers by calling Error object -- see
        /// * http://paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/
        /// * /programming/13815640/a-proper-wrapper-for-console-log-with-correct-line-number
        /// * https://stackoverflow.com/a/3806596/1037948
        /// </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.log) {
            if (console.log.apply) { console.log.apply(console, args); } else { console.log(args); } // nicer display in some browsers
        }
    };
    var extractLineNumberFromStack = function (stack) {
        /// <summary>
        /// Get the line/filename detail from a Webkit stack trace.  See https://stackoverflow.com/a/3806596/1037948
        /// </summary>
        /// <param name="stack" type="String">the stack string</param>

        if(!stack) return '?'; // fix undefined issue reported by @sigod

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

    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)); // turn into proper array
    };//--  fn  returned

})();//--- _log

Это также работает в узле, и вы можете проверить это с помощью:

// no debug mode
_log('this should not appear');

// turn it on
DEBUGMODE = true;

_log('you should', 'see this', {a:1, b:2, c:3});
console.log('--- regular log ---');
_log('you should', 'also see this', {a:4, b:8, c:16});

// turn it off
DEBUGMODE = false;

_log('disabled, should not appear');
console.log('--- regular log2 ---');
drzaus
источник
немного более продвинутый ответ для учета дополнительных consoleметодов, таких как warn, errorи т. д. - stackoverflow.com/a/14842659/1037948
drzaus
1
var line = stack.split('\n')[2];-'undefined' is not an object
sigod
@sigod - вероятно, это зависит либо от браузера, либо от того, что я написал это 2 года назад, и браузеры изменились. какой у тебя сценарий?
drzaus
1
один из моих коллег скопировал ваш код в наш проект. Он сломал сайт в IE11 и Safari 5. Не уверен в других версиях этого браузера. Может, добавишь чек на будущие копировщики?
sigod
1
@sigod, а теперь? добавлен if(!stack) return '?'к методу, который не работает, а не к
месту его вызова
18

Вы можете поддерживать номера строк и выводить уровень журнала с помощью некоторого умного использования Function.prototype.bind:

function setDebug(isDebug) {
  if (window.isDebug) {
    window.debug = window.console.log.bind(window.console, '%s: %s');
  } else {
    window.debug = function() {};
  }
}

setDebug(true);

// ...

debug('level', 'This is my message.'); // --> level: This is my message. (line X)

Сделав еще один шаг, вы можете использовать consoleразличия между ошибками / предупреждениями / информацией и по-прежнему иметь настраиваемые уровни. Попытайся!

function setDebug(isDebug) {
  if (isDebug) {
    window.debug = {
      log: window.console.log.bind(window.console, '%s: %s'),
      error: window.console.error.bind(window.console, 'error: %s'),
      info: window.console.info.bind(window.console, 'info: %s'),
      warn: window.console.warn.bind(window.console, 'warn: %s')
    };
  } else {
    var __no_op = function() {};

    window.debug = {
      log: __no_op,
      error: __no_op,
      warn: __no_op,
      info: __no_op
    }
  }
}

setDebug(true);

// ...

debug.log('wat', 'Yay custom levels.'); // -> wat: Yay custom levels.    (line X)
debug.info('This is info.');            // -> info: This is info.        (line Y)
debug.error('Bad stuff happened.');     // -> error: Bad stuff happened. (line Z)
namuol
источник
1
Я уже некоторое время пытаюсь автоматически префикс вывода console.debug(...)с function nameи arguments- какие-либо мысли о том, как это сделать?
Daniel Sokolowski
3
Я смотрел на множество консольных оберток, прокладок и т. Д. и это первая встреча, которая сочетает в себе сохранение номеров строк с настройкой вывода. Это умное использование того факта, что .bind также выполняет за вас некоторое каррирование, вы можете связать один или несколько аргументов в дополнение к контексту . Вы могли бы пойти дальше и передать ему функцию noop с методом .toString, который может запускать код при вызове метода журнала! См. Этот jsfiddle
Сэм Хаслер
2
Может быть , не во всех браузерах (не смотрел в него), но замен %sс %oв Chrome будет печатать параметры так , как вы ожидали бы (объекты расширения, числа и строки окрашены, и т.д.).
Anson
люблю это решение. Я внес несколько изменений, которые лучше подходят для моего приложения, но основная его часть все еще не повреждена и работает прекрасно. Спасибо
Ward
9

От: Как получить номер строки функции вызывающего абонента JavaScript? Как получить URL-адрес источника вызывающего абонента JavaScript? Errorобъект имеет свойство номер строки (в FF). Так должно работать примерно так:

var err = new Error();
Global.console.log(level + ': '+ msg + 'file: ' + err.fileName + ' line:' + err.lineNumber);

В браузере Webkit err.stackэто строка, представляющая текущий стек вызовов. Он отобразит текущий номер строки и дополнительную информацию.

ОБНОВИТЬ

Чтобы получить правильный номер полотна, вам нужно вызвать ошибку в этой строке. Что-то вроде:

var Log = Error;
Log.prototype.write = function () {
    var args = Array.prototype.slice.call(arguments, 0),
        suffix = this.lineNumber ? 'line: '  + this.lineNumber : 'stack: ' + this.stack;

    console.log.apply(console, args.concat([suffix]));
};

var a = Log().write('monkey' + 1, 'test: ' + 2);

var b = Log().write('hello' + 3, 'test: ' + 4);
Фредрик
источник
1
new Error();дает мне контекст, в котором он выполняется, если я его вставлю debug.js, то получу info: Here is a msg. file: http://localhost/js/debug.js line:7.
Руфус
1
В чем смысл Log = Error? Вы все еще изменяете класс Error, верно?
drzaus
Объединил ваш ответ с парой других - см. Ниже stackoverflow.com/a/14841411/1037948
drzaus
8

Здесь можно сохранить номер строки: https://gist.github.com/bgrins/5108712 . Это более или менее сводится к следующему:

if (Function.prototype.bind) {
    window.log = Function.prototype.bind.call(console.log, console);
}
else {
    window.log = function() { 
        Function.prototype.apply.call(console.log, console, arguments);
    };
}

Вы можете обернуть это с isDebugи установить window.logна , function() { }если вы не отладки.

Брайан Гринстед
источник
7

Вы можете передать номер строки в свой метод отладки, например:

//main.js
debug('Here is a msg.', (new Error).lineNumber);

Здесь (new Error).lineNumberвы получите текущий номер строки в вашем javascriptкоде.

Subodh
источник
2
Немного многословно, не правда ли?
Руфус
2
Думаю, этого достаточно, чтобы ответить на ваш вопрос. :)
Субодх
1
свойство lineNumber нестандартное и сейчас работает только в firefox, см. здесь
Маттиас,
6

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

Просто поместите небольшую обертку console.log в отдельный файл, например

(function() {
    var consolelog = console.log
    console.log = function() {
        // you may do something with side effects here.
        // log to a remote server, whatever you want. here
        // for example we append the log message to the DOM
        var p = document.createElement('p')
        var args = Array.prototype.slice.apply(arguments)
        p.innerText = JSON.stringify(args)
        document.body.appendChild(p)

        // call the original console.log function
        consolelog.apply(console,arguments)
    }
})()

Назовите его как-нибудь вроде log-blackbox.js

Затем перейдите в настройки Chrome Devtools и найдите раздел «Blackboxing», добавьте шаблон для имени файла, которое вы хотите поместить в черный ящик, в данном случае log-blackbox.js

kzahel
источник
Примечание: Убедитесь , что у вас нет никакого кода вы бы хотите , чтобы показать в трассировки стека в том же файле, так как он также будет удален из следа.
jamesthollowell
6

Я нашел простое решение объединить принятый ответ (привязка к console.log / error / etc) с некоторой внешней логикой для фильтрации того, что на самом деле зарегистрировано.

// or window.log = {...}
var log = {
  ASSERT: 1, ERROR: 2, WARN: 3, INFO: 4, DEBUG: 5, VERBOSE: 6,
  set level(level) {
    if (level >= this.ASSERT) this.a = console.assert.bind(window.console);
    else this.a = function() {};
    if (level >= this.ERROR) this.e = console.error.bind(window.console);
    else this.e = function() {};
    if (level >= this.WARN) this.w = console.warn.bind(window.console);
    else this.w = function() {};
    if (level >= this.INFO) this.i = console.info.bind(window.console);
    else this.i = function() {};
    if (level >= this.DEBUG) this.d = console.debug.bind(window.console);
    else this.d = function() {};
    if (level >= this.VERBOSE) this.v = console.log.bind(window.console);
    else this.v = function() {};
    this.loggingLevel = level;
  },
  get level() { return this.loggingLevel; }
};
log.level = log.DEBUG;

Использование:

log.e('Error doing the thing!', e); // console.error
log.w('Bonus feature failed to load.'); // console.warn
log.i('Signed in.'); // console.info
log.d('Is this working as expected?'); // console.debug
log.v('Old debug messages, output dominating messages'); // console.log; ignored because `log.level` is set to `DEBUG`
log.a(someVar == 2) // console.assert
  • Обратите внимание, что console.assertиспользуется условное ведение журнала.
  • Убедитесь, что инструменты разработчика вашего браузера показывают все уровни сообщений!
Джейкоб Филлипс
источник
Потому что он не дает ни номера строки, ни рабочих примеров, показывающих уровень журнала.
not2qubit 08
Номер строки будет таким же, как при использовании консоли напрямую. Я обновил ответ примерами использования. У него не так много голосов, потому что я ответил на него два года спустя :)
Джейкоб Филлипс,
4

Если вы просто хотите контролировать, используется ли отладка и иметь правильный номер строки, вы можете сделать это вместо этого:

if(isDebug && window.console && console.log && console.warn && console.error){
    window.debug = {
        'log': window.console.log,
        'warn': window.console.warn,
        'error': window.console.error
    };
}else{
    window.debug = {
        'log': function(){},
        'warn': function(){},
        'error': function(){}
    };
}

Когда вам нужен доступ для отладки, вы можете сделать это:

debug.log("log");
debug.warn("warn");
debug.error("error");

Если isDebug == true, номера строк и имена файлов, показанные в консоли, будут правильными, потому что debug.logetc на самом деле является псевдонимом console.logetc.

Если isDebug == falseсообщения отладки не отображаются, потому что debug.logetc просто ничего не делает (пустая функция).

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

Элвин Вонг
источник
Отлично, мне нужно быть осторожным с порядком isDebug = trueи debug.js, но этот ответ действительно работает!
Rufus
3
window.debug = window.consoleбыло бы немного чище.
Фредрик
@fredrik, тогда мне нужно «реализовать» все функции-члены if isDebug == false. : {
Элвин Вонг
@AlvinWong Я просто хочу сказать, если isDebug===true. Или событие к этому: jsfiddle.net/fredrik/x6Jw5
fredrik
4

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

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

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

var log = Logger.get("module").level(Logger.WARN);
log.error("An error has occured", errorObject);
log("Always show this.");

РЕДАКТИРОВАТЬ: суть включена ниже

/*
 * Copyright 2016, Matthieu Dumas
 * This work is licensed under the Creative Commons Attribution 4.0 International License.
 * To view a copy of this license, visit http://creativecommons.org/licenses/by/4.0/
 */

/* Usage : 
 * var log = Logger.get("myModule") // .level(Logger.ALL) implicit
 * log.info("always a string as first argument", then, other, stuff)
 * log.level(Logger.WARN) // or ALL, DEBUG, INFO, WARN, ERROR, OFF
 * log.debug("does not show")
 * log("but this does because direct call on logger is not filtered by level")
 */
var Logger = (function() {
    var levels = {
        ALL:100,
        DEBUG:100,
        INFO:200,
        WARN:300,
        ERROR:400,
        OFF:500
    };
    var loggerCache = {};
    var cons = window.console;
    var noop = function() {};
    var level = function(level) {
        this.error = level<=levels.ERROR ? cons.error.bind(cons, "["+this.id+"] - ERROR - %s") : noop;
        this.warn = level<=levels.WARN ? cons.warn.bind(cons, "["+this.id+"] - WARN - %s") : noop;
        this.info = level<=levels.INFO ? cons.info.bind(cons, "["+this.id+"] - INFO - %s") : noop;
        this.debug = level<=levels.DEBUG ? cons.log.bind(cons, "["+this.id+"] - DEBUG - %s") : noop;
        this.log = cons.log.bind(cons, "["+this.id+"] %s");
        return this;
    };
    levels.get = function(id) {
        var res = loggerCache[id];
        if (!res) {
            var ctx = {id:id,level:level}; // create a context
            ctx.level(Logger.ALL); // apply level
            res = ctx.log; // extract the log function, copy context to it and returns it
            for (var prop in ctx)
                res[prop] = ctx[prop];
            loggerCache[id] = res;
        }
        return res;
    };
    return levels; // return levels augmented with "get"
})();

solendil
источник
Этот ответ имеет только 3 положительных голоса, но он невероятно насыщен и чист, чем любой другой на странице
Том
тем не менее, похоже, что все полезные части имеют внешнюю суть.
Райан Лич
3

Идея со связыванием Function.prototype.bindвеликолепна. Вы также можете использовать библиотеку npm line -logger . Он показывает исходные исходные файлы:

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

var LoggerFactory = require('lines-logger').LoggerFactory;
var loggerFactory = new LoggerFactory();
var logger = loggerFactory.getLoggerColor('global', '#753e01');

Журналы печати:

logger.log('Hello world!')();

введите описание изображения здесь

deathangel908
источник
2

Вот способ сохранить существующие consoleоператоры ведения журнала при добавлении имени файла и номера строки или другой информации трассировки стека в вывод:

(function () {
  'use strict';
  var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
  var isChrome = !!window.chrome && !!window.chrome.webstore;
  var isIE = /*@cc_on!@*/false || !!document.documentMode;
  var isEdge = !isIE && !!window.StyleMedia;
  var isPhantom = (/PhantomJS/).test(navigator.userAgent);
  Object.defineProperties(console, ['log', 'info', 'warn', 'error'].reduce(function (props, method) {
    var _consoleMethod = console[method].bind(console);
    props[method] = {
      value: function MyError () {
        var stackPos = isOpera || isChrome ? 2 : 1;
        var err = new Error();
        if (isIE || isEdge || isPhantom) { // Untested in Edge
          try { // Stack not yet defined until thrown per https://docs.microsoft.com/en-us/scripting/javascript/reference/stack-property-error-javascript
            throw err;
          } catch (e) {
            err = e;
          }
          stackPos = isPhantom ? 1 : 2;
        }

        var a = arguments;
        if (err.stack) {
          var st = err.stack.split('\n')[stackPos]; // We could utilize the whole stack after the 0th index
          var argEnd = a.length - 1;
          [].slice.call(a).reverse().some(function(arg, i) {
            var pos = argEnd - i;
            if (typeof a[pos] !== 'string') {
              return false;
            }
            if (typeof a[0] === 'string' && a[0].indexOf('%') > -1) { pos = 0 } // If formatting
            a[pos] += ' \u00a0 (' + st.slice(0, st.lastIndexOf(':')) // Strip out character count
              .slice(st.lastIndexOf('/') + 1) + ')'; // Leave only path and line (which also avoids ":" changing Safari console formatting)
            return true;
          });
        }
        return _consoleMethod.apply(null, a);
      }
    };
    return props;
  }, {}));
}());

Тогда используйте это так:

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <script src="console-log.js"></script>
</head>
<body>
  <script>
  function a () {
    console.log('xyz'); // xyz   (console-log.html:10)
  }
  console.info('abc'); // abc   (console-log.html:12)
  console.log('%cdef', "color:red;"); // (IN RED:) // def   (console-log.html:13)
  a();
  console.warn('uuu'); // uuu   (console-log.html:15)
  console.error('yyy'); // yyy   (console-log.html:16)
  </script>
</body>
</html>

Это работает в Firefox, Opera, Safari, Chrome и IE 10 (еще не протестировано в IE11 или Edge).

Бретт Замир
источник
Хорошая работа, но все же не на 100% то, что мне нужно. Я хотел бы иметь информацию об имени файла и номере строки в правой части представления консоли, где ее можно щелкнуть, чтобы открыть исходный код. Это решение показывает информацию как часть сообщения (например:) my test log message (myscript.js:42) VM167 mypage.html:15, которое не так удобно читать, к тому же оно не связано. По-прежнему хорошая работа, поэтому голос за нее.
Frederic Leitenberger
Да, хотя это было бы идеально, нет способа, AFAIK, подделать ссылку на имя файла, которая отображается в консоли ...
Бретт Замир
@BrettZamir разместил вопрос об этом коде здесь: stackoverflow.com/questions/52618368/…
Mahks 03
1
//isDebug controls the entire site.
var isDebug = true;

//debug.js
function debug(msg, level){
    var Global = this;
    if(!(Global.isDebug && Global.console && Global.console.log)){
        return;
    }
    level = level||'info';
    return 'console.log(\'' + level + ': '+ JSON.stringify(msg) + '\')';
}

//main.js
eval(debug('Here is a msg.'));

Это даст мне info: "Here is a msg." main.js(line:2).

Но evalнужно лишнее , жаль.

Руфус
источник
2
eval - это зло! Так что всякое зло.
Фредрик
1

Код из http://www.briangrinstead.com/blog/console-log-helper-function :

// Full version of `log` that:
//  * Prevents errors on console methods when no console present.
//  * Exposes a global 'log' function that preserves line numbering and formatting.
(function () {
  var method;
  var noop = function () { };
  var methods = [
      'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error',
      'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log',
      'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd',
      'timeStamp', 'trace', 'warn'
  ];
  var length = methods.length;
  var console = (window.console = window.console || {});

  while (length--) {
    method = methods[length];

    // Only stub undefined methods.
    if (!console[method]) {
        console[method] = noop;
    }
  }


  if (Function.prototype.bind) {
    window.log = Function.prototype.bind.call(console.log, console);
  }
  else {
    window.log = function() { 
      Function.prototype.apply.call(console.log, console, arguments);
    };
  }
})();

var a = {b:1};
var d = "test";
log(a, d);
Тимо Кяхконен
источник
Кажется, это не показывает исходный номер строки, откуда logвызывается
ragamufin
Я почти уверен, что это сработало, когда я тестировал, но я заменил код «полной» версией с той же страницы. По крайней мере, работал в Chrome 45.
Тимо Кяхкёнен,
Понял. С изменениями, которые у вас есть сейчас, он по сути такой же, как и некоторые другие ответы и работает. Мне просто был любопытен ваш предыдущий код, потому что в конце у вас было приложение, которое открывало мне интересные возможности использовать его для других целей, но поскольку он не отображал номер строки, я вернулся к исходной точке. Спасибо хоть!
ragamufin
1

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

Я добавил небольшую обертку в начало приложения:

window.log = {
    log_level: 5,
    d: function (level, cb) {
        if (level < this.log_level) {
            cb();
        }
    }
};

Чтобы позже я мог просто сделать:

log.d(3, function(){console.log("file loaded: utils.js");});

Я тестировал его в firefox и crome, и оба браузера, похоже, отображают журнал консоли, как и предполагалось. Если вы заполняете таким образом, вы всегда можете расширить метод 'd' и передать ему другие параметры, чтобы он мог выполнять дополнительную регистрацию.

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

Владимир М
источник
1

window.line = function () {
    var error = new Error(''),
        brower = {
            ie: !-[1,], // !!window.ActiveXObject || "ActiveXObject" in window
            opera: ~window.navigator.userAgent.indexOf("Opera"),
            firefox: ~window.navigator.userAgent.indexOf("Firefox"),
            chrome: ~window.navigator.userAgent.indexOf("Chrome"),
            safari: ~window.navigator.userAgent.indexOf("Safari"), // /^((?!chrome).)*safari/i.test(navigator.userAgent)?
        },
        todo = function () {
            // TODO: 
            console.error('a new island was found, please told the line()\'s author(roastwind)');        
        },
        line = (function(error, origin){
            // line, column, sourceURL
            if(error.stack){
                var line,
                    baseStr = '',
                    stacks = error.stack.split('\n');
                    stackLength = stacks.length,
                    isSupport = false;
                // mac版本chrome(55.0.2883.95 (64-bit))
                if(stackLength == 11 || brower.chrome){
                    line = stacks[3];
                    isSupport = true;
                // mac版本safari(10.0.1 (12602.2.14.0.7))
                }else if(brower.safari){
                    line = stacks[2];
                    isSupport = true;
                }else{
                    todo();
                }
                if(isSupport){
                    line = ~line.indexOf(origin) ? line.replace(origin, '') : line;
                    line = ~line.indexOf('/') ? line.substring(line.indexOf('/')+1, line.lastIndexOf(':')) : line;
                }
                return line;
            }else{
                todo();
            }
            return '😭';
        })(error, window.location.origin);
    return line;
}
window.log = function () {
    var _line = window.line.apply(arguments.callee.caller),
        args = Array.prototype.slice.call(arguments, 0).concat(['\t\t\t@'+_line]);
    window.console.log.apply(window.console, args);
}
log('hello');

вот мое решение по этому вопросу. когда вы вызываете метод: log, он напечатает номер строки, в которой вы распечатываете свой журнал

Фэн Ли
источник
1

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

var debugmode='console';
var debugloglevel=3;

function debug(msg, type, level) {

  if(level && level>=debugloglevel) {
    return(function() {});
  }

  switch(debugmode) {
    case 'alert':
      return(alert.bind(window, type+": "+msg));
    break;
    case 'console':
      return(console.log.bind(window.console, type+": "+msg));
    break;
    default:
      return (function() {});
  }

}

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

debug(message, "serious", 1)();
debug(message, "minor", 4)();
Сол Добни
источник
1

Здесь можно упростить логику. Это предполагает, что ваш глобальный флаг отладки НЕ является динамическим и устанавливается при загрузке приложения или передается как некоторая конфигурация. Это предназначено для использования для маркировки среды (например, только печать в режиме разработки, а не производство)

Ванильный JS:

(function(window){ 
  var Logger = {},
      noop = function(){};

  ['log', 'debug', 'info', 'warn', 'error'].forEach(function(level){
    Logger[level] = window.isDebug ? window.console[level] : noop;
  });

  window.Logger = Logger;
})(this);

ES6:

((window) => {
  const Logger = {};
  const noop = function(){};

  ['log', 'debug', 'info', 'warn', 'error'].forEach((level) => {
    Logger[level] = window.isDebug ? window.console[level] : noop;
  });

  window.Logger = Logger;
})(this);

Модуль:

const Logger = {};
const noop = function(){};

['log', 'debug', 'info', 'warn', 'error'].forEach((level) => {
  Logger[level] = window.isDebug ? window.console[level] : noop;
});

export default Logger;

Угловой 1.x:

angular
  .module('logger', [])
  .factory('Logger', ['$window',
    function Logger($window) {
      const noop = function(){};
      const logger = {};

      ['log', 'debug', 'info', 'warn', 'error'].forEach((level) => {
        logger[level] = $window.isDebug ? $window.console[level] : noop;
      });

      return logger;
    }
  ]);

Все, что вам нужно сделать сейчас, это заменить все ссылки консоли на Logger.

dysfunc
источник
1

Эта реализация основана на выбранном ответе и помогает уменьшить количество шума в консоли ошибок: https://stackoverflow.com/a/32928812/516126

var Logging = Logging || {};

const LOG_LEVEL_ERROR = 0,
    LOG_LEVEL_WARNING = 1,
    LOG_LEVEL_INFO = 2,
    LOG_LEVEL_DEBUG = 3;

Logging.setLogLevel = function (level) {
    const NOOP = function () { }
    Logging.logLevel = level;
    Logging.debug = (Logging.logLevel >= LOG_LEVEL_DEBUG) ? console.log.bind(window.console) : NOOP;
    Logging.info = (Logging.logLevel >= LOG_LEVEL_INFO) ? console.log.bind(window.console) : NOOP;
    Logging.warning = (Logging.logLevel >= LOG_LEVEL_WARNING) ? console.log.bind(window.console) : NOOP;
    Logging.error = (Logging.logLevel >= LOG_LEVEL_ERROR) ? console.log.bind(window.console) : NOOP;

}

Logging.setLogLevel(LOG_LEVEL_INFO);
Брайан
источник
0

Я нашел некоторые ответы на эту проблему слишком сложными для моих нужд. Вот простое решение, созданное в Coffeescript. Это адаптировано из версии Брайана Гринстеда здесь

Он предполагает глобальный объект консоли.

# exposes a global 'log' function that preserves line numbering and formatting.
(() ->
    methods = [
      'assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error',
      'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log',
      'markTimeline', 'profile', 'profileEnd', 'table', 'time', 'timeEnd',
      'timeStamp', 'trace', 'warn']
    noop = () ->
    # stub undefined methods.
    for m in methods  when  !console[m]
        console[m] = noop

    if Function.prototype.bind?
        window.log = Function.prototype.bind.call(console.log, console);
    else
        window.log = () ->
            Function.prototype.apply.call(console.log, console, arguments)
)()
sandover
источник
0

Я решил создать объект, а затем создать новое свойство объекта с помощью Object.defineProperty () и вернуть свойство консоли, которое затем использовалось как обычная функция, но теперь с расширенными возможностями.

var c = {};
var debugMode = true;

var createConsoleFunction = function(property) {
    Object.defineProperty(c, property, {
        get: function() {
            if(debugMode)
                return console[property];
            else
                return function() {};
        }
    });
};

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

createConsoleFunction("warn");
createConsoleFunction("log");
createConsoleFunction("trace");
createConsoleFunction("clear");
createConsoleFunction("error");
createConsoleFunction("info");

И теперь вы можете использовать свою функцию так же, как

c.error("Error!");
Абель Родригес
источник
0

На основе других ответов (в основном @arctelix one) я создал это для Node ES6, но быстрый тест также показал хорошие результаты в браузере. Я просто передаю другую функцию в качестве ссылки.

let debug = () => {};
if (process.argv.includes('-v')) {
    debug = console.log;
    // debug = console; // For full object access
}
vandijkstef
источник
0

Вот моя функция регистратора (на основе некоторых ответов). Надеюсь, кто-то сможет это использовать:

const DEBUG = true;

let log = function ( lvl, msg, fun ) {};

if ( DEBUG === true ) {
    log = function ( lvl, msg, fun ) {
        const d = new Date();
        const timestamp = '[' + d.getHours() + ':' + d.getMinutes() + ':' +
            d.getSeconds() + '.' + d.getMilliseconds() + ']';
        let stackEntry = new Error().stack.split( '\n' )[2];
        if ( stackEntry === 'undefined' || stackEntry === null ) {
            stackEntry = new Error().stack.split( '\n' )[1];
        }
        if ( typeof fun === 'undefined' || fun === null ) {
            fun = stackEntry.substring( stackEntry.indexOf( 'at' ) + 3,
                stackEntry.lastIndexOf( ' ' ) );
            if ( fun === 'undefined' || fun === null || fun.length <= 1 ) {
                fun = 'anonymous';
            }
        }
        const idx = stackEntry.lastIndexOf( '/' );
        let file;
        if ( idx !== -1 ) {
            file = stackEntry.substring( idx + 1, stackEntry.length - 1 );
        } else {
            file = stackEntry.substring( stackEntry.lastIndexOf( '\\' ) + 1,
                stackEntry.length - 1 );
        }
        if ( file === 'undefined' || file === null ) {
            file = '<>';
        }

        const m = timestamp + ' ' + file + '::' + fun + '(): ' + msg;

        switch ( lvl ) {
        case 'log': console.log( m ); break;
        case 'debug': console.log( m ); break;
        case 'info': console.info( m ); break;
        case 'warn': console.warn( m ); break;
        case 'err': console.error( m ); break;
        default: console.log( m ); break;
        }
    };
}

Примеры:

log( 'warn', 'log message', 'my_function' );
log( 'info', 'log message' );
март
источник