Como obter o número da linha da função do chamador JavaScript? Como obter o URL da fonte do chamador JavaScript?

109

Estou usando o seguinte para obter o nome da função do chamador JavaScript:

var callerFunc = arguments.callee.caller.toString();
callerFuncName = (callerFunc.substring(callerFunc.indexOf("function") + 8, callerFunc.indexOf("(")) || "anoynmous")

Existe uma maneira de descobrir o número da linha a partir do qual o método foi chamado?

Além disso, existe uma maneira de obter o nome do arquivo JavaScript a partir do qual o método foi chamado? Ou o URL de origem?

Tal
fonte
2
Não acho que isso seja possível no IE, senão teríamos uma maneira de contornar as mensagens de erro CRAPPY que não fornecem detalhes. Mas se for possível eu ADORARIA saber também!
Zoidberg
Sim. Aqui está uma função para vários navegadores que usa os métodos proprietários de cada navegador: github.com/eriwen/javascript-stacktrace [link fixo]
scotts

Respostas:

99

Isso funciona para mim em chrome / QtWebView

function getErrorObject(){
    try { throw Error('') } catch(err) { return err; }
}

var err = getErrorObject();
var caller_line = err.stack.split("\n")[4];
var index = caller_line.indexOf("at ");
var clean = caller_line.slice(index+2, caller_line.length);
Nathan Landis
fonte
Funciona no nó também. Coloque isso dentro de sua função log () personalizada (que adiciona quaisquer outras soluções úteis de que você precisa - por exemplo, corrigido para registro de array do Chrome) e ainda números de linha de onde quer que você tenha chamado log ().
mikemaccana,
60
Não precisa lançar o erro; simplesmente criá-lo é suficiente:var caller_line = (new Error).stack.split("\n")[4]
ELLIOTTCABLE
2
Esta sugestão foi mesclada com outra resposta semelhante para obter uma resposta "padronizada" do FF / Webkit - consulte stackoverflow.com/a/14841411/1037948
drzaus
1
Funciona no PhantomJS também, mas você tem que jogá-lo, senão o atributo "stack" não será definido no erro.
Joshua Richardson
1
@ELLIOTTCABLE na verdade, em alguns navegadores como o iOS safari, você precisa lançar a exceção! Então, por que não fazer?
arctelix
26

A solução de kangax introduz um escopo try..catch desnecessário. Se precisar acessar o número da linha de algo em JavaScript (desde que esteja usando Firefox ou Opera), basta acessar (new Error).lineNumber.

Eli Gray
fonte
11
Oi, obrigado por esse addon. você sabe se é possível obter o número da linha da chamada anterior? Digamos que o método A chame B, e agora em BI gostaria de saber em qual linha sob A a chamada foi feita?
Tal de
85
Isso está marcado, mas não responde à pergunta, que é como obter o número da linha da função do chamador .
mikemaccana,
3
Além disso, isso é extremamente limitado. A melhor solução é lançar um erro e usar regex no error.stack que está disponível em todos os navegadores modernos. Você pode extrair facilmente esse caminho, arquivo, linha e coluna. Sem problemas.
arctelix
13

Fiquei surpreso com o fato de a maioria dessas respostas presumir que você queria tratar um erro em vez de apenas gerar rastreamentos de depuração úteis para casos normais também.

Por exemplo, gosto de usar um console.logwrapper como este:

consoleLog = function(msg) {//See https://stackoverflow.com/a/27074218/470749
    var e = new Error();
    if (!e.stack)
        try {
            // IE requires the Error to actually be thrown or else the 
            // Error's 'stack' property is undefined.
            throw e;
        } catch (e) {
            if (!e.stack) {
                //return 0; // IE < 10, likely
            }
        }
    var stack = e.stack.toString().split(/\r\n|\n/);
    if (msg === '') {
        msg = '""';
    }
    console.log(msg, '          [' + stack[1] + ']');        
}

Isso acaba imprimindo uma saída como a seguinte no meu console:

1462567104174 [getAllPosts@http://me.com/helper.js:362:9]

Consulte https://stackoverflow.com/a/27074218/ e também Um wrapper adequado para console.log com o número de linha correto?

Ryan
fonte
1
funciona para o navegador firefox, mas não funciona para node.js.
zíper de
1
sob o nó, você deve registrar a pilha [2]
monika mevenkamp
5

Isso geralmente é alcançado lançando um erro do contexto atual; em seguida, analisando o objeto de erro para propriedades como lineNumbere fileName(que alguns navegadores têm)

function getErrorObject(){
  try { throw Error('') } catch(err) { return err; }
}

var err = getErrorObject();

err.fileName;
err.lineNumber; // or `err.line` in WebKit

Não se esqueça de que a callee.callerpropriedade está obsoleta (e nunca foi realmente na 3ª ed. ECMA em primeiro lugar).

Lembre-se também de que a descompilação da função é especificada para ser dependente da implementação e, portanto, pode gerar resultados bastante inesperados. Eu escrevi sobre isso aqui e aqui .

kangax
fonte
Obrigado, parece um pouco problemático adicionar este código no aplicativo de que preciso. (algum framework de rastreamento js) Você conhece algum outro método que não seja obsoleto que eu possa usar?
Tal de
Você deve ser capaz de inspecionar o objeto de erro sem depender de obsoleto callee.caller.
kangax
Você não precisa lançar o Erro. Basta usar (new Error) .lineNumber para acessar o número da linha atual em um script.
Eli Gray
@Elijah Isso é o que vejo no FF3. O WebKit, por outro lado, é populado lineapenas quando ocorre um erro.
kangax
Qual é o substituto para callee.caller? Se eu precisar obter o nome da função?
Tal de
4

Parece que estou meio atrasado :), mas a discussão é bem interessante, então ... aqui vai ... Supondo que você queira construir um manipulador de erros e esteja usando sua própria classe de manipulador de exceções como:

function  errorHandler(error){
    this.errorMessage = error;
}
errorHandler.prototype. displayErrors = function(){
    throw new Error(this.errorMessage);
}

E você está agrupando seu código assim:

try{
if(condition){
    //whatever...
}else{
    throw new errorHandler('Some Error Message');
}
}catch(e){
    e.displayErrors();
}

Muito provavelmente, você terá o manipulador de erros em um arquivo .js separado.

Você notará que no console de erro do firefox ou do Chrome o número da linha de código (e nome do arquivo) mostrado é a linha (arquivo) que lança a exceção 'Error' e não a exceção 'errorHandler' que você realmente deseja para fazer a depuração fácil. É ótimo lançar suas próprias exceções, mas em grandes projetos, localizá-las pode ser um problema, especialmente se elas tiverem mensagens semelhantes. Então, o que você pode fazer é passar uma referência a um objeto Error vazio real para o seu manipulador de erros, e essa referência conterá todas as informações que você deseja (por exemplo, no firefox você pode obter o nome do arquivo e o número da linha etc. ; no cromo, você obtém algo semelhante se ler a propriedade 'stack' da instância Error). Resumindo, você pode fazer algo assim:

function  errorHandler(error, errorInstance){
    this.errorMessage = error;
    this. errorInstance = errorInstance;
}
errorHandler.prototype. displayErrors = function(){
    //add the empty error trace to your message
    this.errorMessage += '  stack trace: '+ this. errorInstance.stack;
    throw new Error(this.errorMessage);
}

try{
if(condition){
    //whatever...
}else{
    throw new errorHandler('Some Error Message', new Error());
}
}catch(e){
    e.displayErrors();
}

Agora você pode obter o arquivo real e o número da linha que gerou a exceção personalizada.

rusu bogdan
fonte
4

O número da linha é na verdade algo estático, então se você quiser apenas para registro, pode ser pré-processado com algo como gulp. Eu escrevi um pequeno plugin gulp que faz exatamente isso:

var gulp = require('gulp');
var logLine = require('gulp-log-line');
gulp.task('log-line', function() {
    return gulp.src("file.js", {buffer : true})
    //Write here the loggers you use.
        .pipe(logLine(['console.log']))
        .pipe(gulp.dest('./build'))

})

gulp.task('default', ['log-line'])

Isso anexará o nome do arquivo e a linha a todos os logs do console.log, e assim console.log(something)será console.log('filePath:fileNumber', something). A vantagem é que agora você pode concatear seus arquivos, transpilá-los ... e você ainda obterá a linha

Gabriel Furstenheim
fonte
Esta parece ser uma ótima sugestão para situações em que um transpiler é usado (por exemplo, ao usar TypeScript). Obrigado!
Andy King
4

Sei que essa é uma pergunta antiga, mas agora existe um método chamado console.trace("Message")que mostrará o número da linha e a cadeia de chamadas de método que levaram ao log junto com a mensagem que você passou. Mais informações sobre truques de log de javascript estão disponíveis aqui no freecodecamp e nesta postagem do blog médio

Japheth Ongeri - inkalimeva
fonte
3

