Um wrapper adequado para console.log com o número de linha correto?

132

Agora estou desenvolvendo um aplicativo e coloque um isDebugswitch global . Gostaria de embrulhar console.logpara uso mais 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.');

Então eu recebo esse resultado no console do Firefox.

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

E se eu quiser registrar com o número da linha onde debug()é chamado, como info: Here is a msg. main.js (line 2)?

Rufus
fonte
Você pode usar console.loginformações, console.warnavisos e console.errorerros, em vez de adicionar algo por console.logmeio de uma função de wrapper.
Alvin Wong
2
@AlvinWong Sim, eu sei disso, mas o problema é que preciso de uma opção de depuração global, que controla se consoleprecisa ser usada. Para atingir esse objetivo, um invólucro parece ser o único caminho?
Rufus
Para o Google Chrome, consulte stackoverflow.com/a/25729203/1429301 No seu caso, o padrão seria debug.js
Frison Alexander

Respostas:

117

Esta é uma pergunta antiga e todas as respostas fornecidas são excessivamente hackey, apresentam problemas principais no navegador e não fornecem nada de super útil. Essa solução funciona em todos os navegadores e relata todos os dados do console exatamente como deveriam. Não são necessários hacks e uma linha de código Confira o código de acesso .

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

Crie a opção assim:

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

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

Em seguida, basta ligar da seguinte maneira:

debug('This is happening.')

Você pode até assumir o console.log com um switch como este:

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

Se você quiser fazer algo útil com isso ... Você pode adicionar todos os métodos do console e agrupá-los em uma função reutilizável que fornece não apenas controle global, mas também o nível de classe:

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

Agora você pode adicioná-lo às suas aulas:

var MyClass = function() {
  this.isDebug = true //local state
  this.debug = Debugger(isDebug, this)
  this.debug.warn('It works in classses')
}
arctelix
fonte
16
Corrija-me se estiver errado, mas isso não permite que você adicione nenhuma funcionalidade adicional, correto? Você está essencialmente fazendo o alias do objeto do console? Um exemplo bruto - não há como console.log () o evento duas vezes para cada debug.log ()?
AB Carroll
3
@ABCarroll Você poderia console.logvincular duas vezes uma log()função personalizada contendo duas chamadas para console.log, no entanto, os números de linha refletiriam a linha console.logna qual realmente reside, e não onde debug.logé chamada. No entanto, você pode fazer coisas como adicionar prefixos / sufixos dinâmicos etc. Também existem maneiras de compensar o problema do número da linha, mas acho que é outra pergunta. Confira este projeto em busca de um exemplo: github.com/arctelix/iDebugConsole/blob/master/README.md
arctelix
2
Este método não funciona no Firefox da versão 47 a 49 inclusive. E foi corrigido apenas na versão 50.0a2. Bem, o FF50 será lançado em duas semanas, mas passo várias horas para perceber por que não está funcionando. Então, acho que essa informação pode ser útil para alguém. link
Vladimir Liubimov 01/11/16
Acredito que o @ABCarroll quis dizer que tudo dentro da instância não pode ser usado em tempo de execução. para outra instância, o estado global pode ser definido apenas na instanciação; portanto, se você mudar posteriormente this.isDebugpara false, não importará. Só não sei se há alguma maneira de contornar isso, talvez seja por design. Nesse sentido, o isDebugargumento é bastante enganador vare deve ser um pouco const.
Cregox #
2
Isso não responde à pergunta "E se eu quiser fazer logon com o número da linha na qual debug () é chamado?"
23419
24

Gostei da resposta do @ fredrik , então juntei-a com outra resposta que divide o rastreamento da pilha do Webkit e a mesclei com o invólucro seguro do console.log do @ PaulIrish . "Padroniza" o filename:lineobjeto "especial" para que ele se destaque e tenha a mesma aparência no FF e no Chrome.

Teste no violino: 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

Isso também funciona no nó e você pode testá-lo com:

// 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
fonte
uma resposta um pouco mais avançado para a conta para o extra 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 - provavelmente depende do navegador ou que eu escrevi isso há 2 anos e o (s) navegador (s) foram alterados. qual é o seu cenário?
Drzaus
1
um de meus colegas copiou seu código em nosso projeto. Ele quebrou o site no IE11 e no Safari 5. Não tenho certeza sobre outras versões desses navegadores. Talvez você adicione uma verificação para futuros copiadores?
sigod
1
@sigod e agora? adicionado if(!stack) return '?'ao método que falha, ao invés de onde ele é chamado (por isso, se alguém usa o método em si que está "protegido" também)
drzaus
18

Você pode manter os números de linha e gerar o nível do log com algum 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)

Indo um passo adiante, você pode fazer uso das consoledistinções de erro / aviso / informação e ainda ter níveis personalizados. Tente!

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
fonte
1
Eu tenho tentado por um tempo agora para prefixar automaticamente a saída console.debug(...)com function namee arguments- quaisquer pensamentos sobre como fazer isso?
precisa
3
Eu estive olhando para a infinidade de wrappers de console / calços / etc. e este é o primeiro que encontrei que combina a preservação dos números de linha com a personalização da saída. É um uso inteligente do fato de que .bind também faz alguns testes para você, você pode vincular um ou mais argumentos além do contexto . Você poderia dar um passo adiante e passar uma função noop com um método .toString que poderia executar código quando o método log for chamado! Veja este jsfiddle
Sam Hasler 08/08
2
Talvez não em todos os navegadores (ainda não pesquisamos), mas a substituição %spor %ono Chrome imprimirá os parâmetros da maneira que você esperaria (objetos são expansíveis, números e seqüências de caracteres são coloridos etc.).
Anson
adoro esta solução. Fiz algumas alterações que funcionam melhor para o meu aplicativo, mas a maior parte ainda está intacta e funcionando perfeitamente. Obrigado
Ward
9

De: como obter o número da linha de função do chamador JavaScript? Como obter o URL de origem do chamador JavaScript? o Errorobjeto possui uma propriedade de número de linha (em FF). Então, algo assim deve funcionar:

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

No navegador Webkit, você possui err.stackuma sequência que representa a pilha de chamadas atual. Ele exibirá o número da linha atual e mais informações.

ATUALIZAR

Para obter o número da roupa correto, é necessário chamar o erro nessa linha. 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
fonte
1
new Error();me dá o contexto em que ele é executado; se eu colocá-lo debug.js, eu receberei info: Here is a msg. file: http://localhost/js/debug.js line:7.
Rufus
1
Qual o sentido disso Log = Error? Você ainda está modificando a classe Error, certo?
drzaus
Combinou sua resposta com algumas outras - veja abaixo stackoverflow.com/a/14841411/1037948
drzaus
8

Uma maneira de manter o número da linha está aqui: https://gist.github.com/bgrins/5108712 . É mais ou menos resumido a isso:

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

Você poderia envolver isso com isDebuge definir window.logpara function() { }se você não está depurando.

Brian Grinstead
fonte
7

Você pode passar o número da linha para o seu método de depuração, assim:

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

Aqui, (new Error).lineNumbervocê forneceria o número da linha atual no seu javascriptcódigo.

Subodh
fonte
2
Um pouco detalhado, não é?
Rufus
2
Eu acho que é suficiente para responder à sua consulta. :)
Subodh
1
a propriedade lineNumber não é padrão e, atualmente, só funciona no firefox, veja aqui
Matthias
6

O Chrome Devtools permite alcançar isso com o Blackboxing . Você pode criar o wrapper console.log que pode ter efeitos colaterais, chamar outras funções, etc., e ainda manter o número da linha que chamou a função de wrapper.

Basta colocar um pequeno wrapper console.log em um arquivo separado, por exemplo

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

Nomeie algo como log-blackbox.js

Em seguida, vá para as configurações do Chrome Devtools e localize a seção "Caixa preta", adicione um padrão para o nome do arquivo que você deseja colocar na caixa preta, nesse caso log-blackbox.js

