Jak uzyskać numer linii funkcji wywołującej JavaScript? Jak uzyskać adres URL źródła wywołującego JavaScript?


109

Do uzyskania nazwy funkcji wywołującej JavaScript używam następującego:

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

Czy istnieje sposób na sprawdzenie numeru wiersza, z którego wywołano metodę?

Czy istnieje również sposób na pobranie nazwy pliku JavaScript, z którego wywołano metodę? Lub źródłowy adres URL?


2
Nie sądzę, żeby to było możliwe w IE, bo inaczej mielibyśmy sposób, aby obejść się tam CRAPPY komunikaty o błędach, które nie dostarczają żadnych szczegółów. Ale jeśli to możliwe, chciałbym również wiedzieć!
Zoidberg

Tak. Oto funkcja między przeglądarkami, która korzysta z zastrzeżonych metod każdej przeglądarki: github.com/eriwen/javascript-stacktrace [stały link]
scotts

Odpowiedzi:


99

To działa dla mnie w 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);

Działa też w węźle. Umieść to w swojej niestandardowej funkcji log () (która dodaje wszelkie inne przydatne obejścia, których potrzebujesz - np. Naprawiono rejestrowanie tablic w Chrome) i nadal numery wierszy z dowolnego miejsca, w którym wywołałeś log ().
mikemaccana

60
Nie musisz zgłaszać błędu; wystarczy go stworzyć:var caller_line = (new Error).stack.split("\n")[4]
ELLIOTTCABLE

2
Połączono tę sugestię z inną podobną odpowiedzią, aby uzyskać „znormalizowaną” odpowiedź FF / Webkit - zobacz stackoverflow.com/a/14841411/1037948
drzaus

1
Działa również w PhantomJS, ale musisz to wyrzucić, inaczej atrybut „stack” nie jest ustawiony na błąd.
Joshua Richardson

1
@ELLIOTTCABLE w rzeczywistości w niektórych przeglądarkach, takich jak safari na iOS, musisz rzucić wyjątek! Więc dlaczego tego nie zrobić?
arctelix

26

Rozwiązanie kangaxa wprowadza niepotrzebny zakres try..catch. Jeśli potrzebujesz uzyskać dostęp do numeru linii czegoś w JavaScript (o ile używasz przeglądarki Firefox lub Opera), po prostu uzyskaj dostęp (new Error).lineNumber.


11
Cześć, dzięki za ten dodatek. czy wiesz, czy można uzyskać numer linii z poprzedniego połączenia? Powiedzmy, że metoda A wywołuje B, a teraz w BI chciałbyś wiedzieć, w której linii pod A zostało wykonane połączenie?
Tal

85
Jest to zaznaczone, ale nie odpowiada na pytanie, jak uzyskać numer linii funkcji dzwoniącego .
mikemaccana

3
Jest to również bardzo ograniczone. Najlepszym rozwiązaniem jest zgłoszenie błędu i użycie wyrażenia regularnego w pliku error.stack, który jest dostępny we wszystkich nowoczesnych przeglądarkach. Możesz łatwo wyodrębnić tę ścieżkę, plik, wiersz i kolumnę. Nie ma problemu.
arctelix

13

Byłem zaskoczony, że większość tych odpowiedzi zakładała, że ​​chcesz obsłużyć błąd, a nie tylko wyprowadzić pomocne ślady debugowania również dla normalnych przypadków.

Na przykład lubię używać takiego console.logopakowania:

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

To kończy się wypisywaniem na moją konsolę wyniku takiego jak następujący:

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

Zobacz https://stackoverflow.com/a/27074218/, a także Prawidłowe opakowanie dla console.log z poprawnym numerem linii?


1
działa w przeglądarce Firefox, ale nie działa w przypadku node.js.
zamek błyskawiczny

1
pod węzłem musisz zalogować stos [2]
monika mevenkamp

5

Często osiąga się to przez wyrzucenie błędu z bieżącego kontekstu; następnie analizuje obiekt błędu pod kątem właściwości takich jak lineNumberi fileName(które mają niektóre przeglądarki)

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

var err = getErrorObject();

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

Nie zapomnij o tym callee.caller właściwość jest przestarzała (i przede wszystkim nigdy nie była w 3. edycji ECMA).

Pamiętaj również, że dekompilacja funkcji jest określona jako zależna od implementacji, więc może dać całkiem nieoczekiwane wyniki. Pisałem o tym tutaj i tutaj .


Dzięki, dodanie tego kodu w aplikacji, której potrzebuję, wydaje się nieco problematyczne. (niektóre ramy śledzenia js) Czy znasz jakąś inną metodę, która nie jest przestarzała, a której mogę użyć?
Tal

