Wie erhalte ich die Funktionszeilennummer des JavaScript-Anrufers? Wie erhalte ich die Quell-URL des JavaScript-Anrufers?

108

Ich verwende Folgendes, um den Funktionsnamen des JavaScript-Aufrufers abzurufen:

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

Gibt es eine Möglichkeit, die Zeilennummer zu ermitteln, von der aus die Methode aufgerufen wurde?

Gibt es auch eine Möglichkeit, den Namen der JavaScript-Datei abzurufen, aus der die Methode aufgerufen wurde? Oder die Quell-URL?

Tal
quelle
2
Ich denke nicht, dass dies im IE möglich ist, sonst hätten wir eine Möglichkeit, CRAPPY-Fehlermeldungen zu umgehen, die keine Details liefern. Aber wenn es möglich ist, würde ich es auch gerne wissen!
Zoidberg
Ja. Hier ist eine browserübergreifende Funktion, die die proprietären Methoden der einzelnen Browser verwendet: github.com/eriwen/javascript-stacktrace [fester Link]
scotts

Antworten:

98

Dies funktioniert bei mir 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);
Nathan Landis
quelle
Funktioniert auch im Knoten. Fügen Sie dies in Ihre benutzerdefinierte log () - Funktion ein (die alle anderen nützlichen Problemumgehungen hinzufügt, die Sie benötigen - z. B. für die Chrome-Array-Protokollierung behoben), und führen Sie weiterhin Zeilennummern von jedem Ort aus, an dem Sie log () aufgerufen haben.
Mikemaccana
60
Sie müssen den Fehler nicht auslösen. Es reicht aus, es einfach zu erstellen:var caller_line = (new Error).stack.split("\n")[4]
ELLIOTTCABLE
2
Dieser Vorschlag wurde mit einer anderen ähnlichen Antwort zusammengeführt, um eine "standardisierte" FF / Webkit-Antwort zu erhalten - siehe stackoverflow.com/a/14841411/1037948
drzaus
1
Funktioniert auch in PhantomJS, aber Sie müssen es werfen, sonst wird das Attribut "stack" für den Fehler nicht festgelegt.
Joshua Richardson
1
@ELLIOTTCABLE tatsächlich in einigen Browsern wie iOS Safari müssen Sie die Ausnahme auslösen! Warum also nicht?
Arctelix
25

Die Lösung von kangax führt unnötige try..catch-Bereiche ein. Wenn Sie auf die Zeilennummer von etwas in JavaScript zugreifen müssen (solange Sie Firefox oder Opera verwenden), greifen Sie einfach zu (new Error).lineNumber.

Eli Gray
quelle
11
Hallo, danke für das Addon. Wissen Sie, ob es möglich ist, die Leitungsnummer aus dem vorherigen Anruf zu erhalten? Nehmen wir an, Methode A ruft B auf und möchte nun in BI wissen, in welcher Zeile unter A der Anruf getätigt wurde.
Tal
85
Dies ist angekreuzt, beantwortet aber nicht die Frage, wie die Zeilennummer der Anruferfunktion abgerufen werden soll .
Mikemaccana
2
Auch dies ist äußerst begrenzt. Die beste Lösung besteht darin, einen Fehler auszulösen und Regex für den error.stack zu verwenden, der in allen modernen Browsern verfügbar ist. Sie können diesen Pfad, diese Datei, diese Zeile und diese Spalte einfach extrahieren. Kein Problem.
Arctelix
12

Ich war überrascht, dass die meisten dieser Antworten davon ausgegangen sind, dass Sie einen Fehler behandeln möchten, anstatt nur hilfreiche Debug-Traces für normale Fälle auszugeben.

Zum Beispiel verwende ich gerne einen console.logWrapper wie diesen:

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

Dadurch wird eine Ausgabe wie die folgende auf meiner Konsole gedruckt:

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

Siehe https://stackoverflow.com/a/27074218/ und auch einen richtigen Wrapper für console.log mit der richtigen Zeilennummer?