kzahel
fonte
Nota: Verifique se você não tem qualquer código que você iria querer aparecer no rastreamento de pilha no mesmo arquivo, como também será removido do traço.
jamesthollowell
6

Encontrei uma solução simples para combinar a resposta aceita (vinculação ao console.log / error / etc) com alguma lógica externa para filtrar o que realmente está registrado.

// 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
  • Observe que console.assert usa o log condicional.
  • Verifique se as ferramentas de desenvolvimento do seu navegador mostram todos os níveis de mensagem!
Jacob Phillips
fonte
Porque não fornece nenhum número de linha nem exemplos de trabalho mostrando o nível do log.
Not2qubit
O número da linha será o mesmo que se você estivesse usando o console diretamente. Atualizei a resposta com exemplos de uso. Ele não tem muitos votos porque eu atendi dois anos mais tarde :)
Jacob Phillips
4

Se você simplesmente deseja controlar se a depuração é usada e tem o número de linha correto, você pode fazer isso:

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

Quando você precisar acessar para depurar, faça o seguinte:

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

Se isDebug == true, os números de linha e nomes de arquivos mostrados no console estarão corretos, porque debug.logetc é realmente um alias deconsole.log etc.

Se isDebug == false, nenhuma mensagem de depuração é mostrada, porque debug.logetc simplesmente não faz nada (uma função vazia).

Como você já sabe, uma função de invólucro irá atrapalhar os números de linha e nomes de arquivos, portanto, é uma boa idéia evitar o uso de funções de invólucro.

