¿Un contenedor apropiado para console.log con el número de línea correcto?

132

Ahora estoy desarrollando una aplicación y coloco un isDebuginterruptor global . Me gustaría envolver console.logpara un uso más conveniente.

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

Luego obtengo este resultado en la consola de Firefox.

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

¿Qué sucede si quiero iniciar sesión con el número de línea donde me debug()llaman, como info: Here is a msg. main.js (line 2)?

Rufus
fuente
Puede usar console.logpara información, console.warnpara advertencia y console.errorpara error, en lugar de agregar algo a console.logtravés de una función de contenedor.
Alvin Wong
2
@AlvinWong Sí, lo sé, pero el problema es que necesito un interruptor de depuración global, que controla si consolees necesario usarlo. Para lograr tal objetivo, un contenedor parece ser la única manera?
Rufus
Para Google Chrome, consulte stackoverflow.com/a/25729203/1429301 En su caso, el patrón sería debug.js
Frison Alexander

Respuestas:

117

Esta es una vieja pregunta y todas las respuestas proporcionadas son excesivamente hackeadas, tienen MAYORES problemas de navegador cruzado y no proporcionan nada súper útil. Esta solución funciona en todos los navegadores e informa todos los datos de la consola exactamente como debería. No se requieren hacks y una línea de código Echa un vistazo al codepen .

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

Crea el interruptor así:

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

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

Luego simplemente llame de la siguiente manera:

debug('This is happening.')

Incluso puede hacerse cargo de console.log con un interruptor como este:

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

Si desea hacer algo útil con eso ... Puede agregar todos los métodos de la consola y envolverlo en una función reutilizable que le brinde no solo control global, sino también nivel de clase:

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

Ahora puedes agregarlo a tus clases:

var MyClass = function() {
  this.isDebug = true //local state
  this.debug = Debugger(isDebug, this)
  this.debug.warn('It works in classses')
}
arctelix
fuente
16
Corrígeme si me equivoco, pero esto no te permite agregar ninguna funcionalidad adicional, ¿correcto? ¿Esencialmente solo estás aliasando el objeto de la consola? Un ejemplo burdo: no hay forma de consolar.log () el evento dos veces por cada debug.log ()?
AB Carroll
3
@ABCarroll Podría hacerlo console.logdos veces vinculando una log()función personalizada que contenga dos llamadas a console.log, sin embargo, los números de línea reflejarían la línea en la que console.logrealmente reside, no dónde debug.logse llama. Sin embargo, podría hacer cosas como agregar prefijos / sufijos dinámicos, etc. También hay formas de compensar el problema del número de línea, pero esa es otra pregunta, creo. Vea este proyecto para ver un ejemplo: github.com/arctelix/iDebugConsole/blob/master/README.md
arctelix
2
Este método no funciona en Firefox desde la versión 47 a la 49 inclusive. Y se corrigió solo en la versión 50.0a2. Bueno, FF50 se lanzará en 2 semanas, pero paso varias horas para darme cuenta de por qué no funciona. Así que creo que esta información puede ser útil para alguien. enlace
Vladimir Liubimov
Creo que lo que quería decir @ABCarroll es que todo lo que está dentro de la instancia no se puede usar en tiempo de ejecución. para otro caso, el estado global solo se puede definir en la instanciación, por lo que si luego cambia this.isDebuga false, no importará. Simplemente no sé si hay alguna forma de evitar esto, tal vez sea por diseño. En ese sentido, el isDebuges bastante engañoso vary debería ser un constlugar.
cregox
2
Esto no responde a la pregunta "¿Qué sucede si quiero iniciar sesión con el número de línea donde se llama a debug ()?"
technomage
24

Me gustó la respuesta de @ fredrik , así que la enrolé con otra respuesta que divide la traza de pila de Webkit , y la fusioné con el contenedor seguro console.log de @ PaulIrish . "Estandariza" el filename:linea un "objeto especial" para que se destaque y se vea casi igual en FF y Chrome.

Pruebas en violín: 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

Esto también funciona en el nodo, y puede probarlo con:

// 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
fuente
una respuesta un poco más avanzada para dar cuenta de los extras consolemétodos como warn, error, etc. - stackoverflow.com/a/14842659/1037948
drzaus
1
var line = stack.split('\n')[2];-'undefined' is not an object
sigod
@sigod: probablemente depende del navegador o de que escribí esto hace 2 años y los navegadores han cambiado. ¿Cuál es tu escenario?
drzaus
1
Uno de mis colegas copió su código en nuestro proyecto. Rompió el sitio en IE11 y Safari 5. No estoy seguro acerca de otras versiones de este navegador. ¿Tal vez agregará un cheque para futuros copy-pasters?
sigod
1
@sigod, ¿qué pasa ahora? agregado if(!stack) return '?'al método que falla, en lugar de donde se llama (así que si alguien usa el método en sí mismo, también está "protegido")
drzaus
18

Puede mantener los números de línea y generar el nivel de registro con un uso inteligente de 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)

Yendo un paso más allá, puede hacer uso de las consoledistinciones de error / advertencia / información y aún tener niveles personalizados. ¡Intentalo!

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
fuente
1
He intentado durante un tiempo prefijar automáticamente la salida de console.debug(...)con function namey arguments, ¿alguna idea sobre cómo hacerlo?
Daniel Sokolowski
3
He estado mirando la multitud de envoltorios de consolas / cuñas / etc. y este es el primero con el que me encuentro que combina la preservación de los números de línea con la personalización de la salida. Es un uso inteligente del hecho de que .bind también hace un poco de curry por ti, puedes vincular uno o más argumentos además del contexto . ¡Podría ir un paso más allá y pasarle una función noop con un método .toString que podría ejecutar código cuando se llama al método de registro! Ver este jsfiddle
Sam Hasler
2
Tal vez no en todos los navegadores (no lo he examinado), pero reemplazarlo %scon %oChrome imprimirá los parámetros de la manera que esperaría (los objetos son expandibles, los números y las cadenas están coloreados, etc.).
anson
Me encanta esta solución. Hice algunos cambios que funcionan mejor para mi aplicación, pero la mayor parte todavía está intacta y funciona maravillosamente. Gracias
Ward
9

De: ¿Cómo obtener el número de línea de la función de llamada de JavaScript? ¿Cómo obtener la URL fuente de la persona que llama JavaScript? el Errorobjeto tiene una propiedad de número de línea (en FF). Entonces algo como esto debería funcionar:

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

En el navegador Webkit, tiene err.stackuna cadena que representa la pila de llamadas actual. Mostrará el número de línea actual y más información.

ACTUALIZAR

Para obtener el número de ropa correcto, debe invocar el error en esa línea. Algo como:

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);
fredrik
fuente
1
new Error();me da el contexto donde se ejecuta, si lo pongo debug.js, lo obtendré info: Here is a msg. file: http://localhost/js/debug.js line:7.
Rufus
1
¿De qué sirve Log = Error? Todavía estás modificando la clase Error, ¿verdad?
drzaus
Combinó su respuesta con un par de otros; consulte a continuación stackoverflow.com/a/14841411/1037948
drzaus
8

Una forma de mantener el número de línea está aquí: https://gist.github.com/bgrins/5108712 . Se reduce más o menos a esto:

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

Usted podría terminar con esto con isDebugy establecer window.loga function() { }si no está depurando.

Brian Grinstead
fuente
7

Puede pasar el número de línea a su método de depuración, así:

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

Aquí, (new Error).lineNumberle daría el número de línea actual en su javascriptcódigo.

Subodh
fuente
2
Un poco detallado, ¿no?
Rufus
2
Creo que es suficiente para responder a su consulta. :)
Subodh
1
la propiedad lineNumber no es estándar, y solo funciona en firefox en este momento, vea aquí
Matthias
6

Chrome Devtools te permite lograr esto con Blackboxing . Puede crear el contenedor console.log que puede tener efectos secundarios, llamar a otras funciones, etc., y aún conservar el número de línea que llamó a la función de contenedor.

Simplemente coloque un pequeño contenedor console.log en un archivo separado, por ejemplo

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

Nómbrelo como log-blackbox.js

Luego vaya a la configuración de Chrome Devtools y busque la sección "Blackboxing", agregue un patrón para el nombre de archivo que desea blackbox, en este caso log-blackbox.js

kzahel
fuente
Nota: Asegúrese de que usted no tiene ningún código que le desee que aparezca en el seguimiento de pila en el mismo archivo, ya que también se eliminará de la traza.
jamesthollowell
6