Powinieneś być w stanie zbadać obiekt błędu bez polegania na przestarzałym callee.caller.
kangax

Nie musisz zgłaszać błędu. Po prostu użyj (new Error) .lineNumber, aby uzyskać dostęp do bieżącego numeru linii w skrypcie.
Eli Gray

@Elijah To właśnie widzę w FF3. Z drugiej strony WebKit zapełnia się linetylko wtedy, gdy zostanie zgłoszony błąd.
kangax

Co jest substytutem dla callee.caller? Czy potrzebuję nazwy funkcji?
Tal

4

Wygląda na to, że trochę się spóźniłem :), ale dyskusja jest dość interesująca, więc ... proszę bardzo ... Zakładając, że chcesz zbudować moduł obsługi błędów i używasz własnej klasy obsługi wyjątków, takiej jak:

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

I opakowujesz swój kod w ten sposób:

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

Najprawdopodobniej będziesz mieć moduł obsługi błędów w oddzielnym pliku .js.

Zauważysz, że w konsoli błędów przeglądarki Firefox lub Chrome pokazany numer linii kodu (i nazwa pliku) to linia (plik), która zgłasza wyjątek „Błąd”, a nie wyjątek „ErrorHandler”, który naprawdę chcesz, aby przeprowadzić debugowanie łatwo. Zgłaszanie własnych wyjątków jest świetne, ale w przypadku dużych projektów ich lokalizacja może być sporym problemem, zwłaszcza jeśli mają podobne komunikaty. Więc co możesz zrobić, to przekazać odniesienie do rzeczywistego pustego obiektu Error do swojego modułu obsługi błędów, a to odniesienie będzie zawierało wszystkie potrzebne informacje (na przykład w przeglądarce Firefox możesz uzyskać nazwę pliku, numer wiersza itp. ; w chrome otrzymasz coś podobnego, jeśli przeczytasz właściwość „stack” instancji Error). Krótko mówiąc, możesz zrobić coś takiego:

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

Teraz możesz uzyskać rzeczywisty plik i numer wiersza, który zgłosił niestandardowy wyjątek.


4

Numer linii jest w rzeczywistości czymś statycznym, więc jeśli chcesz go tylko do logowania, może być wstępnie przetworzony za pomocą czegoś takiego jak łyk. Napisałem małą wtyczkę, która robi dokładnie to:

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

Spowoduje to dołączenie nazwy pliku i wiersza do wszystkich dzienników z console.log, więc console.log(something)stanie się console.log('filePath:fileNumber', something). Zaletą jest to, że teraz możesz konkatować swoje pliki, transponować je ... i nadal otrzymasz linię


Wydaje się to świetną sugestią w sytuacjach, w których używany jest transpiler (np. Podczas używania TypeScript). Dziękuję Ci!
Andy King


3

Jeśli chcesz znać numer linii do celów debugowania lub tylko podczas programowania (z jakiegoś powodu), możesz użyć Firebug (rozszerzenie przeglądarki Firefox) i wyrzucić wyjątek.

Edycja :

Jeśli naprawdę z jakiegoś powodu musisz to zrobić w środowisku produkcyjnym, możesz wstępnie przetworzyć pliki javascript, aby każda funkcja mogła śledzić linię, w której się znajduje. Wiem, że niektóre frameworki, które znajdują pokrycie kodu, używają tego (na przykład JSCoverage ).

Załóżmy na przykład, że Twoje pierwotne połączenie to:

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

Możesz napisać preprocesor, aby zrobić to w:

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

Następnie y()możesz arguments.callee.caller.linepoznać linię, z której została wywołana, na przykład:

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

1
Dzięki, chciałbym to w produkcji ze względów wsparcia. Znalazłem kod, który umożliwia wyświetlenie stosu wywołań całego przepływu aż do metody, ale nie ma numerów linii, w których zostały wywołane metody. Myślę, że to łatwe rozwiązanie?
Tal

3

Tak to zrobiłem, przetestowałem to zarówno w Firefoksie, jak i Chrome. Umożliwia to sprawdzenie nazwy pliku i numeru wiersza miejsca, z którego wywoływana jest funkcja.

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

Oto co napisałem na podstawie informacji znalezionych na tym forum:

Jest to część MyDebugNamespace, Debug jest najwyraźniej zarezerwowana i nie będzie działać jako nazwa przestrzeni nazw.

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

...

Jak zadzwonić:

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

W mojej przestrzeni nazw zawarłem dwie funkcje ze zmienną liczbą argumentów wywołujących ten kod podstawowy, aby opcjonalnie pominąć komunikat lub błąd w wywołaniach.

Działa to dobrze w przeglądarkach Firefox, IE6 i Chrome zgłaszają nazwę pliku i numer wiersza jako niezdefiniowane.