Se você quiser saber o número da linha para fins de depuração, ou apenas durante o desenvolvimento (por uma razão ou outra), você pode usar o Firebug (uma extensão do Firefox) e lançar uma exceção.

Editar :

Se você realmente precisa fazer isso na produção por algum motivo, pode pré-processar seus arquivos javascript para que cada função acompanhe a linha em que está. Eu sei que alguns frameworks que encontram a cobertura de código usam isso (como JSCoverage ).

Por exemplo, digamos que sua chamada original seja:

function x() {
  1 + 1;
  2 + 2;
  y();
}

Você pode escrever um pré-processador para torná-lo:

function x() {
  var me = arguments.callee;
  me.line = 1;
  1 + 1;
  me.line = 2;
  2 + 2;
  me.line = 3;
  y();
}

Em seguida y(), você pode usar arguments.callee.caller.linepara saber a linha de onde foi chamada, como:

function y() {
  alert(arguments.callee.caller.line);
}
Sinan Taifour
fonte
1
Obrigado, gostaria de estar em produção por motivos de suporte. Eu encontrei um código que permite que você veja a pilha de chamadas de todo o fluxo até o método, mas não tem os números das linhas onde os métodos foram chamados. Acho que uma solução fácil para isso?
Tal de
2

Aqui está o que escrevi com base nas informações encontradas neste fórum:

Isso faz parte de um MyDebugNamespace, Debug é aparentemente reservado e não funcionará como nome de namespace.

    var DEBUG = true;

...

    if (true == DEBUG && !test)
    {
        var sAlert = "Assertion failed! ";
        if (null != message)
            sAlert += "\n" + message;
        if (null != err)
            sAlert += "\n" + "File: " + err.fileName + "\n" + "Line: " + err.lineNumber;
        alert(sAlert);
    }

...

Como ligar:

    MyDebugNamespace.Assert(new Error(""), (null != someVar), "Something is wrong!")

Eu incluí duas funções com número variável de argumentos chamando esse código base em meu namespace para, opcionalmente, omitir mensagem ou erro nas chamadas.

Isso funciona bem com o Firefox, IE6 e Chrome relatando o fileName e lineNumber como indefinidos.

pasx
fonte
2

o código a seguir funciona para mim no Mozilla e no Chrome.

Sua função de log que mostra o nome do arquivo e a linha do chamador.