Ryan
quelle
funktioniert für Firefox-Browser, funktioniert aber nicht für node.js.
Reißverschluss
unter
knoten
4

Dies wird häufig erreicht, indem ein Fehler aus dem aktuellen Kontext ausgegeben wird. dann Analyse des Fehlerobjekts auf Eigenschaften wie lineNumberund fileName(die einige Browser haben)

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

var err = getErrorObject();

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

Vergessen Sie nicht, dass callee.callerEigentum veraltet ist (und nie wirklich in ECMA 3. Ausgabe war).

Denken Sie auch daran, dass die Funktionsdekompilierung als implementierungsabhängig angegeben wird und daher zu unerwarteten Ergebnissen führen kann. Ich habe hier und hier darüber geschrieben .

Kangax
quelle
Vielen Dank, es scheint etwas problematisch, diesen Code in die von mir benötigte Anwendung einzufügen. (einige js Tracing Framework) Kennen Sie eine andere Methode, die nicht veraltet ist und die ich verwenden kann?
Tal
Sie sollten in der Lage sein, Fehlerobjekte zu überprüfen, ohne sich auf veraltete zu verlassen callee.caller.
Kangax
Sie müssen den Fehler nicht auslösen. Sie verwenden einfach (neuer Fehler) .lineNumber, um auf die aktuelle Zeilennummer in einem Skript zuzugreifen.
Eli Gray
@ Elijah Das sehe ich in FF3. WebKit hingegen wird linenur ausgefüllt, wenn ein Fehler ausgegeben wird.
Kangax
Was ist der Ersatz für callee.caller? Wenn ich den Funktionsnamen erhalten muss?
Tal
3

Es scheint, dass ich etwas spät dran bin :), aber die Diskussion ist ziemlich interessant, also ... hier ist es ... Angenommen, Sie möchten einen Fehlerhandler erstellen und verwenden Ihre eigene Ausnahmebehandlungsklasse wie:

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

Und Sie verpacken Ihren Code folgendermaßen:

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

Höchstwahrscheinlich haben Sie den Fehlerbehandler in einer separaten .js-Datei.