Alvin Wong
fonte
Ótimo, preciso ter cuidado com a ordem de isDebug = truee debug.js, mas essa resposta funciona!
Rufus
3
window.debug = window.consoleseria um pouco mais limpo.
Fredrik
@fredrik então precisarei "implementar" todas as funções membro se isDebug == false. : {
Alvin Wong
@AlvinWong Acabo de mencionar se isDebug===true. Ou evento para isso: jsfiddle.net/fredrik/x6Jw5
fredrik
4

As soluções de rastreamento de pilha exibem o número da linha, mas não permitem clicar para ir para a origem, o que é um grande problema. A única solução para manter esse comportamento é vincular à função original.

A ligação impede incluir lógica intermediária, porque essa lógica interferiria nos números de linha. No entanto, redefinindo as funções associadas e jogando com a substituição de cadeias de console , ainda é possível um comportamento adicional.

Esta lista mostra uma estrutura de registro minimalista que oferece módulos, níveis de registro, formatação e números de linhas clicáveis ​​adequados em 34 linhas. Use-o como base ou inspiração para suas próprias necessidades.

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

EDIT: essência incluída abaixo

/*
 * 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
fonte
Esta resposta só tem 3 upvotes mas é incrivelmente mais rico e limpo do que qualquer outro na página
Tom
no entanto, parece que todas as partes úteis estão em uma essência externa.
Ryan The Leach
3

A ideia com o bind Function.prototype.bindé brilhante. Você também pode usar o npm library lines-logger . Ele mostra os arquivos de origem:

Crie qualquer logger uma vez no seu projeto:

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

Imprimir registros:

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

insira a descrição da imagem aqui

deathangel908
fonte
2

Aqui está uma maneira de manter suas consoleinstruções de log existentes enquanto adiciona um nome de arquivo e número de linha ou outras informações de rastreamento de pilha na saída:

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

Em seguida, use-o assim:

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

Isso funciona no Firefox, Opera, Safari, Chrome e IE 10 (ainda não testado no IE11 ou Edge).

Brett Zamir
fonte
Bom trabalho, mas ainda não 100% do que eu preciso. Gostaria de ter as informações de nome de arquivo e número de linha no lado direito da tela do console, onde é possível clicar para abrir a fonte. Essa solução mostra as informações como parte da mensagem (assim:) my test log message (myscript.js:42) VM167 mypage.html:15, que não é tão boa de ler e não está vinculada. Ainda bom trabalho, portanto, um voto positivo.
Frederic Leitenberger
Sim, enquanto que seria o ideal, não há nenhuma maneira, AFAIK, para falsificar o link nome de arquivo que aparece no console ...
Brett Zamir
O @BrettZamir postou uma pergunta sobre este código aqui: 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.'));

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

Mas o extra evalé necessário, pena.

Rufus
fonte
2
eval é mau! Então todo mal.
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
fonte
Isto não parece mostrar o número da linha original de onde logé chamado
ragamufin
Tenho quase certeza de que funcionou quando testei, mas substituí o código pela versão "completa" da mesma página. Trabalhou pelo menos no Chrome 45.
Timo Kähkönen
Entendido. Com as alterações que você tem agora, é essencialmente o mesmo que algumas das outras respostas e obras. Fiquei curioso sobre o seu código anterior, porque você tinha uma aplicação no final, o que levantou possibilidades interessantes para eu usar isso por mais, mas como ele não mostrava o número da linha, voltei à estaca zero. Obrigado embora!
Ragamufin 13/10/2015
1

Ultimamente, estive analisando esse problema. Precisava de algo muito simples para controlar o log, mas também para reter os números de linha. Minha solução não parece tão elegante no código, mas fornece o que é necessário para mim. Se alguém for cuidadoso o suficiente com fechamentos e retenção.

Adicionei um pequeno invólucro ao início do aplicativo:

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

Para que mais tarde eu possa simplesmente fazer:

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

Eu testei o firefox e o crome, e os dois navegadores parecem mostrar o log do console como pretendido. Se você preencher assim, sempre poderá estender o método 'd' e passar outros parâmetros para ele, para que ele possa fazer algum log extra.

Ainda não encontrei nenhuma desvantagem séria para minha abordagem, exceto a linha feia no código para registro.

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

aqui estava a minha solução sobre esta questão. quando você chama o método: log, ele imprime o número da linha em que você imprime seu log

Feng Li
fonte
1

Uma pequena variação é fazer com que debug () retorne uma função, que é executada onde você precisa - debug (message) (); e mostra adequadamente o número da linha e o script de chamada corretos na janela do console, permitindo variações como redirecionar como alerta ou salvar no arquivo.

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 ele retorna uma função, essa função precisa ser executada na linha de depuração com () ;. Em segundo lugar, a mensagem é enviada para a função de depuração, e não para a função retornada, permitindo o pré-processamento ou a verificação de que você pode precisar, como verificar o estado do nível do log, tornar a mensagem mais legível, ignorando tipos diferentes ou apenas relatando itens atender aos critérios de nível de log;

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

Você pode simplificar a lógica aqui. Isso pressupõe que seu sinalizador de depuração global NÃO seja dinâmico e definido no carregamento do aplicativo ou passado como alguma configuração. Destina-se a ser usado para sinalização de ambiente (por exemplo, imprimir apenas no modo dev e não na produção)

JS de baunilha:

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

Tudo o que você precisa fazer agora é substituir todas as referências de console pelo Logger

disfuncional
fonte
1

Esta implementação é baseada na resposta selecionada e ajuda a reduzir a quantidade de ruído no console de erros: 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
fonte
0

Eu achei algumas das respostas para esse problema um pouco complexas para minhas necessidades. Aqui está uma solução simples, renderizada em Coffeescript. É adaptado da versão de Brian Grinstead aqui

Ele assume o objeto de console 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)
)()
sandover
fonte
0

A maneira que resolvi foi criar um objeto, criar uma nova propriedade no objeto usando Object.defineProperty () e retornar a propriedade do console, que foi usada como função normal, mas agora com a capacidade estendida.

var c = {};
var debugMode = true;

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

Em seguida, para definir uma propriedade, basta ...

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

E agora você pode usar sua função como

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

Com base em outras respostas (principalmente no @arctelix one), criei isso para o Nó ES6, mas um teste rápido também mostrou bons resultados no navegador. Estou apenas passando a outra função como referência.

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

Aqui está a minha função de logger (com base em algumas das respostas). Espero que alguém possa fazer uso dele:

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

Exemplos:

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