log: function (arg) {
    var toPrint = [];
    for (var i = 0; i < arguments.length; ++i) {
        toPrint.push(arguments[i]);
    }

    function getErrorObject(){
        try { throw Error('') } catch(err) { return err; }
    }

    var err = getErrorObject(),
        caller;

    if ($.browser.mozilla) {
        caller = err.stack.split("\n")[2];
    } else {
        caller = err.stack.split("\n")[4];
    }

    var index = caller.indexOf('.js');

    var str = caller.substr(0, index + 3);
    index = str.lastIndexOf('/');
    str = str.substr(index + 1, str.length);

    var info = "\t\tFile: " + str;

    if ($.browser.mozilla) {
        str = caller;
    } else {
        index = caller.lastIndexOf(':');
        str = caller.substr(0, index);
    }
    index = str.lastIndexOf(':');
    str = str.substr(index + 1, str.length);
    info += " Line: " + str;
    toPrint.push(info);

    console.log.apply(console, toPrint);
}
Noampz
fonte
Parece que falta algo. Eu recebo:SyntaxError: function statement requires a name,log: function (arg) {
spiderplant0
Eu gosto dessa ideia, mas os números das linhas saem incorretos para mim.
Ryan
2

Minha contribuição para erros personalizados em JavaScript:

  1. Em primeiro lugar, concordo com esse cara @BT em Herdar do objeto Error - onde está a propriedade da mensagem? , temos que construí-lo corretamente (na verdade, você deve usar uma biblioteca de objetos js, minha favorita: https://github.com/jiem/my-class ):

    window.g3 = window.g3 || {};
    g3.Error = function (message, name, original) {
         this.original = original;
         this.name = name || 'Error.g3';
         this.message = message || 'A g3.Error was thrown!';
         (original)? this.stack = this.original.stack: this.stack = null;
         this.message += '<br>---STACK---<br>' + this.stack;
     };
    
     var ClassEmpty = function() {};
     ClassEmpty.prototype = Error.prototype;
     g3.Error.prototype = new ClassEmpty();
     g3.Error.prototype.constructor = g3.Error;
  2. então, devemos definir uma função global de tratamento de erros (opcional) ou eles acabarão no motor:

    window.onerror = printError; 
    function printError(msg, url, line){
        document.getElementById('test').innerHTML = msg+'<br>at: '+url+'<br>line: '+line;
        return true;
    }
  3. finalmente, devemos lançar nossos erros personalizados com cuidado:

    //hit it!
    //throw new g3.Error('Hey, this is an error message!', 'Error.Factory.g3');
    throw new g3.Error('Hey, this is an error message!', 'Error.Factory.g3', new Error());

Apenas, ao passar o terceiro parâmetro, new Error()podemos ver a pilha com os números de função e linha!

Em 2, a função também pode manipular erros lançados pelo motor.

Claro, a verdadeira questão é se realmente precisamos e quando; há casos (99% na minha opinião) em que um retorno elegante de falseé suficiente e deixa apenas alguns pontos críticos a serem mostrados com o lançamento de um erro.

Exemplo: http://jsfiddle.net/centurianii/m2sQ3/1/

centuriano
fonte
2

É assim que eu fiz, testei no Firefox e no Chrome. Isso torna possível verificar o nome do arquivo e o número da linha do local de onde a função é chamada.

logFileAndLineNumber(new Error());

function logFileAndLineNumber(newErr)
{
   if(navigator.userAgent.indexOf("Firefox") != -1)
   {
      var originPath = newErr.stack.split('\n')[0].split("/");
      var fileNameAndLineNumber = originPath[originPath.length - 1].split(">")[0];
      console.log(fileNameAndLineNumber);
   }else if(navigator.userAgent.indexOf("Chrome") != -1)
   {
      var originFile = newErr.stack.split('\n')[1].split('/');
      var fileName = originFile[originFile.length - 1].split(':')[0];
      var lineNumber = originFile[originFile.length - 1].split(':')[1];
      console.log(fileName+" line "+lineNumber);
    }
}
Dilshan Liyanage
fonte
2
console.log(new Error);

Ele mostrará a pista inteira.

RatajS
fonte
1

Para determinar em qual linha algo está, você deve pesquisar todo o código do código que ocupa a linha específica de interesse e contar os caracteres "\ n" do topo até a linha de interesse e adicionar 1.

Na verdade, estou fazendo exatamente isso em um aplicativo que estou escrevendo. É um validador de melhores práticas para HTML e ainda está em desenvolvimento, mas o processo de saída de erro no qual você estaria interessado está completo.

http://mailmarkup.org/htmlint/htmlint.html


fonte
a linha de interesse específica pode ser muitas ... Se eu tiver o mesmo método chamado várias vezes de outro método, como posso saber (naquele outro método) de onde veio a chamada?
Tal de
Você não conseguirá analisar a interpretação do JavaScript em tempo de execução de fora do interpretador. Você poderia escrever um programa para rastrear o caminho de execução em seu programa, mas seria esse programa que está executando e não o código que você deseja analisar. Normalmente, essa é uma tarefa complicada executada manualmente com a ajuda de ferramentas. Se você realmente deseja ver o que está acontecendo em seu código conforme ele é executado, faça com que ele grave metadados na tela que informam que você deseja que as decisões estejam executando quais outras partes.
-2

As respostas são simples. Não e Não (Não).

No momento em que o javascript está rodando, o conceito de arquivos / urls de origem dos quais o vir já se foi.

Também não há como determinar um número de linha porque, novamente, no momento da execução, a noção de "linhas" de código não é mais significativa em Javascript.

Implementações específicas podem fornecer ganchos de API para permitir acesso de código privilegiado a tais detalhes para fins de depuração, mas essas APIs não são expostas ao código Javascript padrão comum.

AnthonyWJones
fonte
No firefox as exceções incluem tais informações ... seria possível no firefox, pelo menos?
Zoidberg
Quando você inicia o depurador do MS Script e coloca algum ponto de interrupção, você vê na pilha de chamadas de onde exatamente você veio. Isso é por causa de ganchos especializados?
Tal de
"novamente no momento da execução, a noção de" linhas "de código não é mais significativa em Javascript." Hã? JS já mostra o número da linha toda vez que você executa console.log ()
mikemaccana
@AnthonyWJones Sim. Isso contradiz perfeitamente o absoluto 'Não e Não (Não)'.
mikemaccana,
@nailer: Em 2009, em todos os principais navegadores, de que forma minha resposta contradiz. Tenha em mente que a questão em questão é sobre a descoberta, ao executar o javascript, do número da linha do receptor?
AnthonyWJones