Sie werden feststellen, dass in der Firefox- oder Chrome-Fehlerkonsole die angezeigte Codezeilennummer (und der Dateiname) die Zeile (Datei) ist, die die Ausnahme 'Error' auslöst, und nicht die Ausnahme 'errorHandler', die Sie wirklich für das Debuggen benötigen einfach. Das Auslösen eigener Ausnahmen ist großartig, aber bei großen Projekten kann das Auffinden dieser Ausnahmen ein ziemliches Problem sein, insbesondere wenn sie ähnliche Meldungen enthalten. Sie können also einen Verweis auf ein tatsächlich leeres Fehlerobjekt an Ihren Fehlerbehandler übergeben, und dieser Verweis enthält alle gewünschten Informationen (z. B. können Sie in Firefox den Dateinamen und die Zeilennummer usw. abrufen. ; in Chrome erhalten Sie etwas Ähnliches, wenn Sie die Eigenschaft 'stack' der Fehlerinstanz lesen. Kurz gesagt, Sie können so etwas tun:

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

Jetzt können Sie die tatsächliche Datei- und Zeilennummer abrufen, die Ihre benutzerdefinierte Ausnahme ausgelöst hat.

Rusu Bogdan
quelle
3

Die Zeilennummer ist eigentlich etwas Statisches. Wenn Sie sie also nur zum Protokollieren verwenden möchten, kann sie mit etwas wie Schlucken vorverarbeitet werden. Ich habe ein kleines Schluck- Plugin geschrieben , das genau das tut:

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

Dies wird den Dateinamen und Zeile für alle Protokolle von console.log befestigen, so console.log(something)wird worden console.log('filePath:fileNumber', something). Der Vorteil ist, dass Sie jetzt Ihre Dateien zusammenfassen, transpilieren können ... und Sie trotzdem die Zeile erhalten

Gabriel Furstenheim
quelle
Dies scheint ein guter Vorschlag für Situationen zu sein, in denen ein Transpiler verwendet wird (z. B. bei Verwendung von TypeScript). Danke dir!
Andy King
3

Mir ist klar, dass dies eine alte Frage ist, aber es gibt jetzt eine Methode namens console.trace("Message"), die Ihnen die Zeilennummer und die Kette von Methodenaufrufen anzeigt, die zum Protokoll geführt haben, zusammen mit der Nachricht, die Sie übergeben haben. Weitere Informationen zu Javascript-Protokollierungstricks finden Sie hier im freecodecamp und in diesem mittleren Blogbeitrag

Japheth Ongeri - inkalimeva
quelle
2

Wenn Sie die Zeilennummer zu Debugging-Zwecken oder nur während der Entwicklung (aus dem einen oder anderen Grund) wissen möchten, können Sie Firebug (eine Firefox-Erweiterung) verwenden und werfen eine Ausnahme.

Bearbeiten :

Wenn Sie dies aus irgendeinem Grund in der Produktion wirklich tun müssen, können Sie Ihre Javascript-Dateien vorverarbeiten, damit jede Funktion die Linie verfolgt, in der sie sich befindet. Ich weiß, dass einige Frameworks, die die Abdeckung von Code finden, dies verwenden (wie JSCoverage ).

Angenommen, Ihr ursprünglicher Anruf lautet:

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

Sie könnten einen Präprozessor schreiben, um daraus zu machen:

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

Dann y()könnten Sie in verwenden arguments.callee.caller.line, um die Zeile zu kennen, von der es aufgerufen wurde, wie zum Beispiel:

function y() {
  alert(arguments.callee.caller.line);
}
Sinan Taifour
quelle
1
Danke, ich möchte dies aus Supportgründen in der Produktion tun. Ich habe einen Code gefunden, mit dem Sie den Aufrufstapel aller Abläufe bis zur Methode anzeigen können, aber er enthält nicht die Zeilennummern, unter denen Methoden aufgerufen wurden. Ich denke zu einfache Lösung dafür?
Tal
1

Folgendes habe ich basierend auf Informationen in diesem Forum geschrieben:

Dies ist Teil eines MyDebugNamespace. Debug ist anscheinend reserviert und wird nicht als Namespace-Name verwendet.

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

...

So rufen Sie an:

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

Ich habe zwei Funktionen mit variabler Anzahl von Argumenten, die diesen Basiscode aufrufen, in meinen Namespace aufgenommen, um optional Nachrichten oder Fehler bei Aufrufen wegzulassen.

Dies funktioniert gut mit Firefox, IE6 und Chrome melden den Dateinamen und die Zeilennummer als undefiniert.

pasx
quelle
1

Der folgende Code funktioniert für mich in Mozilla und Chrome.

Seine Protokollfunktion, die den Namen der Datei und die Zeile des Anrufers anzeigt.

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
quelle
Sieht so aus, als ob etwas fehlt. Ich bekomme:SyntaxError: function statement requires a name,log: function (arg) {
Spiderplant0
Ich mag diese Idee, aber die Zeilennummern sind für mich falsch.
Ryan
1

Mein Beitrag zu benutzerdefinierten Fehlern in JavaScript:

  1. Erstens stimme ich diesem @ BT-Typ beim Erben vom Fehlerobjekt zu - wo ist die Nachrichteneigenschaft? , wir müssen es richtig erstellen (eigentlich müssen Sie eine js-Objektbibliothek verwenden, mein Favorit: 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. Dann sollten wir eine globale Fehlerbehandlungsfunktion definieren (optional), oder sie landen bei der Engine:

    window.onerror = printError; 
    function printError(msg, url, line){
        document.getElementById('test').innerHTML = msg+'<br>at: '+url+'<br>line: '+line;
        return true;
    }
  3. Schließlich sollten wir unsere benutzerdefinierten Fehler sorgfältig werfen:

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

Nur wenn new Error()wir den dritten Parameter übergeben, können wir den Stapel mit Funktions- und Zeilennummern sehen!

Bei 2 kann die Funktion auch Fehler verarbeiten, die vom Motor ausgelöst werden.

Die eigentliche Frage ist natürlich, ob und wann wir sie wirklich brauchen. Es gibt Fälle (99% meiner Meinung nach), in denen eine würdevolle Rückkehr vonfalse ausreicht und nur einige kritische Punkte mit einem Fehler angezeigt werden.

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

Centurian
quelle
1

So habe ich es gemacht, ich habe es sowohl in Firefox als auch in Chrome getestet. Auf diese Weise können Sie den Dateinamen und die Zeilennummer des Ortes überprüfen, von dem aus die Funktion aufgerufen wird.

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
quelle
1
console.log(new Error);

Es zeigt Ihnen den gesamten Track.

RatajS
quelle
0

Um festzustellen, in welcher Zeile sich etwas befindet, müssen Sie den gesamten Code nach dem Code durchsuchen, der die jeweilige interessierende Zeile belegt, und die "\ n" -Zeichen von oben bis zu dieser interessierenden Zeile zählen und 1 hinzufügen.

Ich mache genau das in einer Bewerbung, die ich schreibe. Es ist ein Best-Practice-Validator für HTML und befindet sich noch in der Entwicklung, aber der Fehlerausgabevorgang, an dem Sie interessiert wären, ist abgeschlossen.

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


quelle
Die bestimmte Interessenlinie kann viele sein ... Wenn ich dieselbe Methode mehrmals von einer anderen Methode aufgerufen habe, wie kann ich dann (bei dieser anderen Methode) wissen, woher der Anruf kam?
Tal
Sie können die JavaScript-Interpretation zur Ausführungszeit nicht von außerhalb des Interpreters analysieren. Sie könnten ein Programm schreiben, um den Ausführungspfad in Ihrem Programm zu verfolgen, aber es wäre das Programm, das ausgeführt wird, und nicht der Code, den Sie analysieren möchten. Dies ist normalerweise eine komplizierte Aufgabe, die manuell mit Hilfe von Werkzeugen ausgeführt wird. Wenn Sie wirklich sehen möchten, was in Ihrem Code während der Ausführung geschieht, lassen Sie ihn Metadaten auf den Bildschirm schreiben, die Ihnen mitteilen, dass Entscheidungen ausgeführt werden sollen, welche anderen Teile ausgeführt werden.
-2

Die Antworten sind einfach. Nein und Nein (Nein).

Bis Javascript das Konzept der Quelldateien / URLs ausführt, von denen das Kommen weg ist.

Es gibt auch keine Möglichkeit, eine Zeilennummer zu bestimmen, da der Begriff "Zeilen" zum Zeitpunkt der Ausführung in Javascript wiederum nicht mehr von Bedeutung ist.

Bestimmte Implementierungen können API-Hooks bereitstellen, um privilegierten Code zum Debuggen auf solche Details zuzugreifen. Diese APIs sind jedoch keinem normalen Standard-Javascript-Code ausgesetzt.

AnthonyWJones
quelle
In Firefox enthalten die Ausnahmen solche Informationen ... wäre es zumindest in Firefox möglich?
Zoidberg
Wenn Sie den MS Script-Debugger starten und einen Haltepunkt setzen, sehen Sie im Aufrufstapel, woher genau Sie gekommen sind. Dies liegt an speziellen Haken?
Tal
"Zum Zeitpunkt der Ausführung ist der Begriff" Codezeilen "in Javascript nicht mehr von Bedeutung." Huh? JS zeigt bereits die Zeilennummer jedes Mal, wenn Sie console.log ()
ausführen
@AnthonyWJones Ja. Es widerspricht ordentlich dem etwas absoluten "Nein und Nein (Nein)".
Mikemaccana
@nailer: In 2009 widerspricht meine Antwort in allen gängigen Browsern meiner Antwort. Denken Sie daran, dass es bei der vorliegenden Frage um die Entdeckung der Zeilennummer des Angerufenen in Javascript geht.
AnthonyWJones