2

poniższy kod działa u mnie w Mozilli i Chrome.

Jego funkcja dziennika, która pokazuje nazwę pliku i linię dzwoniącego.

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

Wygląda na to, że czegoś brakuje. Dostaję:SyntaxError: function statement requires a name,log: function (arg) {
spiderplant0

Podoba mi się ten pomysł, ale numery wierszy są dla mnie nieprawidłowe.
Ryan

2

Mój wkład w niestandardowe błędy w JavaScript:

  1. Po pierwsze, zgadzam się z tym facetem @BT w Inheriting from the Error - gdzie jest właściwość wiadomości? , musimy to poprawnie zbudować (właściwie musisz użyć biblioteki obiektów js, moja ulubiona: 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. następnie powinniśmy zdefiniować globalną funkcję obsługi błędów (opcjonalnie) lub skończą one na silniku:

    window.onerror = printError; 
    function printError(msg, url, line){
        document.getElementById('test').innerHTML = msg+'<br>at: '+url+'<br>line: '+line;
        return true;
    }
  3. na koniec powinniśmy uważnie zgłaszać nasze niestandardowe błędy:

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

Tylko przy przekazywaniu trzeciego parametru jako new Error() jesteśmy w stanie zobaczyć stos z numerami funkcji i linii!

Na 2 funkcja radzi sobie również z błędami zgłaszanymi przez silnik.

Oczywiście prawdziwe pytanie brzmi, czy naprawdę tego potrzebujemy i kiedy; zdarzają się przypadki (moim zdaniem 99%), w których wystarczy wdzięczny powrót falsei pozostawienie tylko niektórych punktów krytycznych do pokazania z wyrzuceniem błędu.

Przykład: http://jsfiddle.net/centurianii/m2sQ3/1/



1

Aby określić, w którym wierszu jest coś, musisz przeszukać cały kod pod kątem kodu, który zajmuje dany wiersz, i policzyć znaki „\ n” od góry do tego, który Cię interesuje, i dodać 1.

W rzeczywistości robię to właśnie w aplikacji, którą piszę. Jest to walidator najlepszych praktyk dla HTML i wciąż jest intensywnie rozwijany, ale proces wyświetlania błędów, który Cię interesuje, został zakończony.

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


konkretna linia zainteresowania może być wiele ... Jeśli mam tę samą metodę wywoływaną kilka razy z innej metody, skąd mam wiedzieć (w tej innej metodzie), skąd pochodzi połączenie?
Tal

Nie będziesz w stanie analizować interpretacji JavaScript w czasie wykonywania z zewnątrz interpretera. Mógłbyś napisać program do śledzenia ścieżki wykonywania w twoim programie, ale byłby to wykonywany program, a nie kod, który chcesz analizować. Zwykle jest to skomplikowane zadanie, które jest wykonywane ręcznie za pomocą narzędzi. Jeśli naprawdę chcesz zobaczyć, co dzieje się w twoim kodzie podczas jego wykonywania, poproś go o zapisanie metadanych na ekranie, które mówią, że chcesz, aby decyzje były wykonywane, które inne części.

-2

Odpowiedzi są proste. Nie i nie (nie).

Do czasu uruchomienia javascript koncepcja plików źródłowych / adresów URL, z których pochodzą.

Nie ma również sposobu na określenie numeru linii, ponieważ ponownie, do czasu wykonania, pojęcie „linii” kodu nie ma już znaczenia w Javascript.

Konkretne implementacje mogą zapewniać punkty zaczepienia API, aby umożliwić uprzywilejowany dostęp kodu do takich szczegółów w celu debugowania, ale te interfejsy API nie są narażone na zwykły standardowy kod JavaScript.


W programie Firefox wyjątki obejmują takie informacje… czy byłoby to możliwe przynajmniej w programie Firefox?
Zoidberg

Po uruchomieniu debugera MS Script i umieszczeniu punktu przerwania, w stosie wywołań zobaczysz, skąd dokładnie przyszedłeś. Czy to z powodu wyspecjalizowanych haczyków?
Tal

„ponownie, do czasu wykonania, pojęcie„ linii ”kodu nie ma już znaczenia w Javascript.” Co? JS już pokazuje numer linii za każdym razem, gdy uruchamiasz console.log ()
mikemaccana

@AnthonyWJones Yes. Zgrabnie zaprzecza nieco absolutnemu „nie i nie (nie)”.
mikemaccana

@nailer: W 2009 roku we wszystkich głównych przeglądarkach, w jaki sposób moja odpowiedź jest sprzeczna. Pamiętaj, że pytanie dotyczy odkrycia numeru linii dzwoniącego w języku JavaScript?
Anthony WJones
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.