Encontré una solución simple para combinar la respuesta aceptada (enlace a console.log / error / etc) con alguna lógica externa para filtrar lo que realmente se registra.

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

Uso:

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
  • Tenga en cuenta que console.assertutiliza el registro condicional.
  • ¡Asegúrese de que las herramientas de desarrollo de su navegador muestren todos los niveles de mensajes!
Jacob Phillips
fuente
Porque no da ningún número de línea ni ejemplos de trabajo que muestren el nivel de registro.
not2qubit
El número de línea será el mismo que si usara la consola directamente. Actualicé la respuesta con ejemplos de uso. No tiene muchos votos porque lo respondí dos años después :)
Jacob Phillips
4

Si simplemente desea controlar si se utiliza la depuración y tiene el número de línea correcto, puede hacerlo en su lugar:

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(){}
    };
}

Cuando necesite acceso a la depuración, puede hacer esto:

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

Si isDebug == true, los números de línea y los nombres de archivo que se muestran en la consola serán correctos, porque debug.logetc. es en realidad un alias deconsole.log etc.

Si isDebug == falseno se muestran mensajes de depuración, porquedebug.log etc. simplemente no hace nada (una función vacía).

Como ya sabe, una función de contenedor desordenará los números de línea y los nombres de archivo, por lo que es una buena idea evitar el uso de funciones de contenedor.

