Come ottenere il numero di riga della funzione del chiamante JavaScript? Come ottenere l'URL di origine del chiamante JavaScript?


109

Sto usando quanto segue per ottenere il nome della funzione del chiamante JavaScript:

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

C'è un modo per scoprire il numero di riga da cui è stato chiamato il metodo?

Inoltre, esiste un modo per ottenere il nome del file JavaScript da cui è stato chiamato il metodo? O l'URL di origine?


2
Non penso che questo sia possibile in IE, altrimenti avremmo un modo per aggirare i messaggi di errore CRAPPY che non forniscono dettagli. Ma se è possibile mi piacerebbe saperlo anche io!
Zoidberg,

Sì. Ecco una funzione cross-browser che utilizza i metodi proprietari di ogni browser: github.com/eriwen/javascript-stacktrace [fixed link]
scotts

Risposte:


99

Questo funziona per me in 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);

Funziona anche in nodo. Inseriscilo nella tua funzione log () personalizzata (che aggiunge qualsiasi altra soluzione utile di cui hai bisogno, ad esempio risolto per la registrazione dell'array di Chrome) e ancora i numeri di riga da dove hai chiamato log ().
mikemaccana

60
Non è necessario lanciare l'errore; basta crearlo semplicemente:var caller_line = (new Error).stack.split("\n")[4]
ELLIOTTCABLE

2
Fusione questo suggerimento con un'altra risposta simile per ottenere un FF / Webkit "standardizzato" risposta - vedere stackoverflow.com/a/14841411/1037948
drzaus

1
Funziona anche in PhantomJS, ma devi lanciarlo, altrimenti l'attributo "stack" non è impostato sull'errore.
Joshua Richardson

1
@ELLIOTTCABLE in realtà in alcuni browser come iOS Safari devi lanciare l'eccezione! Allora perché non farlo?
arctelix

26

La soluzione di kangax introduce un ambito try..catch non necessario. Se devi accedere al numero di riga di qualcosa in JavaScript (purché utilizzi Firefox o Opera), accedi (new Error).lineNumber.


11
Ciao, grazie per l'addon. sai se è possibile ottenere il numero di linea dalla chiamata precedente? Diciamo che il metodo A chiama B, e ora in BI vorrebbe sapere in quale riga sotto A è stata effettuata la chiamata?
Tal

85
Questo è spuntato, ma non risponde alla domanda, che è come ottenere il numero di linea della funzione chiamante .
mikemaccana

3
Inoltre, questo è estremamente limitato. La soluzione migliore è lanciare un errore e utilizzare regex su error.stack che è disponibile in tutti i browser moderni. Puoi facilmente estrarre quel percorso, file, riga e colonna. Nessun problema.
arctelix

13

Sono rimasto sorpreso dal fatto che la maggior parte di queste risposte presumesse che si volesse gestire un errore piuttosto che produrre solo utili tracce di debug anche per casi normali.

Ad esempio, mi piace usare un console.logwrapper come questo:

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

Questo finisce per stampare un output come il seguente sulla mia console:

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

Vedere https://stackoverflow.com/a/27074218/ e anche un wrapper appropriato per console.log con il numero di riga corretto?


1
funziona per il browser Firefox, ma non funziona per node.js.
cerniera

1
sotto il nodo devi registrare lo stack [2]
monika mevenkamp

5

Ciò si ottiene spesso generando un errore dal contesto corrente; quindi analizzare l'oggetto di errore per proprietà come lineNumbere fileName(che hanno alcuni browser)

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

var err = getErrorObject();

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

Non dimenticare che la callee.callerproprietà è deprecata (e non è mai stata realmente in ECMA 3a ed. In primo luogo).

Ricorda anche che la decompilazione della funzione è specificata come dipendente dall'implementazione e quindi potrebbe produrre risultati abbastanza inaspettati. Ne ho scritto qui e qui .


Grazie, sembra un po 'problematico aggiungere questo codice nell'applicazione di cui ho bisogno. (alcuni js tracing framework) Conosci qualche altro metodo che non è deprecato che posso usare?
Tal

Dovresti essere in grado di esaminare l'oggetto errore senza fare affidamento su deprecato callee.caller.
kangax

Non è necessario lanciare l'errore. Devi solo usare (new Error) .lineNumber per accedere al numero di riga corrente in uno script.
Eli Gray,

@Elijah Questo è quello che vedo in FF3. WebKit, d'altra parte, popola linesolo quando viene generato un errore.
kangax

Qual è il sostituto di callee.caller? Se ho bisogno di ottenere il nome della funzione?
Tal

4

Sembra che io sia un po 'in ritardo :), ma la discussione è piuttosto interessante quindi ... eccolo ... Supponendo che tu voglia costruire un gestore di errori e tu stia usando la tua classe di gestore di eccezioni come:

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

E stai avvolgendo il tuo codice in questo modo:

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

Molto probabilmente avrai il gestore degli errori in un file .js separato.

Noterai che nella console degli errori di Firefox o Chrome il numero di riga di codice (e il nome del file) mostrato è la riga (file) che genera l'eccezione "Error" e non l'eccezione "errorHandler" che desideri veramente per eseguire il debug facile. Lanciare le proprie eccezioni è fantastico, ma in grandi progetti individuarle può essere un bel problema, soprattutto se hanno messaggi simili. Quindi, quello che puoi fare è passare un riferimento a un oggetto Error vuoto effettivo al tuo gestore di errori, e quel riferimento conterrà tutte le informazioni che desideri (ad esempio in firefox puoi ottenere il nome del file, il numero di riga ecc. ; in chrome ottieni qualcosa di simile se leggi la proprietà 'stack' dell'istanza Error). Per farla breve, puoi fare qualcosa del genere:

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

Ora puoi ottenere il file effettivo e il numero di riga che ha generato l'eccezione personalizzata.


4

Il numero di riga è in realtà qualcosa di statico, quindi se lo desideri solo per la registrazione, potrebbe essere preelaborato con qualcosa come gulp. Ho scritto un piccolo plugin per gulp che fa esattamente questo:

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

Questo allegherà il nome del file e la riga a tutti i log da console.log, così console.log(something)diventerà console.log('filePath:fileNumber', something). Il vantaggio è che ora puoi concatenare i tuoi file, trasferirli ... e otterrai comunque la linea


Questo sembra un ottimo suggerimento per le situazioni in cui viene utilizzato un transpiler (ad esempio, quando si utilizza TypeScript). Grazie!
Andy King

4

Mi rendo conto che questa è una vecchia domanda, ma ora esiste un metodo chiamato console.trace("Message")che ti mostrerà il numero di riga e la catena di chiamate al metodo che hanno portato al registro insieme al messaggio che hai passato. Maggiori informazioni sui trucchi per la registrazione di javascript sono disponibili qui su freecodecamp e questo post di blog medio


3

Se desideri conoscere il numero di riga a scopo di debug o solo durante lo sviluppo (per un motivo o per un altro), puoi utilizzare Firebug (un'estensione per Firefox) e generare un'eccezione.

Modifica :

Se hai davvero bisogno di farlo in produzione per qualche motivo, puoi pre-elaborare i tuoi file javascript in modo che ogni funzione tenga traccia della linea su cui si trova. So che alcuni framework che trovano la copertura del codice lo usano (come JSCoverage ).

Ad esempio, supponiamo che la tua chiamata originale sia:

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

Potresti scrivere un preprocessore per trasformarlo in:

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

Quindi in y(), potresti usare arguments.callee.caller.lineper conoscere la linea da cui è stato chiamato, come ad esempio:

function y() {
  alert(arguments.callee.caller.line);
}

1
Grazie, vorrei farlo in produzione per motivi di supporto. Ho trovato del codice che ti consente di vedere lo stack di chiamate di tutto il flusso fino al metodo, ma non ha i numeri di riga in cui sono stati chiamati i metodi. Immagino che una soluzione facile per questo?
Tal

3

Ecco come l'ho fatto, l'ho testato sia su Firefox che su Chrome. Ciò rende possibile controllare il nome del file e il numero di riga del luogo da cui viene chiamata la funzione.

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

2

Ecco cosa ho scritto in base alle informazioni trovate su questo forum:

Questo fa parte di un MyDebugNamespace, Debug è apparentemente riservato e non funzionerà come nome dello spazio dei nomi.

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

...

Come chiamare:

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

Ho incluso due funzioni con un numero variabile di argomenti che chiamano questo codice di base nel mio spazio dei nomi in modo da omettere facoltativamente messaggi o errori nelle chiamate.

Funziona bene con Firefox, IE6 e Chrome riportano il fileName e il lineNumber come non definiti.


2

il codice seguente funziona per me in Mozilla e Chrome.

La sua funzione di registro che mostra il nome del file e la riga del chiamante.

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

Sembra che manchi qualcosa. Ottengo:SyntaxError: function statement requires a name,log: function (arg) {
spiderplant0

Mi piace questa idea, ma i numeri di riga risultano errati per me.
Ryan

2

Il mio contributo agli errori personalizzati in JavaScript:

  1. Innanzitutto, sono d'accordo con questo ragazzo @BT su Inheriting from the Error object - dov'è la proprietà del messaggio? , dobbiamo costruirlo correttamente (in realtà devi usare una libreria di oggetti js, la mia preferita: 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. quindi, dovremmo definire una funzione di gestione degli errori globale (opzionale) oppure, finiranno nel motore:

    window.onerror = printError; 
    function printError(msg, url, line){
        document.getElementById('test').innerHTML = msg+'<br>at: '+url+'<br>line: '+line;
        return true;
    }
  3. infine, dovremmo lanciare con attenzione i nostri errori personalizzati:

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

Solo quando si passa il terzo parametro in quanto new Error()siamo in grado di vedere lo stack con la funzione e i numeri di riga!

A 2, la funzione può anche gestire gli errori generati dal motore.

Naturalmente, la vera domanda è se ne abbiamo davvero bisogno e quando; ci sono casi (99% a mio avviso) in cui falsebasta un aggraziato ritorno di e lasciano solo alcuni punti critici da evidenziare con il lancio di un errore.

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


2
console.log(new Error);

Ti mostrerà l'intera traccia.


1

Per determinare su quale riga si trova qualcosa devi cercare in tutto il codice il codice che occupa la particolare riga di interesse e contare i caratteri "\ n" dall'alto a questo di interesse e aggiungere 1.

In realtà sto facendo proprio questa cosa in un'applicazione che sto scrivendo. È un validatore di best practice per HTML ed è ancora fortemente in fase di sviluppo, ma il processo di output degli errori che ti interesserebbe è completo.

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


la particolare linea di interesse può essere molte ... Se ho lo stesso metodo chiamato più volte da un altro metodo, come posso sapere (in quell'altro metodo) da dove è arrivata la chiamata?
Tal

Non sarai in grado di analizzare l'interpretazione JavaScript al momento dell'esecuzione dall'esterno dell'interprete. Potresti scrivere un programma per tracciare il percorso di esecuzione nel tuo programma, ma sarebbe quel programma che è in esecuzione e non il codice che desideri analizzare. Questa è in genere un'attività complicata che viene eseguita manualmente con l'aiuto di strumenti. Se vuoi davvero vedere cosa sta succedendo nel tuo codice mentre viene eseguito, chiedigli di scrivere i metadati sullo schermo che ti dicono che le decisioni sono in esecuzione quali altre parti.

-2

Le risposte sono semplici. No e No (No).

Nel momento in cui javascript esegue il concetto di file / URL sorgente da cui è sparito il come.

Inoltre, non è possibile determinare un numero di riga perché ancora una volta al momento dell'esecuzione la nozione di "righe" di codice non è più significativa in Javascript.

Implementazioni specifiche possono fornire hook API per consentire l'accesso di codice privilegiato a tali dettagli ai fini del debug, ma queste API non sono esposte al normale codice Javascript standard.


In firefox le eccezioni includono tali informazioni ... sarebbe possibile almeno in firefox?
Zoidberg,

Quando avvii il debugger di MS Script e metti un punto di interruzione, nello stack di chiamate vedi esattamente da dove vieni. Questo è a causa di ganci specializzati?
Tal

"ancora una volta al momento dell'esecuzione la nozione di" righe "di codice non è più significativa in Javascript." Eh? JS mostra già il numero di riga ogni volta che esegui console.log ()
mikemaccana

@AnthonyWJones Sì. Contrasta nettamente il "No e No (No)" in qualche modo assoluto.
mikemaccana

@nailer: nel 2009 su tutti i principali browser, in che modo la mia risposta contraddice. Tieni presente che la domanda in questione riguarda la scoperta, nell'esecuzione di javascript, del numero di riga del chiamato?
AnthonyWJones
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.