Правильна обгортка для 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
Alexander

Відповіді:


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')
}

16
Виправте мене, якщо я помиляюся, але це не дозволяє вам додавати додаткові функціональні можливості, правда? Ви по суті лише псевдоніміть об’єкт консолі? Грубий приклад - не існує способу двічі console.log () двічі за кожен debug.log ()?
AB Керролл

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 тижні, але я витрачаю кілька годин, щоб зрозуміти, чому це не працює. Тому я думаю, що ця інформація для когось може бути корисною. посилання
Володимир Любімов

Я вважаю, що @ABCarroll означав, що все, що знаходиться в екземплярі, не може бути використане під час виконання. для іншого екземпляра глобальний стан можна визначити лише в інстанції, тому якщо згодом ви перейдете this.isDebugна falseце, це не матиме значення. Я просто не знаю, чи є щось навколо, можливо, це задумано. У цьому сенсі isDebugцілком оманливі varі повинні бути constзамість цього.
Крегокс

2
Це не відповідає на питання "Що робити, якщо я хочу увійти з номером рядка, де викликається налагодження ()?"
техногена

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 ---');

трохи більш просунута відповідь для обліку додаткових 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 '?'до методу, який не працює, а не де він викликається (тому, якщо хтось використовує сам метод, він теж "захищений")
drzaus

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)

1
Я вже деякий час намагаюся автоматично ввести префікс виводу console.debug(...)з function nameі arguments- будь-які думки, як це зробити?
Даніель Соколовський

3
Я дивився на безліч обгорткових консолей / прокладки / тощо. і це перший, який я натрапив, який поєднує збереження номерів рядків із налаштуванням виводу. Це розумне використання того факту, що .bind також робить для вас певну користь, ви можете зв’язати один чи більше аргументів на додаток до контексту . Ви можете зробити це на крок далі і передати йому функцію noop з методом .toString, який може запускати код, коли викликається метод журналу! Дивіться цю jsfiddle
Сем Хаслер

2
Може бути , не в усіх браузерах (не дивився в нього), але замін %sз %oв Chrome буде друкувати параметри так , як ви очікували б (об'єкти розширення, числа і рядки пофарбовані, і т.д.).
anson

люблю це рішення. Я вніс кілька змін, які краще допомагають моєму застосуванню, але основна його частина все ще залишається недоторканою і працює чудово. Спасибі
Уорд

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? Ви все ще змінюєте клас Помилка, правда?
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коді.


2
Трохи багатослівний, чи не так?
Руфус

2
Я думаю, що достатньо відповісти на ваш запит. :)
Subodh

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», додайте шаблон для імені файлу, який ви хочете в blackbox, в цьому випадку log-blackbox.js


Примітка. Переконайтеся, що у вас немає коду, який ви хотіли б відобразити у сліді стека в тому ж файлі, оскільки він також буде видалений із сліду.
jamesthollowell

6

Я знайшов просте рішення поєднати прийняту відповідь (прив’язка до console.log / помилка / тощо) з деякою зовнішньою логікою, щоб відфільтрувати те, що насправді записано в систему.

// 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

Номер рядка буде таким самим, як якщо ви використовуєте консоль безпосередньо. Я оновив відповідь на прикладах використання. У нього не багато голосів, тому що я відповів на нього два роки пізніше :)
Джейкоб Філіпс

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.logтощо - це псевдонімconsole.log тощо.

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

Як ви вже знаєте, функція обгортки зіпсує номери рядків та імена файлів, тому корисно запобігти використанню функцій обгортки.


Чудово, мені потрібно бути обережним щодо порядку isDebug = trueта debug.js, але ця відповідь справді працює!
Руфус

3
window.debug = window.consoleбуло б трохи чистіше.
Фредрік

@fredrik, тоді мені потрібно буде "реалізувати" всі функції члена 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.");

EDIT: суть, включена нижче

/*
 * 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"
})();


Ця відповідь має лише 3 оновлення, але неймовірно більш насичена та чиста, ніж будь-яка інша сторінка
Том,

однак, схоже, всі корисні частини знаходяться на зовнішній носії.
Райан Ліч

3

Ідея з палітуркою Function.prototype.bindгеніальна. Ви також можете використовувати npm-бібліотеки рядків-реєстраторів . Він показує вихідні файли:

Створіть реєстратора будь-хто раз у своєму проекті:

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

Друкувати журнали:

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

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


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:), що не так добре читати плюс вона не пов'язана. Все-таки хороша робота, таким чином, виплата.
Фредерік Лейтенбергер

Так, хоча це було б ідеально, AFAIK не може підробити посилання на ім'я файлу, яке відображається в консолі ...
Brett Zamir

@BrettZamir розмістив питання про це коді тут: stackoverflow.com/questions/52618368 / ...
Mahks

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 () повертав функцію, яка виконується там, де вам потрібно - налагодження (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


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)
)()

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 один) я створив це для Node ES6, але швидкий тест показав хороші результати і в браузері. Я просто передаю іншу функцію як орієнтир.

let debug = () => {};
if (process.argv.includes('-v')) {
    debug = console.log;
    // debug = console; // For full object access
}

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' );
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.