Alvin Wong
fuente
Genial, tengo que tener cuidado con el orden de isDebug = truey debug.js, ¡pero esta respuesta funciona!
Rufus
3
window.debug = window.consoleSería un poco más limpio.
fredrik
@fredrik entonces tendré que "implementar" todas las funciones miembro si isDebug == false. : {
Alvin Wong
@AlvinWong Acabo de menat por si isDebug===true. O evento a esto: jsfiddle.net/fredrik/x6Jw5
fredrik
4

Las soluciones de seguimiento de pila muestran el número de línea pero no permiten hacer clic para ir a la fuente, lo cual es un problema importante. La única solución para mantener este comportamiento es unirse a la función original.

La vinculación evita incluir una lógica intermedia, porque esta lógica alteraría los números de línea. Sin embargo, al redefinir las funciones enlazadas y jugar con la sustitución de cadenas de la consola , todavía es posible algún comportamiento adicional.

Esta esencia muestra un marco de registro minimalista que ofrece módulos, niveles de registro, formato y números de línea en los que se puede hacer clic en 34 líneas. Úselo como base o inspiración para sus propias necesidades.

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

EDITAR: esencia incluida a continuación

/*
 * 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
fuente
Esta respuesta solo tiene 3 votos a favor, pero es increíblemente más rica y limpia que cualquier otra en la página
Tom
Sin embargo, parece que todas las partes útiles están en una esencia externa.
Ryan The Leach
3

La idea con Bind Function.prototype.bindes brillante. También puede usar npm library lines-logger . Muestra los archivos fuente de origen:

Crear registrador cualquiera una vez en su proyecto:

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

Imprimir registros:

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

ingrese la descripción de la imagen aquí

deathangel908
fuente
2

Aquí hay una manera de mantener sus consoledeclaraciones de registro existentes mientras agrega un nombre de archivo y número de línea u otra información de seguimiento de pila en la salida:

(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;
  }, {}));
}());

Entonces úsalo así:

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

Esto funciona en Firefox, Opera, Safari, Chrome e IE 10 (aún no probado en IE11 o Edge).

Brett Zamir
fuente
Buen trabajo, pero aún no es 100% lo que necesito. Me gustaría tener la información de nombre de archivo y número de línea en el lado derecho de la vista de consola, donde se puede hacer clic para abrir la fuente. Esta solución muestra la información como parte del mensaje (como esta:) my test log message (myscript.js:42) VM167 mypage.html:15, que no es tan buena de leer y además no está vinculada. Sigue siendo un buen trabajo, por lo tanto, un voto positivo.
Frederic Leitenberger
Sí, aunque eso sería ideal, AFAIK no puede suplantar el enlace del nombre de archivo que aparece en la consola ...
Brett Zamir
@BrettZamir publicó una pregunta sobre este código aquí: 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.'));

Esto me dará info: "Here is a msg." main.js(line:2).

Pero el extra evales necesario, lástima.

Rufus
fuente
2
eval es malvado! Entonces cada maldad.
fredrik
1

Código de 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);
Timo Kähkönen
fuente
Esto no parece mostrar el número de línea original desde donde logse llama
ragamufin
Estoy casi seguro de que funcionó cuando lo probé, pero reemplacé el código con la versión "completa" de la misma página. Trabajó al menos en Chrome 45.
Timo Kähkönen
Entendido. Con los cambios que tiene ahora, es esencialmente lo mismo que algunas de las otras respuestas y funciona. Solo tenía curiosidad sobre su código anterior porque tenía una solicitud al final que me planteó posibilidades interesantes de usar esto para más, pero como no mostraba el número de línea, volví al punto de partida. Gracias sin embargo!
ragamufin
1

He estado revisando este problema yo mismo últimamente. Necesitaba algo muy sencillo para controlar el registro, pero también para retener los números de línea. Mi solución no se ve tan elegante en el código, pero proporciona lo que se necesita para mí. Si uno es lo suficientemente cuidadoso con los cierres y retenciones.

Agregué un pequeño contenedor al comienzo de la aplicación:

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

Para que luego pueda simplemente hacer:

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

Lo probé en Firefox y Crome, y ambos navegadores parecen mostrar el registro de la consola como se esperaba. Si rellena así, siempre puede extender el método 'd' y pasarle otros parámetros, de modo que pueda realizar un registro adicional.

Todavía no he encontrado inconvenientes serios para mi enfoque, excepto la línea fea en el código para iniciar sesión.

Vladimir M
fuente
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');

Aquí estaba mi solución sobre esta pregunta. cuando llame al método: log, imprimirá el número de línea donde imprime su log

Feng Li
fuente
1

Una pequeña variación es hacer que debug () devuelva una función, que luego se ejecuta donde la necesita: debug (message) (); y muestra correctamente el número de línea correcto y la secuencia de comandos de llamada en la ventana de la consola, al tiempo que permite variaciones como redireccionar como alerta o guardar en un archivo.

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() {});
  }

}

Como devuelve una función, esa función debe ejecutarse en la línea de depuración con () ;. En segundo lugar, el mensaje se envía a la función de depuración, en lugar de a la función devuelta, lo que permite el procesamiento previo o la verificación que pueda necesitar, como verificar el estado del nivel de registro, hacer que el mensaje sea más legible, omitir diferentes tipos o solo informar elementos cumplir los criterios de nivel de registro;

debug(message, "serious", 1)();
debug(message, "minor", 4)();
Saul Dobney
fuente
1

Podrías simplificar la lógica aquí. Esto supone que su marca de depuración global NO es dinámica y se establece en la carga de la aplicación o se pasa como alguna configuración. Esto está destinado a ser utilizado para marcar el entorno (por ejemplo, solo imprimir cuando está en modo dev y no producción)

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

Módulo:

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

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

export default Logger;

Angular 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;
    }
  ]);

Todo lo que necesita hacer ahora es reemplazar todas las referencias de la consola con Logger

disfunc
fuente
1

Esta implementación se basa en la respuesta seleccionada y ayuda a reducir la cantidad de ruido en la consola de error: 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);
Brian
fuente
0

Algunas de las respuestas a este problema me parecieron demasiado complejas para mis necesidades. Aquí hay una solución simple, presentada en Coffeescript. Está adaptado de la versión de Brian Grinstead aquí

Asume el objeto de consola global.

# 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)
)()
entrega
fuente
0

La forma en que lo resolví fue crear un objeto, luego crear una nueva propiedad en el objeto usando Object.defineProperty () y devolver la propiedad de la consola, que luego se usó como la función normal, pero ahora con la habilidad extendida.

var c = {};
var debugMode = true;

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

Luego, para definir una propiedad que acaba de hacer ...

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

Y ahora puedes usar tu función como

c.error("Error!");
Abel Rodríguez
fuente
0

Basado en otras respuestas (principalmente @arctelix one), creé esto para el Nodo ES6, pero una prueba rápida también mostró buenos resultados en el navegador. Solo estoy pasando la otra función como referencia.

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

Aquí está mi función de registro (basada en algunas de las respuestas). Espero que alguien pueda usarlo:

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;
        }
    };
}

Ejemplos:

log( 'warn', 'log message', 'my_function' );
log( 'info', 'log message' );
mar
fuente